diff --git a/src/client/client_node_rpcs.ml b/src/client/client_node_rpcs.ml index dd19d219a..74f8b910c 100644 --- a/src/client/client_node_rpcs.ml +++ b/src/client/client_node_rpcs.ml @@ -15,9 +15,8 @@ module Services = Node_rpc_services let errors cctxt = call_service0 cctxt Services.Error.service () -let forge_block cctxt ?net_id ?level ?proto_level ?predecessor ?timestamp fitness ops header = - call_service0 cctxt Services.forge_block - (net_id, level, proto_level, predecessor, timestamp, fitness, ops, header) +let forge_block_header cctxt header = + call_service0 cctxt Services.forge_block_header header let validate_block cctxt net block = call_err_service0 cctxt Services.validate_block (net, block) @@ -72,14 +71,14 @@ module Blocks = struct test_network: Context.test_network; } type preapply_param = Services.Blocks.preapply_param = { + timestamp: Time.t ; + proto_header: MBytes.t ; operations: operation list ; - sort: bool ; - timestamp: Time.t option ; + sort_operations: bool ; } type preapply_result = Services.Blocks.preapply_result = { + shell_header: Block_header.shell_header ; operations: error Prevalidation.preapply_result ; - fitness: MBytes.t list ; - timestamp: Time.t ; } let net_id cctxt h = call_service1 cctxt Services.Blocks.net_id h () @@ -103,10 +102,11 @@ module Blocks = struct let test_network cctxt h = call_service1 cctxt Services.Blocks.test_network h () - let preapply cctxt h ?timestamp ?(sort = false) operations = + let preapply cctxt h + ?(timestamp = Time.now ()) ?(sort = false) ~proto_header operations = call_err_service1 cctxt Services.Blocks.preapply h - { operations ; sort ; timestamp } + { timestamp ; proto_header ; sort_operations = sort ; operations } let pending_operations cctxt block = call_service1 cctxt Services.Blocks.pending_operations block () let info cctxt ?(include_ops = true) h = diff --git a/src/client/client_node_rpcs.mli b/src/client/client_node_rpcs.mli index 518bfa5e5..0485d9059 100644 --- a/src/client/client_node_rpcs.mli +++ b/src/client/client_node_rpcs.mli @@ -12,22 +12,10 @@ open Client_rpcs val errors: config -> Json_schema.schema tzresult Lwt.t -val forge_block: +val forge_block_header: config -> - ?net_id:Net_id.t -> - ?level:Int32.t -> - ?proto_level:int -> - ?predecessor:Block_hash.t -> - ?timestamp:Time.t -> - Fitness.t -> - Operation_list_list_hash.t -> - MBytes.t -> + Block_header.t -> MBytes.t tzresult Lwt.t -(** [forge_block cctxt ?net ?predecessor ?timestamp fitness ops - proto_hdr] returns the serialization of a block header with - [proto_hdr] as protocol-specific part. The arguments [?net] and - [?predecessor] are infered from the current head of main network, - and [?timestamp] defaults to [Time.now ()]. *) val validate_block: config -> @@ -141,9 +129,8 @@ module Blocks : sig unit -> block_info list list tzresult Lwt_stream.t tzresult Lwt.t type preapply_result = { + shell_header: Block_header.shell_header ; operations: error Prevalidation.preapply_result ; - fitness: MBytes.t list ; - timestamp: Time.t ; } val preapply: @@ -151,6 +138,7 @@ module Blocks : sig block -> ?timestamp:Time.t -> ?sort:bool -> + proto_header:MBytes.t -> operation list -> preapply_result tzresult Lwt.t end diff --git a/src/client/embedded/alpha/.merlin b/src/client/embedded/alpha/.merlin index 8c8ccb86b..5d11075e5 100644 --- a/src/client/embedded/alpha/.merlin +++ b/src/client/embedded/alpha/.merlin @@ -9,4 +9,5 @@ S ../../../proto/alpha B _tzbuild FLG -open Client_embedded_proto_alpha FLG -open Register_client_embedded_proto_alpha +FLG -open Environment FLG -open Tezos_context diff --git a/src/client/embedded/alpha/Makefile b/src/client/embedded/alpha/Makefile index 6e79a28f0..d9e0be83b 100644 --- a/src/client/embedded/alpha/Makefile +++ b/src/client/embedded/alpha/Makefile @@ -51,8 +51,8 @@ NODEPENDS := webclient/webclient_proto_static.ml.deps include ../Makefile.shared -${WEBOBJS}: OPENED_MODULES += Tezos_context -${OBJS}: OPENED_MODULES += Tezos_context +${WEBOBJS}: OPENED_MODULES += Environment Tezos_context +${OBJS}: OPENED_MODULES += Environment Tezos_context predepend: concrete_parser.ml concrete_lexer.ml diff --git a/src/client/embedded/alpha/baker/client_mining_daemon.ml b/src/client/embedded/alpha/baker/client_mining_daemon.ml index 84646d527..6a62bfb5e 100644 --- a/src/client/embedded/alpha/baker/client_mining_daemon.ml +++ b/src/client/embedded/alpha/baker/client_mining_daemon.ml @@ -34,11 +34,8 @@ let run cctxt ?max_priority ~delay ?min_date delegates = if Client_proto_args.Daemon.(!all || !mining) then begin Client_mining_blocks.monitor cctxt.rpc_config ?min_date ~min_heads:1 () >>=? fun block_stream -> - (* Temporary desactivate the monitoring of endorsement: - too slow for now. *) - (* Client_mining_operations.monitor_endorsement *) - (* cctxt >>= fun endorsement_stream -> *) - let endorsement_stream, _push = Lwt_stream.create () in + Client_mining_operations.monitor_endorsement + cctxt.rpc_config >>=? fun endorsement_stream -> Client_mining_forge.create cctxt ?max_priority delegates block_stream endorsement_stream >>=? fun () -> return () diff --git a/src/client/embedded/alpha/baker/client_mining_forge.ml b/src/client/embedded/alpha/baker/client_mining_forge.ml index e41ff59c9..7d0cce867 100644 --- a/src/client/embedded/alpha/baker/client_mining_forge.ml +++ b/src/client/embedded/alpha/baker/client_mining_forge.ml @@ -10,7 +10,6 @@ open Client_commands open Logging.Client.Mining -module Ed25519 = Environment.Ed25519 let generate_proof_of_work_nonce () = Sodium.Random.Bigbytes.generate Constants.proof_of_work_nonce_size @@ -21,7 +20,7 @@ let generate_seed_nonce () = | Error _ -> assert false | Ok nonce -> nonce -let rec compute_stamp +let rec forge_block_header cctxt block delegate_sk shell priority seed_nonce_hash = Client_proto_rpcs.Constants.stamp_threshold cctxt block >>=? fun stamp_threshold -> @@ -34,54 +33,79 @@ let rec compute_stamp Ed25519.Signature.append delegate_sk unsigned_header in let block_hash = Block_hash.hash_bytes [signed_header] in if Mining.check_hash block_hash stamp_threshold then - proof_of_work_nonce + signed_header else loop () in return (loop ()) -let inject_block cctxt block - ?force - ~priority ~timestamp ~fitness ~seed_nonce - ~src_sk operations = - let block = Client_rpcs.last_mined_block block in - Client_node_rpcs.Blocks.info cctxt block >>=? fun bi -> - let seed_nonce_hash = Nonce.hash seed_nonce in - Client_proto_rpcs.Context.next_level cctxt block >>=? fun level -> +let empty_proof_of_work_nonce = + MBytes.of_string + (String.make Constants_repr.proof_of_work_nonce_size '\000') + +let forge_faked_proto_header ~priority ~seed_nonce_hash = + Tezos_context.Block_header.forge_unsigned_proto_header + { priority ; seed_nonce_hash ; + proof_of_work_nonce = empty_proof_of_work_nonce } + +let assert_valid_operations_hash shell_header operations = let operations_hash = Operation_list_list_hash.compute - (List.map Operation_list_hash.compute (List.map (List.map (function Client_node_rpcs.Blob op -> Tezos_data.Operation.hash op | Hash oph -> oph)) operations)) in - let shell = - { Tezos_data.Block_header.net_id = bi.net_id ; level = bi.level ; - proto_level = bi.proto_level ; - predecessor = bi.hash ; timestamp ; fitness ; operations_hash } in - compute_stamp cctxt block - src_sk shell priority seed_nonce_hash >>=? fun proof_of_work_nonce -> - Client_proto_rpcs.Helpers.Forge.block cctxt - block - ~net_id:bi.net_id - ~predecessor:bi.hash - ~timestamp - ~fitness - ~operations_hash - ~level:level.level - ~proto_level:bi.proto_level - ~priority:priority - ~seed_nonce_hash - ~proof_of_work_nonce - () >>=? fun unsigned_header -> - let signed_header = Ed25519.Signature.append src_sk unsigned_header in + (List.map Operation_list_hash.compute + (List.map + (List.map + (function + | Client_node_rpcs.Blob op -> Tezos_data.Operation.hash op + | Hash oph -> oph)) operations)) in + fail_unless + (Operation_list_list_hash.equal + operations_hash shell_header.Tezos_data.Block_header.operations_hash) + (failure + "Client_mining_forge.inject_block: \ + inconsistent header.") + +let inject_block cctxt + ?force ~shell_header ~priority ~seed_nonce_hash ~src_sk operations = + assert_valid_operations_hash shell_header operations >>=? fun () -> + let block = `Hash shell_header.Tezos_data.Block_header.predecessor in + forge_block_header cctxt block + src_sk shell_header priority seed_nonce_hash >>=? fun signed_header -> Client_node_rpcs.inject_block cctxt ?force signed_header operations >>=? fun block_hash -> return block_hash +type error += + | Failed_to_preapply of Client_node_rpcs.operation * error list + +let () = + register_error_kind + `Permanent + ~id:"Client_mining_forge.failed_to_preapply" + ~title: "Fail to preapply an operation" + ~description: "" + ~pp:(fun ppf (op, err) -> + let h = + match op with + | Client_node_rpcs.Hash h -> h + | Blob op -> Tezos_data.Operation.hash op in + Format.fprintf ppf "@[Failed to preapply %a:@ %a@]" + Operation_hash.pp_short h + pp_print_error err) + Data_encoding. + (obj2 + (req "operation" (dynamic_size Client_node_rpcs.operation_encoding)) + (req "error" Node_rpc_services.Error.encoding)) + (function + | Failed_to_preapply (hash, err) -> Some (hash, err) + | _ -> None) + (fun (hash, err) -> Failed_to_preapply (hash, err)) + let forge_block cctxt block ?force ?operations ?(best_effort = operations = None) ?(sort = best_effort) ?timestamp ~priority - ~seed_nonce ~src_sk () = + ~seed_nonce_hash ~src_sk () = let block = Client_rpcs.last_mined_block block in - Client_proto_rpcs.Context.next_level cctxt block >>=? fun { level } -> begin match operations with | None -> @@ -103,6 +127,7 @@ let forge_block cctxt block return (prio, time) end | `Auto (src_pkh, max_priority, free_mining) -> + Client_proto_rpcs.Context.next_level cctxt block >>=? fun { level } -> Client_proto_rpcs.Helpers.Rights.mining_rights_for_delegate cctxt ?max_priority ~first_level:level @@ -119,16 +144,16 @@ let forge_block cctxt block List.find (fun (l,p,_) -> l = level && p >= min_prio) possibilities in return (prio, time) with Not_found -> - Error_monad.failwith "No slot found at level %a" Raw_level.pp level + failwith "No slot found at level %a" Raw_level.pp level end >>=? fun (priority, minimal_timestamp) -> - lwt_log_info "Mining block at level %a prio %d" - Raw_level.pp level priority >>= fun () -> + (* lwt_log_info "Mining block at level %a prio %d" *) + (* Raw_level.pp level priority >>= fun () -> *) begin match timestamp, minimal_timestamp with | None, timestamp -> return timestamp | Some timestamp, minimal_timestamp -> if timestamp < minimal_timestamp then - Error_monad.failwith + failwith "Proposed timestamp %a is earlier than minimal timestamp %a" Time.pp_hum timestamp Time.pp_hum minimal_timestamp @@ -136,23 +161,57 @@ let forge_block cctxt block return timestamp end >>=? fun timestamp -> let request = List.length operations in + let proto_header = forge_faked_proto_header ~priority ~seed_nonce_hash in Client_node_rpcs.Blocks.preapply - cctxt block ~timestamp ~sort operations >>=? - fun { operations ; fitness ; timestamp } -> - let valid = List.length operations.applied in + cctxt block ~timestamp ~sort ~proto_header operations >>=? + fun { operations = result ; shell_header } -> + let valid = List.length result.applied in lwt_log_info "Found %d valid operations (%d refused) for timestamp %a" valid (request - valid) Time.pp_hum timestamp >>= fun () -> - lwt_log_info "Computed fitness %a" Fitness.pp fitness >>= fun () -> + lwt_log_info "Computed fitness %a" + Fitness.pp shell_header.fitness >>= fun () -> if best_effort - || ( Operation_hash.Map.is_empty operations.refused - && Operation_hash.Map.is_empty operations.branch_refused - && Operation_hash.Map.is_empty operations.branch_delayed ) then - inject_block cctxt ?force ~src_sk - ~priority ~timestamp ~fitness ~seed_nonce block - [List.map (fun h -> Client_node_rpcs.Hash h) operations.applied] + || ( Operation_hash.Map.is_empty result.refused + && Operation_hash.Map.is_empty result.branch_refused + && Operation_hash.Map.is_empty result.branch_delayed ) then + let operations = + if not best_effort then operations + else + let map = + List.fold_left + (fun map op -> + match op with + | Client_node_rpcs.Hash _ -> map + | Blob op -> + Operation_hash.Map.add (Tezos_data.Operation.hash op) op map) + Operation_hash.Map.empty operations in + List.map + (fun h -> + try Client_node_rpcs.Blob (Operation_hash.Map.find h map) + with _ -> Client_node_rpcs.Hash h) + result.applied in + inject_block cctxt + ?force ~shell_header ~priority ~seed_nonce_hash ~src_sk + [operations] else - failwith "Cannot (fully) validate the given operations." + Lwt.return_error @@ + Utils.filter_map + (fun op -> + let h = + match op with + | Client_node_rpcs.Hash h -> h + | Blob op -> Tezos_data.Operation.hash op in + try Some (Failed_to_preapply + (op, Operation_hash.Map.find h result.refused)) + with Not_found -> + try Some (Failed_to_preapply + (op, Operation_hash.Map.find h result.branch_refused)) + with Not_found -> + try Some (Failed_to_preapply + (op, Operation_hash.Map.find h result.branch_delayed)) + with Not_found -> None) + operations (** Worker *) @@ -350,7 +409,6 @@ let safe_get_unrevealed_nonces cctxt block = lwt_warn "Cannot read nonces: %a@." pp_print_error err >>= fun () -> Lwt.return [] - let get_delegates cctxt state = match state.delegates with | [] -> @@ -406,8 +464,10 @@ let insert_blocks cctxt ?max_priority state bis = let mine cctxt state = let slots = pop_mining_slots state in - map_p - (fun (timestamp, (bi, prio, delegate)) -> + let seed_nonce = generate_seed_nonce () in + let seed_nonce_hash = Nonce.hash seed_nonce in + Error_monad.map_filter_s + (fun (timestamp, (bi, priority, delegate)) -> let block = `Hash bi.Client_mining_blocks.hash in let timestamp = if Block_hash.equal bi.Client_mining_blocks.hash state.genesis then @@ -417,7 +477,7 @@ let mine cctxt state = Client_keys.Public_key_hash.name cctxt delegate >>=? fun name -> lwt_debug "Try mining after %a (slot %d) for %s (%a)" Block_hash.pp_short bi.hash - prio name Time.pp_hum timestamp >>= fun () -> + priority name Time.pp_hum timestamp >>= fun () -> Client_node_rpcs.Blocks.pending_operations cctxt.rpc_config block >>=? fun (res, ops) -> let operations = @@ -425,40 +485,49 @@ let mine cctxt state = List.map (fun x -> Client_node_rpcs.Hash x) @@ elements (union ops (Prevalidation.preapply_result_operations res)) in let request = List.length operations in + let proto_header = + forge_faked_proto_header ~priority ~seed_nonce_hash in Client_node_rpcs.Blocks.preapply cctxt.rpc_config block - ~timestamp ~sort:true operations >>= function + ~timestamp ~sort:true ~proto_header operations >>= function | Error errs -> lwt_log_error "Error while prevalidating operations:\n%a" pp_print_error errs >>= fun () -> return None - | Ok { operations ; fitness ; timestamp } -> + | Ok { operations ; shell_header } -> lwt_debug "Computed condidate block after %a (slot %d): %d/%d fitness: %a" - Block_hash.pp_short bi.hash prio + Block_hash.pp_short bi.hash priority (List.length operations.applied) request - Fitness.pp fitness + Fitness.pp shell_header.fitness >>= fun () -> return - (Some (bi, prio, fitness, timestamp, operations, delegate))) + (Some (bi, priority, shell_header, operations, delegate))) slots >>=? fun candidates -> let candidates = List.sort - (fun (_,_,f1,_,_,_) (_,_,f2,_,_,_) -> ~- (Fitness.compare f1 f2)) - (Utils.unopt_list candidates) in + (fun (_,_,h1,_,_) (_,_,h2,_,_) -> + match + Fitness.compare h1.Tezos_data.Block_header.fitness h2.fitness + with + | 0 -> + Time.compare h1.timestamp h2.timestamp + | cmp -> ~- cmp) + candidates in match candidates with - | (bi, priority, fitness, timestamp, operations, delegate) :: _ - when Fitness.compare state.best.fitness fitness < 0 -> begin + | (bi, priority, shell_header, operations, delegate) :: _ + when Fitness.compare state.best.fitness shell_header.fitness < 0 || + (Fitness.compare state.best.fitness shell_header.fitness = 0 && + Time.compare shell_header.timestamp state.best.timestamp < 0) -> begin let level = Raw_level.succ bi.level.level in cctxt.message "Select candidate block after %a (slot %d) fitness: %a" Block_hash.pp_short bi.hash priority - Fitness.pp fitness >>= fun () -> - let seed_nonce = generate_seed_nonce () in + Fitness.pp shell_header.fitness >>= fun () -> Client_keys.get_key cctxt delegate >>=? fun (_,_,src_sk) -> inject_block cctxt.rpc_config - ~force:true ~src_sk ~priority ~timestamp ~fitness ~seed_nonce - (`Hash bi.hash) [List.map (fun h -> Client_node_rpcs.Hash h) operations.applied] + ~force:true ~shell_header ~priority ~seed_nonce_hash ~src_sk + [List.map (fun h -> Client_node_rpcs.Hash h) operations.applied] |> trace_exn (Failure "Error while injecting block") >>=? fun block_hash -> State.record_block cctxt level block_hash seed_nonce |> trace_exn (Failure "Error while recording block") >>=? fun () -> @@ -470,7 +539,7 @@ let mine cctxt state = name Block_hash.pp_short bi.hash Raw_level.pp level priority - Fitness.pp fitness + Fitness.pp shell_header.fitness (List.length operations.applied) >>= fun () -> return () end diff --git a/src/client/embedded/alpha/baker/client_mining_forge.mli b/src/client/embedded/alpha/baker/client_mining_forge.mli index 58624c6a4..8fc41a0de 100644 --- a/src/client/embedded/alpha/baker/client_mining_forge.mli +++ b/src/client/embedded/alpha/baker/client_mining_forge.mli @@ -15,12 +15,10 @@ val generate_seed_nonce: unit -> Nonce.t val inject_block: Client_rpcs.config -> - Client_proto_rpcs.block -> ?force:bool -> + shell_header:Block_header.shell_header -> priority:int -> - timestamp:Time.t -> - fitness:Fitness.t -> - seed_nonce:Nonce.t -> + seed_nonce_hash:Nonce_hash.t -> src_sk:secret_key -> Client_node_rpcs.operation list list -> Block_hash.t tzresult Lwt.t @@ -30,6 +28,9 @@ val inject_block: will be used to compute the mining slot (level is precomputed). [src_sk] is used to sign the block header. *) +type error += + | Failed_to_preapply of Client_node_rpcs.operation * error list + val forge_block: Client_rpcs.config -> Client_proto_rpcs.block -> @@ -39,7 +40,7 @@ val forge_block: ?sort:bool -> ?timestamp:Time.t -> priority:[`Set of int | `Auto of (public_key_hash * int option * bool)] -> - seed_nonce:Nonce.t -> + seed_nonce_hash:Nonce_hash.t -> src_sk:secret_key -> unit -> Block_hash.t tzresult Lwt.t diff --git a/src/client/embedded/alpha/baker/client_mining_main.ml b/src/client/embedded/alpha/baker/client_mining_main.ml index 19ac3e2e1..6d66850f1 100644 --- a/src/client/embedded/alpha/baker/client_mining_main.ml +++ b/src/client/embedded/alpha/baker/client_mining_main.ml @@ -23,10 +23,11 @@ let mine_block cctxt block Client_proto_rpcs.Context.level cctxt.rpc_config block >>=? fun level -> let level = Raw_level.succ level.level in let seed_nonce = Client_mining_forge.generate_seed_nonce () in + let seed_nonce_hash = Nonce.hash seed_nonce in Client_mining_forge.forge_block cctxt.rpc_config ~timestamp:(Time.now ()) ?force - ~seed_nonce ~src_sk block + ~seed_nonce_hash ~src_sk block ~priority:(`Auto (delegate, max_priority, free_mining)) () >>=? fun block_hash -> Client_mining_forge.State.record_block cctxt level block_hash seed_nonce |> trace_exn (Failure "Error while recording block") >>=? fun () -> diff --git a/src/client/embedded/alpha/baker/client_mining_operations.ml b/src/client/embedded/alpha/baker/client_mining_operations.ml index 575aa1b55..e14793853 100644 --- a/src/client/embedded/alpha/baker/client_mining_operations.ml +++ b/src/client/embedded/alpha/baker/client_mining_operations.ml @@ -44,6 +44,7 @@ type valid_endorsement = { slots: int list ; } +(* let filter_valid_endorsement cctxt ({ hash ; content } : operation) = let open Tezos_context in match content with @@ -115,3 +116,11 @@ let monitor_endorsement cctxt = ops_stream end ; return endorsement_stream +*) + +(* Temporary desactivate the monitoring of endorsement: + too slow for now. *) +let monitor_endorsement _ = + let stream, _push = Lwt_stream.create () in + return stream + diff --git a/src/client/embedded/alpha/baker/client_mining_operations.mli b/src/client/embedded/alpha/baker/client_mining_operations.mli index c1c6c7e39..b627950c8 100644 --- a/src/client/embedded/alpha/baker/client_mining_operations.mli +++ b/src/client/embedded/alpha/baker/client_mining_operations.mli @@ -24,10 +24,13 @@ type valid_endorsement = { slots: int list ; } +(* val filter_valid_endorsement: Client_rpcs.config -> operation -> valid_endorsement option Lwt.t +*) val monitor_endorsement: Client_rpcs.config -> valid_endorsement tzresult Lwt_stream.t tzresult Lwt.t + diff --git a/src/client/embedded/alpha/client_proto_rpcs.ml b/src/client/embedded/alpha/client_proto_rpcs.ml index 3d8f667b8..8b19337fd 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.ml +++ b/src/client/embedded/alpha/client_proto_rpcs.ml @@ -266,12 +266,15 @@ module Helpers = struct let nonce = Sodium.Random.Bigbytes.generate 16 in operations cctxt block ~net_id [Faucet { id ; counter ; nonce }] end - let block cctxt - block ~net_id ~predecessor ~timestamp ~fitness ~operations_hash - ~level ~priority ~proto_level ~seed_nonce_hash ~proof_of_work_nonce () = - call_error_service1 cctxt Services.Helpers.Forge.block block - ((net_id, predecessor, timestamp, fitness, operations_hash), - (level, priority, proto_level, seed_nonce_hash, proof_of_work_nonce)) + let empty_proof_of_work_nonce = + MBytes.of_string + (String.make Constants_repr.proof_of_work_nonce_size '\000') + let block_proto_header cctxt + block + ~priority ~seed_nonce_hash + ?(proof_of_work_nonce = empty_proof_of_work_nonce) () = + call_error_service1 cctxt Services.Helpers.Forge.block_proto_header + block (priority, seed_nonce_hash, proof_of_work_nonce) end module Parse = struct diff --git a/src/client/embedded/alpha/client_proto_rpcs.mli b/src/client/embedded/alpha/client_proto_rpcs.mli index 4b2f83890..01b3358b1 100644 --- a/src/client/embedded/alpha/client_proto_rpcs.mli +++ b/src/client/embedded/alpha/client_proto_rpcs.mli @@ -328,19 +328,12 @@ module Helpers : sig id:public_key_hash -> int32 -> MBytes.t tzresult Lwt.t end - val block: + val block_proto_header: Client_rpcs.config -> block -> - net_id:Net_id.t -> - predecessor:Block_hash.t -> - timestamp:Time.t -> - fitness:Fitness.t -> - operations_hash:Operation_list_list_hash.t -> - level:Raw_level.t -> - priority:int -> - proto_level:int -> - seed_nonce_hash:Nonce_hash.t -> - proof_of_work_nonce:MBytes.t -> + priority: int -> + seed_nonce_hash: Nonce_hash.t -> + ?proof_of_work_nonce: MBytes.t -> unit -> MBytes.t tzresult Lwt.t (** [block cctxt root ~net ~predecessor ~timestamp ~fitness ~operations ~level ~priority ~seed_nonce_hash diff --git a/src/client/embedded/demo/client_proto_main.ml b/src/client/embedded/demo/client_proto_main.ml index 445a2bb4d..1d94f9815 100644 --- a/src/client/embedded/demo/client_proto_main.ml +++ b/src/client/embedded/demo/client_proto_main.ml @@ -46,9 +46,15 @@ let mine cctxt = Lwt.ignore_result (cctxt.message "Cannot parse fitness: %a" Fitness.pp bi.fitness); exit 2 in - Client_node_rpcs.forge_block cctxt.rpc_config - ~net_id:bi.net_id ~predecessor:bi.hash - fitness Operation_list_list_hash.empty (MBytes.create 0) >>=? fun bytes -> + Client_node_rpcs.forge_block_header cctxt.rpc_config + { shell = { net_id = bi.net_id ; + predecessor = bi.hash ; + proto_level = bi.proto_level ; + level = Int32.succ bi.level ; + timestamp = Time.now () ; + fitness ; + operations_hash = Operation_list_list_hash.empty } ; + proto = MBytes.create 0 } >>=? fun bytes -> Client_node_rpcs.inject_block cctxt.rpc_config bytes [] >>=? fun hash -> cctxt.answer "Injected %a" Block_hash.pp_short hash >>= fun () -> return () diff --git a/src/node/shell/node.ml b/src/node/shell/node.ml index 72e153683..0dfc6b5e5 100644 --- a/src/node/shell/node.ml +++ b/src/node/shell/node.ml @@ -451,7 +451,9 @@ module RPC = struct let protocol_content node hash = State.Protocol.read node.state hash - let preapply node block ~timestamp ~sort ops = + let preapply + node block + ~timestamp ~proto_header ~sort_operations:sort ops = begin match block with | `Genesis -> @@ -477,11 +479,32 @@ module RPC = struct let net_db = Validator.net_db node.mainnet_validator in map_p (Distributed_db.resolve_operation net_db) ops >>=? fun rops -> Prevalidation.start_prevalidation - ~predecessor ~timestamp >>=? fun validation_state -> + ~proto_header ~predecessor ~timestamp () >>=? fun validation_state -> Prevalidation.prevalidate - validation_state ~sort rops >>=? fun (validation_state, r) -> - Prevalidation.end_prevalidation validation_state >>=? fun { fitness } -> - return (fitness, { r with applied = List.rev r.applied }) + validation_state ~sort rops >>= fun (validation_state, r) -> + let operations_hash = + Operation_list_list_hash.compute + [Operation_list_hash.compute r.applied] in + Prevalidation.end_prevalidation + validation_state >>=? fun { fitness ; context } -> + let pred_shell_header = State.Block.shell_header predecessor in + State.Block.protocol_hash predecessor >>= fun pred_protocol -> + Context.get_protocol context >>= fun protocol -> + let proto_level = + if Protocol_hash.equal protocol pred_protocol then + pred_shell_header.proto_level + else + ((pred_shell_header.proto_level + 1) mod 256) in + let shell_header : Block_header.shell_header = { + net_id = pred_shell_header.net_id ; + level = Int32.succ pred_shell_header.level ; + proto_level ; + predecessor = State.Block.hash predecessor ; + timestamp ; + operations_hash ; + fitness ; + } in + return (shell_header, r) let complete node ?block str = match block with diff --git a/src/node/shell/node.mli b/src/node/shell/node.mli index 7846113c6..9edf87151 100644 --- a/src/node/shell/node.mli +++ b/src/node/shell/node.mli @@ -80,9 +80,9 @@ module RPC : sig val preapply: t -> block -> - timestamp:Time.t -> sort:bool -> - Distributed_db.operation list -> - (Fitness.t * error Prevalidation.preapply_result) tzresult Lwt.t + timestamp:Time.t -> proto_header:MBytes.t -> + sort_operations:bool -> Distributed_db.operation list -> + (Block_header.shell_header * error Prevalidation.preapply_result) tzresult Lwt.t val validate: t -> Net_id.t -> Block_hash.t -> unit tzresult Lwt.t diff --git a/src/node/shell/node_rpc.ml b/src/node/shell/node_rpc.ml index fbb2d2409..5f8910636 100644 --- a/src/node/shell/node_rpc.ml +++ b/src/node/shell/node_rpc.ml @@ -128,16 +128,13 @@ let register_bi_dir node dir = implementation in let dir = let implementation - b { Services.Blocks.operations ; sort ; timestamp } = - let timestamp = - match timestamp with - | None -> Time.now () - | Some x -> x in - Node.RPC.preapply ~timestamp ~sort - node b operations >>= function - | Ok (fitness, operations) -> + b { Services.Blocks.operations ; sort_operations ; + timestamp ; proto_header} = + Node.RPC.preapply node b + ~timestamp ~proto_header ~sort_operations operations >>= function + | Ok (shell_header, operations) -> RPC.Answer.return - (Ok { Services.Blocks.fitness ; operations ; timestamp }) + (Ok { Services.Blocks.shell_header ; operations }) | Error _ as err -> RPC.Answer.return err in RPC.register1 dir Services.Blocks.preapply implementation in @@ -389,23 +386,11 @@ let build_rpc_directory node = let dir = RPC.register1 dir Services.Protocols.contents (get_protocols node) in let dir = - let implementation - (net_id, level, proto_level, pred, time, - fitness, operations_hash, header) = - Node.RPC.block_info node (`Head 0) >>= fun bi -> - let timestamp = Utils.unopt ~default:(Time.now ()) time in - let net_id = Utils.unopt ~default:bi.net_id net_id in - let predecessor = Utils.unopt ~default:bi.hash pred in - let level = Utils.unopt ~default:(Int32.succ bi.level) level in - let proto_level = Utils.unopt ~default:bi.proto_level proto_level in + let implementation header = let res = - Data_encoding.Binary.to_bytes Block_header.encoding { - shell = { net_id ; predecessor ; level ; proto_level ; - timestamp ; fitness ; operations_hash } ; - proto = header ; - } in + Data_encoding.Binary.to_bytes Block_header.encoding header in RPC.Answer.return res in - RPC.register0 dir Services.forge_block implementation in + RPC.register0 dir Services.forge_block_header implementation in let dir = let implementation (net_id, block_hash) = Node.RPC.validate node net_id block_hash >>= fun res -> diff --git a/src/node/shell/node_rpc_services.ml b/src/node/shell/node_rpc_services.ml index 4b86c6a70..68fca8f0f 100644 --- a/src/node/shell/node_rpc_services.ml +++ b/src/node/shell/node_rpc_services.ml @@ -150,44 +150,6 @@ module Blocks = struct let destruct = parse_block in RPC.Arg.make ~name ~descr ~construct ~destruct () - type preapply_param = { - operations: operation list ; - sort: bool ; - timestamp: Time.t option ; - } - - let preapply_param_encoding = - (conv - (fun { operations ; sort ; timestamp } -> - (operations, Some sort, timestamp)) - (fun (operations, sort, timestamp) -> - let sort = - match sort with - | None -> true - | Some x -> x in - { operations ; sort ; timestamp }) - (obj3 - (req "operations" (list (dynamic_size operation_encoding))) - (opt "sort" bool) - (opt "timestamp" Time.encoding))) - - type preapply_result = { - operations: error Prevalidation.preapply_result ; - fitness: MBytes.t list ; - timestamp: Time.t ; - } - - let preapply_result_encoding = - (conv - (fun { operations ; timestamp ; fitness } -> - (timestamp, fitness, operations)) - (fun (timestamp, fitness, operations) -> - { operations ; timestamp ; fitness }) - (obj3 - (req "timestamp" Time.encoding) - (req "fitness" Fitness.encoding) - (req "operations" (Prevalidation.preapply_result_encoding Error.encoding)))) - let block_path : (unit, unit * block) RPC.Path.path = RPC.Path.(root / "blocks" /: blocks_arg ) @@ -329,6 +291,41 @@ module Blocks = struct let proto_path = RPC.Path.(block_path / "proto") + type preapply_param = { + timestamp: Time.t ; + proto_header: MBytes.t ; + operations: operation list ; + sort_operations: bool ; + } + + let preapply_param_encoding = + (conv + (fun { timestamp ; proto_header ; operations ; sort_operations } -> + (timestamp, proto_header, operations, sort_operations)) + (fun (timestamp, proto_header, operations, sort_operations) -> + { timestamp ; proto_header ; operations ; sort_operations }) + (obj4 + (req "timestamp" Time.encoding) + (req "proto_header" bytes) + (req "operations" (list (dynamic_size operation_encoding))) + (dft "sort_operations" bool false))) + + type preapply_result = { + shell_header: Block_header.shell_header ; + operations: error Prevalidation.preapply_result ; + } + + let preapply_result_encoding = + (conv + (fun { shell_header ; operations } -> + (shell_header, operations)) + (fun (shell_header, operations) -> + { shell_header ; operations }) + (obj2 + (req "shell_header" Block_header.shell_header_encoding) + (req "operations" + (Prevalidation.preapply_result_encoding Error.encoding)))) + let preapply = RPC.service ~description: @@ -612,21 +609,12 @@ module Network = struct end -let forge_block = +let forge_block_header = RPC.service ~description: "Forge a block header" - ~input: - (obj8 - (opt "net_id" Net_id.encoding) - (opt "level" int32) - (opt "proto_level" uint8) - (opt "predecessor" Block_hash.encoding) - (opt "timestamp" Time.encoding) - (req "fitness" Fitness.encoding) - (req "operations" Operation_list_list_hash.encoding) - (req "header" bytes)) + ~input: Block_header.encoding ~output: (obj1 (req "block" bytes)) - RPC.Path.(root / "forge_block") + RPC.Path.(root / "forge_block_header") let validate_block = RPC.service diff --git a/src/node/shell/node_rpc_services.mli b/src/node/shell/node_rpc_services.mli index d71bed614..47a0c6990 100644 --- a/src/node/shell/node_rpc_services.mli +++ b/src/node/shell/node_rpc_services.mli @@ -93,14 +93,15 @@ module Blocks : sig (unit, unit, list_param, block_info list list) RPC.service type preapply_param = { - operations: operation list ; - sort: bool ; - timestamp: Time.t option ; - } - type preapply_result = { - operations: error Prevalidation.preapply_result ; - fitness: MBytes.t list ; timestamp: Time.t ; + proto_header: MBytes.t ; + operations: operation list ; + sort_operations: bool ; + } + + type preapply_result = { + shell_header: Block_header.shell_header ; + operations: error Prevalidation.preapply_result ; } val preapply: (unit, unit * block, preapply_param, preapply_result tzresult) RPC.service @@ -109,6 +110,7 @@ module Blocks : sig val proto_path: (unit, unit * block) RPC.Path.path + end module Protocols : sig @@ -173,11 +175,8 @@ module Network : sig end -val forge_block: - (unit, unit, - Net_id.t option * Int32.t option * int option * Block_hash.t option * - Time.t option * Fitness.t * Operation_list_list_hash.t * MBytes.t, - MBytes.t) RPC.service +val forge_block_header: + (unit, unit, Block_header.t, MBytes.t) RPC.service val validate_block: (unit, unit, Net_id.t * Block_hash.t, unit tzresult) RPC.service diff --git a/src/node/shell/prevalidation.ml b/src/node/shell/prevalidation.ml index ec441e3e0..f8448e037 100644 --- a/src/node/shell/prevalidation.ml +++ b/src/node/shell/prevalidation.ml @@ -73,21 +73,7 @@ let empty_result = branch_refused = Operation_hash.Map.empty ; branch_delayed = Operation_hash.Map.empty } -let merge_result r r' = - let open Updater in - let merge _key a b = - match a, b with - | None, None -> None - | Some x, None -> Some x - | _, Some y -> Some y in - let merge_map = - Operation_hash.Map.merge merge in - { applied = r'.applied @ r.applied ; - refused = merge_map r.refused r'.refused ; - branch_refused = merge_map r.branch_refused r'.branch_refused ; - branch_delayed = r'.branch_delayed } - -let rec apply_operations apply_operation state ~sort ops = +let rec apply_operations apply_operation state r ~sort ops = Lwt_list.fold_left_s (fun (state, r) (hash, op) -> apply_operation state op >>= function @@ -108,7 +94,7 @@ let rec apply_operations apply_operation state ~sort ops = let branch_delayed = Operation_hash.Map.add hash errors r.branch_delayed in Lwt.return (state, { r with branch_delayed })) - (state, empty_result) + (state, r) ops >>= fun (state, r) -> match r.applied with | _ :: _ when sort -> @@ -116,11 +102,13 @@ let rec apply_operations apply_operation state ~sort ops = List.filter (fun (hash, _) -> Operation_hash.Map.mem hash r.branch_delayed) ops in - apply_operations apply_operation - state ~sort rechecked_operations >>=? fun (state, r') -> - return (state, merge_result r r') + let remaining = List.length rechecked_operations in + if remaining = 0 || remaining = List.length ops then + Lwt.return (state, r) + else + apply_operations apply_operation state r ~sort rechecked_operations | _ -> - return (state, r) + Lwt.return (state, r) type prevalidation_state = State : { proto : 'a proto ; state : 'a } @@ -130,9 +118,7 @@ and 'a proto = (module Updater.REGISTRED_PROTOCOL with type validation_state = 'a) -let start_prevalidation - ~predecessor - ~timestamp = +let start_prevalidation ?proto_header ~predecessor ~timestamp () = let { Block_header.shell = { fitness = predecessor_fitness ; timestamp = predecessor_timestamp ; @@ -155,33 +141,47 @@ let start_prevalidation ~predecessor_level ~predecessor ~timestamp + ?proto_header () >>=? fun state -> return (State { proto = (module Proto) ; state }) +type error += Parse_error + let prevalidate (State { proto = (module Proto) ; state }) ~sort ops = - (* The operations list length is bounded by the size of the mempool, - where eventually an operation should not stay more than one hours. *) - Lwt_list.map_p - (fun (h, op) -> - match Proto.parse_operation h op with - | Error _ -> - (* the operation will never be validated in the - current context, it is silently ignored. It may be - reintroduced in the loop by the next `flush`. *) - Lwt.return_none - | Ok p -> Lwt.return (Some (h, p))) - ops >>= fun ops -> - let ops = Utils.unopt_list ops in let ops = + List.map + (fun (h, op) -> + (h, Proto.parse_operation h op |> record_trace Parse_error)) + ops in + let invalid_ops = + Utils.filter_map + (fun (h, op) -> match op with + | Ok _ -> None + | Error err -> Some (h, err)) ops + and parsed_ops = + Utils.filter_map + (fun (h, op) -> match op with + | Ok op -> Some (h, op) + | Error _ -> None) ops in + let sorted_ops = if sort then let compare (_, op1) (_, op2) = Proto.compare_operations op1 op2 in - List.sort compare ops - else ops in - apply_operations Proto.apply_operation state ~sort ops >>=? fun (state, r) -> - return (State { proto = (module Proto) ; state }, r) + List.sort compare parsed_ops + else parsed_ops in + apply_operations + Proto.apply_operation + state empty_result ~sort sorted_ops >>= fun (state, r) -> + let r = + { r with + applied = List.rev r.applied ; + branch_refused = + List.fold_left + (fun map (h, err) -> Operation_hash.Map.add h err map) + r.branch_refused invalid_ops } in + Lwt.return (State { proto = (module Proto) ; state }, r) let end_prevalidation (State { proto = (module Proto) ; state }) = Proto.finalize_block state diff --git a/src/node/shell/prevalidation.mli b/src/node/shell/prevalidation.mli index b5e6282ea..02c20661a 100644 --- a/src/node/shell/prevalidation.mli +++ b/src/node/shell/prevalidation.mli @@ -29,14 +29,15 @@ val preapply_result_encoding : type prevalidation_state val start_prevalidation : + ?proto_header: MBytes.t -> predecessor: State.Block.t -> timestamp: Time.t -> - prevalidation_state tzresult Lwt.t + unit -> prevalidation_state tzresult Lwt.t val prevalidate : prevalidation_state -> sort:bool -> (Operation_hash.t * Operation.t) list -> - (prevalidation_state * error preapply_result) tzresult Lwt.t + (prevalidation_state * error preapply_result) Lwt.t val end_prevalidation : prevalidation_state -> Updater.validation_result tzresult Lwt.t diff --git a/src/node/shell/prevalidator.ml b/src/node/shell/prevalidator.ml index 86a5c43f5..391112cd1 100644 --- a/src/node/shell/prevalidator.ml +++ b/src/node/shell/prevalidator.ml @@ -73,7 +73,7 @@ let create net_db = Chain.head net_state >>= fun head -> let timestamp = ref (Time.now ()) in - (start_prevalidation head !timestamp >|= ref) >>= fun validation_state -> + (start_prevalidation head !timestamp () >|= ref) >>= fun validation_state -> let pending = Operation_hash.Table.create 53 in let head = ref head in let operations = ref empty_result in @@ -86,7 +86,7 @@ let create net_db = Lwt.return_unit in let reset_validation_state head timestamp = - start_prevalidation head timestamp >>= fun state -> + start_prevalidation head timestamp () >>= fun state -> validation_state := state; Lwt.return_unit in @@ -109,12 +109,13 @@ let create net_db = Lwt_list.map_p (fun h -> Distributed_db.Operation.read_opt net_db h >>= function - | None -> Lwt.return_none - | Some po -> Lwt.return_some (h, po)) + | Some po -> + Lwt.return_some (h, po) + | None -> Lwt.return_none) (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 + (prevalidate validation_state ~sort:true rops >>= return)) >>= function | Ok (state, r) -> Lwt.return (Ok state, r) | Error err -> let r = @@ -129,16 +130,16 @@ let create net_db = List.fold_right Operation_hash.Map.remove s m in operations := { applied = List.rev_append r.applied !operations.applied ; - refused = Operation_hash.Map.empty ; - branch_refused = - Operation_hash.Map.merge merge - (* filter_out should not be required here, TODO warn ? *) - (filter_out r.applied !operations.branch_refused) - r.branch_refused ; - branch_delayed = - Operation_hash.Map.merge merge - (filter_out r.applied !operations.branch_delayed) - r.branch_delayed ; + refused = Operation_hash.Map.empty ; + branch_refused = + Operation_hash.Map.merge merge + (* filter_out should not be required here, TODO warn ? *) + (filter_out r.applied !operations.branch_refused) + r.branch_refused ; + branch_delayed = + Operation_hash.Map.merge merge + (filter_out r.applied !operations.branch_delayed) + r.branch_delayed ; } ; if broadcast then broadcast_operation r.applied ; Lwt_list.iter_s @@ -181,7 +182,8 @@ let create net_db = let result = let rops = Operation_hash.Map.bindings ops in Lwt.return !validation_state >>=? fun validation_state -> - prevalidate validation_state ~sort:true rops >>=? fun (state, res) -> + prevalidate validation_state + ~sort:true rops >>= fun (state, res) -> let register h = let op = Operation_hash.Map.find h ops in Distributed_db.inject_operation diff --git a/src/proto/alpha/services.ml b/src/proto/alpha/services.ml index 322eccb1e..b0bcbd244 100644 --- a/src/proto/alpha/services.ml +++ b/src/proto/alpha/services.ml @@ -622,28 +622,6 @@ module Helpers = struct ~output: (wrap_tzerror bytes) RPC.Path.(custom_root / "helpers" / "forge" / "block_proto_header") - let block custom_root = - RPC.service - ~description: "Forge a block header" - ~input: - (merge_objs - (obj5 - (req "net_id" Net_id.encoding) - (req "predecessor" Block_hash.encoding) - (req "timestamp" Timestamp.encoding) - (req "fitness" Fitness.encoding) - (req "operations" Operation_list_list_hash.encoding)) - (obj5 - (req "level" Raw_level.encoding) - (req "priority" uint16) - (req "proto_level" uint8) - (req "nonce_hash" Nonce_hash.encoding) - (req "proof_of_work_nonce" - (Fixed.bytes - Tezos_context.Constants.proof_of_work_nonce_size)))) - ~output: (wrap_tzerror bytes) - RPC.Path.(custom_root / "helpers" / "forge" / "block") - end module Parse = struct diff --git a/src/proto/alpha/services_registration.ml b/src/proto/alpha/services_registration.ml index 921cb9f62..e5cd39e22 100644 --- a/src/proto/alpha/services_registration.ml +++ b/src/proto/alpha/services_registration.ml @@ -496,17 +496,6 @@ let forge_block_proto_header _ctxt let () = register1 Services.Helpers.Forge.block_proto_header forge_block_proto_header -let forge_block _ctxt - ((net_id, predecessor, timestamp, fitness, operations_hash), - (level, priority, proto_level, seed_nonce_hash, proof_of_work_nonce)) : MBytes.t tzresult Lwt.t = - let level = Raw_level.to_int32 level in - return (Block_header.forge_unsigned - { net_id ; level ; proto_level ; predecessor ; - timestamp ; fitness ; operations_hash } - { priority ; seed_nonce_hash ; proof_of_work_nonce }) - -let () = register1 Services.Helpers.Forge.block forge_block - (*-- Helpers.Parse -----------------------------------------------------------*) let dummy_hash = Operation_hash.hash_bytes [] diff --git a/src/utils/error_monad.ml b/src/utils/error_monad.ml index e7bf7774d..e193fbaf8 100644 --- a/src/utils/error_monad.ml +++ b/src/utils/error_monad.ml @@ -279,6 +279,18 @@ module Make() = struct map_filter_s f t >>=? fun rt -> return (rh :: rt) + let rec map_filter_p f l = + match l with + | [] -> return [] + | h :: t -> + let th = f h + and tt = map_filter_s f t in + th >>=? function + | None -> tt + | Some rh -> + tt >>=? fun rt -> + return (rh :: rt) + let rec iter_s f l = match l with | [] -> return () diff --git a/src/utils/error_monad_sig.ml b/src/utils/error_monad_sig.ml index 7b757bc53..8afb570d5 100644 --- a/src/utils/error_monad_sig.ml +++ b/src/utils/error_monad_sig.ml @@ -137,6 +137,8 @@ module type S = sig (** A {!List.map_filter} in the monad *) val map_filter_s : ('a -> 'b option tzresult Lwt.t) -> 'a list -> 'b list tzresult Lwt.t + val map_filter_p : + ('a -> 'b option tzresult Lwt.t) -> 'a list -> 'b list tzresult Lwt.t (** A {!List.fold_left} in the monad *) val fold_left_s : diff --git a/test/proto_alpha/.merlin b/test/proto_alpha/.merlin index 085399bd3..ad5447adb 100644 --- a/test/proto_alpha/.merlin +++ b/test/proto_alpha/.merlin @@ -18,8 +18,9 @@ B ../../src/proto S ../../src/client B ../../src/client S ../../src/client/embedded/alpha +B ../../src/client/embedded/alpha S ../../src/client/embedded/alpha/baker -B ../../src/client/embedded +B ../../src/client/embedded/alpha/baker S ../lib B ../lib FLG -open Error_monad -open Hash -open Utils -open Environment -open Tezos_data diff --git a/test/proto_alpha/proto_alpha_helpers.ml b/test/proto_alpha/proto_alpha_helpers.ml index f544c15b5..c6edff704 100644 --- a/test/proto_alpha/proto_alpha_helpers.ml +++ b/test/proto_alpha/proto_alpha_helpers.ml @@ -13,6 +13,8 @@ open Client_alpha let (//) = Filename.concat +let () = Random.self_init () + let rpc_config : Client_rpcs.config = { host = "localhost" ; port = 8192 + Random.int 8192 ; @@ -33,7 +35,6 @@ let activate_alpha () = fitness dictator_sk let init ?(sandbox = "sandbox.json") () = - Random.self_init () ; Unix.chdir (Filename.dirname (Filename.dirname Sys.executable_name)) ; let pid = Node_helpers.fork_node @@ -311,6 +312,23 @@ module Assert = struct List.exists f errors | _ -> false + let hash = function + | Client_node_rpcs.Hash h -> h + | Blob op -> Tezos_data.Operation.hash op + + let failed_to_preapply ~msg ?op f = + Assert.contain_error ~msg ~f:begin function + | Client_mining_forge.Failed_to_preapply (op', err) -> + begin + match op with + | None -> true + | Some op -> + let h = hash op and h' = hash op' in + Operation_hash.equal h h' + end && List.exists (ecoproto_error f) err + | _ -> false + end + let generic_economic_error ~msg = Assert.contain_error ~msg ~f:(ecoproto_error (fun _ -> true)) @@ -363,12 +381,6 @@ module Assert = struct | _ -> false) end - let invalid_endorsement_slot ~msg = - Assert.contain_error ~msg ~f:begin ecoproto_error (function - | Mining.Invalid_endorsement_slot _ -> true - | _ -> false) - end - let check_protocol ?msg ~block h = Client_node_rpcs.Blocks.protocol rpc_config block >>=? fun block_proto -> return @@ Assert.equal @@ -388,128 +400,28 @@ end module Mining = struct - let get_first_priority - ?(max_priority=1024) - level - (contract : Account.t) - block = - Client_proto_rpcs.Helpers.Rights.mining_rights_for_delegate - rpc_config - ~max_priority - ~first_level:level - ~last_level:level - block contract.Account.pkh () >>=? fun possibilities -> - try - let _, prio, _ = - List.find (fun (l,_,_) -> l = level) possibilities in - return prio - with Not_found -> - failwith "No slot found at level %a" Raw_level.pp level - - let rec mine_stamp - block - delegate_sk - shell - priority - seed_nonce_hash = - Client_proto_rpcs.Constants.stamp_threshold - rpc_config block >>=? fun stamp_threshold -> - let rec loop () = - let proof_of_work_nonce = - Sodium.Random.Bigbytes.generate Constants.proof_of_work_nonce_size in - let unsigned_header = - Block_header.forge_unsigned - shell { priority ; seed_nonce_hash ; proof_of_work_nonce } in - let signed_header = - Ed25519.Signature.append delegate_sk unsigned_header in - let block_hash = Block_hash.hash_bytes [signed_header] in - if Mining.check_hash block_hash stamp_threshold then - proof_of_work_nonce - else - loop () in - return (loop ()) - - let inject_block - block - ?force - ?proto_level - ~priority - ~timestamp - ~fitness - ~seed_nonce - ~src_sk - operations = - let block = match block with `Prevalidation -> `Head 0 | block -> block in - Client_node_rpcs.Blocks.info rpc_config block >>=? fun bi -> - let proto_level = Utils.unopt ~default:bi.proto_level proto_level in + let mine block (contract: Account.t) operations = + let operations = List.map (fun op -> Client_node_rpcs.Blob op) operations in + let seed_nonce = + match Nonce.of_bytes @@ + Sodium.Random.Bigbytes.generate Constants.nonce_length with + | Error _ -> assert false + | Ok nonce -> nonce in let seed_nonce_hash = Nonce.hash seed_nonce in - Client_proto_rpcs.Context.next_level rpc_config block >>=? fun level -> - let operation_hashes = List.map Tezos_data.Operation.hash operations in - let operations_hash = - Operation_list_list_hash.compute - [Operation_list_hash.compute operation_hashes] in - let shell = - { Tezos_data.Block_header.net_id = bi.net_id ; predecessor = bi.hash ; - timestamp ; fitness ; operations_hash ; - level = Raw_level.to_int32 level.level ; - proto_level } in - mine_stamp - block src_sk shell priority seed_nonce_hash >>=? fun proof_of_work_nonce -> - Client_proto_rpcs.Helpers.Forge.block rpc_config + Client_mining_forge.forge_block + rpc_config block - ~net_id:bi.net_id - ~predecessor:bi.hash - ~timestamp - ~fitness - ~operations_hash - ~level:level.level - ~proto_level - ~priority + ~operations + ~force:true + ~best_effort:false + ~sort:false + ~priority:(`Auto (contract.pkh, Some 1024, false)) ~seed_nonce_hash - ~proof_of_work_nonce - () >>=? fun unsigned_header -> - let signed_header = Ed25519.Signature.append src_sk unsigned_header in - Client_node_rpcs.inject_block rpc_config - ?force signed_header - [List.map (fun h -> Client_node_rpcs.Blob h) operations] >>=? fun block_hash -> - return block_hash + ~src_sk:contract.sk + () - let mine - ?(force = true) - ?(operations = []) - ?(fitness_gap = 1) - ?proto_level - contract - block = - Client_mining_blocks.info rpc_config block >>=? fun bi -> - let seed_nonce = - match Nonce.of_bytes @@ - Sodium.Random.Bigbytes.generate Constants.nonce_length with - | Error _ -> assert false - | Ok nonce -> nonce in - let timestamp = Time.add (Time.now ()) 1L in - Client_proto_rpcs.Context.level rpc_config block >>=? fun level -> - let level = Raw_level.succ level.level in - get_first_priority level contract block >>=? fun priority -> - (Lwt.return (Fitness_repr.to_int64 bi.fitness) >|= - Register_client_embedded_proto_alpha.wrap_error) >>=? fun fitness -> - let fitness = - Fitness_repr.from_int64 @@ - Int64.add fitness (Int64.of_int fitness_gap) in - inject_block - ~force - ?proto_level - ~priority - ~timestamp - ~fitness - ~seed_nonce - ~src_sk:contract.sk - block - operations - - let endorsement_reward contract block = - Client_mining_blocks.info rpc_config block >>=? fun bi -> - get_first_priority bi.level.level contract block >>=? fun prio -> + let endorsement_reward block = + Client_proto_rpcs.Header.priority rpc_config block >>=? fun prio -> Mining.endorsement_reward ~block_priority:prio >|= Register_client_embedded_proto_alpha.wrap_error >>|? Tez.to_cents diff --git a/test/proto_alpha/proto_alpha_helpers.mli b/test/proto_alpha/proto_alpha_helpers.mli index b282b7224..f427cdbf3 100644 --- a/test/proto_alpha/proto_alpha_helpers.mli +++ b/test/proto_alpha/proto_alpha_helpers.mli @@ -93,48 +93,15 @@ end module Mining : sig - val get_first_priority : - ?max_priority:int -> - Raw_level.t -> - Account.t -> - Client_proto_rpcs.block -> - int tzresult Lwt.t - (** [get_first_priority ?max_prio level account block] is the - best (first) mining priority on [block] for [account] at - [level]. *) - - val mine_stamp : - Client_proto_rpcs.block -> - secret_key -> - Block_header.shell_header -> - int -> - Nonce_hash.t -> - MBytes.t tzresult Lwt.t - - val inject_block : + val mine: Client_node_rpcs.Blocks.block -> - ?force:bool -> - ?proto_level:int -> - priority:int -> - timestamp:Time.t -> - fitness:Fitness.t -> - seed_nonce:Nonce.nonce -> - src_sk:secret_key -> - Operation.raw list -> Block_hash.t tzresult Lwt.t - - val mine : - ?force:bool -> - ?operations:Operation.raw list -> - ?fitness_gap:int -> - ?proto_level:int -> Account.t -> - Client_node_rpcs.Blocks.block -> + Operation.raw list -> Block_hash.t tzresult Lwt.t - val endorsement_reward : - Account.t -> - Client_node_rpcs.Blocks.block -> - int64 tzresult Lwt.t + val endorsement_reward: + Client_node_rpcs.Blocks.block -> int64 tzresult Lwt.t + end module Endorse : sig @@ -186,6 +153,13 @@ module Assert : sig ?block:Client_node_rpcs.Blocks.block -> msg:string -> Contract.t -> public_key_hash option -> unit tzresult Lwt.t + val failed_to_preapply: + msg:string -> + ?op:Client_node_rpcs.operation -> + (Register_client_embedded_proto_alpha.Packed_protocol.error -> + bool) -> + 'a tzresult -> unit + val ecoproto_error: (Register_client_embedded_proto_alpha.Packed_protocol.error -> bool) -> error -> bool @@ -211,8 +185,6 @@ module Assert : sig val wrong_delegate : msg:string -> 'a tzresult -> unit - val invalid_endorsement_slot : msg:string -> 'a tzresult -> unit - val check_protocol : ?msg:string -> block:Client_node_rpcs.Blocks.block -> Protocol_hash.t -> unit tzresult Lwt.t diff --git a/test/proto_alpha/test_endorsement.ml b/test/proto_alpha/test_endorsement.ml index 68908f32c..49a60af20 100644 --- a/test/proto_alpha/test_endorsement.ml +++ b/test/proto_alpha/test_endorsement.ml @@ -17,25 +17,25 @@ module Assert = Helpers.Assert let test_double_endorsement contract block = (* Double endorsement for the same level *) - Helpers.Mining.mine ~fitness_gap:1 contract block >>=? fun b1 -> + Helpers.Mining.mine block contract [] >>=? fun b1 -> (* branch root *) - Helpers.Mining.mine ~fitness_gap:1 contract (`Hash b1) >>=? fun b2 -> + Helpers.Mining.mine (`Hash b1) contract [] >>=? fun b2 -> (* changing branch *) - Helpers.Mining.mine ~fitness_gap:1 contract (`Hash b1) >>=? fun b2' -> + Helpers.Mining.mine (`Hash b1) contract [] >>=? fun b2' -> (* branch root *) Helpers.Endorse.endorse contract (`Hash b2) >>=? fun op -> - Helpers.Mining.mine ~fitness_gap:2 ~operations:[ op ] contract (`Hash b2) >>=? fun _b3 -> + Helpers.Mining.mine (`Hash b2) contract [ op ] >>=? fun _b3 -> Helpers.Endorse.endorse contract (`Hash b2') >>=? fun op -> - Helpers.Mining.mine ~fitness_gap:2 ~operations:[ op ] contract (`Hash b2') >>=? fun b3' -> + Helpers.Mining.mine (`Hash b2') contract [ op ] >>=? fun b3' -> Helpers.Endorse.endorse contract (`Hash b3') >>=? fun op -> - Helpers.Mining.mine ~fitness_gap:2 ~operations:[ op ] contract (`Hash b3') >>=? fun b4' -> + Helpers.Mining.mine (`Hash b3') contract [ op ] >>=? fun b4' -> (* TODO: Inject double endorsement op ! *) - Helpers.Mining.mine ~fitness_gap:1 contract (`Hash b4') + Helpers.Mining.mine (`Hash b4') contract [] (* FIXME: Mining.Invalid_signature is unclassified *) let test_invalid_signature block = @@ -48,7 +48,7 @@ let test_invalid_signature block = DYfTKhq7rDQujdn5WWzwUMeV3agaZ6J2vPQT58jJAJPi" in let account = Helpers.Account.create ~keys:(secret_key, public_key) "WRONG SIGNATURE" in - Helpers.Mining.mine ~fitness_gap:1 account block >>= fun res -> + Helpers.Mining.mine block account [] >>= fun res -> Assert.generic_economic_error ~msg:__LOC__ res ; return () @@ -61,37 +61,38 @@ let contain_tzerror ?(msg="") ~f t = let test_wrong_delegate ~miner contract head = let block = `Hash head in - contain_tzerror ~msg:__LOC__ ~f:begin Assert.ecoproto_error (function - | Mining.Wrong_delegate _ -> true - | _ -> false) - end begin + begin Helpers.Endorse.endorse ~slot:1 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] miner block >>=? fun _ -> + Helpers.Mining.mine block miner [ op ] >>=? fun _ -> Helpers.Endorse.endorse ~slot:2 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] miner block >>=? fun _ -> + Helpers.Mining.mine block miner [ op ] >>=? fun _ -> Helpers.Endorse.endorse ~slot:3 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] miner block >>=? fun _ -> + Helpers.Mining.mine block miner [ op ] >>=? fun _ -> Helpers.Endorse.endorse ~slot:4 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] miner block >>=? fun _ -> + Helpers.Mining.mine block miner [ op ] >>=? fun _ -> Helpers.Endorse.endorse ~slot:5 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] miner block >>=? fun _ -> + Helpers.Mining.mine block miner [ op ] >>=? fun _ -> return () - end + end >>= fun res -> + Assert.failed_to_preapply ~msg:__LOC__ begin function + | Mining.Wrong_delegate _ -> true + | _ -> false + end res ; + Lwt.return_unit let test_invalid_endorsement_slot contract block = Helpers.Endorse.endorse ~slot:~-1 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] contract block >>= fun res -> - Assert.invalid_endorsement_slot ~msg:__LOC__ res ; + Helpers.Mining.mine block contract [ op ] >>= fun res -> + Assert.failed_to_preapply ~msg:__LOC__ ~op:(Blob op) begin function + | Mining.Invalid_endorsement_slot _ -> true + | _ -> false + end res ; Helpers.Endorse.endorse ~slot:16 contract block >>=? fun op -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ op ] contract block >>= fun res -> - Assert.invalid_endorsement_slot ~msg:__LOC__ res ; + Helpers.Mining.mine block contract [ op ] >>= fun res -> + Assert.failed_to_preapply ~msg:__LOC__ ~op:(Blob op) begin function + | Mining.Invalid_endorsement_slot _ -> true + | _ -> false + end res ; return () let test_endorsement_rewards @@ -112,8 +113,7 @@ let test_endorsement_rewards get_endorser_except [ b1 ] accounts >>=? fun (account0, slot0) -> Helpers.Account.balance ~block account0 >>=? fun balance0 -> Helpers.Endorse.endorse ~slot:slot0 account0 block >>=? fun ops -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ ops ] b1 block >>=? fun head0 -> + Helpers.Mining.mine block b1 [ ops ] >>=? fun head0 -> Helpers.display_level (`Hash head0) >>=? fun () -> Assert.balance_equal ~block:(`Hash head0) ~msg:__LOC__ account0 (Int64.sub (Tez.to_cents balance0) bond) >>=? fun () -> @@ -125,32 +125,31 @@ let test_endorsement_rewards get_endorser_except [ b1 ; account0 ] accounts >>=? fun (account1, slot1) -> Helpers.Account.balance ~block:block0 account1 >>=? fun balance1 -> Helpers.Endorse.endorse ~slot:slot1 account1 block0 >>=? fun ops -> - Helpers.Mining.mine - ~fitness_gap:2 ~operations:[ ops ] b1 block0 >>=? fun head1 -> + Helpers.Mining.mine block0 b1 [ ops ] >>=? fun head1 -> Helpers.display_level (`Hash head1) >>=? fun () -> Assert.balance_equal ~block:(`Hash head1) ~msg:__LOC__ account1 (Int64.sub (Tez.to_cents balance1) bond) >>=? fun () -> (* Check rewards after one cycle for account0 *) - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head1) >>=? fun head2 -> + Helpers.Mining.mine (`Hash head1) b1 [] >>=? fun head2 -> Helpers.display_level (`Hash head2) >>=? fun () -> - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head2) >>=? fun head3 -> + Helpers.Mining.mine (`Hash head2) b1 [] >>=? fun head3 -> Helpers.display_level (`Hash head3) >>=? fun () -> - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head3) >>=? fun head4 -> + Helpers.Mining.mine (`Hash head3) b1 [] >>=? fun head4 -> Helpers.display_level (`Hash head4) >>=? fun () -> - Helpers.Mining.endorsement_reward b1 block0 >>=? fun rw0 -> + Helpers.Mining.endorsement_reward block0 >>=? fun rw0 -> Assert.balance_equal ~block:(`Hash head4) ~msg:__LOC__ account0 (Int64.add (Tez.to_cents balance0) rw0) >>=? fun () -> (* Check rewards after one cycle for account1 *) - Helpers.Mining.endorsement_reward b1 (`Hash head1) >>=? fun rw1 -> + Helpers.Mining.endorsement_reward (`Hash head1) >>=? fun rw1 -> Assert.balance_equal ~block:(`Hash head4) ~msg:__LOC__ account1 (Int64.add (Tez.to_cents balance1) rw1) >>=? fun () -> (* #2 endorse and check reward only on the good chain *) - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head4) >>=? fun head -> + Helpers.Mining.mine (`Hash head4) b1 []>>=? fun head -> Helpers.display_level (`Hash head) >>=? fun () -> - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head4) >>=? fun fork -> + Helpers.Mining.mine (`Hash head4) b1 [] >>=? fun fork -> Helpers.display_level (`Hash fork) >>=? fun () -> (* working on head *) @@ -159,7 +158,7 @@ let test_endorsement_rewards Helpers.Account.balance ~block:(`Hash head) account3 >>=? fun balance3 -> Helpers.Endorse.endorse ~slot:slot3 account3 (`Hash head) >>=? fun ops -> - Helpers.Mining.mine ~fitness_gap:2 ~operations:[ ops ] b1 (`Hash head) >>=? fun new_head -> + Helpers.Mining.mine (`Hash head) b1 [ ops ] >>=? fun new_head -> Helpers.display_level (`Hash new_head) >>=? fun () -> (* working on fork *) @@ -167,17 +166,17 @@ let test_endorsement_rewards get_endorser_except [ b1 ] accounts >>=? fun (account4, slot4) -> Helpers.Account.balance ~block:(`Hash new_head) account4 >>=? fun _balance4 -> Helpers.Endorse.endorse ~slot:slot4 account4 (`Hash fork) >>=? fun ops -> - Helpers.Mining.mine ~fitness_gap:2 ~operations:[ ops ] b1 (`Hash fork) >>=? fun _new_fork -> + Helpers.Mining.mine (`Hash fork) b1 [ ops ] >>=? fun _new_fork -> Helpers.display_level (`Hash _new_fork) >>=? fun () -> Helpers.Account.balance ~block:(`Hash new_head) account4 >>=? fun balance4 -> - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash new_head) >>=? fun head -> + Helpers.Mining.mine (`Hash new_head) b1 [] >>=? fun head -> Helpers.display_level (`Hash head) >>=? fun () -> - Helpers.Mining.mine ~fitness_gap:1 b1 (`Hash head) >>=? fun head -> + Helpers.Mining.mine (`Hash head) b1 [] >>=? fun head -> Helpers.display_level (`Hash head) >>=? fun () -> (* Check rewards after one cycle *) - Helpers.Mining.endorsement_reward b1 (`Hash new_head) >>=? fun reward -> + Helpers.Mining.endorsement_reward (`Hash new_head) >>=? fun reward -> Assert.balance_equal ~block:(`Hash head) ~msg:__LOC__ account3 (Int64.add (Tez.to_cents balance3) reward) >>=? fun () -> @@ -217,8 +216,8 @@ let run head (({ b1 ; b2 ; b3 ; b4 ; b5 } : Helpers.Account.bootstrap_accounts) (* Endorse with a contract with wrong delegate: - contract with no endorsement rights - contract which signs at every available slots *) - test_wrong_delegate ~miner:b1 default_account head >>=? fun () -> - test_wrong_delegate ~miner:b1 b5 head >>=? fun () -> + test_wrong_delegate ~miner:b1 default_account head >>= fun () -> + test_wrong_delegate ~miner:b1 b5 head >>= fun () -> (* Endorse with a wrong slot : -1 and max (16) *) test_invalid_endorsement_slot b3 (`Hash head) >>=? fun () -> diff --git a/test/proto_alpha/test_origination.ml b/test/proto_alpha/test_origination.ml index 265cd41e9..5fe27f1db 100644 --- a/test/proto_alpha/test_origination.ml +++ b/test/proto_alpha/test_origination.ml @@ -15,7 +15,7 @@ module Assert = Helpers.Assert let run blkid ({ b1 ; b2 ; _ } : Helpers.Account.bootstrap_accounts) = - Helpers.Mining.mine ~fitness_gap:1 b1 blkid >>=? fun blkh -> + Helpers.Mining.mine blkid b1 [] >>=? fun blkh -> let foo = Helpers.Account.create "foo" in (* Origination with amount = 0 tez *) diff --git a/test/proto_alpha/test_transaction.ml b/test/proto_alpha/test_transaction.ml index abf8a64ba..43a9a8621 100644 --- a/test/proto_alpha/test_transaction.ml +++ b/test/proto_alpha/test_transaction.ml @@ -15,7 +15,7 @@ module Assert = Helpers.Assert let run blkid ({ b1 ; b2 ; b3 ; _ } : Helpers.Account.bootstrap_accounts) = - Helpers.Mining.mine ~fitness_gap:1 b1 blkid >>=? fun blkh -> + Helpers.Mining.mine blkid b1 [] >>=? fun blkh -> let foo = Helpers.Account.create "foo" in let bar = Helpers.Account.create "bar" in diff --git a/test/proto_alpha/test_vote.ml b/test/proto_alpha/test_vote.ml index f485ec8df..0db47c326 100644 --- a/test/proto_alpha/test_vote.ml +++ b/test/proto_alpha/test_vote.ml @@ -20,12 +20,13 @@ let print_level head = return @@ Format.eprintf "voting_period = %a.%ld@." Voting_period.pp lvl.voting_period lvl.voting_period_position -let run_change_to_demo_proto block ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstrap_accounts) = - Mining.mine b1 block >>=? fun head -> +let run_change_to_demo_proto block + ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstrap_accounts) = + Mining.mine block b1 [] >>=? fun head -> Format.eprintf "Entering `Proposal` voting period@."; Assert.check_voting_period_kind ~msg:__LOC__ ~block:(`Hash head) Voting_period.Proposal >>=? fun () -> - Mining.mine b2 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b2 [] >>=? fun head -> (* 1. Propose the 'demo' protocol as b1 (during the Proposal period) *) Protocol.proposals @@ -34,9 +35,9 @@ let run_change_to_demo_proto block ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstr [demo_protocol] >>=? fun op -> (* Mine blocks to switch to next vote period (Testing_vote) *) - Mining.mine ~operations:[op] b3 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b3 [op] >>=? fun head -> Format.eprintf "Entering `Testing_vote` voting period@."; - Mining.mine b4 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b4 [] >>=? fun head -> Assert.check_voting_period_kind ~msg:__LOC__ ~block:(`Hash head) Voting_period.Testing_vote >>=? fun () -> @@ -55,18 +56,18 @@ let run_change_to_demo_proto block ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstr all_accounts >>=? fun operations -> (* Mine blocks to switch to next vote period (Testing) *) - Mining.mine ~operations b5 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b5 operations >>=? fun head -> Format.eprintf "Entering `Testing` voting period@."; - Mining.mine b1 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b1 [] >>=? fun head -> Assert.check_voting_period_kind ~msg:__LOC__ ~block:(`Hash head) Voting_period.Testing >>=? fun () -> (* 3. Test the proposed protocol *) - (* Mine blocks to switch to next vote period (Promotion_vote) *) - Mining.mine b2 (`Hash head) >>=? fun head -> - Format.eprintf "Entering `Promotion_vote` voting period@."; - Mining.mine b3 (`Hash head) >>=? fun head -> + (* Mine blocks to switch to next vote period (Promote_vote) *) + Mining.mine (`Hash head) b2 [] >>=? fun head -> + Format.eprintf "Entering `Promote_vote` voting period@."; + Mining.mine (`Hash head) b3 [] >>=? fun head -> Assert.check_voting_period_kind ~msg:__LOC__ ~block:(`Hash head) Voting_period.Promotion_vote >>=? fun () -> @@ -76,10 +77,11 @@ let run_change_to_demo_proto block ({ b1 ; b2 ; b3 ; b4 ; b5 } : Account.bootstr (* Mine blocks to switch to end the vote cycle (back to Proposal) *) Format.eprintf "Switching to `demo` protocol@."; - Mining.mine ~operations b4 (`Hash head) >>=? fun head -> - Mining.mine ~proto_level:2 b5 (`Hash head) >>=? fun head -> + Mining.mine (`Hash head) b4 operations >>=? fun head -> + Mining.mine (`Hash head) b5 [] >>=? fun head -> - Assert.check_protocol ~msg:__LOC__ ~block:(`Hash head) demo_protocol >>=? fun () -> + Assert.check_protocol + ~msg:__LOC__ ~block:(`Hash head) demo_protocol >>=? fun () -> return (`Hash head)