From 437e4f1dce8d58cd7be815033af928ad5d0d1e5a Mon Sep 17 00:00:00 2001 From: phink Date: Fri, 24 Jan 2025 15:29:11 +0100 Subject: [PATCH 1/4] Tezt: add rex for DAL operation errors --- tezt/lib_tezos/operation_core.ml | 8 ++++++++ tezt/lib_tezos/operation_core.mli | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index 4dd040715e60..95172391ae52 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -920,9 +920,17 @@ let already_denounced = rex {|Delegate ([\w\d]+) at level ([\d]+) has already been denounced for a double ([\w]+).|} +let already_dal_denounced = + rex + {|Delegate ([\w\d]+) at level ([\d]+) has already been denounced for a DAL entrapment.|} + let outdated_denunciation = rex {|A double-([\w]+) denunciation is outdated \(last acceptable cycle: ([\d]+), given level: ([\d]+)\).|} +let outdated_dal_denunciation = + rex + {|A DAL entrapment denunciation is outdated \(last acceptable cycle: ([\d]+), given level: ([\d]+)\).|} + let injection_error_unknown_branch = rex {|Operation ([\w\d]+) is branched on either:|} diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 5c66f8c7a630..9938c6be508e 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -714,12 +714,26 @@ val inject_error_check_recommended_fee : val already_denounced : rex (** Matches the message produced by - [Outdated_denunciation {kind; level; last_cycle}] + [Dal_already_denounced {delegate; level}] + from [src/proto_xxx/lib_protocol/validate_errors]. + + Captures [delegate], [level]. *) +val already_dal_denounced : rex + +(** Matches the message produced by + [Outdated_dal_denunciation {kind; level; last_cycle}] from [src/proto_xxx/lib_protocol/validate_errors]. Captures [kind], [last_cycle], [level]. *) val outdated_denunciation : rex +(** Matches the message produced by + [Outdated_dal_denunciation {level; last_cycle}] + from [src/proto_xxx/lib_protocol/validate_errors]. + + Captures [last_cycle], [level]. *) +val outdated_dal_denunciation : rex + (** Matches the message [Operation %a is branched on either:] from [src/lib_shell/prevalidator.ml]. -- GitLab From 8dc3692ad27f286cbc64b76e1bdabcc5de994d0a Mon Sep 17 00:00:00 2001 From: phink Date: Mon, 27 Jan 2025 18:05:23 +0100 Subject: [PATCH 2/4] Tezt/DAL: move up TODO --- tezt/tests/dal.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 4a516a8a45a1..b1a6166b3c6a 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -8314,6 +8314,12 @@ let test_attesters_receive_dal_rewards protocol dal_parameters _cryptobox node ~error_msg:"Unexpected delegate to lose DAL rewards (got %R expected %L)" ; unit +(* TODO: https://gitlab.com/tezos/tezos/-/issues/7686 + In the following accusation tests, we bake two blocks because we + need the accusation to be introduced at level at least 10 (2 = 10 - + attestation_lag). In protocol S we will not need this + restriction. *) + let test_inject_accusation _protocol dal_parameters cryptobox node client _bootstrap_key = let slot_index = 0 in @@ -8325,10 +8331,6 @@ let test_inject_accusation _protocol dal_parameters cryptobox node client Helpers.get_commitment_and_shards_with_proofs cryptobox ~slot in Log.info "Bake two blocks" ; - (* TODO: https://gitlab.com/tezos/tezos/-/issues/7686 - We bake two blocks because we need the accusation to be introduced at level - at least 10 (2 = 10 - attestation_lag). In protocol S we will not need this - restriction. *) let* () = bake_for ~count:2 client in let* _op_hash = Helpers.publish_commitment -- GitLab From 45ac61ece3b3f5c482beed222933c0f25e6dd20e Mon Sep 17 00:00:00 2001 From: phink Date: Mon, 27 Jan 2025 18:05:53 +0100 Subject: [PATCH 3/4] Tezt/DAL: test_duplicate_denunciations --- tezt/tests/dal.ml | 144 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index b1a6166b3c6a..dfb497bd8190 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -8637,6 +8637,143 @@ let test_attester_did_not_attest_slot (_protocol : Protocol.t) not_attested_by_bootstrap2 ; unit +(* [test_duplicate_denunciations] publishes a commitment for slot [0]. + The test then injects a DAL trapped attestation at level [2] and + after baking [attestation_lag] blocks, it attempts to: + + - inject a first denunciation that is expected to succeed, + + - inject a second denunciation built using a different shard that + is a trap as well, but using the same slot and the same l1 level, + where each try is expected to fail. The injection temptatives are + done: + 1. at the next block level (expecting delegate already denounced) + 2. at the next cycle (expecting delegate already denounced) + 3. at the next-next cycle (expecting outdated evidence) +*) +let test_duplicate_denunciations _protocol dal_parameters cryptobox node client + _bootstrap_key = + let slot_index = 0 in + Log.info "Bake two blocks" ; + let* () = bake_for ~count:2 client in + let accused = Constant.bootstrap2 in + let* current_level = Node.get_level node in + let* shards_with_proofs = + let slot_size = dal_parameters.Dal.Parameters.cryptobox.slot_size in + let slot = Helpers.generate_slot ~slot_size in + let commitment, proof, shards_with_proofs = + Helpers.get_commitment_and_shards_with_proofs cryptobox ~slot + in + Log.info "Publish commitment at level %d" current_level ; + let* _op_hash_0 = + Helpers.publish_commitment + ~source:accused + ~index:slot_index + ~commitment + ~proof + client + in + return shards_with_proofs + in + let* () = bake_for ~count:dal_parameters.attestation_lag client in + let* current_level = Node.get_level node in + Log.info "Injecting an attestation at level %d" current_level ; + let availability = Slots [slot_index] in + let signer = Constant.bootstrap2 in + let* attestation, _op_hash = + inject_dal_attestation + ~signer + ~nb_slots:dal_parameters.number_of_slots + availability + client + in + let* signature = Operation.sign attestation client in + let attestation = (attestation, signature) in + let* shard_assignments = + Node.RPC.call node + @@ RPC.get_chain_block_context_dal_shards + ~delegates:[accused.public_key_hash] + () + in + let shard_indexes = + JSON.(shard_assignments |> as_list |> List.hd |-> "indexes" |> as_list) + in + let[@ocaml.warning "-8"] (shard_index_1 :: shard_index_2 :: _) = + List.map JSON.as_int shard_indexes + in + let accusation = + let shard1, proof1 = + Seq.find + (fun (Cryptobox.{index; _}, _proof) -> index = shard_index_1) + shards_with_proofs + |> Option.get + in + Operation.Anonymous.dal_entrapment_evidence + ~attestation + ~slot_index + shard1 + proof1 + in + let accusation_dup = + let shard2, proof2 = + Seq.find + (fun (Cryptobox.{index; _}, _proof) -> index = shard_index_2) + shards_with_proofs + |> Option.get + in + Operation.Anonymous.dal_entrapment_evidence + ~attestation + ~slot_index + shard2 + proof2 + in + let* level_accusation = Node.get_level node in + Log.info + "Injecting a first accusation (level %d) expected to succeed" + level_accusation ; + let* (`OpHash _) = Operation.Anonymous.inject accusation client in + let* () = bake_for client in + let* current_level = Node.get_level node in + Log.info + "Injecting the second accusation at the next level (level %d), expected to \ + fail because the faulty delegate is already denounced" + current_level ; + let* (`OpHash _) = + Operation.Anonymous.inject + accusation_dup + client + ~error:Operation_core.already_dal_denounced + in + let* proto_params = + Node.RPC.call node @@ RPC.get_chain_block_context_constants () + in + let blocks_per_cycle = JSON.(proto_params |-> "blocks_per_cycle" |> as_int) in + let* () = bake_for ~count:blocks_per_cycle client in + let* current_level = Node.get_level node in + Log.info + "Injecting at the next cycle (level %d), expected to fail because the \ + delegate is already denounced for this level" + current_level ; + let* (`OpHash _) = + Operation.Anonymous.inject + accusation_dup + client + ~error:Operation_core.already_dal_denounced + in + let* () = bake_for ~count:blocks_per_cycle client in + let* current_level = Node.get_level node in + Log.info + "Injecting at the next cycle (level %d), expected to fail because the DAL \ + denunciation is outdated" + current_level ; + let* (`OpHash _) = + Operation.Anonymous.inject + accusation_dup + client + ~error:Operation_core.outdated_dal_denunciation + in + unit + let register ~protocols = (* Tests with Layer1 node only *) scenario_with_layer1_node @@ -8679,7 +8816,12 @@ let register ~protocols = "inject accusation" test_inject_accusation (List.filter (fun p -> Protocol.number p >= 022) protocols) ; - + scenario_with_layer1_node + ~incentives_enable:true + ~traps_fraction:Q.one + "inject a duplicated denunciation at different steps" + test_duplicate_denunciations + (List.filter (fun p -> Protocol.number p >= 022) protocols) ; (* Tests with layer1 and dal nodes *) test_dal_node_startup protocols ; scenario_with_layer1_and_dal_nodes -- GitLab From e7cc7e2116c365cd6d986b7ae87caaad49147611 Mon Sep 17 00:00:00 2001 From: phink Date: Mon, 27 Jan 2025 18:06:11 +0100 Subject: [PATCH 4/4] Tezt/DAL: test_denunciation_next_cycle --- tezt/tests/dal.ml | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index dfb497bd8190..3e65a29f3c60 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -8774,6 +8774,156 @@ let test_duplicate_denunciations _protocol dal_parameters cryptobox node client in unit +(* [test_denunciation_next_cycle] injects two trapped attestations and + inject an accusation for the first one at cycle `c` and another at + for the second one at cycle `c + 1`, both injections are expected + to succeed. *) +let test_denunciation_next_cycle _protocol dal_parameters cryptobox node client + _bootstrap_key = + let* proto_params = + Node.RPC.call node @@ RPC.get_chain_block_context_constants () + in + let slot_index = 0 in + let accused = Constant.bootstrap2 in + let blocks_per_cycle = JSON.(proto_params |-> "blocks_per_cycle" |> as_int) in + let* () = bake_for ~count:2 client in + let* shards_with_proofs1 = + let* current_level = Node.get_level node in + Log.info "Publish first commitment at level %d" current_level ; + let slot_size = dal_parameters.Dal.Parameters.cryptobox.slot_size in + let slot = Helpers.generate_slot ~slot_size in + let commitment, proof, shards_with_proofs = + Helpers.get_commitment_and_shards_with_proofs cryptobox ~slot + in + let* _op_hash_0 = + Helpers.publish_commitment + ~source:accused + ~index:slot_index + ~commitment + ~proof + client + in + return shards_with_proofs + in + let* () = bake_for ~count:1 client in + let* shards_with_proofs2 = + let* current_level = Node.get_level node in + Log.info "Publish second commitment at level %d" current_level ; + let slot_size = dal_parameters.Dal.Parameters.cryptobox.slot_size in + let slot = Helpers.generate_slot ~slot_size in + let commitment, proof, shards_with_proofs = + Helpers.get_commitment_and_shards_with_proofs cryptobox ~slot + in + let* _op_hash_0 = + Helpers.publish_commitment + ~source:accused + ~index:slot_index + ~commitment + ~proof + client + in + return shards_with_proofs + in + let* () = bake_for ~count:(dal_parameters.attestation_lag - 1) client in + let* attestation = + let* current_level = Node.get_level node in + Log.info "Injecting a first attestation at level %d" current_level ; + let availability = Slots [slot_index] in + let* attestation, _op_hash = + inject_dal_attestation + ~signer:Constant.bootstrap2 + ~nb_slots:dal_parameters.number_of_slots + availability + client + in + let* signature = Operation.sign attestation client in + return (attestation, signature) + in + let* () = + let* shard_assignments = + Node.RPC.call node + @@ RPC.get_chain_block_context_dal_shards + ~delegates:[accused.public_key_hash] + () + in + let shard_indexes = + JSON.(shard_assignments |> as_list |> List.hd |-> "indexes" |> as_list) + in + let[@ocaml.warning "-8"] (shard_index :: _) = + List.map JSON.as_int shard_indexes + in + let shard, proof = + Seq.find + (fun (Cryptobox.{index; _}, _proof) -> index = shard_index) + shards_with_proofs1 + |> Option.get + in + let accusation = + Operation.Anonymous.dal_entrapment_evidence + ~attestation + ~slot_index + shard + proof + in + let* level_accusation = Node.get_level node in + Log.info + "Injecting a first accusation (level %d) expected to succeed" + level_accusation ; + let* (`OpHash _) = Operation.Anonymous.inject accusation client in + unit + in + let* () = bake_for ~count:1 client in + let* attestation = + let* current_level = Node.get_level node in + Log.info "Injecting a second attestation at level %d" current_level ; + let availability = Slots [slot_index] in + let signer = Constant.bootstrap2 in + let* attestation, _op_hash = + inject_dal_attestation + ~signer + ~nb_slots:dal_parameters.number_of_slots + availability + client + in + let* signature = Operation.sign attestation client in + return (attestation, signature) + in + let* () = + let* shard_assignments = + Node.RPC.call node + @@ RPC.get_chain_block_context_dal_shards + ~delegates:[accused.public_key_hash] + () + in + let shard_indexes = + JSON.(shard_assignments |> as_list |> List.hd |-> "indexes" |> as_list) + in + let[@ocaml.warning "-8"] (shard_index :: _) = + List.map JSON.as_int shard_indexes + in + let shard, proof = + Seq.find + (fun (Cryptobox.{index; _}, _proof) -> index = shard_index) + shards_with_proofs2 + |> Option.get + in + let accusation = + Operation.Anonymous.dal_entrapment_evidence + ~attestation + ~slot_index + shard + proof + in + let* () = bake_for ~count:blocks_per_cycle client in + let* level = Node.get_level node in + Log.info + "Injecting the second accusation (level %d) expected to succeed" + level ; + let* (`OpHash _) = Operation.Anonymous.inject accusation client in + unit + in + unit + let register ~protocols = (* Tests with Layer1 node only *) scenario_with_layer1_node @@ -8822,6 +8972,12 @@ let register ~protocols = "inject a duplicated denunciation at different steps" test_duplicate_denunciations (List.filter (fun p -> Protocol.number p >= 022) protocols) ; + scenario_with_layer1_node + ~incentives_enable:true + ~traps_fraction:Q.one + "inject a denunciation at the next cycle" + test_denunciation_next_cycle + (List.filter (fun p -> Protocol.number p >= 022) protocols) ; (* Tests with layer1 and dal nodes *) test_dal_node_startup protocols ; scenario_with_layer1_and_dal_nodes -- GitLab