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..fc1bd0542af29cf4940da2c814db343b238cfca5 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 @@ -68,7 +68,24 @@ let empty_aggregation_committee = function | Validate_errors.Consensus.Empty_aggregation_committee -> true | _ -> false -let find_aggregate_result receipt = +let find_preattestations_aggregate_result receipt = + let result_opt = + List.find_map + (function + | Tezos_protocol_alpha__Protocol.Apply_results.Operation_metadata + { + contents = + Single_result (Preattestations_aggregate_result _ as result); + } -> + Some result + | _ -> None) + receipt + in + match result_opt with + | Some res -> res + | None -> Test.fail "No preattestations aggregate result found" + +let find_attestations_aggregate_result receipt = let result_opt = List.find_map (function @@ -83,21 +100,23 @@ let find_aggregate_result receipt = in match result_opt with | Some res -> res - | None -> Test.fail "No aggregate result found" + | None -> Test.fail "No attestations aggregate result found" -(* [check_attestations_aggregate_result ~committee result] verifies that - [result] has the following properties: - - [balance_update] is empty; - - [voting_power] equals the sum of slots owned by attesters in [committee]; - - the public key hashes in [result] committee match those of [committee]. *) -let check_attestations_aggregate_result ~committee - (result : - Alpha_context.Kind.attestations_aggregate - Tezos_protocol_alpha__Protocol.Apply_results.contents_result) = +type 'kind aggregate = + | Preattestation : Alpha_context.Kind.preattestations_aggregate aggregate + | Attestation : Alpha_context.Kind.attestations_aggregate aggregate + +let check_aggregate_result (type kind) (kind : kind aggregate) ~committee + (result : kind Tezos_protocol_alpha__Protocol.Apply_results.contents_result) + = let open Lwt_result_syntax in - match result with - | Attestations_aggregate_result - {balance_updates; committee = resulting_committee; consensus_power} -> + match (kind, result) with + | ( Preattestation, + Preattestations_aggregate_result + {balance_updates; committee = resulting_committee; consensus_power} ) + | ( Attestation, + Attestations_aggregate_result + {balance_updates; committee = resulting_committee; consensus_power} ) -> (* Check balance updates *) let* () = match balance_updates with @@ -151,6 +170,28 @@ let check_attestations_aggregate_result ~committee pp resulting_committee_pkhs +(* [check_preattestations_aggregate_result ~committee result] verifies that + [result] has the following properties: + - [balance_update] is empty; + - [voting_power] equals the sum of slots owned by attesters in [committee]; + - the public key hashes in [result] committee match those of [committee]. *) +let check_preattestations_aggregate_result ~committee + (result : + Alpha_context.Kind.preattestations_aggregate + Tezos_protocol_alpha__Protocol.Apply_results.contents_result) = + check_aggregate_result Preattestation ~committee result + +(* [check_attestations_aggregate_result ~committee result] verifies that + [result] has the following properties: + - [balance_update] is empty; + - [voting_power] equals the sum of slots owned by attesters in [committee]; + - the public key hashes in [result] committee match those of [committee]. *) +let check_attestations_aggregate_result ~committee + (result : + Alpha_context.Kind.attestations_aggregate + Tezos_protocol_alpha__Protocol.Apply_results.contents_result) = + check_aggregate_result Attestation ~committee result + (* [find_attester_with_bls_key attesters] returns the first attester with a BLS key, if any. *) let find_attester_with_bls_key = @@ -198,7 +239,7 @@ let test_aggregate_feature_flag_disabled () = ~mempool_error:aggregate_in_mempool_error Aggregate -let test_aggregate_attestation_with_a_single_bls_attestation () = +let test_attestations_aggregate_with_a_single_delegate () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -217,10 +258,46 @@ let test_aggregate_attestation_with_a_single_bls_attestation () = WithExceptions.Option.get ~loc:__LOC__ (Op.aggregate [attestation]) in let* _, (_, receipt) = Block.bake_with_metadata ~operation block in - let result = find_aggregate_result receipt in + let result = find_attestations_aggregate_result receipt in check_attestations_aggregate_result ~committee:[attester] result -let test_aggregate_attestation_with_multiple_bls_attestations () = +let test_preattestations_aggregate_with_a_single_delegate () = + let open Lwt_result_syntax in + let* _genesis, block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + let* block' = Block.bake block in + let* attesters = Context.get_attesters (B block') in + (* Find an attester with a BLS consensus key. *) + let attester, slot = + WithExceptions.Option.get + ~loc:__LOC__ + (find_attester_with_bls_key attesters) + in + let* operation = + let* preattestation = + Op.raw_preattestation + ~delegate:attester.RPC.Validators.delegate + ~slot + block' + in + return + @@ WithExceptions.Option.get ~loc:__LOC__ + @@ Op.aggregate_preattestations [preattestation] + in + let* _, (_, receipt) = + let round_zero = Alpha_context.Round.zero in + Block.bake_with_metadata + ~policy:(By_round 1) + ~payload_round:(Some round_zero) + ~locked_round:(Some round_zero) + ~operation + block + in + let result = find_preattestations_aggregate_result receipt in + check_preattestations_aggregate_result ~committee:[attester] result + +let test_attestations_aggregate_with_multiple_delegates () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -243,11 +320,47 @@ let test_aggregate_attestation_with_multiple_bls_attestations () = let* _, (_, receipt) = Block.bake_with_metadata ~operation:aggregation block in - let result = find_aggregate_result receipt in + let result = find_attestations_aggregate_result receipt in let delegates = List.map fst bls_delegates_with_slots in check_attestations_aggregate_result ~committee:delegates result -let test_aggregate_attestation_invalid_signature () = +let test_preattestations_aggregate_with_multiple_delegates () = + let open Lwt_result_syntax in + let* _genesis, block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + let* block' = Block.bake block in + 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 + let* preattestations = + List.map_es + (fun (delegate, slot) -> + Op.raw_preattestation + ~delegate:delegate.RPC.Validators.delegate + ~slot + block') + bls_delegates_with_slots + in + let operation = + WithExceptions.Option.get + ~loc:__LOC__ + (Op.aggregate_preattestations preattestations) + in + let* _, (_, receipt) = + let round_zero = Alpha_context.Round.zero in + Block.bake_with_metadata + ~policy:(By_round 1) + ~payload_round:(Some round_zero) + ~locked_round:(Some round_zero) + ~operation + block + in + let result = find_preattestations_aggregate_result receipt in + let delegates = List.map fst bls_delegates_with_slots in + check_preattestations_aggregate_result ~committee:delegates result + +let test_attestations_aggregate_invalid_signature () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -279,7 +392,96 @@ let test_aggregate_attestation_invalid_signature () = in Assert.proto_error ~loc:__LOC__ res signature_invalid_error -let test_aggregate_attestation_non_bls_delegate () = +let test_preattestations_aggregate_invalid_signature () = + let open Lwt_result_syntax in + let* _genesis, block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + let* block' = Block.bake block in + let* attesters = Context.get_attesters (B block) in + (* Find an attester with a BLS consensus key. *) + let attester, _ = + WithExceptions.Option.get + ~loc:__LOC__ + (find_attester_with_bls_key attesters) + in + (* Craft a preattestations_aggregate with this delegate *) + let* aggregate = + Op.preattestations_aggregate ~committee:[attester.consensus_key] block' + in + (* Swap the aggregate signature for Signature.Bls.zero *) + match aggregate.protocol_data with + | Operation_data {contents; _} -> + let aggregate_with_incorrect_signature = + { + aggregate with + protocol_data = + Operation_data {contents; signature = Some (Bls Signature.Bls.zero)}; + } + in + (* Bake a block containing this operation and expect an error *) + let*! res = + let round_zero = Alpha_context.Round.zero in + Block.bake + ~policy:(By_round 1) + ~payload_round:(Some round_zero) + ~locked_round:(Some round_zero) + ~operation:aggregate_with_incorrect_signature + block + in + Assert.proto_error ~loc:__LOC__ res signature_invalid_error + +let test_preattestations_aggregate_non_bls_delegate () = + let open Lwt_result_syntax in + let* _genesis, block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + let* block' = Block.bake block in + let* attesters = Context.get_attesters (B block') in + (* Find an attester with a non-BLS consensus key. *) + let attester, slot = + WithExceptions.Option.get + ~loc:__LOC__ + (find_attester_with_non_bls_key attesters) + in + (* Craft a preattestation for this attester to retreive a signature and a + triplet {level, round, block_payload_hash} *) + let* {shell; protocol_data = {contents; signature}} = + Op.raw_preattestation + ~delegate:attester.RPC.Validators.delegate + ~slot + block' + in + match contents with + | Single (Preattestation consensus_content) -> + let {level; round; block_payload_hash; _} : + Alpha_context.consensus_content = + consensus_content + in + (* Craft an aggregate including the attester slot and signature *) + let consensus_content : Alpha_context.consensus_aggregate_content = + {level; round; block_payload_hash} + in + let contents : _ Alpha_context.contents_list = + Single + (Preattestations_aggregate {consensus_content; committee = [slot]}) + in + let operation : operation = + {shell; protocol_data = Operation_data {contents; signature}} + in + (* Bake a block containing this aggregate and expect an error *) + let*! res = + let round_zero = Alpha_context.Round.zero in + Block.bake + ~policy:(By_round 1) + ~payload_round:(Some round_zero) + ~locked_round:(Some round_zero) + ~operation + block + in + Assert.proto_error ~loc:__LOC__ res non_bls_in_aggregate + +let test_attestations_aggregate_non_bls_delegate () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -320,7 +522,7 @@ let test_aggregate_attestation_non_bls_delegate () = let*! res = Block.bake ~operation:aggregate block in Assert.proto_error ~loc:__LOC__ res non_bls_in_aggregate -let test_multiple_aggregations_per_block_forbidden () = +let test_multiple_aggregates_per_block_forbidden () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -339,7 +541,7 @@ 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 test_eligible_attestation_must_be_aggregated () = let open Lwt_result_syntax in let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () @@ -363,6 +565,7 @@ let test_empty_committee () = let* _genesis, block = init_genesis_with_some_bls_accounts ~aggregate_attestation:true () in + (* Crafting an attestations_aggregate with an empty committee *) let* consensus_content = let* attestation = Op.raw_attestation block in match attestation.protocol_data with @@ -377,8 +580,36 @@ let test_empty_committee () = in let signature = Some Signature.(of_bls Signature.Bls.zero) in let operation = Op.pack_operation (B block) signature (Single contents) in + (* Baking with the attestations_aggregate and expecting an error *) let*! res = Block.bake ~operation block in - Assert.proto_error ~loc:__LOC__ res empty_aggregation_committee + let* () = Assert.proto_error ~loc:__LOC__ res empty_aggregation_committee in + (* Crafting a preattestations_aggregate with an empty committee *) + let* consensus_content = + let* block = Block.bake block in + let* preattestation = Op.raw_preattestation block in + match preattestation.protocol_data with + | {contents = Single (Preattestation consensus_content); _} -> + let Alpha_context.{level; round; block_payload_hash; slot = _} = + consensus_content + in + return Alpha_context.{level; round; block_payload_hash} + in + let contents = + Alpha_context.Preattestations_aggregate {consensus_content; committee = []} + in + let operation = Op.pack_operation (B block) signature (Single contents) in + (* Baking with the preattestations_aggregate and expecting 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) + ~operation + block + in + let* () = Assert.proto_error ~loc:__LOC__ res empty_aggregation_committee in + return_unit let tests = [ @@ -391,30 +622,46 @@ let tests = `Quick test_aggregate_feature_flag_disabled; Tztest.tztest - "test_aggregate_attestation_with_a_single_bls_attestation" + "test_preattestations_aggregate_with_a_single_delegate" + `Quick + test_preattestations_aggregate_with_a_single_delegate; + Tztest.tztest + "test_attestations_aggregate_with_a_single_delegate" + `Quick + test_attestations_aggregate_with_a_single_delegate; + Tztest.tztest + "test_preattestations_aggregate_with_multiple_delegates" + `Quick + test_preattestations_aggregate_with_multiple_delegates; + Tztest.tztest + "test_attestations_aggregate_with_multiple_delegates" + `Quick + test_attestations_aggregate_with_multiple_delegates; + Tztest.tztest + "test_preattestations_aggregate_invalid_signature" `Quick - test_aggregate_attestation_with_a_single_bls_attestation; + test_preattestations_aggregate_invalid_signature; Tztest.tztest - "test_aggregate_attestation_with_multiple_bls_attestations" + "test_attestations_aggregate_invalid_signature" `Quick - test_aggregate_attestation_with_multiple_bls_attestations; + test_attestations_aggregate_invalid_signature; Tztest.tztest - "test_aggregate_attestation_invalid_signature" + "test_preattestations_aggregate_non_bls_delegate" `Quick - test_aggregate_attestation_invalid_signature; + test_preattestations_aggregate_non_bls_delegate; Tztest.tztest - "test_aggregate_attestation_non_bls_delegate" + "test_attestations_aggregate_non_bls_delegate" `Quick - test_aggregate_attestation_non_bls_delegate; + test_attestations_aggregate_non_bls_delegate; Tztest.tztest - "test_multiple_aggregations_per_block_forbidden" + "test_multiple_aggregates_per_block_forbidden" `Quick - test_multiple_aggregations_per_block_forbidden; + test_multiple_aggregates_per_block_forbidden; Tztest.tztest - "eligible_attestation_must_be_aggregated" + "test_eligible_attestation_must_be_aggregated" `Quick - eligible_attestation_must_be_aggregated; - Tztest.tztest "empty committee" `Quick test_empty_committee; + test_eligible_attestation_must_be_aggregated; + Tztest.tztest "test_empty_committee" `Quick test_empty_committee; ] let () =