From fba95e94855a76343fb6454c224071b83e5c8313 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 3 Jan 2023 19:06:03 +0100 Subject: [PATCH 1/5] SCORU/Node: Store uses indexed files DAL data remains in an irmin store for now. --- src/proto_alpha/bin_sc_rollup_node/store.ml | 343 ++++++++++++------- src/proto_alpha/bin_sc_rollup_node/store.mli | 124 +++---- 2 files changed, 274 insertions(+), 193 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/store.ml b/src/proto_alpha/bin_sc_rollup_node/store.ml index 5c22868889fb..9b459d85b7ed 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.ml +++ b/src/proto_alpha/bin_sc_rollup_node/store.ml @@ -30,123 +30,124 @@ include Store_utils (** Aggregated collection of messages from the L1 inbox *) open Alpha_context -module IStore = Irmin_store.Make (struct - let name = "Tezos smart rollup node" -end) +module Irmin_store = struct + module IStore = Irmin_store.Make (struct + let name = "Tezos smart rollup node" + end) -include Store_utils.Make (IStore) + include IStore + include Store_utils.Make (IStore) +end -type 'a store = 'a IStore.t +module Empty_header = struct + type t = unit -type 'a t = ([< `Read | `Write > `Read] as 'a) store + let name = "empty" -type rw = Store_sigs.rw t + let encoding = Data_encoding.unit -type ro = Store_sigs.ro t + let fixed_size = 0 +end -let close = IStore.close +module Add_empty_header = struct + module Header = Empty_header -let load = IStore.load + let header _ = () +end -let readonly = IStore.readonly +module Make_hash_index_key (H : Environment.S.HASH) = +Indexed_store.Make_index_key (struct + include Indexed_store.Make_fixed_encodable (H) + + let equal = H.equal +end) (** L2 blocks *) module L2_blocks = - Make_append_only_map + Indexed_store.Make_indexed_file (struct - let path = ["state_info"] + let name = "l2_blocks" end) + (Tezos_store_shared.Block_key) (struct - type key = Block_hash.t + type t = (unit, unit) Sc_rollup_block.block - let to_path_representation = Block_hash.to_b58check - end) - (struct - type value = Sc_rollup_block.t + let name = "sc_rollup_block_info" + + let encoding = + Sc_rollup_block.block_encoding Data_encoding.unit Data_encoding.unit - let name = "sc_rollup_block" + module Header = struct + type t = Sc_rollup_block.header - let encoding = Sc_rollup_block.encoding + let name = "sc_rollup_block_header" + + let encoding = Sc_rollup_block.header_encoding + + let fixed_size = Sc_rollup_block.header_size + end end) (** Unaggregated messages per block *) -module Messages = struct - type info = { - predecessor : Block_hash.t; - predecessor_timestamp : Timestamp.t; - messages : Sc_rollup.Inbox_message.t list; - } +module Messages = + Indexed_store.Make_indexed_file + (struct + let name = "messages" + end) + (Make_hash_index_key (Sc_rollup.Inbox_merkelized_payload_hashes.Hash)) + (struct + type t = Sc_rollup.Inbox_message.t list - let encoding = - let open Data_encoding in - conv - (fun {predecessor; predecessor_timestamp; messages} -> - (predecessor, predecessor_timestamp, messages)) - (fun (predecessor, predecessor_timestamp, messages) -> - {predecessor; predecessor_timestamp; messages}) - @@ obj3 - (req "predecessor" Block_hash.encoding) - (req "predecessor_timestamp" Timestamp.encoding) - (req - "messages" - (list @@ dynamic_size Sc_rollup.Inbox_message.encoding)) + let name = "messages_list" - include - Make_append_only_map - (struct - let path = ["messages"] - end) - (struct - type key = Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + let encoding = + Data_encoding.(list @@ dynamic_size Sc_rollup.Inbox_message.encoding) - let to_path_representation = - Sc_rollup.Inbox_merkelized_payload_hashes.Hash.to_b58check - end) - (struct - type value = info + module Header = struct + type t = Block_hash.t * Timestamp.t - let name = "messages" + let name = "messages_inbox_info" - let encoding = encoding - end) -end + let encoding = + let open Data_encoding in + obj2 + (req "predecessor" Block_hash.encoding) + (req "predecessor_timestamp" Timestamp.encoding) + + let fixed_size = + WithExceptions.Option.get ~loc:__LOC__ + @@ Data_encoding.Binary.fixed_length encoding + end + end) (** Inbox state for each block *) module Inboxes = - Make_append_only_map + Indexed_store.Make_simple_indexed_file (struct - let path = ["inboxes"] + let name = "inboxes" end) + (Make_hash_index_key (Sc_rollup.Inbox.Hash)) (struct - type key = Sc_rollup.Inbox.Hash.t - - let to_path_representation = Sc_rollup.Inbox.Hash.to_b58check - end) - (struct - type value = Sc_rollup.Inbox.t + type t = Sc_rollup.Inbox.t let name = "inbox" let encoding = Sc_rollup.Inbox.encoding + + include Add_empty_header end) module Commitments = - Make_append_only_map + Indexed_store.Make_indexable (struct - let path = ["commitments"; "computed"] + let name = "commitments" end) - (struct - type key = Sc_rollup.Commitment.Hash.t - - let to_path_representation = Sc_rollup.Commitment.Hash.to_b58check - end) - (struct - type value = Sc_rollup.Commitment.t + (Make_hash_index_key (Sc_rollup.Commitment.Hash)) + (Indexed_store.Make_index_value (Indexed_store.Make_fixed_encodable (struct + include Sc_rollup.Commitment let name = "commitment" - - let encoding = Sc_rollup.Commitment.encoding - end) + end))) module Commitments_published_at_level = struct type element = { @@ -156,6 +157,12 @@ module Commitments_published_at_level = struct let element_encoding = let open Data_encoding in + let opt_level_encoding = + conv + (function None -> -1l | Some l -> Raw_level.to_int32 l) + (fun l -> if l = -1l then None else Some (Raw_level.of_int32_exn l)) + Data_encoding.int32 + in conv (fun {first_published_at_level; published_at_level} -> (first_published_at_level, published_at_level)) @@ -163,82 +170,64 @@ module Commitments_published_at_level = struct {first_published_at_level; published_at_level}) @@ obj2 (req "first_published_at_level" Raw_level.encoding) - (opt "published_at_level" Raw_level.encoding) + (req "published_at_level" opt_level_encoding) include - Make_updatable_map + Indexed_store.Make_indexable (struct - let path = ["commitments"; "published_at_level"] + let name = "commitments" end) - (struct - type key = Sc_rollup.Commitment.Hash.t - - let to_path_representation = Sc_rollup.Commitment.Hash.to_b58check - end) - (struct - type value = element + (Make_hash_index_key (Sc_rollup.Commitment.Hash)) + (Indexed_store.Make_index_value (Indexed_store.Make_fixed_encodable (struct + type t = element let name = "published_levels" let encoding = element_encoding - end) + end))) end -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4392 - Use file. *) -module L2_head = - Make_mutable_value - (struct - let path = ["l2_head"] - end) - (struct - type value = Sc_rollup_block.t +module L2_head = Indexed_store.Make_singleton (struct + type t = Sc_rollup_block.t - let name = "l2_block" + let name = "l2_head" - let encoding = Sc_rollup_block.encoding - end) + let encoding = Sc_rollup_block.encoding +end) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4392 - Use file. *) -module Last_finalized_head = - Make_mutable_value - (struct - let path = ["finalized_head"] - end) - (struct - type value = Sc_rollup_block.t +module Last_finalized_head = Indexed_store.Make_singleton (struct + type t = Sc_rollup_block.t - let name = "l2_block" + let name = "finalized_head" - let encoding = Sc_rollup_block.encoding - end) + let encoding = Sc_rollup_block.encoding +end) (** Table from L1 levels to blocks hashes. *) module Levels_to_hashes = - Make_updatable_map + Indexed_store.Make_indexable (struct - let path = ["tezos"; "levels"] + let name = "tezos_levels" end) - (struct - type key = int32 + (Indexed_store.Make_index_key (struct + type t = int32 - let to_path_representation = Int32.to_string - end) - (struct - type value = Block_hash.t + let encoding = Data_encoding.int32 - let name = "block_hash" + let name = "level" - let encoding = Block_hash.encoding - end) + let fixed_size = 4 + + let equal = Int32.equal + end)) + (Tezos_store_shared.Block_key) (* Published slot headers per block hash, stored as a list of bindings from `Dal_slot_index.t` to `Dal.Slot.t`. The encoding function converts this list into a `Dal.Slot_index.t`-indexed map. *) module Dal_slot_pages = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "slot_pages"] end) @@ -270,7 +259,7 @@ module Dal_slot_pages = (** stores slots whose data have been considered and pages stored to disk (if they are confirmed). *) module Dal_processed_slots = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "processed_slots"] end) @@ -312,7 +301,7 @@ module Dal_processed_slots = end) module Dal_slots_headers = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "slot_headers"] end) @@ -348,7 +337,7 @@ module Dal_slots_headers = (** Confirmed DAL slots history. See documentation of {Dal_slot_repr.Slots_history} for more details. *) module Dal_confirmed_slots_history = - Make_append_only_map + Irmin_store.Make_append_only_map (struct let path = ["dal"; "confirmed_slots_history"] end) @@ -370,7 +359,7 @@ module Dal_confirmed_slots_history = module Dal_confirmed_slots_histories = (* TODO: https://gitlab.com/tezos/tezos/-/issues/4390 Store single history points in map instead of whole history. *) - Make_append_only_map + Irmin_store.Make_append_only_map (struct let path = ["dal"; "confirmed_slots_histories_cache"] end) @@ -386,3 +375,105 @@ module Dal_confirmed_slots_histories = let encoding = Dal.Slots_history.History_cache.encoding end) + +type 'a store = { + l2_blocks : 'a L2_blocks.t; + messages : 'a Messages.t; + inboxes : 'a Inboxes.t; + commitments : 'a Commitments.t; + commitments_published_at_level : 'a Commitments_published_at_level.t; + l2_head : 'a L2_head.t; + last_finalized_head : 'a Last_finalized_head.t; + levels_to_hashes : 'a Levels_to_hashes.t; + irmin_store : 'a Irmin_store.t; +} + +type 'a t = ([< `Read | `Write > `Read] as 'a) store + +type rw = Store_sigs.rw t + +type ro = Store_sigs.ro t + +let readonly + ({ + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head; + last_finalized_head; + levels_to_hashes; + irmin_store; + } : + _ t) : ro = + { + l2_blocks = L2_blocks.readonly l2_blocks; + messages = Messages.readonly messages; + inboxes = Inboxes.readonly inboxes; + commitments = Commitments.readonly commitments; + commitments_published_at_level = + Commitments_published_at_level.readonly commitments_published_at_level; + l2_head = L2_head.readonly l2_head; + last_finalized_head = Last_finalized_head.readonly last_finalized_head; + levels_to_hashes = Levels_to_hashes.readonly levels_to_hashes; + irmin_store = Irmin_store.readonly irmin_store; + } + +let close + ({ + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head = _; + last_finalized_head = _; + levels_to_hashes; + irmin_store; + } : + _ t) = + let open Lwt_result_syntax in + let+ () = L2_blocks.close l2_blocks + and+ () = Messages.close messages + and+ () = Inboxes.close inboxes + and+ () = Commitments.close commitments + and+ () = Commitments_published_at_level.close commitments_published_at_level + and+ () = Levels_to_hashes.close levels_to_hashes + and+ () = Irmin_store.close irmin_store |> Lwt_result.ok in + () + +let load (type a) (mode : a mode) data_dir : a store tzresult Lwt.t = + let open Lwt_result_syntax in + let path name = Filename.concat data_dir name in + let cache_size = 10_000 in + let* l2_blocks = L2_blocks.load mode ~path:(path "l2_blocks") ~cache_size in + let* messages = Messages.load mode ~path:(path "messages") ~cache_size in + let* inboxes = Inboxes.load mode ~path:(path "inboxes") ~cache_size in + let* commitments = Commitments.load mode ~path:(path "commitments") in + let* commitments_published_at_level = + Commitments_published_at_level.load + mode + ~path:(path "commitments_published_at_level") + in + let* l2_head = L2_head.load mode ~path:(path "l2_head") in + let* last_finalized_head = + Last_finalized_head.load mode ~path:(path "last_finalized_head") + in + let* levels_to_hashes = + Levels_to_hashes.load mode ~path:(path "levels_to_hashes") + in + let+ irmin_store = + Irmin_store.load mode (path "irmin_store") |> Lwt_result.ok + in + { + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head; + last_finalized_head; + levels_to_hashes; + irmin_store; + } diff --git a/src/proto_alpha/bin_sc_rollup_node/store.mli b/src/proto_alpha/bin_sc_rollup_node/store.mli index 4c2d9524da08..9af7a1b7731d 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.mli +++ b/src/proto_alpha/bin_sc_rollup_node/store.mli @@ -23,74 +23,38 @@ (* *) (*****************************************************************************) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/3471 - Use indexed file for append-only instead of Irmin. *) - -(* TODO: https://gitlab.com/tezos/tezos/-/issues/3739 - Refactor the store file to have functors in their own - separate module, and return errors within the Error monad. *) - open Protocol open Alpha_context +open Indexed_store -type +'a store - -include Store_sigs.Store with type 'a t = 'a store - -(** Type of store. The parameter indicates if the store can be written or only - read. *) -type 'a t = ([< `Read | `Write > `Read] as 'a) store - -(** Read/write store {!t}. *) -type rw = Store_sigs.rw t - -(** Read only store {!t}. *) -type ro = Store_sigs.ro t - -(** [close store] closes the store. *) -val close : _ t -> unit Lwt.t - -(** [load mode directory] loads a store from the data persisted in [directory].*) -val load : 'a Store_sigs.mode -> string -> 'a store Lwt.t - -(** [readonly store] returns a read-only version of [store]. *) -val readonly : _ t -> ro +module Irmin_store : Store_sigs.Store module L2_blocks : - Store_sigs.Append_only_map + INDEXED_FILE with type key := Block_hash.t - and type value := Sc_rollup_block.t - and type 'a store := 'a store + and type value := (unit, unit) Sc_rollup_block.block + and type header := Sc_rollup_block.header (** Storage for persisting messages downloaded from the L1 node. *) -module Messages : sig - type info = { - predecessor : Block_hash.t; - predecessor_timestamp : Timestamp.t; - messages : Sc_rollup.Inbox_message.t list; - } - - include - Store_sigs.Append_only_map - with type key := Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t - and type value := info - and type 'a store := 'a store -end +module Messages : + INDEXED_FILE + with type key := Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + and type value := Sc_rollup.Inbox_message.t list + and type header := Block_hash.t * Timestamp.t (** Aggregated collection of messages from the L1 inbox *) module Inboxes : - Store_sigs.Append_only_map + SIMPLE_INDEXED_FILE with type key := Sc_rollup.Inbox.Hash.t and type value := Sc_rollup.Inbox.t - and type 'a store := 'a store + and type header := unit (** Storage containing commitments and corresponding commitment hashes that the rollup node has knowledge of. *) module Commitments : - Store_sigs.Append_only_map + INDEXABLE_STORE with type key := Sc_rollup.Commitment.Hash.t and type value := Sc_rollup.Commitment.t - and type 'a store := 'a store (** Storage mapping commitment hashes to the level when they were published by the rollup node. It only contains hashes of commitments published by this @@ -106,27 +70,18 @@ module Commitments_published_at_level : sig } include - Store_sigs.Map + INDEXABLE_STORE with type key := Sc_rollup.Commitment.Hash.t and type value := element - and type 'a store := 'a store end -module L2_head : - Store_sigs.Mutable_value - with type value := Sc_rollup_block.t - and type 'a store := 'a store +module L2_head : SINGLETON_STORE with type value := Sc_rollup_block.t module Last_finalized_head : - Store_sigs.Mutable_value - with type value := Sc_rollup_block.t - and type 'a store := 'a store + SINGLETON_STORE with type value := Sc_rollup_block.t module Levels_to_hashes : - Store_sigs.Map - with type key := int32 - and type value := Block_hash.t - and type 'a store := 'a store + INDEXABLE_STORE with type key := int32 and type value := Block_hash.t (** Published slot headers per block hash, stored as a list of bindings from [Dal_slot_index.t] @@ -137,13 +92,13 @@ module Dal_slots_headers : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t and type value := Dal.Slot.Header.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t module Dal_confirmed_slots_history : Store_sigs.Append_only_map with type key := Block_hash.t and type value := Dal.Slots_history.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** Confirmed DAL slots histories cache. See documentation of {Dal_slot_repr.Slots_history} for more details. *) @@ -151,7 +106,7 @@ module Dal_confirmed_slots_histories : Store_sigs.Append_only_map with type key := Block_hash.t and type value := Dal.Slots_history.History_cache.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** [Dal_slot_pages] is a [Store_utils.Nested_map] used to store the contents of dal slots fetched by the rollup node, as a list of pages. The values of @@ -164,7 +119,7 @@ module Dal_slot_pages : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t * Dal.Page.Index.t and type value := Dal.Page.content - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** [Dal_processed_slots] is a [Store_utils.Nested_map] used to store the processing status of dal slots content fetched by the rollup node. The values of @@ -178,4 +133,39 @@ module Dal_processed_slots : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t and type value := [`Confirmed | `Unconfirmed] - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t + +type +'a store = { + l2_blocks : 'a L2_blocks.t; + messages : 'a Messages.t; + inboxes : 'a Inboxes.t; + commitments : 'a Commitments.t; + commitments_published_at_level : 'a Commitments_published_at_level.t; + l2_head : 'a L2_head.t; + last_finalized_head : 'a Last_finalized_head.t; + levels_to_hashes : 'a Levels_to_hashes.t; + irmin_store : 'a Irmin_store.t; +} + +(** Type of store. The parameter indicates if the store can be written or only + read. *) +type 'a t = ([< `Read | `Write > `Read] as 'a) store + +(** Read/write store {!t}. *) +type rw = Store_sigs.rw t + +(** Read only store {!t}. *) +type ro = Store_sigs.ro t + +(** [close store] closes the store. *) +val close : _ t -> unit tzresult Lwt.t + +(** [load mode directory] loads a store from the data persisted in + [directory]. If [mode] is {!Store_sigs.Read_only}, then the indexes and + irmin store will be opened in readonly mode and only read operations will be + permitted. This allows to open a store for read access that is already + opened in {!Store_sigs.Read_write} mode in another process. *) +val load : 'a Store_sigs.mode -> string -> 'a store tzresult Lwt.t + +(** [readonly store] returns a read-only version of [store]. *) +val readonly : _ t -> ro -- GitLab From c788783358686c9fd6129877eb1b26e59b78597c Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Mon, 23 Jan 2023 17:15:08 +0100 Subject: [PATCH 2/5] SCORU/Node: new interface for store access through node context --- .../RPC_directory_helpers.ml | 14 +- .../bin_sc_rollup_node/RPC_server.ml | 100 ++++--- .../bin_sc_rollup_node/commitment.ml | 54 ++-- src/proto_alpha/bin_sc_rollup_node/daemon.ml | 20 +- src/proto_alpha/bin_sc_rollup_node/inbox.ml | 4 +- .../bin_sc_rollup_node/node_context.ml | 266 +++++++++++------- .../bin_sc_rollup_node/node_context.mli | 65 +++-- .../bin_sc_rollup_node/refutation_game.ml | 13 +- 8 files changed, 315 insertions(+), 221 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_directory_helpers.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_directory_helpers.ml index c60df3e8a355..8f29f3dbe634 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_directory_helpers.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_directory_helpers.ml @@ -68,27 +68,23 @@ end module Block_directory_helpers = struct let get_head store = let open Lwt_result_syntax in - let*! head = Node_context.last_processed_head_opt store in + let* head = Node_context.last_processed_head_opt store in match head with | None -> failwith "No head" | Some {header = {block_hash; _}; _} -> return block_hash let get_finalized node_ctxt = let open Lwt_result_syntax in - let*! head = Node_context.get_finalized_head_opt node_ctxt in + let* head = Node_context.get_finalized_head_opt node_ctxt in match head with | None -> failwith "No finalized head" | Some {header = {block_hash; _}; _} -> return block_hash let get_last_cemented (node_ctxt : _ Node_context.t) = - let open Lwt_result_syntax in protect @@ fun () -> - let* lcc_hash = - Node_context.hash_of_level - node_ctxt - (Alpha_context.Raw_level.to_int32 node_ctxt.lcc.level) - in - return lcc_hash + Node_context.hash_of_level + node_ctxt + (Alpha_context.Raw_level.to_int32 node_ctxt.lcc.level) let block_of_prefix node_ctxt block = match block with diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 15859eb7ce0a..3189cc9b3af5 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -30,18 +30,19 @@ open RPC_directory_helpers open Protocol let get_head_hash_opt node_ctxt = - let open Lwt_option_syntax in - let+ {header = {block_hash; _}; _} = - Node_context.last_processed_head_opt node_ctxt - in - block_hash + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res let get_head_level_opt node_ctxt = - let open Lwt_option_syntax in - let+ {header = {level; _}; _} = - Node_context.last_processed_head_opt node_ctxt - in - Alpha_context.Raw_level.to_int32 level + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {level; _}; _} -> + Alpha_context.Raw_level.to_int32 level) + res module Slot_pages_map = struct open Protocol @@ -146,9 +147,7 @@ module Common = struct let () = Block_directory.register0 Sc_rollup_services.Global.Block.block @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let*! b = Node_context.get_full_l2_block node_ctxt block in - return b + Node_context.get_full_l2_block node_ctxt block let () = Block_directory.register0 Sc_rollup_services.Global.Block.num_messages @@ -166,11 +165,11 @@ module Common = struct let () = Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt >>= return + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt let () = Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt >>= return + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt let () = Block_directory.register0 Sc_rollup_services.Global.Block.hash @@ -297,44 +296,41 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment @@ fun node_ctxt () () -> let open Lwt_result_syntax in - let*! res = - let open Lwt_option_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let* commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - return (commitment, commitment_hash) - in - return res + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment let () = Local_directory.register0 Sc_rollup_services.Local.last_published_commitment @@ fun node_ctxt () () -> let open Lwt_result_syntax in - let*! result = - let open Lwt_option_syntax in - let*? commitment = node_ctxt.lpc in - let hash = - Alpha_context.Sc_rollup.Commitment.hash_uncarbonated commitment - in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let*! published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return (commitment, hash, first_published, published) - in - return result + match node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = + Alpha_context.Sc_rollup.Commitment.hash_uncarbonated commitment + in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) let () = Block_directory.register0 Sc_rollup_services.Global.Block.status @@ -424,7 +420,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = let open Alpha_context in - let open Lwt_syntax in + let open Lwt_result_syntax in let+ finalized_head = Node_context.get_finalized_head_opt node_ctxt in let finalized = match finalized_head with @@ -457,7 +453,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct | Some (Injected info) -> return (Sc_rollup_services.Injected info) | Some (Included info) -> ( - let*! inbox_info = + let* inbox_info = inbox_info_of_level node_ctxt info.l1_level in let commitment_level = @@ -467,7 +463,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct | None -> return (Sc_rollup_services.Included (info, inbox_info)) | Some commitment_level -> ( - let*! block = + let* block = Node_context.find_l2_block_by_level node_ctxt (Alpha_context.Raw_level.to_int32 commitment_level) @@ -484,7 +480,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct block.header.commitment_hash in (* Commitment computed *) - let*! published_at = + let* published_at = Node_context.commitment_published_at_level node_ctxt commitment_hash diff --git a/src/proto_alpha/bin_sc_rollup_node/commitment.ml b/src/proto_alpha/bin_sc_rollup_node/commitment.ml index c23d02958c07..69f46477f999 100644 --- a/src/proto_alpha/bin_sc_rollup_node/commitment.ml +++ b/src/proto_alpha/bin_sc_rollup_node/commitment.ml @@ -44,6 +44,21 @@ open Protocol open Alpha_context +module Lwt_result_option_syntax = struct + let ( let** ) a f = + let open Lwt_result_syntax in + let* a = a in + match a with None -> return_none | Some a -> f a +end + +module Lwt_result_option_list_syntax = struct + (** A small monadic combinator to return an empty list on None results. *) + let ( let*& ) x f = + let open Lwt_result_syntax in + let* x = x in + match x with None -> return_nil | Some x -> f x +end + let add_level level increment = (* We only use this function with positive increments so it is safe *) if increment < 0 then invalid_arg "Commitment.add_level negative increment" ; @@ -161,13 +176,13 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct match commitment with | None -> return_none | Some commitment -> - let*! commitment_hash = + let* commitment_hash = Node_context.save_commitment node_ctxt commitment in return_some commitment_hash let missing_commitments (node_ctxt : _ Node_context.t) = - let open Lwt_syntax in + let open Lwt_result_syntax in let lpc_level = match node_ctxt.lpc with | None -> node_ctxt.genesis_info.level @@ -244,7 +259,7 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct (* Configured to not publish commitments *) return_unit | Some source -> - let*! commitments = missing_commitments node_ctxt in + let* commitments = missing_commitments node_ctxt in List.iter_es (publish_commitment node_ctxt ~source) commitments let publish_single_commitment node_ctxt commitment_hash = @@ -264,11 +279,12 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct (* Commitments can only be cemented after [sc_rollup_challenge_window] has passed since they were first published. *) let earliest_cementing_level node_ctxt commitment_hash = - let open Lwt_option_syntax in - let+ {first_published_at_level; _} = + let open Lwt_result_option_syntax in + let** {first_published_at_level; _} = Node_context.commitment_published_at_level node_ctxt commitment_hash in - add_level first_published_at_level (sc_rollup_challenge_window node_ctxt) + return_some + @@ add_level first_published_at_level (sc_rollup_challenge_window node_ctxt) (** [latest_cementable_commitment node_ctxt head] is the most recent commitment hash that could be cemented in [head]'s successor if: @@ -280,15 +296,17 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct start the search for cementable commitments. *) let latest_cementable_commitment (node_ctxt : _ Node_context.t) (head : Sc_rollup_block.t) = - let open Lwt_option_syntax in + let open Lwt_result_option_syntax in let commitment_hash = Sc_rollup_block.most_recent_commitment head.header in - let* commitment = Node_context.find_commitment node_ctxt commitment_hash in - let*? cementable_level_bound = - sub_level commitment.inbox_level (sc_rollup_challenge_window node_ctxt) + let** commitment = Node_context.find_commitment node_ctxt commitment_hash in + let** cementable_level_bound = + return + @@ sub_level commitment.inbox_level (sc_rollup_challenge_window node_ctxt) in - if Raw_level.(cementable_level_bound <= node_ctxt.lcc.level) then fail + if Raw_level.(cementable_level_bound <= node_ctxt.lcc.level) then + return_none else - let* cementable_bound_block = + let** cementable_bound_block = Node_context.find_l2_block_by_level node_ctxt (Raw_level.to_int32 cementable_level_bound) @@ -296,20 +314,14 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct let cementable_commitment = Sc_rollup_block.most_recent_commitment cementable_bound_block.header in - return cementable_commitment + return_some cementable_commitment let cementable_commitments (node_ctxt : _ Node_context.t) = let open Lwt_result_syntax in - let ( let*& ) x f = - (* A small monadic combinator to return an empty list of cementable - commitments on None results. *) - let*! x = x in - match x with None -> return_nil | Some x -> f x - in + let open Lwt_result_option_list_syntax in let*& head = Node_context.last_processed_head_opt node_ctxt in let head_level = head.header.level in let rec gather acc (commitment_hash : Sc_rollup.Commitment.Hash.t) = - let open Lwt_syntax in let* commitment = Node_context.find_commitment node_ctxt commitment_hash in @@ -343,7 +355,7 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct let*& latest_cementable_commitment = latest_cementable_commitment node_ctxt head in - let*! cementable = gather [] latest_cementable_commitment in + let* cementable = gather [] latest_cementable_commitment in match cementable with | [] -> return_nil | first_cementable :: _ -> diff --git a/src/proto_alpha/bin_sc_rollup_node/daemon.ml b/src/proto_alpha/bin_sc_rollup_node/daemon.ml index 9acf456b1041..32554eeb1d2a 100644 --- a/src/proto_alpha/bin_sc_rollup_node/daemon.ml +++ b/src/proto_alpha/bin_sc_rollup_node/daemon.ml @@ -51,7 +51,7 @@ module Make (PVM : Pvm.S) = struct let commitment_hash = Sc_rollup.Commitment.hash_uncarbonated commitment in - let*! () = + let* () = Node_context.set_commitment_published_at_level node_ctxt commitment_hash @@ -67,13 +67,13 @@ module Make (PVM : Pvm.S) = struct {published_at_level; staked_hash = their_commitment_hash; _} ) -> (* Commitment published by someone else *) (* We first register the publication information *) - let*! known_commitment = + let* known_commitment = Node_context.commitment_exists node_ctxt their_commitment_hash in let* () = if not known_commitment then return_unit else - let*! republication = + let* republication = Node_context.commitment_was_published node_ctxt ~source:Anyone @@ -81,7 +81,7 @@ module Make (PVM : Pvm.S) = struct in if republication then return_unit else - let*! () = + let* () = Node_context.set_commitment_published_at_level node_ctxt their_commitment_hash @@ -259,7 +259,7 @@ module Make (PVM : Pvm.S) = struct let rec processed_finalized_block (node_ctxt : _ Node_context.t) Layer1.({hash; level} as block) = let open Lwt_result_syntax in - let*! last_finalized = Node_context.get_finalized_head_opt node_ctxt 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 @@ -273,14 +273,14 @@ module Make (PVM : Pvm.S) = struct in let*! () = Daemon_event.head_processing hash 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 hash in return_unit let process_head (node_ctxt : _ Node_context.t) Layer1.({hash; level} as head) = let open Lwt_result_syntax in let*! () = Daemon_event.head_processing hash level ~finalized:false in - let*! () = Node_context.save_level node_ctxt head in + let* () = Node_context.save_level node_ctxt head in let* inbox_hash, inbox, inbox_witness, messages, ctxt = Inbox.process_head node_ctxt head in @@ -334,7 +334,7 @@ module Make (PVM : Pvm.S) = struct head in let* () = processed_finalized_block node_ctxt finalized_block in - let*! () = Node_context.save_l2_head node_ctxt l2_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) in @@ -362,7 +362,7 @@ module Make (PVM : Pvm.S) = struct imply the processing of head~3, etc). *) let on_layer_1_head node_ctxt head = let open Lwt_result_syntax in - let*! old_head = Node_context.last_processed_head_opt node_ctxt in + let* old_head = Node_context.last_processed_head_opt node_ctxt in let old_head = match old_head with | Some h -> @@ -463,7 +463,7 @@ module Make (PVM : Pvm.S) = struct let* () = Injector.shutdown () in let* () = message "Shutting down Batcher@." in let* () = Components.Batcher.shutdown () in - let* () = Node_context.close node_ctxt in + let* (_ : unit tzresult) = Node_context.close node_ctxt in let* () = Event.shutdown_node exit_status in Tezos_base_unix.Internal_event_unix.close () diff --git a/src/proto_alpha/bin_sc_rollup_node/inbox.ml b/src/proto_alpha/bin_sc_rollup_node/inbox.ml index 32eae4f34640..c1db7fda7d65 100644 --- a/src/proto_alpha/bin_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/bin_sc_rollup_node/inbox.ml @@ -167,7 +167,7 @@ let process_head (node_ctxt : _ Node_context.t) in Metrics.Inbox.Stats.head_messages_list := messages_with_protocol_internal_messages ; - let*! () = + let* () = Node_context.save_messages node_ctxt witness_hash @@ -178,7 +178,7 @@ let process_head (node_ctxt : _ Node_context.t) } in let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - let*! inbox_hash = Node_context.save_inbox node_ctxt inbox in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in return ( inbox_hash, inbox, diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.ml b/src/proto_alpha/bin_sc_rollup_node/node_context.ml index 553e4ae7d5dc..c5f129fb208b 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.ml @@ -125,7 +125,7 @@ let init (cctxt : Protocol_client_context.full) dal_cctxt ~data_dir mode _; } as configuration) = let open Lwt_result_syntax in - let*! store = Store.load mode Configuration.(default_storage_dir data_dir) in + let* store = Store.load mode Configuration.(default_storage_dir data_dir) in let*! context = Context.load mode (Configuration.default_context_dir data_dir) in @@ -161,24 +161,26 @@ let init (cctxt : Protocol_client_context.full) dal_cctxt ~data_dir mode } let close {cctxt; store; context; l1_ctxt; _} = - let open Lwt_syntax in + let open Lwt_result_syntax in let message = cctxt#message in - let* () = message "Shutting down L1@." in - let* () = Layer1.shutdown l1_ctxt in - let* () = message "Closing context@." in - let* () = Context.close context in - let* () = message "Closing store@." in + let*! () = message "Shutting down L1@." in + let*! () = Layer1.shutdown l1_ctxt in + let*! () = message "Closing context@." in + let*! () = Context.close context in + let*! () = message "Closing store@." in let* () = Store.close store in return_unit let checkout_context node_ctxt block_hash = let open Lwt_result_syntax in - let*! l2_block = Store.L2_blocks.find node_ctxt.store block_hash in + let* l2_header = + Store.L2_blocks.header node_ctxt.store.l2_blocks block_hash + in let*? context_hash = - match l2_block with + match l2_header with | None -> error (Sc_rollup_node_errors.Cannot_checkout_context (block_hash, None)) - | Some {header = {context; _}; _} -> ok context + | Some {context; _} -> ok context in let*! ctxt = Context.checkout node_ctxt.context context_hash in match ctxt with @@ -218,161 +220,188 @@ let trace_lwt_result_with x = x let hash_of_level_opt {store; cctxt; _} level = - let open Lwt_syntax in - let* hash = Store.Levels_to_hashes.find store level in + let open Lwt_result_syntax in + let* hash = Store.Levels_to_hashes.find store.levels_to_hashes level in match hash with | Some hash -> return_some hash | None -> - let+ hash = + let*! hash = Tezos_shell_services.Shell_services.Blocks.hash cctxt ~chain:cctxt#chain ~block:(`Level level) () in - Result.to_option hash + return (Result.to_option hash) let hash_of_level node_ctxt level = let open Lwt_result_syntax in - let*! hash = hash_of_level_opt node_ctxt level in + let* hash = hash_of_level_opt node_ctxt level in match hash with | Some h -> return h | None -> failwith "Cannot retrieve hash of level %ld" level let level_of_hash {l1_ctxt; store; _} hash = let open Lwt_result_syntax in - let*! block = Store.L2_blocks.find store hash in - match block with - | Some {header = {level; _}; _} -> return (Raw_level.to_int32 level) + let* l2_header = Store.L2_blocks.header store.l2_blocks hash in + match l2_header with + | Some {level; _} -> return (Raw_level.to_int32 level) | None -> let+ {level; _} = Layer1.fetch_tezos_shell_header l1_ctxt hash in level let save_level {store; _} Layer1.{hash; level} = - Store.Levels_to_hashes.add store level hash + Store.Levels_to_hashes.add store.levels_to_hashes level hash let save_l2_head {store; _} (head : Sc_rollup_block.t) = - let open Lwt_syntax in - let* () = Store.L2_blocks.add store head.header.block_hash head in - Store.L2_head.set store head + let open Lwt_result_syntax in + let head_info = {head with header = (); content = ()} in + let* () = + Store.L2_blocks.append + store.l2_blocks + ~key:head.header.block_hash + ~header:head.header + ~value:head_info + in + Store.L2_head.write store.l2_head head -let is_processed {store; _} head = Store.L2_blocks.mem store head +let is_processed {store; _} head = Store.L2_blocks.mem store.l2_blocks head -let last_processed_head_opt {store; _} = Store.L2_head.find store +let last_processed_head_opt {store; _} = Store.L2_head.read store.l2_head let mark_finalized_head {store; _} head_hash = - let open Lwt_syntax in - let* block = Store.L2_blocks.find store head_hash in + let open Lwt_result_syntax in + let* block = Store.L2_blocks.read store.l2_blocks head_hash in match block with | None -> return_unit - | Some block -> Store.Last_finalized_head.set store block + | Some (block_info, header) -> + let block = {block_info with header} in + Store.Last_finalized_head.write store.last_finalized_head block -let get_finalized_head_opt {store; _} = Store.Last_finalized_head.find store +let get_finalized_head_opt {store; _} = + Store.Last_finalized_head.read store.last_finalized_head (* TODO: https://gitlab.com/tezos/tezos/-/issues/4532 Make this logarithmic, by storing pointers to muliple predecessor and by dichotomy. *) let block_before {store; _} tick = let open Lwt_result_syntax in - let*! head = Store.L2_head.find store in + let* head = Store.L2_head.read store.l2_head in match head with | None -> return_none | Some head -> let rec search block_hash = - let*! block = Store.L2_blocks.find store block_hash in + let* block = Store.L2_blocks.read store.l2_blocks block_hash in match block with | None -> failwith "Missing block %a" Block_hash.pp block_hash - | Some block -> - if Sc_rollup.Tick.(block.initial_tick <= tick) then - return_some block - else search block.header.predecessor + | Some (info, header) -> + if Sc_rollup.Tick.(info.initial_tick <= tick) then + return_some {info with header} + else search header.predecessor in search head.header.block_hash let get_l2_block {store; _} block_hash = - trace_lwt_with "Could not retrieve L2 block for %a" Block_hash.pp block_hash - @@ Store.L2_blocks.get store block_hash + let open Lwt_result_syntax in + let* block = Store.L2_blocks.read store.l2_blocks block_hash in + match block with + | None -> + failwith "Could not retrieve L2 block for %a" Block_hash.pp block_hash + | Some (info, header) -> return {info with Sc_rollup_block.header} -let find_l2_block {store; _} block_hash = Store.L2_blocks.find store block_hash +let find_l2_block {store; _} block_hash = + let open Lwt_result_syntax in + let+ block = Store.L2_blocks.read store.l2_blocks block_hash in + Option.map (fun (info, header) -> {info with Sc_rollup_block.header}) block let get_l2_block_by_level node_ctxt level = let open Lwt_result_syntax in trace_lwt_result_with "Could not retrieve L2 block at level %ld" level @@ let* block_hash = hash_of_level node_ctxt level in - let*! block = Store.L2_blocks.get node_ctxt.store block_hash in - return block + get_l2_block node_ctxt block_hash let find_l2_block_by_level node_ctxt level = - let open Lwt_option_syntax in + let open Lwt_result_syntax in let* block_hash = hash_of_level_opt node_ctxt level in - Store.L2_blocks.find node_ctxt.store block_hash - -let get_full_l2_block {store; _} block_hash = - let open Lwt_syntax in - let* block = Store.L2_blocks.get store block_hash in - let* inbox = Store.Inboxes.get store block.header.inbox_hash - and* {messages; _} = Store.Messages.get store block.header.inbox_witness - and* commitment = - Option.map_s (Store.Commitments.get store) block.header.commitment_hash - in - return {block with content = {Sc_rollup_block.inbox; messages; commitment}} + match block_hash with + | None -> return_none + | Some block_hash -> find_l2_block node_ctxt block_hash let get_commitment {store; _} commitment_hash = - trace_lwt_with - "Could not retrieve commitment %a" - Sc_rollup.Commitment.Hash.pp - commitment_hash - @@ Store.Commitments.get store commitment_hash + let open Lwt_result_syntax in + let* commitment = Store.Commitments.find store.commitments commitment_hash in + match commitment with + | None -> + failwith + "Could not retrieve commitment %a" + Sc_rollup.Commitment.Hash.pp + commitment_hash + | Some c -> return c -let find_commitment {store; _} hash = Store.Commitments.find store hash +let find_commitment {store; _} hash = + Store.Commitments.find store.commitments hash -let commitment_exists {store; _} hash = Store.Commitments.mem store hash +let commitment_exists {store; _} hash = + Store.Commitments.mem store.commitments hash let save_commitment {store; _} commitment = - let open Lwt_syntax in + let open Lwt_result_syntax in let hash = Sc_rollup.Commitment.hash_uncarbonated commitment in - let+ () = Store.Commitments.add store hash commitment in + let+ () = Store.Commitments.add store.commitments hash commitment in hash let commitment_published_at_level {store; _} commitment = - Store.Commitments_published_at_level.find store commitment + Store.Commitments_published_at_level.find + store.commitments_published_at_level + commitment let set_commitment_published_at_level {store; _} = - Store.Commitments_published_at_level.add store + Store.Commitments_published_at_level.add store.commitments_published_at_level type commitment_source = Anyone | Us let commitment_was_published {store; _} ~source commitment_hash = - let open Lwt_syntax in + let open Lwt_result_syntax in match source with - | Anyone -> Store.Commitments_published_at_level.mem store commitment_hash + | Anyone -> + Store.Commitments_published_at_level.mem + store.commitments_published_at_level + commitment_hash | Us -> ( let+ info = - Store.Commitments_published_at_level.find store commitment_hash + Store.Commitments_published_at_level.find + store.commitments_published_at_level + commitment_hash in match info with | Some {published_at_level = Some _; _} -> true | _ -> false) let get_inbox {store; _} inbox_hash = - trace_lwt_with - "Could not retrieve inbox %a" - Sc_rollup.Inbox.Hash.pp - inbox_hash - @@ Store.Inboxes.get store inbox_hash + let open Lwt_result_syntax in + let* inbox = Store.Inboxes.read store.inboxes inbox_hash in + match inbox with + | None -> + failwith "Could not retrieve inbox %a" Sc_rollup.Inbox.Hash.pp inbox_hash + | Some (i, ()) -> return i -let find_inbox {store; _} hash = Store.Inboxes.find store hash +let find_inbox {store; _} hash = + let open Lwt_result_syntax in + let+ inbox = Store.Inboxes.read store.inboxes hash in + Option.map fst inbox let save_inbox {store; _} inbox = - let open Lwt_syntax in + let open Lwt_result_syntax in let hash = Sc_rollup.Inbox.hash inbox in - let+ () = Store.Inboxes.add store hash inbox in + let+ () = Store.Inboxes.append store.inboxes ~key:hash ~value:inbox in hash -let find_inbox_by_block_hash {store; _} block_hash = - let open Lwt_option_syntax in - let* l2_block = Store.L2_blocks.find store block_hash in - Store.Inboxes.find store l2_block.header.inbox_hash +let find_inbox_by_block_hash ({store; _} as node_ctxt) block_hash = + let open Lwt_result_syntax in + let* header = Store.L2_blocks.header store.l2_blocks block_hash in + match header with + | None -> return_none + | Some {inbox_hash; _} -> find_inbox node_ctxt inbox_hash let genesis_inbox node_ctxt = let genesis_level = Raw_level.to_int32 node_ctxt.genesis_info.level in @@ -382,7 +411,7 @@ let genesis_inbox node_ctxt = let inbox_of_head node_ctxt Layer1.{hash = block_hash; level = block_level} = let open Lwt_result_syntax in - let*! possible_inbox = find_inbox_by_block_hash node_ctxt block_hash in + let* possible_inbox = find_inbox_by_block_hash node_ctxt block_hash in (* Pre-condition: forall l. (l > genesis_level) => inbox[l] <> None. *) match possible_inbox with | None -> @@ -413,16 +442,49 @@ let get_inbox_by_block_hash node_ctxt hash = let* level = level_of_hash node_ctxt hash in inbox_of_head node_ctxt {hash; level} -let get_messages {store; _} messages_hash = - trace_lwt_with - "Could not retrieve messages with payloads merkelized hash %a" - Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp - messages_hash - @@ Store.Messages.get store messages_hash - -let find_messages {store; _} hash = Store.Messages.find store hash +type messages_info = { + predecessor : Block_hash.t; + predecessor_timestamp : Timestamp.t; + messages : Sc_rollup.Inbox_message.t list; +} -let save_messages {store; _} = Store.Messages.add store +let get_messages {store; _} messages_hash = + let open Lwt_result_syntax in + let* msg = Store.Messages.read store.messages messages_hash in + match msg with + | None -> + failwith + "Could not retrieve messages with payloads merkelized hash %a" + Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp + messages_hash + | Some (messages, (predecessor, predecessor_timestamp)) -> + return {predecessor; predecessor_timestamp; messages} + +let find_messages {store; _} hash = + let open Lwt_result_syntax in + let+ msgs = Store.Messages.read store.messages hash in + Option.map + (fun (messages, (predecessor, predecessor_timestamp)) -> + {predecessor; predecessor_timestamp; messages}) + msgs + +let save_messages {store; _} key {predecessor; predecessor_timestamp; messages} + = + Store.Messages.append + store.messages + ~key + ~header:(predecessor, predecessor_timestamp) + ~value:messages + +let get_full_l2_block node_ctxt block_hash = + let open Lwt_result_syntax in + let* block = get_l2_block node_ctxt block_hash in + let* inbox = get_inbox node_ctxt block.header.inbox_hash + and* {messages; _} = get_messages node_ctxt block.header.inbox_witness + and* commitment = + Option.map_es (get_commitment node_ctxt) block.header.commitment_hash + in + return {block with content = {Sc_rollup_block.inbox; messages; commitment}} let get_slot_header {store; _} ~published_in_block_hash slot_index = trace_lwt_with @@ -432,47 +494,49 @@ let get_slot_header {store; _} ~published_in_block_hash slot_index = Block_hash.pp published_in_block_hash @@ Store.Dal_slots_headers.get - store + store.irmin_store ~primary_key:published_in_block_hash ~secondary_key:slot_index let get_all_slot_headers {store; _} ~published_in_block_hash = - Store.Dal_slots_headers.list_values store ~primary_key:published_in_block_hash + Store.Dal_slots_headers.list_values + store.irmin_store + ~primary_key:published_in_block_hash let get_slot_indexes {store; _} ~published_in_block_hash = Store.Dal_slots_headers.list_secondary_keys - store + store.irmin_store ~primary_key:published_in_block_hash let save_slot_header {store; _} ~published_in_block_hash (slot_header : Dal.Slot.Header.t) = Store.Dal_slots_headers.add - store + store.irmin_store ~primary_key:published_in_block_hash ~secondary_key:slot_header.id.index slot_header let processed_slot {store; _} ~confirmed_in_block_hash slot_index = Store.Dal_processed_slots.find - store + store.irmin_store ~primary_key:confirmed_in_block_hash ~secondary_key:slot_index let list_slot_pages {store; _} ~confirmed_in_block_hash = Store.Dal_slot_pages.list_secondary_keys_with_values - store + store.irmin_store ~primary_key:confirmed_in_block_hash let find_slot_page {store; _} ~confirmed_in_block_hash ~slot_index ~page_index = Store.Dal_slot_pages.find - store + store.irmin_store ~primary_key:confirmed_in_block_hash ~secondary_key:(slot_index, page_index) let save_unconfirmed_slot {store; _} current_block_hash slot_index = (* No page is actually saved *) Store.Dal_processed_slots.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:slot_index `Unconfirmed @@ -485,26 +549,26 @@ let save_confirmed_slot {store; _} current_block_hash slot_index pages = List.iteri_s (fun page_number page -> Store.Dal_slot_pages.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:(slot_index, page_number) page) pages in Store.Dal_processed_slots.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:slot_index `Confirmed let find_confirmed_slots_history {store; _} = - Store.Dal_confirmed_slots_history.find store + Store.Dal_confirmed_slots_history.find store.irmin_store let save_confirmed_slots_history {store; _} = - Store.Dal_confirmed_slots_history.add store + Store.Dal_confirmed_slots_history.add store.irmin_store let find_confirmed_slots_histories {store; _} = - Store.Dal_confirmed_slots_histories.find store + Store.Dal_confirmed_slots_histories.find store.irmin_store let save_confirmed_slots_histories {store; _} = - Store.Dal_confirmed_slots_histories.add store + Store.Dal_confirmed_slots_histories.add store.irmin_store diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.mli b/src/proto_alpha/bin_sc_rollup_node/node_context.mli index efffb7619227..c004ce470817 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.mli +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.mli @@ -110,7 +110,7 @@ val init : 'a t tzresult Lwt.t (** Closes the store, context and Layer 1 monitor. *) -val close : _ t -> unit Lwt.t +val close : _ t -> unit tzresult Lwt.t (** [checkout_context node_ctxt block_hash] returns the context at block [block_hash]. *) @@ -136,7 +136,7 @@ type 'a delayed_write = ('a, rw) Delayed_write_monad.t (** [is_processed store hash] returns [true] if the block with [hash] has already been processed by the daemon. *) -val is_processed : _ t -> Block_hash.t -> bool Lwt.t +val is_processed : _ t -> Block_hash.t -> bool tzresult Lwt.t (** [get_l2_block t hash] returns the Layer 2 block known by the rollup node for Layer 1 block [hash]. *) @@ -144,39 +144,42 @@ val get_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.t tzresult Lwt.t (** Same as {!get_l2_block} but returns [None] when the Layer 2 block is not available. *) -val find_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.t option Lwt.t +val find_l2_block : + _ t -> Block_hash.t -> Sc_rollup_block.t option tzresult Lwt.t (** Same as {!get_l2_block} but retrieves the Layer 2 block by its level. *) val get_l2_block_by_level : _ t -> int32 -> Sc_rollup_block.t tzresult Lwt.t (** Same as {!get_l2_block_by_level} but returns [None] when the Layer 2 block is not available. *) -val find_l2_block_by_level : _ t -> int32 -> Sc_rollup_block.t option Lwt.t +val find_l2_block_by_level : + _ t -> int32 -> Sc_rollup_block.t option tzresult Lwt.t (** [get_full_l2_block node_ctxt hash] returns the full L2 block for L1 block hash [hash]. The result contains the L2 block and its content (inbox, messages, commitment). *) -val get_full_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.full Lwt.t +val get_full_l2_block : + _ t -> Block_hash.t -> Sc_rollup_block.full tzresult Lwt.t (** [save_level t head] registers the correspondences [head.level |-> head.hash] in the store. *) -val save_level : rw -> Layer1.head -> unit Lwt.t +val save_level : rw -> Layer1.head -> unit tzresult Lwt.t -(** [save_l2_head t l2_block] remembers that the [l2_block.head] is +(** [save_l2_head t l2_block] remembers that the [l2_block.head] is processed. The system should not have to come back to it. *) -val save_l2_head : rw -> Sc_rollup_block.t -> unit Lwt.t +val save_l2_head : rw -> Sc_rollup_block.t -> unit tzresult Lwt.t (** [last_processed_head_opt store] returns the last processed head if it exists. *) -val last_processed_head_opt : _ t -> Sc_rollup_block.t option Lwt.t +val last_processed_head_opt : _ t -> Sc_rollup_block.t option tzresult Lwt.t (** [mark_finalized_head store head] remembers that the [head] is finalized. By construction, every block whose level is smaller than [head]'s is also finalized. *) -val mark_finalized_head : rw -> Block_hash.t -> unit Lwt.t +val mark_finalized_head : rw -> Block_hash.t -> unit tzresult Lwt.t (** [last_finalized_head_opt store] returns the last finalized head if it exists. *) -val get_finalized_head_opt : _ t -> Sc_rollup_block.t option Lwt.t +val get_finalized_head_opt : _ t -> Sc_rollup_block.t option tzresult Lwt.t (** [hash_of_level node_ctxt level] returns the current block hash for a given [level]. *) @@ -184,7 +187,7 @@ val hash_of_level : _ t -> int32 -> Block_hash.t tzresult Lwt.t (** [hash_of_level_opt] is like {!hash_of_level} but returns [None] if the [level] is not known. *) -val hash_of_level_opt : _ t -> int32 -> Block_hash.t option Lwt.t +val hash_of_level_opt : _ t -> int32 -> Block_hash.t option tzresult Lwt.t (** [level_of_hash node_ctxt hash] returns the level for Tezos block hash [hash] if it is known by the Tezos Layer 1 node. *) @@ -205,16 +208,19 @@ val get_commitment : (** Same as {!get_commitment} but returns [None] if this commitment hash is not known by the rollup node. *) val find_commitment : - _ t -> Sc_rollup.Commitment.Hash.t -> Sc_rollup.Commitment.t option Lwt.t + _ t -> + Sc_rollup.Commitment.Hash.t -> + Sc_rollup.Commitment.t option tzresult Lwt.t (** [commitment_exists t hash] returns [true] if the commitment with [hash] is known (i.e. stored) by the rollup node. *) -val commitment_exists : _ t -> Sc_rollup.Commitment.Hash.t -> bool Lwt.t +val commitment_exists : + _ t -> Sc_rollup.Commitment.Hash.t -> bool tzresult Lwt.t (** [save_commitment t commitment] saves a commitment in the store an returns is hash. *) val save_commitment : - rw -> Sc_rollup.Commitment.t -> Sc_rollup.Commitment.Hash.t Lwt.t + rw -> Sc_rollup.Commitment.t -> Sc_rollup.Commitment.Hash.t tzresult Lwt.t (** [commitment_published_at_level t hash] returns the levels at which the commitment was first published and the one at which it was included by in a @@ -224,7 +230,7 @@ val save_commitment : val commitment_published_at_level : _ t -> Sc_rollup.Commitment.Hash.t -> - Store.Commitments_published_at_level.element option Lwt.t + Store.Commitments_published_at_level.element option tzresult Lwt.t (** [save_commitment_published_at_level t hash levels] saves the publication/inclusion information for a commitment with [hash]. *) @@ -232,7 +238,7 @@ val set_commitment_published_at_level : rw -> Sc_rollup.Commitment.Hash.t -> Store.Commitments_published_at_level.element -> - unit Lwt.t + unit tzresult Lwt.t type commitment_source = Anyone | Us @@ -241,21 +247,32 @@ type commitment_source = Anyone | Us the publication status for commitments we published ourselves [`Us] or that [`Anyone] published. *) val commitment_was_published : - _ t -> source:commitment_source -> Sc_rollup.Commitment.Hash.t -> bool Lwt.t + _ t -> + source:commitment_source -> + Sc_rollup.Commitment.Hash.t -> + bool tzresult Lwt.t (** {3 Inboxes} *) +type messages_info = { + predecessor : Block_hash.t; + predecessor_timestamp : Timestamp.t; + messages : Sc_rollup.Inbox_message.t list; +} + (** [get_inbox t inbox_hash] retrieves the inbox whose hash is [inbox_hash] from the rollup node's storage. *) val get_inbox : _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t tzresult Lwt.t (** Same as {!get_inbox} but returns [None] if this inbox is not known. *) -val find_inbox : _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t option Lwt.t +val find_inbox : + _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t option tzresult Lwt.t (** [save_inbox t inbox] remembers the [inbox] in the storage. It is associated to its hash which is returned. *) -val save_inbox : rw -> Sc_rollup.Inbox.t -> Sc_rollup.Inbox.Hash.t Lwt.t +val save_inbox : + rw -> Sc_rollup.Inbox.t -> Sc_rollup.Inbox.Hash.t tzresult Lwt.t (** [inbox_of_head node_ctxt block] returns the latest inbox at the given [block]. This function always returns [inbox] for all levels at and @@ -275,13 +292,13 @@ val genesis_inbox : _ t -> Sc_rollup.Inbox.t tzresult Lwt.t val get_messages : _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info tzresult Lwt.t + messages_info tzresult Lwt.t (** Same as {!get_messages} but returns [None] if the payloads hash is not known. *) val find_messages : _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info option Lwt.t + messages_info option tzresult Lwt.t (** [save_messages t payloads_hash messages] associates the list of [messages] to the [payloads_hash]. The payload hash must be computed by calling, @@ -289,8 +306,8 @@ val find_messages : val save_messages : rw -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info -> - unit Lwt.t + messages_info -> + unit tzresult Lwt.t (** {3 DAL} *) diff --git a/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml b/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml index cafd561296a1..e231d517da4e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml +++ b/src/proto_alpha/bin_sc_rollup_node/refutation_game.ml @@ -224,9 +224,18 @@ module Make (Interpreter : Interpreter.S) : let inbox = snapshot let get_history inbox_hash = - let open Lwt_option_syntax in + let open Lwt_syntax in let+ inbox = Node_context.find_inbox node_ctxt inbox_hash in - Sc_rollup.Inbox.take_snapshot inbox + match inbox with + | Error err -> + Format.kasprintf + Stdlib.failwith + "Refutation game: Cannot get inbox history for %a, %a" + Sc_rollup.Inbox.Hash.pp + inbox_hash + pp_print_trace + err + | Ok inbox -> Option.map Sc_rollup.Inbox.take_snapshot inbox let get_payloads_history witness = Lwt.map -- GitLab From ddd2a2dfeea4247a8f9b151ccb86eb89e25f1801 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Fri, 6 Jan 2023 11:16:24 +0100 Subject: [PATCH 3/5] SCORU/Node: store number of messages in index (header) for efficiency This allows to not read all messages from the disk when we care only about their number. --- .../bin_sc_rollup_node/RPC_server.ml | 6 +++--- .../bin_sc_rollup_node/node_context.ml | 18 +++++++++++++++--- .../bin_sc_rollup_node/node_context.mli | 6 ++++++ src/proto_alpha/bin_sc_rollup_node/store.ml | 5 +++-- src/proto_alpha/bin_sc_rollup_node/store.mli | 2 +- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml index 3189cc9b3af5..add034f04ef6 100644 --- a/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/bin_sc_rollup_node/RPC_server.ml @@ -154,10 +154,10 @@ module Common = struct @@ fun (node_ctxt, block) () () -> let open Lwt_result_syntax in let* l2_block = Node_context.get_l2_block node_ctxt block in - let* {messages; _} = - Node_context.get_messages node_ctxt l2_block.header.inbox_witness + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness in - return @@ Z.of_int (List.length messages) + Z.of_int num_messages let () = Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.ml b/src/proto_alpha/bin_sc_rollup_node/node_context.ml index c5f129fb208b..a9d5599f810e 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.ml @@ -457,23 +457,35 @@ let get_messages {store; _} messages_hash = "Could not retrieve messages with payloads merkelized hash %a" Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp messages_hash - | Some (messages, (predecessor, predecessor_timestamp)) -> + | Some (messages, (predecessor, predecessor_timestamp, _num_messages)) -> return {predecessor; predecessor_timestamp; messages} let find_messages {store; _} hash = let open Lwt_result_syntax in let+ msgs = Store.Messages.read store.messages hash in Option.map - (fun (messages, (predecessor, predecessor_timestamp)) -> + (fun (messages, (predecessor, predecessor_timestamp, _num_messages)) -> {predecessor; predecessor_timestamp; messages}) msgs +let get_num_messages {store; _} hash = + let open Lwt_result_syntax in + let* header = Store.Messages.header store.messages hash in + match header with + | None -> + failwith + "Could not retrieve number of messages for inbox witness %a" + Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp + hash + | Some (_predecessor, _predecessor_timestamp, num_messages) -> + return num_messages + let save_messages {store; _} key {predecessor; predecessor_timestamp; messages} = Store.Messages.append store.messages ~key - ~header:(predecessor, predecessor_timestamp) + ~header:(predecessor, predecessor_timestamp, List.length messages) ~value:messages let get_full_l2_block node_ctxt block_hash = diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.mli b/src/proto_alpha/bin_sc_rollup_node/node_context.mli index c004ce470817..0ed4fe46cc67 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.mli +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.mli @@ -300,6 +300,12 @@ val find_messages : Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> messages_info option tzresult Lwt.t +(** [get_num_messages t witness_hash] retrieves (without reading all the messages + from disk) the number of messages for the inbox witness [witness_hash] + stored by the rollup node. *) +val get_num_messages : + _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> int tzresult Lwt.t + (** [save_messages t payloads_hash messages] associates the list of [messages] to the [payloads_hash]. The payload hash must be computed by calling, e.g. {!Sc_rollup.Inbox.add_all_messages}. *) diff --git a/src/proto_alpha/bin_sc_rollup_node/store.ml b/src/proto_alpha/bin_sc_rollup_node/store.ml index 9b459d85b7ed..5eb38c22c5f5 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.ml +++ b/src/proto_alpha/bin_sc_rollup_node/store.ml @@ -104,15 +104,16 @@ module Messages = Data_encoding.(list @@ dynamic_size Sc_rollup.Inbox_message.encoding) module Header = struct - type t = Block_hash.t * Timestamp.t + type t = Block_hash.t * Timestamp.t * int let name = "messages_inbox_info" let encoding = let open Data_encoding in - obj2 + obj3 (req "predecessor" Block_hash.encoding) (req "predecessor_timestamp" Timestamp.encoding) + (req "num_messages" int31) let fixed_size = WithExceptions.Option.get ~loc:__LOC__ diff --git a/src/proto_alpha/bin_sc_rollup_node/store.mli b/src/proto_alpha/bin_sc_rollup_node/store.mli index 9af7a1b7731d..11baf20ca241 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.mli +++ b/src/proto_alpha/bin_sc_rollup_node/store.mli @@ -40,7 +40,7 @@ module Messages : INDEXED_FILE with type key := Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t and type value := Sc_rollup.Inbox_message.t list - and type header := Block_hash.t * Timestamp.t + and type header := Block_hash.t * Timestamp.t * int (** Aggregated collection of messages from the L1 inbox *) module Inboxes : -- GitLab From 7377419a75b136f8d65496786a977e25234ce390 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Mon, 23 Jan 2023 18:07:34 +0100 Subject: [PATCH 4/5] SCORU/Node: configurable L2 blocks cache size --- .../bin_sc_rollup_node/configuration.ml | 22 +++++++++++++++---- .../bin_sc_rollup_node/configuration.mli | 5 +++++ .../main_sc_rollup_node_alpha.ml | 1 + .../bin_sc_rollup_node/node_context.ml | 8 ++++++- src/proto_alpha/bin_sc_rollup_node/store.ml | 5 +++-- src/proto_alpha/bin_sc_rollup_node/store.mli | 18 ++++++++++----- 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/proto_alpha/bin_sc_rollup_node/configuration.ml b/src/proto_alpha/bin_sc_rollup_node/configuration.ml index 56a722c5556a..4f34793ffbe4 100644 --- a/src/proto_alpha/bin_sc_rollup_node/configuration.ml +++ b/src/proto_alpha/bin_sc_rollup_node/configuration.ml @@ -64,6 +64,7 @@ type t = { dal_node_port : int; batcher : batcher; injector_retention_period : int; + l2_blocks_cache_size : int; } let default_data_dir = @@ -225,6 +226,8 @@ let max_injector_retention_period = let default_injector_retention_period = 2048 +let default_l2_blocks_cache_size = 64 + let string_of_purpose = function | Publish -> "publish" | Add_messages -> "add_messages" @@ -495,6 +498,7 @@ let encoding : t Data_encoding.t = dal_node_port; batcher; injector_retention_period; + l2_blocks_cache_size; } -> ( ( sc_rollup_address, sc_rollup_node_operators, @@ -505,7 +509,11 @@ let encoding : t Data_encoding.t = fee_parameters, mode, loser_mode ), - (dal_node_addr, dal_node_port, batcher, injector_retention_period) )) + ( dal_node_addr, + dal_node_port, + batcher, + injector_retention_period, + l2_blocks_cache_size ) )) (fun ( ( sc_rollup_address, sc_rollup_node_operators, rpc_addr, @@ -515,7 +523,11 @@ let encoding : t Data_encoding.t = fee_parameters, mode, loser_mode ), - (dal_node_addr, dal_node_port, batcher, injector_retention_period) ) -> + ( dal_node_addr, + dal_node_port, + batcher, + injector_retention_period, + l2_blocks_cache_size ) ) -> if injector_retention_period > max_injector_retention_period then Format.ksprintf Stdlib.failwith @@ -535,6 +547,7 @@ let encoding : t Data_encoding.t = dal_node_port; batcher; injector_retention_period; + l2_blocks_cache_size; }) (merge_objs (obj9 @@ -574,14 +587,15 @@ let encoding : t Data_encoding.t = test only!)" Loser_mode.encoding Loser_mode.no_failures)) - (obj4 + (obj5 (dft "DAL node address" string default_dal_node_addr) (dft "DAL node port" int16 default_dal_node_port) (dft "batcher" batcher_encoding default_batcher) (dft "injector_retention_period" uint16 - default_injector_retention_period))) + default_injector_retention_period) + (dft "l2_blocks_cache_size" int31 default_l2_blocks_cache_size))) let check_mode config = let open Result_syntax in diff --git a/src/proto_alpha/bin_sc_rollup_node/configuration.mli b/src/proto_alpha/bin_sc_rollup_node/configuration.mli index 906be2d80421..c0a459fe31a8 100644 --- a/src/proto_alpha/bin_sc_rollup_node/configuration.mli +++ b/src/proto_alpha/bin_sc_rollup_node/configuration.mli @@ -84,6 +84,7 @@ type t = { dal_node_port : int; batcher : batcher; injector_retention_period : int; + l2_blocks_cache_size : int; } (** [make_purpose_map ~default purposes] constructs a purpose map from a list of @@ -145,6 +146,10 @@ val default_batcher : batcher injector will keep in memory. *) val default_injector_retention_period : int +(** [default_l2_blocks_cache_size] is the default number of L2 blocks that are + cached by the rollup node *) +val default_l2_blocks_cache_size : int + (** [max_injector_retention_period] is the maximum allowed value for [injector_retention_period]. *) val max_injector_retention_period : int diff --git a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml index 7bb5a66971c2..e0a0af3858f1 100644 --- a/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml +++ b/src/proto_alpha/bin_sc_rollup_node/main_sc_rollup_node_alpha.ml @@ -293,6 +293,7 @@ let config_init_command = loser_mode; batcher = Configuration.default_batcher; injector_retention_period; + l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size; } in let*? config = check_mode config in diff --git a/src/proto_alpha/bin_sc_rollup_node/node_context.ml b/src/proto_alpha/bin_sc_rollup_node/node_context.ml index a9d5599f810e..e7aaefcb28e7 100644 --- a/src/proto_alpha/bin_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/bin_sc_rollup_node/node_context.ml @@ -122,10 +122,16 @@ let init (cctxt : Protocol_client_context.full) dal_cctxt ~data_dir mode mode = operating_mode; fee_parameters; loser_mode; + l2_blocks_cache_size; _; } as configuration) = let open Lwt_result_syntax in - let* store = Store.load mode Configuration.(default_storage_dir data_dir) in + let* store = + Store.load + mode + ~l2_blocks_cache_size + Configuration.(default_storage_dir data_dir) + in let*! context = Context.load mode (Configuration.default_context_dir data_dir) in diff --git a/src/proto_alpha/bin_sc_rollup_node/store.ml b/src/proto_alpha/bin_sc_rollup_node/store.ml index 5eb38c22c5f5..62b2404dd3ce 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.ml +++ b/src/proto_alpha/bin_sc_rollup_node/store.ml @@ -444,10 +444,11 @@ let close and+ () = Irmin_store.close irmin_store |> Lwt_result.ok in () -let load (type a) (mode : a mode) data_dir : a store tzresult Lwt.t = +let load (type a) (mode : a mode) ~l2_blocks_cache_size data_dir : + a store tzresult Lwt.t = let open Lwt_result_syntax in let path name = Filename.concat data_dir name in - let cache_size = 10_000 in + let cache_size = l2_blocks_cache_size in let* l2_blocks = L2_blocks.load mode ~path:(path "l2_blocks") ~cache_size in let* messages = Messages.load mode ~path:(path "messages") ~cache_size in let* inboxes = Inboxes.load mode ~path:(path "inboxes") ~cache_size in diff --git a/src/proto_alpha/bin_sc_rollup_node/store.mli b/src/proto_alpha/bin_sc_rollup_node/store.mli index 11baf20ca241..252c0f25c100 100644 --- a/src/proto_alpha/bin_sc_rollup_node/store.mli +++ b/src/proto_alpha/bin_sc_rollup_node/store.mli @@ -160,12 +160,18 @@ type ro = Store_sigs.ro t (** [close store] closes the store. *) val close : _ t -> unit tzresult Lwt.t -(** [load mode directory] loads a store from the data persisted in - [directory]. If [mode] is {!Store_sigs.Read_only}, then the indexes and - irmin store will be opened in readonly mode and only read operations will be - permitted. This allows to open a store for read access that is already - opened in {!Store_sigs.Read_write} mode in another process. *) -val load : 'a Store_sigs.mode -> string -> 'a store tzresult Lwt.t +(** [load mode ~l2_blocks_cache_size directory] loads a store from the data + persisted in [directory]. If [mode] is {!Store_sigs.Read_only}, then the + indexes and irmin store will be opened in readonly mode and only read + operations will be permitted. This allows to open a store for read access + that is already opened in {!Store_sigs.Read_write} mode in another + process. [l2_blocks_cache_size] is the number of L2 blocks the rollup node + will keep in memory. *) +val load : + 'a Store_sigs.mode -> + l2_blocks_cache_size:int -> + string -> + 'a store tzresult Lwt.t (** [readonly store] returns a read-only version of [store]. *) val readonly : _ t -> ro -- GitLab From 2dc0269ba3002cb14daf8bc5dd78edabe8a6fb39 Mon Sep 17 00:00:00 2001 From: Alain Mebsout Date: Tue, 3 Jan 2023 19:06:03 +0100 Subject: [PATCH 5/5] SCORU/Node: Backport !7251 to Mumbai - SCORU/Node: Store uses indexed files - SCORU/Node: new interface for store access through node context - SCORU/Node: store number of messages in index (header) for efficiency - SCORU/Node: configurable L2 blocks cache size --- .../RPC_directory_helpers.ml | 14 +- .../bin_sc_rollup_node/RPC_server.ml | 106 +++--- .../bin_sc_rollup_node/commitment.ml | 54 +-- .../bin_sc_rollup_node/configuration.ml | 22 +- .../bin_sc_rollup_node/configuration.mli | 5 + .../bin_sc_rollup_node/daemon.ml | 20 +- .../bin_sc_rollup_node/inbox.ml | 4 +- .../main_sc_rollup_node_016_PtMumbai.ml | 1 + .../bin_sc_rollup_node/node_context.ml | 282 +++++++++----- .../bin_sc_rollup_node/node_context.mli | 71 ++-- .../bin_sc_rollup_node/refutation_game.ml | 13 +- .../bin_sc_rollup_node/store.ml | 345 +++++++++++------- .../bin_sc_rollup_node/store.mli | 130 ++++--- 13 files changed, 647 insertions(+), 420 deletions(-) diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_directory_helpers.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_directory_helpers.ml index c60df3e8a355..8f29f3dbe634 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_directory_helpers.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_directory_helpers.ml @@ -68,27 +68,23 @@ end module Block_directory_helpers = struct let get_head store = let open Lwt_result_syntax in - let*! head = Node_context.last_processed_head_opt store in + let* head = Node_context.last_processed_head_opt store in match head with | None -> failwith "No head" | Some {header = {block_hash; _}; _} -> return block_hash let get_finalized node_ctxt = let open Lwt_result_syntax in - let*! head = Node_context.get_finalized_head_opt node_ctxt in + let* head = Node_context.get_finalized_head_opt node_ctxt in match head with | None -> failwith "No finalized head" | Some {header = {block_hash; _}; _} -> return block_hash let get_last_cemented (node_ctxt : _ Node_context.t) = - let open Lwt_result_syntax in protect @@ fun () -> - let* lcc_hash = - Node_context.hash_of_level - node_ctxt - (Alpha_context.Raw_level.to_int32 node_ctxt.lcc.level) - in - return lcc_hash + Node_context.hash_of_level + node_ctxt + (Alpha_context.Raw_level.to_int32 node_ctxt.lcc.level) let block_of_prefix node_ctxt block = match block with diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_server.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_server.ml index 15859eb7ce0a..add034f04ef6 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_server.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/RPC_server.ml @@ -30,18 +30,19 @@ open RPC_directory_helpers open Protocol let get_head_hash_opt node_ctxt = - let open Lwt_option_syntax in - let+ {header = {block_hash; _}; _} = - Node_context.last_processed_head_opt node_ctxt - in - block_hash + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res let get_head_level_opt node_ctxt = - let open Lwt_option_syntax in - let+ {header = {level; _}; _} = - Node_context.last_processed_head_opt node_ctxt - in - Alpha_context.Raw_level.to_int32 level + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {level; _}; _} -> + Alpha_context.Raw_level.to_int32 level) + res module Slot_pages_map = struct open Protocol @@ -146,19 +147,17 @@ module Common = struct let () = Block_directory.register0 Sc_rollup_services.Global.Block.block @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let*! b = Node_context.get_full_l2_block node_ctxt block in - return b + Node_context.get_full_l2_block node_ctxt block let () = Block_directory.register0 Sc_rollup_services.Global.Block.num_messages @@ fun (node_ctxt, block) () () -> let open Lwt_result_syntax in let* l2_block = Node_context.get_l2_block node_ctxt block in - let* {messages; _} = - Node_context.get_messages node_ctxt l2_block.header.inbox_witness + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness in - return @@ Z.of_int (List.length messages) + Z.of_int num_messages let () = Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address @@ -166,11 +165,11 @@ module Common = struct let () = Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt >>= return + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt let () = Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt >>= return + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt let () = Block_directory.register0 Sc_rollup_services.Global.Block.hash @@ -297,44 +296,41 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment @@ fun node_ctxt () () -> let open Lwt_result_syntax in - let*! res = - let open Lwt_option_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let* commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - return (commitment, commitment_hash) - in - return res + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment let () = Local_directory.register0 Sc_rollup_services.Local.last_published_commitment @@ fun node_ctxt () () -> let open Lwt_result_syntax in - let*! result = - let open Lwt_option_syntax in - let*? commitment = node_ctxt.lpc in - let hash = - Alpha_context.Sc_rollup.Commitment.hash_uncarbonated commitment - in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let*! published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return (commitment, hash, first_published, published) - in - return result + match node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = + Alpha_context.Sc_rollup.Commitment.hash_uncarbonated commitment + in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) let () = Block_directory.register0 Sc_rollup_services.Global.Block.status @@ -424,7 +420,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = let open Alpha_context in - let open Lwt_syntax in + let open Lwt_result_syntax in let+ finalized_head = Node_context.get_finalized_head_opt node_ctxt in let finalized = match finalized_head with @@ -457,7 +453,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct | Some (Injected info) -> return (Sc_rollup_services.Injected info) | Some (Included info) -> ( - let*! inbox_info = + let* inbox_info = inbox_info_of_level node_ctxt info.l1_level in let commitment_level = @@ -467,7 +463,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct | None -> return (Sc_rollup_services.Included (info, inbox_info)) | Some commitment_level -> ( - let*! block = + let* block = Node_context.find_l2_block_by_level node_ctxt (Alpha_context.Raw_level.to_int32 commitment_level) @@ -484,7 +480,7 @@ module Make (Simulation : Simulation.S) (Batcher : Batcher.S) = struct block.header.commitment_hash in (* Commitment computed *) - let*! published_at = + let* published_at = Node_context.commitment_published_at_level node_ctxt commitment_hash diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/commitment.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/commitment.ml index c23d02958c07..69f46477f999 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/commitment.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/commitment.ml @@ -44,6 +44,21 @@ open Protocol open Alpha_context +module Lwt_result_option_syntax = struct + let ( let** ) a f = + let open Lwt_result_syntax in + let* a = a in + match a with None -> return_none | Some a -> f a +end + +module Lwt_result_option_list_syntax = struct + (** A small monadic combinator to return an empty list on None results. *) + let ( let*& ) x f = + let open Lwt_result_syntax in + let* x = x in + match x with None -> return_nil | Some x -> f x +end + let add_level level increment = (* We only use this function with positive increments so it is safe *) if increment < 0 then invalid_arg "Commitment.add_level negative increment" ; @@ -161,13 +176,13 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct match commitment with | None -> return_none | Some commitment -> - let*! commitment_hash = + let* commitment_hash = Node_context.save_commitment node_ctxt commitment in return_some commitment_hash let missing_commitments (node_ctxt : _ Node_context.t) = - let open Lwt_syntax in + let open Lwt_result_syntax in let lpc_level = match node_ctxt.lpc with | None -> node_ctxt.genesis_info.level @@ -244,7 +259,7 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct (* Configured to not publish commitments *) return_unit | Some source -> - let*! commitments = missing_commitments node_ctxt in + let* commitments = missing_commitments node_ctxt in List.iter_es (publish_commitment node_ctxt ~source) commitments let publish_single_commitment node_ctxt commitment_hash = @@ -264,11 +279,12 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct (* Commitments can only be cemented after [sc_rollup_challenge_window] has passed since they were first published. *) let earliest_cementing_level node_ctxt commitment_hash = - let open Lwt_option_syntax in - let+ {first_published_at_level; _} = + let open Lwt_result_option_syntax in + let** {first_published_at_level; _} = Node_context.commitment_published_at_level node_ctxt commitment_hash in - add_level first_published_at_level (sc_rollup_challenge_window node_ctxt) + return_some + @@ add_level first_published_at_level (sc_rollup_challenge_window node_ctxt) (** [latest_cementable_commitment node_ctxt head] is the most recent commitment hash that could be cemented in [head]'s successor if: @@ -280,15 +296,17 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct start the search for cementable commitments. *) let latest_cementable_commitment (node_ctxt : _ Node_context.t) (head : Sc_rollup_block.t) = - let open Lwt_option_syntax in + let open Lwt_result_option_syntax in let commitment_hash = Sc_rollup_block.most_recent_commitment head.header in - let* commitment = Node_context.find_commitment node_ctxt commitment_hash in - let*? cementable_level_bound = - sub_level commitment.inbox_level (sc_rollup_challenge_window node_ctxt) + let** commitment = Node_context.find_commitment node_ctxt commitment_hash in + let** cementable_level_bound = + return + @@ sub_level commitment.inbox_level (sc_rollup_challenge_window node_ctxt) in - if Raw_level.(cementable_level_bound <= node_ctxt.lcc.level) then fail + if Raw_level.(cementable_level_bound <= node_ctxt.lcc.level) then + return_none else - let* cementable_bound_block = + let** cementable_bound_block = Node_context.find_l2_block_by_level node_ctxt (Raw_level.to_int32 cementable_level_bound) @@ -296,20 +314,14 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct let cementable_commitment = Sc_rollup_block.most_recent_commitment cementable_bound_block.header in - return cementable_commitment + return_some cementable_commitment let cementable_commitments (node_ctxt : _ Node_context.t) = let open Lwt_result_syntax in - let ( let*& ) x f = - (* A small monadic combinator to return an empty list of cementable - commitments on None results. *) - let*! x = x in - match x with None -> return_nil | Some x -> f x - in + let open Lwt_result_option_list_syntax in let*& head = Node_context.last_processed_head_opt node_ctxt in let head_level = head.header.level in let rec gather acc (commitment_hash : Sc_rollup.Commitment.Hash.t) = - let open Lwt_syntax in let* commitment = Node_context.find_commitment node_ctxt commitment_hash in @@ -343,7 +355,7 @@ module Make (PVM : Pvm.S) : Commitment_sig.S with module PVM = PVM = struct let*& latest_cementable_commitment = latest_cementable_commitment node_ctxt head in - let*! cementable = gather [] latest_cementable_commitment in + let* cementable = gather [] latest_cementable_commitment in match cementable with | [] -> return_nil | first_cementable :: _ -> diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.ml index d966625f7806..a7a481cdb5c1 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.ml @@ -64,6 +64,7 @@ type t = { dal_node_port : int; batcher : batcher; injector_retention_period : int; + l2_blocks_cache_size : int; } let default_data_dir = @@ -225,6 +226,8 @@ let max_injector_retention_period = let default_injector_retention_period = 2048 +let default_l2_blocks_cache_size = 64 + let string_of_purpose = function | Publish -> "publish" | Add_messages -> "add_messages" @@ -496,6 +499,7 @@ let encoding : t Data_encoding.t = dal_node_port; batcher; injector_retention_period; + l2_blocks_cache_size; } -> ( ( sc_rollup_address, sc_rollup_node_operators, @@ -506,7 +510,11 @@ let encoding : t Data_encoding.t = fee_parameters, mode, loser_mode ), - (dal_node_addr, dal_node_port, batcher, injector_retention_period) )) + ( dal_node_addr, + dal_node_port, + batcher, + injector_retention_period, + l2_blocks_cache_size ) )) (fun ( ( sc_rollup_address, sc_rollup_node_operators, rpc_addr, @@ -516,7 +524,11 @@ let encoding : t Data_encoding.t = fee_parameters, mode, loser_mode ), - (dal_node_addr, dal_node_port, batcher, injector_retention_period) ) -> + ( dal_node_addr, + dal_node_port, + batcher, + injector_retention_period, + l2_blocks_cache_size ) ) -> if injector_retention_period > max_injector_retention_period then Format.ksprintf Stdlib.failwith @@ -536,6 +548,7 @@ let encoding : t Data_encoding.t = dal_node_port; batcher; injector_retention_period; + l2_blocks_cache_size; }) (merge_objs (obj9 @@ -575,14 +588,15 @@ let encoding : t Data_encoding.t = test only!)" Loser_mode.encoding Loser_mode.no_failures)) - (obj4 + (obj5 (dft "DAL node address" string default_dal_node_addr) (dft "DAL node port" int16 default_dal_node_port) (dft "batcher" batcher_encoding default_batcher) (dft "injector_retention_period" uint16 - default_injector_retention_period))) + default_injector_retention_period) + (dft "l2_blocks_cache_size" int31 default_l2_blocks_cache_size))) let check_mode config = let open Result_syntax in diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.mli b/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.mli index 0248dac64fe3..e055c576bb21 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.mli +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/configuration.mli @@ -84,6 +84,7 @@ type t = { dal_node_port : int; batcher : batcher; injector_retention_period : int; + l2_blocks_cache_size : int; } (** [make_purpose_map ~default purposes] constructs a purpose map from a list of @@ -145,6 +146,10 @@ val default_batcher : batcher injector will keep in memory. *) val default_injector_retention_period : int +(** [default_l2_blocks_cache_size] is the default number of L2 blocks that are + cached by the rollup node *) +val default_l2_blocks_cache_size : int + (** [max_injector_retention_period] is the maximum allowed value for [injector_retention_period]. *) val max_injector_retention_period : int diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/daemon.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/daemon.ml index dca3913d4b50..d2236fd2d7c5 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/daemon.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/daemon.ml @@ -51,7 +51,7 @@ module Make (PVM : Pvm.S) = struct let commitment_hash = Sc_rollup.Commitment.hash_uncarbonated commitment in - let*! () = + let* () = Node_context.set_commitment_published_at_level node_ctxt commitment_hash @@ -67,13 +67,13 @@ module Make (PVM : Pvm.S) = struct {published_at_level; staked_hash = their_commitment_hash; _} ) -> (* Commitment published by someone else *) (* We first register the publication information *) - let*! known_commitment = + let* known_commitment = Node_context.commitment_exists node_ctxt their_commitment_hash in let* () = if not known_commitment then return_unit else - let*! republication = + let* republication = Node_context.commitment_was_published node_ctxt ~source:Anyone @@ -81,7 +81,7 @@ module Make (PVM : Pvm.S) = struct in if republication then return_unit else - let*! () = + let* () = Node_context.set_commitment_published_at_level node_ctxt their_commitment_hash @@ -258,7 +258,7 @@ module Make (PVM : Pvm.S) = struct let rec processed_finalized_block (node_ctxt : _ Node_context.t) Layer1.({hash; level} as block) = let open Lwt_result_syntax in - let*! last_finalized = Node_context.get_finalized_head_opt node_ctxt 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 @@ -272,14 +272,14 @@ module Make (PVM : Pvm.S) = struct in let*! () = Daemon_event.head_processing hash 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 hash in return_unit let process_head (node_ctxt : _ Node_context.t) Layer1.({hash; level} as head) = let open Lwt_result_syntax in let*! () = Daemon_event.head_processing hash level ~finalized:false in - let*! () = Node_context.save_level node_ctxt head in + let* () = Node_context.save_level node_ctxt head in let* inbox_hash, inbox, inbox_witness, messages, ctxt = Inbox.process_head node_ctxt head in @@ -333,7 +333,7 @@ module Make (PVM : Pvm.S) = struct head in let* () = processed_finalized_block node_ctxt finalized_block in - let*! () = Node_context.save_l2_head node_ctxt l2_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) in @@ -361,7 +361,7 @@ module Make (PVM : Pvm.S) = struct imply the processing of head~3, etc). *) let on_layer_1_head node_ctxt head = let open Lwt_result_syntax in - let*! old_head = Node_context.last_processed_head_opt node_ctxt in + let* old_head = Node_context.last_processed_head_opt node_ctxt in let old_head = match old_head with | Some h -> @@ -462,7 +462,7 @@ module Make (PVM : Pvm.S) = struct let* () = Injector.shutdown () in let* () = message "Shutting down Batcher@." in let* () = Components.Batcher.shutdown () in - let* () = Node_context.close node_ctxt in + let* (_ : unit tzresult) = Node_context.close node_ctxt in let* () = Event.shutdown_node exit_status in Tezos_base_unix.Internal_event_unix.close () diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/inbox.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/inbox.ml index 32eae4f34640..c1db7fda7d65 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/inbox.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/inbox.ml @@ -167,7 +167,7 @@ let process_head (node_ctxt : _ Node_context.t) in Metrics.Inbox.Stats.head_messages_list := messages_with_protocol_internal_messages ; - let*! () = + let* () = Node_context.save_messages node_ctxt witness_hash @@ -178,7 +178,7 @@ let process_head (node_ctxt : _ Node_context.t) } in let* () = same_inbox_as_layer_1 node_ctxt head_hash inbox in - let*! inbox_hash = Node_context.save_inbox node_ctxt inbox in + let* inbox_hash = Node_context.save_inbox node_ctxt inbox in return ( inbox_hash, inbox, diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/main_sc_rollup_node_016_PtMumbai.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/main_sc_rollup_node_016_PtMumbai.ml index b5be3825d925..482ca95d488b 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/main_sc_rollup_node_016_PtMumbai.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/main_sc_rollup_node_016_PtMumbai.ml @@ -293,6 +293,7 @@ let config_init_command = loser_mode; batcher = Configuration.default_batcher; injector_retention_period; + l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size; } in let*? config = check_mode config in diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.ml index 84d819ffdff5..0325b84d7333 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.ml @@ -122,10 +122,16 @@ let init (cctxt : Protocol_client_context.full) dal_cctxt ~data_dir mode mode = operating_mode; fee_parameters; loser_mode; + l2_blocks_cache_size; _; } as configuration) = let open Lwt_result_syntax in - let*! store = Store.load mode Configuration.(default_storage_dir data_dir) in + let* store = + Store.load + mode + ~l2_blocks_cache_size + Configuration.(default_storage_dir data_dir) + in let*! context = Context.load mode (Configuration.default_context_dir data_dir) in @@ -161,24 +167,26 @@ let init (cctxt : Protocol_client_context.full) dal_cctxt ~data_dir mode } let close {cctxt; store; context; l1_ctxt; _} = - let open Lwt_syntax in + let open Lwt_result_syntax in let message = cctxt#message in - let* () = message "Shutting down L1@." in - let* () = Layer1.shutdown l1_ctxt in - let* () = message "Closing context@." in - let* () = Context.close context in - let* () = message "Closing store@." in + let*! () = message "Shutting down L1@." in + let*! () = Layer1.shutdown l1_ctxt in + let*! () = message "Closing context@." in + let*! () = Context.close context in + let*! () = message "Closing store@." in let* () = Store.close store in return_unit let checkout_context node_ctxt block_hash = let open Lwt_result_syntax in - let*! l2_block = Store.L2_blocks.find node_ctxt.store block_hash in + let* l2_header = + Store.L2_blocks.header node_ctxt.store.l2_blocks block_hash + in let*? context_hash = - match l2_block with + match l2_header with | None -> error (Sc_rollup_node_errors.Cannot_checkout_context (block_hash, None)) - | Some {header = {context; _}; _} -> ok context + | Some {context; _} -> ok context in let*! ctxt = Context.checkout node_ctxt.context context_hash in match ctxt with @@ -218,161 +226,188 @@ let trace_lwt_result_with x = x let hash_of_level_opt {store; cctxt; _} level = - let open Lwt_syntax in - let* hash = Store.Levels_to_hashes.find store level in + let open Lwt_result_syntax in + let* hash = Store.Levels_to_hashes.find store.levels_to_hashes level in match hash with | Some hash -> return_some hash | None -> - let+ hash = + let*! hash = Tezos_shell_services.Shell_services.Blocks.hash cctxt ~chain:cctxt#chain ~block:(`Level level) () in - Result.to_option hash + return (Result.to_option hash) let hash_of_level node_ctxt level = let open Lwt_result_syntax in - let*! hash = hash_of_level_opt node_ctxt level in + let* hash = hash_of_level_opt node_ctxt level in match hash with | Some h -> return h | None -> failwith "Cannot retrieve hash of level %ld" level let level_of_hash {l1_ctxt; store; _} hash = let open Lwt_result_syntax in - let*! block = Store.L2_blocks.find store hash in - match block with - | Some {header = {level; _}; _} -> return (Raw_level.to_int32 level) + let* l2_header = Store.L2_blocks.header store.l2_blocks hash in + match l2_header with + | Some {level; _} -> return (Raw_level.to_int32 level) | None -> let+ {level; _} = Layer1.fetch_tezos_shell_header l1_ctxt hash in level let save_level {store; _} Layer1.{hash; level} = - Store.Levels_to_hashes.add store level hash + Store.Levels_to_hashes.add store.levels_to_hashes level hash let save_l2_head {store; _} (head : Sc_rollup_block.t) = - let open Lwt_syntax in - let* () = Store.L2_blocks.add store head.header.block_hash head in - Store.L2_head.set store head + let open Lwt_result_syntax in + let head_info = {head with header = (); content = ()} in + let* () = + Store.L2_blocks.append + store.l2_blocks + ~key:head.header.block_hash + ~header:head.header + ~value:head_info + in + Store.L2_head.write store.l2_head head -let is_processed {store; _} head = Store.L2_blocks.mem store head +let is_processed {store; _} head = Store.L2_blocks.mem store.l2_blocks head -let last_processed_head_opt {store; _} = Store.L2_head.find store +let last_processed_head_opt {store; _} = Store.L2_head.read store.l2_head let mark_finalized_head {store; _} head_hash = - let open Lwt_syntax in - let* block = Store.L2_blocks.find store head_hash in + let open Lwt_result_syntax in + let* block = Store.L2_blocks.read store.l2_blocks head_hash in match block with | None -> return_unit - | Some block -> Store.Last_finalized_head.set store block + | Some (block_info, header) -> + let block = {block_info with header} in + Store.Last_finalized_head.write store.last_finalized_head block -let get_finalized_head_opt {store; _} = Store.Last_finalized_head.find store +let get_finalized_head_opt {store; _} = + Store.Last_finalized_head.read store.last_finalized_head (* TODO: https://gitlab.com/tezos/tezos/-/issues/4532 Make this logarithmic, by storing pointers to muliple predecessor and by dichotomy. *) let block_before {store; _} tick = let open Lwt_result_syntax in - let*! head = Store.L2_head.find store in + let* head = Store.L2_head.read store.l2_head in match head with | None -> return_none | Some head -> let rec search block_hash = - let*! block = Store.L2_blocks.find store block_hash in + let* block = Store.L2_blocks.read store.l2_blocks block_hash in match block with | None -> failwith "Missing block %a" Block_hash.pp block_hash - | Some block -> - if Sc_rollup.Tick.(block.initial_tick <= tick) then - return_some block - else search block.header.predecessor + | Some (info, header) -> + if Sc_rollup.Tick.(info.initial_tick <= tick) then + return_some {info with header} + else search header.predecessor in search head.header.block_hash let get_l2_block {store; _} block_hash = - trace_lwt_with "Could not retrieve L2 block for %a" Block_hash.pp block_hash - @@ Store.L2_blocks.get store block_hash + let open Lwt_result_syntax in + let* block = Store.L2_blocks.read store.l2_blocks block_hash in + match block with + | None -> + failwith "Could not retrieve L2 block for %a" Block_hash.pp block_hash + | Some (info, header) -> return {info with Sc_rollup_block.header} -let find_l2_block {store; _} block_hash = Store.L2_blocks.find store block_hash +let find_l2_block {store; _} block_hash = + let open Lwt_result_syntax in + let+ block = Store.L2_blocks.read store.l2_blocks block_hash in + Option.map (fun (info, header) -> {info with Sc_rollup_block.header}) block let get_l2_block_by_level node_ctxt level = let open Lwt_result_syntax in trace_lwt_result_with "Could not retrieve L2 block at level %ld" level @@ let* block_hash = hash_of_level node_ctxt level in - let*! block = Store.L2_blocks.get node_ctxt.store block_hash in - return block + get_l2_block node_ctxt block_hash let find_l2_block_by_level node_ctxt level = - let open Lwt_option_syntax in + let open Lwt_result_syntax in let* block_hash = hash_of_level_opt node_ctxt level in - Store.L2_blocks.find node_ctxt.store block_hash - -let get_full_l2_block {store; _} block_hash = - let open Lwt_syntax in - let* block = Store.L2_blocks.get store block_hash in - let* inbox = Store.Inboxes.get store block.header.inbox_hash - and* {messages; _} = Store.Messages.get store block.header.inbox_witness - and* commitment = - Option.map_s (Store.Commitments.get store) block.header.commitment_hash - in - return {block with content = {Sc_rollup_block.inbox; messages; commitment}} + match block_hash with + | None -> return_none + | Some block_hash -> find_l2_block node_ctxt block_hash let get_commitment {store; _} commitment_hash = - trace_lwt_with - "Could not retrieve commitment %a" - Sc_rollup.Commitment.Hash.pp - commitment_hash - @@ Store.Commitments.get store commitment_hash + let open Lwt_result_syntax in + let* commitment = Store.Commitments.find store.commitments commitment_hash in + match commitment with + | None -> + failwith + "Could not retrieve commitment %a" + Sc_rollup.Commitment.Hash.pp + commitment_hash + | Some c -> return c -let find_commitment {store; _} hash = Store.Commitments.find store hash +let find_commitment {store; _} hash = + Store.Commitments.find store.commitments hash -let commitment_exists {store; _} hash = Store.Commitments.mem store hash +let commitment_exists {store; _} hash = + Store.Commitments.mem store.commitments hash let save_commitment {store; _} commitment = - let open Lwt_syntax in + let open Lwt_result_syntax in let hash = Sc_rollup.Commitment.hash_uncarbonated commitment in - let+ () = Store.Commitments.add store hash commitment in + let+ () = Store.Commitments.add store.commitments hash commitment in hash let commitment_published_at_level {store; _} commitment = - Store.Commitments_published_at_level.find store commitment + Store.Commitments_published_at_level.find + store.commitments_published_at_level + commitment let set_commitment_published_at_level {store; _} = - Store.Commitments_published_at_level.add store + Store.Commitments_published_at_level.add store.commitments_published_at_level type commitment_source = Anyone | Us let commitment_was_published {store; _} ~source commitment_hash = - let open Lwt_syntax in + let open Lwt_result_syntax in match source with - | Anyone -> Store.Commitments_published_at_level.mem store commitment_hash + | Anyone -> + Store.Commitments_published_at_level.mem + store.commitments_published_at_level + commitment_hash | Us -> ( let+ info = - Store.Commitments_published_at_level.find store commitment_hash + Store.Commitments_published_at_level.find + store.commitments_published_at_level + commitment_hash in match info with | Some {published_at_level = Some _; _} -> true | _ -> false) let get_inbox {store; _} inbox_hash = - trace_lwt_with - "Could not retrieve inbox %a" - Sc_rollup.Inbox.Hash.pp - inbox_hash - @@ Store.Inboxes.get store inbox_hash + let open Lwt_result_syntax in + let* inbox = Store.Inboxes.read store.inboxes inbox_hash in + match inbox with + | None -> + failwith "Could not retrieve inbox %a" Sc_rollup.Inbox.Hash.pp inbox_hash + | Some (i, ()) -> return i -let find_inbox {store; _} hash = Store.Inboxes.find store hash +let find_inbox {store; _} hash = + let open Lwt_result_syntax in + let+ inbox = Store.Inboxes.read store.inboxes hash in + Option.map fst inbox let save_inbox {store; _} inbox = - let open Lwt_syntax in + let open Lwt_result_syntax in let hash = Sc_rollup.Inbox.hash inbox in - let+ () = Store.Inboxes.add store hash inbox in + let+ () = Store.Inboxes.append store.inboxes ~key:hash ~value:inbox in hash -let find_inbox_by_block_hash {store; _} block_hash = - let open Lwt_option_syntax in - let* l2_block = Store.L2_blocks.find store block_hash in - Store.Inboxes.find store l2_block.header.inbox_hash +let find_inbox_by_block_hash ({store; _} as node_ctxt) block_hash = + let open Lwt_result_syntax in + let* header = Store.L2_blocks.header store.l2_blocks block_hash in + match header with + | None -> return_none + | Some {inbox_hash; _} -> find_inbox node_ctxt inbox_hash let genesis_inbox node_ctxt = let genesis_level = Raw_level.to_int32 node_ctxt.genesis_info.level in @@ -382,7 +417,7 @@ let genesis_inbox node_ctxt = let inbox_of_head node_ctxt Layer1.{hash = block_hash; level = block_level} = let open Lwt_result_syntax in - let*! possible_inbox = find_inbox_by_block_hash node_ctxt block_hash in + let* possible_inbox = find_inbox_by_block_hash node_ctxt block_hash in (* Pre-condition: forall l. (l > genesis_level) => inbox[l] <> None. *) match possible_inbox with | None -> @@ -413,16 +448,61 @@ let get_inbox_by_block_hash node_ctxt hash = let* level = level_of_hash node_ctxt hash in inbox_of_head node_ctxt {hash; level} -let get_messages {store; _} messages_hash = - trace_lwt_with - "Could not retrieve messages with payloads merkelized hash %a" - Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp - messages_hash - @@ Store.Messages.get store messages_hash +type messages_info = { + predecessor : Block_hash.t; + predecessor_timestamp : Timestamp.t; + messages : Sc_rollup.Inbox_message.t list; +} -let find_messages {store; _} hash = Store.Messages.find store hash +let get_messages {store; _} messages_hash = + let open Lwt_result_syntax in + let* msg = Store.Messages.read store.messages messages_hash in + match msg with + | None -> + failwith + "Could not retrieve messages with payloads merkelized hash %a" + Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp + messages_hash + | Some (messages, (predecessor, predecessor_timestamp, _num_messages)) -> + return {predecessor; predecessor_timestamp; messages} + +let find_messages {store; _} hash = + let open Lwt_result_syntax in + let+ msgs = Store.Messages.read store.messages hash in + Option.map + (fun (messages, (predecessor, predecessor_timestamp, _num_messages)) -> + {predecessor; predecessor_timestamp; messages}) + msgs -let save_messages {store; _} = Store.Messages.add store +let get_num_messages {store; _} hash = + let open Lwt_result_syntax in + let* header = Store.Messages.header store.messages hash in + match header with + | None -> + failwith + "Could not retrieve number of messages for inbox witness %a" + Sc_rollup.Inbox_merkelized_payload_hashes.Hash.pp + hash + | Some (_predecessor, _predecessor_timestamp, num_messages) -> + return num_messages + +let save_messages {store; _} key {predecessor; predecessor_timestamp; messages} + = + Store.Messages.append + store.messages + ~key + ~header:(predecessor, predecessor_timestamp, List.length messages) + ~value:messages + +let get_full_l2_block node_ctxt block_hash = + let open Lwt_result_syntax in + let* block = get_l2_block node_ctxt block_hash in + let* inbox = get_inbox node_ctxt block.header.inbox_hash + and* {messages; _} = get_messages node_ctxt block.header.inbox_witness + and* commitment = + Option.map_es (get_commitment node_ctxt) block.header.commitment_hash + in + return {block with content = {Sc_rollup_block.inbox; messages; commitment}} let get_slot_header {store; _} ~published_in_block_hash slot_index = trace_lwt_with @@ -432,47 +512,49 @@ let get_slot_header {store; _} ~published_in_block_hash slot_index = Block_hash.pp published_in_block_hash @@ Store.Dal_slots_headers.get - store + store.irmin_store ~primary_key:published_in_block_hash ~secondary_key:slot_index let get_all_slot_headers {store; _} ~published_in_block_hash = - Store.Dal_slots_headers.list_values store ~primary_key:published_in_block_hash + Store.Dal_slots_headers.list_values + store.irmin_store + ~primary_key:published_in_block_hash let get_slot_indexes {store; _} ~published_in_block_hash = Store.Dal_slots_headers.list_secondary_keys - store + store.irmin_store ~primary_key:published_in_block_hash let save_slot_header {store; _} ~published_in_block_hash (slot_header : Dal.Slot.Header.t) = Store.Dal_slots_headers.add - store + store.irmin_store ~primary_key:published_in_block_hash ~secondary_key:slot_header.id.index slot_header let processed_slot {store; _} ~confirmed_in_block_hash slot_index = Store.Dal_processed_slots.find - store + store.irmin_store ~primary_key:confirmed_in_block_hash ~secondary_key:slot_index let list_slot_pages {store; _} ~confirmed_in_block_hash = Store.Dal_slot_pages.list_secondary_keys_with_values - store + store.irmin_store ~primary_key:confirmed_in_block_hash let find_slot_page {store; _} ~confirmed_in_block_hash ~slot_index ~page_index = Store.Dal_slot_pages.find - store + store.irmin_store ~primary_key:confirmed_in_block_hash ~secondary_key:(slot_index, page_index) let save_unconfirmed_slot {store; _} current_block_hash slot_index = (* No page is actually saved *) Store.Dal_processed_slots.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:slot_index `Unconfirmed @@ -485,26 +567,26 @@ let save_confirmed_slot {store; _} current_block_hash slot_index pages = List.iteri_s (fun page_number page -> Store.Dal_slot_pages.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:(slot_index, page_number) page) pages in Store.Dal_processed_slots.add - store + store.irmin_store ~primary_key:current_block_hash ~secondary_key:slot_index `Confirmed let find_confirmed_slots_history {store; _} = - Store.Dal_confirmed_slots_history.find store + Store.Dal_confirmed_slots_history.find store.irmin_store let save_confirmed_slots_history {store; _} = - Store.Dal_confirmed_slots_history.add store + Store.Dal_confirmed_slots_history.add store.irmin_store let find_confirmed_slots_histories {store; _} = - Store.Dal_confirmed_slots_histories.find store + Store.Dal_confirmed_slots_histories.find store.irmin_store let save_confirmed_slots_histories {store; _} = - Store.Dal_confirmed_slots_histories.add store + Store.Dal_confirmed_slots_histories.add store.irmin_store diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.mli b/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.mli index 809233e1a55c..6e76dca69f17 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.mli +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/node_context.mli @@ -112,7 +112,7 @@ val init : 'a t tzresult Lwt.t (** Closes the store, context and Layer 1 monitor. *) -val close : _ t -> unit Lwt.t +val close : _ t -> unit tzresult Lwt.t (** [checkout_context node_ctxt block_hash] returns the context at block [block_hash]. *) @@ -138,7 +138,7 @@ type 'a delayed_write = ('a, rw) Delayed_write_monad.t (** [is_processed store hash] returns [true] if the block with [hash] has already been processed by the daemon. *) -val is_processed : _ t -> Block_hash.t -> bool Lwt.t +val is_processed : _ t -> Block_hash.t -> bool tzresult Lwt.t (** [get_l2_block t hash] returns the Layer 2 block known by the rollup node for Layer 1 block [hash]. *) @@ -146,39 +146,42 @@ val get_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.t tzresult Lwt.t (** Same as {!get_l2_block} but returns [None] when the Layer 2 block is not available. *) -val find_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.t option Lwt.t +val find_l2_block : + _ t -> Block_hash.t -> Sc_rollup_block.t option tzresult Lwt.t (** Same as {!get_l2_block} but retrieves the Layer 2 block by its level. *) val get_l2_block_by_level : _ t -> int32 -> Sc_rollup_block.t tzresult Lwt.t (** Same as {!get_l2_block_by_level} but returns [None] when the Layer 2 block is not available. *) -val find_l2_block_by_level : _ t -> int32 -> Sc_rollup_block.t option Lwt.t +val find_l2_block_by_level : + _ t -> int32 -> Sc_rollup_block.t option tzresult Lwt.t (** [get_full_l2_block node_ctxt hash] returns the full L2 block for L1 block hash [hash]. The result contains the L2 block and its content (inbox, messages, commitment). *) -val get_full_l2_block : _ t -> Block_hash.t -> Sc_rollup_block.full Lwt.t +val get_full_l2_block : + _ t -> Block_hash.t -> Sc_rollup_block.full tzresult Lwt.t (** [save_level t head] registers the correspondences [head.level |-> head.hash] in the store. *) -val save_level : rw -> Layer1.head -> unit Lwt.t +val save_level : rw -> Layer1.head -> unit tzresult Lwt.t -(** [save_l2_head t l2_block] remembers that the [l2_block.head] is +(** [save_l2_head t l2_block] remembers that the [l2_block.head] is processed. The system should not have to come back to it. *) -val save_l2_head : rw -> Sc_rollup_block.t -> unit Lwt.t +val save_l2_head : rw -> Sc_rollup_block.t -> unit tzresult Lwt.t (** [last_processed_head_opt store] returns the last processed head if it exists. *) -val last_processed_head_opt : _ t -> Sc_rollup_block.t option Lwt.t +val last_processed_head_opt : _ t -> Sc_rollup_block.t option tzresult Lwt.t (** [mark_finalized_head store head] remembers that the [head] is finalized. By construction, every block whose level is smaller than [head]'s is also finalized. *) -val mark_finalized_head : rw -> Block_hash.t -> unit Lwt.t +val mark_finalized_head : rw -> Block_hash.t -> unit tzresult Lwt.t (** [last_finalized_head_opt store] returns the last finalized head if it exists. *) -val get_finalized_head_opt : _ t -> Sc_rollup_block.t option Lwt.t +val get_finalized_head_opt : _ t -> Sc_rollup_block.t option tzresult Lwt.t (** [hash_of_level node_ctxt level] returns the current block hash for a given [level]. *) @@ -186,7 +189,7 @@ val hash_of_level : _ t -> int32 -> Block_hash.t tzresult Lwt.t (** [hash_of_level_opt] is like {!hash_of_level} but returns [None] if the [level] is not known. *) -val hash_of_level_opt : _ t -> int32 -> Block_hash.t option Lwt.t +val hash_of_level_opt : _ t -> int32 -> Block_hash.t option tzresult Lwt.t (** [level_of_hash node_ctxt hash] returns the level for Tezos block hash [hash] if it is known by the Tezos Layer 1 node. *) @@ -207,16 +210,19 @@ val get_commitment : (** Same as {!get_commitment} but returns [None] if this commitment hash is not known by the rollup node. *) val find_commitment : - _ t -> Sc_rollup.Commitment.Hash.t -> Sc_rollup.Commitment.t option Lwt.t + _ t -> + Sc_rollup.Commitment.Hash.t -> + Sc_rollup.Commitment.t option tzresult Lwt.t (** [commitment_exists t hash] returns [true] if the commitment with [hash] is known (i.e. stored) by the rollup node. *) -val commitment_exists : _ t -> Sc_rollup.Commitment.Hash.t -> bool Lwt.t +val commitment_exists : + _ t -> Sc_rollup.Commitment.Hash.t -> bool tzresult Lwt.t (** [save_commitment t commitment] saves a commitment in the store an returns is hash. *) val save_commitment : - rw -> Sc_rollup.Commitment.t -> Sc_rollup.Commitment.Hash.t Lwt.t + rw -> Sc_rollup.Commitment.t -> Sc_rollup.Commitment.Hash.t tzresult Lwt.t (** [commitment_published_at_level t hash] returns the levels at which the commitment was first published and the one at which it was included by in a @@ -226,7 +232,7 @@ val save_commitment : val commitment_published_at_level : _ t -> Sc_rollup.Commitment.Hash.t -> - Store.Commitments_published_at_level.element option Lwt.t + Store.Commitments_published_at_level.element option tzresult Lwt.t (** [save_commitment_published_at_level t hash levels] saves the publication/inclusion information for a commitment with [hash]. *) @@ -234,7 +240,7 @@ val set_commitment_published_at_level : rw -> Sc_rollup.Commitment.Hash.t -> Store.Commitments_published_at_level.element -> - unit Lwt.t + unit tzresult Lwt.t type commitment_source = Anyone | Us @@ -243,21 +249,32 @@ type commitment_source = Anyone | Us the publication status for commitments we published ourselves [`Us] or that [`Anyone] published. *) val commitment_was_published : - _ t -> source:commitment_source -> Sc_rollup.Commitment.Hash.t -> bool Lwt.t + _ t -> + source:commitment_source -> + Sc_rollup.Commitment.Hash.t -> + bool tzresult Lwt.t (** {3 Inboxes} *) +type messages_info = { + predecessor : Block_hash.t; + predecessor_timestamp : Timestamp.t; + messages : Sc_rollup.Inbox_message.t list; +} + (** [get_inbox t inbox_hash] retrieves the inbox whose hash is [inbox_hash] from the rollup node's storage. *) val get_inbox : _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t tzresult Lwt.t (** Same as {!get_inbox} but returns [None] if this inbox is not known. *) -val find_inbox : _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t option Lwt.t +val find_inbox : + _ t -> Sc_rollup.Inbox.Hash.t -> Sc_rollup.Inbox.t option tzresult Lwt.t (** [save_inbox t inbox] remembers the [inbox] in the storage. It is associated to its hash which is returned. *) -val save_inbox : rw -> Sc_rollup.Inbox.t -> Sc_rollup.Inbox.Hash.t Lwt.t +val save_inbox : + rw -> Sc_rollup.Inbox.t -> Sc_rollup.Inbox.Hash.t tzresult Lwt.t (** [inbox_of_head node_ctxt block] returns the latest inbox at the given [block]. This function always returns [inbox] for all levels at and @@ -277,13 +294,19 @@ val genesis_inbox : _ t -> Sc_rollup.Inbox.t tzresult Lwt.t val get_messages : _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info tzresult Lwt.t + messages_info tzresult Lwt.t (** Same as {!get_messages} but returns [None] if the payloads hash is not known. *) val find_messages : _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info option Lwt.t + messages_info option tzresult Lwt.t + +(** [get_num_messages t witness_hash] retrieves (without reading all the messages + from disk) the number of messages for the inbox witness [witness_hash] + stored by the rollup node. *) +val get_num_messages : + _ t -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> int tzresult Lwt.t (** [save_messages t payloads_hash messages] associates the list of [messages] to the [payloads_hash]. The payload hash must be computed by calling, @@ -291,8 +314,8 @@ val find_messages : val save_messages : rw -> Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t -> - Store.Messages.info -> - unit Lwt.t + messages_info -> + unit tzresult Lwt.t (** {3 DAL} *) diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/refutation_game.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/refutation_game.ml index fed8e13eb443..c71b2b9614cd 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/refutation_game.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/refutation_game.ml @@ -224,9 +224,18 @@ module Make (Interpreter : Interpreter.S) : let inbox = snapshot let get_history inbox_hash = - let open Lwt_option_syntax in + let open Lwt_syntax in let+ inbox = Node_context.find_inbox node_ctxt inbox_hash in - Sc_rollup.Inbox.take_snapshot inbox + match inbox with + | Error err -> + Format.kasprintf + Stdlib.failwith + "Refutation game: Cannot get inbox history for %a, %a" + Sc_rollup.Inbox.Hash.pp + inbox_hash + pp_print_trace + err + | Ok inbox -> Option.map Sc_rollup.Inbox.take_snapshot inbox let get_payloads_history witness = Lwt.map diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/store.ml b/src/proto_016_PtMumbai/bin_sc_rollup_node/store.ml index 5c22868889fb..62b2404dd3ce 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/store.ml +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/store.ml @@ -30,123 +30,125 @@ include Store_utils (** Aggregated collection of messages from the L1 inbox *) open Alpha_context -module IStore = Irmin_store.Make (struct - let name = "Tezos smart rollup node" -end) +module Irmin_store = struct + module IStore = Irmin_store.Make (struct + let name = "Tezos smart rollup node" + end) -include Store_utils.Make (IStore) + include IStore + include Store_utils.Make (IStore) +end -type 'a store = 'a IStore.t +module Empty_header = struct + type t = unit -type 'a t = ([< `Read | `Write > `Read] as 'a) store + let name = "empty" -type rw = Store_sigs.rw t + let encoding = Data_encoding.unit -type ro = Store_sigs.ro t + let fixed_size = 0 +end -let close = IStore.close +module Add_empty_header = struct + module Header = Empty_header -let load = IStore.load + let header _ = () +end -let readonly = IStore.readonly +module Make_hash_index_key (H : Environment.S.HASH) = +Indexed_store.Make_index_key (struct + include Indexed_store.Make_fixed_encodable (H) + + let equal = H.equal +end) (** L2 blocks *) module L2_blocks = - Make_append_only_map + Indexed_store.Make_indexed_file (struct - let path = ["state_info"] + let name = "l2_blocks" end) + (Tezos_store_shared.Block_key) (struct - type key = Block_hash.t + type t = (unit, unit) Sc_rollup_block.block - let to_path_representation = Block_hash.to_b58check - end) - (struct - type value = Sc_rollup_block.t + let name = "sc_rollup_block_info" + + let encoding = + Sc_rollup_block.block_encoding Data_encoding.unit Data_encoding.unit - let name = "sc_rollup_block" + module Header = struct + type t = Sc_rollup_block.header - let encoding = Sc_rollup_block.encoding + let name = "sc_rollup_block_header" + + let encoding = Sc_rollup_block.header_encoding + + let fixed_size = Sc_rollup_block.header_size + end end) (** Unaggregated messages per block *) -module Messages = struct - type info = { - predecessor : Block_hash.t; - predecessor_timestamp : Timestamp.t; - messages : Sc_rollup.Inbox_message.t list; - } +module Messages = + Indexed_store.Make_indexed_file + (struct + let name = "messages" + end) + (Make_hash_index_key (Sc_rollup.Inbox_merkelized_payload_hashes.Hash)) + (struct + type t = Sc_rollup.Inbox_message.t list - let encoding = - let open Data_encoding in - conv - (fun {predecessor; predecessor_timestamp; messages} -> - (predecessor, predecessor_timestamp, messages)) - (fun (predecessor, predecessor_timestamp, messages) -> - {predecessor; predecessor_timestamp; messages}) - @@ obj3 - (req "predecessor" Block_hash.encoding) - (req "predecessor_timestamp" Timestamp.encoding) - (req - "messages" - (list @@ dynamic_size Sc_rollup.Inbox_message.encoding)) + let name = "messages_list" - include - Make_append_only_map - (struct - let path = ["messages"] - end) - (struct - type key = Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + let encoding = + Data_encoding.(list @@ dynamic_size Sc_rollup.Inbox_message.encoding) - let to_path_representation = - Sc_rollup.Inbox_merkelized_payload_hashes.Hash.to_b58check - end) - (struct - type value = info + module Header = struct + type t = Block_hash.t * Timestamp.t * int - let name = "messages" + let name = "messages_inbox_info" - let encoding = encoding - end) -end + let encoding = + let open Data_encoding in + obj3 + (req "predecessor" Block_hash.encoding) + (req "predecessor_timestamp" Timestamp.encoding) + (req "num_messages" int31) + + let fixed_size = + WithExceptions.Option.get ~loc:__LOC__ + @@ Data_encoding.Binary.fixed_length encoding + end + end) (** Inbox state for each block *) module Inboxes = - Make_append_only_map + Indexed_store.Make_simple_indexed_file (struct - let path = ["inboxes"] + let name = "inboxes" end) + (Make_hash_index_key (Sc_rollup.Inbox.Hash)) (struct - type key = Sc_rollup.Inbox.Hash.t - - let to_path_representation = Sc_rollup.Inbox.Hash.to_b58check - end) - (struct - type value = Sc_rollup.Inbox.t + type t = Sc_rollup.Inbox.t let name = "inbox" let encoding = Sc_rollup.Inbox.encoding + + include Add_empty_header end) module Commitments = - Make_append_only_map + Indexed_store.Make_indexable (struct - let path = ["commitments"; "computed"] + let name = "commitments" end) - (struct - type key = Sc_rollup.Commitment.Hash.t - - let to_path_representation = Sc_rollup.Commitment.Hash.to_b58check - end) - (struct - type value = Sc_rollup.Commitment.t + (Make_hash_index_key (Sc_rollup.Commitment.Hash)) + (Indexed_store.Make_index_value (Indexed_store.Make_fixed_encodable (struct + include Sc_rollup.Commitment let name = "commitment" - - let encoding = Sc_rollup.Commitment.encoding - end) + end))) module Commitments_published_at_level = struct type element = { @@ -156,6 +158,12 @@ module Commitments_published_at_level = struct let element_encoding = let open Data_encoding in + let opt_level_encoding = + conv + (function None -> -1l | Some l -> Raw_level.to_int32 l) + (fun l -> if l = -1l then None else Some (Raw_level.of_int32_exn l)) + Data_encoding.int32 + in conv (fun {first_published_at_level; published_at_level} -> (first_published_at_level, published_at_level)) @@ -163,82 +171,64 @@ module Commitments_published_at_level = struct {first_published_at_level; published_at_level}) @@ obj2 (req "first_published_at_level" Raw_level.encoding) - (opt "published_at_level" Raw_level.encoding) + (req "published_at_level" opt_level_encoding) include - Make_updatable_map + Indexed_store.Make_indexable (struct - let path = ["commitments"; "published_at_level"] + let name = "commitments" end) - (struct - type key = Sc_rollup.Commitment.Hash.t - - let to_path_representation = Sc_rollup.Commitment.Hash.to_b58check - end) - (struct - type value = element + (Make_hash_index_key (Sc_rollup.Commitment.Hash)) + (Indexed_store.Make_index_value (Indexed_store.Make_fixed_encodable (struct + type t = element let name = "published_levels" let encoding = element_encoding - end) + end))) end -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4392 - Use file. *) -module L2_head = - Make_mutable_value - (struct - let path = ["l2_head"] - end) - (struct - type value = Sc_rollup_block.t +module L2_head = Indexed_store.Make_singleton (struct + type t = Sc_rollup_block.t - let name = "l2_block" + let name = "l2_head" - let encoding = Sc_rollup_block.encoding - end) + let encoding = Sc_rollup_block.encoding +end) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/4392 - Use file. *) -module Last_finalized_head = - Make_mutable_value - (struct - let path = ["finalized_head"] - end) - (struct - type value = Sc_rollup_block.t +module Last_finalized_head = Indexed_store.Make_singleton (struct + type t = Sc_rollup_block.t - let name = "l2_block" + let name = "finalized_head" - let encoding = Sc_rollup_block.encoding - end) + let encoding = Sc_rollup_block.encoding +end) (** Table from L1 levels to blocks hashes. *) module Levels_to_hashes = - Make_updatable_map + Indexed_store.Make_indexable (struct - let path = ["tezos"; "levels"] + let name = "tezos_levels" end) - (struct - type key = int32 + (Indexed_store.Make_index_key (struct + type t = int32 - let to_path_representation = Int32.to_string - end) - (struct - type value = Block_hash.t + let encoding = Data_encoding.int32 - let name = "block_hash" + let name = "level" - let encoding = Block_hash.encoding - end) + let fixed_size = 4 + + let equal = Int32.equal + end)) + (Tezos_store_shared.Block_key) (* Published slot headers per block hash, stored as a list of bindings from `Dal_slot_index.t` to `Dal.Slot.t`. The encoding function converts this list into a `Dal.Slot_index.t`-indexed map. *) module Dal_slot_pages = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "slot_pages"] end) @@ -270,7 +260,7 @@ module Dal_slot_pages = (** stores slots whose data have been considered and pages stored to disk (if they are confirmed). *) module Dal_processed_slots = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "processed_slots"] end) @@ -312,7 +302,7 @@ module Dal_processed_slots = end) module Dal_slots_headers = - Make_nested_map + Irmin_store.Make_nested_map (struct let path = ["dal"; "slot_headers"] end) @@ -348,7 +338,7 @@ module Dal_slots_headers = (** Confirmed DAL slots history. See documentation of {Dal_slot_repr.Slots_history} for more details. *) module Dal_confirmed_slots_history = - Make_append_only_map + Irmin_store.Make_append_only_map (struct let path = ["dal"; "confirmed_slots_history"] end) @@ -370,7 +360,7 @@ module Dal_confirmed_slots_history = module Dal_confirmed_slots_histories = (* TODO: https://gitlab.com/tezos/tezos/-/issues/4390 Store single history points in map instead of whole history. *) - Make_append_only_map + Irmin_store.Make_append_only_map (struct let path = ["dal"; "confirmed_slots_histories_cache"] end) @@ -386,3 +376,106 @@ module Dal_confirmed_slots_histories = let encoding = Dal.Slots_history.History_cache.encoding end) + +type 'a store = { + l2_blocks : 'a L2_blocks.t; + messages : 'a Messages.t; + inboxes : 'a Inboxes.t; + commitments : 'a Commitments.t; + commitments_published_at_level : 'a Commitments_published_at_level.t; + l2_head : 'a L2_head.t; + last_finalized_head : 'a Last_finalized_head.t; + levels_to_hashes : 'a Levels_to_hashes.t; + irmin_store : 'a Irmin_store.t; +} + +type 'a t = ([< `Read | `Write > `Read] as 'a) store + +type rw = Store_sigs.rw t + +type ro = Store_sigs.ro t + +let readonly + ({ + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head; + last_finalized_head; + levels_to_hashes; + irmin_store; + } : + _ t) : ro = + { + l2_blocks = L2_blocks.readonly l2_blocks; + messages = Messages.readonly messages; + inboxes = Inboxes.readonly inboxes; + commitments = Commitments.readonly commitments; + commitments_published_at_level = + Commitments_published_at_level.readonly commitments_published_at_level; + l2_head = L2_head.readonly l2_head; + last_finalized_head = Last_finalized_head.readonly last_finalized_head; + levels_to_hashes = Levels_to_hashes.readonly levels_to_hashes; + irmin_store = Irmin_store.readonly irmin_store; + } + +let close + ({ + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head = _; + last_finalized_head = _; + levels_to_hashes; + irmin_store; + } : + _ t) = + let open Lwt_result_syntax in + let+ () = L2_blocks.close l2_blocks + and+ () = Messages.close messages + and+ () = Inboxes.close inboxes + and+ () = Commitments.close commitments + and+ () = Commitments_published_at_level.close commitments_published_at_level + and+ () = Levels_to_hashes.close levels_to_hashes + and+ () = Irmin_store.close irmin_store |> Lwt_result.ok in + () + +let load (type a) (mode : a mode) ~l2_blocks_cache_size data_dir : + a store tzresult Lwt.t = + let open Lwt_result_syntax in + let path name = Filename.concat data_dir name in + let cache_size = l2_blocks_cache_size in + let* l2_blocks = L2_blocks.load mode ~path:(path "l2_blocks") ~cache_size in + let* messages = Messages.load mode ~path:(path "messages") ~cache_size in + let* inboxes = Inboxes.load mode ~path:(path "inboxes") ~cache_size in + let* commitments = Commitments.load mode ~path:(path "commitments") in + let* commitments_published_at_level = + Commitments_published_at_level.load + mode + ~path:(path "commitments_published_at_level") + in + let* l2_head = L2_head.load mode ~path:(path "l2_head") in + let* last_finalized_head = + Last_finalized_head.load mode ~path:(path "last_finalized_head") + in + let* levels_to_hashes = + Levels_to_hashes.load mode ~path:(path "levels_to_hashes") + in + let+ irmin_store = + Irmin_store.load mode (path "irmin_store") |> Lwt_result.ok + in + { + l2_blocks; + messages; + inboxes; + commitments; + commitments_published_at_level; + l2_head; + last_finalized_head; + levels_to_hashes; + irmin_store; + } diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/store.mli b/src/proto_016_PtMumbai/bin_sc_rollup_node/store.mli index 4c2d9524da08..252c0f25c100 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/store.mli +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/store.mli @@ -23,74 +23,38 @@ (* *) (*****************************************************************************) -(* TODO: https://gitlab.com/tezos/tezos/-/issues/3471 - Use indexed file for append-only instead of Irmin. *) - -(* TODO: https://gitlab.com/tezos/tezos/-/issues/3739 - Refactor the store file to have functors in their own - separate module, and return errors within the Error monad. *) - open Protocol open Alpha_context +open Indexed_store -type +'a store - -include Store_sigs.Store with type 'a t = 'a store - -(** Type of store. The parameter indicates if the store can be written or only - read. *) -type 'a t = ([< `Read | `Write > `Read] as 'a) store - -(** Read/write store {!t}. *) -type rw = Store_sigs.rw t - -(** Read only store {!t}. *) -type ro = Store_sigs.ro t - -(** [close store] closes the store. *) -val close : _ t -> unit Lwt.t - -(** [load mode directory] loads a store from the data persisted in [directory].*) -val load : 'a Store_sigs.mode -> string -> 'a store Lwt.t - -(** [readonly store] returns a read-only version of [store]. *) -val readonly : _ t -> ro +module Irmin_store : Store_sigs.Store module L2_blocks : - Store_sigs.Append_only_map + INDEXED_FILE with type key := Block_hash.t - and type value := Sc_rollup_block.t - and type 'a store := 'a store + and type value := (unit, unit) Sc_rollup_block.block + and type header := Sc_rollup_block.header (** Storage for persisting messages downloaded from the L1 node. *) -module Messages : sig - type info = { - predecessor : Block_hash.t; - predecessor_timestamp : Timestamp.t; - messages : Sc_rollup.Inbox_message.t list; - } - - include - Store_sigs.Append_only_map - with type key := Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t - and type value := info - and type 'a store := 'a store -end +module Messages : + INDEXED_FILE + with type key := Sc_rollup.Inbox_merkelized_payload_hashes.Hash.t + and type value := Sc_rollup.Inbox_message.t list + and type header := Block_hash.t * Timestamp.t * int (** Aggregated collection of messages from the L1 inbox *) module Inboxes : - Store_sigs.Append_only_map + SIMPLE_INDEXED_FILE with type key := Sc_rollup.Inbox.Hash.t and type value := Sc_rollup.Inbox.t - and type 'a store := 'a store + and type header := unit (** Storage containing commitments and corresponding commitment hashes that the rollup node has knowledge of. *) module Commitments : - Store_sigs.Append_only_map + INDEXABLE_STORE with type key := Sc_rollup.Commitment.Hash.t and type value := Sc_rollup.Commitment.t - and type 'a store := 'a store (** Storage mapping commitment hashes to the level when they were published by the rollup node. It only contains hashes of commitments published by this @@ -106,27 +70,18 @@ module Commitments_published_at_level : sig } include - Store_sigs.Map + INDEXABLE_STORE with type key := Sc_rollup.Commitment.Hash.t and type value := element - and type 'a store := 'a store end -module L2_head : - Store_sigs.Mutable_value - with type value := Sc_rollup_block.t - and type 'a store := 'a store +module L2_head : SINGLETON_STORE with type value := Sc_rollup_block.t module Last_finalized_head : - Store_sigs.Mutable_value - with type value := Sc_rollup_block.t - and type 'a store := 'a store + SINGLETON_STORE with type value := Sc_rollup_block.t module Levels_to_hashes : - Store_sigs.Map - with type key := int32 - and type value := Block_hash.t - and type 'a store := 'a store + INDEXABLE_STORE with type key := int32 and type value := Block_hash.t (** Published slot headers per block hash, stored as a list of bindings from [Dal_slot_index.t] @@ -137,13 +92,13 @@ module Dal_slots_headers : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t and type value := Dal.Slot.Header.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t module Dal_confirmed_slots_history : Store_sigs.Append_only_map with type key := Block_hash.t and type value := Dal.Slots_history.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** Confirmed DAL slots histories cache. See documentation of {Dal_slot_repr.Slots_history} for more details. *) @@ -151,7 +106,7 @@ module Dal_confirmed_slots_histories : Store_sigs.Append_only_map with type key := Block_hash.t and type value := Dal.Slots_history.History_cache.t - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** [Dal_slot_pages] is a [Store_utils.Nested_map] used to store the contents of dal slots fetched by the rollup node, as a list of pages. The values of @@ -164,7 +119,7 @@ module Dal_slot_pages : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t * Dal.Page.Index.t and type value := Dal.Page.content - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t (** [Dal_processed_slots] is a [Store_utils.Nested_map] used to store the processing status of dal slots content fetched by the rollup node. The values of @@ -178,4 +133,45 @@ module Dal_processed_slots : with type primary_key := Block_hash.t and type secondary_key := Dal.Slot_index.t and type value := [`Confirmed | `Unconfirmed] - and type 'a store := 'a store + and type 'a store := 'a Irmin_store.t + +type +'a store = { + l2_blocks : 'a L2_blocks.t; + messages : 'a Messages.t; + inboxes : 'a Inboxes.t; + commitments : 'a Commitments.t; + commitments_published_at_level : 'a Commitments_published_at_level.t; + l2_head : 'a L2_head.t; + last_finalized_head : 'a Last_finalized_head.t; + levels_to_hashes : 'a Levels_to_hashes.t; + irmin_store : 'a Irmin_store.t; +} + +(** Type of store. The parameter indicates if the store can be written or only + read. *) +type 'a t = ([< `Read | `Write > `Read] as 'a) store + +(** Read/write store {!t}. *) +type rw = Store_sigs.rw t + +(** Read only store {!t}. *) +type ro = Store_sigs.ro t + +(** [close store] closes the store. *) +val close : _ t -> unit tzresult Lwt.t + +(** [load mode ~l2_blocks_cache_size directory] loads a store from the data + persisted in [directory]. If [mode] is {!Store_sigs.Read_only}, then the + indexes and irmin store will be opened in readonly mode and only read + operations will be permitted. This allows to open a store for read access + that is already opened in {!Store_sigs.Read_write} mode in another + process. [l2_blocks_cache_size] is the number of L2 blocks the rollup node + will keep in memory. *) +val load : + 'a Store_sigs.mode -> + l2_blocks_cache_size:int -> + string -> + 'a store tzresult Lwt.t + +(** [readonly store] returns a read-only version of [store]. *) +val readonly : _ t -> ro -- GitLab