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 f3663cebab3ebf0f5ba0547a14d10f361245424d..cd2fbffd88a788452616394a416424ba7c7f3b75 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 () = diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 336e28593a6b77207d3dea4b9acd20cde577c3e4..8259229e5507a45acea29fdb87032f5e6fc9778d 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 diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 0e3a0aea0a7329a8b3c832941ac7e0c61d6209e3..7e77d5e21ae102c44b6e89ebb1f27b09928a5d7c 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 2345124329c1d91eccea8e8f3199b1e4bc7e20be..1e48e261632865a7c3415816c33d0c208a4b6ac9 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. *)