diff --git a/src/lib_crawler/reorg.ml b/src/lib_crawler/reorg.ml index 0a7e6aad3066aa566c5a25e23b30bf66f0b49742..99ff139e2a003d44d7bd02ff941d06adbc99ed23 100644 --- a/src/lib_crawler/reorg.ml +++ b/src/lib_crawler/reorg.ml @@ -39,3 +39,15 @@ let encoding block_encoding = let map f {old_chain; new_chain} = {old_chain = List.map f old_chain; new_chain = List.map f new_chain} + +let map_es f {old_chain; new_chain} = + let open Lwt_result_syntax in + let* old_chain = List.map_es f old_chain in + let* new_chain = List.map_es f new_chain in + return {old_chain; new_chain} + +let map_ep f {old_chain; new_chain} = + let open Lwt_result_syntax in + let* old_chain = List.map_ep f old_chain + and* new_chain = List.map_ep f new_chain in + return {old_chain; new_chain} diff --git a/src/lib_crawler/reorg.mli b/src/lib_crawler/reorg.mli index 0cbc0f8e03231876aacdb7a599ca2c8446ec09c0..56dd0d1cf0598882e30dc7ed48c845c337b9da7b 100644 --- a/src/lib_crawler/reorg.mli +++ b/src/lib_crawler/reorg.mli @@ -38,3 +38,7 @@ val no_reorg : 'a t val encoding : 'a Data_encoding.t -> 'a t Data_encoding.t val map : ('a -> 'b) -> 'a t -> 'b t + +val map_es : ('a -> 'b tzresult Lwt.t) -> 'a t -> 'b t tzresult Lwt.t + +val map_ep : ('a -> 'b tzresult Lwt.t) -> 'a t -> 'b t tzresult Lwt.t diff --git a/src/lib_injector/injector_sigs.ml b/src/lib_injector/injector_sigs.ml index b3a1bd85ec8866179f304d8477449ed25029b4fa..f36dd6d3ae29ea381c114fb3b80facef89f3f8da 100644 --- a/src/lib_injector/injector_sigs.ml +++ b/src/lib_injector/injector_sigs.ml @@ -236,7 +236,7 @@ module type PROTOCOL_CLIENT = sig (** [time_until_next_block state block_header] computes the time until the block following [block_header], with respect to the current time. *) val time_until_next_block : - state -> Tezos_base.Block_header.t option -> Ptime.span + state -> Tezos_base.Block_header.shell_header option -> Ptime.span end (** Output signature for functor {!Injector_functor.Make}. *) @@ -332,7 +332,10 @@ module type S = sig operations. [header] must be provided for the [`Delay_block] strategy to compute the next block timestamp. *) val inject : - ?tags:tag list -> ?header:Tezos_base.Block_header.t -> unit -> unit Lwt.t + ?tags:tag list -> + ?header:Tezos_base.Block_header.shell_header -> + unit -> + unit Lwt.t (** Shutdown the injectors, waiting for the ongoing request to be processed. *) val shutdown : unit -> unit Lwt.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml index 237c288a0ba95874f9b40146bd79f885cdf0c329..e9fcb6c4e348a7b01f4e7e2ff8e645f5ebee342a 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment.ml @@ -175,8 +175,8 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct Some commitment else return_none - let process_head (node_ctxt : _ Node_context.t) ~predecessor Layer1.{level; _} - ctxt = + let process_head (node_ctxt : _ Node_context.t) ~predecessor + Layer1.{level; header = _; _} ctxt = let open Lwt_result_syntax in let current_level = Raw_level.of_int32_exn level in let* commitment = diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_sig.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_sig.ml index 96f2306dd8f084e7f35c0c07c79a5f17c21ddc4b..fccdc19b185443f2da39dbcfe443d4a0f6d8d7d1 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_sig.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/commitment_sig.ml @@ -50,7 +50,7 @@ module type S = sig val process_head : Node_context.rw -> predecessor:Block_hash.t -> - Layer1.head -> + Layer1.header -> Context.rw -> Protocol.Alpha_context.Sc_rollup.Commitment.Hash.t option tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml index 34b844f6a2eccb2b72207ce49c198d258a3222fe..8d1270b24f8eafe4ebf8a7e8111c77ba4eae699a 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml @@ -84,7 +84,7 @@ module Make (PVM : Pvm.S) = struct for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) - head ~source (operation : kind manager_operation) + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind successful_manager_operation_result) = let open Lwt_result_syntax in match (operation, result) with @@ -227,8 +227,8 @@ module Make (PVM : Pvm.S) = struct (_result : kind successful_manager_operation_result) = return_unit - let process_l1_operation (type kind) ~finalized node_ctxt head ~source - (operation : kind manager_operation) + let process_l1_operation (type kind) ~finalized node_ctxt + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind Apply_results.manager_operation_result) = let open Lwt_result_syntax in let is_for_my_rollup : type kind. kind manager_operation -> bool = function @@ -266,10 +266,11 @@ module Make (PVM : Pvm.S) = struct (* No action for non successful operations *) return_unit - let process_l1_block_operations ~finalized node_ctxt - (Layer1.{hash; _} as head) = + let process_l1_block_operations ~finalized node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in + let* block = + Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt head.hash + in let apply (type kind) accu ~source (operation : kind manager_operation) result = let open Lwt_result_syntax in @@ -289,46 +290,53 @@ module Make (PVM : Pvm.S) = struct in return_unit - let before_origination (node_ctxt : _ Node_context.t) Layer1.{level; _} = + let before_origination (node_ctxt : _ Node_context.t) (header : Layer1.header) + = let origination_level = Raw_level.to_int32 node_ctxt.genesis_info.level in - level < origination_level + header.level < origination_level let rec processed_finalized_block (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as block) = + (block : Layer1.header) = let open Lwt_result_syntax in let* last_finalized = Node_context.get_finalized_head_opt node_ctxt in let already_finalized = match last_finalized with - | Some finalized -> level <= Raw_level.to_int32 finalized.header.level + | Some finalized -> + block.level <= Raw_level.to_int32 finalized.header.level | None -> false in unless (already_finalized || before_origination node_ctxt block) @@ fun () -> - let* predecessor = Node_context.get_predecessor_opt node_ctxt block in + let* predecessor = + Node_context.get_predecessor_header_opt node_ctxt block + in let* () = Option.iter_es (processed_finalized_block node_ctxt) predecessor in - let*! () = Daemon_event.head_processing hash level ~finalized:true in + let*! () = + Daemon_event.head_processing block.hash block.level ~finalized:true + in let* () = process_l1_block_operations ~finalized:true node_ctxt block in - let* () = Node_context.mark_finalized_head node_ctxt hash in + let* () = Node_context.mark_finalized_head node_ctxt block.hash in return_unit - let previous_context (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{hash = _; level} = + let previous_context (node_ctxt : _ Node_context.t) + ~(predecessor : Layer1.header) = let open Lwt_result_syntax in - if level <= Raw_level.to_int32 node_ctxt.genesis_info.level then + if predecessor.level < Raw_level.to_int32 node_ctxt.genesis_info.level then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) else Node_context.checkout_context node_ctxt predecessor.Layer1.hash - let rec process_head (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as head) = + let rec process_head (node_ctxt : _ Node_context.t) (head : Layer1.header) = let open Lwt_result_syntax in - let* already_processed = Node_context.is_processed node_ctxt hash in + let* already_processed = Node_context.is_processed node_ctxt head.hash in unless (already_processed || before_origination node_ctxt head) @@ fun () -> - let*! () = Daemon_event.head_processing hash level ~finalized:false in - let* predecessor = Node_context.get_predecessor_opt node_ctxt head in + let*! () = + Daemon_event.head_processing head.hash head.level ~finalized:false + in + let* predecessor = Node_context.get_predecessor_header_opt node_ctxt head in match predecessor with | None -> (* Predecessor not available on the L1, which means the block does not @@ -336,14 +344,18 @@ module Make (PVM : Pvm.S) = struct return_unit | Some predecessor -> let* () = process_head node_ctxt predecessor in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level + node_ctxt + {Layer1.hash = head.hash; level = head.level} + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.process_head node_ctxt ~predecessor head in let* () = when_ (Node_context.dal_supported node_ctxt) @@ fun () -> - Dal_slots_tracker.process_head node_ctxt head + Dal_slots_tracker.process_head node_ctxt (Layer1.head_of_header head) in let* () = process_l1_block_operations ~finalized:false node_ctxt head in (* Avoid storing and publishing commitments if the head is not final. *) @@ -365,7 +377,7 @@ module Make (PVM : Pvm.S) = struct head ctxt in - let level = Raw_level.of_int32_exn level in + let level = Raw_level.of_int32_exn head.level in let* previous_commitment_hash = if level = node_ctxt.genesis_info.Sc_rollup.Commitment.level then (* Previous commitment for rollup genesis is itself. *) @@ -377,7 +389,7 @@ module Make (PVM : Pvm.S) = struct let header = Sc_rollup_block. { - block_hash = hash; + block_hash = head.hash; level; predecessor = predecessor.hash; commitment_hash; @@ -391,7 +403,7 @@ module Make (PVM : Pvm.S) = struct Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* finalized_block, _ = - Node_context.nth_predecessor + Node_context.nth_predecessor_header node_ctxt node_ctxt.block_finality_time head @@ -399,7 +411,7 @@ module Make (PVM : Pvm.S) = struct let* () = processed_finalized_block node_ctxt finalized_block in let* () = Node_context.save_l2_head node_ctxt l2_block in let*! () = - Daemon_event.new_head_processed hash (Raw_level.to_int32 level) + Daemon_event.new_head_processed head.hash (Raw_level.to_int32 level) in return_unit @@ -407,7 +419,7 @@ module Make (PVM : Pvm.S) = struct also processes any missing blocks that were not processed. Every time a head is processed we also process head~2 as finalized (which may recursively imply the processing of head~3, etc). *) - let on_layer_1_head node_ctxt head = + let on_layer_1_head node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in let* old_head = Node_context.last_processed_head_opt node_ctxt in let old_head = @@ -427,8 +439,9 @@ module Make (PVM : Pvm.S) = struct in `Level (Int32.pred origination_level) in + let stripped_head = Layer1.head_of_header head in let*! reorg = - Node_context.get_tezos_reorg_for_new_head node_ctxt old_head head + Node_context.get_tezos_reorg_for_new_head node_ctxt old_head stripped_head in let*? reorg = match reorg with @@ -444,30 +457,33 @@ module Make (PVM : Pvm.S) = struct trace -> (* The reorganization could not be computed entirely because of missing info on the Layer 1. We fallback to a recursive process_head. *) - Ok {Reorg.no_reorg with new_chain = [head]} + Ok {Reorg.no_reorg with new_chain = [stripped_head]} | _ -> reorg in (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 Rollback state information on reorganization, i.e. for reorg.old_chain. *) - let* new_head = Layer1.fetch_tezos_block node_ctxt.cctxt head.Layer1.hash in - let header = - Block_header.( - raw - { - shell = new_head.header.shell; - protocol_data = new_head.header.protocol_data; - }) - in let*! () = Daemon_event.processing_heads_iteration reorg.new_chain in - let* () = List.iter_es (process_head node_ctxt) reorg.new_chain in + let get_header Layer1.{hash; level} = + if Block_hash.equal hash head.hash then return head + else + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + in + let* () = + List.iter_es + (fun block -> + let* header = get_header block in + process_head node_ctxt header) + reorg.new_chain + in let* () = Components.Commitment.Publisher.publish_commitments () in let* () = Components.Commitment.Publisher.cement_commitments () in let*! () = Daemon_event.new_heads_processed reorg.new_chain in - let* () = Components.Refutation_coordinator.process head in + let* () = Components.Refutation_coordinator.process stripped_head in let* () = Components.Batcher.batch () in - let* () = Components.Batcher.new_head head in - let*! () = Injector.inject ~header () in + let* () = Components.Batcher.new_head stripped_head in + let*! () = Injector.inject ~header:head.header () in return_unit let daemonize (node_ctxt : _ Node_context.t) = @@ -482,7 +498,9 @@ module Make (PVM : Pvm.S) = struct let*! () = message "Shutting down Commitment Publisher@." in let*! () = Components.Commitment.Publisher.shutdown () in Layer1.iter_heads node_ctxt.l1_ctxt @@ fun head -> - let* () = Components.Refutation_coordinator.process head in + let* () = + Components.Refutation_coordinator.process (Layer1.head_of_header head) + in let*! () = Injector.inject () in return_unit @@ -613,16 +631,17 @@ module Make (PVM : Pvm.S) = struct (** Same as {!process_head} but only builds and stores the L2 block corresponding to [messages]. It is used by the unit tests to build an L2 chain. *) - let process_messages (node_ctxt : _ Node_context.t) ~predecessor - ~predecessor_timestamp head messages = + let process_messages (node_ctxt : _ Node_context.t) ~predecessor head + messages = let open Lwt_result_syntax in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level node_ctxt (Layer1.head_of_header head) + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.Internal_for_tests.process_messages node_ctxt ~predecessor - ~predecessor_timestamp ~level:head.level messages in diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml index 6043df1b50041ccf0698c8662140d6cd7d93cdc7..7a3806c0c037182c1b05ce80ce3099f7a8753c64 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml @@ -78,11 +78,7 @@ let get_messages Node_context.{cctxt; _} head = block.operations {apply; apply_internal}) in - let ({predecessor; _} : Block_header.shell_header) = block.header.shell in - let* {timestamp = predecessor_timestamp; _} = - Layer1.fetch_tezos_shell_header cctxt predecessor - in - return (List.rev rev_messages, predecessor_timestamp, predecessor) + return (List.rev rev_messages) let same_inbox_as_layer_1 node_ctxt head_hash inbox = let open Lwt_result_syntax in @@ -120,10 +116,13 @@ let add_messages ~predecessor_timestamp ~predecessor inbox messages = inbox, messages_with_protocol_internal_messages ) -let process_messages (node_ctxt : _ Node_context.t) ~predecessor - ~predecessor_timestamp ~level messages = +let process_messages (node_ctxt : _ Node_context.t) + ~(predecessor : Layer1.header) ~level messages = let open Lwt_result_syntax in - let* inbox = Node_context.inbox_of_head node_ctxt predecessor in + let* inbox = + Node_context.inbox_of_head node_ctxt (Layer1.head_of_header predecessor) + in + let predecessor_timestamp = predecessor.header.timestamp in let inbox_metrics = Metrics.Inbox.metrics in Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; let* ( _messages_history, @@ -148,22 +147,22 @@ let process_messages (node_ctxt : _ Node_context.t) ~predecessor return (inbox_hash, inbox, witness_hash, messages_with_protocol_internal_messages) -let process_head (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{level; hash = head_hash} = +let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) + (head : Layer1.header) = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in - if level >= first_inbox_level then ( + if head.level >= first_inbox_level then (* We compute the inbox of this block using the inbox of its predecessor. That way, the computation of inboxes is robust to chain reorganization. *) - let* collected_messages, predecessor_timestamp, predecessor_hash = - get_messages node_ctxt head_hash - in - assert (Block_hash.(predecessor.Layer1.hash = predecessor_hash)) ; + let* collected_messages = get_messages node_ctxt head.hash in let*! () = - Inbox_event.get_messages head_hash level (List.length collected_messages) + Inbox_event.get_messages + head.hash + head.level + (List.length collected_messages) in let* (( _inbox_hash, inbox, @@ -172,12 +171,11 @@ let process_head (node_ctxt : _ Node_context.t) ~predecessor process_messages node_ctxt ~predecessor - ~predecessor_timestamp - ~level + ~level:head.level collected_messages in - let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - return res) + let* () = same_inbox_as_layer_1 node_ctxt head.hash inbox in + return res else let* inbox = Node_context.genesis_inbox node_ctxt in return diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli index 1226a2a198003d6f10bc8eabe4055bebcb5aa334..9f5fd133ee79e64d61df8ba5fe660bbc7448435d 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli @@ -42,8 +42,8 @@ open Sc_rollup [operations] of the [head] block. *) val process_head : Node_context.rw -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> (Sc_rollup.Inbox.Hash.t * Sc_rollup.Inbox.t * Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t @@ -83,8 +83,7 @@ val payloads_history_of_messages : module Internal_for_tests : sig val process_messages : Node_context.rw -> - predecessor:Layer1.head -> - predecessor_timestamp:Timestamp.time -> + predecessor:Layer1.header -> level:int32 -> Sc_rollup.Inbox_message.t list -> (Sc_rollup.Inbox.Hash.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml index 1c10f32696dd8b6162199b30d38753881a86c1b9..bc0d0cf333a861982d4f2e52e9e56c222505bbf5 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/injector.ml @@ -415,7 +415,7 @@ module Proto_client = struct Data_encoding.Binary.to_bytes_exn Operation.encoding op let time_until_next_block (node_ctxt : Node_context.ro) - (header : Tezos_base.Block_header.t option) = + (header : Tezos_base.Block_header.shell_header option) = let open Result_syntax in let Constants.Parametric.{minimal_block_delay; delay_increment_per_round; _} = @@ -432,12 +432,10 @@ module Proto_client = struct ~first_round_duration:minimal_block_delay ~delay_increment_per_round in - let* predecessor_round = - Fitness.round_from_raw header.shell.fitness - in + let* predecessor_round = Fitness.round_from_raw header.fitness in Round.timestamp_of_round durations - ~predecessor_timestamp:header.shell.timestamp + ~predecessor_timestamp:header.timestamp ~predecessor_round ~round:Round.zero in @@ -447,7 +445,7 @@ module Proto_client = struct ~default: (WithExceptions.Result.get_ok ~loc:__LOC__ - Timestamp.(header.shell.timestamp +? minimal_block_delay)) + Timestamp.(header.timestamp +? minimal_block_delay)) in Ptime.diff (Time.System.of_protocol_exn next_level_timestamp) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml index 381cd852ffd7a31a203694ec728b2a5d094f26c9..132b6b921519fce21c6f7ddc3b3da5d9da706046 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml @@ -38,8 +38,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t @@ -165,14 +165,19 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) - let process_head (node_ctxt : _ Node_context.t) ctxt ~predecessor head - inbox_messages = + let process_head (node_ctxt : _ Node_context.t) ctxt + ~(predecessor : Layer1.header) (head : Layer1.header) inbox_messages = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in if head.Layer1.level >= first_inbox_level then - transition_pvm node_ctxt ctxt predecessor head inbox_messages + transition_pvm + node_ctxt + ctxt + (Layer1.head_of_header predecessor) + (Layer1.head_of_header head) + inbox_messages else if head.Layer1.level = Raw_level.to_int32 node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli index 3bc4a0aa7aeeb0ec00405fe6dd0dc0fd8d3d1e08..99dd231a3ab9c78206f2babbaa708bafd1e96a4d 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli @@ -45,8 +45,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml index fe5776cb19d136698ca62c3ce2adf3d88db1e895..c551b9252139f7f7e020d028753da6d962c444cc 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml @@ -58,14 +58,31 @@ let () = *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + +let header_encoding = + let open Data_encoding in + conv + (fun {hash; level = _; header} -> (hash, header)) + (fun (hash, header) -> {hash; level = header.level; header}) + (merge_objs + (obj1 (req "hash" Block_hash.encoding)) + Block_header.shell_header_encoding) + type head = {hash : Block_hash.t; level : int32} let head_encoding = - Data_encoding.( - conv - (fun {hash; level} -> (hash, level)) - (fun (hash, level) -> {hash; level}) - (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32))) + let open Data_encoding in + conv + (fun {hash; level} -> (hash, level)) + (fun (hash, level) -> {hash; level}) + (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32)) + +let head_of_header {hash; level; header = _} = {hash; level} module Blocks_cache = Aches_lwt.Lache.Make_option @@ -73,13 +90,23 @@ module Blocks_cache = type blocks_cache = Alpha_block_services.block_info Blocks_cache.t +type headers_cache = Block_header.shell_header Blocks_cache.t + (** Global blocks cache for the smart rollup node. *) let blocks_cache : blocks_cache = Blocks_cache.create 32 +(** Global block headers cache for the smart rollup node. *) +let headers_cache : headers_cache = Blocks_cache.create 32 + include Octez_crawler.Layer_1 +let cache_shell_header hash header = + Blocks_cache.put headers_cache hash (Lwt.return_some header) + let iter_heads l1_ctxt f = - iter_heads l1_ctxt @@ fun (hash, {shell = {level; _}; _}) -> f {hash; level} + iter_heads l1_ctxt @@ fun (hash, {shell = {level; _} as header; _}) -> + cache_shell_header hash header ; + f {hash; level; header} (** @@ -111,12 +138,7 @@ let fetch_tezos_shell_header cctxt hash = | Ok shell_header -> return_some shell_header in let+ shell_header = - let res = - Blocks_cache.bind blocks_cache hash (function - | Some block_info -> Lwt.return_some block_info.header.shell - | None -> Lwt.return_none) - in - match res with Some lwt -> lwt | None -> fetch hash + Blocks_cache.bind_or_put headers_cache hash fetch Lwt.return in match (shell_header, !errors) with | None, None -> diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli index c9697e4fe5c0eae830ca054d187a4db7489a3f50..845e97a4f1c3e6e01fd3155da66aeed2be329e10 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli @@ -30,10 +30,20 @@ subscribing to the head monitoring RPC offered by the Tezos node. *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + type head = {hash : Block_hash.t; level : int32} +val header_encoding : header Data_encoding.t + val head_encoding : head Data_encoding.t +val head_of_header : header -> head + include module type of Octez_crawler.Layer_1 (* TODO: https://gitlab.com/tezos/tezos/-/issues/3311 @@ -43,10 +53,14 @@ include module type of Octez_crawler.Layer_1 chain. In case of a disconnection with the layer 1 node, it reconnects automatically. If [f] returns an error (other than a disconnection) it, [iter_heads] terminates and returns the error. *) -val iter_heads : t -> (head -> unit tzresult Lwt.t) -> unit tzresult Lwt.t +val iter_heads : t -> (header -> unit tzresult Lwt.t) -> unit tzresult Lwt.t (** {2 Helpers } *) +(** [cache_shell_header hash header] saves in the local cache the shell [header] + for the block [hash]. *) +val cache_shell_header : Block_hash.t -> Block_header.shell_header -> unit + (** [fetch_tezos_shell_header cctxt hash] returns the block shell header of [hash]. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml index 6912eb61d1804c96227aad11b8b499d53c4605b5..fd241d16ee474e15ad0a0c2c40fb2690603c8a08 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml @@ -457,12 +457,24 @@ let nth_predecessor node_ctxt n block = in (head_of_block_level res, List.map head_of_block_level preds) +let header_of_head node_ctxt Layer1.{hash; level} = + let open Lwt_result_syntax in + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + +let nth_predecessor_header node_ctxt n block = + let open Lwt_result_syntax in + let* res, preds = nth_predecessor node_ctxt n (Layer1.head_of_header block) in + let* res = header_of_head node_ctxt res + and* preds = List.map_ep (header_of_head node_ctxt) preds in + return (res, preds) + let get_tezos_reorg_for_new_head node_ctxt old_head new_head = let open Lwt_result_syntax in let old_head = match old_head with | `Level l -> `Level l - | `Head h -> `Head (block_level_of_head h) + | `Head Layer1.{hash; level} -> `Head (hash, level) in let+ reorg = Layer1.get_tezos_reorg_for_new_head @@ -483,6 +495,16 @@ let get_predecessor node_ctxt head = let+ res = get_predecessor node_ctxt (block_level_of_head head) in head_of_block_level res +let get_predecessor_header_opt node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor_opt node_ctxt (Layer1.head_of_header head) in + Option.map_es (header_of_head node_ctxt) res + +let get_predecessor_header node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor node_ctxt (Layer1.head_of_header head) in + header_of_head node_ctxt res + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4128 Unit test the function tick_search. *) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli index 35be8cb4316c1c5b079db9abbad4cef802517fb8..eead4d447f69ebba67fdcebd6c75f11123c24354 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.mli @@ -224,6 +224,21 @@ val get_predecessor : _ t -> Layer1.head -> Layer1.head tzresult Lwt.t val nth_predecessor : _ t -> int -> Layer1.head -> (Layer1.head * Layer1.head list) tzresult Lwt.t +(** Same as {!get_predecessor_opt} with headers. *) +val get_predecessor_header_opt : + _ t -> Layer1.header -> Layer1.header option tzresult Lwt.t + +(** Same as {!get_predecessor} with headers. *) +val get_predecessor_header : + _ t -> Layer1.header -> Layer1.header tzresult Lwt.t + +(** Same as {!nth_predecessor} with headers. *) +val nth_predecessor_header : + _ t -> + int -> + Layer1.header -> + (Layer1.header * Layer1.header list) tzresult Lwt.t + (** [get_tezos_reorg_for_new_head node_ctxt old_head new_head] returns the L1 reorganization between [old_head] and [new_head]. *) val get_tezos_reorg_for_new_head : diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml index 5a5b48da0d7ab40e1d5ebb65dfbb5c551cf5008b..85fb36ce7ef4b40c27698c14a09c3f95ee0772d1 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml @@ -180,6 +180,24 @@ module Make (PVM : Pvm.S) = struct in return_unit + let head_of_level ~predecessor level = + let hash = block_hash_of_level level in + let timestamp = Time.Protocol.of_seconds (Int64.of_int32 level) in + let header : Block_header.shell_header = + { + level; + predecessor; + timestamp; + (* dummy values below *) + proto_level = 0; + validation_passes = 3; + operations_hash = Tezos_crypto.Hashed.Operation_list_list_hash.zero; + fitness = []; + context = Tezos_crypto.Hashed.Context_hash.zero; + } + in + {Layer1.hash; level; header} + let append_l2_block (node_ctxt : _ Node_context.t) messages = let open Lwt_result_syntax in let* predecessor_l2_block = @@ -191,25 +209,18 @@ module Make (PVM : Pvm.S) = struct | None -> failwith "No genesis block, please add one with add_l2_genesis_block" in - let level = - Raw_level.(to_int32 @@ succ predecessor_l2_block.header.level) - in - let hash = block_hash_of_level level in + let pred_level = Raw_level.to_int32 predecessor_l2_block.header.level in let predecessor = - Layer1. - { - hash = predecessor_l2_block.header.block_hash; - level = Raw_level.to_int32 predecessor_l2_block.header.level; - } + head_of_level + ~predecessor:predecessor_l2_block.header.predecessor + pred_level in - let head = Layer1.{hash; level} in - let predecessor_timestamp = - Time.Protocol.of_seconds (Int64.of_int32 level) + let head = + head_of_level ~predecessor:predecessor.hash (Int32.succ pred_level) in Daemon.Internal_for_tests.process_messages node_ctxt ~predecessor - ~predecessor_timestamp head messages end diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/commitment.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/commitment.ml index 237c288a0ba95874f9b40146bd79f885cdf0c329..e9fcb6c4e348a7b01f4e7e2ff8e645f5ebee342a 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/commitment.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/commitment.ml @@ -175,8 +175,8 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct Some commitment else return_none - let process_head (node_ctxt : _ Node_context.t) ~predecessor Layer1.{level; _} - ctxt = + let process_head (node_ctxt : _ Node_context.t) ~predecessor + Layer1.{level; header = _; _} ctxt = let open Lwt_result_syntax in let current_level = Raw_level.of_int32_exn level in let* commitment = diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/commitment_sig.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/commitment_sig.ml index 96f2306dd8f084e7f35c0c07c79a5f17c21ddc4b..fccdc19b185443f2da39dbcfe443d4a0f6d8d7d1 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/commitment_sig.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/commitment_sig.ml @@ -50,7 +50,7 @@ module type S = sig val process_head : Node_context.rw -> predecessor:Block_hash.t -> - Layer1.head -> + Layer1.header -> Context.rw -> Protocol.Alpha_context.Sc_rollup.Commitment.Hash.t option tzresult Lwt.t diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml index c57632ef578958b2878b80c6a188a74cba8d901d..c68f10befc14e93d80e29ce5d448d00ba3d19fff 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml @@ -84,7 +84,7 @@ module Make (PVM : Pvm.S) = struct for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) - head ~source (operation : kind manager_operation) + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind successful_manager_operation_result) = let open Lwt_result_syntax in match (operation, result) with @@ -227,8 +227,8 @@ module Make (PVM : Pvm.S) = struct (_result : kind successful_manager_operation_result) = return_unit - let process_l1_operation (type kind) ~finalized node_ctxt head ~source - (operation : kind manager_operation) + let process_l1_operation (type kind) ~finalized node_ctxt + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind Apply_results.manager_operation_result) = let open Lwt_result_syntax in let is_for_my_rollup : type kind. kind manager_operation -> bool = function @@ -263,10 +263,11 @@ module Make (PVM : Pvm.S) = struct (* No action for non successful operations *) return_unit - let process_l1_block_operations ~finalized node_ctxt - (Layer1.{hash; _} as head) = + let process_l1_block_operations ~finalized node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in + let* block = + Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt head.hash + in let apply (type kind) accu ~source (operation : kind manager_operation) result = let open Lwt_result_syntax in @@ -286,46 +287,53 @@ module Make (PVM : Pvm.S) = struct in return_unit - let before_origination (node_ctxt : _ Node_context.t) Layer1.{level; _} = + let before_origination (node_ctxt : _ Node_context.t) (header : Layer1.header) + = let origination_level = Raw_level.to_int32 node_ctxt.genesis_info.level in - level < origination_level + header.level < origination_level let rec processed_finalized_block (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as block) = + (block : Layer1.header) = let open Lwt_result_syntax in let* last_finalized = Node_context.get_finalized_head_opt node_ctxt in let already_finalized = match last_finalized with - | Some finalized -> level <= Raw_level.to_int32 finalized.header.level + | Some finalized -> + block.level <= Raw_level.to_int32 finalized.header.level | None -> false in unless (already_finalized || before_origination node_ctxt block) @@ fun () -> - let* predecessor = Node_context.get_predecessor_opt node_ctxt block in + let* predecessor = + Node_context.get_predecessor_header_opt node_ctxt block + in let* () = Option.iter_es (processed_finalized_block node_ctxt) predecessor in - let*! () = Daemon_event.head_processing hash level ~finalized:true in + let*! () = + Daemon_event.head_processing block.hash block.level ~finalized:true + in let* () = process_l1_block_operations ~finalized:true node_ctxt block in - let* () = Node_context.mark_finalized_head node_ctxt hash in + let* () = Node_context.mark_finalized_head node_ctxt block.hash in return_unit - let previous_context (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{hash = _; level} = + let previous_context (node_ctxt : _ Node_context.t) + ~(predecessor : Layer1.header) = let open Lwt_result_syntax in - if level <= Raw_level.to_int32 node_ctxt.genesis_info.level then + if predecessor.level < Raw_level.to_int32 node_ctxt.genesis_info.level then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) else Node_context.checkout_context node_ctxt predecessor.Layer1.hash - let rec process_head (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as head) = + let rec process_head (node_ctxt : _ Node_context.t) (head : Layer1.header) = let open Lwt_result_syntax in - let* already_processed = Node_context.is_processed node_ctxt hash in + let* already_processed = Node_context.is_processed node_ctxt head.hash in unless (already_processed || before_origination node_ctxt head) @@ fun () -> - let*! () = Daemon_event.head_processing hash level ~finalized:false in - let* predecessor = Node_context.get_predecessor_opt node_ctxt head in + let*! () = + Daemon_event.head_processing head.hash head.level ~finalized:false + in + let* predecessor = Node_context.get_predecessor_header_opt node_ctxt head in match predecessor with | None -> (* Predecessor not available on the L1, which means the block does not @@ -333,14 +341,18 @@ module Make (PVM : Pvm.S) = struct return_unit | Some predecessor -> let* () = process_head node_ctxt predecessor in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level + node_ctxt + {Layer1.hash = head.hash; level = head.level} + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.process_head node_ctxt ~predecessor head in let* () = when_ (Node_context.dal_supported node_ctxt) @@ fun () -> - Dal_slots_tracker.process_head node_ctxt head + Dal_slots_tracker.process_head node_ctxt (Layer1.head_of_header head) in let* () = process_l1_block_operations ~finalized:false node_ctxt head in (* Avoid storing and publishing commitments if the head is not final. *) @@ -362,7 +374,7 @@ module Make (PVM : Pvm.S) = struct head ctxt in - let level = Raw_level.of_int32_exn level in + let level = Raw_level.of_int32_exn head.level in let* previous_commitment_hash = if level = node_ctxt.genesis_info.Sc_rollup.Commitment.level then (* Previous commitment for rollup genesis is itself. *) @@ -374,7 +386,7 @@ module Make (PVM : Pvm.S) = struct let header = Sc_rollup_block. { - block_hash = hash; + block_hash = head.hash; level; predecessor = predecessor.hash; commitment_hash; @@ -388,7 +400,7 @@ module Make (PVM : Pvm.S) = struct Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* finalized_block, _ = - Node_context.nth_predecessor + Node_context.nth_predecessor_header node_ctxt node_ctxt.block_finality_time head @@ -396,7 +408,7 @@ module Make (PVM : Pvm.S) = struct let* () = processed_finalized_block node_ctxt finalized_block in let* () = Node_context.save_l2_head node_ctxt l2_block in let*! () = - Daemon_event.new_head_processed hash (Raw_level.to_int32 level) + Daemon_event.new_head_processed head.hash (Raw_level.to_int32 level) in return_unit @@ -404,7 +416,7 @@ module Make (PVM : Pvm.S) = struct also processes any missing blocks that were not processed. Every time a head is processed we also process head~2 as finalized (which may recursively imply the processing of head~3, etc). *) - let on_layer_1_head node_ctxt head = + let on_layer_1_head node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in let* old_head = Node_context.last_processed_head_opt node_ctxt in let old_head = @@ -424,8 +436,9 @@ module Make (PVM : Pvm.S) = struct in `Level (Int32.pred origination_level) in + let stripped_head = Layer1.head_of_header head in let*! reorg = - Node_context.get_tezos_reorg_for_new_head node_ctxt old_head head + Node_context.get_tezos_reorg_for_new_head node_ctxt old_head stripped_head in let*? reorg = match reorg with @@ -441,30 +454,33 @@ module Make (PVM : Pvm.S) = struct trace -> (* The reorganization could not be computed entirely because of missing info on the Layer 1. We fallback to a recursive process_head. *) - Ok {Reorg.no_reorg with new_chain = [head]} + Ok {Reorg.no_reorg with new_chain = [stripped_head]} | _ -> reorg in (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 Rollback state information on reorganization, i.e. for reorg.old_chain. *) - let* new_head = Layer1.fetch_tezos_block node_ctxt.cctxt head.Layer1.hash in - let header = - Block_header.( - raw - { - shell = new_head.header.shell; - protocol_data = new_head.header.protocol_data; - }) - in let*! () = Daemon_event.processing_heads_iteration reorg.new_chain in - let* () = List.iter_es (process_head node_ctxt) reorg.new_chain in + let get_header Layer1.{hash; level} = + if Block_hash.equal hash head.hash then return head + else + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + in + let* () = + List.iter_es + (fun block -> + let* header = get_header block in + process_head node_ctxt header) + reorg.new_chain + in let* () = Components.Commitment.Publisher.publish_commitments () in let* () = Components.Commitment.Publisher.cement_commitments () in let*! () = Daemon_event.new_heads_processed reorg.new_chain in - let* () = Components.Refutation_coordinator.process head in + let* () = Components.Refutation_coordinator.process stripped_head in let* () = Components.Batcher.batch () in - let* () = Components.Batcher.new_head head in - let*! () = Injector.inject ~header () in + let* () = Components.Batcher.new_head stripped_head in + let*! () = Injector.inject ~header:head.header () in return_unit let daemonize (node_ctxt : _ Node_context.t) = @@ -479,7 +495,9 @@ module Make (PVM : Pvm.S) = struct let*! () = message "Shutting down Commitment Publisher@." in let*! () = Components.Commitment.Publisher.shutdown () in Layer1.iter_heads node_ctxt.l1_ctxt @@ fun head -> - let* () = Components.Refutation_coordinator.process head in + let* () = + Components.Refutation_coordinator.process (Layer1.head_of_header head) + in let*! () = Injector.inject () in return_unit @@ -606,16 +624,17 @@ module Make (PVM : Pvm.S) = struct corresponding to [messages]. It is used by the unit tests to build an L2 chain. *) let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block - ~predecessor ~predecessor_timestamp head messages = + ~predecessor head messages = let open Lwt_result_syntax in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level node_ctxt (Layer1.head_of_header head) + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.Internal_for_tests.process_messages node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp ~level:head.level messages in diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml index 39e235d2a6b8f05851770a75388f78a74fd91387..fcafe33af0b04b5c27aca0199a8cd7526b173f7c 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml @@ -78,22 +78,7 @@ let get_messages Node_context.{cctxt; _} head = block.operations {apply; apply_internal}) in - let ({predecessor; _} : Block_header.shell_header) = block.header.shell in - let* { - timestamp = predecessor_timestamp; - proto_level = predecessor_proto_level; - _; - } = - Layer1.fetch_tezos_shell_header cctxt predecessor - in - let is_migration_block = - block.header.shell.proto_level <> predecessor_proto_level - in - return - ( is_migration_block, - List.rev rev_messages, - predecessor_timestamp, - predecessor ) + return (List.rev rev_messages) let same_inbox_as_layer_1 node_ctxt head_hash inbox = let open Lwt_result_syntax in @@ -134,9 +119,12 @@ let add_messages ~is_migration_block ~predecessor_timestamp ~predecessor inbox messages_with_protocol_internal_messages ) let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block - ~predecessor ~predecessor_timestamp ~level messages = + ~(predecessor : Layer1.header) ~level messages = let open Lwt_result_syntax in - let* inbox = Node_context.inbox_of_head node_ctxt predecessor in + let* inbox = + Node_context.inbox_of_head node_ctxt (Layer1.head_of_header predecessor) + in + let predecessor_timestamp = predecessor.header.timestamp in let inbox_metrics = Metrics.Inbox.metrics in Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; let* ( _messages_history, @@ -167,25 +155,25 @@ let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block return (inbox_hash, inbox, witness_hash, messages_with_protocol_internal_messages) -let process_head (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{level; hash = head_hash} = +let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) + (head : Layer1.header) = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in - if level >= first_inbox_level then ( + if head.level >= first_inbox_level then (* We compute the inbox of this block using the inbox of its predecessor. That way, the computation of inboxes is robust to chain reorganization. *) - let* ( is_migration_block, - collected_messages, - predecessor_timestamp, - predecessor_hash ) = - get_messages node_ctxt head_hash - in - assert (Block_hash.(predecessor.Layer1.hash = predecessor_hash)) ; + let* collected_messages = get_messages node_ctxt head.hash in let*! () = - Inbox_event.get_messages head_hash level (List.length collected_messages) + Inbox_event.get_messages + head.hash + head.level + (List.length collected_messages) + in + let is_migration_block = + head.header.proto_level <> predecessor.header.proto_level in let* (( _inbox_hash, inbox, @@ -195,12 +183,11 @@ let process_head (node_ctxt : _ Node_context.t) ~predecessor node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp - ~level + ~level:head.level collected_messages in - let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - return res) + let* () = same_inbox_as_layer_1 node_ctxt head.hash inbox in + return res else let* inbox = Node_context.genesis_inbox node_ctxt in return diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.mli index 33e895f825932b95faa917b3a3498033b2e2ef48..a723ad47e53ef6f4933b7d11e28e60e0e8530920 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.mli @@ -42,8 +42,8 @@ open Sc_rollup [operations] of the [head] block. *) val process_head : Node_context.rw -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> (Sc_rollup.Inbox.Hash.t * Sc_rollup.Inbox.t * Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t @@ -88,8 +88,7 @@ module Internal_for_tests : sig val process_messages : Node_context.rw -> is_migration_block:bool -> - predecessor:Layer1.head -> - predecessor_timestamp:Timestamp.time -> + predecessor:Layer1.header -> level:int32 -> Sc_rollup.Inbox_message.t list -> (Sc_rollup.Inbox.Hash.t diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/injector.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/injector.ml index 1c10f32696dd8b6162199b30d38753881a86c1b9..bc0d0cf333a861982d4f2e52e9e56c222505bbf5 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/injector.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/injector.ml @@ -415,7 +415,7 @@ module Proto_client = struct Data_encoding.Binary.to_bytes_exn Operation.encoding op let time_until_next_block (node_ctxt : Node_context.ro) - (header : Tezos_base.Block_header.t option) = + (header : Tezos_base.Block_header.shell_header option) = let open Result_syntax in let Constants.Parametric.{minimal_block_delay; delay_increment_per_round; _} = @@ -432,12 +432,10 @@ module Proto_client = struct ~first_round_duration:minimal_block_delay ~delay_increment_per_round in - let* predecessor_round = - Fitness.round_from_raw header.shell.fitness - in + let* predecessor_round = Fitness.round_from_raw header.fitness in Round.timestamp_of_round durations - ~predecessor_timestamp:header.shell.timestamp + ~predecessor_timestamp:header.timestamp ~predecessor_round ~round:Round.zero in @@ -447,7 +445,7 @@ module Proto_client = struct ~default: (WithExceptions.Result.get_ok ~loc:__LOC__ - Timestamp.(header.shell.timestamp +? minimal_block_delay)) + Timestamp.(header.timestamp +? minimal_block_delay)) in Ptime.diff (Time.System.of_protocol_exn next_level_timestamp) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml index cc5fb9ca23382ff4b42f938b6b7d17a809c90980..41ad610367b3e528955603e186853b8c0e1d644c 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml @@ -38,8 +38,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t @@ -165,14 +165,19 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) - let process_head (node_ctxt : _ Node_context.t) ctxt ~predecessor head - inbox_messages = + let process_head (node_ctxt : _ Node_context.t) ctxt + ~(predecessor : Layer1.header) (head : Layer1.header) inbox_messages = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in if head.Layer1.level >= first_inbox_level then - transition_pvm node_ctxt ctxt predecessor head inbox_messages + transition_pvm + node_ctxt + ctxt + (Layer1.head_of_header predecessor) + (Layer1.head_of_header head) + inbox_messages else if head.Layer1.level = Raw_level.to_int32 node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli index 3bc4a0aa7aeeb0ec00405fe6dd0dc0fd8d3d1e08..99dd231a3ab9c78206f2babbaa708bafd1e96a4d 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli @@ -45,8 +45,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.ml index 70c02794a585657767ae121156473b616af4b19c..517fe9692479c996e4534b69e07e4db9898d6225 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.ml @@ -58,14 +58,31 @@ let () = *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + +let header_encoding = + let open Data_encoding in + conv + (fun {hash; level = _; header} -> (hash, header)) + (fun (hash, header) -> {hash; level = header.level; header}) + (merge_objs + (obj1 (req "hash" Block_hash.encoding)) + Block_header.shell_header_encoding) + type head = {hash : Block_hash.t; level : int32} let head_encoding = - Data_encoding.( - conv - (fun {hash; level} -> (hash, level)) - (fun (hash, level) -> {hash; level}) - (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32))) + let open Data_encoding in + conv + (fun {hash; level} -> (hash, level)) + (fun (hash, level) -> {hash; level}) + (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32)) + +let head_of_header {hash; level; header = _} = {hash; level} module Blocks_cache = Aches_lwt.Lache.Make_option @@ -73,13 +90,23 @@ module Blocks_cache = type blocks_cache = Alpha_block_services.block_info Blocks_cache.t +type headers_cache = Block_header.shell_header Blocks_cache.t + (** Global blocks cache for the smart rollup node. *) let blocks_cache : blocks_cache = Blocks_cache.create 32 +(** Global block headers cache for the smart rollup node. *) +let headers_cache : headers_cache = Blocks_cache.create 32 + include Octez_crawler.Layer_1 +let cache_shell_header hash header = + Blocks_cache.put headers_cache hash (Lwt.return_some header) + let iter_heads l1_ctxt f = - iter_heads l1_ctxt @@ fun (hash, {shell = {level; _}; _}) -> f {hash; level} + iter_heads l1_ctxt @@ fun (hash, {shell = {level; _} as header; _}) -> + cache_shell_header hash header ; + f {hash; level; header} (** @@ -111,12 +138,7 @@ let fetch_tezos_shell_header cctxt hash = | Ok shell_header -> return_some shell_header in let+ shell_header = - let res = - Blocks_cache.bind blocks_cache hash (function - | Some block_info -> Lwt.return_some block_info.header.shell - | None -> Lwt.return_none) - in - match res with Some lwt -> lwt | None -> fetch hash + Blocks_cache.bind_or_put headers_cache hash fetch Lwt.return in match (shell_header, !errors) with | None, None -> diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.mli index c9697e4fe5c0eae830ca054d187a4db7489a3f50..845e97a4f1c3e6e01fd3155da66aeed2be329e10 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1.mli @@ -30,10 +30,20 @@ subscribing to the head monitoring RPC offered by the Tezos node. *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + type head = {hash : Block_hash.t; level : int32} +val header_encoding : header Data_encoding.t + val head_encoding : head Data_encoding.t +val head_of_header : header -> head + include module type of Octez_crawler.Layer_1 (* TODO: https://gitlab.com/tezos/tezos/-/issues/3311 @@ -43,10 +53,14 @@ include module type of Octez_crawler.Layer_1 chain. In case of a disconnection with the layer 1 node, it reconnects automatically. If [f] returns an error (other than a disconnection) it, [iter_heads] terminates and returns the error. *) -val iter_heads : t -> (head -> unit tzresult Lwt.t) -> unit tzresult Lwt.t +val iter_heads : t -> (header -> unit tzresult Lwt.t) -> unit tzresult Lwt.t (** {2 Helpers } *) +(** [cache_shell_header hash header] saves in the local cache the shell [header] + for the block [hash]. *) +val cache_shell_header : Block_hash.t -> Block_header.shell_header -> unit + (** [fetch_tezos_shell_header cctxt hash] returns the block shell header of [hash]. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.ml index 7d25d833265f4cdf6b0770fae043227354d790e6..d7909111fed8412a70f9bf593b8d59e9e837d3fa 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.ml @@ -455,12 +455,24 @@ let nth_predecessor node_ctxt n block = in (head_of_block_level res, List.map head_of_block_level preds) +let header_of_head node_ctxt Layer1.{hash; level} = + let open Lwt_result_syntax in + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + +let nth_predecessor_header node_ctxt n block = + let open Lwt_result_syntax in + let* res, preds = nth_predecessor node_ctxt n (Layer1.head_of_header block) in + let* res = header_of_head node_ctxt res + and* preds = List.map_ep (header_of_head node_ctxt) preds in + return (res, preds) + let get_tezos_reorg_for_new_head node_ctxt old_head new_head = let open Lwt_result_syntax in let old_head = match old_head with | `Level l -> `Level l - | `Head h -> `Head (block_level_of_head h) + | `Head Layer1.{hash; level} -> `Head (hash, level) in let+ reorg = Layer1.get_tezos_reorg_for_new_head @@ -481,6 +493,16 @@ let get_predecessor node_ctxt head = let+ res = get_predecessor node_ctxt (block_level_of_head head) in head_of_block_level res +let get_predecessor_header_opt node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor_opt node_ctxt (Layer1.head_of_header head) in + Option.map_es (header_of_head node_ctxt) res + +let get_predecessor_header node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor node_ctxt (Layer1.head_of_header head) in + header_of_head node_ctxt res + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4128 Unit test the function tick_search. *) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.mli index 3d2bb256c51df7026f87f94ad83b60a4a5f8b430..2bbf577df8fe80f51c5c50ffb97076f1138517c9 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/node_context.mli @@ -222,6 +222,21 @@ val get_predecessor : _ t -> Layer1.head -> Layer1.head tzresult Lwt.t val nth_predecessor : _ t -> int -> Layer1.head -> (Layer1.head * Layer1.head list) tzresult Lwt.t +(** Same as {!get_predecessor_opt} with headers. *) +val get_predecessor_header_opt : + _ t -> Layer1.header -> Layer1.header option tzresult Lwt.t + +(** Same as {!get_predecessor} with headers. *) +val get_predecessor_header : + _ t -> Layer1.header -> Layer1.header tzresult Lwt.t + +(** Same as {!nth_predecessor} with headers. *) +val nth_predecessor_header : + _ t -> + int -> + Layer1.header -> + (Layer1.header * Layer1.header list) tzresult Lwt.t + (** [get_tezos_reorg_for_new_head node_ctxt old_head new_head] returns the L1 reorganization between [old_head] and [new_head]. *) val get_tezos_reorg_for_new_head : diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml index 202233df253fad522d007e05cdc12e621f70d81d..1178838d8360b3fe286e7ce8630510b42408feb3 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml @@ -180,6 +180,24 @@ module Make (PVM : Pvm.S) = struct in return_unit + let head_of_level ~predecessor level = + let hash = block_hash_of_level level in + let timestamp = Time.Protocol.of_seconds (Int64.of_int32 level) in + let header : Block_header.shell_header = + { + level; + predecessor; + timestamp; + (* dummy values below *) + proto_level = 0; + validation_passes = 3; + operations_hash = Tezos_crypto.Hashed.Operation_list_list_hash.zero; + fitness = []; + context = Tezos_crypto.Hashed.Context_hash.zero; + } + in + {Layer1.hash; level; header} + let append_l2_block (node_ctxt : _ Node_context.t) ?(is_migration_block = false) messages = let open Lwt_result_syntax in @@ -192,26 +210,19 @@ module Make (PVM : Pvm.S) = struct | None -> failwith "No genesis block, please add one with add_l2_genesis_block" in - let level = - Raw_level.(to_int32 @@ succ predecessor_l2_block.header.level) - in - let hash = block_hash_of_level level in + let pred_level = Raw_level.to_int32 predecessor_l2_block.header.level in let predecessor = - Layer1. - { - hash = predecessor_l2_block.header.block_hash; - level = Raw_level.to_int32 predecessor_l2_block.header.level; - } + head_of_level + ~predecessor:predecessor_l2_block.header.predecessor + pred_level in - let head = Layer1.{hash; level} in - let predecessor_timestamp = - Time.Protocol.of_seconds (Int64.of_int32 level) + let head = + head_of_level ~predecessor:predecessor.hash (Int32.succ pred_level) in Daemon.Internal_for_tests.process_messages node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp head messages end diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment.ml b/src/proto_alpha/lib_sc_rollup_node/commitment.ml index 237c288a0ba95874f9b40146bd79f885cdf0c329..e9fcb6c4e348a7b01f4e7e2ff8e645f5ebee342a 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment.ml +++ b/src/proto_alpha/lib_sc_rollup_node/commitment.ml @@ -175,8 +175,8 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct Some commitment else return_none - let process_head (node_ctxt : _ Node_context.t) ~predecessor Layer1.{level; _} - ctxt = + let process_head (node_ctxt : _ Node_context.t) ~predecessor + Layer1.{level; header = _; _} ctxt = let open Lwt_result_syntax in let current_level = Raw_level.of_int32_exn level in let* commitment = diff --git a/src/proto_alpha/lib_sc_rollup_node/commitment_sig.ml b/src/proto_alpha/lib_sc_rollup_node/commitment_sig.ml index 96f2306dd8f084e7f35c0c07c79a5f17c21ddc4b..fccdc19b185443f2da39dbcfe443d4a0f6d8d7d1 100644 --- a/src/proto_alpha/lib_sc_rollup_node/commitment_sig.ml +++ b/src/proto_alpha/lib_sc_rollup_node/commitment_sig.ml @@ -50,7 +50,7 @@ module type S = sig val process_head : Node_context.rw -> predecessor:Block_hash.t -> - Layer1.head -> + Layer1.header -> Context.rw -> Protocol.Alpha_context.Sc_rollup.Commitment.Hash.t option tzresult Lwt.t diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon.ml b/src/proto_alpha/lib_sc_rollup_node/daemon.ml index c57632ef578958b2878b80c6a188a74cba8d901d..c68f10befc14e93d80e29ce5d448d00ba3d19fff 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon.ml @@ -84,7 +84,7 @@ module Make (PVM : Pvm.S) = struct for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) - head ~source (operation : kind manager_operation) + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind successful_manager_operation_result) = let open Lwt_result_syntax in match (operation, result) with @@ -227,8 +227,8 @@ module Make (PVM : Pvm.S) = struct (_result : kind successful_manager_operation_result) = return_unit - let process_l1_operation (type kind) ~finalized node_ctxt head ~source - (operation : kind manager_operation) + let process_l1_operation (type kind) ~finalized node_ctxt + (head : Layer1.header) ~source (operation : kind manager_operation) (result : kind Apply_results.manager_operation_result) = let open Lwt_result_syntax in let is_for_my_rollup : type kind. kind manager_operation -> bool = function @@ -263,10 +263,11 @@ module Make (PVM : Pvm.S) = struct (* No action for non successful operations *) return_unit - let process_l1_block_operations ~finalized node_ctxt - (Layer1.{hash; _} as head) = + let process_l1_block_operations ~finalized node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in + let* block = + Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt head.hash + in let apply (type kind) accu ~source (operation : kind manager_operation) result = let open Lwt_result_syntax in @@ -286,46 +287,53 @@ module Make (PVM : Pvm.S) = struct in return_unit - let before_origination (node_ctxt : _ Node_context.t) Layer1.{level; _} = + let before_origination (node_ctxt : _ Node_context.t) (header : Layer1.header) + = let origination_level = Raw_level.to_int32 node_ctxt.genesis_info.level in - level < origination_level + header.level < origination_level let rec processed_finalized_block (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as block) = + (block : Layer1.header) = let open Lwt_result_syntax in let* last_finalized = Node_context.get_finalized_head_opt node_ctxt in let already_finalized = match last_finalized with - | Some finalized -> level <= Raw_level.to_int32 finalized.header.level + | Some finalized -> + block.level <= Raw_level.to_int32 finalized.header.level | None -> false in unless (already_finalized || before_origination node_ctxt block) @@ fun () -> - let* predecessor = Node_context.get_predecessor_opt node_ctxt block in + let* predecessor = + Node_context.get_predecessor_header_opt node_ctxt block + in let* () = Option.iter_es (processed_finalized_block node_ctxt) predecessor in - let*! () = Daemon_event.head_processing hash level ~finalized:true in + let*! () = + Daemon_event.head_processing block.hash block.level ~finalized:true + in let* () = process_l1_block_operations ~finalized:true node_ctxt block in - let* () = Node_context.mark_finalized_head node_ctxt hash in + let* () = Node_context.mark_finalized_head node_ctxt block.hash in return_unit - let previous_context (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{hash = _; level} = + let previous_context (node_ctxt : _ Node_context.t) + ~(predecessor : Layer1.header) = let open Lwt_result_syntax in - if level <= Raw_level.to_int32 node_ctxt.genesis_info.level then + if predecessor.level < Raw_level.to_int32 node_ctxt.genesis_info.level then (* This is before we have interpreted the boot sector, so we start with an empty context in genesis *) return (Context.empty node_ctxt.context) else Node_context.checkout_context node_ctxt predecessor.Layer1.hash - let rec process_head (node_ctxt : _ Node_context.t) - Layer1.({hash; level} as head) = + let rec process_head (node_ctxt : _ Node_context.t) (head : Layer1.header) = let open Lwt_result_syntax in - let* already_processed = Node_context.is_processed node_ctxt hash in + let* already_processed = Node_context.is_processed node_ctxt head.hash in unless (already_processed || before_origination node_ctxt head) @@ fun () -> - let*! () = Daemon_event.head_processing hash level ~finalized:false in - let* predecessor = Node_context.get_predecessor_opt node_ctxt head in + let*! () = + Daemon_event.head_processing head.hash head.level ~finalized:false + in + let* predecessor = Node_context.get_predecessor_header_opt node_ctxt head in match predecessor with | None -> (* Predecessor not available on the L1, which means the block does not @@ -333,14 +341,18 @@ module Make (PVM : Pvm.S) = struct return_unit | Some predecessor -> let* () = process_head node_ctxt predecessor in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level + node_ctxt + {Layer1.hash = head.hash; level = head.level} + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.process_head node_ctxt ~predecessor head in let* () = when_ (Node_context.dal_supported node_ctxt) @@ fun () -> - Dal_slots_tracker.process_head node_ctxt head + Dal_slots_tracker.process_head node_ctxt (Layer1.head_of_header head) in let* () = process_l1_block_operations ~finalized:false node_ctxt head in (* Avoid storing and publishing commitments if the head is not final. *) @@ -362,7 +374,7 @@ module Make (PVM : Pvm.S) = struct head ctxt in - let level = Raw_level.of_int32_exn level in + let level = Raw_level.of_int32_exn head.level in let* previous_commitment_hash = if level = node_ctxt.genesis_info.Sc_rollup.Commitment.level then (* Previous commitment for rollup genesis is itself. *) @@ -374,7 +386,7 @@ module Make (PVM : Pvm.S) = struct let header = Sc_rollup_block. { - block_hash = hash; + block_hash = head.hash; level; predecessor = predecessor.hash; commitment_hash; @@ -388,7 +400,7 @@ module Make (PVM : Pvm.S) = struct Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* finalized_block, _ = - Node_context.nth_predecessor + Node_context.nth_predecessor_header node_ctxt node_ctxt.block_finality_time head @@ -396,7 +408,7 @@ module Make (PVM : Pvm.S) = struct let* () = processed_finalized_block node_ctxt finalized_block in let* () = Node_context.save_l2_head node_ctxt l2_block in let*! () = - Daemon_event.new_head_processed hash (Raw_level.to_int32 level) + Daemon_event.new_head_processed head.hash (Raw_level.to_int32 level) in return_unit @@ -404,7 +416,7 @@ module Make (PVM : Pvm.S) = struct also processes any missing blocks that were not processed. Every time a head is processed we also process head~2 as finalized (which may recursively imply the processing of head~3, etc). *) - let on_layer_1_head node_ctxt head = + let on_layer_1_head node_ctxt (head : Layer1.header) = let open Lwt_result_syntax in let* old_head = Node_context.last_processed_head_opt node_ctxt in let old_head = @@ -424,8 +436,9 @@ module Make (PVM : Pvm.S) = struct in `Level (Int32.pred origination_level) in + let stripped_head = Layer1.head_of_header head in let*! reorg = - Node_context.get_tezos_reorg_for_new_head node_ctxt old_head head + Node_context.get_tezos_reorg_for_new_head node_ctxt old_head stripped_head in let*? reorg = match reorg with @@ -441,30 +454,33 @@ module Make (PVM : Pvm.S) = struct trace -> (* The reorganization could not be computed entirely because of missing info on the Layer 1. We fallback to a recursive process_head. *) - Ok {Reorg.no_reorg with new_chain = [head]} + Ok {Reorg.no_reorg with new_chain = [stripped_head]} | _ -> reorg in (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 Rollback state information on reorganization, i.e. for reorg.old_chain. *) - let* new_head = Layer1.fetch_tezos_block node_ctxt.cctxt head.Layer1.hash in - let header = - Block_header.( - raw - { - shell = new_head.header.shell; - protocol_data = new_head.header.protocol_data; - }) - in let*! () = Daemon_event.processing_heads_iteration reorg.new_chain in - let* () = List.iter_es (process_head node_ctxt) reorg.new_chain in + let get_header Layer1.{hash; level} = + if Block_hash.equal hash head.hash then return head + else + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + in + let* () = + List.iter_es + (fun block -> + let* header = get_header block in + process_head node_ctxt header) + reorg.new_chain + in let* () = Components.Commitment.Publisher.publish_commitments () in let* () = Components.Commitment.Publisher.cement_commitments () in let*! () = Daemon_event.new_heads_processed reorg.new_chain in - let* () = Components.Refutation_coordinator.process head in + let* () = Components.Refutation_coordinator.process stripped_head in let* () = Components.Batcher.batch () in - let* () = Components.Batcher.new_head head in - let*! () = Injector.inject ~header () in + let* () = Components.Batcher.new_head stripped_head in + let*! () = Injector.inject ~header:head.header () in return_unit let daemonize (node_ctxt : _ Node_context.t) = @@ -479,7 +495,9 @@ module Make (PVM : Pvm.S) = struct let*! () = message "Shutting down Commitment Publisher@." in let*! () = Components.Commitment.Publisher.shutdown () in Layer1.iter_heads node_ctxt.l1_ctxt @@ fun head -> - let* () = Components.Refutation_coordinator.process head in + let* () = + Components.Refutation_coordinator.process (Layer1.head_of_header head) + in let*! () = Injector.inject () in return_unit @@ -606,16 +624,17 @@ module Make (PVM : Pvm.S) = struct corresponding to [messages]. It is used by the unit tests to build an L2 chain. *) let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block - ~predecessor ~predecessor_timestamp head messages = + ~predecessor head messages = let open Lwt_result_syntax in - let* ctxt = previous_context node_ctxt ~predecessor head in - let* () = Node_context.save_level node_ctxt head in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = + Node_context.save_level node_ctxt (Layer1.head_of_header head) + in let* inbox_hash, inbox, inbox_witness, messages = Inbox.Internal_for_tests.process_messages node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp ~level:head.level messages in diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.ml b/src/proto_alpha/lib_sc_rollup_node/inbox.ml index 5e6572c4e51c50eff2a2fd4a38ce0743592d6bd8..3a15cf6eba8d88ce9b633fcac38fed0a329b6d00 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.ml @@ -78,22 +78,7 @@ let get_messages Node_context.{cctxt; _} head = block.operations {apply; apply_internal}) in - let ({predecessor; _} : Block_header.shell_header) = block.header.shell in - let* { - timestamp = predecessor_timestamp; - proto_level = predecessor_proto_level; - _; - } = - Layer1.fetch_tezos_shell_header cctxt predecessor - in - let is_migration_block = - block.header.shell.proto_level <> predecessor_proto_level - in - return - ( is_migration_block, - List.rev rev_messages, - predecessor_timestamp, - predecessor ) + return (List.rev rev_messages) let same_inbox_as_layer_1 node_ctxt head_hash inbox = let open Lwt_result_syntax in @@ -134,9 +119,12 @@ let add_messages ~is_migration_block ~predecessor_timestamp ~predecessor inbox messages_with_protocol_internal_messages ) let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block - ~predecessor ~predecessor_timestamp ~level messages = + ~(predecessor : Layer1.header) ~level messages = let open Lwt_result_syntax in - let* inbox = Node_context.inbox_of_head node_ctxt predecessor in + let* inbox = + Node_context.inbox_of_head node_ctxt (Layer1.head_of_header predecessor) + in + let predecessor_timestamp = predecessor.header.timestamp in let inbox_metrics = Metrics.Inbox.metrics in Prometheus.Gauge.set inbox_metrics.head_inbox_level @@ Int32.to_float level ; let* ( _messages_history, @@ -167,25 +155,25 @@ let process_messages (node_ctxt : _ Node_context.t) ~is_migration_block return (inbox_hash, inbox, witness_hash, messages_with_protocol_internal_messages) -let process_head (node_ctxt : _ Node_context.t) ~predecessor - Layer1.{level; hash = head_hash} = +let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) + (head : Layer1.header) = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in - if level >= first_inbox_level then ( + if head.level >= first_inbox_level then (* We compute the inbox of this block using the inbox of its predecessor. That way, the computation of inboxes is robust to chain reorganization. *) - let* ( is_migration_block, - collected_messages, - predecessor_timestamp, - predecessor_hash ) = - get_messages node_ctxt head_hash - in - assert (Block_hash.(predecessor.Layer1.hash = predecessor_hash)) ; + let* collected_messages = get_messages node_ctxt head.hash in let*! () = - Inbox_event.get_messages head_hash level (List.length collected_messages) + Inbox_event.get_messages + head.hash + head.level + (List.length collected_messages) + in + let is_migration_block = + head.header.proto_level <> predecessor.header.proto_level in let* (( _inbox_hash, inbox, @@ -195,12 +183,11 @@ let process_head (node_ctxt : _ Node_context.t) ~predecessor node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp - ~level + ~level:head.level collected_messages in - let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - return res) + let* () = same_inbox_as_layer_1 node_ctxt head.hash inbox in + return res else let* inbox = Node_context.genesis_inbox node_ctxt in return diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.mli b/src/proto_alpha/lib_sc_rollup_node/inbox.mli index 33e895f825932b95faa917b3a3498033b2e2ef48..a723ad47e53ef6f4933b7d11e28e60e0e8530920 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.mli +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.mli @@ -42,8 +42,8 @@ open Sc_rollup [operations] of the [head] block. *) val process_head : Node_context.rw -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> (Sc_rollup.Inbox.Hash.t * Sc_rollup.Inbox.t * Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t @@ -88,8 +88,7 @@ module Internal_for_tests : sig val process_messages : Node_context.rw -> is_migration_block:bool -> - predecessor:Layer1.head -> - predecessor_timestamp:Timestamp.time -> + predecessor:Layer1.header -> level:int32 -> Sc_rollup.Inbox_message.t list -> (Sc_rollup.Inbox.Hash.t diff --git a/src/proto_alpha/lib_sc_rollup_node/injector.ml b/src/proto_alpha/lib_sc_rollup_node/injector.ml index 1c10f32696dd8b6162199b30d38753881a86c1b9..bc0d0cf333a861982d4f2e52e9e56c222505bbf5 100644 --- a/src/proto_alpha/lib_sc_rollup_node/injector.ml +++ b/src/proto_alpha/lib_sc_rollup_node/injector.ml @@ -415,7 +415,7 @@ module Proto_client = struct Data_encoding.Binary.to_bytes_exn Operation.encoding op let time_until_next_block (node_ctxt : Node_context.ro) - (header : Tezos_base.Block_header.t option) = + (header : Tezos_base.Block_header.shell_header option) = let open Result_syntax in let Constants.Parametric.{minimal_block_delay; delay_increment_per_round; _} = @@ -432,12 +432,10 @@ module Proto_client = struct ~first_round_duration:minimal_block_delay ~delay_increment_per_round in - let* predecessor_round = - Fitness.round_from_raw header.shell.fitness - in + let* predecessor_round = Fitness.round_from_raw header.fitness in Round.timestamp_of_round durations - ~predecessor_timestamp:header.shell.timestamp + ~predecessor_timestamp:header.timestamp ~predecessor_round ~round:Round.zero in @@ -447,7 +445,7 @@ module Proto_client = struct ~default: (WithExceptions.Result.get_ok ~loc:__LOC__ - Timestamp.(header.shell.timestamp +? minimal_block_delay)) + Timestamp.(header.timestamp +? minimal_block_delay)) in Ptime.diff (Time.System.of_protocol_exn next_level_timestamp) diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml index cc5fb9ca23382ff4b42f938b6b7d17a809c90980..41ad610367b3e528955603e186853b8c0e1d644c 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml @@ -38,8 +38,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t @@ -165,14 +165,19 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) - let process_head (node_ctxt : _ Node_context.t) ctxt ~predecessor head - inbox_messages = + let process_head (node_ctxt : _ Node_context.t) ctxt + ~(predecessor : Layer1.header) (head : Layer1.header) inbox_messages = let open Lwt_result_syntax in let first_inbox_level = Raw_level.to_int32 node_ctxt.genesis_info.level |> Int32.succ in if head.Layer1.level >= first_inbox_level then - transition_pvm node_ctxt ctxt predecessor head inbox_messages + transition_pvm + node_ctxt + ctxt + (Layer1.head_of_header predecessor) + (Layer1.head_of_header head) + inbox_messages else if head.Layer1.level = Raw_level.to_int32 node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.mli b/src/proto_alpha/lib_sc_rollup_node/interpreter.mli index 3bc4a0aa7aeeb0ec00405fe6dd0dc0fd8d3d1e08..99dd231a3ab9c78206f2babbaa708bafd1e96a4d 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.mli @@ -45,8 +45,8 @@ module type S = sig val process_head : Node_context.rw -> 'a Context.t -> - predecessor:Layer1.head -> - Layer1.head -> + predecessor:Layer1.header -> + Layer1.header -> Sc_rollup.Inbox.t * Sc_rollup.Inbox_message.t list -> ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1.ml b/src/proto_alpha/lib_sc_rollup_node/layer1.ml index 70c02794a585657767ae121156473b616af4b19c..517fe9692479c996e4534b69e07e4db9898d6225 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1.ml +++ b/src/proto_alpha/lib_sc_rollup_node/layer1.ml @@ -58,14 +58,31 @@ let () = *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + +let header_encoding = + let open Data_encoding in + conv + (fun {hash; level = _; header} -> (hash, header)) + (fun (hash, header) -> {hash; level = header.level; header}) + (merge_objs + (obj1 (req "hash" Block_hash.encoding)) + Block_header.shell_header_encoding) + type head = {hash : Block_hash.t; level : int32} let head_encoding = - Data_encoding.( - conv - (fun {hash; level} -> (hash, level)) - (fun (hash, level) -> {hash; level}) - (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32))) + let open Data_encoding in + conv + (fun {hash; level} -> (hash, level)) + (fun (hash, level) -> {hash; level}) + (obj2 (req "hash" Block_hash.encoding) (req "level" Data_encoding.int32)) + +let head_of_header {hash; level; header = _} = {hash; level} module Blocks_cache = Aches_lwt.Lache.Make_option @@ -73,13 +90,23 @@ module Blocks_cache = type blocks_cache = Alpha_block_services.block_info Blocks_cache.t +type headers_cache = Block_header.shell_header Blocks_cache.t + (** Global blocks cache for the smart rollup node. *) let blocks_cache : blocks_cache = Blocks_cache.create 32 +(** Global block headers cache for the smart rollup node. *) +let headers_cache : headers_cache = Blocks_cache.create 32 + include Octez_crawler.Layer_1 +let cache_shell_header hash header = + Blocks_cache.put headers_cache hash (Lwt.return_some header) + let iter_heads l1_ctxt f = - iter_heads l1_ctxt @@ fun (hash, {shell = {level; _}; _}) -> f {hash; level} + iter_heads l1_ctxt @@ fun (hash, {shell = {level; _} as header; _}) -> + cache_shell_header hash header ; + f {hash; level; header} (** @@ -111,12 +138,7 @@ let fetch_tezos_shell_header cctxt hash = | Ok shell_header -> return_some shell_header in let+ shell_header = - let res = - Blocks_cache.bind blocks_cache hash (function - | Some block_info -> Lwt.return_some block_info.header.shell - | None -> Lwt.return_none) - in - match res with Some lwt -> lwt | None -> fetch hash + Blocks_cache.bind_or_put headers_cache hash fetch Lwt.return in match (shell_header, !errors) with | None, None -> diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1.mli b/src/proto_alpha/lib_sc_rollup_node/layer1.mli index c9697e4fe5c0eae830ca054d187a4db7489a3f50..845e97a4f1c3e6e01fd3155da66aeed2be329e10 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1.mli +++ b/src/proto_alpha/lib_sc_rollup_node/layer1.mli @@ -30,10 +30,20 @@ subscribing to the head monitoring RPC offered by the Tezos node. *) +type header = { + hash : Block_hash.t; + level : int32; + header : Block_header.shell_header; +} + type head = {hash : Block_hash.t; level : int32} +val header_encoding : header Data_encoding.t + val head_encoding : head Data_encoding.t +val head_of_header : header -> head + include module type of Octez_crawler.Layer_1 (* TODO: https://gitlab.com/tezos/tezos/-/issues/3311 @@ -43,10 +53,14 @@ include module type of Octez_crawler.Layer_1 chain. In case of a disconnection with the layer 1 node, it reconnects automatically. If [f] returns an error (other than a disconnection) it, [iter_heads] terminates and returns the error. *) -val iter_heads : t -> (head -> unit tzresult Lwt.t) -> unit tzresult Lwt.t +val iter_heads : t -> (header -> unit tzresult Lwt.t) -> unit tzresult Lwt.t (** {2 Helpers } *) +(** [cache_shell_header hash header] saves in the local cache the shell [header] + for the block [hash]. *) +val cache_shell_header : Block_hash.t -> Block_header.shell_header -> unit + (** [fetch_tezos_shell_header cctxt hash] returns the block shell header of [hash]. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) diff --git a/src/proto_alpha/lib_sc_rollup_node/node_context.ml b/src/proto_alpha/lib_sc_rollup_node/node_context.ml index 00d8ed1ac165fa2a4ecb19a85da47c3bc4f2e1c9..58315a24a2955d3abf29203b0b54b8fbe78ef18a 100644 --- a/src/proto_alpha/lib_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/lib_sc_rollup_node/node_context.ml @@ -471,12 +471,24 @@ let nth_predecessor node_ctxt n block = in (head_of_block_level res, List.map head_of_block_level preds) +let header_of_head node_ctxt Layer1.{hash; level} = + let open Lwt_result_syntax in + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.cctxt hash in + {Layer1.hash; level; header} + +let nth_predecessor_header node_ctxt n block = + let open Lwt_result_syntax in + let* res, preds = nth_predecessor node_ctxt n (Layer1.head_of_header block) in + let* res = header_of_head node_ctxt res + and* preds = List.map_ep (header_of_head node_ctxt) preds in + return (res, preds) + let get_tezos_reorg_for_new_head node_ctxt old_head new_head = let open Lwt_result_syntax in let old_head = match old_head with | `Level l -> `Level l - | `Head h -> `Head (block_level_of_head h) + | `Head Layer1.{hash; level} -> `Head (hash, level) in let+ reorg = Layer1.get_tezos_reorg_for_new_head @@ -497,6 +509,16 @@ let get_predecessor node_ctxt head = let+ res = get_predecessor node_ctxt (block_level_of_head head) in head_of_block_level res +let get_predecessor_header_opt node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor_opt node_ctxt (Layer1.head_of_header head) in + Option.map_es (header_of_head node_ctxt) res + +let get_predecessor_header node_ctxt head = + let open Lwt_result_syntax in + let* res = get_predecessor node_ctxt (Layer1.head_of_header head) in + header_of_head node_ctxt res + (* TODO: https://gitlab.com/tezos/tezos/-/issues/4128 Unit test the function tick_search. *) diff --git a/src/proto_alpha/lib_sc_rollup_node/node_context.mli b/src/proto_alpha/lib_sc_rollup_node/node_context.mli index 71ff7edd5a2844e77641a5a9c3612ad469f65d38..7323aa7ae4a6b17cd4b3f4af968ede1ac1e067e3 100644 --- a/src/proto_alpha/lib_sc_rollup_node/node_context.mli +++ b/src/proto_alpha/lib_sc_rollup_node/node_context.mli @@ -224,6 +224,21 @@ val get_predecessor : _ t -> Layer1.head -> Layer1.head tzresult Lwt.t val nth_predecessor : _ t -> int -> Layer1.head -> (Layer1.head * Layer1.head list) tzresult Lwt.t +(** Same as {!get_predecessor_opt} with headers. *) +val get_predecessor_header_opt : + _ t -> Layer1.header -> Layer1.header option tzresult Lwt.t + +(** Same as {!get_predecessor} with headers. *) +val get_predecessor_header : + _ t -> Layer1.header -> Layer1.header tzresult Lwt.t + +(** Same as {!nth_predecessor} with headers. *) +val nth_predecessor_header : + _ t -> + int -> + Layer1.header -> + (Layer1.header * Layer1.header list) tzresult Lwt.t + (** [get_tezos_reorg_for_new_head node_ctxt old_head new_head] returns the L1 reorganization between [old_head] and [new_head]. *) val get_tezos_reorg_for_new_head : diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml index 202233df253fad522d007e05cdc12e621f70d81d..1178838d8360b3fe286e7ce8630510b42408feb3 100644 --- a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml @@ -180,6 +180,24 @@ module Make (PVM : Pvm.S) = struct in return_unit + let head_of_level ~predecessor level = + let hash = block_hash_of_level level in + let timestamp = Time.Protocol.of_seconds (Int64.of_int32 level) in + let header : Block_header.shell_header = + { + level; + predecessor; + timestamp; + (* dummy values below *) + proto_level = 0; + validation_passes = 3; + operations_hash = Tezos_crypto.Hashed.Operation_list_list_hash.zero; + fitness = []; + context = Tezos_crypto.Hashed.Context_hash.zero; + } + in + {Layer1.hash; level; header} + let append_l2_block (node_ctxt : _ Node_context.t) ?(is_migration_block = false) messages = let open Lwt_result_syntax in @@ -192,26 +210,19 @@ module Make (PVM : Pvm.S) = struct | None -> failwith "No genesis block, please add one with add_l2_genesis_block" in - let level = - Raw_level.(to_int32 @@ succ predecessor_l2_block.header.level) - in - let hash = block_hash_of_level level in + let pred_level = Raw_level.to_int32 predecessor_l2_block.header.level in let predecessor = - Layer1. - { - hash = predecessor_l2_block.header.block_hash; - level = Raw_level.to_int32 predecessor_l2_block.header.level; - } + head_of_level + ~predecessor:predecessor_l2_block.header.predecessor + pred_level in - let head = Layer1.{hash; level} in - let predecessor_timestamp = - Time.Protocol.of_seconds (Int64.of_int32 level) + let head = + head_of_level ~predecessor:predecessor.hash (Int32.succ pred_level) in Daemon.Internal_for_tests.process_messages node_ctxt ~is_migration_block ~predecessor - ~predecessor_timestamp head messages end