2016-10-20 20:54:16 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
2017-11-14 03:36:14 +04:00
|
|
|
(* Copyright (c) 2014 - 2017. *)
|
2016-10-20 20:54:16 +04:00
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
|
|
|
|
type 'error preapply_result = {
|
2017-11-14 02:27:19 +04:00
|
|
|
applied: (Operation_hash.t * Operation.t) list;
|
|
|
|
refused: (Operation.t * 'error list) Operation_hash.Map.t;
|
|
|
|
branch_refused: (Operation.t * 'error list) Operation_hash.Map.t;
|
|
|
|
branch_delayed: (Operation.t * 'error list) Operation_hash.Map.t;
|
2016-10-20 20:54:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let empty_result = {
|
|
|
|
applied = [] ;
|
|
|
|
refused = Operation_hash.Map.empty ;
|
|
|
|
branch_refused = Operation_hash.Map.empty ;
|
|
|
|
branch_delayed = Operation_hash.Map.empty ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let map_result f r = {
|
|
|
|
applied = r.applied;
|
|
|
|
refused = Operation_hash.Map.map f r.refused ;
|
|
|
|
branch_refused = Operation_hash.Map.map f r.branch_refused ;
|
|
|
|
branch_delayed = Operation_hash.Map.map f r.branch_delayed ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let preapply_result_encoding error_encoding =
|
|
|
|
let open Data_encoding in
|
2017-11-14 02:27:19 +04:00
|
|
|
let operation_encoding =
|
|
|
|
merge_objs
|
|
|
|
(obj1 (req "hash" Operation_hash.encoding))
|
|
|
|
(dynamic_size Operation.encoding) in
|
|
|
|
let refused_encoding =
|
|
|
|
merge_objs
|
|
|
|
(obj1 (req "hash" Operation_hash.encoding))
|
|
|
|
(merge_objs
|
|
|
|
(dynamic_size Operation.encoding)
|
|
|
|
(obj1 (req "error" error_encoding))) in
|
2016-10-20 20:54:16 +04:00
|
|
|
let build_list map = Operation_hash.Map.bindings map in
|
|
|
|
let build_map list =
|
|
|
|
List.fold_right
|
|
|
|
(fun (k, e) m -> Operation_hash.Map.add k e m)
|
|
|
|
list Operation_hash.Map.empty in
|
|
|
|
conv
|
|
|
|
(fun { applied ; refused ; branch_refused ; branch_delayed } ->
|
|
|
|
(applied, build_list refused,
|
|
|
|
build_list branch_refused, build_list branch_delayed))
|
|
|
|
(fun (applied, refused, branch_refused, branch_delayed) ->
|
|
|
|
let refused = build_map refused in
|
|
|
|
let branch_refused = build_map branch_refused in
|
|
|
|
let branch_delayed = build_map branch_delayed in
|
|
|
|
{ applied ; refused ; branch_refused ; branch_delayed })
|
|
|
|
(obj4
|
2017-11-14 02:27:19 +04:00
|
|
|
(req "applied" (list operation_encoding))
|
2016-10-20 20:54:16 +04:00
|
|
|
(req "refused" (list refused_encoding))
|
|
|
|
(req "branch_refused" (list refused_encoding))
|
|
|
|
(req "branch_delayed" (list refused_encoding)))
|
|
|
|
|
|
|
|
let preapply_result_operations t =
|
|
|
|
let ops =
|
|
|
|
List.fold_left
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun acc (h, op) -> Operation_hash.Map.add h op acc)
|
|
|
|
Operation_hash.Map.empty t.applied in
|
2016-10-20 20:54:16 +04:00
|
|
|
let ops =
|
|
|
|
Operation_hash.Map.fold
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun h (op, _err) acc -> Operation_hash.Map.add h op acc)
|
2016-10-20 20:54:16 +04:00
|
|
|
t.branch_delayed ops in
|
|
|
|
let ops =
|
|
|
|
Operation_hash.Map.fold
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun h (op, _err) acc -> Operation_hash.Map.add h op acc)
|
2016-10-20 20:54:16 +04:00
|
|
|
t.branch_refused ops in
|
|
|
|
ops
|
|
|
|
|
|
|
|
let empty_result =
|
|
|
|
{ applied = [] ;
|
|
|
|
refused = Operation_hash.Map.empty ;
|
|
|
|
branch_refused = Operation_hash.Map.empty ;
|
|
|
|
branch_delayed = Operation_hash.Map.empty }
|
|
|
|
|
2017-11-19 18:15:03 +04:00
|
|
|
let rec apply_operations apply_operation state r max_ops ~sort ops =
|
2016-10-20 20:54:16 +04:00
|
|
|
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
|
2016-10-20 20:54:16 +04:00
|
|
|
| Ok state ->
|
2017-11-14 02:27:19 +04:00
|
|
|
let applied = (hash, op) :: r.applied in
|
2017-11-19 18:15:03 +04:00
|
|
|
Lwt.return (state, max_ops - 1, { r with applied })
|
2016-10-20 20:54:16 +04:00
|
|
|
| Error errors ->
|
|
|
|
match classify_errors errors with
|
|
|
|
| `Branch ->
|
|
|
|
let branch_refused =
|
2017-11-14 02:27:19 +04:00
|
|
|
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 })
|
2016-10-20 20:54:16 +04:00
|
|
|
| `Permanent ->
|
|
|
|
let refused =
|
2017-11-14 02:27:19 +04:00
|
|
|
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 })
|
2016-10-20 20:54:16 +04:00
|
|
|
| `Temporary ->
|
|
|
|
let branch_delayed =
|
2017-11-14 02:27:19 +04:00
|
|
|
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) ->
|
2016-10-20 20:54:16 +04:00
|
|
|
match r.applied with
|
|
|
|
| _ :: _ when sort ->
|
|
|
|
let rechecked_operations =
|
|
|
|
List.filter
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun (hash, _, _) -> Operation_hash.Map.mem hash r.branch_delayed)
|
2016-10-20 20:54:16 +04:00
|
|
|
ops in
|
2017-04-27 03:01:05 +04:00
|
|
|
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)
|
2017-04-27 03:01:05 +04:00
|
|
|
else
|
2017-11-19 18:15:03 +04:00
|
|
|
apply_operations apply_operation state r max_ops ~sort rechecked_operations
|
2016-10-20 20:54:16 +04:00
|
|
|
| _ ->
|
2017-11-19 18:15:03 +04:00
|
|
|
Lwt.return (state, max_ops, r)
|
2016-10-20 20:54:16 +04:00
|
|
|
|
|
|
|
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 }
|
2016-10-20 20:54:16 +04:00
|
|
|
-> prevalidation_state
|
|
|
|
|
|
|
|
and 'a proto =
|
2017-10-09 12:55:12 +04:00
|
|
|
(module State.Registred_protocol.T with type validation_state = 'a)
|
2016-10-20 20:54:16 +04:00
|
|
|
|
2017-11-19 18:15:03 +04:00
|
|
|
let start_prevalidation
|
|
|
|
?proto_header
|
|
|
|
?max_number_of_operations
|
|
|
|
~predecessor ~timestamp () =
|
2017-04-19 23:46:10 +04:00
|
|
|
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_number_of_operations =
|
|
|
|
match max_number_of_operations with
|
|
|
|
| Some max -> max
|
|
|
|
| None ->
|
|
|
|
try List.hd (State.Block.max_number_of_operations predecessor)
|
|
|
|
with _ -> 0 in
|
|
|
|
let max_operation_data_length =
|
|
|
|
State.Block.max_operation_data_length predecessor in
|
2017-04-19 23:46:10 +04:00
|
|
|
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
|
2017-10-09 12:55:12 +04:00
|
|
|
match State.Registred_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) ->
|
2017-04-10 23:14:17 +04:00
|
|
|
Context.reset_test_network
|
|
|
|
predecessor_context predecessor
|
|
|
|
timestamp >>= fun predecessor_context ->
|
2016-10-20 20:54:16 +04:00
|
|
|
Proto.begin_construction
|
|
|
|
~predecessor_context
|
|
|
|
~predecessor_timestamp
|
2017-04-10 14:14:11 +04:00
|
|
|
~predecessor_fitness
|
2017-04-10 15:01:22 +04:00
|
|
|
~predecessor_level
|
2016-10-20 20:54:16 +04:00
|
|
|
~predecessor
|
2017-04-10 14:14:11 +04:00
|
|
|
~timestamp
|
2017-04-27 03:01:05 +04:00
|
|
|
?proto_header
|
2017-04-26 17:01:39 +04:00
|
|
|
()
|
2017-04-10 14:14:11 +04:00
|
|
|
>>=? fun state ->
|
2017-11-19 18:15:03 +04:00
|
|
|
return (State { proto = (module Proto) ; state ;
|
|
|
|
max_number_of_operations ; max_operation_data_length })
|
2016-10-20 20:54:16 +04:00
|
|
|
|
2017-04-27 03:01:05 +04:00
|
|
|
type error += Parse_error
|
2017-11-19 18:15:03 +04:00
|
|
|
type error += Too_many_operations
|
|
|
|
type error += Oversized_operation of { size: int ; max: int }
|
|
|
|
|
|
|
|
let () =
|
|
|
|
register_error_kind `Temporary
|
|
|
|
~id:"prevalidation.too_many_operations"
|
|
|
|
~title:"Too many pending operations in prevalidation"
|
|
|
|
~description:"The prevalidation context is full."
|
|
|
|
~pp:(fun ppf () ->
|
|
|
|
Format.fprintf ppf "Too many operation in prevalidation context.")
|
|
|
|
Data_encoding.empty
|
|
|
|
(function Too_many_operations -> Some () | _ -> None)
|
|
|
|
(fun () -> Too_many_operations) ;
|
|
|
|
register_error_kind `Permanent
|
|
|
|
~id:"prevalidation.oversized_operation"
|
|
|
|
~title:"Oversized operation"
|
|
|
|
~description:"The operation size is bigger than allowed."
|
|
|
|
~pp:(fun ppf (size, max) ->
|
|
|
|
Format.fprintf ppf "Oversized operation (size: %d, max: %d)"
|
|
|
|
size max)
|
|
|
|
Data_encoding.(obj2
|
|
|
|
(req "size" int31)
|
|
|
|
(req "max_size" int31))
|
|
|
|
(function Oversized_operation { size ; max } -> Some (size, max) | _ -> None)
|
|
|
|
(fun (size, max) -> Oversized_operation { size ; max })
|
2017-04-27 03:01:05 +04:00
|
|
|
|
2016-10-20 20:54:16 +04:00
|
|
|
let prevalidate
|
2017-11-19 18:15:03 +04:00
|
|
|
(State { proto = (module Proto) ; state ;
|
|
|
|
max_number_of_operations ; max_operation_data_length })
|
2017-11-14 02:27:19 +04:00
|
|
|
~sort (ops : (Operation_hash.t * Operation.t) list)=
|
2016-10-20 20:54:16 +04:00
|
|
|
let ops =
|
2017-04-27 03:01:05 +04:00
|
|
|
List.map
|
|
|
|
(fun (h, op) ->
|
2017-11-14 02:27:19 +04:00
|
|
|
(h, op, Proto.parse_operation h op |> record_trace Parse_error))
|
2017-04-27 03:01:05 +04:00
|
|
|
ops in
|
|
|
|
let invalid_ops =
|
2017-11-27 09:13:12 +04:00
|
|
|
List.filter_map
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun (h, op, parsed_op) -> match parsed_op with
|
2017-04-27 03:01:05 +04:00
|
|
|
| Ok _ -> None
|
2017-11-14 02:27:19 +04:00
|
|
|
| Error err -> Some (h, op, err)) ops
|
2017-04-27 03:01:05 +04:00
|
|
|
and parsed_ops =
|
2017-11-27 09:13:12 +04:00
|
|
|
List.filter_map
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun (h, op, parsed_op) -> match parsed_op with
|
|
|
|
| Ok parsed_op -> Some (h, op, parsed_op)
|
2017-04-27 03:01:05 +04:00
|
|
|
| Error _ -> None) ops in
|
|
|
|
let sorted_ops =
|
2016-10-20 20:54:16 +04:00
|
|
|
if sort then
|
2017-11-14 02:27:19 +04:00
|
|
|
let compare (_, _, op1) (_, _, op2) = Proto.compare_operations op1 op2 in
|
2017-04-27 03:01:05 +04:00
|
|
|
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
|
2017-04-27 03:01:05 +04:00
|
|
|
apply_operations
|
2017-11-19 18:15:03 +04:00
|
|
|
apply_operation
|
|
|
|
state empty_result max_number_of_operations
|
|
|
|
~sort sorted_ops >>= fun (state, max_number_of_operations, r) ->
|
2017-04-27 03:01:05 +04:00
|
|
|
let r =
|
|
|
|
{ r with
|
|
|
|
applied = List.rev r.applied ;
|
|
|
|
branch_refused =
|
|
|
|
List.fold_left
|
2017-11-14 02:27:19 +04:00
|
|
|
(fun map (h, op, err) -> Operation_hash.Map.add h (op, err) map)
|
2017-04-27 03:01:05 +04:00
|
|
|
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)
|
2016-10-20 20:54:16 +04:00
|
|
|
|
|
|
|
let end_prevalidation (State { proto = (module Proto) ; state }) =
|
|
|
|
Proto.finalize_block state
|