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 dca10743eded83929f1db7da96395bdc5ec4cf91..4cced942f7f8cfc922fd22de512c48dd6b38f950 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 diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 039de91f22dee0b645f6f0d0ef7df0dee0580350..9194f9e5c23c6b67bbd7ae8353a96cf6336ab6ce 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 = { @@ -1167,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 diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index e3923e0f6132b5eb7110ba4bf9fb7174aadbdc98..cd09b6087ee975ae87deaa47db598acff8ca1ce1 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 c95b59efb28de137c06f2c290e1de6713569c988..32dd91de35ac7af6bd07a738a29ffa8e92bf4ee0 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 +=