ligo/src/lib_shell/prevalidation.ml

169 lines
6.3 KiB
OCaml
Raw Normal View History

(**************************************************************************)
(* *)
2018-02-06 00:17:03 +04:00
(* Copyright (c) 2014 - 2018. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
open Preapply_result
open Validation_errors
2017-11-19 18:15:03 +04:00
let rec apply_operations apply_operation state r max_ops ~sort ops =
Lwt_list.fold_left_s
2017-11-19 18:15:03 +04:00
(fun (state, max_ops, r) (hash, op, parsed_op) ->
apply_operation state max_ops op parsed_op >>= function
| Ok (state, _metadata) ->
let applied = (hash, op) :: r.applied in
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops - 1, { r with applied })
| Error errors ->
match classify_errors errors with
| `Branch ->
let branch_refused =
Operation_hash.Map.add hash (op, errors) r.branch_refused in
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops, { r with branch_refused })
| `Permanent ->
let refused =
Operation_hash.Map.add hash (op, errors) r.refused in
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops, { r with refused })
| `Temporary ->
let branch_delayed =
Operation_hash.Map.add hash (op, errors) r.branch_delayed in
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops, { r with branch_delayed }))
(state, max_ops, r)
ops >>= fun (state, max_ops, r) ->
match r.applied with
| _ :: _ when sort ->
let rechecked_operations =
List.filter
(fun (hash, _, _) -> Operation_hash.Map.mem hash r.branch_delayed)
ops in
let remaining = List.length rechecked_operations in
if remaining = 0 || remaining = List.length ops then
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops, r)
else
2017-11-19 18:15:03 +04:00
apply_operations apply_operation state r max_ops ~sort rechecked_operations
| _ ->
2017-11-19 18:15:03 +04:00
Lwt.return (state, max_ops, r)
type prevalidation_state =
2017-11-19 18:15:03 +04:00
State : { proto : 'a proto ; state : 'a ;
max_number_of_operations : int ;
max_operation_data_length : int }
-> prevalidation_state
and 'a proto =
(module Registered_protocol.T with type validation_state = 'a)
2017-11-19 18:15:03 +04:00
let start_prevalidation
?protocol_data
2017-11-19 18:15:03 +04:00
~predecessor ~timestamp () =
let { Block_header.shell =
{ fitness = predecessor_fitness ;
timestamp = predecessor_timestamp ;
level = predecessor_level } } =
State.Block.header predecessor in
2017-11-19 18:15:03 +04:00
let max_operation_data_length =
State.Block.max_operation_data_length predecessor in
State.Block.context predecessor >>= fun predecessor_context ->
Context.get_protocol predecessor_context >>= fun protocol ->
let predecessor = State.Block.hash predecessor in
2017-10-27 21:41:47 +04:00
begin
match Registered_protocol.get protocol with
2017-10-27 21:41:47 +04:00
| None ->
(* FIXME. *)
(* This should not happen: it should be handled in the validator. *)
failwith "Prevalidation: missing protocol '%a' for the current block."
Protocol_hash.pp_short protocol
| Some protocol ->
return protocol
end >>=? fun (module Proto) ->
2018-02-16 04:26:24 +04:00
Context.reset_test_chain
predecessor_context predecessor
timestamp >>= fun predecessor_context ->
begin
match protocol_data with
| None -> return None
| Some protocol_data ->
match
Data_encoding.Binary.of_bytes
Proto.block_header_data_encoding
protocol_data
with
| None -> failwith "Invalid block header"
| Some protocol_data -> return (Some protocol_data)
end >>=? fun protocol_data ->
Proto.begin_construction
~predecessor_context
~predecessor_timestamp
~predecessor_fitness
~predecessor_level
~predecessor
~timestamp
?protocol_data
()
>>=? fun state ->
(* FIXME arbitrary value, to be customisable *)
let max_number_of_operations = 1000 in
2017-11-19 18:15:03 +04:00
return (State { proto = (module Proto) ; state ;
max_number_of_operations ; max_operation_data_length })
let prevalidate
2017-11-19 18:15:03 +04:00
(State { proto = (module Proto) ; state ;
max_number_of_operations ; max_operation_data_length })
~sort (ops : (Operation_hash.t * Operation.t) list)=
let ops =
List.map
(fun (h, op) ->
let parsed_op =
match Data_encoding.Binary.of_bytes
Proto.operation_data_encoding
op.Operation.proto with
| None -> error Parse_error
| Some protocol_data -> Ok ({ shell = op.shell ; protocol_data }: Proto.operation) in
(h, op, parsed_op))
ops in
let invalid_ops =
List.filter_map
(fun (h, op, parsed_op) -> match parsed_op with
| Ok _ -> None
| Error err -> Some (h, op, err)) ops
and parsed_ops =
List.filter_map
(fun (h, op, parsed_op) -> match parsed_op with
| Ok parsed_op -> Some (h, op, parsed_op)
| Error _ -> None) ops in
let sorted_ops =
if sort then
let compare (_, _, op1) (_, _, op2) = Proto.compare_operations op1 op2 in
List.sort compare parsed_ops
else parsed_ops in
2017-11-19 18:15:03 +04:00
let apply_operation state max_ops op parse_op =
let size = Data_encoding.Binary.length Operation.encoding op in
if max_ops <= 0 then
fail Too_many_operations
else if size > max_operation_data_length then
fail (Oversized_operation { size ; max = max_operation_data_length })
else
Proto.apply_operation state parse_op in
apply_operations
2017-11-19 18:15:03 +04:00
apply_operation
state Preapply_result.empty max_number_of_operations
2017-11-19 18:15:03 +04:00
~sort sorted_ops >>= fun (state, max_number_of_operations, r) ->
let r =
{ r with
applied = List.rev r.applied ;
branch_refused =
List.fold_left
(fun map (h, op, err) -> Operation_hash.Map.add h (op, err) map)
r.branch_refused invalid_ops } in
2017-11-19 18:15:03 +04:00
Lwt.return (State { proto = (module Proto) ; state ;
max_number_of_operations ; max_operation_data_length },
r)
let end_prevalidation (State { proto = (module Proto) ; state }) =
Proto.finalize_block state >>=? fun (result, _metadata) ->
return result