From 009d562e083f83fc69e4b845d275a62e973e4605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Henry?= Date: Tue, 5 Dec 2017 15:19:22 +0100 Subject: [PATCH] Shell: add `context` in block header --- lib_base/block_header.ml | 18 +++-- lib_base/block_header.mli | 1 + lib_client_base/client_node_rpcs.ml | 1 + lib_client_base/client_node_rpcs.mli | 1 + .../src/block_header_repr.ml | 6 +- lib_embedded_protocol_genesis/src/services.ml | 10 +-- lib_node_services/node_rpc_services.ml | 20 ++++-- lib_node_services/node_rpc_services.mli | 5 ++ lib_node_shell/node.ml | 8 ++- lib_node_shell/state.ml | 1 + lib_node_updater/updater.ml | 1 + lib_node_updater/updater.mli | 1 + lib_protocol_environment_sigs/v1/hash.mli | 3 + .../v1/tezos_data.mli | 1 + test/shell/test_state.ml | 71 ++++++++----------- test/shell/test_store.ml | 3 +- 16 files changed, 92 insertions(+), 59 deletions(-) diff --git a/lib_base/block_header.ml b/lib_base/block_header.ml index 978b02c79..15c9df101 100644 --- a/lib_base/block_header.ml +++ b/lib_base/block_header.ml @@ -15,27 +15,33 @@ type shell_header = { validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; } let shell_header_encoding = let open Data_encoding in conv (fun { level ; proto_level ; predecessor ; - timestamp ; validation_passes ; operations_hash ; fitness } -> + timestamp ; validation_passes ; operations_hash ; fitness ; + context } -> (level, proto_level, predecessor, - timestamp, validation_passes, operations_hash, fitness)) + timestamp, validation_passes, operations_hash, fitness, + context)) (fun (level, proto_level, predecessor, - timestamp, validation_passes, operations_hash, fitness) -> + timestamp, validation_passes, operations_hash, fitness, + context) -> { level ; proto_level ; predecessor ; - timestamp ; validation_passes ; operations_hash ; fitness }) - (obj7 + timestamp ; validation_passes ; operations_hash ; fitness ; + context }) + (obj8 (req "level" int32) (req "proto" uint8) (req "predecessor" Block_hash.encoding) (req "timestamp" Time.encoding) (req "validation_pass" uint8) (req "operations_hash" Operation_list_list_hash.encoding) - (req "fitness" Fitness.encoding)) + (req "fitness" Fitness.encoding) + (req "context" Context_hash.encoding)) type t = { shell: shell_header ; diff --git a/lib_base/block_header.mli b/lib_base/block_header.mli index bfe33a418..16d636bbb 100644 --- a/lib_base/block_header.mli +++ b/lib_base/block_header.mli @@ -15,6 +15,7 @@ type shell_header = { validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; } val shell_header_encoding: shell_header Data_encoding.t diff --git a/lib_client_base/client_node_rpcs.ml b/lib_client_base/client_node_rpcs.ml index 9160d29e4..c47313564 100644 --- a/lib_client_base/client_node_rpcs.ml +++ b/lib_client_base/client_node_rpcs.ml @@ -61,6 +61,7 @@ module Blocks = struct validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; data: MBytes.t ; operations: (Operation_hash.t * Operation.t) list list option ; protocol: Protocol_hash.t ; diff --git a/lib_client_base/client_node_rpcs.mli b/lib_client_base/client_node_rpcs.mli index 7034ca69f..a8e7867fc 100644 --- a/lib_client_base/client_node_rpcs.mli +++ b/lib_client_base/client_node_rpcs.mli @@ -89,6 +89,7 @@ module Blocks : sig validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; data: MBytes.t ; operations: (Operation_hash.t * Operation.t) list list option ; protocol: Protocol_hash.t ; diff --git a/lib_embedded_protocol_alpha/src/block_header_repr.ml b/lib_embedded_protocol_alpha/src/block_header_repr.ml index 14734cbe8..439cbe2a6 100644 --- a/lib_embedded_protocol_alpha/src/block_header_repr.ml +++ b/lib_embedded_protocol_alpha/src/block_header_repr.ml @@ -82,14 +82,16 @@ type error += let parse ({ shell = { level ; proto_level ; predecessor ; - timestamp ; fitness ; validation_passes ; operations_hash } ; + timestamp ; fitness ; validation_passes ; operations_hash ; + context } ; proto } : Block_header.t) : block_header tzresult = match Data_encoding.Binary.of_bytes signed_proto_header_encoding proto with | None -> Error [Cant_parse_proto_header] | Some (proto, signature) -> let shell = { Block_header.level ; proto_level ; predecessor ; - timestamp ; fitness ; validation_passes ; operations_hash } in + timestamp ; fitness ; validation_passes ; operations_hash ; + context } in Ok { shell ; proto ; signature } let parse_unsigned_proto_header bytes = diff --git a/lib_embedded_protocol_genesis/src/services.ml b/lib_embedded_protocol_genesis/src/services.ml index 2709c775c..31c4386dc 100644 --- a/lib_embedded_protocol_genesis/src/services.ml +++ b/lib_embedded_protocol_genesis/src/services.ml @@ -40,12 +40,12 @@ module Forge = struct ~input: (merge_objs (obj6 - (req "net_id" Net_id.encoding) (req "level" int32) (req "proto_level" uint8) (req "predecessor" Block_hash.encoding) (req "timestamp" Time.encoding) - (req "fitness" Fitness.encoding)) + (req "fitness" Fitness.encoding) + (req "context" Context_hash.encoding)) Data.Command.encoding) ~output: (obj1 (req "payload" bytes)) ~error: Data_encoding.empty @@ -66,11 +66,11 @@ let rpc_services : Updater.rpc_context RPC_directory.t = RPC_directory.register dir (Forge.block RPC_path.open_root) - (fun _ctxt () ((_net_id, level, proto_level, predecessor, - timestamp, fitness), command) -> + (fun _ctxt () ((level, proto_level, predecessor, + timestamp, fitness, context), command) -> let shell = { Block_header.level ; proto_level ; predecessor ; timestamp ; fitness ; validation_passes = 0 ; - operations_hash } in + operations_hash ; context } in let bytes = Data.Command.forge shell command in RPC_answer.return bytes) in dir diff --git a/lib_node_services/node_rpc_services.ml b/lib_node_services/node_rpc_services.ml index fa929e621..d8222f14c 100644 --- a/lib_node_services/node_rpc_services.ml +++ b/lib_node_services/node_rpc_services.ml @@ -68,6 +68,7 @@ module Blocks = struct validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; data: MBytes.t ; operations: (Operation_hash.t * Operation.t) list list option ; protocol: Protocol_hash.t ; @@ -82,21 +83,23 @@ module Blocks = struct conv (fun { hash ; net_id ; level ; proto_level ; predecessor ; fitness ; timestamp ; protocol ; - validation_passes ; operations_hash ; data ; + validation_passes ; operations_hash ; context ; data ; operations ; test_network } -> ((hash, net_id, operations, protocol, test_network), { Block_header.shell = { level ; proto_level ; predecessor ; - timestamp ; validation_passes ; operations_hash ; fitness } ; + timestamp ; validation_passes ; operations_hash ; fitness ; + context } ; proto = data })) (fun ((hash, net_id, operations, protocol, test_network), { Block_header.shell = { level ; proto_level ; predecessor ; - timestamp ; validation_passes ; operations_hash ; fitness } ; + timestamp ; validation_passes ; operations_hash ; fitness ; + context } ; proto = data }) -> { hash ; net_id ; level ; proto_level ; predecessor ; fitness ; timestamp ; protocol ; - validation_passes ; operations_hash ; data ; + validation_passes ; operations_hash ; context ; data ; operations ; test_network }) (dynamic_size (merge_objs @@ -214,6 +217,15 @@ module Blocks = struct ~error: Data_encoding.empty RPC_path.(block_path / "fitness") + let context = + RPC_service.post_service + ~description:"Returns the hash of the resulting context." + ~query: RPC_query.empty + ~input: empty + ~output: (obj1 (req "context" Context_hash.encoding)) + ~error: Data_encoding.empty + RPC_path.(block_path / "context") + let timestamp = RPC_service.post_service ~description:"Returns the block's timestamp." diff --git a/lib_node_services/node_rpc_services.mli b/lib_node_services/node_rpc_services.mli index 1c20fe938..287e2b357 100644 --- a/lib_node_services/node_rpc_services.mli +++ b/lib_node_services/node_rpc_services.mli @@ -37,6 +37,7 @@ module Blocks : sig validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; data: MBytes.t ; operations: (Operation_hash.t * Operation.t) list list option ; protocol: Protocol_hash.t ; @@ -75,6 +76,10 @@ module Blocks : sig ([ `POST ], unit, unit * block, unit, unit, MBytes.t list, unit) RPC_service.t + val context: + ([ `POST ], unit, + unit * block, unit, unit, + Context_hash.t, unit) RPC_service.t type operations_param = { contents: bool ; diff --git a/lib_node_shell/node.ml b/lib_node_shell/node.ml index 75d39e794..de9f4e899 100644 --- a/lib_node_shell/node.ml +++ b/lib_node_shell/node.ml @@ -149,6 +149,7 @@ module RPC = struct validation_passes: int ; (* uint8 *) operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; data: MBytes.t ; operations: (Operation_hash.t * Operation.t) list list option ; protocol: Protocol_hash.t ; @@ -174,6 +175,7 @@ module RPC = struct validation_passes = header.shell.validation_passes ; operations_hash = header.shell.operations_hash ; fitness = header.shell.fitness ; + context = header.shell.context ; data = header.proto ; operations = Some operations ; protocol ; @@ -281,6 +283,7 @@ module RPC = struct (fun ops -> Operation_list_hash.compute (List.map fst ops)) operations) ; operations = Some operations ; + context = Context_hash.zero ; data = MBytes.of_string "" ; net_id = head_net_id ; test_network ; @@ -356,6 +359,7 @@ module RPC = struct validation_passes = List.length operation_hashes ; operations_hash ; fitness ; + context = Context_hash.zero ; } ; proto = MBytes.create 0 ; } ; @@ -490,7 +494,7 @@ module RPC = struct (List.map fst r.Preapply_result.applied)) rs) in Prevalidation.end_prevalidation - validation_state >>=? fun { fitness ; context } -> + validation_state >>=? fun { fitness ; context ; message } -> let pred_shell_header = State.Block.shell_header predecessor in State.Block.protocol_hash predecessor >>= fun pred_protocol -> Context.get_protocol context >>= fun protocol -> @@ -499,6 +503,7 @@ module RPC = struct pred_shell_header.proto_level else ((pred_shell_header.proto_level + 1) mod 256) in + Context.commit ?message ~time:timestamp context >>= fun context -> let shell_header : Block_header.shell_header = { level = Int32.succ pred_shell_header.level ; proto_level ; @@ -507,6 +512,7 @@ module RPC = struct validation_passes = List.length rs ; operations_hash ; fitness ; + context ; } in return (shell_header, rs) diff --git a/lib_node_shell/state.ml b/lib_node_shell/state.ml index a18956079..33b5c94fc 100644 --- a/lib_node_shell/state.ml +++ b/lib_node_shell/state.ml @@ -159,6 +159,7 @@ module Locked_block = struct fitness = [] ; validation_passes = 0 ; operations_hash = Operation_list_list_hash.empty ; + context ; } in let header : Block_header.t = { shell ; proto = MBytes.create 0 } in Store.Block.Contents.store (store, genesis.block) diff --git a/lib_node_updater/updater.ml b/lib_node_updater/updater.ml index 4a9764c8f..8e4804a18 100644 --- a/lib_node_updater/updater.ml +++ b/lib_node_updater/updater.ml @@ -101,6 +101,7 @@ module Node_protocol_environment_sigs = struct and type Hash.Block_hash.t = Block_hash.t and type Hash.Operation_hash.t = Operation_hash.t and type Hash.Operation_list_list_hash.t = Operation_list_list_hash.t + and type Hash.Context_hash.t = Context_hash.t and type Context.t = Context.t and type Time.t = Time.t and type MBytes.t = MBytes.t diff --git a/lib_node_updater/updater.mli b/lib_node_updater/updater.mli index 63c452cdc..3a9398f4f 100644 --- a/lib_node_updater/updater.mli +++ b/lib_node_updater/updater.mli @@ -91,6 +91,7 @@ module Node_protocol_environment_sigs : sig and type Hash.Block_hash.t = Block_hash.t and type Hash.Operation_hash.t = Operation_hash.t and type Hash.Operation_list_list_hash.t = Operation_list_list_hash.t + and type Hash.Context_hash.t = Context_hash.t and type Context.t = Context.t and type Time.t = Time.t and type MBytes.t = MBytes.t diff --git a/lib_protocol_environment_sigs/v1/hash.mli b/lib_protocol_environment_sigs/v1/hash.mli index b8306049e..eec3f662a 100644 --- a/lib_protocol_environment_sigs/v1/hash.mli +++ b/lib_protocol_environment_sigs/v1/hash.mli @@ -131,4 +131,7 @@ module Operation_list_list_hash : (** Protocol versions / source hashes. *) module Protocol_hash : HASH +(** Commited conntext. *) +module Context_hash : HASH + module Net_id : HASH diff --git a/lib_protocol_environment_sigs/v1/tezos_data.mli b/lib_protocol_environment_sigs/v1/tezos_data.mli index 9c7655846..f632f3d90 100644 --- a/lib_protocol_environment_sigs/v1/tezos_data.mli +++ b/lib_protocol_environment_sigs/v1/tezos_data.mli @@ -85,6 +85,7 @@ module Block_header : sig validation_passes: int ; operations_hash: Operation_list_list_hash.t ; fitness: MBytes.t list ; + context: Context_hash.t ; } val shell_header_encoding: shell_header Data_encoding.t diff --git a/test/shell/test_state.ml b/test/shell/test_state.ml index 0d7b1fb27..19df00bfb 100644 --- a/test/shell/test_state.ml +++ b/test/shell/test_state.ml @@ -58,21 +58,6 @@ let operation op = op, Data_encoding.Binary.to_bytes Operation.encoding op -let block _state ?(operations = []) pred_hash pred name : Block_header.t = - let operations_hash = - Operation_list_list_hash.compute - [Operation_list_hash.compute operations] in - let fitness = incr_fitness pred.Block_header.shell.fitness in - let timestamp = incr_timestamp pred.shell.timestamp in - { shell = { - level = Int32.succ pred.shell.level ; - proto_level = pred.shell.proto_level ; - predecessor = pred_hash ; - validation_passes = 1 ; - timestamp ; operations_hash ; fitness } ; - proto = MBytes.of_string name ; - } - let equal_operation ?msg op1 op2 = let msg = Assert.format_msg msg in let eq op1 op2 = @@ -98,7 +83,7 @@ let equal_block ?msg st1 st2 = | Some st -> Block_hash.to_hex (Block_header.hash st) in Assert.equal ?msg ~prn ~eq st1 st2 -let block _state ?(operations = []) (pred: State.Block.t) name +let block _state ?(context = Context_hash.zero) ?(operations = []) (pred: State.Block.t) name : Block_header.t = let operations_hash = Operation_list_list_hash.compute @@ -110,37 +95,43 @@ let block _state ?(operations = []) (pred: State.Block.t) name proto_level = pred_header.proto_level ; predecessor = State.Block.hash pred ; validation_passes = 1 ; - timestamp ; operations_hash ; fitness } ; + timestamp ; operations_hash ; fitness ; + context } ; proto = MBytes.of_string name ; } let build_valid_chain state vtbl pred names = Lwt_list.fold_left_s (fun pred name -> - begin - let oph, op, _bytes = operation name in - let block = block state ~operations:[oph] pred name in - let hash = Block_header.hash block in - let pred_header = State.Block.header pred in - State.Block.context pred >>= fun predecessor_context -> + State.Block.context pred >>= fun predecessor_context -> + let rec attempt context = begin - Proto.begin_application - ~predecessor_context - ~predecessor_timestamp: pred_header.shell.timestamp - ~predecessor_fitness: pred_header.shell.fitness - block >>=? fun vstate -> - (* no operations *) - Proto.finalize_block vstate - end >>=? fun ctxt -> - State.Block.store state block [[op]] ctxt >>=? fun _vblock -> - State.Block.read state hash >>=? fun vblock -> - Hashtbl.add vtbl name vblock ; - return vblock - end >>= function - | Ok v -> Lwt.return v - | Error err -> - Error_monad.pp_print_error Format.err_formatter err ; - assert false) + let oph, op, _bytes = operation name in + let block = block ?context state ~operations:[oph] pred name in + let hash = Block_header.hash block in + let pred_header = State.Block.header pred in + begin + Proto.begin_application + ~predecessor_context + ~predecessor_timestamp: pred_header.shell.timestamp + ~predecessor_fitness: pred_header.shell.fitness + block >>=? fun vstate -> + (* no operations *) + Proto.finalize_block vstate + end >>=? fun ctxt -> + State.Block.store state block [[op]] ctxt >>=? fun _vblock -> + State.Block.read state hash >>=? fun vblock -> + Hashtbl.add vtbl name vblock ; + return vblock + end >>= function + | Ok v -> Lwt.return v + | Error [ State.Block.Inconsistent_hash (got, _) ] -> + (* Kind of a hack, but at least it tests idempotence to some extent. *) + attempt (Some got) + | Error err -> + Error_monad.pp_print_error Format.err_formatter err ; + assert false in + attempt None) pred names >>= fun _ -> Lwt.return () diff --git a/test/shell/test_store.ml b/test/shell/test_store.ml index 4fc9cfadd..c53e0b7d5 100644 --- a/test/shell/test_store.ml +++ b/test/shell/test_store.ml @@ -83,7 +83,8 @@ let lolblock ?(operations = []) header = validation_passes = Random.int 32 ; predecessor = genesis_block ; operations_hash ; fitness = [MBytes.of_string @@ string_of_int @@ String.length header; - MBytes.of_string @@ string_of_int @@ 12] } ; + MBytes.of_string @@ string_of_int @@ 12] ; + context = Context_hash.zero } ; proto = MBytes.of_string header ; } ; max_operations_ttl = 0 ;