2016-09-08 21:13:10 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Copyright (c) 2014 - 2016. *)
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
2016-10-20 20:54:16 +04:00
|
|
|
open Logging.Node.Prevalidator
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
let list_pendings net_db ~from_block ~to_block old_mempool =
|
|
|
|
let rec pop_blocks ancestor hash mempool =
|
|
|
|
if Block_hash.equal hash ancestor then
|
|
|
|
Lwt.return mempool
|
|
|
|
else
|
|
|
|
Distributed_db.Block_header.read_exn net_db hash >>= fun { shell } ->
|
2017-03-30 15:16:21 +04:00
|
|
|
Distributed_db.Operation_list.read_all_exn
|
|
|
|
net_db hash >>= fun operations ->
|
2017-02-24 20:17:53 +04:00
|
|
|
let mempool =
|
|
|
|
List.fold_left
|
2017-03-30 15:16:21 +04:00
|
|
|
(List.fold_left (fun mempool h -> Operation_hash.Set.add h mempool))
|
|
|
|
mempool operations in
|
2017-02-24 20:17:53 +04:00
|
|
|
pop_blocks ancestor shell.predecessor mempool
|
|
|
|
in
|
2017-03-30 15:16:21 +04:00
|
|
|
let push_block mempool (hash, _shell) =
|
|
|
|
Distributed_db.Operation_list.read_all_exn
|
|
|
|
net_db hash >|= fun operations ->
|
2017-02-24 20:17:53 +04:00
|
|
|
List.fold_left
|
2017-03-30 15:16:21 +04:00
|
|
|
(List.fold_left (fun mempool h -> Operation_hash.Set.remove h mempool))
|
|
|
|
mempool operations
|
2017-02-24 20:17:53 +04:00
|
|
|
in
|
|
|
|
let net_state = Distributed_db.state net_db in
|
|
|
|
State.Valid_block.Current.new_blocks
|
|
|
|
net_state ~from_block ~to_block >>= fun (ancestor, path) ->
|
|
|
|
pop_blocks ancestor from_block.hash old_mempool >>= fun mempool ->
|
2017-03-30 15:16:21 +04:00
|
|
|
Lwt_list.fold_left_s push_block mempool path >>= fun new_mempool ->
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.return new_mempool
|
|
|
|
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
(** Worker *)
|
|
|
|
|
|
|
|
exception Invalid_operation of Operation_hash.t
|
|
|
|
|
2016-10-20 20:54:16 +04:00
|
|
|
open Prevalidation
|
|
|
|
|
2016-09-08 21:13:10 +04:00
|
|
|
type t = {
|
2017-02-24 20:17:53 +04:00
|
|
|
net_db: Distributed_db.net ;
|
|
|
|
flush: State.Valid_block.t -> unit;
|
2017-03-04 12:53:44 +04:00
|
|
|
notify_operations: P2p.Peer_id.t -> Operation_hash.t list -> unit ;
|
2016-09-08 21:13:10 +04:00
|
|
|
prevalidate_operations:
|
2017-04-19 21:21:23 +04:00
|
|
|
bool -> Operation.t list ->
|
2016-10-20 20:54:16 +04:00
|
|
|
(Operation_hash.t list * error preapply_result) tzresult Lwt.t ;
|
|
|
|
operations: unit -> error preapply_result * Operation_hash.Set.t ;
|
2017-02-24 20:17:53 +04:00
|
|
|
pending: ?block:State.Valid_block.t -> unit -> Operation_hash.Set.t Lwt.t ;
|
2016-09-08 21:13:10 +04:00
|
|
|
timestamp: unit -> Time.t ;
|
2017-04-10 14:14:11 +04:00
|
|
|
context: unit -> Updater.validation_result tzresult Lwt.t ;
|
2016-09-08 21:13:10 +04:00
|
|
|
shutdown: unit -> unit Lwt.t ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let merge _key a b =
|
|
|
|
match a, b with
|
|
|
|
| None, None -> None
|
|
|
|
| Some x, None -> Some x
|
|
|
|
| _, Some y -> Some y
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
let create net_db =
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
let net_state = Distributed_db.state net_db in
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
let cancelation, cancel, _on_cancel = Lwt_utils.canceler () in
|
|
|
|
let push_to_worker, worker_waiter = Lwt_utils.queue () in
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
State.Valid_block.Current.head net_state >>= fun head ->
|
|
|
|
State.Operation.list_pending net_state >>= fun initial_mempool ->
|
2016-09-08 21:13:10 +04:00
|
|
|
let timestamp = ref (Time.now ()) in
|
2016-10-20 20:54:16 +04:00
|
|
|
(start_prevalidation head !timestamp >|= ref) >>= fun validation_state ->
|
2017-03-04 12:53:44 +04:00
|
|
|
let pending = Operation_hash.Table.create 53 in
|
2017-02-24 20:17:53 +04:00
|
|
|
let head = ref head in
|
2016-10-20 20:54:16 +04:00
|
|
|
let operations = ref empty_result in
|
2016-09-08 21:13:10 +04:00
|
|
|
let running_validation = ref Lwt.return_unit in
|
2017-02-24 20:17:53 +04:00
|
|
|
let unprocessed = ref initial_mempool in
|
2016-09-08 21:13:10 +04:00
|
|
|
let broadcast_unprocessed = ref false in
|
|
|
|
|
2016-10-20 20:54:16 +04:00
|
|
|
let set_validation_state state =
|
|
|
|
validation_state := state;
|
|
|
|
Lwt.return_unit in
|
|
|
|
|
|
|
|
let reset_validation_state head timestamp =
|
|
|
|
start_prevalidation head timestamp >>= fun state ->
|
|
|
|
validation_state := state;
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return_unit in
|
|
|
|
|
|
|
|
let broadcast_operation ops =
|
2017-02-24 20:17:53 +04:00
|
|
|
Distributed_db.broadcast_head net_db !head.hash ops in
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
let handle_unprocessed () =
|
2017-02-24 20:17:53 +04:00
|
|
|
if Operation_hash.Set.is_empty !unprocessed then
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return ()
|
|
|
|
else
|
|
|
|
(* We assume that `!unprocessed` does not contain any operations
|
|
|
|
from `!operations`. *)
|
|
|
|
let ops = !unprocessed in
|
|
|
|
let broadcast = !broadcast_unprocessed in
|
2017-02-24 20:17:53 +04:00
|
|
|
unprocessed := Operation_hash.Set.empty ;
|
2016-09-08 21:13:10 +04:00
|
|
|
broadcast_unprocessed := false ;
|
|
|
|
running_validation := begin
|
|
|
|
begin
|
2016-10-20 20:54:16 +04:00
|
|
|
Lwt_list.map_p
|
|
|
|
(fun h ->
|
|
|
|
Distributed_db.Operation.read net_db h >>= function
|
|
|
|
| None -> Lwt.return_none
|
|
|
|
| Some po -> Lwt.return_some (h, po))
|
|
|
|
(Operation_hash.Set.elements ops) >>= fun rops ->
|
|
|
|
let rops = Utils.unopt_list rops in
|
|
|
|
(Lwt.return !validation_state >>=? fun validation_state ->
|
|
|
|
prevalidate validation_state ~sort:true rops) >>= function
|
|
|
|
| Ok (state, r) -> Lwt.return (Ok state, r)
|
2016-09-08 21:13:10 +04:00
|
|
|
| Error err ->
|
|
|
|
let r =
|
2016-10-20 20:54:16 +04:00
|
|
|
{ empty_result with
|
2016-09-08 21:13:10 +04:00
|
|
|
branch_delayed =
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Set.fold
|
|
|
|
(fun op m -> Operation_hash.Map.add op err m)
|
|
|
|
ops Operation_hash.Map.empty ; } in
|
2016-10-20 20:54:16 +04:00
|
|
|
Lwt.return (!validation_state, r)
|
|
|
|
end >>= fun (state, r) ->
|
2016-09-08 21:13:10 +04:00
|
|
|
let filter_out s m =
|
2017-02-24 20:17:53 +04:00
|
|
|
List.fold_right Operation_hash.Map.remove s m in
|
2016-09-08 21:13:10 +04:00
|
|
|
operations := {
|
2016-10-20 20:54:16 +04:00
|
|
|
applied = List.rev_append r.applied !operations.applied ;
|
2017-02-24 20:17:53 +04:00
|
|
|
refused = Operation_hash.Map.empty ;
|
2016-09-08 21:13:10 +04:00
|
|
|
branch_refused =
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Map.merge merge
|
2016-09-08 21:13:10 +04:00
|
|
|
(* filter_out should not be required here, TODO warn ? *)
|
|
|
|
(filter_out r.applied !operations.branch_refused)
|
|
|
|
r.branch_refused ;
|
|
|
|
branch_delayed =
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Map.merge merge
|
2016-09-08 21:13:10 +04:00
|
|
|
(filter_out r.applied !operations.branch_delayed)
|
|
|
|
r.branch_delayed ;
|
|
|
|
} ;
|
2016-10-20 20:54:16 +04:00
|
|
|
if broadcast then broadcast_operation r.applied ;
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt_list.iter_s
|
2017-02-24 20:17:53 +04:00
|
|
|
(fun (_op, _exns) ->
|
|
|
|
(* FIXME *)
|
|
|
|
(* Distributed_db.Operation.mark_invalid net_db op exns >>= fun _ -> *)
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return_unit)
|
2016-10-20 20:54:16 +04:00
|
|
|
(Operation_hash.Map.bindings r.refused) >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
(* TODO. Keep a bounded set of 'refused' operations. *)
|
|
|
|
(* TODO. Log the error in some statistics associated to
|
|
|
|
the peers that informed us of the operations. And
|
|
|
|
eventually blacklist bad peers. *)
|
|
|
|
(* TODO. Keep a bounded set of 'branch_refused' operations
|
|
|
|
into the 'state'. It should be associated to the
|
|
|
|
current block, and updated on 'set_current_head'. *)
|
2016-10-20 20:54:16 +04:00
|
|
|
set_validation_state state
|
2016-09-08 21:13:10 +04:00
|
|
|
end;
|
|
|
|
Lwt.catch
|
|
|
|
(fun () -> !running_validation)
|
|
|
|
(fun _ -> lwt_debug "<- prevalidate (cancel)")
|
|
|
|
in
|
|
|
|
|
|
|
|
let prevalidation_worker =
|
|
|
|
|
|
|
|
let rec worker_loop () =
|
|
|
|
(* TODO cleanup the mempool from outdated operation (1h like
|
|
|
|
Bitcoin ?). And log the removal in some statistic associated
|
|
|
|
to then peers that informed us of the operation. *)
|
|
|
|
(* TODO lookup in `!pending` for 'outdated' ops and re-add them
|
|
|
|
in `unprocessed` (e.g. if the previous tentative was
|
|
|
|
more 5 seconds ago) *)
|
|
|
|
handle_unprocessed () >>= fun () ->
|
|
|
|
Lwt.pick [(worker_waiter () >|= fun q -> `Process q);
|
|
|
|
(cancelation () >|= fun () -> `Cancel)] >>= function
|
|
|
|
| `Cancel -> Lwt.return_unit
|
|
|
|
| `Process q ->
|
|
|
|
Lwt_list.iter_s
|
|
|
|
(function
|
|
|
|
| `Prevalidate (ops, w, force) -> begin
|
|
|
|
let result =
|
2016-10-20 20:54:16 +04:00
|
|
|
let rops = Operation_hash.Map.bindings ops in
|
|
|
|
Lwt.return !validation_state >>=? fun validation_state ->
|
|
|
|
prevalidate validation_state ~sort:true rops >>=? fun (state, res) ->
|
2016-09-08 21:13:10 +04:00
|
|
|
let register h =
|
2017-02-24 20:17:53 +04:00
|
|
|
let op = Operation_hash.Map.find h ops in
|
|
|
|
Distributed_db.Operation.inject
|
|
|
|
net_db h op >>= fun _ ->
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return_unit in
|
|
|
|
Lwt_list.iter_s
|
|
|
|
(fun h ->
|
|
|
|
register h >>= fun () ->
|
|
|
|
operations :=
|
|
|
|
{ !operations with
|
|
|
|
applied = h :: !operations.applied };
|
|
|
|
Lwt.return_unit )
|
2016-10-20 20:54:16 +04:00
|
|
|
res.applied >>= fun () ->
|
|
|
|
broadcast_operation res.applied ;
|
2016-09-08 21:13:10 +04:00
|
|
|
begin
|
|
|
|
if force then
|
|
|
|
Lwt_list.iter_p
|
|
|
|
(fun (h, _exns) -> register h)
|
2017-02-24 20:17:53 +04:00
|
|
|
(Operation_hash.Map.bindings
|
2016-10-20 20:54:16 +04:00
|
|
|
res.branch_delayed) >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt_list.iter_p
|
|
|
|
(fun (h, _exns) -> register h)
|
2017-02-24 20:17:53 +04:00
|
|
|
(Operation_hash.Map.bindings
|
2016-10-20 20:54:16 +04:00
|
|
|
res.branch_refused) >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
operations :=
|
|
|
|
{ !operations with
|
|
|
|
branch_delayed =
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Map.merge merge
|
2016-09-08 21:13:10 +04:00
|
|
|
!operations.branch_delayed res.branch_delayed ;
|
|
|
|
branch_refused =
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Map.merge merge
|
2016-09-08 21:13:10 +04:00
|
|
|
!operations.branch_refused res.branch_refused ;
|
|
|
|
} ;
|
|
|
|
Lwt.return_unit
|
|
|
|
else
|
|
|
|
Lwt.return_unit
|
|
|
|
end >>= fun () ->
|
2016-10-20 20:54:16 +04:00
|
|
|
set_validation_state (Ok state) >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
return res
|
|
|
|
in
|
|
|
|
result >>= fun result ->
|
|
|
|
Lwt.wakeup w result ;
|
|
|
|
Lwt.return_unit
|
|
|
|
end
|
2017-03-04 12:53:44 +04:00
|
|
|
| `Register (gid, ops) ->
|
|
|
|
Lwt_list.filter_p
|
|
|
|
(fun op ->
|
|
|
|
Distributed_db.Operation.known net_db op >|= not)
|
|
|
|
ops >>= fun new_ops ->
|
|
|
|
let known_ops, unknown_ops =
|
|
|
|
List.partition
|
|
|
|
(fun op -> Operation_hash.Table.mem pending op) new_ops in
|
|
|
|
let fetch op =
|
|
|
|
Distributed_db.Operation.fetch
|
|
|
|
net_db ~peer:gid op >>= fun _op ->
|
|
|
|
push_to_worker (`Handle op) ;
|
|
|
|
Lwt.return_unit
|
|
|
|
in
|
|
|
|
List.iter
|
|
|
|
(fun op -> Operation_hash.Table.add pending op (fetch op))
|
|
|
|
unknown_ops ;
|
|
|
|
List.iter (fun op ->
|
|
|
|
Lwt.ignore_result
|
|
|
|
(Distributed_db.Operation.fetch net_db ~peer:gid op))
|
|
|
|
known_ops ;
|
|
|
|
Lwt.return_unit
|
|
|
|
| `Handle op ->
|
2016-09-08 21:13:10 +04:00
|
|
|
lwt_debug "register %a" Operation_hash.pp_short op >>= fun () ->
|
2017-03-04 12:53:44 +04:00
|
|
|
Operation_hash.Table.remove pending op ;
|
2016-09-08 21:13:10 +04:00
|
|
|
broadcast_unprocessed := true ;
|
2017-02-24 20:17:53 +04:00
|
|
|
unprocessed := Operation_hash.Set.singleton op ;
|
2017-03-04 12:53:44 +04:00
|
|
|
lwt_debug "register %a" Operation_hash.pp_short op >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return_unit
|
2017-02-24 20:17:53 +04:00
|
|
|
| `Flush (new_head : State.Valid_block.t) ->
|
|
|
|
list_pendings
|
|
|
|
net_db ~from_block:!head ~to_block:new_head
|
2016-10-20 20:54:16 +04:00
|
|
|
(preapply_result_operations !operations) >>= fun new_mempool ->
|
2016-09-08 21:13:10 +04:00
|
|
|
lwt_debug "flush %a (mempool: %d)"
|
|
|
|
Block_hash.pp_short new_head.hash
|
2017-02-24 20:17:53 +04:00
|
|
|
(Operation_hash.Set.cardinal new_mempool) >>= fun () ->
|
2016-09-08 21:13:10 +04:00
|
|
|
(* Reset the pre-validation context *)
|
2017-02-24 20:17:53 +04:00
|
|
|
head := new_head ;
|
2016-10-20 20:54:16 +04:00
|
|
|
operations := empty_result ;
|
2016-09-08 21:13:10 +04:00
|
|
|
broadcast_unprocessed := false ;
|
2017-02-24 20:17:53 +04:00
|
|
|
unprocessed := new_mempool ;
|
|
|
|
timestamp := Time.now () ;
|
2016-10-20 20:54:16 +04:00
|
|
|
(* Reset the prevalidation context. *)
|
|
|
|
reset_validation_state new_head !timestamp)
|
2016-09-08 21:13:10 +04:00
|
|
|
q >>= fun () ->
|
|
|
|
worker_loop ()
|
|
|
|
in
|
|
|
|
Lwt_utils.worker "prevalidator" ~run:worker_loop ~cancel in
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
let flush head =
|
|
|
|
push_to_worker (`Flush head) ;
|
2016-09-08 21:13:10 +04:00
|
|
|
if not (Lwt.is_sleeping !running_validation) then
|
|
|
|
Lwt.cancel !running_validation
|
|
|
|
in
|
2017-03-04 12:53:44 +04:00
|
|
|
let notify_operations gid ops =
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.async begin fun () ->
|
2017-03-04 12:53:44 +04:00
|
|
|
push_to_worker (`Register (gid, ops)) ;
|
2017-02-24 20:17:53 +04:00
|
|
|
Lwt.return_unit
|
|
|
|
end in
|
2016-09-08 21:13:10 +04:00
|
|
|
let prevalidate_operations force raw_ops =
|
2017-04-19 21:21:23 +04:00
|
|
|
let ops = List.map Operation.hash raw_ops in
|
2016-09-08 21:13:10 +04:00
|
|
|
let ops_map =
|
|
|
|
List.fold_left
|
|
|
|
(fun map op ->
|
2017-04-19 21:21:23 +04:00
|
|
|
Operation_hash.Map.add (Operation.hash op) op map)
|
2017-02-24 20:17:53 +04:00
|
|
|
Operation_hash.Map.empty raw_ops in
|
2016-09-08 21:13:10 +04:00
|
|
|
let wait, waker = Lwt.wait () in
|
|
|
|
push_to_worker (`Prevalidate (ops_map, waker, force));
|
|
|
|
wait >>=? fun result ->
|
|
|
|
return (ops, result) in
|
|
|
|
let shutdown () =
|
|
|
|
lwt_debug "shutdown" >>= fun () ->
|
|
|
|
if not (Lwt.is_sleeping !running_validation) then
|
|
|
|
Lwt.cancel !running_validation;
|
|
|
|
cancel () >>= fun () ->
|
|
|
|
prevalidation_worker in
|
2017-02-24 20:17:53 +04:00
|
|
|
let pending ?block () =
|
2016-10-20 20:54:16 +04:00
|
|
|
let ops = preapply_result_operations !operations in
|
2017-02-24 20:17:53 +04:00
|
|
|
match block with
|
|
|
|
| None -> Lwt.return ops
|
|
|
|
| Some to_block ->
|
2016-10-20 20:54:16 +04:00
|
|
|
list_pendings net_db ~from_block:!head ~to_block ops in
|
|
|
|
let context () =
|
|
|
|
Lwt.return !validation_state >>=? fun prevalidation_state ->
|
|
|
|
Prevalidation.end_prevalidation prevalidation_state in
|
2016-09-08 21:13:10 +04:00
|
|
|
Lwt.return {
|
2017-02-24 20:17:53 +04:00
|
|
|
net_db ;
|
2016-09-08 21:13:10 +04:00
|
|
|
flush ;
|
2017-03-04 12:53:44 +04:00
|
|
|
notify_operations ;
|
2016-09-08 21:13:10 +04:00
|
|
|
prevalidate_operations ;
|
|
|
|
operations =
|
|
|
|
(fun () ->
|
|
|
|
{ !operations with applied = List.rev !operations.applied },
|
|
|
|
!unprocessed) ;
|
2017-02-24 20:17:53 +04:00
|
|
|
pending ;
|
2016-09-08 21:13:10 +04:00
|
|
|
timestamp = (fun () -> !timestamp) ;
|
2016-10-20 20:54:16 +04:00
|
|
|
context ;
|
2016-09-08 21:13:10 +04:00
|
|
|
shutdown ;
|
|
|
|
}
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
let flush pv head = pv.flush head
|
2017-03-04 12:53:44 +04:00
|
|
|
let notify_operations pv = pv.notify_operations
|
2016-09-08 21:13:10 +04:00
|
|
|
let prevalidate_operations pv = pv.prevalidate_operations
|
|
|
|
let operations pv = pv.operations ()
|
2017-02-24 20:17:53 +04:00
|
|
|
let pending ?block pv = pv.pending ?block ()
|
2016-09-08 21:13:10 +04:00
|
|
|
let timestamp pv = pv.timestamp ()
|
|
|
|
let context pv = pv.context ()
|
|
|
|
let shutdown pv = pv.shutdown ()
|
|
|
|
|
2017-04-19 21:21:23 +04:00
|
|
|
let inject_operation pv ?(force = false) (op: Operation.t) =
|
2017-02-24 20:17:53 +04:00
|
|
|
let net_id = State.Net.id (Distributed_db.state pv.net_db) in
|
2016-09-08 21:13:10 +04:00
|
|
|
let wrap_error h map =
|
|
|
|
begin
|
2017-02-24 20:17:53 +04:00
|
|
|
try return (Operation_hash.Map.find h map)
|
2016-09-08 21:13:10 +04:00
|
|
|
with Not_found ->
|
|
|
|
failwith "unexpected protocol result"
|
|
|
|
end >>=? fun errors ->
|
|
|
|
Lwt.return (Error errors) in
|
2017-03-31 15:04:05 +04:00
|
|
|
fail_unless (Net_id.equal net_id op.shell.net_id)
|
2016-09-08 21:13:10 +04:00
|
|
|
(Unclassified
|
|
|
|
"Prevalidator.inject_operation: invalid network") >>=? fun () ->
|
|
|
|
pv.prevalidate_operations force [op] >>=? function
|
2016-10-20 20:54:16 +04:00
|
|
|
| ([h], { applied = [h'] }) when Operation_hash.equal h h' ->
|
2016-09-08 21:13:10 +04:00
|
|
|
return ()
|
2016-10-20 20:54:16 +04:00
|
|
|
| ([h], { refused })
|
2017-02-24 20:17:53 +04:00
|
|
|
when Operation_hash.Map.cardinal refused = 1 ->
|
2016-09-08 21:13:10 +04:00
|
|
|
wrap_error h refused
|
2016-10-20 20:54:16 +04:00
|
|
|
| ([h], { branch_refused })
|
2017-02-24 20:17:53 +04:00
|
|
|
when Operation_hash.Map.cardinal branch_refused = 1 && not force ->
|
2016-09-08 21:13:10 +04:00
|
|
|
wrap_error h branch_refused
|
2016-10-20 20:54:16 +04:00
|
|
|
| ([h], { branch_delayed })
|
2017-02-24 20:17:53 +04:00
|
|
|
when Operation_hash.Map.cardinal branch_delayed = 1 && not force ->
|
2016-09-08 21:13:10 +04:00
|
|
|
wrap_error h branch_delayed
|
|
|
|
| _ ->
|
|
|
|
if force then
|
|
|
|
return ()
|
|
|
|
else
|
|
|
|
failwith "Unexpected result for prevalidation."
|