From f0910543bef7ab61936a50ed6d167d546b3cc9d5 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Tue, 25 Nov 2025 19:21:59 +0100 Subject: [PATCH 1/4] Tezt/DAL/Rollups: New kernel that requests DAL pages far in the future --- tezt/lib_tezos/constant.ml | 10 ++ ...al_reveal_pages_high_target_pub_level.wabt | 108 ++++++++++++++++++ ...al_reveal_pages_high_target_pub_level.wasm | Bin 0 -> 471 bytes 3 files changed, 118 insertions(+) create mode 100644 tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wabt create mode 100644 tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wasm diff --git a/tezt/lib_tezos/constant.ml b/tezt/lib_tezos/constant.ml index 98e118d7c5d6..c162177d5d31 100644 --- a/tezt/lib_tezos/constant.ml +++ b/tezt/lib_tezos/constant.ml @@ -173,6 +173,16 @@ module WASM = struct ~path:"tezt/tests/kernels/echo_dal_reveal_pages.wasm" () + (* Same as {!echo_dal_reveal_pages} above, but the publish level is equal to + 3000. It is used to test the corner case where a rollup is asked to import + a slot whose level is very far in the future. *) + let echo_dal_reveal_pages_high_target_pub_level = + Uses.make + ~tag:"echo_dal_reveal_pages" + ~path: + "tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wasm" + () + let evm_kernel = Uses.make ~how_to_build:"make -f etherlink.mk build" diff --git a/tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wabt b/tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wabt new file mode 100644 index 000000000000..32cd35365f57 --- /dev/null +++ b/tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wabt @@ -0,0 +1,108 @@ +(module + (import "smart_rollup_core" "reveal" + (func $reveal (param i32 i32 i32 i32) (result i32))) + (import "smart_rollup_core" "store_write" + (func $store_write (param i32 i32 i32 i32 i32) (result i32))) + (import "smart_rollup_core" "store_value_size" + (func $store_value_size (param i32 i32) (result i32))) + + (memory 2) + (export "memory" (memory 0)) + + (global $PUBLISHED_LEVEL i32 (i32.const 3000)) + (global $SLOT_INDEX i32 (i32.const 1)) + (global $NUM_PAGES i32 (i32.const 32)) + ;; CHANGEMENT MINIMAL: 3967 -> 4096 pour laisser la place complete a la page + (global $PAGE_SIZE i32 (i32.const 4096)) + (global $CHUNK i32 (i32.const 1024)) + + ;; Buffers + (global $REQ_PTR i32 (i32.const 64)) ;; payload 8B + (global $PAGE_BUF i32 (i32.const 128)) ;; page buffer + (data (i32.const 65536) "/dal/page") + (global $KEY_PTR i32 (i32.const 65536)) + (global $KEY_LEN i32 (i32.const 9)) + + (func $write_chunked (param $path_ptr i32) (param $path_len i32) + (param $base_off i32) (param $src_ptr i32) (param $len i32) + (result i32) + (local $written i32) (local $part i32) (local $rc i32) + (local.set $written (i32.const 0)) + (block $done + (loop $loop + (br_if $done (i32.ge_u (local.get $written) (local.get $len))) + (local.set $part + (select + (global.get $CHUNK) + (i32.sub (local.get $len) (local.get $written)) + (i32.gt_u (i32.sub (local.get $len) (local.get $written)) (global.get $CHUNK)) + ) + ) + (local.set $rc + (call $store_write + (local.get $path_ptr) (local.get $path_len) + (i32.add (local.get $base_off) (local.get $written)) + (i32.add (local.get $src_ptr) (local.get $written)) + (local.get $part) + ) + ) + (br_if $done (i32.lt_s (local.get $rc) (i32.const 0))) + (local.set $written (i32.add (local.get $written) (local.get $part))) + (br $loop) + ) + ) + (local.get $written) + ) + + (func (export "kernel_run") + (local $page_index i32) (local $len i32) (local $offset i32) (local $cur_len i32) + (local.set $page_index (i32.const 2)) + + ;; payload [0x02 | level(i32 BE) | slot(u8) | page(i16 BE)] + (i32.store8 (global.get $REQ_PTR) (i32.const 2)) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 1)) + (i32.shr_u (global.get $PUBLISHED_LEVEL) (i32.const 24))) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 2)) + (i32.shr_u (global.get $PUBLISHED_LEVEL) (i32.const 16))) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 3)) + (i32.shr_u (global.get $PUBLISHED_LEVEL) (i32.const 8))) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 4)) + (global.get $PUBLISHED_LEVEL)) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 5)) + (global.get $SLOT_INDEX)) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 6)) + (i32.shr_u (local.get $page_index) (i32.const 8))) + (i32.store8 (i32.add (global.get $REQ_PTR) (i32.const 7)) + (local.get $page_index)) + + ;; reveal vers PAGE_BUF, avec dst_max = 4096 + (local.set $len + (call $reveal + (global.get $REQ_PTR) (i32.const 8) + (global.get $PAGE_BUF) (global.get $PAGE_SIZE) + ) + ) + + ;; ecriture en append sous "/dal/page" + ;; cur_len = store_value_size("/dal/page") + (local.set $cur_len + (call $store_value_size + (global.get $KEY_PTR) (global.get $KEY_LEN) + ) + ) + ;; si cur_len < 0 (pas de valeur / erreur), on part de 0 + (local.set $offset + (select + (i32.const 0) + (local.get $cur_len) + (i32.lt_s (local.get $cur_len) (i32.const 0)) + ) + ) + (drop + (call $write_chunked + (global.get $KEY_PTR) (global.get $KEY_LEN) + (local.get $offset) (global.get $PAGE_BUF) (local.get $len) + ) + ) + ) +) diff --git a/tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wasm b/tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wasm new file mode 100644 index 0000000000000000000000000000000000000000..51674a7d1bb27933c9e740cf16f6a319941ea033 GIT binary patch literal 471 zcmZQbEY4+QU|?XBW=UYFudlCXtWRJC(I5sBn9IPxl*lYtoSRrw5?_>`lT%s{pPXNm z%2t$GmYSHuzmU0jl1lp0@Nlv$F>z=$a?0Ff_C%qdNcFV3t=Wnf}vW@2P!WoBex zVl(BeXK>sh&Rx&o$Os}7KtuzCODnP|Feor8 zFe$KRDKLWwHUUOOb_I4v1`l3F1y%*NEJaox25xQzR_;DVMwWUq0sw5# BV1WPt literal 0 HcmV?d00001 -- GitLab From 26d8252b6d89a03c425b2b20319b55477e6a297a Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 26 Nov 2025 07:37:14 +0100 Subject: [PATCH 2/4] Tezt/DAL/Rollups: add a refutation test for when the requested published level is far in the future --- .../runtime-dependency-tags.out | 1 + tezt/tests/sc_rollup.ml | 96 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/tezt/lib_wrapper/expected/tezt_wrapper.ml/runtime-dependency-tags.out b/tezt/lib_wrapper/expected/tezt_wrapper.ml/runtime-dependency-tags.out index 9de645d80429..8bc6e22bb066 100644 --- a/tezt/lib_wrapper/expected/tezt_wrapper.ml/runtime-dependency-tags.out +++ b/tezt/lib_wrapper/expected/tezt_wrapper.ml/runtime-dependency-tags.out @@ -43,6 +43,7 @@ riscv: src/riscv/assets/preimages riscv: src/riscv/assets/riscv-dummy.elf riscv: src/riscv/assets/riscv-dummy.elf.checksum echo_dal_reveal_pages: tezt/tests/kernels/echo_dal_reveal_pages.wasm +echo_dal_reveal_pages: tezt/tests/kernels/echo_dal_reveal_pages_high_target_pub_level.wasm echo_dal_reveal_parameters: tezt/tests/kernels/echo_dal_reveal_parameters.wasm riscv: tezt/tests/riscv-tests/jstz-inbox.json alpha_json: tezt/tests/weeklynet_configs/alpha.json diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index a22e0b47a088..3970ac5995fc 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -3695,6 +3695,101 @@ let test_refutation_with_dal_page_import protocols = ~with_dal) all_cases +let test_refutation_with_dal_page_import_id_far_in_the_future protocols = + (* This is the published level for which the toy kernel + [Constant.WASM.echo_dal_reveal_pages_high_target_pub_level] asks to import + page 2 of slot 1. *) + let published_level = 3000 in + (* This is the test's attestation lag (checked in the scenario). *) + let dal_lag = 5 in + (* This is the slot index at which we'll possibly publish a slot. *) + let slot_index = 1 in + (* Once a slot is attested, its import is valid for this number of + blocks. After that, any import is considered invalid. *) + let dal_ttl = 50 in + + (* This inbox level is too small w.r.t. to target published level to import. + The rollup node should be able to progress even without knowing the status + of that slot whose id is far in the future. *) + let inbox_level = 5 in + + (* Which player will start the game. This will have an impact on which one + will provide the final proof. *) + let dimension_player_priority = [`Priority_loser; `Priority_honest] in + + (* The list of tests, as a cartesian product of 3 dimensions above. *) + let all_cases = + List.fold_left + (fun accu player_priority -> + {inbox_level; player_priority; attestation_status = `Unattested} :: accu) + [] + dimension_player_priority + in + + List.iter + (fun {inbox_level; attestation_status; player_priority} -> + (* One test name for each dimensions combination. *) + let variant = + Format.sprintf + "dal_page_with_id_far_in_the_future_flipped_at_inbox_level_%d_%s_%s" + inbox_level + (player_priority_to_string player_priority) + (attestation_status_to_string attestation_status) + in + (* The behaviour of the loser mode is parameterized by the inbox level at + which the payload of imported page is flipped. *) + let loser_modes = + (* See src/lib_smart_rollup_node/loser_mode.mli for the semantics of this + loser mode. *) + [ + Format.sprintf + "reveal_dal_page published_level:%d slot_index:1 page_index:2 \ + strategy:flip inbox_level:%d" + published_level + inbox_level; + ] + in + let may_publish_and_attest_slot = + if attestation_status = `Unattested then None + else Some (published_level, slot_index) + in + let with_dal = + with_dal_ready_for_echo_dal_reveal_pages + ~operator_profiles:[slot_index] + ~kernel_imported_publish_level:inbox_level + (* We'll not progress until published_level on purpose. *) + ~expected_attestation_lag:dal_lag + ~may_publish_and_attest_slot + in + let priority = + (player_priority :> [`No_priority | `Priority_honest | `Priority_loser]) + in + test_refutation_scenario + ~uses:(fun _protocol -> + [Constant.WASM.echo_dal_reveal_pages; Constant.octez_dal_node]) + ~kind:"wasm_2_0_0" + ~mode:Operator + ~challenge_window:150 + ~timeout:120 + ~commitment_period:10 + ~dal_attested_slots_validity_lag:dal_ttl + ~variant + ~boot_sector: + (read_kernel + ~base:"" + ~suffix:"" + (Uses.path + Constant.WASM.echo_dal_reveal_pages_high_target_pub_level)) + (refutation_scenario_parameters + ~loser_modes + (inputs_for 10) + ~final_level:100 + ~priority) + protocols + ~regression:false + ~with_dal) + all_cases + (** Run one of the refutation tests with an accuser instead of a full operator. *) let test_accuser protocols = test_refutation_scenario @@ -7677,6 +7772,7 @@ let register_protocol_independent () = test_accuser protocols ; test_invalid_dal_parameters protocols ; test_refutation_with_dal_page_import protocols ; + test_refutation_with_dal_page_import_id_far_in_the_future protocols ; test_bailout_refutation protocols ; test_multiple_batcher_key ~kind protocols ; test_batcher_order_msgs ~kind protocols ; -- GitLab From 056d73fdf673c159b53662265346ace54dc3a815 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Mon, 1 Dec 2025 18:16:29 +0100 Subject: [PATCH 3/4] Proto/DAL: Harden the import of a page whose published level in far in the future --- .../lib_sc_rollup_node/dal_pages_request.ml | 114 +++++++++++++----- 1 file changed, 84 insertions(+), 30 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 84857d84d0fc..0e74e5b5d382 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 @@ -316,6 +316,46 @@ let get_dal_node cctxt_opt = let*! err = tzfail No_dal_node_provided in Environment.wrap_tzresult err |> Lwt.return +(* Check whether [page_id] could be valid for at least one admissible DAL + attestation lag. + + This function mirrors the pre-check done in DAL refutations' + [produce_proof_repr]. At this point we do not know the exact DAL + attestation lag, so we over-approximate the admissible range by the whole + interval [0, legacy_attestation_lag] and reuse [page_id_is_valid] on both + extremal values: + + - If [page_id_is_valid] is [false] for both lags (0 and + [legacy_attestation_lag]), then [page_id] is definitely invalid for any + admissible lag. In that case, any import of the corresponding slot/page + is known to be invalid and we can reject it immediately, without ever + querying the DAL node. + + - Otherwise, there exists at least one admissible lag for which + [page_id] may still be valid, and the DAL node must be consulted to + decide. + + This early check protects the rollup node from pathological situations + where the DAL node cannot answer about a slot/page whose published level is + very far in the past or future: when the result is [false], we already know + that no import could be valid. *) +let may_be_valid_in_our_range_of_lags chain_id ~number_of_slots ~number_of_pages + ~dal_activation_level ~origination_level ~inbox_level page_id + ~dal_attested_slots_validity_lag = + let f = + page_id_is_valid + chain_id + ~number_of_slots + ~number_of_pages + ~dal_activation_level + ~origination_level + ~inbox_level + ~dal_attested_slots_validity_lag + page_id + in + f ~dal_attestation_lag:0 + || f ~dal_attestation_lag:Dal.Slots_history.legacy_attestation_lag + let page_content_int (dal_constants : Octez_smart_rollup.Rollup_constants.dal_constants) ~dal_activation_level ~inbox_level node_ctxt page_id @@ -325,38 +365,52 @@ let page_content_int node_ctxt in let* chain_id = Layer1.get_chain_id l1_ctxt in - let* dal_cctxt = get_dal_node node_ctxt.dal_cctxt in - let Dal.Slot.Header.{published_level; index} = page_id.Dal.Page.slot_id in - let dal_slot_status_max_fetch_attempts = - node_ctxt.config.dal_slot_status_max_fetch_attempts + let may_be_valid_id = + may_be_valid_in_our_range_of_lags + chain_id + ~number_of_slots:dal_constants.number_of_slots + ~number_of_pages: + (Dal.Page.pages_per_slot dal_constants.cryptobox_parameters) + ~dal_activation_level + ~origination_level + ~inbox_level + page_id + ~dal_attested_slots_validity_lag in + if not may_be_valid_id then return_none + else + let* dal_cctxt = get_dal_node node_ctxt.dal_cctxt in + let Dal.Slot.Header.{published_level; index} = page_id.Dal.Page.slot_id in + let dal_slot_status_max_fetch_attempts = + node_ctxt.config.dal_slot_status_max_fetch_attempts + in - let* status = - get_slot_header_attestation_info - dal_cctxt - ~published_level - ~index - ~dal_slot_status_max_fetch_attempts - in - match status with - | `Attested attestation_lag -> - if - not - @@ page_id_is_valid - chain_id - ~dal_attestation_lag:attestation_lag - ~number_of_slots:dal_constants.number_of_slots - ~number_of_pages: - (Dal.Page.pages_per_slot dal_constants.cryptobox_parameters) - ~dal_activation_level - ~origination_level - ~inbox_level - ~dal_attested_slots_validity_lag - page_id - then return_none - else get_page dal_cctxt ~inbox_level page_id - | `Unattested | `Unpublished -> return_none - | `Waiting_attestation -> attestation_status_not_final published_level index + let* status = + get_slot_header_attestation_info + dal_cctxt + ~published_level + ~index + ~dal_slot_status_max_fetch_attempts + in + match status with + | `Attested attestation_lag -> + if + not + @@ page_id_is_valid + chain_id + ~dal_attestation_lag:attestation_lag + ~number_of_slots:dal_constants.number_of_slots + ~number_of_pages: + (Dal.Page.pages_per_slot dal_constants.cryptobox_parameters) + ~dal_activation_level + ~origination_level + ~inbox_level + ~dal_attested_slots_validity_lag + page_id + then return_none + else get_page dal_cctxt ~inbox_level page_id + | `Unattested | `Unpublished -> return_none + | `Waiting_attestation -> attestation_status_not_final published_level index let with_errors_logging ~inbox_level slot_id f = let open Lwt_syntax in -- GitLab From 91e8be36ca974cb9edc9f89fa36fe24dfb2698b8 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 26 Nov 2025 10:02:17 +0100 Subject: [PATCH 4/4] Proto/DAL: Harden handling of Invalid_page_id proofs in produce_proof --- src/proto_alpha/lib_protocol/dal_slot_repr.ml | 266 +++++++++--------- 1 file changed, 136 insertions(+), 130 deletions(-) diff --git a/src/proto_alpha/lib_protocol/dal_slot_repr.ml b/src/proto_alpha/lib_protocol/dal_slot_repr.ml index 1abeaf33c273..6b1aaa26885d 100644 --- a/src/proto_alpha/lib_protocol/dal_slot_repr.ml +++ b/src/proto_alpha/lib_protocol/dal_slot_repr.ml @@ -1338,7 +1338,7 @@ module History = struct in Format.fprintf fmt - "Page_unconfirmed (%a attestation_threshold_percent:%a@ \ + "Invalid_page_id (%a attestation_threshold_percent:%a@ \ commitment_publisher:%a)" pp_target_and_proof target_cell_and_inc_proof @@ -1466,148 +1466,154 @@ module History = struct ~attestation_threshold_percent ~restricted_commitments_publishers page_id ~page_info ~get_history slots_hist = let open Lwt_result_syntax in - let Page.{slot_id = target_slot_id; page_index = _} = page_id in - (* We first search for the slots attested at level [published_level]. *) - let*! search_result = - Skip_list.search ~deref:get_history ~target_slot_id ~cell:slots_hist + (* We might be able to decide about the validity of [page_id] even + though we do not know the exact DAL attestation lag. To do so, we + over-approximate the set of admissible lags by the whole interval + [0, legacy_attestation_lag]. + + - If [page_id_is_valid] is false for both extremal lags (0 and + [legacy_attestation_lag]), then [page_id] is definitely invalid + for any admissible lag, and we can immediately return + [Invalid_page_id]. + + - Otherwise, there exists at least one admissible lag for which + [page_id] may still be valid. In that case, a failure to fetch + the target skip-list cell is interpreted as missing history in + the operator's context rather than a definitely invalid page id. + The proof may later be requalified as [Invalid_page_id] once the + actual attestation lag is known; see the second call to + [page_id_is_valid] below. + + This check used to be done after the skip-list search, but it was + moved before it in order to catch as many definitely-invalid cases as + possible, even when the skip-list history is incomplete. *) + let max_attestation_lag = legacy_attestation_lag in + let may_be_valid_in_our_range_of_lags = + page_id_is_valid ~dal_attestation_lag:max_attestation_lag page_id + || page_id_is_valid ~dal_attestation_lag:0 page_id in - (* The search should necessarily find a cell in the skip list (assuming + if not may_be_valid_in_our_range_of_lags then + (* Page id is definitely invalid, even under the upper bound. *) + return + ( Invalid_page_id + { + target_cell_and_inc_proof = None; + attestation_threshold_percent; + restricted_commitments_publishers; + }, + None ) + else + let Page.{slot_id = target_slot_id; page_index = _} = page_id in + (* We first search for the slots attested at level [published_level]. *) + let*! search_result = + Skip_list.search ~deref:get_history ~target_slot_id ~cell:slots_hist + in + (* The search should necessarily find a cell in the skip list (assuming enough cache is given) under the assumptions made when calling {!produce_proof_repr}. *) - match search_result.Skip_list.last_cell with - | Deref_returned_none -> - (* We do not know the exact attestation lag here and we failed to - fetch the target cell from the skip-list DB. To decide whether the - page id could still be valid, we conservatively consider the whole - interval of possible attestation lags, from [0] up to - [legacy_attestation_lag] (our historical upper bound): - - - If [page_id_is_valid] holds either with [~dal_attestation_lag = - 0] or with [~dal_attestation_lag = legacy_attestation_lag], then - there exists at least one admissible lag for which the page id may - still be valid. In that case, the failure is possibly due to - missing skip-list cells in the operator's context. We cannot safely - produce a proof: the operator must first fetch and store the - missing cells. Later, the proof may still be requalified as - [Invalid_page_id] if the actual attestation lag rules it out; see - the second call to [page_id_is_valid] below. - - - If [page_id_is_valid] does not hold for either extremal value, - the page id is definitely invalid for any lag in the admissible - range, and returning [Invalid_page_id] is sound. *) - let max_attestation_lag = legacy_attestation_lag in - if - page_id_is_valid ~dal_attestation_lag:max_attestation_lag page_id - || page_id_is_valid ~dal_attestation_lag:0 page_id - then - (* Page id may be valid, but we are missing skip-list cells. *) + match search_result.Skip_list.last_cell with + | Deref_returned_none -> tzfail @@ dal_proof_error "Skip_list.search returned 'Deref_returned_none': Slots \ history cache is ill-formed or has too few entries." - else - (* Page id is definitely invalid, even under the upper bound. *) - return - ( Invalid_page_id - { - target_cell_and_inc_proof = None; - attestation_threshold_percent; - restricted_commitments_publishers; - }, - None ) - | No_exact_or_lower_ptr -> - tzfail - @@ dal_proof_error - "Skip_list.search returned 'No_exact_or_lower_ptr', while it is \ - initialized with a min elt (slot zero)." - | Nearest _ -> - (* This should not happen: there is one cell at each level - after DAL activation. The case where the page's level is before DAL - activation level should be handled by the caller - ({!Sc_refutation_proof.produce} in our case). *) - tzfail - @@ dal_proof_error - "Skip_list.search returned Nearest', while all given levels to \ - produce proofs are supposed to be in the skip list." - | Found target_cell -> - let* proof, page_opt = - let inc_proof = List.rev search_result.Skip_list.rev_path in - let target_cell_content = Skip_list.content target_cell in - let is_commitment_attested = - is_commitment_attested - ~attestation_threshold_percent - ~restricted_commitments_publishers - target_cell_content - in - let {attestation_lag; header_id = _} = - Content.content_id target_cell_content - in - let dal_attestation_lag = attestation_lag_value attestation_lag in - if not (page_id_is_valid ~dal_attestation_lag page_id) then - return - ( Invalid_page_id - { - target_cell_and_inc_proof = Some (target_cell, inc_proof); - attestation_threshold_percent; - restricted_commitments_publishers; - }, - None ) - else - match (page_info, is_commitment_attested) with - | Some (page_data, page_proof), Some commitment -> - (* The case where the slot to which the page is supposed to belong + | No_exact_or_lower_ptr -> + tzfail + @@ dal_proof_error + "Skip_list.search returned 'No_exact_or_lower_ptr', while it \ + is initialized with a min elt (slot zero)." + | Nearest _ -> + (* This should not happen under normal settings: there is one cell + at each level after DAL activation. The case where the page's + level is before DAL activation level should be handled by the + caller ({!Sc_refutation_proof.produce} in our case). + + For a published level far in the future w.r.t. to inbox level, + the test with may_be_valid_in_our_range_of_lags should prevent us + to run this part of the code. *) + tzfail + @@ dal_proof_error + "Skip_list.search returned Nearest', while all given levels \ + to produce proofs are supposed to be in the skip list." + | Found target_cell -> + let* proof, page_opt = + let inc_proof = List.rev search_result.Skip_list.rev_path in + let target_cell_content = Skip_list.content target_cell in + let is_commitment_attested = + is_commitment_attested + ~attestation_threshold_percent + ~restricted_commitments_publishers + target_cell_content + in + let {attestation_lag; header_id = _} = + Content.content_id target_cell_content + in + let dal_attestation_lag = attestation_lag_value attestation_lag in + if not (page_id_is_valid ~dal_attestation_lag page_id) then + return + ( Invalid_page_id + { + target_cell_and_inc_proof = Some (target_cell, inc_proof); + attestation_threshold_percent; + restricted_commitments_publishers; + }, + None ) + else + match (page_info, is_commitment_attested) with + | Some (page_data, page_proof), Some commitment -> + (* The case where the slot to which the page is supposed to belong is found and the page's information are given. *) - let*? () = - (* We check the page's proof against the commitment. *) - check_page_proof - dal_params - page_proof - page_data - page_id - commitment - in - (* All checks succeeded. We return a `Page_confirmed` proof. *) - return - ( Page_confirmed - { - target_cell; - inc_proof; - page_data; - page_proof; - attestation_threshold_percent; - restricted_commitments_publishers; - }, - Some page_data ) - | None, None -> - (* The slot corresponding to the given page's index is not found in + let*? () = + (* We check the page's proof against the commitment. *) + check_page_proof + dal_params + page_proof + page_data + page_id + commitment + in + (* All checks succeeded. We return a `Page_confirmed` proof. *) + return + ( Page_confirmed + { + target_cell; + inc_proof; + page_data; + page_proof; + attestation_threshold_percent; + restricted_commitments_publishers; + }, + Some page_data ) + | None, None -> + (* The slot corresponding to the given page's index is not found in the attested slots of the page's level, and no information is given for that page. So, we produce a proof that the page is not attested. *) - return - ( Page_unconfirmed - { - target_cell; - inc_proof; - attestation_threshold_percent; - restricted_commitments_publishers; - }, - None ) - | None, Some _ -> - (* Mismatch: case where no page information are given, but the + return + ( Page_unconfirmed + { + target_cell; + inc_proof; + attestation_threshold_percent; + restricted_commitments_publishers; + }, + None ) + | None, Some _ -> + (* Mismatch: case where no page information are given, but the slot is attested. *) - tzfail - @@ dal_proof_error - "The page ID's slot is confirmed, but no page content \ - and proof are provided." - | Some _, None -> - (* Mismatch: case where page information are given, but the slot + tzfail + @@ dal_proof_error + "The page ID's slot is confirmed, but no page content \ + and proof are provided." + | Some _, None -> + (* Mismatch: case where page information are given, but the slot is not attested. *) - tzfail - @@ dal_proof_error - "The page ID's slot is not confirmed, but page content \ - and proof are provided." - in - return (proof, page_opt) + tzfail + @@ dal_proof_error + "The page ID's slot is not confirmed, but page \ + content and proof are provided." + in + return (proof, page_opt) let produce_proof dal_params ~page_id_is_valid ~attestation_threshold_percent ~restricted_commitments_publishers -- GitLab