From 20d2dd337be8d6d1528671167e3de66e583612d4 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Fri, 15 Nov 2024 16:49:00 +0100 Subject: [PATCH 1/8] Rollups/DAL: remove unused error Dal_slot_not_found_in_store --- .../lib_sc_rollup_node/dal_pages_request.ml | 11 ----------- .../lib_sc_rollup_node/dal_pages_request.mli | 4 ---- 2 files changed, 15 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index a9a6340689d4..b1ca262e69d5 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -34,20 +34,9 @@ open Alpha_context is not confirmed. *) type error += - | Dal_slot_not_found_in_store of Dal.Slot.Header.id | Dal_invalid_page_for_slot of Dal.Page.t let () = - Sc_rollup_node_errors.register_error_kind - `Permanent - ~id:"dal_pages_request.dal_slot_not_found_in_store" - ~title:"Dal slot not found in store" - ~description:"The Dal slot whose ID is given is not found in the store" - ~pp:(fun ppf -> - Format.fprintf ppf "Dal slot not found in store %a" Dal.Slot.Header.pp_id) - Data_encoding.(obj1 (req "slot_id" Dal.Slot.Header.id_encoding)) - (function Dal_slot_not_found_in_store slot_id -> Some slot_id | _ -> None) - (fun slot_id -> Dal_slot_not_found_in_store slot_id) ; Sc_rollup_node_errors.register_error_kind `Permanent ~id:"dal_pages_request.dal_invalid_page_for_slot" diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.mli b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.mli index 17d384fed0c2..254157d26e4d 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.mli +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.mli @@ -37,10 +37,6 @@ open Alpha_context the rollup node. *) -(** This error is returned when a slot, identified by its ID, is not found in - the store. *) -type error += Dal_slot_not_found_in_store of Dal.Slot.Header.id - (** Retrieve the pages' content of the given slot ID's from the store. The function returns [Dal_slot_not_found_in_store] if no entry is found in -- GitLab From 87fb907d0fcd087d30eef5bb26565ca25faa2ede Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Fri, 15 Nov 2024 16:57:52 +0100 Subject: [PATCH 2/8] Rollups/DAL: add a helper function to fetch the skip list cells content from dal_pages_request.ml In the next commit, we'll use the information stored in the cells' content to decide if a slot is attested, instead of relying on some data crawled by the rollup node. The information will also be used in 'Adjustable DAL' --- .../lib_sc_rollup_node/dal_pages_request.ml | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index b1ca262e69d5..a2d4f248a72a 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -35,6 +35,7 @@ open Alpha_context type error += | Dal_invalid_page_for_slot of Dal.Page.t + | Dal_expected_skip_list_cell_not_found of Dal.Slot.Header.id let () = Sc_rollup_node_errors.register_error_kind @@ -48,6 +49,21 @@ let () = (function Dal_invalid_page_for_slot page_id -> Some page_id | _ -> None) (fun page_id -> Dal_invalid_page_for_slot page_id) +let () = + Sc_rollup_node_errors.register_error_kind + `Permanent + ~id:"dal_pages_request.dal_expected_skip_list_cell_not_found" + ~title:"Expected DAL skip list cell not found" + ~description: + "The skip list cell whose ID is given is not found in the context" + ~pp:(fun ppf -> + Format.fprintf ppf "Dal slot not found in store %a" Dal.Slot.Header.pp_id) + Data_encoding.(obj1 (req "slot_id" Dal.Slot.Header.id_encoding)) + (function + | Dal_expected_skip_list_cell_not_found slot_id -> Some slot_id + | _ -> None) + (fun slot_id -> Dal_expected_skip_list_cell_not_found slot_id) + module Event = struct include Internal_event.Simple @@ -126,6 +142,67 @@ let download_confirmed_slot_pages ({Node_context.dal_cctxt; _} as node_ctxt) in get_slot_pages dal_cctxt slot_id +module Slots_statuses_cache = + Aches.Vache.Map (Aches.Vache.FIFO_Precise) (Aches.Vache.Strong) + (struct + type t = Dal.Slot.Header.id + + let equal = Dal.Slot.Header.slot_id_equal + + let hash id = Hashtbl.hash id + end) + +let download_skip_list_cells_of_level node_ctxt + (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) + ~published_level = + let attested_level = + Int32.add + (Raw_level.to_int32 published_level) + (Int32.of_int dal_constants.attestation_lag) + in + Plugin.RPC.Dal.skip_list_cells_of_level + (new Protocol_client_context.wrap_full node_ctxt.Node_context.cctxt) + (`Main, `Level attested_level) + () + +(* We have currently 32 slots per level. We retain the information for 32 levels + (assuming no reorgs with different payload occur). *) +let skip_list_cells_content_cache = + (* Attestation lag * number of slots * 32 retention levels, assuming no + reorgs. *) + let attestation_lag = 8 in + let number_of_slots = 32 in + let retention_levels = 32 in + Slots_statuses_cache.create + (attestation_lag * number_of_slots * retention_levels) + +let dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id = + let open Lwt_result_syntax in + match Slots_statuses_cache.find_opt skip_list_cells_content_cache slot_id with + | Some res -> return res + | None -> ( + let* hash_with_cells = + download_skip_list_cells_of_level + node_ctxt + dal_constants + ~published_level:slot_id.published_level + in + List.iter + (fun (_hash, cell) -> + let content = Dal.Slots_history.content cell in + Slots_statuses_cache.replace + skip_list_cells_content_cache + (Dal.Slots_history.content_id content) + content) + hash_with_cells ; + (* The [find] below validates the fact that we fetched the info of + commitments published at level [slot_id.published_level]. *) + match + Slots_statuses_cache.find_opt skip_list_cells_content_cache slot_id + with + | Some res -> return res + | None -> tzfail @@ Dal_expected_skip_list_cell_not_found slot_id) + let get_page node_ctxt ~inbox_level page_id = let open Lwt_result_syntax in let Dal.Page.{slot_id; page_index} = page_id in -- GitLab From 087dae2b7ca31b58cafbcb1fb6c75ce9ba973493 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Fri, 15 Nov 2024 17:34:44 +0100 Subject: [PATCH 3/8] Rollups/DAL: add a function lot_proto_attestation_status based on skip lists cells This function will replace the old code that retrieves the attestation/ confirmation status from Rollup nodes' store. --- .../lib_sc_rollup_node/dal_pages_request.ml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index a2d4f248a72a..a62fedee5de3 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -203,6 +203,18 @@ let dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id = | Some res -> return res | None -> tzfail @@ Dal_expected_skip_list_cell_not_found slot_id) +let _slot_proto_attestation_status node_ctxt dal_constants slot_id = + let open Lwt_result_syntax in + let* res = + dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id + in + match res with + | Dal.Slots_history.Unpublished _ + | Dal.Slots_history.Published {is_proto_attested = false; _} -> + return `Unattested + | Dal.Slots_history.Published {is_proto_attested = true; _} -> + return `Attested + let get_page node_ctxt ~inbox_level page_id = let open Lwt_result_syntax in let Dal.Page.{slot_id; page_index} = page_id in -- GitLab From ac889dd5f7668f07e72fd1bd75329b913e2e94ef Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Fri, 15 Nov 2024 19:50:23 +0100 Subject: [PATCH 4/8] Rollups/DAL: Use slot_proto_attestation_status to know if a slot is attested --- .../lib_sc_rollup_node/dal_pages_request.ml | 55 +++++-------------- 1 file changed, 14 insertions(+), 41 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index a62fedee5de3..0421beab73cb 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -99,12 +99,6 @@ module Event = struct ~pp5:pp_content_elipsis end -let store_entry_from_published_level ~dal_attestation_lag ~published_level - node_ctxt = - Node_context.hash_of_level node_ctxt - @@ Int32.( - add (of_int dal_attestation_lag) (Raw_level.to_int32 published_level)) - module Slot_id = struct include Tezos_dal_node_services.Types.Slot_id @@ -203,7 +197,7 @@ let dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id = | Some res -> return res | None -> tzfail @@ Dal_expected_skip_list_cell_not_found slot_id) -let _slot_proto_attestation_status node_ctxt dal_constants slot_id = +let slot_proto_attestation_status node_ctxt dal_constants slot_id = let open Lwt_result_syntax in let* res = dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id @@ -236,14 +230,6 @@ let get_page node_ctxt ~inbox_level page_id = return @@ Some page | None -> tzfail @@ Dal_invalid_page_for_slot page_id -let storage_invariant_broken published_level index = - failwith - "Internal error: [Node_context.find_slot_status] is supposed to have \ - registered the status of the slot %d published at level %a in the store" - index - Raw_level.pp - published_level - let slot_id_is_valid (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) ~dal_activation_level ~origination_level ~inbox_level slot_id @@ -304,31 +290,23 @@ let slot_pages slot_id then return_none else - let* confirmed_in_block_hash = - store_entry_from_published_level - ~dal_attestation_lag:dal_constants.attestation_lag - ~published_level - node_ctxt + let* status = + slot_proto_attestation_status node_ctxt dal_constants slot_id in - let index = Sc_rollup_proto_types.Dal.Slot_index.to_octez index in - let* processed = - Node_context.find_slot_status node_ctxt ~confirmed_in_block_hash index - in - match processed with - | Some `Confirmed -> + match status with + | `Attested -> + let index = Sc_rollup_proto_types.Dal.Slot_index.to_octez index in let* pages = download_confirmed_slot_pages node_ctxt ~published_level ~index in return (Some pages) - | Some `Unconfirmed -> return_none - | None -> storage_invariant_broken published_level index + | `Unattested -> return_none let page_content (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) ~dal_activation_level ~inbox_level node_ctxt page_id ~dal_attested_slots_validity_lag = let open Lwt_result_syntax in - let Dal.Slot.Header.{published_level; index} = page_id.Dal.Page.slot_id in let Node_context.{genesis_info = {level = origination_level; _}; _} = node_ctxt in @@ -343,17 +321,12 @@ let page_content page_id then return_none else - let* confirmed_in_block_hash = - store_entry_from_published_level - ~dal_attestation_lag:dal_constants.attestation_lag - ~published_level + let* status = + slot_proto_attestation_status node_ctxt + dal_constants + page_id.Dal.Page.slot_id in - let index = Sc_rollup_proto_types.Dal.Slot_index.to_octez index in - let* processed = - Node_context.find_slot_status node_ctxt ~confirmed_in_block_hash index - in - match processed with - | Some `Confirmed -> get_page node_ctxt ~inbox_level page_id - | Some `Unconfirmed -> return_none - | None -> storage_invariant_broken published_level index + match status with + | `Attested -> get_page node_ctxt ~inbox_level page_id + | `Unattested -> return_none -- GitLab From 5a53c590c0a0b3cffdc702a290f274bd382b1f6b Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Fri, 15 Nov 2024 19:54:39 +0100 Subject: [PATCH 5/8] Rollups/DAL: add a fixme to remove unused data in the rollup's store --- src/lib_smart_rollup_node/node_context.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib_smart_rollup_node/node_context.ml b/src/lib_smart_rollup_node/node_context.ml index bfed31a3b7dd..fcff11c8507b 100644 --- a/src/lib_smart_rollup_node/node_context.ml +++ b/src/lib_smart_rollup_node/node_context.ml @@ -891,6 +891,10 @@ let save_slot_header {store; _} ~published_in_block_hash Store.Dal_slots_headers.store store published_in_block_hash slot_header let find_slot_status {store; _} ~confirmed_in_block_hash slot_index = + (* DAL/FIXME: https://gitlab.com/tezos/tezos/-/issues/7604 + + Remove Dal_slots_statuses if MR + https://gitlab.com/tezos/tezos/-/merge_requests/15640 is merged. *) Store.Dal_slots_statuses.find_slot_status store confirmed_in_block_hash -- GitLab From 5b81779f0c6cbe79167be08595f463791e9f8bfa Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Sun, 17 Nov 2024 20:35:05 +0100 Subject: [PATCH 6/8] DAL/Proto: export more functions for debugging purpose --- src/proto_alpha/lib_protocol/alpha_context.mli | 4 ++++ src/proto_alpha/lib_protocol/dal_slot_repr.ml | 7 ++++--- src/proto_alpha/lib_protocol/dal_slot_repr.mli | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index d62c18b0ce6c..8b470d30c5cc 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2962,6 +2962,10 @@ module Dal : sig val content_id : cell_content -> Slot.Header.id + val pp : Format.formatter -> t -> unit + + val pp_content : Format.formatter -> cell_content -> unit + module Pointer_hash : S.HASH type hash = Pointer_hash.t diff --git a/src/proto_alpha/lib_protocol/dal_slot_repr.ml b/src/proto_alpha/lib_protocol/dal_slot_repr.ml index aea65d068c79..004243b64b37 100644 --- a/src/proto_alpha/lib_protocol/dal_slot_repr.ml +++ b/src/proto_alpha/lib_protocol/dal_slot_repr.ml @@ -519,9 +519,8 @@ module History = struct -> Format.fprintf fmt - "Published { @[header: %a@@] @[publisher: %a@@] \ - @[is_proto_attested: %b@@] @[attested_shards: %d@@] \ - @[total_shards: %d@@] }" + "Published { @[header: %a@] @[publisher: %a@] @[is_proto_attested: \ + %b@] @[attested_shards: %d@] @[total_shards: %d@] }" Header.pp header Contract_repr.pp @@ -1328,6 +1327,8 @@ module History = struct let content_id = Content_v2.content_id + let pp_content = Content_v2.pp + module Internal_for_tests = struct let proof_statement_is serialized_proof expected = match deserialize_proof serialized_proof with diff --git a/src/proto_alpha/lib_protocol/dal_slot_repr.mli b/src/proto_alpha/lib_protocol/dal_slot_repr.mli index ab893aa0b8bc..1ba544357124 100644 --- a/src/proto_alpha/lib_protocol/dal_slot_repr.mli +++ b/src/proto_alpha/lib_protocol/dal_slot_repr.mli @@ -259,6 +259,8 @@ module History : sig (** Returns the {!cell_content} of the given skip list cell. *) val content : t -> cell_content + val pp_content : Format.formatter -> cell_content -> unit + (** Returns the slot id of the cell whose content is given. *) val content_id : cell_content -> Header.id -- GitLab From c91008bc122858846a512ee6d37e13d07f1ed6cb Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Sun, 17 Nov 2024 20:49:43 +0100 Subject: [PATCH 7/8] Rollups/DAL: remove useless namespace when registering errors --- src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index 0421beab73cb..781d89033e36 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -38,7 +38,7 @@ type error += | Dal_expected_skip_list_cell_not_found of Dal.Slot.Header.id let () = - Sc_rollup_node_errors.register_error_kind + register_error_kind `Permanent ~id:"dal_pages_request.dal_invalid_page_for_slot" ~title:"Invalid Dal page requested for slot" @@ -50,7 +50,7 @@ let () = (fun page_id -> Dal_invalid_page_for_slot page_id) let () = - Sc_rollup_node_errors.register_error_kind + register_error_kind `Permanent ~id:"dal_pages_request.dal_expected_skip_list_cell_not_found" ~title:"Expected DAL skip list cell not found" -- GitLab From 00de1f5b64fc3245a2753ac8d8d68c40d4f9ccc9 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Mon, 18 Nov 2024 17:09:37 +0100 Subject: [PATCH 8/8] Rollups/DAL: detect reorgs and update the cache accordingly --- .../lib_sc_rollup_node/dal_pages_request.ml | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml index 781d89033e36..75730f1cfd5d 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_pages_request.ml @@ -33,9 +33,7 @@ open Alpha_context - Add entries [None] for the slot's pages in the store, if the slot is not confirmed. *) -type error += - | Dal_invalid_page_for_slot of Dal.Page.t - | Dal_expected_skip_list_cell_not_found of Dal.Slot.Header.id +type error += Dal_invalid_page_for_slot of Dal.Page.t let () = register_error_kind @@ -49,21 +47,6 @@ let () = (function Dal_invalid_page_for_slot page_id -> Some page_id | _ -> None) (fun page_id -> Dal_invalid_page_for_slot page_id) -let () = - register_error_kind - `Permanent - ~id:"dal_pages_request.dal_expected_skip_list_cell_not_found" - ~title:"Expected DAL skip list cell not found" - ~description: - "The skip list cell whose ID is given is not found in the context" - ~pp:(fun ppf -> - Format.fprintf ppf "Dal slot not found in store %a" Dal.Slot.Header.pp_id) - Data_encoding.(obj1 (req "slot_id" Dal.Slot.Header.id_encoding)) - (function - | Dal_expected_skip_list_cell_not_found slot_id -> Some slot_id - | _ -> None) - (fun slot_id -> Dal_expected_skip_list_cell_not_found slot_id) - module Event = struct include Internal_event.Simple @@ -146,14 +129,7 @@ module Slots_statuses_cache = let hash id = Hashtbl.hash id end) -let download_skip_list_cells_of_level node_ctxt - (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) - ~published_level = - let attested_level = - Int32.add - (Raw_level.to_int32 published_level) - (Int32.of_int dal_constants.attestation_lag) - in +let download_skip_list_cells_of_level node_ctxt ~attested_level = Plugin.RPC.Dal.skip_list_cells_of_level (new Protocol_client_context.wrap_full node_ctxt.Node_context.cctxt) (`Main, `Level attested_level) @@ -170,16 +146,25 @@ let skip_list_cells_content_cache = Slots_statuses_cache.create (attestation_lag * number_of_slots * retention_levels) -let dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id = +let dal_skip_list_cell_content_of_slot_id node_ctxt + (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) slot_id + = let open Lwt_result_syntax in + let attested_level = + Int32.add + (Raw_level.to_int32 slot_id.Dal.Slot.Header.published_level) + (Int32.of_int dal_constants.attestation_lag) + in + let* attested_level_hash = + Node_context.hash_of_level node_ctxt attested_level + in match Slots_statuses_cache.find_opt skip_list_cells_content_cache slot_id with - | Some res -> return res - | None -> ( + | Some (cell_content, block_hash) + when Block_hash.equal attested_level_hash block_hash -> + return cell_content + | None | Some _ -> ( let* hash_with_cells = - download_skip_list_cells_of_level - node_ctxt - dal_constants - ~published_level:slot_id.published_level + download_skip_list_cells_of_level node_ctxt ~attested_level in List.iter (fun (_hash, cell) -> @@ -187,15 +172,35 @@ let dal_skip_list_cell_content_of_slot_id node_ctxt dal_constants slot_id = Slots_statuses_cache.replace skip_list_cells_content_cache (Dal.Slots_history.content_id content) - content) + (content, attested_level_hash)) hash_with_cells ; (* The [find] below validates the fact that we fetched the info of commitments published at level [slot_id.published_level]. *) match Slots_statuses_cache.find_opt skip_list_cells_content_cache slot_id with - | Some res -> return res - | None -> tzfail @@ Dal_expected_skip_list_cell_not_found slot_id) + | Some (cell_content, block_hash) + when Block_hash.equal attested_level_hash block_hash -> + return cell_content + | None -> + Stdlib.failwith + (Format.asprintf + "Unreachable: We expect to find some data for slot %a, but got \ + none" + Dal.Slot.Header.pp_id + slot_id) + | Some (_, got_hash) -> + Stdlib.failwith + (Format.asprintf + "Unreachable: We expect to find some data for slot %a \ + associated to block hash %a, but got data associated to block \ + hash %a" + Dal.Slot.Header.pp_id + slot_id + Block_hash.pp_short + attested_level_hash + Block_hash.pp_short + got_hash)) let slot_proto_attestation_status node_ctxt dal_constants slot_id = let open Lwt_result_syntax in -- GitLab