From 6d47c8e4e03b9dc499459df0bd36bd7480392b0a Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Mon, 27 Oct 2025 15:54:46 +0100 Subject: [PATCH 1/7] Rollups: fix typo in encoding field name --- src/lib_smart_rollup_node/loser_mode.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_smart_rollup_node/loser_mode.ml b/src/lib_smart_rollup_node/loser_mode.ml index 1b15b7c4e590..89848b796710 100644 --- a/src/lib_smart_rollup_node/loser_mode.ml +++ b/src/lib_smart_rollup_node/loser_mode.ml @@ -70,7 +70,7 @@ let encoding = ~title:"Invalid_dal_parameters" (obj4 (req "number_of_slots" int64) - (req "attestion_lag" int64) + (req "attestation_lag" int64) (req "slot_size" int64) (req "page_size" int64)) (function -- GitLab From 57b6964151012c5a21fc4c865abe0d3cf58ad9ec Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 29 Oct 2025 17:01:12 +0100 Subject: [PATCH 2/7] Tezt/Rollups/DAL: add prefix _parameters to toy kernel echo_dal_reveal --- tezt/lib_tezos/constant.ml | 6 +++--- .../tezt_wrapper.ml/runtime-dependency-tags.out | 2 +- ..._reveal.wabt => echo_dal_reveal_parameters.wabt} | 0 ..._reveal.wasm => echo_dal_reveal_parameters.wasm} | Bin tezt/tests/sc_rollup.ml | 6 ++++-- 5 files changed, 8 insertions(+), 6 deletions(-) rename tezt/tests/kernels/{echo_dal_reveal.wabt => echo_dal_reveal_parameters.wabt} (100%) rename tezt/tests/kernels/{echo_dal_reveal.wasm => echo_dal_reveal_parameters.wasm} (100%) diff --git a/tezt/lib_tezos/constant.ml b/tezt/lib_tezos/constant.ml index 7adb78d25150..8c30e47a3ee7 100644 --- a/tezt/lib_tezos/constant.ml +++ b/tezt/lib_tezos/constant.ml @@ -152,10 +152,10 @@ module WASM = struct "src/proto_alpha/lib_protocol/test/integration/wasm_kernel/echo.wasm" () - let echo_dal_reveal = + let echo_dal_reveal_parameters = Uses.make - ~tag:"echo_dal_reveal" - ~path:"tezt/tests/kernels/echo_dal_reveal.wasm" + ~tag:"echo_dal_reveal_parameters" + ~path:"tezt/tests/kernels/echo_dal_reveal_parameters.wasm" () let evm_kernel = 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 3edfbec3e128..2a0a9de69d34 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 @@ -42,7 +42,7 @@ riscv: src/riscv/assets/jstz.checksum riscv: src/riscv/assets/preimages riscv: src/riscv/assets/riscv-dummy.elf riscv: src/riscv/assets/riscv-dummy.elf.checksum -echo_dal_reveal: tezt/tests/kernels/echo_dal_reveal.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 last_snapshotted_protocol_json: tezt/tests/weeklynet_configs/last_snapshotted_protocol.json diff --git a/tezt/tests/kernels/echo_dal_reveal.wabt b/tezt/tests/kernels/echo_dal_reveal_parameters.wabt similarity index 100% rename from tezt/tests/kernels/echo_dal_reveal.wabt rename to tezt/tests/kernels/echo_dal_reveal_parameters.wabt diff --git a/tezt/tests/kernels/echo_dal_reveal.wasm b/tezt/tests/kernels/echo_dal_reveal_parameters.wasm similarity index 100% rename from tezt/tests/kernels/echo_dal_reveal.wasm rename to tezt/tests/kernels/echo_dal_reveal_parameters.wasm diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 5d332b897dd5..8af44d105b10 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -3427,7 +3427,9 @@ let test_refutation protocols ~kind = let test_invalid_dal_parameters protocols = test_refutation_scenario ~uses:(fun _protocol -> - [Constant.WASM.echo_dal_reveal; Constant.smart_rollup_installer]) + [ + Constant.WASM.echo_dal_reveal_parameters; Constant.smart_rollup_installer; + ]) ~kind:"wasm_2_0_0" ~mode:Operator ~challenge_window:10 @@ -3438,7 +3440,7 @@ let test_invalid_dal_parameters protocols = (read_kernel ~base:"" ~suffix:"" - (Uses.path Constant.WASM.echo_dal_reveal)) + (Uses.path Constant.WASM.echo_dal_reveal_parameters)) (refutation_scenario_parameters ~loser_modes:["reveal_dal_parameters 6 6 6 6"] (inputs_for 10) -- GitLab From 722c864a5c0c5c6b3709e340e32fd832b6c3eb0c Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Tue, 28 Oct 2025 17:30:19 +0100 Subject: [PATCH 3/7] Tezt/rollups: setup_rollup takes an optional DAL node --- tezt/lib_tezos/sc_rollup_helpers.ml | 21 +++++++++++++++++++-- tezt/lib_tezos/sc_rollup_helpers.mli | 1 + 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tezt/lib_tezos/sc_rollup_helpers.ml b/tezt/lib_tezos/sc_rollup_helpers.ml index 85acc91940d6..3b99502120d7 100644 --- a/tezt/lib_tezos/sc_rollup_helpers.ml +++ b/tezt/lib_tezos/sc_rollup_helpers.ml @@ -979,7 +979,7 @@ let bake_until ?hook cond n client = its deposit while the honest one has not. *) -let test_refutation_scenario_aux ~(mode : Sc_rollup_node.mode) ~kind +let test_refutation_scenario_aux ~(mode : Sc_rollup_node.mode) ~kind ?with_dal { loser_modes; inputs; @@ -1006,6 +1006,12 @@ let test_refutation_scenario_aux ~(mode : Sc_rollup_node.mode) ~kind let detected_timeouts = Hashtbl.create 5 in let dissections = Hashtbl.create 17 in + let* dal_node = + match with_dal with + | None -> Lwt.return_none + | Some init_dal_node -> init_dal_node node client + in + let run_honest_node sc_rollup_node = let gather_conflicts_promise = let rec gather_conflicts () = @@ -1062,8 +1068,18 @@ let test_refutation_scenario_aux ~(mode : Sc_rollup_node.mode) ~kind in if priority = `Priority_honest then prioritize_refute_operations sc_rollup_node ; + let arguments = + Option.fold + ~none:[] + ~some:(fun dal -> [Sc_rollup_node.Dal_node dal]) + dal_node + in let* () = - Sc_rollup_node.run ~event_level:`Debug sc_rollup_node sc_rollup_address [] + Sc_rollup_node.run + ~event_level:`Debug + sc_rollup_node + sc_rollup_address + arguments in return [ @@ -1083,6 +1099,7 @@ let test_refutation_scenario_aux ~(mode : Sc_rollup_node.mode) ~kind Sc_rollup_node.create Operator node + ?dal_node ~base_dir:(Client.base_dir client) ~default_operator ~name:rollup_node_name diff --git a/tezt/lib_tezos/sc_rollup_helpers.mli b/tezt/lib_tezos/sc_rollup_helpers.mli index 06b40a8adc6f..4f1e4cf77f4e 100644 --- a/tezt/lib_tezos/sc_rollup_helpers.mli +++ b/tezt/lib_tezos/sc_rollup_helpers.mli @@ -401,6 +401,7 @@ val reveal_hash : protocol:'a -> kind:string -> string -> reveal_hash val test_refutation_scenario_aux : mode:Sc_rollup_node.mode -> kind:string -> + ?with_dal:(Node.t -> Client.t -> Dal_node.t option Lwt.t) -> refutation_scenario_parameters -> 'a -> Sc_rollup_node.t -> -- GitLab From 81d55ebf40ed4c1e95ab1712653625de96910d3c Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 29 Oct 2025 10:21:20 +0100 Subject: [PATCH 4/7] Tezt/rollups: extend test_refutation_scenario to optionally support DAL --- tezt/tests/sc_rollup.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 8af44d105b10..4c92a0935705 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -3180,7 +3180,7 @@ let test_can_stake ~kind = let test_refutation_scenario ?commitment_period ?challenge_window ~variant ~mode ~kind ?(ci_disabled = false) ?uses ?(timeout = 60) ?timestamp ?boot_sector - ?(extra_tags = []) ({allow_degraded; _} as scenario) = + ?(extra_tags = []) ?with_dal ({allow_degraded; _} as scenario) = let regression = (* TODO: https://gitlab.com/tezos/tezos/-/issues/5313 Disabled dissection regressions for parallel games, as it introduces @@ -3210,7 +3210,7 @@ let test_refutation_scenario ?commitment_period ?challenge_window ~variant ~mode variant = Some variant; description = "refutation games winning strategies"; } - (test_refutation_scenario_aux ~mode ~kind scenario) + (test_refutation_scenario_aux ?with_dal ~mode ~kind scenario) let test_refutation protocols ~kind = let challenge_window = 10 in -- GitLab From b92bf38532bc1c5cbcc8428ec573caa02c451261 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 29 Oct 2025 15:45:47 +0100 Subject: [PATCH 5/7] Rollups/loser_mode: add a mode to forge invalid DAL pages content --- src/lib_smart_rollup_node/loser_mode.ml | 92 +++++++++++++++++++++++- src/lib_smart_rollup_node/loser_mode.mli | 46 ++++++++++++ 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/src/lib_smart_rollup_node/loser_mode.ml b/src/lib_smart_rollup_node/loser_mode.ml index 89848b796710..a782a831531a 100644 --- a/src/lib_smart_rollup_node/loser_mode.ml +++ b/src/lib_smart_rollup_node/loser_mode.ml @@ -53,7 +53,27 @@ type dal_parameters = { page_size : int64; } -type t = Failure of failure list | Invalid_dal_parameters of dal_parameters +(** See the mli file for the doc. *) +type dal_page = { + published_level : int32 option; + slot_index : int option; + page_index : int option; + page_payload_strategy : [`Alter | `Flip]; +} + +type t = + | Failure of failure list + | Invalid_dal_parameters of dal_parameters + | Invalid_dal_page of dal_page + +let page_payload_strategy_of_string s = + match String.lowercase_ascii s with + | "flip" -> `Flip + | "alter" -> `Alter + | s -> Stdlib.failwith ("Unknown DAL content strategy " ^ s) + +let page_payload_strategy_to_string strat = + match strat with `Flip -> "flip" | `Alter -> "alter" let encoding = let open Data_encoding in @@ -81,10 +101,38 @@ let encoding = (fun (number_of_slots, attestation_lag, slot_size, page_size) -> Invalid_dal_parameters {number_of_slots; attestation_lag; slot_size; page_size}); + case + (Tag 2) + ~title:"Invalid_dal_page" + (obj4 + (opt "published_level" int32) + (opt "slot_index" uint8) + (opt "page_index" uint16) + (req "page_payload_strategy" string)) + (function + | Invalid_dal_page + {published_level; slot_index; page_index; page_payload_strategy} + -> + Some + ( published_level, + slot_index, + page_index, + page_payload_strategy_to_string page_payload_strategy ) + | _ -> None) + (fun (published_level, slot_index, page_index, strat) -> + Invalid_dal_page + { + published_level; + slot_index; + page_index; + page_payload_strategy = page_payload_strategy_of_string strat; + }); ] let no_failures = Failure [] +let wildcard_or_value f str = match str with "*" -> None | _ -> Some (f str) + let make s = let tokens = String.split_on_char ' ' s in match tokens with @@ -103,6 +151,15 @@ let make s = slot_size = Int64.of_string slot_size; page_size = Int64.of_string page_size; }) + | ["reveal_dal_page"; published_level; slot_index; page_index; strategy] -> + Some + (Invalid_dal_page + { + published_level = wildcard_or_value Int32.of_string published_level; + slot_index = wildcard_or_value int_of_string slot_index; + page_index = wildcard_or_value int_of_string page_index; + page_payload_strategy = page_payload_strategy_of_string strategy; + }) | _ -> ( let rec chop = function | [] | [""] -> [] @@ -120,7 +177,7 @@ let make s = let is_failure t ~level ~message_index = match t with - | Invalid_dal_parameters _ -> [] + | Invalid_dal_parameters _ | Invalid_dal_page _ -> [] | Failure failures -> List.filter_map (fun f -> @@ -130,5 +187,34 @@ let is_failure t ~level ~message_index = failures let is_invalid_dal_parameters = function - | Failure _ -> None + | Failure _ | Invalid_dal_page _ -> None | Invalid_dal_parameters parameters -> Some parameters + +let is_invalid_dal_page ~published_level ~slot_index ~page_index ~page_size + ~honest_payload = + let eq_or_wildcard v = function None -> true | Some w -> v = w in + function + | Failure _ | Invalid_dal_parameters _ -> Either.left () + | Invalid_dal_page + { + published_level = pl; + slot_index = si; + page_index = pi; + page_payload_strategy; + } -> + if + eq_or_wildcard published_level pl + && eq_or_wildcard slot_index si + && eq_or_wildcard page_index pi + then + Either.right + @@ + match (page_payload_strategy, honest_payload) with + | `Flip, Some _ -> None + | `Flip, None -> Some (Bytes.make page_size 'L') + | `Alter, None -> None + | `Alter, Some data -> + let lll = Bytes.make page_size 'L' in + let zzz = Bytes.make page_size 'Z' in + Option.some (if Bytes.equal data lll then zzz else lll) + else Either.left () diff --git a/src/lib_smart_rollup_node/loser_mode.mli b/src/lib_smart_rollup_node/loser_mode.mli index ac845e403ac1..8ac1c53cf1f7 100644 --- a/src/lib_smart_rollup_node/loser_mode.mli +++ b/src/lib_smart_rollup_node/loser_mode.mli @@ -33,6 +33,31 @@ type dal_parameters = { page_size : int64; } +(** DAL page selector and payload-forging strategy. + + Optional fields act as wildcards: + - [None] means "match any value" for that dimension. + Example: + { published_level = None; slot_index = Some 3; page_index = None; _ } + matches every page of slot index 3 at any level. + + Fields: + - [published_level]: L1 level at which the slot was published (or [None]). + - [slot_index]: Index of the target slot at that level (or [None]). + - [page_index]: Index of the target page within the slot (or [None]). + - [page_payload_strategy]: How the faulty/losing node derives the payload + from the honest page: + - [`Alter]: Mutate the bytes of an existing [Some payload]. + No effect if the honest payload is [None]. + - [`Flip]: Toggle presence: [None] -> [Some bytes], + [Some _] -> [None]. *) +type dal_page = { + published_level : int32 option; + slot_index : int option; + page_index : int option; + page_payload_strategy : [`Alter | `Flip]; +} + val encoding : t Data_encoding.t (** [no_failures] are planned. *) @@ -53,3 +78,24 @@ val make : string -> t option val is_failure : t -> level:int -> message_index:int -> int64 list val is_invalid_dal_parameters : t -> dal_parameters option + +(** Decide whether to corrupt a DAL page and, if so, how. + + Given the current [published_level], [slot_index], [page_index], [page_size], + the honest page payload ([honest_payload]), and a failure plan [t], this + function checks whether an [Invalid_dal_page] rule matches (wildcards allowed + via [None] in the rule). If no rule applies, returns [Either.left ()]. + + If a rule applies, returns [Either.right forged]: + - [forged = None] -> payload is removed (flip from [Some _] to [None]). + - [forged = Some bs] -> payload is replaced with [bs]. + + See {!dal_page} above for forge strategies. *) +val is_invalid_dal_page : + published_level:int32 -> + slot_index:int -> + page_index:int -> + page_size:int -> + honest_payload:bytes option -> + t -> + (unit, bytes option) Either.t -- GitLab From c6b64422bbe6c8f86daaddbb93ab9312c8e447e7 Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 29 Oct 2025 11:34:13 +0100 Subject: [PATCH 6/7] Rollups/DAL: split into nested matchs for the next commit --- src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml index a972ac758266..c704b85fa040 100644 --- a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml @@ -165,11 +165,13 @@ module Make_fueled (F : Fuel.S) : FUELED_PVM with type fuel = F.t = struct a tzresult. *) (* This happens when, for example, the kernel requests a page from a future level. *) Lwt.fail (Error_wrapper error) - | Ok None -> - (* The page was not confirmed by L1. + | Ok data_opt -> ( + match data_opt with + | None -> + (* The page was not confirmed by L1. We return empty string in this case, as done in the slow executon. *) - Lwt.return "" - | Ok (Some b) -> Lwt.return (Bytes.to_string b)) + Lwt.return "" + | Some b -> Lwt.return (Bytes.to_string b))) | Reveal_dal_parameters -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/6562 Consider supporting revealing of historical DAL parameters. *) -- GitLab From 7593b82c38b65ef29429bf811adc393dc6568aed Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Wed, 29 Oct 2025 11:52:53 +0100 Subject: [PATCH 7/7] Rollups/DAL: plug Loser_mode.is_invalid_dal_page in fueled_pvm --- .../lib_sc_rollup_node/fueled_pvm.ml | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml index c704b85fa040..f2f6a69c71dc 100644 --- a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml @@ -166,10 +166,28 @@ module Make_fueled (F : Fuel.S) : FUELED_PVM with type fuel = F.t = struct (* This happens when, for example, the kernel requests a page from a future level. *) Lwt.fail (Error_wrapper error) | Ok data_opt -> ( + let data_opt = + let published_level = + Raw_level.to_int32 dal_page.slot_id.published_level + in + let slot_index = Dal.Slot_index.to_int dal_page.slot_id.index in + let page_index = dal_page.page_index in + match + Loser_mode.is_invalid_dal_page + node_ctxt.config.loser_mode + ~published_level + ~slot_index + ~page_index + ~page_size:(Int64.to_int dal_parameters.page_size) + ~honest_payload:data_opt + with + | Either.Left () -> data_opt + | Either.Right data_opt -> data_opt + in match data_opt with | None -> (* The page was not confirmed by L1. - We return empty string in this case, as done in the slow executon. *) + We return empty string in this case, as done in the slow executon. *) Lwt.return "" | Some b -> Lwt.return (Bytes.to_string b))) | Reveal_dal_parameters -> -- GitLab