From 2d0bd7d88ab556e3781b97ac283b3c0b9834df07 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Wed, 26 Mar 2025 18:24:33 +0100 Subject: [PATCH 1/3] proto: add an error for unaggregated eligible operations --- .../lib_protocol/validate_errors.ml | 18 +++++++++++++++++- .../lib_protocol/validate_errors.mli | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 0e3a0aea0a73..7e77d5e21ae1 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -134,6 +134,7 @@ module Consensus = struct | Aggregate_not_implemented | Non_bls_key_in_aggregate | Public_key_aggregation_failure + | Unaggregated_eligible_attestation of Operation_hash.t let () = register_error_kind @@ -413,7 +414,22 @@ module Consensus = struct ~pp:(fun ppf () -> Format.fprintf ppf "Public key aggregation failed") Data_encoding.empty (function Public_key_aggregation_failure -> Some () | _ -> None) - (fun () -> Public_key_aggregation_failure) + (fun () -> Public_key_aggregation_failure) ; + register_error_kind + `Permanent + ~id:"validate.unaggregated_eligible_attestation" + ~title:"Unaggregated eligible attestation" + ~description:"An eligible attestation was found unaggregated" + ~pp:(fun ppf hash -> + Format.fprintf + ppf + "Attestation %a should have been aggregated." + Operation_hash.pp + hash) + Data_encoding.(obj1 (req "hash" Operation_hash.encoding)) + (function + | Unaggregated_eligible_attestation hash -> Some hash | _ -> None) + (fun hash -> Unaggregated_eligible_attestation hash) end module Voting = struct diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 2345124329c1..1e48e2616328 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -87,6 +87,7 @@ module Consensus : sig | Aggregate_not_implemented | Non_bls_key_in_aggregate | Public_key_aggregation_failure + | Unaggregated_eligible_attestation of Operation_hash.t end (** Errors that may arise while validating a voting operation. *) -- GitLab From cbe3e67324d51731dd4d19820ccc3f3a3ab592f4 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Wed, 26 Mar 2025 18:25:03 +0100 Subject: [PATCH 2/3] proto: unaggregated eligible attestations are invalid in blocks --- src/proto_alpha/lib_protocol/validate.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 336e28593a6b..8259229e5507 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -787,7 +787,7 @@ module Consensus = struct in let* consensus_key, voting_power = match vi.mode with - | Application _ | Partial_validation _ | Construction _ -> + | Application _ | Partial_validation _ | Construction _ -> ( let* () = check_block_attestation vi consensus_info consensus_content in @@ -797,7 +797,14 @@ module Consensus = struct Attestation consensus_content.slot in - return (consensus_key, voting_power) + match (consensus_key.consensus_pk, dal_content) with + | Bls _, None when Constants.aggregate_attestation vi.ctxt -> + (* An attestation signed by a tz4 without DAL content must be + included in an Attestations_aggregate, even when it is the only + one in the quorum. *) + let hash = Operation.hash operation in + tzfail (Unaggregated_eligible_attestation hash) + | _ -> return (consensus_key, voting_power)) | Mempool -> check_mempool_consensus vi -- GitLab From cc53b9306d4d63ffaef9f92610ab934a73117f4d Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Wed, 26 Mar 2025 20:43:37 +0100 Subject: [PATCH 3/3] proto-unit-tests: add test for unaggregated eligible attestation --- .../integration/consensus/test_aggregate.ml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml index f3663cebab3e..cd2fbffd88a7 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml @@ -60,6 +60,10 @@ let conflicting_consensus_operation = function | Validate_errors.Consensus.Conflicting_consensus_operation _ -> true | _ -> false +let unaggregated_eligible_attestation = function + | Validate_errors.Consensus.Unaggregated_eligible_attestation _ -> true + | _ -> false + let find_aggregate_result receipt = let result_opt = List.find_map @@ -327,6 +331,25 @@ let test_multiple_aggregations_per_block_forbidden () = let*! res = Block.bake ~operations:aggregates block in Assert.proto_error ~loc:__LOC__ res conflicting_consensus_operation +let eligible_attestation_must_be_aggregated () = + let open Lwt_result_syntax in + let* _genesis, block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + let* attesters = Context.get_attesters (B block) in + let attester = find_attester_with_bls_key attesters in + match attester with + | Some (attester, _) -> + let* operation = Op.attestation ~delegate:attester.consensus_key block in + (* Operation is valid in the Mempool *) + let* inc = Incremental.begin_construction ~mempool_mode:true block in + let* inc = Incremental.add_operation inc operation in + let* _ = Incremental.finalize_block inc in + (* Operation is invalid in a block *) + let*! res = Block.bake ~operation block in + Assert.proto_error ~loc:__LOC__ res unaggregated_eligible_attestation + | _ -> assert false + let tests = [ Tztest.tztest @@ -357,6 +380,10 @@ let tests = "test_multiple_aggregations_per_block_forbidden" `Quick test_multiple_aggregations_per_block_forbidden; + Tztest.tztest + "eligible_attestation_must_be_aggregated" + `Quick + eligible_attestation_must_be_aggregated; ] let () = -- GitLab