From 24d3c405fd94bc2132ad91eaf148b0ef6eb9745e Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Thu, 9 Oct 2025 11:48:56 +0200 Subject: [PATCH 1/4] Tezt/Lib_tezos/DAL: Add a function bytes_of_slot --- tezt/lib_tezos/dal_common.ml | 2 ++ tezt/lib_tezos/dal_common.mli | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tezt/lib_tezos/dal_common.ml b/tezt/lib_tezos/dal_common.ml index 69c7f0ff9847..382c6af5205f 100644 --- a/tezt/lib_tezos/dal_common.ml +++ b/tezt/lib_tezos/dal_common.ml @@ -607,6 +607,8 @@ module Helpers = struct type slot = string + let bytes_of_slot = Bytes.of_string + let make_slot ?(padding = true) ~slot_size slot = let actual_slot_size = String.length slot in if actual_slot_size < slot_size && padding then diff --git a/tezt/lib_tezos/dal_common.mli b/tezt/lib_tezos/dal_common.mli index a03dfdc2e1e3..0fa1cf0491a5 100644 --- a/tezt/lib_tezos/dal_common.mli +++ b/tezt/lib_tezos/dal_common.mli @@ -76,6 +76,8 @@ module Helpers : sig are smaller than the expected size of a slot. *) type slot + val bytes_of_slot : slot -> bytes + (** [make_slot ?padding ~slot_size content] produces a slot. If [padding=true] (which is the default), then the content is padded to reach the expected size given by [slot_size] (which is usually obtained from -- GitLab From 3483b8a7bf995aa5d6d18229839a93569123cef1 Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Thu, 9 Oct 2025 11:51:06 +0200 Subject: [PATCH 2/4] Tezt/Lib_tezos/DAL: Add an injection error for trap denunciation of wrong commitment --- tezt/lib_tezos/operation_core.ml | 7 +++++++ tezt/lib_tezos/operation_core.mli | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index 5fb35da1b1c7..ef90e08cd0db 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -1232,3 +1232,10 @@ let outdated_dal_denunciation = let injection_error_unknown_branch = rex {|Operation ([\w\d]+) is branched on either:|} + +let dal_entrapment_wrong_commitment = + rex {|DAL shard proof error: Invalid shard \(for commitment = ([\w\d]+)\).|} + +let dal_entrapment_of_not_published_commitment = + rex + {|Invalid accusation for delegate ([\w\d]+), level ([\d]+), and DAL slot index ([\d]+): the DAL slot was not published.|} diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 119375d2513a..246be36ab3e8 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -812,3 +812,14 @@ val outdated_dal_denunciation : rex Captures [hash]. *) val injection_error_unknown_branch : rex + +(** Matches the message + [DAL shard proof error: Invalid shard (for commitment = %a)] + from [src/proto_alpha/lib_protocol/dal_slot_repr.ml]. *) +val dal_entrapment_wrong_commitment : rex + +(** Matches the message + [Invalid accusation for delegate %a, level %d, and DAL slot index %d: the DAL slot was not published.] + from [src/proto_alpha/lib_protocol/validate_errors.ml] +*) +val dal_entrapment_of_not_published_commitment : rex -- GitLab From 4e7d7a3bfbd39b316662ecfb72a7204ccadfd2ad Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Thu, 9 Oct 2025 11:54:09 +0200 Subject: [PATCH 3/4] Tezt/DAL: Add a migration test for entrapment evidence when the attestation lag changed. --- tezt/tests/dal.ml | 235 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 4f1ea56e5cf0..a8864bd3af8d 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -4809,6 +4809,236 @@ let test_migration_with_attestation_lag_change ~migrate_from ~migrate_to = ~migrate_to () +let get_delegate node ~level ~tb_index = + let* json = + Node.RPC.call node @@ RPC.get_chain_block_helper_validators ~level () + in + let pkh = + Option.get + @@ List.find_map + (fun elem -> + if + List.mem + tb_index + JSON.(elem |-> "slots" |> as_list |> List.map as_int) + then Some JSON.(elem |-> "delegate" |> as_string) + else None) + (JSON.as_list json) + in + return + @@ List.find + (fun acc -> acc.Account.public_key_hash = pkh) + Constant.all_secret_keys + +(* A commitment is published in the "previous protocol" and attested in the + same protocol. Then, in the "new protocol" a trap denunciation is sent. + + This denunciation is valid only if the denounced trap refers to the + publication sent "previous attestation lag" before. + + Still in the "previous protocol", a baker DAL attests the publication slot + of all levels between the publication level and the migration level + (despite the trap fraction being 1). + + Since the migration level is expected to be after publication level + + previous attestation lag, the attestation is expected to be in the + "previous protocol". + + Then denunciations are sent in the "new protocol", both for the attestation + "new attestation lag" and "previous attestation lag" after the publication. + + We expect the one using the "new attestation lag" to be invalid and the + other one to be included. +*) +let test_accusation_migration_with_attestation_lag_decrease ~migrate_from + ~migrate_to = + let slot_index = 0 in + let tags = ["migration"; "dal"; "accusation"; "attestation_lag"] in + let description = + "test accusation during migration with reduction of the attestation_lag" + in + let scenario ~migration_level (dal_parameters : Dal.Parameters.t) client node + dal_node = + (* We do not use the DAL node in this test *) + let* () = Dal_node.terminate dal_node in + Log.info "Compute the attestation lag before and after migration" ; + (* [dal_parameters] should contain the parameters of the "previous" + protocol. *) + let old_lag = dal_parameters.attestation_lag in + let base = Either.right (migrate_to, None) in + let* new_proto_parameters = + generate_protocol_parameters base migrate_to [] + in + let new_dal_parameters = + Dal.Parameters.from_protocol_parameters new_proto_parameters + in + let new_lag = new_dal_parameters.attestation_lag in + Log.info "Initializing the cryptobox" ; + let* () = Helpers.init_prover ~__LOC__ () in + let* cryptobox = Helpers.make_cryptobox dal_parameters.cryptobox in + let slot_size = dal_parameters.cryptobox.slot_size in + (* A slot which will be published should be targeted by the denunciation. *) + let slot_with_trap = Helpers.(bytes_of_slot (make_slot ~slot_size "A")) in + let commitment_with_trap, proof_with_trap, shards_with_proofs_with_trap = + Helpers.get_commitment_and_shards_with_proofs + cryptobox + ~slot:slot_with_trap + in + let shard_index = 0 in + let shard_denounced, proof_denounced = + Seq.find + (fun (Cryptobox.{index; _}, _proof) -> index = shard_index) + shards_with_proofs_with_trap + |> Option.get + in + + let* level = Client.level client in + let publi_with_trap_level = level + 1 in + Log.info + "Computing the delegate which sends the attestation related to the shard \ + we want to denounce." ; + (* Given that there are 2 potential attestation lags, we compute the one + associated to both. *) + (* This computation relies on the fact the shard index is the same as the + index in the Tenderbake committee. *) + let* delegate_old = + get_delegate + node + ~level:(publi_with_trap_level + old_lag - 1) + ~tb_index:shard_index + in + let* delegate_new = + get_delegate + node + ~level:(publi_with_trap_level + new_lag - 1) + ~tb_index:shard_index + in + (* We want to have enough levels to attest in "previous" what has been + published at [publi_with_trap_level]. *) + assert (publi_with_trap_level + old_lag <= migration_level) ; + Log.info "Publishing the to-be-denounced slot." ; + let* _op_hash = + Helpers.publish_commitment + ~source:Constant.bootstrap1 + ~index:slot_index + ~commitment:commitment_with_trap + ~proof:proof_with_trap + client + in + let* () = bake_for client in + Log.info + "The to-be-denounced delegates attest at every level, until the \ + attestation of the denounced shard (included)." ; + let availability = Slots [slot_index] in + let craft_attestation delegate = + let* attestation, _op_hash = + inject_dal_attestation_exn + ~protocol:migrate_from + ~signer:delegate + ~nb_slots:dal_parameters.number_of_slots + availability + client + in + let* signature = Operation.sign attestation client in + return (attestation, signature) + in + let* () = bake_for ~count:(new_lag - 1) client in + Log.info "Crafting the attestation using the \"new\" attestation lag" ; + let* attestation_new = craft_attestation delegate_new in + let* () = bake_for ~count:(old_lag - new_lag) client in + Log.info "Crafting the attestation using the \"old\" attestation lag" ; + let* attestation_old = craft_attestation delegate_old in + Log.info "We bake until 2 levels after the migration." ; + let* () = + repeat + (migration_level - publi_with_trap_level - old_lag + 3) + (fun () -> bake_for client) + in + Log.info + "Craft an entrapment evidence which uses the \"new\" attestation lag" ; + let accusation = + Operation.Anonymous.dal_entrapment_evidence_standalone_attestation + ~protocol:migrate_to + ~attestation:attestation_new + ~slot_index + shard_denounced + proof_denounced + in + Log.info "Inject this accusation" ; + (* This accusation should be accepted only if the protocol migration does + not imply a variation of the attestation lag *) + if new_lag = old_lag then ( + let () = + Log.info + "Since attestation lag did not change, this accusation should be \ + injected and included without issue." + in + let* _op_hash = Operation.Anonymous.inject accusation client in + let* () = bake_for client in + let* ops = + Node.RPC.call node + @@ RPC.get_chain_block_operations_validation_pass + ~block:"head" + ~validation_pass:2 + () + in + Check.(List.length (JSON.as_list ops) = 1) + ~__LOC__ + Check.(int) + ~error_msg:"Expected exactly one anonymous op. Got: %L" ; + unit) + else + let () = + Log.info + "Since attestation lag changed, this accusation refer to the wrong \ + commitment, hence injection should fail." + in + let* _op_hash = + Operation.Anonymous.inject + ~error:Operation_core.dal_entrapment_of_not_published_commitment + accusation + client + in + let* () = bake_for client in + Log.info + "Craft an entrapment evidence which uses the \"old\" attestation lag" ; + let accusation = + Operation.Anonymous.dal_entrapment_evidence_standalone_attestation + ~protocol:migrate_to + ~attestation:attestation_old + ~slot_index + shard_denounced + proof_denounced + in + Log.info + "Injection of this new accusation should work fine and be included in \ + the next block" ; + let* _op_hash = Operation.Anonymous.inject accusation client in + let* () = bake_for client in + let* ops = + Node.RPC.call node + @@ RPC.get_chain_block_operations_validation_pass + ~block:"head" + ~validation_pass:2 + () + in + Check.(List.length (JSON.as_list ops) = 1) + ~__LOC__ + Check.(int) + ~error_msg:"Expected exactly one anonymous op. Got: %L" ; + unit + in + test_l1_migration_scenario + ~scenario + ~tags + ~description + ~activation_timestamp:Now + ~operator_profiles:[slot_index] + ~traps_fraction:Q.one + ~migration_level:10 + ~migrate_to + () + let test_operator_profile _protocol _dal_parameters _cryptobox _node _client dal_node = let index = 0 in @@ -12136,7 +12366,10 @@ let register_migration ~migrate_from ~migrate_to = ~migrate_to ; tests_start_dal_node_around_migration ~migrate_from ~migrate_to ; test_migration_accuser_issue ~migration_level:4 ~migrate_from ~migrate_to ; - test_migration_with_attestation_lag_change ~migrate_from ~migrate_to + test_migration_with_attestation_lag_change ~migrate_from ~migrate_to; + test_accusation_migration_with_attestation_lag_decrease + ~migrate_from + ~migrate_to let () = Regression.register -- GitLab From 2f5903ee7eaa08f89358deb6baedea4ca676d983 Mon Sep 17 00:00:00 2001 From: Guillaume Genestier Date: Mon, 20 Oct 2025 12:29:18 +0200 Subject: [PATCH 4/4] Tezt/DAL: Adapt to the T version of the validators RPC --- tezt/tests/dal.ml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index a8864bd3af8d..b3b6df1f185e 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -4809,10 +4809,14 @@ let test_migration_with_attestation_lag_change ~migrate_from ~migrate_to = ~migrate_to () -let get_delegate node ~level ~tb_index = +let get_delegate node ~migrate_from ~level ~tb_index = let* json = Node.RPC.call node @@ RPC.get_chain_block_helper_validators ~level () in + let indices_field_name, json = + if Protocol.number migrate_from <= 023 then ("slots", json) + else ("rounds", JSON.geti 0 json |> JSON.get "delegates") + in let pkh = Option.get @@ List.find_map @@ -4820,7 +4824,7 @@ let get_delegate node ~level ~tb_index = if List.mem tb_index - JSON.(elem |-> "slots" |> as_list |> List.map as_int) + JSON.(elem |-> indices_field_name |> as_list |> List.map as_int) then Some JSON.(elem |-> "delegate" |> as_string) else None) (JSON.as_list json) @@ -4904,12 +4908,14 @@ let test_accusation_migration_with_attestation_lag_decrease ~migrate_from let* delegate_old = get_delegate node + ~migrate_from ~level:(publi_with_trap_level + old_lag - 1) ~tb_index:shard_index in let* delegate_new = get_delegate node + ~migrate_from ~level:(publi_with_trap_level + new_lag - 1) ~tb_index:shard_index in @@ -4945,7 +4951,10 @@ let test_accusation_migration_with_attestation_lag_decrease ~migrate_from let* () = bake_for ~count:(new_lag - 1) client in Log.info "Crafting the attestation using the \"new\" attestation lag" ; let* attestation_new = craft_attestation delegate_new in - let* () = bake_for ~count:(old_lag - new_lag) client in + let* () = + if old_lag <> new_lag then bake_for ~count:(old_lag - new_lag) client + else unit + in Log.info "Crafting the attestation using the \"old\" attestation lag" ; let* attestation_old = craft_attestation delegate_old in Log.info "We bake until 2 levels after the migration." ; @@ -5036,6 +5045,7 @@ let test_accusation_migration_with_attestation_lag_decrease ~migrate_from ~operator_profiles:[slot_index] ~traps_fraction:Q.one ~migration_level:10 + ~migrate_from ~migrate_to () @@ -12366,7 +12376,7 @@ let register_migration ~migrate_from ~migrate_to = ~migrate_to ; tests_start_dal_node_around_migration ~migrate_from ~migrate_to ; test_migration_accuser_issue ~migration_level:4 ~migrate_from ~migrate_to ; - test_migration_with_attestation_lag_change ~migrate_from ~migrate_to; + test_migration_with_attestation_lag_change ~migrate_from ~migrate_to ; test_accusation_migration_with_attestation_lag_decrease ~migrate_from ~migrate_to -- GitLab