diff --git a/etherlink/bin_node/lib_dev/durable_storage_path.ml b/etherlink/bin_node/lib_dev/durable_storage_path.ml index 54b7b70a90819d12c1ba18a32d9fdfabc1c3bc19..eafb4d51f3d9ec9da803b6cbaceb55c2da7b8c0d 100644 --- a/etherlink/bin_node/lib_dev/durable_storage_path.ml +++ b/etherlink/bin_node/lib_dev/durable_storage_path.ml @@ -152,6 +152,16 @@ module Code = struct let code code_hash = code_storage code_hash ^ code end +module Blueprint = struct + let blueprint blueprint_number = + EVM.make "/blueprints/" ^ Z.to_string blueprint_number + + let chunk ~blueprint_number ~chunk_index = + blueprint blueprint_number ^ "/" ^ string_of_int chunk_index + + let nb_chunks ~blueprint_number = blueprint blueprint_number ^ "/nb_chunks" +end + module Block = struct type number = Current | Nth of Z.t diff --git a/etherlink/bin_node/lib_dev/durable_storage_path.mli b/etherlink/bin_node/lib_dev/durable_storage_path.mli index 14097e1eb5168158b5079edafea244c5b1f4fdcb..4a64a3790e7aebe7a677c4acef85f34f75703682 100644 --- a/etherlink/bin_node/lib_dev/durable_storage_path.mli +++ b/etherlink/bin_node/lib_dev/durable_storage_path.mli @@ -89,6 +89,12 @@ module Code : sig val code : hash -> path end +module Blueprint : sig + val chunk : blueprint_number:Z.t -> chunk_index:int -> path + + val nb_chunks : blueprint_number:Z.t -> path +end + (** Paths related to blocks. *) module Block : sig (** Block number is either the current head or a specific height. *) diff --git a/etherlink/bin_node/lib_dev/evm_context.ml b/etherlink/bin_node/lib_dev/evm_context.ml index 556e65748b770fa22d1aa4bdfa7d9cd851b87798..678b380385dc255367edfdd4bef16f52642fe03a 100644 --- a/etherlink/bin_node/lib_dev/evm_context.ml +++ b/etherlink/bin_node/lib_dev/evm_context.ml @@ -868,11 +868,21 @@ module State = struct ctxt.session.next_blueprint_number in + let* sequencer = Durable_storage.sequencer (read_from_state evm_state) in + let*? chunks = + List.map_e + (fun chunk -> + let open Result_syntax in + let* chunk = Sequencer_blueprint.chunk_of_external_message chunk in + Sequencer_blueprint.check_signature sequencer chunk) + payload + in + let* try_apply = Misc.with_timing (fun time -> Lwt.return (time_processed := time)) (fun () -> - Evm_state.apply_blueprint + Evm_state.apply_unsigned_chunks ~native_execution_policy: ctxt.configuration.kernel_execution.native_execution_policy ~wasm_pvm_fallback:(not @@ List.is_empty delayed_transactions) @@ -880,7 +890,7 @@ module State = struct ~chain_family ~config evm_state - payload) + chunks) in match try_apply with diff --git a/etherlink/bin_node/lib_dev/evm_ro_context.ml b/etherlink/bin_node/lib_dev/evm_ro_context.ml index ccc8da31624464be44a2e01035fbee132575d245..87c092ca94da64c56ab6599943c68371bfe194a1 100644 --- a/etherlink/bin_node/lib_dev/evm_ro_context.ml +++ b/etherlink/bin_node/lib_dev/evm_ro_context.ml @@ -509,7 +509,16 @@ let replay ctxt ?(log_file = "replay") ?profile process_time := dt ; Lwt.return_unit) @@ fun () -> - Evm_state.apply_blueprint + let*? chunks = + List.map_e + (fun chunk -> + let open Result_syntax in + let+ chunk = Sequencer_blueprint.chunk_of_external_message chunk in + (* We are replaying, so we can assume the signature is correct *) + Sequencer_blueprint.unsafe_drop_signature chunk) + blueprint.blueprint.payload + in + Evm_state.apply_unsigned_chunks ~log_file ?profile ~data_dir:ctxt.data_dir @@ -517,7 +526,7 @@ let replay ctxt ?(log_file = "replay") ?profile ~config:(pvm_config ctxt) ~native_execution_policy:ctxt.native_execution_policy evm_state - blueprint.blueprint.payload + chunks in match apply_result with | Apply_success {block; evm_state} -> diff --git a/etherlink/bin_node/lib_dev/evm_state.ml b/etherlink/bin_node/lib_dev/evm_state.ml index 25d6a5599bc8fce8c047b1874408aab4d6411e18..0c5a99d777010863d7771e73acec73b18d8ed2bf 100644 --- a/etherlink/bin_node/lib_dev/evm_state.ml +++ b/etherlink/bin_node/lib_dev/evm_state.ml @@ -232,9 +232,9 @@ let current_block_height ~root evm_state = match current_block_number with | None -> (* No block has been created yet and we are waiting for genesis, - whose number will be [zero]. Since the semantics of [apply_blueprint] - is to verify the block height has been incremented once, we default to - [-1]. *) + whose number will be [zero]. Since the semantics of + [apply_unsigned_chunks] is to verify the block height has been + incremented once, we default to [-1]. *) return (Qty Z.(pred zero)) | Some current_block_number -> let (Qty current_block_number) = decode_number_le current_block_number in @@ -318,6 +318,39 @@ let execute_and_inspect ?wasm_pvm_fallback ~data_dir ?wasm_entrypoint ~config let*! values = List.map_p (fun key -> inspect evm_state key) keys in return values +let store_blueprint_chunk evm_state (chunk : Sequencer_blueprint.unsigned_chunk) + = + let open Lwt_result_syntax in + let (Qty number) = chunk.number in + let key = + Durable_storage_path.Blueprint.chunk + ~blueprint_number:number + ~chunk_index:chunk.chunk_index + in + let value = + (* We want to encode a [StoreBlueprint] (see blueprint_storage.rs in + kernel_latest/kernel). The [StoreBlueprint] has two variants, and we + want to store a [SequencerChunk] whose tag is 0. [Value ""] is the + RLP-encoded for 0. *) + Rlp.List [Rlp.Value (Bytes.of_string ""); Value chunk.value] + |> Rlp.encode |> Bytes.to_string + in + let*! evm_state = modify ~key ~value evm_state in + return evm_state + +let store_blueprint_chunks ~blueprint_number evm_state + (chunks : Sequencer_blueprint.unsigned_chunk list) = + let open Lwt_result_syntax in + let nb_chunks = List.length chunks in + let* evm_state = List.fold_left_es store_blueprint_chunk evm_state chunks in + let*! evm_state = + modify + ~key:(Durable_storage_path.Blueprint.nb_chunks ~blueprint_number) + ~value:(Z.to_bits (Z.of_int nb_chunks)) + evm_state + in + return evm_state + type apply_result = | Apply_success of { evm_state : t; @@ -325,17 +358,18 @@ type apply_result = } | Apply_failure -let apply_blueprint ?wasm_pvm_fallback ?log_file ?profile ~data_dir +let apply_unsigned_chunks ?wasm_pvm_fallback ?log_file ?profile ~data_dir ~chain_family ~config ~native_execution_policy evm_state - (blueprint : Blueprint_types.payload) = + (chunks : Sequencer_blueprint.unsigned_chunk list) = let open Lwt_result_syntax in let root = Durable_storage_path.root_of_chain_family chain_family in - let exec_inputs = - List.map - (function `External payload -> `Input ("\001" ^ payload)) - blueprint - in let*! (Qty before_height) = current_block_height ~root evm_state in + let* evm_state = + store_blueprint_chunks + ~blueprint_number:(Z.succ before_height) + evm_state + chunks + in let* evm_state = execute ~native_execution:(native_execution_policy = Configuration.Always) @@ -346,7 +380,7 @@ let apply_blueprint ?wasm_pvm_fallback ?log_file ?profile ~data_dir ~config ?log_file evm_state - exec_inputs + [] in let* block_hash = current_block_hash ~chain_family evm_state in let root = Durable_storage_path.root_of_chain_family chain_family in diff --git a/etherlink/bin_node/lib_dev/evm_state.mli b/etherlink/bin_node/lib_dev/evm_state.mli index 8ae6cc74b5191356f621f9a6d1f288d1f4465a48..3bdea3d4ffb71b53ff2a7a5b01adc0ba3151b3cb 100644 --- a/etherlink/bin_node/lib_dev/evm_state.mli +++ b/etherlink/bin_node/lib_dev/evm_state.mli @@ -95,15 +95,15 @@ type apply_result = } | Apply_failure -(** [apply_blueprint ~data-dir ~config state payload] applies the - blueprint [payload] on top of [evm_state]. If the payload produces +(** [apply_unsigned_chunks ~data-dir ~config state chunks] applies the + blueprint [chunks] on top of [evm_state]. If the payload produces a block, the new updated EVM state is returned along with the new block’s height. The [data-dir] is used to store the kernel logs in the {!kernel_logs_directory}. *) -val apply_blueprint : +val apply_unsigned_chunks : ?wasm_pvm_fallback:bool -> ?log_file:string -> ?profile:Configuration.profile_mode -> @@ -112,7 +112,7 @@ val apply_blueprint : config:Wasm_debugger.config -> native_execution_policy:Configuration.native_execution_policy -> t -> - Blueprint_types.payload -> + Sequencer_blueprint.unsigned_chunk list -> apply_result tzresult Lwt.t (** [flag_local_exec evm_state] adds a flag telling the kernel it is executed diff --git a/etherlink/bin_node/lib_dev/sequencer_blueprint.ml b/etherlink/bin_node/lib_dev/sequencer_blueprint.ml index 203eebe02692ed18991f5fde1582776cce034063..dde1fd767baf50c4bf76db584644bbceb6467821 100644 --- a/etherlink/bin_node/lib_dev/sequencer_blueprint.ml +++ b/etherlink/bin_node/lib_dev/sequencer_blueprint.ml @@ -239,6 +239,16 @@ let check_signature_opt sequencer chunk = in if correctly_signed then Some chunk else None +let check_signature sequencer chunk = + let open Result_syntax in + match check_signature_opt sequencer chunk with + | Some chunk -> return chunk.unsigned_chunk + | None -> + error_with + "Signature check failed for the provided blueprint with public key %a" + Signature.Public_key.pp + sequencer + let decode_inbox_payload sequencer (payload : Blueprint_types.payload) = List.filter_map (fun chunk -> diff --git a/etherlink/bin_node/lib_dev/sequencer_blueprint.mli b/etherlink/bin_node/lib_dev/sequencer_blueprint.mli index 3e5d33c89835ee57e1de25a9694a66976e29434b..10f6400addb952728b88ab76affd156a729f82b6 100644 --- a/etherlink/bin_node/lib_dev/sequencer_blueprint.mli +++ b/etherlink/bin_node/lib_dev/sequencer_blueprint.mli @@ -16,9 +16,16 @@ type unsigned_chunk = private { type t (** [unsafe_drop_signature chunk] gives back the content of [chunk] - {e without checking if its signature is valid}. *) + {e without checking if its signature is valid}. See {!check_signature} if + you want to get the unsigned content iff the signature is correct. *) val unsafe_drop_signature : t -> unsigned_chunk +(** [check_signature pubkey chunk] will return the (unsigned) chunk content in + the case that it was indeed signed for [pubkey]. Otherwise it returns an + error. See {!unsafe_drop_signature} if you want to skip the signature + verification and just get the unsigned content. *) +val check_signature : Signature.public_key -> t -> unsigned_chunk tzresult + val chunk_encoding : t Data_encoding.t (** [chunk_to_rlp chunk] encodes a chunk into its RLP format. *)