From 1f54b62af1da060bcbcaa271829d43980951c15d Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Tue, 29 Apr 2025 13:20:40 +0200 Subject: [PATCH 1/4] proto/validate: add a field in the validation state Adding the field `preattestations_aggregate_seen` to track preattestations_aggregate conflicts during validation. --- src/proto_alpha/lib_protocol/validate.ml | 25 +++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 039de91f22de..68621e26be46 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -73,6 +73,7 @@ type consensus_state = { preattestations_seen : Operation_hash.t Consensus_conflict_map.t; attestations_seen : Operation_hash.t Consensus_conflict_map.t; attestations_aggregate_seen : Operation_hash.t option; + preattestations_aggregate_seen : Operation_hash.t option; } let consensus_conflict_map_encoding = @@ -95,22 +96,36 @@ let consensus_state_encoding = preattestations_seen; attestations_seen; attestations_aggregate_seen; + preattestations_aggregate_seen; } -> - (preattestations_seen, attestations_seen, attestations_aggregate_seen)) + ( preattestations_seen, + attestations_seen, + attestations_aggregate_seen, + preattestations_aggregate_seen )) (fun ( preattestations_seen, attestations_seen, - attestations_aggregate_seen ) -> - {preattestations_seen; attestations_seen; attestations_aggregate_seen}) - (obj3 + attestations_aggregate_seen, + preattestations_aggregate_seen ) -> + { + preattestations_seen; + attestations_seen; + attestations_aggregate_seen; + preattestations_aggregate_seen; + }) + (obj4 (req "preattestations_seen" consensus_conflict_map_encoding) (req "attestations_seen" consensus_conflict_map_encoding) - (req "attestations_aggregate_seen" (option Operation_hash.encoding))) + (req "attestations_aggregate_seen" (option Operation_hash.encoding)) + (req + "preattestations_aggregate_seen" + (option Operation_hash.encoding))) let empty_consensus_state = { preattestations_seen = Consensus_conflict_map.empty; attestations_seen = Consensus_conflict_map.empty; attestations_aggregate_seen = None; + preattestations_aggregate_seen = None; } type voting_state = { -- GitLab From 2d6e35c626e347e28e28dd84f8a41304245eb1a1 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Tue, 29 Apr 2025 13:23:02 +0200 Subject: [PATCH 2/4] proto/validate_errors: update consensus conflicts errors Update consensus conflicts errors to handle preattestations_aggregates. --- src/proto_alpha/lib_protocol/validate_errors.ml | 4 ++++ src/proto_alpha/lib_protocol/validate_errors.mli | 1 + 2 files changed, 5 insertions(+) diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index e3923e0f6132..cd09b6087ee9 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -74,6 +74,7 @@ module Consensus = struct | Preattestation | Attestation | Attestations_aggregate + | Preattestations_aggregate let consensus_operation_kind_encoding = Data_encoding.string_enum @@ -81,12 +82,15 @@ module Consensus = struct ("Preattestation", Preattestation); ("Attestation", Attestation); ("Attestations_aggregate", Attestations_aggregate); + ("Preattestations_aggregate", Preattestations_aggregate); ] let consensus_operation_kind_pp fmt = function | Preattestation -> Format.fprintf fmt "Preattestation" | Attestation -> Format.fprintf fmt "Attestation" | Attestations_aggregate -> Format.fprintf fmt "Attestations_aggregate" + | Preattestations_aggregate -> + Format.fprintf fmt "Preattestations_aggregate" (** Errors for preattestation and attestation. *) type error += diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index c95b59efb28d..32dd91de35ac 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -39,6 +39,7 @@ module Consensus : sig | Preattestation | Attestation | Attestations_aggregate + | Preattestations_aggregate (** Errors for preattestations and attestations. *) type error += -- GitLab From 4389a50971262876925b15b6ef4a01739bd8b31e Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Tue, 29 Apr 2025 13:29:58 +0200 Subject: [PATCH 3/4] proto/validate: handle preattestations_aggregate conflicts Ensures that a valid block contains at most one `Preattestations_aggregate` operation. --- src/proto_alpha/lib_protocol/validate.ml | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 68621e26be46..9194f9e5c23c 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -1182,11 +1182,43 @@ module Consensus = struct in return {info; operation_state; block_state} + let check_preattestations_aggregate_conflict vs oph = + match vs.consensus_state.preattestations_aggregate_seen with + | None -> ok_unit + | Some existing -> + Error (Operation_conflict {existing; new_operation = oph}) + + let wrap_preattestations_aggregate_conflict = function + | Ok () -> ok_unit + | Error conflict -> + result_error + Validate_errors.Consensus.( + Conflicting_consensus_operation + {kind = Preattestations_aggregate; conflict}) + + let add_preattestations_aggregate operation_state oph = + { + operation_state with + consensus_state = + { + operation_state.consensus_state with + preattestations_aggregate_seen = Some oph; + }; + } + let handle_preattestations_aggregate_conflicts {info; operation_state; block_state} oph ({shell; protocol_data = {contents = Single content; _}} : Kind.preattestations_aggregate operation) = let open Lwt_result_syntax in + (* Check that no other Preattestations_aggregate operation was previously + recorded in the operation state *) + let*? () = + check_preattestations_aggregate_conflict operation_state oph + |> wrap_preattestations_aggregate_conflict + in + (* Record the aggregate in the operation state *) + let operation_state = add_preattestations_aggregate operation_state oph in (* Check for preattestations conflicts and register each operation in the operation state *) let (Preattestations_aggregate {consensus_content; committee}) = content in -- GitLab From 224829d822194a5c7f5c81ee35aecac1941c7098 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Tue, 29 Apr 2025 13:43:37 +0200 Subject: [PATCH 4/4] proto/test_aggregate: add test for preattestations_aggregate conflicts --- .../integration/consensus/test_aggregate.ml | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) 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 dca10743eded..4cced942f7f8 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 @@ -56,8 +56,10 @@ let non_bls_in_aggregate = function | Validate_errors.Consensus.Non_bls_key_in_aggregate -> true | _ -> false -let conflicting_consensus_operation = function - | Validate_errors.Consensus.Conflicting_consensus_operation _ -> true +let conflicting_consensus_operation ?kind = function + | Validate_errors.Consensus.Conflicting_consensus_operation {kind = kind'; _} + -> + Option.fold ~none:true ~some:(fun kind -> kind = kind') kind | _ -> false let unaggregated_eligible_attestation = function @@ -328,7 +330,7 @@ let test_multiple_aggregations_per_block_forbidden () = let* attesters = Context.get_attesters (B block) in (* Filter delegates with BLS keys that have at least one slot *) let bls_delegates_with_slots = filter_attesters_with_bls_key attesters in - (* Craft one aggregate per attester *) + (* Craft one attestations_aggregate per attester *) let* aggregates = List.map_es (fun ((delegate : RPC.Validators.t), _) -> @@ -337,7 +339,36 @@ let test_multiple_aggregations_per_block_forbidden () = in (* Bake a block containing the multiple aggregates and expect an error *) let*! res = Block.bake ~operations:aggregates block in - Assert.proto_error ~loc:__LOC__ res conflicting_consensus_operation + let* () = + Assert.proto_error + ~loc:__LOC__ + res + (conflicting_consensus_operation + ~kind:Validate_errors.Consensus.Attestations_aggregate) + in + (* Craft one preattestations_aggregate per attester *) + let* block' = Block.bake block in + let* aggregates = + List.map_es + (fun ((delegate : RPC.Validators.t), _) -> + Op.preattestations_aggregate ~committee:[delegate.consensus_key] block') + bls_delegates_with_slots + in + (* Bake a block containing the multiple aggregates and expect an error *) + let round_zero = Alpha_context.Round.zero in + let*! res = + Block.bake + ~policy:(By_round 1) + ~payload_round:(Some round_zero) + ~locked_round:(Some round_zero) + ~operations:aggregates + block + in + Assert.proto_error + ~loc:__LOC__ + res + (conflicting_consensus_operation + ~kind:Validate_errors.Consensus.Preattestations_aggregate) let eligible_attestation_must_be_aggregated () = let open Lwt_result_syntax in -- GitLab