From a3ada83fa9071fc1f1ded8dc03f0c493be5ae5df Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Thu, 6 Mar 2025 13:11:16 +0100 Subject: [PATCH 1/3] proto: add aggregation related new errors --- .../lib_protocol/validate_errors.ml | 25 ++++++++++++++++++- .../lib_protocol/validate_errors.mli | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index dcde533261f2..3ee59a7cde96 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -124,6 +124,8 @@ module Consensus = struct | Aggregate_disabled | Aggregate_in_mempool | Aggregate_not_implemented + | Non_bls_key_in_aggregate + | Public_key_aggregation_failure let () = register_error_kind @@ -382,7 +384,28 @@ module Consensus = struct Format.fprintf ppf "Aggregate operations are not implemented yet") Data_encoding.empty (function Aggregate_not_implemented -> Some () | _ -> None) - (fun () -> Aggregate_not_implemented) + (fun () -> Aggregate_not_implemented) ; + register_error_kind + `Permanent + ~id:"validate.non_bls_key_in_aggregate" + ~title:"Non BLS key in aggregate" + ~description:"Non Bls key in a consensus aggregate operation" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Ill-formed aggregation : a slot owner doesn't own a BLS key") + Data_encoding.empty + (function Non_bls_key_in_aggregate -> Some () | _ -> None) + (fun () -> Non_bls_key_in_aggregate) ; + register_error_kind + `Permanent + ~id:"validate.public_key_aggregation_failure" + ~title:"Public key aggregation failure" + ~description:"Public key aggregation failed" + ~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) 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 c75daf09d45a..4d0d0ff6d48c 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -82,6 +82,8 @@ module Consensus : sig | Aggregate_disabled | Aggregate_in_mempool | Aggregate_not_implemented + | Non_bls_key_in_aggregate + | Public_key_aggregation_failure end (** Errors that may arise while validating a voting operation. *) -- GitLab From 9959a7d6ed9662535eb89960956ca939bb1d43ba Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Fri, 7 Mar 2025 15:21:08 +0100 Subject: [PATCH 2/3] proto: split check_block_attestation --- src/proto_alpha/lib_protocol/validate.ml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index c6a7dee2aad8..edfe18d61fcd 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -744,11 +744,9 @@ module Consensus = struct (** Attestation checks for all modes that involve a block: Application, Partial_validation, and Construction. - Checks regarding the DAL content are done separately. - - Return the slot owner's consensus key and voting power. *) + Checks regarding the DAL content are done separately. *) let check_block_attestation vi consensus_info - {level; round; block_payload_hash = bph; slot} = + {level; round; block_payload_hash = bph; slot = _} = let open Lwt_result_syntax in let*? expected_payload_hash = match Consensus.attestation_branch vi.ctxt with @@ -765,10 +763,7 @@ module Consensus = struct let*? () = check_level kind consensus_info.predecessor_level level in let*? () = check_round kind consensus_info.predecessor_round round in let*? () = check_payload_hash kind expected_payload_hash bph in - let*? consensus_key, voting_power, _dal_power = - get_delegate_details consensus_info.attestation_slot_map kind slot - in - return (consensus_key, voting_power) + return_unit let check_attestation vi ~check_signature (operation : Kind.attestation operation) = @@ -784,7 +779,16 @@ module Consensus = struct let* consensus_key, voting_power = match vi.mode with | Application _ | Partial_validation _ | Construction _ -> - check_block_attestation vi consensus_info consensus_content + let* () = + check_block_attestation vi consensus_info consensus_content + in + let*? consensus_key, voting_power, _dal_power = + get_delegate_details + consensus_info.attestation_slot_map + Attestation + consensus_content.slot + in + return (consensus_key, voting_power) | Mempool -> check_mempool_consensus vi -- GitLab From 05c84f0e4a1b318f3476a05899dd7772a878baed Mon Sep 17 00:00:00 2001 From: Adam Allombert-Goget Date: Fri, 7 Mar 2025 15:22:09 +0100 Subject: [PATCH 3/3] proto: implement attestations aggregate validation --- src/proto_alpha/lib_protocol/validate.ml | 140 +++++++++++++++++++++-- 1 file changed, 132 insertions(+), 8 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index edfe18d61fcd..798c7b2e2c85 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -947,18 +947,141 @@ module Consensus = struct 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) = + let check_attestation_aggregate_signature info public_keys + ({shell; protocol_data = {contents = Single content; signature}} : + Kind.attestations_aggregate Operation.t) = + let open Lwt_result_syntax in + let (Attestations_aggregate {consensus_content; _}) = content in + let {level; round; block_payload_hash} : consensus_aggregate_content = + consensus_content + in + (* We disable subgroup check (for better performances) since public keys are + retreived from the context and assumed valid. *) + match Bls.aggregate_public_key_opt ~subgroup_check:false public_keys with + | None -> + (* This is never supposed to happen since keys are assumed valid. *) + tzfail Validate_errors.Consensus.Public_key_aggregation_failure + | Some public_keys_aggregate -> + (* Reconstructing an attestation to match the content signed by each + delegate. Fields slot and dal_content are filled with dummy values as + they are not part of the signed payload *) + let consensus_content = + {slot = Slot.zero; level; round; block_payload_hash} + in + let contents = + Single (Attestation {consensus_content; dal_content = None}) + in + let attestation : Kind.attestation operation = + {shell; protocol_data = {contents; signature}} + in + let*? () = + Operation.check_signature + info.ctxt + (Bls public_keys_aggregate) + info.chain_id + attestation + in + return_unit + + (* Check conflicts and register each attestations in the conflict map *) + let handle_attestation_aggregate_conflicts validation_state oph + ({shell; protocol_data = {contents = Single content; _}} : + Kind.attestations_aggregate operation) = + let open Lwt_result_syntax in + let (Attestations_aggregate {consensus_content; committee}) = content in + let {level; round; block_payload_hash} : consensus_aggregate_content = + consensus_content + in + List.fold_left_es + (fun {info; operation_state; block_state} slot -> + let attestation : Kind.attestation operation = + let consensus_content = {slot; level; round; block_payload_hash} in + let contents = + Single (Attestation {consensus_content; dal_content = None}) + in + {shell; protocol_data = {contents; signature = None}} + in + let*? () = + check_attestation_conflict operation_state oph attestation + |> wrap_attestation_conflict + in + let operation_state = add_attestation operation_state oph attestation in + return {info; operation_state; block_state}) + validation_state + committee + + let validate_attestations_aggregate ~check_signature info operation_state + block_state oph + ({protocol_data = {contents = Single content; _}; _} as op : + Kind.attestations_aggregate operation) = + let open Lwt_result_syntax in match info.mode with | Mempool -> - (* Aggregate operations are built at baking time and shouldn't be - broadcasted between mempools. *) + (* Attestations_aggregate operations are built at baking time and + should not be propagated 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 + let*? () = + (* Attestations_aggregates are currently under feature flag *) + error_unless + (Constants.aggregate_attestation info.ctxt) + Validate_errors.Consensus.Aggregate_disabled + in + let*? consensus_info = + Option.value_e + ~error:(trace_of_error Consensus_operation_not_allowed) + info.consensus_info + in + let (Attestations_aggregate {consensus_content; committee}) = content in + let {level; round; block_payload_hash} : consensus_aggregate_content = + consensus_content + in + (* Check level, round and block_payload_hash + (uses a dummy slot value) *) + let* () = + check_block_attestation + info + consensus_info + {level; round; block_payload_hash; slot = Slot.zero} + in + (* Retreive public keys and compute total voting power *) + let* public_keys, total_voting_power = + List.fold_left_es + (fun (public_keys, total_voting_power) slot -> + (* Lookup the slot owner *) + let*? consensus_key, power, _ = + get_delegate_details + consensus_info.attestation_slot_map + Attestation + slot + in + let* () = + check_delegate_is_not_forbidden info.ctxt consensus_key.delegate + in + match consensus_key.consensus_pk with + | Bls pk -> return (pk :: public_keys, power + total_voting_power) + | _ -> tzfail Validate_errors.Consensus.Non_bls_key_in_aggregate) + ([], 0) + committee + in + (* Check signature *) + let* () = + if check_signature then + check_attestation_aggregate_signature info public_keys op + else return_unit + in + (* Check conflicts and register each attestation in the conflict map *) + let* validation_state = + handle_attestation_aggregate_conflicts + {info; operation_state; block_state} + oph + op + in + (* Increment block voting power *) + let block_state = + may_update_attestation_power info block_state total_voting_power + in + return {validation_state with block_state} end (** {2 Validation of voting operations} @@ -3158,6 +3281,7 @@ let validate_operation ?(check_signature = true) operation | Single (Attestations_aggregate _) -> Consensus.validate_attestations_aggregate + ~check_signature info operation_state block_state -- GitLab