From f9ee08745f6661a15a6e4a693214f6926c580773 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Wed, 16 Oct 2024 10:26:36 +0200 Subject: [PATCH 1/3] proto: add attestations_aggregate consensus operation --- .../lib_client/operation_result.ml | 17 ++ .../client_baking_denunciation.ml | 203 +++++++++++------- .../lib_injector/injector_plugin.ml | 1 + src/proto_alpha/lib_plugin/mempool.ml | 28 ++- .../lib_protocol/alpha_context.mli | 24 +++ src/proto_alpha/lib_protocol/apply.ml | 11 +- src/proto_alpha/lib_protocol/apply_results.ml | 46 +++- .../lib_protocol/apply_results.mli | 6 + .../lib_protocol/delegate_consensus_key.ml | 9 + .../lib_protocol/delegate_consensus_key.mli | 2 + .../lib_protocol/operation_repr.ml | 129 ++++++++++- .../lib_protocol/operation_repr.mli | 41 +++- .../lib_protocol/test/helpers/block.ml | 1 + .../test/helpers/consensus_helpers.ml | 9 +- .../lib_protocol/test/helpers/op.ml | 66 ++++++ .../lib_protocol/test/helpers/op.mli | 20 ++ .../test/helpers/slashing_helpers.ml | 14 +- .../validate/generator_descriptors.ml | 35 +++ .../validate/generator_descriptors.mli | 1 + .../test/integration/validate/test_sanity.ml | 3 + src/proto_alpha/lib_protocol/validate.ml | 44 +++- .../lib_protocol/validate_errors.ml | 53 ++++- .../lib_protocol/validate_errors.mli | 4 + .../lib_sc_rollup_node/sc_rollup_injector.ml | 1 + 24 files changed, 656 insertions(+), 112 deletions(-) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index 833a9ceaea6d..f35a32c3fe38 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -1033,6 +1033,23 @@ let pp_contents_and_result : Consensus_key.pp {delegate; consensus_pkh = consensus_key} consensus_power + | ( Attestations_aggregate {consensus_content = {level; _}; _}, + Attestations_aggregate_result + {balance_updates; committee; consensus_power} ) -> + Format.fprintf + ppf + "@[Attestations aggregate:@,\ + Level: %a@,\ + Balance updates:%a@,\ + Delegates: %a@,\ + Consensus Power: %d@]" + Raw_level.pp + level + pp_balance_updates + balance_updates + (Format.pp_print_list Consensus_key.pp) + committee + consensus_power | ( Double_attestation_evidence {op1; op2}, Double_attestation_evidence_result {forbidden_delegate; balance_updates} ) -> diff --git a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml index 49a3cb5aeafe..1972f8c4c78f 100644 --- a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml +++ b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml @@ -96,6 +96,22 @@ type 'a state = { mutable ops_stream_stopper : unit -> unit; } +type error += Aggregate_denunciation_not_implemented + +let () = + register_error_kind + `Permanent + ~id:"client_baking_denunciation.aggregate_denunciation_not_implemented" + ~title:"Aggregate denunciation not implemented" + ~description:"Denunciation of aggregate operations is not yet implemented" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Denunciation of aggregate operations is not yet implemented") + Data_encoding.empty + (function Aggregate_denunciation_not_implemented -> Some () | _ -> None) + (fun () -> Aggregate_denunciation_not_implemented) + let create_state ~preserved_levels blocks_stream ops_stream ops_stream_stopper = let clean_frequency = max 1 (preserved_levels / 10) in let validators_rights = Validators_cache.create (preserved_levels + 2) in @@ -140,17 +156,27 @@ let get_block_offset level = let get_payload_hash (type kind) (op_kind : kind consensus_operation_type) (op : kind Operation.t) = + let open Lwt_result_syntax in match (op_kind, op.protocol_data.contents) with | Preattestation, Single (Preattestation consensus_content) | Attestation, Single (Attestation {consensus_content; _}) -> - consensus_content.block_payload_hash + return consensus_content.block_payload_hash + | Attestations_aggregate, Single (Attestations_aggregate _) -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + tzfail Aggregate_denunciation_not_implemented let get_slot (type kind) (op_kind : kind consensus_operation_type) (op : kind Operation.t) = + let open Lwt_result_syntax in match (op_kind, op.protocol_data.contents) with | Preattestation, Single (Preattestation consensus_content) | Attestation, Single (Attestation {consensus_content; _}) -> - consensus_content.slot + return consensus_content.slot + | Attestations_aggregate, Single (Attestations_aggregate _) -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + tzfail Aggregate_denunciation_not_implemented let double_consensus_op_evidence (type kind) : kind consensus_operation_type -> @@ -163,15 +189,26 @@ let double_consensus_op_evidence (type kind) : bytes Environment.Error_monad.shell_tzresult Lwt.t = function | Attestation -> Plugin.RPC.Forge.double_attestation_evidence | Preattestation -> Plugin.RPC.Forge.double_preattestation_evidence + | Attestations_aggregate -> + fun _ _ ~branch:_ ~op1:_ ~op2:_ () -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + Lwt_result_syntax.tzfail Aggregate_denunciation_not_implemented let lookup_recorded_consensus (type kind) consensus_key - (op_kind : kind consensus_operation_type) map : kind recorded_consensus = + (op_kind : kind consensus_operation_type) map : + (kind recorded_consensus, error trace) result Lwt.t = + let open Lwt_result_syntax in match Delegate_map.find consensus_key map with - | None -> No_operation_seen + | None -> return No_operation_seen | Some {attestation; preattestation} -> ( match op_kind with - | Attestation -> attestation - | Preattestation -> preattestation) + | Attestation -> return attestation + | Preattestation -> return preattestation + | Attestations_aggregate -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + tzfail Aggregate_denunciation_not_implemented) let add_consensus_operation (type kind) consensus_key (op_kind : kind consensus_operation_type) @@ -190,7 +227,8 @@ let add_consensus_operation (type kind) consensus_key in match op_kind with | Attestation -> Some {record with attestation = recorded_operation} - | Preattestation -> Some {record with preattestation = recorded_operation}) + | Preattestation -> Some {record with preattestation = recorded_operation} + | Attestations_aggregate -> x) map let get_validator_rights state cctxt level = @@ -245,7 +283,10 @@ let process_consensus_op (type kind) state cctxt state.consensus_operations_table (chain_id, level, round) in - match lookup_recorded_consensus consensus_key op_kind round_map with + let* recorded_consensus = + lookup_recorded_consensus consensus_key op_kind round_map + in + match recorded_consensus with | No_operation_seen -> return @@ HLevel.add @@ -257,74 +298,84 @@ let process_consensus_op (type kind) state cctxt (Operation_seen {operation = new_op; previously_denounced_oph = None}) round_map) - | Operation_seen {operation = existing_op; previously_denounced_oph} - when Block_payload_hash.( - get_payload_hash op_kind existing_op - <> get_payload_hash op_kind new_op) - || Slot.(get_slot op_kind existing_op <> slot) - || Block_hash.(existing_op.shell.branch <> new_op.shell.branch) - -> - (* Same level, round, and delegate, and: - different payload hash OR different slot OR different branch *) - let new_op_hash, existing_op_hash = - (Operation.hash new_op, Operation.hash existing_op) - in - let op1, op2 = - if Operation_hash.(new_op_hash < existing_op_hash) then - (new_op, existing_op) - else (existing_op, new_op) - in - let*! block = get_block_offset level in - let chain = `Hash chain_id in - let* block_hash = - Alpha_block_services.hash cctxt ~chain ~block () - in - let* bytes = - double_consensus_op_evidence - op_kind - cctxt - (`Hash chain_id, block) - ~branch:block_hash - ~op1 - ~op2 - () - in - let bytes = Signature.concat bytes Signature.zero in - let double_op_detected, double_op_denounced = - Events.( - match op_kind with - | Attestation -> - (double_attestation_detected, double_attestation_denounced) - | Preattestation -> - ( double_preattestation_detected, - double_preattestation_denounced )) - in - let*! () = - Events.(emit double_op_detected) (new_op_hash, existing_op_hash) - in - let* op_hash = - Shell_services.Injection.private_operation cctxt ~chain bytes - in - let*! () = - match previously_denounced_oph with - | Some oph -> Events.(emit double_consensus_already_denounced) oph - | None -> Lwt.return_unit - in - HLevel.replace - state.consensus_operations_table - (chain_id, level, round) - (add_consensus_operation - consensus_key - op_kind - (Operation_seen - { - operation = new_op; - previously_denounced_oph = Some op_hash; - }) - round_map) ; - let*! () = Events.(emit double_op_denounced) (op_hash, bytes) in - return_unit - | _ -> return_unit) + | Operation_seen {operation = existing_op; previously_denounced_oph} -> + let* existing_payload_hash = get_payload_hash op_kind existing_op in + let* new_payload_hash = get_payload_hash op_kind new_op in + let* existing_slot = get_slot op_kind existing_op in + if + Block_payload_hash.(existing_payload_hash <> new_payload_hash) + || Slot.(existing_slot <> slot) + || Block_hash.(existing_op.shell.branch <> new_op.shell.branch) + then ( + (* Same level, round, and delegate, and: + different payload hash OR different slot OR different branch *) + let new_op_hash, existing_op_hash = + (Operation.hash new_op, Operation.hash existing_op) + in + let op1, op2 = + if Operation_hash.(new_op_hash < existing_op_hash) then + (new_op, existing_op) + else (existing_op, new_op) + in + let*! block = get_block_offset level in + let chain = `Hash chain_id in + let* block_hash = + Alpha_block_services.hash cctxt ~chain ~block () + in + let* bytes = + double_consensus_op_evidence + op_kind + cctxt + (`Hash chain_id, block) + ~branch:block_hash + ~op1 + ~op2 + () + in + let bytes = Signature.concat bytes Signature.zero in + let* double_op_detected, double_op_denounced = + Events.( + match op_kind with + | Attestation -> + return + ( double_attestation_detected, + double_attestation_denounced ) + | Preattestation -> + return + ( double_preattestation_detected, + double_preattestation_denounced ) + | Attestations_aggregate -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + tzfail Aggregate_denunciation_not_implemented) + in + let*! () = + Events.(emit double_op_detected) (new_op_hash, existing_op_hash) + in + let* op_hash = + Shell_services.Injection.private_operation cctxt ~chain bytes + in + let*! () = + match previously_denounced_oph with + | Some oph -> + Events.(emit double_consensus_already_denounced) oph + | None -> Lwt.return_unit + in + HLevel.replace + state.consensus_operations_table + (chain_id, level, round) + (add_consensus_operation + consensus_key + op_kind + (Operation_seen + { + operation = new_op; + previously_denounced_oph = Some op_hash; + }) + round_map) ; + let*! () = Events.(emit double_op_denounced) (op_hash, bytes) in + return_unit) + else return_unit) let process_operations (cctxt : #Protocol_client_context.full) state (attestations : 'a list) ~packed_op chain_id = diff --git a/src/proto_alpha/lib_injector/injector_plugin.ml b/src/proto_alpha/lib_injector/injector_plugin.ml index 6d8b121028dd..f9afe4f07348 100644 --- a/src/proto_alpha/lib_injector/injector_plugin.ml +++ b/src/proto_alpha/lib_injector/injector_plugin.ml @@ -154,6 +154,7 @@ module Proto_client = struct match op_result with | Preattestation_result _ -> Successful | Attestation_result _ -> Successful + | Attestations_aggregate_result _ -> Successful | Seed_nonce_revelation_result _ -> Successful | Vdf_revelation_result _ -> Successful | Double_attestation_evidence_result _ -> Successful diff --git a/src/proto_alpha/lib_plugin/mempool.ml b/src/proto_alpha/lib_plugin/mempool.ml index 706ab10a65e3..84df0640e5ca 100644 --- a/src/proto_alpha/lib_plugin/mempool.ml +++ b/src/proto_alpha/lib_plugin/mempool.ml @@ -552,6 +552,10 @@ let pre_filter info config {level = consensus_content.level; round = consensus_content.round} in prefilter_consensus_operation info config level_and_round + | Single (Attestations_aggregate _) -> + (* Aggregate are built at baking time and shouldn't be broadcasted between + mempools. *) + return (`Refused [Environment.wrap_tzerror Wrong_operation]) | Single (Seed_nonce_revelation _) | Single (Double_preattestation_evidence _) | Single (Double_attestation_evidence _) @@ -690,11 +694,11 @@ let find_manager {shell = _; protocol_data = Operation_data {contents; _}} = | Single (Manager_operation {source; _}) -> Some source | Cons (Manager_operation {source; _}, _) -> Some source | Single - ( Preattestation _ | Attestation _ | Proposals _ | Ballot _ - | Seed_nonce_revelation _ | Vdf_revelation _ | Double_baking_evidence _ - | Double_preattestation_evidence _ | Double_attestation_evidence _ - | Dal_entrapment_evidence _ | Activate_account _ | Drain_delegate _ - | Failing_noop _ ) -> + ( Preattestation _ | Attestation _ | Attestations_aggregate _ + | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Vdf_revelation _ + | Double_baking_evidence _ | Double_preattestation_evidence _ + | Double_attestation_evidence _ | Dal_entrapment_evidence _ + | Activate_account _ | Drain_delegate _ | Failing_noop _ ) -> None (* The purpose of this module is to offer a version of @@ -812,6 +816,20 @@ let sources_from_operation ctxt | Ok (_ctxt, {delegate; consensus_pkh; consensus_pk = _}) -> return [delegate; consensus_pkh] | Error _ -> return_nil) + | Single (Attestations_aggregate {consensus_content; committee}) -> + let level = Level.from_raw ctxt consensus_content.level in + let* sources = + Lwt_list.fold_left_s + (fun acc slot -> + let* slot_owner = Stake_distribution.slot_owner ctxt level slot in + match slot_owner with + | Ok (_ctxt, {delegate; consensus_pkh; consensus_pk = _}) -> + return (delegate :: consensus_pkh :: acc) + | Error _ -> return acc) + [] + committee + in + return sources | Single (Seed_nonce_revelation _) | Single (Double_preattestation_evidence _) | Single (Double_attestation_evidence _) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 650fa349060c..ef222fda1c3a 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2244,6 +2244,8 @@ module Consensus_key : sig consensus_pkh : Signature.Public_key_hash.t; } + val encoding : t Data_encoding.t + val zero : t val pp : Format.formatter -> t -> unit @@ -4528,14 +4530,21 @@ module Kind : sig type attestation_consensus_kind = Attestation_consensus_kind + type attestations_aggregate_consensus_kind = + | Attestations_aggregate_consensus_kind + type 'a consensus = | Preattestation_kind : preattestation_consensus_kind consensus | Attestation_kind : attestation_consensus_kind consensus + | Attestations_aggregate_kind + : attestations_aggregate_consensus_kind consensus type preattestation = preattestation_consensus_kind consensus type attestation = attestation_consensus_kind consensus + type attestations_aggregate = attestations_aggregate_consensus_kind consensus + type seed_nonce_revelation = Seed_nonce_revelation_kind type vdf_revelation = Vdf_revelation_kind @@ -4639,6 +4648,14 @@ end type 'a consensus_operation_type = | Attestation : Kind.attestation consensus_operation_type | Preattestation : Kind.preattestation consensus_operation_type + | Attestations_aggregate + : Kind.attestations_aggregate consensus_operation_type + +type consensus_aggregate_content = { + level : Raw_level.t; + round : Round.t; + block_payload_hash : Block_payload_hash.t; +} type consensus_content = { slot : Slot.t; @@ -4679,6 +4696,11 @@ and _ contents = dal_content : dal_content option; } -> Kind.attestation contents + | Attestations_aggregate : { + consensus_content : consensus_aggregate_content; + committee : Slot.t list; + } + -> Kind.attestations_aggregate contents | Seed_nonce_revelation : { level : Raw_level.t; nonce : Nonce.t; @@ -4954,6 +4976,8 @@ module Operation : sig val attestation_case : Kind.attestation case + val attestations_aggregate_case : Kind.attestations_aggregate case + val attestation_with_dal_case : Kind.attestation case val seed_nonce_revelation_case : Kind.seed_nonce_revelation case diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index e48a2c7bc02b..c105c62c6679 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -154,7 +154,6 @@ let () = Data_encoding.(obj1 (req "source" Destination.encoding)) (function Non_empty_transaction_from c -> Some c | _ -> None) (fun c -> Non_empty_transaction_from c) ; - register_error_kind `Permanent ~id:"internal_operation_replay" @@ -2237,6 +2236,7 @@ let record_operation (type kind) ctxt hash (operation : kind operation) : match operation.protocol_data.contents with | Single (Preattestation _) -> ctxt | Single (Attestation _) -> ctxt + | Single (Attestations_aggregate _) -> ctxt | Single ( Failing_noop _ | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Vdf_revelation _ | Double_attestation_evidence _ @@ -2398,7 +2398,7 @@ let punish_delegate ctxt ~operation_hash delegate level misbehaviour mk_result ~payload_producer = let open Lwt_result_syntax in let rewarded = payload_producer.Consensus_key.delegate in - let+ ctxt = + let* ctxt = Delegate.punish_double_signing ctxt ~operation_hash @@ -2407,7 +2407,8 @@ let punish_delegate ctxt ~operation_hash delegate level misbehaviour mk_result level ~rewarded in - (ctxt, Single_result (mk_result (Some delegate) [])) + let content_result = mk_result (Some delegate) [] in + return (ctxt, Single_result content_result) let punish_double_preattestation ctxt ~operation_hash ~(op1 : Kind.preattestation Operation.t) ~payload_producer = @@ -2493,6 +2494,10 @@ let apply_contents_list (type kind) ctxt chain_id (mode : mode) record_preattestation ctxt mode consensus_content | Single (Attestation {consensus_content; dal_content}) -> record_attestation ctxt mode consensus_content dal_content + | Single (Attestations_aggregate _) -> + if Constants.aggregate_attestation ctxt then + tzfail Validate_errors.Consensus.Aggregate_not_implemented + else tzfail Validate_errors.Consensus.Aggregate_disabled | Single (Seed_nonce_revelation {level; nonce}) -> let level = Level.from_raw ctxt level in let* ctxt = Nonce.reveal ctxt level nonce in diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index 6de114e96383..5a961b2c5abb 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -887,6 +887,12 @@ type 'kind contents_result = consensus_power : int; } -> Kind.attestation contents_result + | Attestations_aggregate_result : { + balance_updates : Receipt.balance_updates; + committee : Consensus_key.t list; + consensus_power : int; + } + -> Kind.attestations_aggregate contents_result | Seed_nonce_revelation_result : Receipt.balance_updates -> Kind.seed_nonce_revelation contents_result @@ -1024,6 +1030,13 @@ module Encoding = struct (req "consensus_power" int31) (req "consensus_key" Signature.Public_key_hash.encoding) + let consensus_aggregate_result_encoding = + let open Data_encoding in + obj3 + (dft "balance_updates" Receipt.balance_updates_encoding []) + (req "committee" (list Consensus_key.encoding)) + (req "consensus_power" int31) + type case = | Case : { op_case : 'kind Operation.Encoding.case; @@ -1120,6 +1133,31 @@ module Encoding = struct {balance_updates; delegate; consensus_key; consensus_power}); } + let attestations_aggregate_case = + Case + { + op_case = Operation.Encoding.attestations_aggregate_case; + encoding = consensus_aggregate_result_encoding; + select = + (function + | Contents_result (Attestations_aggregate_result _ as op) -> Some op + | _ -> None); + mselect = + (function + | Contents_and_result ((Attestations_aggregate _ as op), res) -> + Some (op, res) + | _ -> None); + proj = + (function + | Attestations_aggregate_result + {balance_updates; committee; consensus_power} -> + (balance_updates, committee, consensus_power)); + inj = + (fun (balance_updates, committee, consensus_power) -> + Attestations_aggregate_result + {balance_updates; committee; consensus_power}); + } + let seed_nonce_revelation_case = Case { @@ -1397,6 +1435,7 @@ module Encoding = struct {op with operation_result = Failed (kind, errs)})) | Contents_result (Preattestation_result _) -> None | Contents_result (Attestation_result _) -> None + | Contents_result (Attestations_aggregate_result _) -> None | Contents_result Ballot_result -> None | Contents_result (Seed_nonce_revelation_result _) -> None | Contents_result (Vdf_revelation_result _) -> None @@ -1692,8 +1731,9 @@ let common_cases = let contents_cases = let open Encoding in attestation_case :: attestation_with_dal_case :: preattestation_case - :: double_attestation_evidence_case :: double_preattestation_evidence_case - :: dal_entrapment_evidence_case :: common_cases + :: attestations_aggregate_case :: double_attestation_evidence_case + :: double_preattestation_evidence_case :: dal_entrapment_evidence_case + :: common_cases let make_contents_result (Encoding.Case @@ -1853,6 +1893,8 @@ let kind_equal : | Attestation _, _ -> None | Preattestation _, Preattestation_result _ -> Some Eq | Preattestation _, _ -> None + | Attestations_aggregate _, Attestations_aggregate_result _ -> Some Eq + | Attestations_aggregate _, _ -> None | Seed_nonce_revelation _, Seed_nonce_revelation_result _ -> Some Eq | Seed_nonce_revelation _, _ -> None | Vdf_revelation _, Vdf_revelation_result _ -> Some Eq diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 2828fe10c6c0..845cfff47b08 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -73,6 +73,12 @@ and 'kind contents_result = consensus_power : int; } -> Kind.attestation contents_result + | Attestations_aggregate_result : { + balance_updates : Receipt.balance_updates; + committee : Consensus_key.t list; + consensus_power : int; + } + -> Kind.attestations_aggregate contents_result | Seed_nonce_revelation_result : Receipt.balance_updates -> Kind.seed_nonce_revelation contents_result diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index f0748736b13c..004dd0d6816c 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -82,6 +82,15 @@ type t = { consensus_pkh : Signature.Public_key_hash.t; } +let encoding = + let open Data_encoding in + conv + (fun {delegate; consensus_pkh} -> (delegate, consensus_pkh)) + (fun (delegate, consensus_pkh) -> {delegate; consensus_pkh}) + @@ obj2 + (req "delegate" Signature.Public_key_hash.encoding) + (req "consensus_pkh" Signature.Public_key_hash.encoding) + let pkh {delegate; consensus_pkh; consensus_pk = _} = {delegate; consensus_pkh} let zero = diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index 0e7ed066f4e5..920ec51e028c 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -47,6 +47,8 @@ type t = { consensus_pkh : Signature.Public_key_hash.t; } +val encoding : t Data_encoding.t + val zero : t val pp : Format.formatter -> t -> unit diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index 2f556cb6d7e3..b72844ace2df 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -32,14 +32,21 @@ module Kind = struct type attestation_consensus_kind = Attestation_consensus_kind + type attestations_aggregate_consensus_kind = + | Attestations_aggregate_consensus_kind + type 'a consensus = | Preattestation_kind : preattestation_consensus_kind consensus | Attestation_kind : attestation_consensus_kind consensus + | Attestations_aggregate_kind + : attestations_aggregate_consensus_kind consensus type preattestation = preattestation_consensus_kind consensus type attestation = attestation_consensus_kind consensus + type attestations_aggregate = attestations_aggregate_consensus_kind consensus + type seed_nonce_revelation = Seed_nonce_revelation_kind type vdf_revelation = Vdf_revelation_kind @@ -141,6 +148,14 @@ end type 'a consensus_operation_type = | Attestation : Kind.attestation consensus_operation_type | Preattestation : Kind.preattestation consensus_operation_type + | Attestations_aggregate + : Kind.attestations_aggregate consensus_operation_type + +type consensus_aggregate_content = { + level : Raw_level_repr.t; + round : Round_repr.t; + block_payload_hash : Block_payload_hash.t; +} type consensus_content = { slot : Slot_repr.t; @@ -158,19 +173,29 @@ type consensus_content = { type dal_content = {attestation : Dal_attestation_repr.t} -let consensus_content_encoding = +let consensus_aggregate_content_encoding = let open Data_encoding in conv - (fun {slot; level; round; block_payload_hash} -> - (slot, level, round, block_payload_hash)) - (fun (slot, level, round, block_payload_hash) -> - {slot; level; round; block_payload_hash}) - (obj4 - (req "slot" Slot_repr.encoding) + (fun ({level; round; block_payload_hash} : consensus_aggregate_content) -> + (level, round, block_payload_hash)) + (fun (level, round, block_payload_hash) : consensus_aggregate_content -> + {level; round; block_payload_hash}) + (obj3 (req "level" Raw_level_repr.encoding) (req "round" Round_repr.encoding) (req "block_payload_hash" Block_payload_hash.encoding)) +let consensus_content_encoding = + let open Data_encoding in + conv + (fun {slot; level; round; block_payload_hash} -> + (slot, {level; round; block_payload_hash})) + (fun (slot, {level; round; block_payload_hash}) -> + {slot; level; round; block_payload_hash}) + (merge_objs + (obj1 (req "slot" Slot_repr.encoding)) + consensus_aggregate_content_encoding) + let pp_consensus_content ppf content = Format.fprintf ppf @@ -238,6 +263,11 @@ and _ contents = dal_content : dal_content option; } -> Kind.attestation contents + | Attestations_aggregate : { + consensus_content : consensus_aggregate_content; + committee : Slot_repr.t list; + } + -> Kind.attestations_aggregate contents | Seed_nonce_revelation : { level : Raw_level_repr.t; nonce : Seed_repr.nonce; @@ -1151,6 +1181,28 @@ module Encoding = struct ])) (varopt "signature" Signature.encoding))) + let attestations_aggregate_encoding = + obj2 + (req "consensus_content" consensus_aggregate_content_encoding) + (req "committee" (list Slot_repr.encoding)) + + let attestations_aggregate_case = + Case + { + tag = 31; + name = "attestations_aggregate"; + encoding = attestations_aggregate_encoding; + select = + (function + | Contents (Attestations_aggregate _ as op) -> Some op | _ -> None); + proj = + (fun (Attestations_aggregate {consensus_content; committee}) -> + (consensus_content, committee)); + inj = + (fun (consensus_content, committee) -> + Attestations_aggregate {consensus_content; committee}); + } + let seed_nonce_revelation_case = Case { @@ -1480,6 +1532,7 @@ module Encoding = struct [ PCase preattestation_case; PCase attestation_case; + PCase attestations_aggregate_case; PCase attestation_with_dal_case; PCase double_preattestation_evidence_case; PCase double_attestation_evidence_case; @@ -1737,6 +1790,7 @@ let acceptable_pass (op : packed_operation) = | Single (Failing_noop _) -> None | Single (Preattestation _) -> Some consensus_pass | Single (Attestation _) -> Some consensus_pass + | Single (Attestations_aggregate _) -> Some consensus_pass | Single (Proposals _) -> Some voting_pass | Single (Ballot _) -> Some voting_pass | Single (Seed_nonce_revelation _) -> Some anonymous_pass @@ -1834,7 +1888,7 @@ let check_signature (type kind) key chain_id (op : kind operation) = | Vdf_revelation _ | Double_attestation_evidence _ | Double_preattestation_evidence _ | Double_baking_evidence _ | Dal_entrapment_evidence _ | Activate_account _ | Drain_delegate _ - | Manager_operation _ ) -> + | Manager_operation _ | Attestations_aggregate _ ) -> Generic_operation | Cons (Manager_operation _, _ops) -> Generic_operation in @@ -1914,6 +1968,8 @@ let equal_contents_kind : type a b. a contents -> b contents -> (a, b) eq option | Preattestation _, _ -> None | Attestation _, Attestation _ -> Some Eq | Attestation _, _ -> None + | Attestations_aggregate _, Attestations_aggregate _ -> Some Eq + | Attestations_aggregate _, _ -> None | Seed_nonce_revelation _, Seed_nonce_revelation _ -> Some Eq | Seed_nonce_revelation _, _ -> None | Vdf_revelation _, Vdf_revelation _ -> Some Eq @@ -2022,6 +2078,10 @@ type round_infos = {level : int32; round : int} convert into an {!int}. *) type preattestation_infos = {round : round_infos; slot : int} +(** [aggregate_infos] is the pair of a {!round_infos} and a [committee] that is + a list of slots converted into a {!int list}. *) +type aggregate_infos = {round : round_infos; committee : int list} + (** [attestation_infos] is the tuple consisting of a {!round_infos} value, a [slot], and the number of DAL slots in the DAL attestation. *) type attestation_infos = { @@ -2085,6 +2145,24 @@ let attestation_infos_from_content (c : consensus_content) d; } +(** Compute an {!aggregate_info} value from {!consensus_aggregate_content} + and {!Slot_repr.t list} values. It is used to compute the weight of an + {!Aggregate_attestation}. + + Precondition: [aggregate_consensus_content] and [committee] come from a + valid operation. *) +let aggregate_infos_from_content (proposal : consensus_aggregate_content) + (committee : Slot_repr.t list) = + let level = Raw_level_repr.to_int32 proposal.level in + let round = + match Round_repr.to_int proposal.round with + | Ok round -> round + | Error _ -> -1 + in + let round_infos = {level; round} in + let committee = List.map Slot_repr.to_int committee in + {round = round_infos; committee} + (** Compute a {!double_baking_infos} and a {!Block_header_repr.hash} from a {!Block_header_repr.t}. It is used to compute the weight of a {!Double_baking_evidence}. @@ -2151,6 +2229,9 @@ type dal_entrapment_info = {level : int32; number_of_attested_slots : int} type _ weight = | Weight_attestation : attestation_infos -> consensus_pass_type weight | Weight_preattestation : preattestation_infos -> consensus_pass_type weight + | Weight_attestations_aggregate : + aggregate_infos + -> consensus_pass_type weight | Weight_proposals : int32 * Signature.Public_key_hash.t -> voting_pass_type weight @@ -2252,6 +2333,11 @@ let weight_of : packed_operation -> operation_weight = ( Consensus, Weight_attestation (attestation_infos_from_content consensus_content dal_content) ) + | Single (Attestations_aggregate {consensus_content; committee}) -> + let aggregate_infos = + aggregate_infos_from_content consensus_content committee + in + W (Consensus, Weight_attestations_aggregate aggregate_infos) | Single (Proposals {period; source; _}) -> W (Voting, Weight_proposals (period, source)) | Single (Ballot {period; source; _}) -> @@ -2387,6 +2473,18 @@ let compare_dal_entrapment_infos infos1 infos2 = (infos1.level, infos1.number_of_attested_slots) (infos2.level, infos2.number_of_attested_slots) +(** Two {!Attestations_aggregate} are compared by their {!aggregate_infos}. When their + {!round_infos} are equal, they are compared according to their [committee] + by lexicographic order. *) +let compare_attestations_aggregate_infos + {round = infos1; committee = committee1} + {round = infos2; committee = committee2} = + compare_pair_in_lexico_order + ~cmp_fst:compare_round_infos + ~cmp_snd:(List.compare Compare.Int.compare) + (infos1, committee1) + (infos2, committee2) + (** {4 Comparison of valid operations of the same validation pass} *) (** {5 Comparison of valid consensus operations} *) @@ -2415,6 +2513,21 @@ let compare_consensus_weight w1 w2 = Weight_attestation {round_infos = round_infos2; _} ) -> let cmp = compare_round_infos round_infos1 round_infos2 in if Compare.Int.(cmp <> 0) then cmp else -1 + | Weight_attestations_aggregate infos1, Weight_attestations_aggregate infos2 + -> + compare_attestations_aggregate_infos infos1 infos2 + | ( Weight_attestations_aggregate {round = round_infos1; _}, + Weight_preattestation {round = round_infos2; _} ) + | ( Weight_attestations_aggregate {round = round_infos1; _}, + Weight_attestation {round_infos = round_infos2; _} ) -> + let cmp = compare_round_infos round_infos1 round_infos2 in + if Compare.Int.(cmp <> 0) then cmp else 1 + | ( Weight_preattestation {round = round_infos1; _}, + Weight_attestations_aggregate {round = round_infos2; _} ) + | ( Weight_attestation {round_infos = round_infos1; _}, + Weight_attestations_aggregate {round = round_infos2; _} ) -> + let cmp = compare_round_infos round_infos1 round_infos2 in + if Compare.Int.(cmp <> 0) then cmp else -1 (** {5 Comparison of valid voting operations} *) diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index c6179418ccd4..96b0808a163b 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -30,6 +30,7 @@ Defines kinds of operations that can be performed on chain: - preattestation - attestation + - attestations_aggregate - double baking evidence - double preattestation evidence - double attestation evidence @@ -67,14 +68,21 @@ module Kind : sig type attestation_consensus_kind = Attestation_consensus_kind + type attestations_aggregate_consensus_kind = + | Attestations_aggregate_consensus_kind + type 'a consensus = | Preattestation_kind : preattestation_consensus_kind consensus | Attestation_kind : attestation_consensus_kind consensus + | Attestations_aggregate_kind + : attestations_aggregate_consensus_kind consensus type preattestation = preattestation_consensus_kind consensus type attestation = attestation_consensus_kind consensus + type attestations_aggregate = attestations_aggregate_consensus_kind consensus + type seed_nonce_revelation = Seed_nonce_revelation_kind type vdf_revelation = Vdf_revelation_kind @@ -176,6 +184,14 @@ end type 'a consensus_operation_type = | Attestation : Kind.attestation consensus_operation_type | Preattestation : Kind.preattestation consensus_operation_type + | Attestations_aggregate + : Kind.attestations_aggregate consensus_operation_type + +type consensus_aggregate_content = { + level : Raw_level_repr.t; + round : Round_repr.t; + block_payload_hash : Block_payload_hash.t; +} type consensus_content = { slot : Slot_repr.t; @@ -251,6 +267,12 @@ and _ contents = dal_content : dal_content option; } -> Kind.attestation contents + (* Aggregate of attestations without dal_content. *) + | Attestations_aggregate : { + consensus_content : consensus_aggregate_content; + committee : Slot_repr.t list; + } + -> Kind.attestations_aggregate contents (* Seed_nonce_revelation: Nonces are created by bakers and are combined to create pseudo-random seeds. Bakers are urged to reveal their nonces after a given number of cycles to keep their block rewards @@ -602,11 +624,11 @@ val compare_by_passes : packed_operation -> packed_operation -> int The global order is as follows: - {!Attestation} and {!Preattestation} > {!Proposals} > {!Ballot} > - {!Double_preattestation_evidence} > {!Double_attestation_evidence} > - {!Double_baking_evidence} > {!Dal_entrapment_evidence} > {!Vdf_revelation} > - {!Seed_nonce_revelation} > {!Activate_account} > {!Drain_delegate} > - {!Manager_operation}. + {!Attestations_aggregate}, {!Attestation} and {!Preattestation} > + {!Proposals} > {!Ballot} > {!Double_preattestation_evidence} > + {!Double_attestation_evidence} > {!Double_baking_evidence} > + {!Dal_entrapment_evidence} > {!Vdf_revelation} > {!Seed_nonce_revelation} > + {!Activate_account} > {!Drain_delegate} > {!Manager_operation}. {!Attestation} and {!Preattestation} are compared by the pair of their [level] and [round] such as the farther to the current state [level] and @@ -617,6 +639,13 @@ val compare_by_passes : packed_operation -> packed_operation -> int DAL attested slots, the more the better. When the pair is equal and comparing an {!Attestation} to a {!Preattestation}, the {!Attestation} is better. + {!Attestations_aggregate} are compared to {!Attestations} and + {!Preattestations} by the pair of their [level] and [round] as well. If the + pairs are equal, an {!Attestation_aggregate} is preferred over an + {!Attestation} or a {!Preattestation}. For equal pairs, two + {!Attestations_aggregate} are compared by their [committee] in lexicographic + order. + Two voting operations are compared in the lexicographic order of the pair of their [period] and [source]. A {!Proposals} is better than a {!Ballot}. @@ -681,6 +710,8 @@ module Encoding : sig val attestation_with_dal_case : Kind.attestation case + val attestations_aggregate_case : Kind.attestations_aggregate case + val seed_nonce_revelation_case : Kind.seed_nonce_revelation case val vdf_revelation_case : Kind.vdf_revelation case diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index bf2b7da53478..916c28091a85 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -1168,6 +1168,7 @@ let balance_updates_of_single_content : | Proposals_result | Ballot_result -> [] | Preattestation_result {balance_updates; _} | Attestation_result {balance_updates; _} + | Attestations_aggregate_result {balance_updates; _} | Seed_nonce_revelation_result balance_updates | Vdf_revelation_result balance_updates | Double_attestation_evidence_result {balance_updates; _} diff --git a/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml index c5fe62a3b10d..dd3323ad19ec 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml @@ -33,7 +33,7 @@ let show_mode = function | Construction -> "Construction" | Mempool -> "Mempool" -type kind = Preattestation | Attestation +type kind = Preattestation | Attestation | Aggregate (** Craft an attestation or preattestation, and bake a block containing it (in application or construction modes) or inject it @@ -74,6 +74,13 @@ let test_consensus_operation ?delegate ?slot ?level ?round ?block_payload_hash ?block_payload_hash ?branch attested_block + | Aggregate -> + Op.attestations_aggregate + ?level + ?round + ?block_payload_hash + ?branch + attested_block in let check_error res = match error with diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.ml b/src/proto_alpha/lib_protocol/test/helpers/op.ml index 8533a584da89..7a9ea8c4d771 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/op.ml @@ -129,6 +129,40 @@ let raw_attestation ?delegate ?slot ?level ?round ?block_payload_hash branch op) +let aggregate attestations = + let aggregate_content = + List.fold_left + (fun acc op -> + let {shell; protocol_data = {contents; signature}} : _ Operation.t = + op + in + match (contents, signature) with + | Single (Attestation {consensus_content; _}), Some (Bls bls_sig) -> ( + let {slot; _} = consensus_content in + match acc with + | Some (shell, proposal, slots, signatures) -> + Some (shell, proposal, slot :: slots, bls_sig :: signatures) + | None -> + let {level; round; block_payload_hash; _} = consensus_content in + let proposal = {level; round; block_payload_hash} in + Some (shell, proposal, [slot], [bls_sig])) + | _, _ -> acc) + None + attestations + in + let open Option_syntax in + let* shell, consensus_content, committee, signatures = aggregate_content in + let+ signature = + Bls12_381_signature.MinPk.aggregate_signature_opt signatures + in + let contents = + Single (Attestations_aggregate {consensus_content; committee}) + in + let protocol_data = + Operation_data {contents; signature = Some (Bls signature)} + in + {shell; protocol_data} + let attestation ?delegate ?slot ?level ?round ?block_payload_hash ?dal_content ?branch attested_block = let open Lwt_result_syntax in @@ -145,6 +179,38 @@ let attestation ?delegate ?slot ?level ?round ?block_payload_hash ?dal_content in return (Operation.pack op) +let attestations_aggregate ?committee ?level ?round ?block_payload_hash ?branch + attested_block = + let open Lwt_result_syntax in + let* committee = + match committee with + | Some committee -> return committee + | None -> + let* attesters = Context.get_attesters (B attested_block) in + return + @@ List.filter_map + (fun attester -> + match attester.Plugin.RPC.Validators.consensus_key with + | Bls _ -> Some attester.delegate + | _ -> None) + attesters + in + let* attestations = + List.map_es + (fun delegate -> + raw_attestation + ~delegate + ?level + ?round + ?block_payload_hash + ?branch + attested_block) + committee + in + match aggregate attestations with + | Some aggregate_attestation -> return aggregate_attestation + | None -> failwith "no Bls delegate found" + let raw_preattestation ?delegate ?slot ?level ?round ?block_payload_hash ?branch attested_block = let open Lwt_result_syntax in diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.mli b/src/proto_alpha/lib_protocol/test/helpers/op.mli index cdbbd151e819..ab6b5477f890 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/op.mli @@ -100,6 +100,26 @@ val attestation : Block.t -> Operation.packed tzresult Lwt.t +(** Create a packed attestations_aggregate that is expected for a given + [Block.t]. Block context is expected to include at least one delegate with a + BLS key (or a registered consensus keys). *) +val attestations_aggregate : + ?committee:public_key_hash list -> + ?level:Raw_level.t -> + ?round:Round.t -> + ?block_payload_hash:Block_payload_hash.t -> + ?branch:Block_hash.t -> + Block.t -> + Operation.packed tzresult Lwt.t + +(** Aggregate a list of attestations in a single Attestations_aggregate. + Attestations signed by non-bls delegates are ignored. Evaluates to {!None} if + no bls-signed attestations are found or if signature_aggregation failed + (due to unreadable signature representation). *) +val aggregate : + Kind.attestation_consensus_kind Kind.consensus operation trace -> + Operation.packed option + (** Create a packed preattestation that is expected for a given [Block.t] by packing the result of {!raw_preattestation}. *) val preattestation : diff --git a/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml index 32933208f0dc..7732626dde9c 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml @@ -46,14 +46,14 @@ module Misbehaviour_repr = struct (duplicate_op : a Protocol.Alpha_context.Kind.consensus Protocol.Alpha_context.operation) = - let ( ({slot = _; level; round; block_payload_hash = _} : - Protocol.Alpha_context.consensus_content), - kind ) = + let level, round, kind = match duplicate_op.protocol_data.contents with - | Single (Preattestation consensus_content) -> - (consensus_content, Double_preattesting) - | Single (Attestation {consensus_content; _}) -> - (consensus_content, Double_attesting) + | Single (Preattestation {level; round; _}) -> + (level, round, Double_preattesting) + | Single (Attestation {consensus_content = {level; round; _}; _}) + | Single + (Attestations_aggregate {consensus_content = {level; round; _}; _}) -> + (level, round, Double_attesting) in let level = Protocol.Alpha_context.Raw_level.Internal_for_tests.to_repr level diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml index 3fff4b2463db..c82f7520c2e8 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml @@ -645,6 +645,37 @@ let attestation_descriptor = List.filter_map_es gen state.delegates); } +let attestations_aggregate_descriptor = + let open Lwt_result_syntax in + { + parameters = Fun.id; + required_cycle = (fun _ -> 1); + required_block = (fun _ -> 1); + prelude = (On 1, fun state -> return ([], state)); + opt_prelude = None; + candidates_generator = + (fun state -> + let open Lwt_result_syntax in + let* attestations = + List.filter_map_es + (fun (delegate, consensus_key_opt) -> + let* slots_opt = + Context.get_attester_slot (B state.block) delegate + in + let delegate = Option.value ~default:delegate consensus_key_opt in + let* signer = Account.find delegate in + match (slots_opt, signer.sk) with + | Some (_ :: _), Bls _ -> + let* op = Op.raw_attestation ~delegate state.block in + return (Some op) + | _, _ -> return_none) + state.delegates + in + match Op.aggregate attestations with + | Some op -> return [op] + | None -> return_nil); + } + module Manager = Manager_operation_helpers let required_nb_account = 7 @@ -799,6 +830,7 @@ let manager_descriptor max_batch_size nb_accounts = type op_kind = | KAttestation | KPreattestation + | KAttestations_aggregate | KBallotExp | KBallotProm | KProposals @@ -816,6 +848,7 @@ let op_kind_of_packed_operation op = match contents with | Single (Preattestation _) -> KPreattestation | Single (Attestation _) -> KAttestation + | Single (Attestations_aggregate _) -> KAttestations_aggregate | Single (Seed_nonce_revelation _) -> KNonce | Single (Vdf_revelation _) -> KVdf | Single (Double_attestation_evidence _) -> KDbl_consensus @@ -835,6 +868,7 @@ let pp_op_kind fmt kind = fmt (match kind with | KManager -> "manager" + | KAttestations_aggregate -> "attestations_aggregate" | KAttestation -> "attestation" | KPreattestation -> "preattestation" | KBallotExp -> "ballot" @@ -851,6 +885,7 @@ let pp_op_kind fmt kind = let descriptor_of ~nb_bootstrap ~max_batch_size = function | KManager -> manager_descriptor max_batch_size nb_bootstrap | KAttestation -> attestation_descriptor + | KAttestations_aggregate -> attestations_aggregate_descriptor | KPreattestation -> preattestation_descriptor | KBallotExp -> ballot_exploration_descriptor | KBallotProm -> ballot_promotion_descriptor diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.mli b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.mli index f2909c84f996..809f0342d1ef 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.mli +++ b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.mli @@ -123,6 +123,7 @@ type descriptor = { type op_kind = | KAttestation | KPreattestation + | KAttestations_aggregate | KBallotExp | KBallotProm | KProposals diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/test_sanity.ml b/src/proto_alpha/lib_protocol/test/integration/validate/test_sanity.ml index 37bbcd2e90ea..cde4789fe309 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/test_sanity.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/test_sanity.ml @@ -109,6 +109,9 @@ let covalidation_sanity () = | Single (Preattestation _), _ -> assert false | Single (Attestation _), KAttestation -> return_unit | Single (Attestation _), _ -> assert false + | Single (Attestations_aggregate _), KAttestations_aggregate -> + return_unit + | Single (Attestations_aggregate _), _ -> assert false | Single (Seed_nonce_revelation _), KNonce -> return_unit | Single (Seed_nonce_revelation _), _ -> assert false | Single (Vdf_revelation _), KVdf -> return_unit diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 8388314b15cc..56e5fef14757 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -920,6 +920,19 @@ module Consensus = struct let block_state = may_update_attestation_power info block_state power in let operation_state = add_attestation operation_state oph operation in return {info; operation_state; block_state} + + let validate_attestations_aggregate info _operation_state _block_state _oph + (_operation : Kind.attestations_aggregate operation) = + match info.mode with + | Mempool -> + (* Aggregate operations are built at baking time and shouldn't be + broadcasted between mempools. *) + tzfail Validate_errors.Consensus.Aggregate_in_mempool + | Application _ | Partial_validation _ | Construction _ -> + (* Feature flag check *) + if Constants.aggregate_attestation info.ctxt then + tzfail Validate_errors.Consensus.Aggregate_not_implemented + else tzfail Validate_errors.Consensus.Aggregate_disabled end (** {2 Validation of voting operations} @@ -1331,13 +1344,17 @@ module Anonymous = struct (op1 : kind Kind.consensus Operation.t) (op2 : kind Kind.consensus Operation.t) = let open Lwt_result_syntax in - let e1, e2, kind = + let* e1, e2, kind = match (op1.protocol_data.contents, op2.protocol_data.contents) with | Single (Preattestation e1), Single (Preattestation e2) -> - (e1, e2, Misbehaviour.Double_preattesting) + return (e1, e2, Misbehaviour.Double_preattesting) | ( Single (Attestation {consensus_content = e1; dal_content = _}), Single (Attestation {consensus_content = e2; dal_content = _}) ) -> - (e1, e2, Double_attesting) + return (e1, e2, Misbehaviour.Double_attesting) + | Single (Attestations_aggregate _), Single (Attestations_aggregate _) -> + (* TODO : https://gitlab.com/tezos/tezos/-/issues/7598 + handle denunciation for aggregates. *) + tzfail Aggregate_denunciation_not_implemented in let op1_hash = Operation.hash op1 in let op2_hash = Operation.hash op2 in @@ -2816,6 +2833,8 @@ let check_operation ?(check_signature = true) info (type kind) Consensus.check_attestation info ~check_signature operation in return_unit + | Single (Attestations_aggregate _) -> + tzfail Validate_errors.Consensus.Aggregate_in_mempool | Single (Proposals _) -> Voting.check_proposals info ~check_signature operation | Single (Ballot _) -> Voting.check_ballot info ~check_signature operation @@ -2873,6 +2892,10 @@ let check_operation_conflict (type kind) operation_conflict_state oph operation_conflict_state oph operation + | Single (Attestations_aggregate _) -> + (* This case is unreachable because the operation is assumed to be valid, + and aggregates are never valid in mempools. *) + assert false | Single (Proposals _) -> Voting.check_proposals_conflict operation_conflict_state oph operation | Single (Ballot _) -> @@ -2933,6 +2956,10 @@ let add_valid_operation operation_conflict_state oph (type kind) Consensus.add_preattestation operation_conflict_state oph operation | Single (Attestation _) -> Consensus.add_attestation operation_conflict_state oph operation + | Single (Attestations_aggregate _) -> + (* This case is unreachable because the operation is assumed to be valid, + and aggregates are never valid in mempools. *) + assert false | Single (Proposals _) -> Voting.add_proposals operation_conflict_state oph operation | Single (Ballot _) -> @@ -2981,6 +3008,10 @@ let remove_operation operation_conflict_state (type kind) Consensus.remove_preattestation operation_conflict_state operation | Single (Attestation _) -> Consensus.remove_attestation operation_conflict_state operation + | Single (Attestations_aggregate _) -> + (* This case is unreachable because the operation is assumed to be valid, + and aggregates are never valid in mempools. *) + assert false | Single (Proposals _) -> Voting.remove_proposals operation_conflict_state operation | Single (Ballot _) -> Voting.remove_ballot operation_conflict_state operation @@ -3081,6 +3112,13 @@ let validate_operation ?(check_signature = true) block_state oph operation + | Single (Attestations_aggregate _) -> + Consensus.validate_attestations_aggregate + info + operation_state + block_state + oph + operation | Single (Proposals _) -> let open Voting in let* () = check_proposals info ~check_signature operation in diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 9413fdd0976b..dcde533261f2 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -121,6 +121,9 @@ module Consensus = struct conflict : operation_conflict; } | Consensus_operation_not_allowed + | Aggregate_disabled + | Aggregate_in_mempool + | Aggregate_not_implemented let () = register_error_kind @@ -346,7 +349,40 @@ module Consensus = struct Format.fprintf ppf "Validation of consensus operation if forbidden ") Data_encoding.empty (function Consensus_operation_not_allowed -> Some () | _ -> None) - (fun () -> Consensus_operation_not_allowed) + (fun () -> Consensus_operation_not_allowed) ; + register_error_kind + `Permanent + ~id:"validate.aggregate_operation_not_allowed_in_mempool" + ~title:"Aggregate operation not allowed in mempool" + ~description:"Aggregate operations are not allowed in a mempool" + ~pp:(fun ppf () -> + Format.fprintf ppf "Aggregate operations are not allowed in a mempool") + Data_encoding.empty + (function Aggregate_in_mempool -> Some () | _ -> None) + (fun () -> Aggregate_in_mempool) ; + register_error_kind + `Permanent + ~id:"validate.aggregate_disabled" + ~title:"Aggregate operations disabled" + ~description: + "Aggregate operations are disabled by the corresponding feature flag" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Aggregate operations are disabled by the corresponding feature flag") + Data_encoding.empty + (function Aggregate_disabled -> Some () | _ -> None) + (fun () -> Aggregate_disabled) ; + register_error_kind + `Permanent + ~id:"validate.aggregate_not_implemented" + ~title:"Aggregate operations not implemented" + ~description:"Aggregate operations are not implemented yet" + ~pp:(fun ppf () -> + Format.fprintf ppf "Aggregate operations are not implemented yet") + Data_encoding.empty + (function Aggregate_not_implemented -> Some () | _ -> None) + (fun () -> Aggregate_not_implemented) end module Voting = struct @@ -711,6 +747,7 @@ module Anonymous = struct level : Raw_level.t; last_cycle : Cycle.t; } + | Aggregate_denunciation_not_implemented let () = register_error_kind @@ -891,7 +928,19 @@ module Anonymous = struct Some (kind, level, last_cycle) | _ -> None) (fun (kind, level, last_cycle) -> - Outdated_denunciation {kind; level; last_cycle}) + Outdated_denunciation {kind; level; last_cycle}) ; + register_error_kind + `Permanent + ~id:"operations.validation.aggregate_denunciation_not_implemented" + ~title:"Aggregate denunciation not implemented" + ~description:"Denunciation of aggregate operations is not yet implemented" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Denunciation of aggregate operations is not yet implemented") + Data_encoding.empty + (function Aggregate_denunciation_not_implemented -> Some () | _ -> None) + (fun () -> Aggregate_denunciation_not_implemented) type error += | Too_early_dal_denunciation of {level : Raw_level.t; current : Raw_level.t} diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 540e4e322efe..c75daf09d45a 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -79,6 +79,9 @@ module Consensus : sig kind : consensus_operation_kind; conflict : operation_conflict; } + | Aggregate_disabled + | Aggregate_in_mempool + | Aggregate_not_implemented end (** Errors that may arise while validating a voting operation. *) @@ -226,6 +229,7 @@ module Anonymous : sig delegate : Signature.Public_key_hash.t; conflict : operation_conflict; } + | Aggregate_denunciation_not_implemented end (** Errors that may arise while validating a manager operation. *) diff --git a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml index 102b03789a4b..53337355fb6f 100644 --- a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml +++ b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml @@ -204,6 +204,7 @@ module Proto_client = struct match op_result with | Preattestation_result _ -> Successful | Attestation_result _ -> Successful + | Attestations_aggregate_result _ -> Successful | Seed_nonce_revelation_result _ -> Successful | Vdf_revelation_result _ -> Successful | Double_attestation_evidence_result _ -> Successful -- GitLab From a4bbd1bbf0efd8beb1ccff849117ddcb9a741016 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Thu, 17 Oct 2024 09:35:41 +0200 Subject: [PATCH 2/3] kaitai: update struct files generated automatically using `make check-kaitai-struct-files` --- .../files/alpha__operation.ksy | 36 +++++++++++++++++++ .../files/alpha__operation__contents.ksy | 36 +++++++++++++++++++ .../files/alpha__operation__contents_list.ksy | 36 +++++++++++++++++++ .../files/alpha__operation__protocol_data.ksy | 36 +++++++++++++++++++ .../files/alpha__operation__unsigned.ksy | 36 +++++++++++++++++++ 5 files changed, 180 insertions(+) diff --git a/client-libs/kaitai-struct-files/files/alpha__operation.ksy b/client-libs/kaitai-struct-files/files/alpha__operation.ksy index e94392aaf79e..35d2fa1f99b7 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation.ksy @@ -142,6 +142,9 @@ types: - id: attestation type: attestation if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestation) + - id: attestations_aggregate + type: attestations_aggregate + if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestations_aggregate) - id: attestation_with_dal type: attestation_with_dal if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestation_with_dal) @@ -305,6 +308,12 @@ types: size: 32 - id: dal_attestation type: z + attestations_aggregate: + seq: + - id: consensus_content + type: consensus_content + - id: committee + type: committee_0 ballot: seq: - id: source @@ -390,6 +399,32 @@ types: size: 32 - id: number_of_ticks type: s8be + committee: + seq: + - id: committee_entries + type: committee_entries + repeat: eos + committee_0: + seq: + - id: len_committee + type: u4be + valid: + max: 1073741823 + - id: committee + type: committee + size: len_committee + committee_entries: + seq: + - id: committee_elt + type: u2be + consensus_content: + seq: + - id: level + type: s4be + - id: round + type: s4be + - id: block_payload_hash + size: 32 contents_and_signature_prefix_entries: seq: - id: alpha__operation__alpha__contents_or_signature_prefix @@ -1970,6 +2005,7 @@ enums: 21: attestation 23: attestation_with_dal 24: dal_entrapment_evidence + 31: attestations_aggregate 107: reveal 108: transaction 109: origination diff --git a/client-libs/kaitai-struct-files/files/alpha__operation__contents.ksy b/client-libs/kaitai-struct-files/files/alpha__operation__contents.ksy index ac4011dc123c..4cf6ecdfb976 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation__contents.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation__contents.ksy @@ -131,6 +131,9 @@ types: - id: attestation type: attestation if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation) + - id: attestations_aggregate + type: attestations_aggregate + if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestations_aggregate) - id: attestation_with_dal type: attestation_with_dal if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation_with_dal) @@ -294,6 +297,12 @@ types: size: 32 - id: dal_attestation type: z + attestations_aggregate: + seq: + - id: consensus_content + type: consensus_content + - id: committee + type: committee_0 ballot: seq: - id: source @@ -371,6 +380,32 @@ types: size: 32 - id: number_of_ticks type: s8be + committee: + seq: + - id: committee_entries + type: committee_entries + repeat: eos + committee_0: + seq: + - id: len_committee + type: u4be + valid: + max: 1073741823 + - id: committee + type: committee + size: len_committee + committee_entries: + seq: + - id: committee_elt + type: u2be + consensus_content: + seq: + - id: level + type: s4be + - id: round + type: s4be + - id: block_payload_hash + size: 32 dal__page__proof: seq: - id: dal_page_id @@ -1947,6 +1982,7 @@ enums: 21: attestation 23: attestation_with_dal 24: dal_entrapment_evidence + 31: attestations_aggregate 107: reveal 108: transaction 109: origination diff --git a/client-libs/kaitai-struct-files/files/alpha__operation__contents_list.ksy b/client-libs/kaitai-struct-files/files/alpha__operation__contents_list.ksy index 8d5e4fba1167..f024d3a35a89 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation__contents_list.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation__contents_list.ksy @@ -131,6 +131,9 @@ types: - id: attestation type: attestation if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation) + - id: attestations_aggregate + type: attestations_aggregate + if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestations_aggregate) - id: attestation_with_dal type: attestation_with_dal if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation_with_dal) @@ -298,6 +301,12 @@ types: size: 32 - id: dal_attestation type: z + attestations_aggregate: + seq: + - id: consensus_content + type: consensus_content + - id: committee + type: committee_0 ballot: seq: - id: source @@ -375,6 +384,32 @@ types: size: 32 - id: number_of_ticks type: s8be + committee: + seq: + - id: committee_entries + type: committee_entries + repeat: eos + committee_0: + seq: + - id: len_committee + type: u4be + valid: + max: 1073741823 + - id: committee + type: committee + size: len_committee + committee_entries: + seq: + - id: committee_elt + type: u2be + consensus_content: + seq: + - id: level + type: s4be + - id: round + type: s4be + - id: block_payload_hash + size: 32 dal__page__proof: seq: - id: dal_page_id @@ -1951,6 +1986,7 @@ enums: 21: attestation 23: attestation_with_dal 24: dal_entrapment_evidence + 31: attestations_aggregate 107: reveal 108: transaction 109: origination diff --git a/client-libs/kaitai-struct-files/files/alpha__operation__protocol_data.ksy b/client-libs/kaitai-struct-files/files/alpha__operation__protocol_data.ksy index 9e7c8d3d8c09..668c27c3d91c 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation__protocol_data.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation__protocol_data.ksy @@ -142,6 +142,9 @@ types: - id: attestation type: attestation if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestation) + - id: attestations_aggregate + type: attestations_aggregate + if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestations_aggregate) - id: attestation_with_dal type: attestation_with_dal if: (alpha__operation__alpha__contents_or_signature_prefix_tag == alpha__operation__alpha__contents_or_signature_prefix_tag::attestation_with_dal) @@ -305,6 +308,12 @@ types: size: 32 - id: dal_attestation type: z + attestations_aggregate: + seq: + - id: consensus_content + type: consensus_content + - id: committee + type: committee_0 ballot: seq: - id: source @@ -390,6 +399,32 @@ types: size: 32 - id: number_of_ticks type: s8be + committee: + seq: + - id: committee_entries + type: committee_entries + repeat: eos + committee_0: + seq: + - id: len_committee + type: u4be + valid: + max: 1073741823 + - id: committee + type: committee + size: len_committee + committee_entries: + seq: + - id: committee_elt + type: u2be + consensus_content: + seq: + - id: level + type: s4be + - id: round + type: s4be + - id: block_payload_hash + size: 32 contents_and_signature_prefix_entries: seq: - id: alpha__operation__alpha__contents_or_signature_prefix @@ -1970,6 +2005,7 @@ enums: 21: attestation 23: attestation_with_dal 24: dal_entrapment_evidence + 31: attestations_aggregate 107: reveal 108: transaction 109: origination diff --git a/client-libs/kaitai-struct-files/files/alpha__operation__unsigned.ksy b/client-libs/kaitai-struct-files/files/alpha__operation__unsigned.ksy index d64450b3484c..091ff1eeea10 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation__unsigned.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation__unsigned.ksy @@ -131,6 +131,9 @@ types: - id: attestation type: attestation if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation) + - id: attestations_aggregate + type: attestations_aggregate + if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestations_aggregate) - id: attestation_with_dal type: attestation_with_dal if: (alpha__operation__alpha__contents_tag == alpha__operation__alpha__contents_tag::attestation_with_dal) @@ -301,6 +304,12 @@ types: size: 32 - id: dal_attestation type: z + attestations_aggregate: + seq: + - id: consensus_content + type: consensus_content + - id: committee + type: committee_0 ballot: seq: - id: source @@ -378,6 +387,32 @@ types: size: 32 - id: number_of_ticks type: s8be + committee: + seq: + - id: committee_entries + type: committee_entries + repeat: eos + committee_0: + seq: + - id: len_committee + type: u4be + valid: + max: 1073741823 + - id: committee + type: committee + size: len_committee + committee_entries: + seq: + - id: committee_elt + type: u2be + consensus_content: + seq: + - id: level + type: s4be + - id: round + type: s4be + - id: block_payload_hash + size: 32 contents_entries: seq: - id: alpha__operation__alpha__contents @@ -1958,6 +1993,7 @@ enums: 21: attestation 23: attestation_with_dal 24: dal_entrapment_evidence + 31: attestations_aggregate 107: reveal 108: transaction 109: origination -- GitLab From fe61a199fc05cc88ccd8d36f2dd13e593cf95832 Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Tue, 8 Oct 2024 08:57:09 +0200 Subject: [PATCH 3/3] proto-unit-test: add tests for attestation aggregate --- manifest/product_octez.ml | 47 +++++----- .../test/integration/consensus/dune | 3 +- .../integration/consensus/test_aggregate.ml | 91 +++++++++++++++++++ 3 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml diff --git a/manifest/product_octez.ml b/manifest/product_octez.ml index f53027580409..923d21f972eb 100644 --- a/manifest/product_octez.ml +++ b/manifest/product_octez.ml @@ -5502,29 +5502,34 @@ end = struct let name_dash = Name.name_dash name in let number = Name.number name in let path = Name.base_path name in + let modules = + [ + ("test_baking", true); + ("test_consensus_key", true); + ("test_deactivation", true); + ("test_delegation", true); + ("test_double_baking", true); + ("test_double_attestation", N.(number >= 018)); + ("test_double_endorsement", N.(number <= 017)); + ("test_double_preattestation", N.(number >= 018)); + ("test_double_preendorsement", N.(number <= 017)); + ("test_attestation", N.(number >= 018)); + ("test_endorsement", N.(number <= 017)); + ("test_frozen_deposits", true); + ("test_helpers_rpcs", true); + ("test_participation", true); + ("test_preattestation_functor", N.(number >= 018)); + ("test_preendorsement_functor", N.(number <= 017)); + ("test_preattestation", N.(number >= 018)); + ("test_preendorsement", N.(number <= 017)); + ("test_seed", true); + ("test_aggregate", N.(number >= 022)); + ] + |> conditional_list + in let _integration_consensus = tezt - [ - "test_baking"; - "test_consensus_key"; - "test_deactivation"; - "test_delegation"; - "test_double_baking"; - (if N.(number >= 018) then "test_double_attestation" - else "test_double_endorsement"); - (if N.(number >= 018) then "test_double_preattestation" - else "test_double_preendorsement"); - (if N.(number >= 018) then "test_attestation" - else "test_endorsement"); - "test_frozen_deposits"; - "test_helpers_rpcs"; - "test_participation"; - (if N.(number >= 018) then "test_preattestation_functor" - else "test_preendorsement_functor"); - (if N.(number >= 018) then "test_preattestation" - else "test_preendorsement"); - "test_seed"; - ] + modules ~path:(path // "lib_protocol/test/integration/consensus") ~with_macos_security_framework:true ~opam:(sf "tezos-protocol-%s-tests" name_dash) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/dune b/src/proto_alpha/lib_protocol/test/integration/consensus/dune index 10c3636ec861..7003ee398802 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/dune +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/dune @@ -41,7 +41,8 @@ test_participation test_preattestation_functor test_preattestation - test_seed)) + test_seed + test_aggregate)) (executable (name main) 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 new file mode 100644 index 000000000000..feaa83e51025 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_aggregate.ml @@ -0,0 +1,91 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Protocol (aggregate) + Invocation: dune exec src/proto_alpha/lib_protocol/test/integration/consensus/main.exe \ + -- --file test_aggregate.ml +*) + +open Protocol + +(* Init genesis with 5 accounts including at least 2 BLS *) +let init_genesis_with_some_bls_accounts ?policy ?dal_enable + ?aggregate_attestation () = + let open Lwt_result_syntax in + let*? random_accounts = Account.generate_accounts 3 in + let*? bls_accounts = + List.init ~when_negative_length:[] 2 (fun _ -> + Account.new_account ~algo:Signature.Bls ()) + in + let bootstrap_accounts = + Account.make_bootstrap_accounts (random_accounts @ bls_accounts) + in + let* genesis = + Block.genesis + ?dal_enable + ?aggregate_attestation + ~consensus_threshold_size:0 + bootstrap_accounts + in + let* b = Block.bake ?policy genesis in + return (genesis, b) + +let aggregate_in_mempool_error = function + | Validate_errors.Consensus.Aggregate_in_mempool -> true + | _ -> false + +let aggregate_disabled_error = function + | Validate_errors.Consensus.Aggregate_disabled -> true + | _ -> false + +let aggregate_unimplemented_error = function + | Validate_errors.Consensus.Aggregate_not_implemented -> true + | _ -> false + +let test_aggregate_feature_flag_enabled () = + let open Lwt_result_syntax in + let* _genesis, attested_block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:true () + in + Consensus_helpers.test_consensus_operation_all_modes_different_outcomes + ~loc:__LOC__ + ~attested_block + ~application_error:aggregate_unimplemented_error + ~construction_error:aggregate_unimplemented_error + ~mempool_error:aggregate_in_mempool_error + Aggregate + +let test_aggregate_feature_flag_disabled () = + let open Lwt_result_syntax in + let* _genesis, attested_block = + init_genesis_with_some_bls_accounts ~aggregate_attestation:false () + in + Consensus_helpers.test_consensus_operation_all_modes_different_outcomes + ~loc:__LOC__ + ~attested_block + ~application_error:aggregate_disabled_error + ~construction_error:aggregate_disabled_error + ~mempool_error:aggregate_in_mempool_error + Aggregate + +let tests = + [ + Tztest.tztest + "test_aggregate_feature_flag_enabled" + `Quick + test_aggregate_feature_flag_enabled; + Tztest.tztest + "test_aggregate_feature_flag_disabled" + `Quick + test_aggregate_feature_flag_disabled; + ] + +let () = + Alcotest_lwt.run ~__FILE__ Protocol.name [("aggregate", tests)] + |> Lwt_main.run -- GitLab