From 439554f04ea151ae542fed14d024c5b5ea62abaa Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 14 May 2025 13:59:58 +0200 Subject: [PATCH 1/9] Proto/abaab: check level in alpha_context --- src/proto_alpha/lib_protocol/alpha_context.ml | 3 +++ src/proto_alpha/lib_protocol/alpha_context.mli | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index df9752cc6165..08c3eac02073 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -625,6 +625,9 @@ module Delegate = struct end module Stake_distribution = struct + let check_all_bakers_attest_at_level = + Delegate_sampler.check_all_bakers_attest_at_level + let baking_rights_owner = Delegate_sampler.baking_rights_owner let slot_owner = Delegate_sampler.slot_owner diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index e29efc3d8bd3..c337d571a3d9 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -5204,6 +5204,8 @@ end (** This module re-exports definitions from {!Stake_storage}, {!Delegate_storage} and {!Delegate}. *) module Stake_distribution : sig + val check_all_bakers_attest_at_level : context -> Level.t -> bool + val baking_rights_owner : context -> Level.t -> -- GitLab From ed13299bfccf084182e1a5e024697660091650b5 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Tue, 29 Jul 2025 14:52:31 +0200 Subject: [PATCH 2/9] Proto: refactor slot maps --- src/proto_alpha/lib_plugin/RPC.ml | 30 ++++--- .../lib_protocol/alpha_context.mli | 4 +- src/proto_alpha/lib_protocol/apply.ml | 49 +++++++---- src/proto_alpha/lib_protocol/baking.ml | 28 ++++-- src/proto_alpha/lib_protocol/baking.mli | 2 +- .../lib_protocol/delegate_consensus_key.ml | 6 ++ .../lib_protocol/delegate_consensus_key.mli | 7 ++ src/proto_alpha/lib_protocol/raw_context.ml | 30 ++++--- src/proto_alpha/lib_protocol/raw_context.mli | 25 ++++-- src/proto_alpha/lib_protocol/validate.ml | 85 +++++++++---------- 10 files changed, 165 insertions(+), 101 deletions(-) diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index 13a6247505a4..706e15cc1a9b 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -3897,17 +3897,27 @@ module Attestation_rights = struct let rights = Slot.Map.fold (fun first_slot - ( { - Consensus_key.delegate; - consensus_pk = _; - consensus_pkh = consensus_key; - companion_pk = _; - companion_pkh = _; - }, - attestation_power, - _dal_power ) + ({ + consensus_key = + { + Consensus_key.delegate; + consensus_pk = _; + consensus_pkh = consensus_key; + companion_pk = _; + companion_pkh = _; + }; + attesting_power; + dal_power = _; + } : + Consensus_key.power) acc -> - {delegate; consensus_key; first_slot; attestation_power} :: acc) + { + delegate; + consensus_key; + first_slot; + attestation_power = attesting_power; + } + :: acc) rights [] in diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index c337d571a3d9..2de22af12c8f 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2249,6 +2249,8 @@ module Consensus_key : sig consensus_pkh : Signature.Public_key_hash.t; } + type power = {consensus_key : pk; attesting_power : int; dal_power : int} + val encoding : t Data_encoding.t val zero : t @@ -5502,7 +5504,7 @@ module Consensus : sig and type 'a level_map := 'a Level.Map.t and type slot_set := Slot.Set.t and type round := Round.t - and type consensus_pk := Consensus_key.pk + and type consensus_power := Consensus_key.power (** [store_attestation_branch context branch] sets the "attestation branch" (see {!Storage.Tenderbake.Attestation_branch} to [branch] in both the disk diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index b8e5d86d6220..6da808a1731b 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2293,8 +2293,7 @@ let find_in_slot_map slot slot_map = | None -> (* This should not happen: operation validation should have failed. *) tzfail Faulty_validation_wrong_slot - | Some (consensus_key, power, dal_power) -> - return (consensus_key, power, dal_power)) + | Some x -> return x) let record_preattestation ctxt (mode : mode) (content : consensus_content) : (context * Kind.preattestation contents_result_list) tzresult Lwt.t = @@ -2320,7 +2319,7 @@ let record_preattestation ctxt (mode : mode) (content : consensus_content) : in match mode with | Application _ | Full_construction _ -> - let*? consensus_key, power, _dal_power = + let*? {consensus_key; attesting_power = power; dal_power = _} = find_in_slot_map content.slot (Consensus.allowed_preattestations ctxt) in let*? ctxt = @@ -2371,14 +2370,17 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) in match mode with | Application _ | Full_construction _ -> - let*? consensus_key, power, dal_power = + let*? {consensus_key; attesting_power; dal_power} = find_in_slot_map consensus.slot (Consensus.allowed_attestations ctxt) in let*? ctxt = - Consensus.record_attestation ctxt ~initial_slot:consensus.slot ~power + Consensus.record_attestation + ctxt + ~initial_slot:consensus.slot + ~power:attesting_power in let*? ctxt = record_dal_content ctxt consensus.slot ~dal_power dal in - return (ctxt, mk_attestation_result consensus_key power) + return (ctxt, mk_attestation_result consensus_key attesting_power) | Partial_construction _ -> (* In mempool mode, attestations are allowed for various levels and rounds. We do not record attestations because we could get @@ -2404,16 +2406,25 @@ let record_attestations_aggregate ctxt (mode : mode) committee : let open Result_syntax in List.fold_left_e (fun (ctxt, consensus_keys, consensus_power) (slot, dal) -> - let* {delegate; consensus_pkh; _}, power, dal_power = + let* { + consensus_key = {delegate; consensus_pkh; _}; + attesting_power; + dal_power; + } = find_in_slot_map slot slot_map in let* ctxt = - Consensus.record_attestation ctxt ~initial_slot:slot ~power + Consensus.record_attestation + ctxt + ~initial_slot:slot + ~power:attesting_power in let* ctxt = record_dal_content ctxt slot ~dal_power dal in let key = ({delegate; consensus_pkh} : Consensus_key.t) in return - (ctxt, (key, power) :: consensus_keys, power + consensus_power)) + ( ctxt, + (key, attesting_power) :: consensus_keys, + attesting_power + consensus_power )) (ctxt, [], 0) committee in @@ -2453,19 +2464,25 @@ let record_preattestations_aggregate ctxt (mode : mode) let open Result_syntax in List.fold_left_e (fun (ctxt, consensus_keys, consensus_power) slot -> - let* {delegate; consensus_pkh; _}, power, _dal_power = + let* { + consensus_key = {delegate; consensus_pkh; _}; + attesting_power; + dal_power = _; + } = find_in_slot_map slot slot_map in let* ctxt = Consensus.record_preattestation ctxt ~initial_slot:slot - ~power + ~power:attesting_power round in let key = ({delegate; consensus_pkh} : Consensus_key.t) in return - (ctxt, (key, power) :: consensus_keys, power + consensus_power)) + ( ctxt, + (key, attesting_power) :: consensus_keys, + attesting_power + consensus_power )) (ctxt, [], 0) committee in @@ -2974,7 +2991,7 @@ let record_attesting_participation ctxt dal_attestation = | Some validators -> Slot.Map.fold_es (fun initial_slot - ((consensus_pk : Consensus_key.pk), power, dal_power) + ({consensus_key; attesting_power; dal_power} : Consensus_key.power) ctxt -> let participation = if Slot.Set.mem initial_slot (Consensus.attestations_seen ctxt) then @@ -2984,13 +3001,13 @@ let record_attesting_participation ctxt dal_attestation = let* ctxt = Delegate.record_attesting_participation ctxt - ~delegate:consensus_pk.delegate + ~delegate:consensus_key.delegate ~participation - ~attesting_power:power + ~attesting_power in Dal_apply.record_participation ctxt - consensus_pk.delegate + consensus_key.delegate initial_slot ~dal_power dal_attestation) diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index 3bad0551af7f..c6237c4c01e4 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -108,7 +108,8 @@ let attesting_rights (ctxt : t) level = (ctxt, Signature.Public_key_hash.Map.empty) slots -let attesting_rights_by_first_slot ctxt level = +let attesting_rights_by_first_slot ctxt level : + (t * Consensus_key.power Slot.Map.t) tzresult Lwt.t = let open Lwt_result_syntax in let*? slots = Slot.Range.create ~min:0 ~count:(Constants.consensus_committee_size ctxt) @@ -117,19 +118,19 @@ let attesting_rights_by_first_slot ctxt level = let* ctxt, (_, slots_map) = Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> - let+ ctxt, consensus_pk = + let+ ctxt, consensus_key = Stake_distribution.slot_owner ctxt level slot in let initial_slot, delegates_map = match Signature.Public_key_hash.Map.find - consensus_pk.delegate + consensus_key.delegate delegates_map with | None -> ( slot, Signature.Public_key_hash.Map.add - consensus_pk.delegate + consensus_key.delegate slot delegates_map ) | Some initial_slot -> (initial_slot, delegates_map) @@ -143,9 +144,22 @@ let attesting_rights_by_first_slot ctxt level = Slot.Map.update initial_slot (function - | None -> Some (consensus_pk, 1, in_dal_committee) - | Some (consensus_pk, count, dal_count) -> - Some (consensus_pk, count + 1, dal_count + in_dal_committee)) + | None -> + Some + { + consensus_key; + attesting_power = 1; + dal_power = in_dal_committee; + } + | Some + ({consensus_key; attesting_power; dal_power} : + Consensus_key.power) -> + Some + { + consensus_key; + attesting_power = attesting_power + 1; + dal_power = dal_power + in_dal_committee; + }) slots_map in (ctxt, (delegates_map, slots_map))) diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index 540ec98a72c9..ed491d8bfaf7 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -59,7 +59,7 @@ val attesting_rights : val attesting_rights_by_first_slot : context -> Level.t -> - (context * (Consensus_key.pk * int * int) Slot.Map.t) tzresult Lwt.t + (context * Consensus_key.power Slot.Map.t) tzresult Lwt.t (** Computes the bonus baking reward depending on the attestation power. *) val bonus_baking_reward : context -> attestation_power:int -> Tez.t tzresult diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index a8220c2e955b..bac79afbac72 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -126,6 +126,12 @@ type pk = Raw_context.consensus_pk = { companion_pkh : Bls.Public_key_hash.t option; } +type power = Raw_context.consensus_power = { + consensus_key : pk; + attesting_power : int; + dal_power : int; +} + type t = { delegate : Signature.Public_key_hash.t; consensus_pkh : Signature.Public_key_hash.t; diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index 18b7addb9fbb..45ceafd0f645 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -50,6 +50,13 @@ type pk = Raw_context.consensus_pk = { companion_pkh : Bls.Public_key_hash.t option; } +(** The attesting and dal power related to the associated consensus key *) +type power = Raw_context.consensus_power = { + consensus_key : pk; + attesting_power : int; + dal_power : int; +} + (** The public key hash of a consensus key and the associated delegate. *) type t = { delegate : Signature.Public_key_hash.t; diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index ec2b289a4d2d..3ae47bb0f310 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -91,6 +91,12 @@ let consensus_pk_encoding = (opt "delegate" Signature.Public_key_hash.encoding) (opt "companion_pk" Bls.Public_key.encoding)) +type consensus_power = { + consensus_key : consensus_pk; + attesting_power : int; + dal_power : int; +} + module Raw_consensus = struct (** Consensus operations are indexed by their [initial slots]. Given a delegate, the [initial slot] is the lowest slot assigned to @@ -99,21 +105,20 @@ module Raw_consensus = struct type t = { current_attestation_power : int; (** Number of attestation slots recorded for the current block. *) - allowed_attestations : (consensus_pk * int * int) Slot_repr.Map.t option; + allowed_attestations : consensus_power Slot_repr.Map.t option; (** Attestations rights for the current block. Only an attestation for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its consensus attestation power and DAL attestation power. This is [None] only in mempool mode. *) - allowed_preattestations : (consensus_pk * int * int) Slot_repr.Map.t option; + allowed_preattestations : consensus_power Slot_repr.Map.t option; (** Preattestations rights for the current block. Only a preattestation for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its consensus attestation power and DAL attestation power. This is [None] only in mempool mode, or in application mode when there is no locked round (so the block cannot contain any preattestations). *) - allowed_consensus : - (consensus_pk * int * int) Slot_repr.Map.t Level_repr.Map.t option; + allowed_consensus : consensus_power Slot_repr.Map.t Level_repr.Map.t option; (** In mempool mode, hold delegates minimal slots for all allowed levels. [None] in all other modes. *) forbidden_delegates : Signature.Public_key_hash.Set.t; @@ -2141,14 +2146,13 @@ module type CONSENSUS = sig type round - type consensus_pk + type consensus_power - val allowed_attestations : t -> (consensus_pk * int * int) slot_map option + val allowed_attestations : t -> consensus_power slot_map option - val allowed_preattestations : t -> (consensus_pk * int * int) slot_map option + val allowed_preattestations : t -> consensus_power slot_map option - val allowed_consensus : - t -> (consensus_pk * int * int) slot_map level_map option + val allowed_consensus : t -> consensus_power slot_map level_map option val forbidden_delegates : t -> Signature.Public_key_hash.Set.t @@ -2158,9 +2162,9 @@ module type CONSENSUS = sig val initialize_consensus_operation : t -> - allowed_attestations:(consensus_pk * int * int) slot_map option -> - allowed_preattestations:(consensus_pk * int * int) slot_map option -> - allowed_consensus:(consensus_pk * int * int) slot_map level_map option -> + allowed_attestations:consensus_power slot_map option -> + allowed_preattestations:consensus_power slot_map option -> + allowed_consensus:consensus_power slot_map level_map option -> t val record_attestation : t -> initial_slot:slot -> power:int -> t tzresult @@ -2193,7 +2197,7 @@ module Consensus : and type 'a level_map := 'a Level_repr.Map.t and type slot_set := Slot_repr.Set.t and type round := Round_repr.t - and type consensus_pk := consensus_pk = struct + and type consensus_power := consensus_power = struct let[@inline] update_consensus_with ctxt f = {ctxt with back = {ctxt.back with consensus = f ctxt.back.consensus}} diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 1c649bd923f3..6fc8a8ccea04 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -355,6 +355,12 @@ module Internal_for_tests : sig val add_cycles : t -> int -> t end +type consensus_power = { + consensus_key : consensus_pk; + attesting_power : int; + dal_power : int; +} + module type CONSENSUS = sig type t @@ -368,21 +374,22 @@ module type CONSENSUS = sig type round - type consensus_pk + (** Info on the power of a given member of the consensus committee. + Contains a consensus_pk, its attesting power and its DAL power. *) + type consensus_power (** Returns a map where from the initial slot of each attester in the TB committee for a given level, to the attester's public key and its consensus power and DAL power. *) - val allowed_attestations : t -> (consensus_pk * int * int) slot_map option + val allowed_attestations : t -> consensus_power slot_map option (** See {!allowed_attestations}. *) - val allowed_preattestations : t -> (consensus_pk * int * int) slot_map option + val allowed_preattestations : t -> consensus_power slot_map option (** Returns a map that associates a level with a slot map. The slot map links a delegate's public key to a tuple containing (minimal_slot, voting_power, dal_power). See {!allowed_attestations} *) - val allowed_consensus : - t -> (consensus_pk * int * int) slot_map level_map option + val allowed_consensus : t -> consensus_power slot_map level_map option (** Returns the set of delegates that are not allowed to bake or attest blocks; i.e., delegates which have zero frozen deposit @@ -401,9 +408,9 @@ module type CONSENSUS = sig operation. *) val initialize_consensus_operation : t -> - allowed_attestations:(consensus_pk * int * int) slot_map option -> - allowed_preattestations:(consensus_pk * int * int) slot_map option -> - allowed_consensus:(consensus_pk * int * int) slot_map level_map option -> + allowed_attestations:consensus_power slot_map option -> + allowed_preattestations:consensus_power slot_map option -> + allowed_consensus:consensus_power slot_map level_map option -> t (** [record_attestation ctx ~initial_slot ~power] records an @@ -465,7 +472,7 @@ module Consensus : and type 'a level_map := 'a Level_repr.Map.t and type slot_set := Slot_repr.Set.t and type round := Round_repr.t - and type consensus_pk := consensus_pk + and type consensus_power := consensus_power module Sc_rollup_in_memory_inbox : sig val current_messages : t -> Sc_rollup_inbox_merkelized_payload_hashes_repr.t diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 76f9ee5770c7..67c6b58ed95e 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -30,10 +30,9 @@ open Alpha_context type consensus_info = { predecessor_level : Raw_level.t; predecessor_round : Round.t; - preattestation_slot_map : (Consensus_key.pk * int * int) Slot.Map.t option; - attestation_slot_map : (Consensus_key.pk * int * int) Slot.Map.t option; - consensus_slot_map : - (Consensus_key.pk * int * int) Slot.Map.t Level.Map.t option; + preattestation_slot_map : Consensus_key.power Slot.Map.t option; + attestation_slot_map : Consensus_key.power Slot.Map.t option; + consensus_slot_map : Consensus_key.power Slot.Map.t Level.Map.t option; } let init_consensus_info ctxt (predecessor_level, predecessor_round) = @@ -604,8 +603,8 @@ module Consensus = struct Returns the corresponding delegate's consensus key and attesting power. *) let check_mempool_consensus vi consensus_info kind {level; slot; _} = - let open Lwt_result_syntax in - let*? () = + let open Result_syntax in + let* () = if Raw_level.(succ level < consensus_info.predecessor_level) then let expected = consensus_info.predecessor_level and provided = level in result_error @@ -621,10 +620,8 @@ module Consensus = struct | None -> tzfail (Consensus.Slot_map_not_found {loc = __LOC__}) | Some level_map -> let slot_map = Level.Map.find level level_map in - let*? consensus_key, attesting_power, _dal_power = - get_delegate_details slot_map kind slot - in - return (consensus_key, attesting_power) + get_delegate_details slot_map kind slot + (* We do not check that the frozen deposits are positive because this only needs to be true in the context of a block that actually contains the operation, which may not be the same as the current @@ -641,7 +638,7 @@ module Consensus = struct let (Single (Preattestation consensus_content)) = operation.protocol_data.contents in - let* consensus_key, attesting_power = + let* {consensus_key; attesting_power; dal_power = _} = match vi.mode with | Application block_info | Partial_validation block_info -> let* () = @@ -650,13 +647,11 @@ module Consensus = struct block_info consensus_content in - let*? consensus_key, voting_power, _dal_power = - get_delegate_details - consensus_info.preattestation_slot_map - Preattestation - consensus_content.slot - in - return (consensus_key, voting_power) + Lwt.return + @@ get_delegate_details + consensus_info.preattestation_slot_map + Preattestation + consensus_content.slot | Construction construction_info -> let* () = check_constructed_block_preattestation @@ -664,19 +659,18 @@ module Consensus = struct construction_info consensus_content in - let*? consensus_key, voting_power, _dal_power = - get_delegate_details - consensus_info.preattestation_slot_map - Preattestation - consensus_content.slot - in - return (consensus_key, voting_power) + Lwt.return + @@ get_delegate_details + consensus_info.preattestation_slot_map + Preattestation + consensus_content.slot | Mempool -> - check_mempool_consensus - vi - consensus_info - Preattestation - consensus_content + Lwt.return + @@ check_mempool_consensus + vi + consensus_info + Preattestation + consensus_content in let* () = match vi.mode with @@ -875,19 +869,19 @@ module Consensus = struct let (Single (Attestation {consensus_content; dal_content})) = operation.protocol_data.contents in - let* consensus_key, attesting_power = + let* {consensus_key; attesting_power; dal_power = _} = match vi.mode with | Application _ | Partial_validation _ | Construction _ -> ( let* () = check_block_attestation vi consensus_info consensus_content in - let*? consensus_key, attesting_power, _dal_power = + let*? details = get_delegate_details consensus_info.attestation_slot_map Attestation consensus_content.slot in - match consensus_key.consensus_pk with + match details.consensus_key.consensus_pk with | Bls _ when Constants.aggregate_attestation vi.ctxt -> (* An attestation with a BLS signature must be included in an Attestations_aggregate, even if there is only one in the block. @@ -895,13 +889,14 @@ module Consensus = struct let hash = Operation.hash operation in tzfail (Unaggregated_eligible_operation {kind = Attestation; hash}) - | _ -> return (consensus_key, attesting_power)) + | _ -> return details) | Mempool -> - check_mempool_consensus - vi - consensus_info - Attestation - consensus_content + Lwt.return + @@ check_mempool_consensus + vi + consensus_info + Attestation + consensus_content in let* () = check_delegate_is_not_forbidden vi.ctxt consensus_key.delegate in let* () = @@ -1318,11 +1313,11 @@ module Consensus = struct | Mempool -> return_unit in (* Retrieve public keys and compute total voting power *) - let* public_keys, 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, _ = + let*? {consensus_key; attesting_power; dal_power = _} = get_delegate_details consensus_info.preattestation_slot_map Preattestation @@ -1332,7 +1327,9 @@ module Consensus = struct 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) + | Bls pk -> + return + (pk :: public_keys, attesting_power + total_voting_power) | _ -> tzfail Validate_errors.Consensus.Non_bls_key_in_aggregate) ([], 0) committee @@ -1363,7 +1360,7 @@ module Consensus = struct block_state info.mode {level; round; block_payload_hash; slot = Slot.zero} - voting_power + total_voting_power in return {validation_state with block_state} @@ -1425,7 +1422,7 @@ module Consensus = struct List.fold_left_es (fun (pks, weighted_pks, total_power) (slot, dal) -> (* Lookup the slot owner *) - let*? consensus_key, attesting_power, _dal_power = + let*? {consensus_key; attesting_power; dal_power = _} = get_delegate_details consensus_info.attestation_slot_map Attestation -- GitLab From 544cdb0366a67cbf632e47dd478fe1326e4f3797 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Tue, 29 Jul 2025 16:06:49 +0200 Subject: [PATCH 3/9] =?UTF-8?q?Proto/aba=C3=A6b:=20attestation=20power=20r?= =?UTF-8?q?epr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 1 + .../lib_protocol/attestation_power_repr.ml | 22 ++++++++++++++++++ .../lib_protocol/attestation_power_repr.mli | 23 +++++++++++++++++++ src/proto_alpha/lib_protocol/dune | 4 ++++ 4 files changed, 50 insertions(+) create mode 100644 src/proto_alpha/lib_protocol/attestation_power_repr.ml create mode 100644 src/proto_alpha/lib_protocol/attestation_power_repr.mli diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 1655f02a81bd..12b9e2994017 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -26,6 +26,7 @@ "Cycle_repr", "Tez_repr", "Deposits_repr", + "Attestation_power_repr", "Unstaked_frozen_deposits_repr", "Staking_pseudotoken_repr", "Period_repr", diff --git a/src/proto_alpha/lib_protocol/attestation_power_repr.ml b/src/proto_alpha/lib_protocol/attestation_power_repr.ml new file mode 100644 index 000000000000..33a4d1c4268a --- /dev/null +++ b/src/proto_alpha/lib_protocol/attestation_power_repr.ml @@ -0,0 +1,22 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +type t = {slots : int; stake : int64} + +let encoding = + let open Data_encoding in + conv + (fun {slots; stake} -> (slots, stake)) + (fun (slots, stake) -> {slots; stake}) + (obj2 (req "slots" int31) (req "stake" int64)) + +let pp ppf {slots; stake} = + Format.fprintf ppf "(slots:%d, stake:%Ld)" slots stake + +let zero = {slots = 0; stake = 0L} + +let add a b = {slots = a.slots + b.slots; stake = Int64.add a.stake b.stake} diff --git a/src/proto_alpha/lib_protocol/attestation_power_repr.mli b/src/proto_alpha/lib_protocol/attestation_power_repr.mli new file mode 100644 index 000000000000..7684e35d30b9 --- /dev/null +++ b/src/proto_alpha/lib_protocol/attestation_power_repr.mli @@ -0,0 +1,23 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** Internal representation of attestation power. + + If the consensus is distributed randomly for 7000 slots, + the (relative) power is the number of slots of the given delegate, + noted in [slots]. Otherwise, if all bakers attest, then its power is its stake + for the cycle in which the rights were distributed, noted in [stake]. *) + +type t = {slots : int; stake : int64} + +val encoding : t Data_encoding.t + +val pp : Format.formatter -> t -> unit + +val zero : t + +val add : t -> t -> t diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 1a34312188b7..8e2f79df254b 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -54,6 +54,7 @@ Cycle_repr Tez_repr Deposits_repr + Attestation_power_repr Unstaked_frozen_deposits_repr Staking_pseudotoken_repr Period_repr @@ -342,6 +343,7 @@ cycle_repr.ml cycle_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + attestation_power_repr.ml attestation_power_repr.mli unstaked_frozen_deposits_repr.ml unstaked_frozen_deposits_repr.mli staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli @@ -635,6 +637,7 @@ cycle_repr.ml cycle_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + attestation_power_repr.ml attestation_power_repr.mli unstaked_frozen_deposits_repr.ml unstaked_frozen_deposits_repr.mli staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli @@ -912,6 +915,7 @@ cycle_repr.ml cycle_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + attestation_power_repr.ml attestation_power_repr.mli unstaked_frozen_deposits_repr.ml unstaked_frozen_deposits_repr.mli staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli -- GitLab From ffb7f5501f42e278640ae74aa434b330717d1c09 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Tue, 29 Jul 2025 17:05:41 +0200 Subject: [PATCH 4/9] =?UTF-8?q?Proto/aba=C3=A6b:=20change=20type=20of=20at?= =?UTF-8?q?testing=20power=20where=20applicable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We also register the staking power, even if unused --- .../lib_client/operation_result.ml | 20 ++++-- src/proto_alpha/lib_plugin/RPC.ml | 4 +- .../lib_protocol/alpha_context.mli | 7 +- src/proto_alpha/lib_protocol/apply.ml | 58 ++++++++++------ src/proto_alpha/lib_protocol/apply_results.ml | 18 ++--- .../lib_protocol/apply_results.mli | 12 ++-- src/proto_alpha/lib_protocol/baking.ml | 17 ++++- src/proto_alpha/lib_protocol/baking.mli | 3 +- .../lib_protocol/delegate_consensus_key.ml | 2 +- .../lib_protocol/delegate_consensus_key.mli | 2 +- .../delegate_missed_attestations_storage.ml | 4 +- .../delegate_missed_attestations_storage.mli | 1 + src/proto_alpha/lib_protocol/raw_context.ml | 66 ++++++++++-------- src/proto_alpha/lib_protocol/raw_context.mli | 17 +++-- .../integration/consensus/test_aggregate.ml | 9 ++- src/proto_alpha/lib_protocol/validate.ml | 69 ++++++++++++------- .../lib_protocol/validate_errors.ml | 13 +++- .../lib_protocol/validate_errors.mli | 1 + tezt/tests/dal.ml | 7 +- .../alpha_machine.real.ml | 6 +- 20 files changed, 220 insertions(+), 116 deletions(-) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index 200fb656e5fe..70205b0ed8c7 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -982,7 +982,13 @@ let pp_contents_and_result : rewarded_delegate in let pp_committee ppf (delegate, voting_power) = - Format.fprintf ppf "(%a, %d)" Consensus_key.pp delegate voting_power + Format.fprintf + ppf + "(%a, %a)" + Consensus_key.pp + delegate + Attestation_power_repr.pp + voting_power in fun ppf -> function | Seed_nonce_revelation {level; nonce}, Seed_nonce_revelation_result bus -> @@ -1027,13 +1033,14 @@ let pp_contents_and_result : Level: %a@,\ Balance updates:%a@,\ Delegate: %a@,\ - Consensus Power: %d@]" + Consensus Power: %a@]" Raw_level.pp level pp_balance_updates balance_updates Consensus_key.pp {delegate; consensus_pkh = consensus_key} + Attestation_power_repr.pp consensus_power | ( Attestation {consensus_content = {level; _}; dal_content = _}, Attestation_result @@ -1044,13 +1051,14 @@ let pp_contents_and_result : Level: %a@,\ Balance updates:%a@,\ Delegate: %a@,\ - Consensus Power: %d@]" + Consensus Power: %a@]" Raw_level.pp level pp_balance_updates balance_updates Consensus_key.pp {delegate; consensus_pkh = consensus_key} + Attestation_power_repr.pp consensus_power | ( Preattestations_aggregate {consensus_content = {level; _}; _}, Preattestations_aggregate_result @@ -1061,13 +1069,14 @@ let pp_contents_and_result : Level: %a@,\ Balance updates:%a@,\ Delegates: %a@,\ - Consensus Power: %d@]" + Consensus Power: %a@]" Raw_level.pp level pp_balance_updates balance_updates (Format.pp_print_list pp_committee) committee + Attestation_power_repr.pp total_consensus_power | ( Attestations_aggregate {consensus_content = {level; _}; _}, Attestations_aggregate_result @@ -1078,13 +1087,14 @@ let pp_contents_and_result : Level: %a@,\ Balance updates:%a@,\ Delegates: %a@,\ - Consensus Power: %d@]" + Consensus Power: %a@]" Raw_level.pp level pp_balance_updates balance_updates (Format.pp_print_list pp_committee) committee + Attestation_power_repr.pp total_consensus_power | ( Double_consensus_operation_evidence {slot; op1; op2}, Double_consensus_operation_evidence_result double_signing_result ) -> diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index 706e15cc1a9b..d4736610f9f2 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -3906,7 +3906,7 @@ module Attestation_rights = struct companion_pk = _; companion_pkh = _; }; - attesting_power; + attestation_power; dal_power = _; } : Consensus_key.power) @@ -3915,7 +3915,7 @@ module Attestation_rights = struct delegate; consensus_key; first_slot; - attestation_power = attesting_power; + attestation_power = attestation_power.slots; } :: acc) rights diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 2de22af12c8f..e4be93e4708c 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2249,7 +2249,11 @@ module Consensus_key : sig consensus_pkh : Signature.Public_key_hash.t; } - type power = {consensus_key : pk; attesting_power : int; dal_power : int} + type power = { + consensus_key : pk; + attestation_power : Attestation_power_repr.t; + dal_power : int; + } val encoding : t Data_encoding.t @@ -2353,6 +2357,7 @@ module Delegate : sig delegate:public_key_hash -> participation:level_participation -> attesting_power:int -> + staking_weight:Int64.t -> context tzresult Lwt.t val record_dal_participation : diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 6da808a1731b..e2cc11ade727 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2319,17 +2319,17 @@ let record_preattestation ctxt (mode : mode) (content : consensus_content) : in match mode with | Application _ | Full_construction _ -> - let*? {consensus_key; attesting_power = power; dal_power = _} = + let*? {consensus_key; attestation_power; dal_power = _} = find_in_slot_map content.slot (Consensus.allowed_preattestations ctxt) in let*? ctxt = Consensus.record_preattestation ctxt ~initial_slot:content.slot - ~power + ~power:attestation_power content.round in - return (ctxt, mk_preattestation_result consensus_key power) + return (ctxt, mk_preattestation_result consensus_key attestation_power) | Partial_construction _ -> (* In mempool mode, preattestations are allowed for various levels and rounds. We do not record preattestations because we could get @@ -2342,7 +2342,11 @@ let record_preattestation ctxt (mode : mode) (content : consensus_content) : let level = Level.from_raw ctxt content.level in Stake_distribution.slot_owner ctxt level content.slot in - return (ctxt, mk_preattestation_result consensus_key 0 (* Fake power. *)) + return + ( ctxt, + mk_preattestation_result + consensus_key + Attestation_power_repr.zero (* Fake power. *) ) let record_dal_content ctxt slot ~dal_power = function | None -> Result.return ctxt @@ -2370,17 +2374,17 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) in match mode with | Application _ | Full_construction _ -> - let*? {consensus_key; attesting_power; dal_power} = + let*? {consensus_key; attestation_power; dal_power} = find_in_slot_map consensus.slot (Consensus.allowed_attestations ctxt) in let*? ctxt = Consensus.record_attestation ctxt ~initial_slot:consensus.slot - ~power:attesting_power + ~power:attestation_power in let*? ctxt = record_dal_content ctxt consensus.slot ~dal_power dal in - return (ctxt, mk_attestation_result consensus_key attesting_power) + return (ctxt, mk_attestation_result consensus_key attestation_power) | Partial_construction _ -> (* In mempool mode, attestations are allowed for various levels and rounds. We do not record attestations because we could get @@ -2393,7 +2397,11 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) let level = Level.from_raw ctxt consensus.level in Stake_distribution.slot_owner ctxt level consensus.slot in - return (ctxt, mk_attestation_result consensus_key 0 (* Fake power. *)) + return + ( ctxt, + mk_attestation_result + consensus_key + Attestation_power_repr.zero (* Fake power. *) ) let record_attestations_aggregate ctxt (mode : mode) committee : (context * Kind.attestations_aggregate contents_result_list) tzresult Lwt.t @@ -2408,7 +2416,7 @@ let record_attestations_aggregate ctxt (mode : mode) committee : (fun (ctxt, consensus_keys, consensus_power) (slot, dal) -> let* { consensus_key = {delegate; consensus_pkh; _}; - attesting_power; + attestation_power; dal_power; } = find_in_slot_map slot slot_map @@ -2417,15 +2425,15 @@ let record_attestations_aggregate ctxt (mode : mode) committee : Consensus.record_attestation ctxt ~initial_slot:slot - ~power:attesting_power + ~power:attestation_power in let* ctxt = record_dal_content ctxt slot ~dal_power dal in let key = ({delegate; consensus_pkh} : Consensus_key.t) in return ( ctxt, - (key, attesting_power) :: consensus_keys, - attesting_power + consensus_power )) - (ctxt, [], 0) + (key, attestation_power) :: consensus_keys, + Attestation_power_repr.add attestation_power consensus_power )) + (ctxt, [], Attestation_power_repr.zero) committee in let result = @@ -2466,7 +2474,7 @@ let record_preattestations_aggregate ctxt (mode : mode) (fun (ctxt, consensus_keys, consensus_power) slot -> let* { consensus_key = {delegate; consensus_pkh; _}; - attesting_power; + attestation_power; dal_power = _; } = find_in_slot_map slot slot_map @@ -2475,15 +2483,15 @@ let record_preattestations_aggregate ctxt (mode : mode) Consensus.record_preattestation ctxt ~initial_slot:slot - ~power:attesting_power + ~power:attestation_power round in let key = ({delegate; consensus_pkh} : Consensus_key.t) in return ( ctxt, - (key, attesting_power) :: consensus_keys, - attesting_power + consensus_power )) - (ctxt, [], 0) + (key, attestation_power) :: consensus_keys, + Attestation_power_repr.add attestation_power consensus_power )) + (ctxt, [], Attestation_power_repr.zero) committee in let result = @@ -2991,7 +2999,8 @@ let record_attesting_participation ctxt dal_attestation = | Some validators -> Slot.Map.fold_es (fun initial_slot - ({consensus_key; attesting_power; dal_power} : Consensus_key.power) + ({consensus_key; attestation_power; dal_power} : + Consensus_key.power) ctxt -> let participation = if Slot.Set.mem initial_slot (Consensus.attestations_seen ctxt) then @@ -3003,7 +3012,8 @@ let record_attesting_participation ctxt dal_attestation = ctxt ~delegate:consensus_key.delegate ~participation - ~attesting_power + ~attesting_power:attestation_power.slots + ~staking_weight:attestation_power.stake in Dal_apply.record_participation ctxt @@ -3237,8 +3247,14 @@ let finalize_application ctxt block_data_contents ~round ~predecessor_hash let* ctxt, reward_bonus = if required_attestations then let* ctxt = record_attesting_participation ctxt dal_attestation in + let all_bakers_attest_enabled = + Stake_distribution.check_all_bakers_attest_at_level ctxt level + in let*? rewards_bonus = - Baking.bonus_baking_reward ctxt ~attestation_power + if all_bakers_attest_enabled then + error Validate_errors.Consensus.All_bakers_attest_not_implemented + (* TODO ABAAB *) + else Baking.bonus_baking_reward ctxt ~attestation_power in return (ctxt, Some rewards_bonus) else return (ctxt, None) diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index 609d28bdb3c8..b2c614b5a838 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -888,26 +888,26 @@ type 'kind contents_result = balance_updates : Receipt.balance_updates; delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; - consensus_power : int; + consensus_power : Attestation_power_repr.t; } -> Kind.preattestation contents_result | Attestation_result : { balance_updates : Receipt.balance_updates; delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; - consensus_power : int; + consensus_power : Attestation_power_repr.t; } -> Kind.attestation contents_result | Preattestations_aggregate_result : { balance_updates : Receipt.balance_updates; - committee : (Consensus_key.t * int) list; - total_consensus_power : int; + committee : (Consensus_key.t * Attestation_power_repr.t) list; + total_consensus_power : Attestation_power_repr.t; } -> Kind.preattestations_aggregate contents_result | Attestations_aggregate_result : { balance_updates : Receipt.balance_updates; - committee : (Consensus_key.t * int) list; - total_consensus_power : int; + committee : (Consensus_key.t * Attestation_power_repr.t) list; + total_consensus_power : Attestation_power_repr.t; } -> Kind.attestations_aggregate contents_result | Seed_nonce_revelation_result : @@ -1035,7 +1035,7 @@ module Encoding = struct obj4 (dft "balance_updates" Receipt.balance_updates_encoding []) (req "delegate" Signature.Public_key_hash.encoding) - (req "consensus_power" int31) + (req "consensus_power" Attestation_power_repr.encoding) (req "consensus_key" Signature.Public_key_hash.encoding) let consensus_aggregate_result_encoding = @@ -1047,8 +1047,8 @@ module Encoding = struct (list (merge_objs Consensus_key.encoding - (obj1 (req "consensus_power" int31))))) - (req "total_consensus_power" int31) + (obj1 (req "consensus_power" Attestation_power_repr.encoding))))) + (req "total_consensus_power" Attestation_power_repr.encoding) type case = | Case : { diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 6fa6fab25c51..e9f7ec3088df 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -69,26 +69,26 @@ and 'kind contents_result = balance_updates : Receipt.balance_updates; delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; - consensus_power : int; + consensus_power : Attestation_power_repr.t; } -> Kind.preattestation contents_result | Attestation_result : { balance_updates : Receipt.balance_updates; delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; - consensus_power : int; + consensus_power : Attestation_power_repr.t; } -> Kind.attestation contents_result | Preattestations_aggregate_result : { balance_updates : Receipt.balance_updates; - committee : (Consensus_key.t * int) list; - total_consensus_power : int; + committee : (Consensus_key.t * Attestation_power_repr.t) list; + total_consensus_power : Attestation_power_repr.t; } -> Kind.preattestations_aggregate contents_result | Attestations_aggregate_result : { balance_updates : Receipt.balance_updates; - committee : (Consensus_key.t * int) list; - total_consensus_power : int; + committee : (Consensus_key.t * Attestation_power_repr.t) list; + total_consensus_power : Attestation_power_repr.t; } -> Kind.attestations_aggregate contents_result | Seed_nonce_revelation_result : diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index c6237c4c01e4..d48f92ea39e4 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -60,6 +60,8 @@ let () = let bonus_baking_reward ctxt ~attestation_power = let open Result_syntax in + (* TODO ABAAB *) + let attestation_power = attestation_power.Attestation_power_repr.slots in let consensus_threshold_size = Constants.consensus_threshold_size ctxt in let* baking_reward_bonus_per_slot = Delegate.Rewards.baking_reward_bonus_per_slot ctxt @@ -148,16 +150,25 @@ let attesting_rights_by_first_slot ctxt level : Some { consensus_key; - attesting_power = 1; + attestation_power = + { + Attestation_power_repr.slots = 1; + (* TODO *) stake = 0L; + }; dal_power = in_dal_committee; } | Some - ({consensus_key; attesting_power; dal_power} : + ({consensus_key; attestation_power; dal_power} : Consensus_key.power) -> Some { consensus_key; - attesting_power = attesting_power + 1; + attestation_power = + (* TODO *) + { + attestation_power with + slots = attestation_power.slots + 1; + }; dal_power = dal_power + in_dal_committee; }) slots_map diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index ed491d8bfaf7..20cee99b3835 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -62,4 +62,5 @@ val attesting_rights_by_first_slot : (context * Consensus_key.power Slot.Map.t) tzresult Lwt.t (** Computes the bonus baking reward depending on the attestation power. *) -val bonus_baking_reward : context -> attestation_power:int -> Tez.t tzresult +val bonus_baking_reward : + context -> attestation_power:Attestation_power_repr.t -> Tez.t tzresult diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index bac79afbac72..66deb6f268e6 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -128,7 +128,7 @@ type pk = Raw_context.consensus_pk = { type power = Raw_context.consensus_power = { consensus_key : pk; - attesting_power : int; + attestation_power : Attestation_power_repr.t; dal_power : int; } diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index 45ceafd0f645..7be5382d70e3 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -53,7 +53,7 @@ type pk = Raw_context.consensus_pk = { (** The attesting and dal power related to the associated consensus key *) type power = Raw_context.consensus_power = { consensus_key : pk; - attesting_power : int; + attestation_power : Attestation_power_repr.t; dal_power : int; } diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml index e17b412e2fe4..1d15b7b0989b 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml @@ -62,8 +62,10 @@ type level_participation = Participated | Didn't_participate (* Note that the participation for the last block of a cycle is recorded in the next cycle. *) let record_attesting_participation ctxt ~delegate ~participation - ~attesting_power = + ~attesting_power ~staking_weight = let open Lwt_result_syntax in + ignore staking_weight ; + (* TODO *) match participation with | Participated -> Stake_storage.set_active ctxt delegate | Didn't_participate -> ( diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli index ef8366b62c10..fc651f5b2df8 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli @@ -57,6 +57,7 @@ val record_attesting_participation : delegate:Signature.Public_key_hash.t -> participation:level_participation -> attesting_power:int -> + staking_weight:Int64.t -> Raw_context.t tzresult Lwt.t (** Update the participation of a delegate as a DAL attester in the current diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index 3ae47bb0f310..b86be5abd6db 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -93,7 +93,7 @@ let consensus_pk_encoding = type consensus_power = { consensus_key : consensus_pk; - attesting_power : int; + attestation_power : Attestation_power_repr.t; dal_power : int; } @@ -103,19 +103,20 @@ module Raw_consensus = struct this delegate. *) type t = { - current_attestation_power : int; - (** Number of attestation slots recorded for the current block. *) + current_attestation_power : Attestation_power_repr.t; + (** Number of attestation slots and their related staking power recorded + for the current block. *) allowed_attestations : consensus_power Slot_repr.Map.t option; (** Attestations rights for the current block. Only an attestation for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its - consensus attestation power and DAL attestation power. This is - [None] only in mempool mode. *) + consensus attestation power, DAL attestation power, and staking power. + This is [None] only in mempool mode. *) allowed_preattestations : consensus_power Slot_repr.Map.t option; (** Preattestations rights for the current block. Only a preattestation for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its - consensus attestation power and DAL attestation power. This is + consensus attestation power, DAL attestation power and staking power. This is [None] only in mempool mode, or in application mode when there is no locked round (so the block cannot contain any preattestations). *) allowed_consensus : consensus_power Slot_repr.Map.t Level_repr.Map.t option; @@ -130,8 +131,8 @@ module Raw_consensus = struct preattestations_seen : Slot_repr.Set.t; (** Record the preattestations already seen. Only initial slots are indexed. *) - locked_round_evidence : (Round_repr.t * int) option; - (** Record the preattestation power for a locked round. *) + locked_round_evidence : (Round_repr.t * Attestation_power_repr.t) option; + (** Record the preattestation power and staking power for a locked round. *) preattestations_quorum_round : Round_repr.t option; (** in block construction mode, record the round of preattestations included in a block. *) @@ -150,7 +151,7 @@ module Raw_consensus = struct let empty : t = { - current_attestation_power = 0; + current_attestation_power = Attestation_power_repr.zero; allowed_attestations = Some Slot_repr.Map.empty; allowed_preattestations = Some Slot_repr.Map.empty; allowed_consensus = None; @@ -179,20 +180,24 @@ module Raw_consensus = struct let record_attestation t ~initial_slot ~power = let open Result_syntax in - let+ () = + let* () = error_when (Slot_repr.Set.mem initial_slot t.attestations_seen) Double_inclusion_of_consensus_operation in - { - t with - current_attestation_power = t.current_attestation_power + power; - attestations_seen = Slot_repr.Set.add initial_slot t.attestations_seen; - } + let current_attestation_power = + Attestation_power_repr.add power t.current_attestation_power + in + return + { + t with + current_attestation_power; + attestations_seen = Slot_repr.Set.add initial_slot t.attestations_seen; + } let record_preattestation ~initial_slot ~power round t = let open Result_syntax in - let+ () = + let* () = error_when (Slot_repr.Set.mem initial_slot t.preattestations_seen) Double_inclusion_of_consensus_operation @@ -205,14 +210,16 @@ module Raw_consensus = struct It doesn't matter in that case since quorum certificates are not used in mempool. For other cases [Apply.check_round] verifies it. *) - Some (round, evidences + power) + let power = Attestation_power_repr.add power evidences in + Some (round, power) in - { - t with - locked_round_evidence; - preattestations_seen = - Slot_repr.Set.add initial_slot t.preattestations_seen; - } + return + { + t with + locked_round_evidence; + preattestations_seen = + Slot_repr.Set.add initial_slot t.preattestations_seen; + } let set_forbidden_delegates delegates t = {t with forbidden_delegates = delegates} @@ -2158,7 +2165,7 @@ module type CONSENSUS = sig type error += Slot_map_not_found of {loc : string} - val current_attestation_power : t -> int + val current_attestation_power : t -> Attestation_power_repr.t val initialize_consensus_operation : t -> @@ -2167,10 +2174,15 @@ module type CONSENSUS = sig allowed_consensus:consensus_power slot_map level_map option -> t - val record_attestation : t -> initial_slot:slot -> power:int -> t tzresult + val record_attestation : + t -> initial_slot:slot -> power:Attestation_power_repr.t -> t tzresult val record_preattestation : - t -> initial_slot:slot -> power:int -> round -> t tzresult + t -> + initial_slot:slot -> + power:Attestation_power_repr.t -> + round -> + t tzresult val forbid_delegate : t -> Signature.Public_key_hash.t -> t @@ -2182,7 +2194,7 @@ module type CONSENSUS = sig val set_preattestations_quorum_round : t -> round -> t - val locked_round_evidence : t -> (round * int) option + val locked_round_evidence : t -> (round * Attestation_power_repr.t) option val set_attestation_branch : t -> Block_hash.t * Block_payload_hash.t -> t diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 6fc8a8ccea04..14d52d2ac6a7 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -357,7 +357,7 @@ end type consensus_power = { consensus_key : consensus_pk; - attesting_power : int; + attestation_power : Attestation_power_repr.t; dal_power : int; } @@ -399,9 +399,9 @@ module type CONSENSUS = sig (** Missing pre-computed map by first slot. This error should not happen. *) type error += Slot_map_not_found of {loc : string} - (** [attestation power ctx] returns the attestation power of the + (** [attestation power ctx] returns the attestation power and stake of the current block. *) - val current_attestation_power : t -> int + val current_attestation_power : t -> Attestation_power_repr.t (** Initializes the map of allowed attestations and preattestations, this function must be called only once and before applying any consensus @@ -419,7 +419,8 @@ module type CONSENSUS = sig The attestation should be valid in the sense that [Int_map.find_opt initial_slot allowed_attestation ctx = Some (pkh, power)]. *) - val record_attestation : t -> initial_slot:slot -> power:int -> t tzresult + val record_attestation : + t -> initial_slot:slot -> power:Attestation_power_repr.t -> t tzresult (** [record_preattestation ctx ~initial_slot ~power round payload_hash power] records a preattestation for a proposal at @@ -429,7 +430,11 @@ module type CONSENSUS = sig [Int_map.find_opt initial_slot allowed_preattestation ctx = Some (pkh, power)]. *) val record_preattestation : - t -> initial_slot:slot -> power:int -> round -> t tzresult + t -> + initial_slot:slot -> + power:Attestation_power_repr.t -> + round -> + t tzresult (** [forbid_delegate ctx delegate] adds [delegate] to the set of forbidden delegates, which prevents this delegate from baking or @@ -457,7 +462,7 @@ module type CONSENSUS = sig (** [locked_round_evidence ctx] returns the round of the recorded preattestations as well as their power. *) - val locked_round_evidence : t -> (round * int) option + val locked_round_evidence : t -> (round * Attestation_power_repr.t) option val set_attestation_branch : t -> Block_hash.t * Block_payload_hash.t -> t 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 3cf1c161be52..0cebaf6a384f 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 @@ -108,12 +108,12 @@ let check_aggregate_result (type kind) (kind : kind aggregate) ~attesters 0 attesters in - if voting_power = total_consensus_power then return_unit + if voting_power = total_consensus_power.slots then return_unit else Test.fail "Wrong voting power : expected %d, found %d" voting_power - total_consensus_power + total_consensus_power.slots in (* Check committee *) let expected_committee = @@ -123,6 +123,11 @@ let check_aggregate_result (type kind) (kind : kind aggregate) ~attesters ({Alpha_context.Consensus_key.delegate; consensus_pkh}, power)) attesters in + let resulting_committee = + List.map + (fun (a, b) -> (a, b.Attestation_power_repr.slots)) + resulting_committee + in if List.equal (fun ( {Alpha_context.Consensus_key.delegate = d1; consensus_pkh = c1}, diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 67c6b58ed95e..deae3b3cd691 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -431,8 +431,8 @@ type block_state = { remaining_block_gas : Gas.Arith.fp; recorded_operations_rev : Operation_hash.t list; last_op_validation_pass : int option; - locked_round_evidence : (Round.t * int) option; - attestation_power : int; + locked_round_evidence : (Round.t * Attestation_power_repr.t) option; + attestation_power : Attestation_power_repr.t; } type validation_state = { @@ -480,7 +480,7 @@ let init_block_state vi = recorded_operations_rev = []; last_op_validation_pass = None; locked_round_evidence = None; - attestation_power = 0; + attestation_power = Attestation_power_repr.zero; } let get_initial_ctxt {info; _} = info.ctxt @@ -638,7 +638,7 @@ module Consensus = struct let (Single (Preattestation consensus_content)) = operation.protocol_data.contents in - let* {consensus_key; attesting_power; dal_power = _} = + let* {consensus_key; attestation_power; dal_power = _} = match vi.mode with | Application block_info | Partial_validation block_info -> let* () = @@ -699,7 +699,7 @@ module Consensus = struct ] else [] in - return (attesting_power, check_signature) + return (attestation_power, check_signature) let check_preattestation_conflict vs oph (op : Kind.preattestation operation) = @@ -751,7 +751,9 @@ module Consensus = struct construction mode. *) Some ( consensus_content.round, - total_attesting_power + attesting_power )) + Attestation_power_repr.add + total_attesting_power + attesting_power )) in {block_state with locked_round_evidence} @@ -869,7 +871,7 @@ module Consensus = struct let (Single (Attestation {consensus_content; dal_content})) = operation.protocol_data.contents in - let* {consensus_key; attesting_power; dal_power = _} = + let* {consensus_key; attestation_power; dal_power = _} = match vi.mode with | Application _ | Partial_validation _ | Construction _ -> ( let* () = @@ -912,7 +914,7 @@ module Consensus = struct [(fun () -> check_attestation_signature vi consensus_key operation)] else [] in - return (attesting_power, check_signature) + return (attestation_power, check_signature) let check_attestation_conflict vs oph (operation : Kind.attestation operation) = @@ -957,7 +959,10 @@ module Consensus = struct | Application _ | Partial_validation _ | Construction _ -> { block_state with - attestation_power = block_state.attestation_power + attesting_power; + attestation_power = + Attestation_power_repr.add + block_state.attestation_power + attesting_power; } (* Hypothesis: this function will only be called in mempool mode *) @@ -1312,12 +1317,12 @@ module Consensus = struct return_unit | Mempool -> return_unit in - (* Retrieve public keys and compute total voting power *) - let* public_keys, total_voting_power = + (* Retrieve public keys and compute total attestation power *) + let* public_keys, total_attestation_power = List.fold_left_es - (fun (public_keys, total_voting_power) slot -> + (fun (public_keys, total_attestation_power) slot -> (* Lookup the slot owner *) - let*? {consensus_key; attesting_power; dal_power = _} = + let*? {consensus_key; attestation_power; dal_power = _} = get_delegate_details consensus_info.preattestation_slot_map Preattestation @@ -1329,9 +1334,12 @@ module Consensus = struct match consensus_key.consensus_pk with | Bls pk -> return - (pk :: public_keys, attesting_power + total_voting_power) + ( pk :: public_keys, + Attestation_power_repr.add + attestation_power + total_attestation_power ) | _ -> tzfail Validate_errors.Consensus.Non_bls_key_in_aggregate) - ([], 0) + ([], Attestation_power_repr.zero) committee in (* Fail on empty committee *) @@ -1360,7 +1368,7 @@ module Consensus = struct block_state info.mode {level; round; block_payload_hash; slot = Slot.zero} - total_voting_power + total_attestation_power in return {validation_state with block_state} @@ -1422,7 +1430,7 @@ module Consensus = struct List.fold_left_es (fun (pks, weighted_pks, total_power) (slot, dal) -> (* Lookup the slot owner *) - let*? {consensus_key; attesting_power; dal_power = _} = + let*? {consensus_key; attestation_power; dal_power = _} = get_delegate_details consensus_info.attestation_slot_map Attestation @@ -1432,7 +1440,9 @@ module Consensus = struct check_delegate_is_not_forbidden info.ctxt consensus_key.delegate in let* () = check_dal_content info level slot consensus_key dal in - let total_power = attesting_power + total_power in + let total_power = + Attestation_power_repr.add attestation_power total_power + in match consensus_key.consensus_pk with | Bls consensus_pk -> ( let pks = consensus_pk :: pks in @@ -1458,7 +1468,7 @@ module Consensus = struct (Missing_companion_key_for_bls_dal (Consensus_key.pkh consensus_key))) | _ -> tzfail Validate_errors.Consensus.Non_bls_key_in_aggregate) - ([], [], 0) + ([], [], Attestation_power_repr.zero) committee in (* Fail on empty committee *) @@ -4019,7 +4029,8 @@ let check_attestation_power vi bs = in if are_attestations_required then let required = Constants.consensus_threshold_size vi.ctxt in - let provided = bs.attestation_power in + (* TODO ABAÆB *) + let provided = bs.attestation_power.slots in fail_unless Compare.Int.(provided >= required) (Not_enough_attestations {required; provided}) @@ -4066,10 +4077,20 @@ let check_preattestation_round_and_power vi vs round = {locked_round = preattestation_round; round}) in let consensus_threshold = Constants.consensus_threshold_size vi.ctxt in - error_when - Compare.Int.(total_attesting_power < consensus_threshold) - (Insufficient_locked_round_evidence - {total_attesting_power; consensus_threshold}) + let all_bakers_attest_enabled = + Stake_distribution.check_all_bakers_attest_at_level + vi.ctxt + vi.current_level + in + if all_bakers_attest_enabled then + error Validate_errors.Consensus.All_bakers_attest_not_implemented + (* TODO ABAAB *) + else + let total_attesting_power = total_attesting_power.slots in + error_when + Compare.Int.(total_attesting_power < consensus_threshold) + (Insufficient_locked_round_evidence + {total_attesting_power; consensus_threshold}) let check_payload_hash block_state ~predecessor_hash (block_header_contents : Block_header.contents) = diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index db5d7c73c924..a89f60292688 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -144,6 +144,7 @@ module Consensus = struct hash : Operation_hash.t; } | Empty_aggregation_committee + | All_bakers_attest_not_implemented let () = register_error_kind @@ -464,7 +465,17 @@ module Consensus = struct Format.fprintf ppf "The aggregation committee is empty.") Data_encoding.empty (function Empty_aggregation_committee -> Some () | _ -> None) - (fun () -> Empty_aggregation_committee) + (fun () -> Empty_aggregation_committee) ; + register_error_kind + `Permanent + ~id:"validate.all_bakers_attest_not_implemented" + ~title:"All bakers attest not implemented" + ~description:"All bakers attest is not implemented yet" + ~pp:(fun ppf () -> + Format.fprintf ppf "All bakers attest is not implemented yet") + Data_encoding.empty + (function All_bakers_attest_not_implemented -> Some () | _ -> None) + (fun () -> All_bakers_attest_not_implemented) 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 aa1bf223faa1..1fd06f503ed2 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -94,6 +94,7 @@ module Consensus : sig hash : Operation_hash.t; } | Empty_aggregation_committee + | All_bakers_attest_not_implemented end (** Errors that may arise while validating a voting operation. *) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 47ebb6ff20a8..23fb0a5c5745 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -9309,7 +9309,7 @@ let create_tz4_accounts_stake_and_wait ~funders ~client ~node nb_to_create = new_tz4_accounts companions -let test_aggregation_required_to_pass_quorum _protocol dal_parameters _cryptobox +let test_aggregation_required_to_pass_quorum protocol dal_parameters _cryptobox node client dal_node = let slot_index = 0 in let* () = Dal_RPC.(call dal_node (patch_profiles [Operator slot_index])) in @@ -9353,7 +9353,10 @@ let test_aggregation_required_to_pass_quorum _protocol dal_parameters _cryptobox let aggregated_attestation_consensus_power = JSON.( aggregated_attestation |-> "contents" |> as_list |> List.hd |-> "metadata" - |-> "total_consensus_power" |> as_int) + |-> "total_consensus_power" + |> fun x -> + if Protocol.number protocol >= 024 then x |-> "slots" |> as_int + else x |> as_int) in let* constants = Node.RPC.call node @@ RPC.get_chain_block_context_constants () diff --git a/teztale/bin_teztale_archiver/alpha_machine.real.ml b/teztale/bin_teztale_archiver/alpha_machine.real.ml index 9b328bcd3f78..9271b7d4db54 100644 --- a/teztale/bin_teztale_archiver/alpha_machine.real.ml +++ b/teztale/bin_teztale_archiver/alpha_machine.real.ml @@ -240,7 +240,7 @@ module Services : Protocol_machinery.PROTOCOL_SERVICES = struct kind; }; delegate = Tezos_crypto.Signature.Of_V2.public_key_hash ck.delegate; - power; + power = power.Protocol.Attestation_power_repr.slots; } :: acc) acc @@ -276,7 +276,7 @@ module Services : Protocol_machinery.PROTOCOL_SERVICES = struct }; delegate = Tezos_crypto.Signature.Of_V2.public_key_hash delegate; - power = consensus_power; + power = consensus_power.slots; } :: acc | Receipt @@ -298,7 +298,7 @@ module Services : Protocol_machinery.PROTOCOL_SERVICES = struct }; delegate = Tezos_crypto.Signature.Of_V2.public_key_hash delegate; - power = consensus_power; + power = consensus_power.slots; } :: acc | Receipt -- GitLab From 01bbc2cb5bc29d9e2efddf5cd8ef2ba04d80b8d6 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 14 May 2025 14:45:26 +0200 Subject: [PATCH 5/9] Proto: attesting_rights_by_first_slot with staking weight --- src/proto_alpha/lib_protocol/baking.ml | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index d48f92ea39e4..fd49c03e5bb4 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -117,7 +117,7 @@ let attesting_rights_by_first_slot ctxt level : Slot.Range.create ~min:0 ~count:(Constants.consensus_committee_size ctxt) in let number_of_shards = Constants.dal_number_of_shards ctxt in - let* ctxt, (_, slots_map) = + let* ctxt, (delegates_map, slots_map) = Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> let+ ctxt, consensus_key = @@ -153,7 +153,7 @@ let attesting_rights_by_first_slot ctxt level : attestation_power = { Attestation_power_repr.slots = 1; - (* TODO *) stake = 0L; + (* built at a later step *) stake = 0L; }; dal_power = in_dal_committee; } @@ -164,7 +164,6 @@ let attesting_rights_by_first_slot ctxt level : { consensus_key; attestation_power = - (* TODO *) { attestation_power with slots = attestation_power.slots + 1; @@ -177,4 +176,27 @@ let attesting_rights_by_first_slot ctxt level : (ctxt, (Signature.Public_key_hash.Map.empty, Slot.Map.empty)) slots in + let* ctxt, _, stake_info_list = Stake_distribution.stake_info ctxt level in + let slots_map = + List.fold_left + (fun acc ((consensus_pk, weight) : Consensus_key.pk * Int64.t) -> + match + Signature.Public_key_hash.Map.find consensus_pk.delegate delegates_map + with + | None -> acc + | Some slot -> ( + match Slot.Map.find slot slots_map with + | None -> acc (* Impossible by construction *) + | Some v -> + Slot.Map.add + slot + { + v with + attestation_power = + {v.attestation_power with stake = weight}; + } + acc)) + Slot.Map.empty + stake_info_list + in return (ctxt, slots_map) -- GitLab From 2208c21578f0cef0060b8cf4e8eee4d8c9fff644 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 30 Jul 2025 12:29:53 +0200 Subject: [PATCH 6/9] Proto: update record_attesting_participation with new data --- .../lib_protocol/alpha_context.mli | 1 - src/proto_alpha/lib_protocol/apply.ml | 1 - .../delegate_missed_attestations_storage.ml | 29 +++++++------------ .../delegate_missed_attestations_storage.mli | 1 - 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index e4be93e4708c..4965c7c28b66 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2357,7 +2357,6 @@ module Delegate : sig delegate:public_key_hash -> participation:level_participation -> attesting_power:int -> - staking_weight:Int64.t -> context tzresult Lwt.t val record_dal_participation : diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index e2cc11ade727..6b82310a1ff3 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -3013,7 +3013,6 @@ let record_attesting_participation ctxt dal_attestation = ~delegate:consensus_key.delegate ~participation ~attesting_power:attestation_power.slots - ~staking_weight:attestation_power.stake in Dal_apply.record_participation ctxt diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml index 1d15b7b0989b..6de36bb5fe2f 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml @@ -62,10 +62,8 @@ type level_participation = Participated | Didn't_participate (* Note that the participation for the last block of a cycle is recorded in the next cycle. *) let record_attesting_participation ctxt ~delegate ~participation - ~attesting_power ~staking_weight = + ~attesting_power = let open Lwt_result_syntax in - ignore staking_weight ; - (* TODO *) match participation with | Participated -> Stake_storage.set_active ctxt delegate | Didn't_participate -> ( @@ -80,12 +78,16 @@ let record_attesting_participation ctxt ~delegate ~participation {remaining_slots; missed_levels = missed_levels + 1} | None -> ( let level = Level_storage.current ctxt in - let*? stake_distribution = - Raw_context.stake_distribution_for_current_cycle ctxt + let* ctxt, total_active_stake_weight, stake_list = + Delegate_sampler.stake_info ctxt level in - match - Signature.Public_key_hash.Map.find delegate stake_distribution - with + let stake_weight_info = + List.find + (fun (pk, _) -> + Signature.Public_key_hash.equal delegate pk.Raw_context.delegate) + stake_list + in + match stake_weight_info with | None -> (* This happens when the block is the first one in a cycle, and therefore the attestations are for the last @@ -94,17 +96,8 @@ let record_attesting_participation ctxt ~delegate ~participation case its participation is simply ignored. *) assert (Compare.Int32.(level.cycle_position = 0l)) ; return ctxt - | Some active_stake -> - let* total_active_stake = - Stake_storage.get_total_active_stake ctxt level.cycle - in + | Some (_, active_stake_weight) -> let expected_slots = - let active_stake_weight = - Stake_repr.staking_weight active_stake - in - let total_active_stake_weight = - Stake_repr.staking_weight total_active_stake - in expected_slots_for_given_active_stake ctxt ~total_active_stake_weight diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli index fc651f5b2df8..ef8366b62c10 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli @@ -57,7 +57,6 @@ val record_attesting_participation : delegate:Signature.Public_key_hash.t -> participation:level_participation -> attesting_power:int -> - staking_weight:Int64.t -> Raw_context.t tzresult Lwt.t (** Update the participation of a delegate as a DAL attester in the current -- GitLab From 503af52f4f4dc9be3e2d241b8a6d5b6b1db1d021 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 30 Jul 2025 12:30:15 +0200 Subject: [PATCH 7/9] Proto/RPC: add and test new stake_info RPC --- docs/protocols/alpha.rst | 4 ++++ src/proto_alpha/lib_plugin/RPC.ml | 18 +++++++++++++++++- tezt/lib_tezos/RPC.ml | 3 +++ tezt/lib_tezos/RPC.mli | 2 ++ tezt/tests/consensus_key.ml | 19 ++++++++++++++----- ... - delegate - consensus - destination).out | 18 ++++++++++++++++++ ...- delegate - consensus -- destination).out | 18 ++++++++++++++++++ ...-- delegate - consensus - destination).out | 18 ++++++++++++++++++ ...- delegate - consensus -- destination).out | 18 ++++++++++++++++++ 9 files changed, 112 insertions(+), 6 deletions(-) diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 5da70a609501..d7ab696e0b94 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -34,6 +34,10 @@ Breaking Changes RPC Changes ----------- +- Added ``GET /chains//blocks//helpers/stake_info``, + which returns the staking power distribution for all the active delegates + at the current cycle. (MR :gl:`!18019`) + Operation receipts ------------------ diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index d4736610f9f2..cb5df08aaa88 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -4159,6 +4159,14 @@ module S = struct ~query:RPC_query.empty ~output:Data_encoding.int64 RPC_path.(path / "total_baking_power") + + let stake_info = + RPC_service.get_service + ~description:"Returns the stake info." + ~query:RPC_query.empty + ~output: + Data_encoding.(tup2 int64 (list (tup2 Consensus_key.encoding int64))) + RPC_path.(path / "stake_info") end type Environment.Error_monad.error += Negative_level_offset @@ -4220,7 +4228,15 @@ let register () = Registration.register0 ~chunked:false S.total_baking_power (fun ctxt () () -> Stake_distribution.For_RPC.total_baking_power ctxt - (Level.current ctxt).cycle) + (Level.current ctxt).cycle) ; + Registration.register0 ~chunked:false S.stake_info (fun ctxt () () -> + let* _, total_stake, stake_info_list = + Stake_distribution.stake_info ctxt (Level.current ctxt) + in + let stake_info_list = + List.map (fun (x, y) -> (Consensus_key.pkh x, y)) stake_info_list + in + return (total_stake, stake_info_list)) let current_level ctxt ?(offset = 0l) block = RPC_context.make_call0 S.current_level ctxt block {offset} () diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 649ba8f620f5..1000774b045b 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -2015,3 +2015,6 @@ let get_stake_distribution ?(chain = "main") ?(block = "head") ~cycle () = baking_power = frozen_stake + delegated_stake; }) bakers_with_pow + +let get_stake_info ?(chain = "main") ?(block = "head") () = + make GET ["chains"; chain; "blocks"; block; "helpers"; "stake_info"] Fun.id diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index 48daf6e6641f..99491f6869f8 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -1467,3 +1467,5 @@ type baker_with_power = {delegate : string; baking_power : int} val get_stake_distribution : ?chain:string -> ?block:string -> cycle:int -> unit -> baker_with_power list t + +val get_stake_info : ?chain:string -> ?block:string -> unit -> JSON.t t diff --git a/tezt/tests/consensus_key.ml b/tezt/tests/consensus_key.ml index 1d8d9877697f..9868e7ecb635 100644 --- a/tezt/tests/consensus_key.ml +++ b/tezt/tests/consensus_key.ml @@ -819,7 +819,7 @@ let test_revert_to_unique_consensus_key ?(baker = Constant.bootstrap1.alias) let test_drain_delegate_1 ?(baker = Constant.bootstrap1.alias) ~(delegate : Account.key) ~(consensus : Account.key) - ~(destination : Account.key) client = + ~(destination : Account.key) ~protocol client = let* () = update_consensus_key ~baker ~src:delegate ~consensus_key:consensus client in @@ -855,6 +855,11 @@ let test_drain_delegate_1 ?(baker = Constant.bootstrap1.alias) (* In case delegate = baker, use the consensus key updated above *) client in + let* _ = + if Protocol.number protocol >= 024 then + Lwt.map ignore @@ Client.RPC.call ~hooks client @@ RPC.get_stake_info () + else return () + in let* _ = Client.RPC.call ~hooks client @@ RPC.get_chain_block_context_delegate delegate.public_key_hash @@ -999,7 +1004,7 @@ let register ~protocols = let () = register "Test drain delegate with (baker = delegate & consensus = destination)" - (fun _protocol client baker_0 _baker_1 account_0 _account_1 _account_2 -> + (fun protocol client baker_0 _baker_1 account_0 _account_1 _account_2 -> let delegate = baker_0 in let consensus = account_0 in let destination = account_0 in @@ -1008,13 +1013,14 @@ let register ~protocols = ~delegate ~consensus ~destination + ~protocol client) protocols in let () = register "Test drain delegate with (baker = delegate & consensus <> destination)" - (fun _protocol client baker_0 _baker_1 account_0 account_1 _account_2 -> + (fun protocol client baker_0 _baker_1 account_0 account_1 _account_2 -> let delegate = baker_0 in let consensus = account_0 in let destination = account_1 in @@ -1023,13 +1029,14 @@ let register ~protocols = ~delegate ~consensus ~destination + ~protocol client) protocols in let () = register "Test drain delegate with (baker <> delegate & consensus = destination)" - (fun _protocol client baker_0 baker_1 account_0 _account_1 _account_2 -> + (fun protocol client baker_0 baker_1 account_0 _account_1 _account_2 -> let delegate = baker_0 in let consensus = account_0 in let destination = account_0 in @@ -1038,13 +1045,14 @@ let register ~protocols = ~delegate ~consensus ~destination + ~protocol client) protocols in let () = register "Test drain delegate with (baker <> delegate & consensus <> destination)" - (fun _protocol client baker_0 baker_1 account_0 account_1 _account_2 -> + (fun protocol client baker_0 baker_1 account_0 account_1 _account_2 -> let delegate = baker_0 in let consensus = account_0 in let destination = account_1 in @@ -1053,6 +1061,7 @@ let register ~protocols = ~delegate ~consensus ~destination + ~protocol client) protocols in diff --git a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus - destination).out b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus - destination).out index c5f7b3f38b51..d88e0964d1d7 100644 --- a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus - destination).out +++ b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus - destination).out @@ -225,6 +225,24 @@ This sequence of operations was run: [PUBLIC_KEY_HASH] ... +ꜩ38000.028561 +./octez-client rpc get /chains/main/blocks/head/helpers/stake_info +[ "7333333414212", + [ [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666747548" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ] ] ] + ./octez-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]' { "deactivated": false, "is_forbidden": false, "participation": diff --git a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus -- destination).out b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus -- destination).out index 0631ee030dd2..e8cdc200f2c9 100644 --- a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus -- destination).out +++ b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker - delegate - consensus -- destination).out @@ -225,6 +225,24 @@ This sequence of operations was run: [PUBLIC_KEY_HASH] ... +ꜩ38000.028561 +./octez-client rpc get /chains/main/blocks/head/helpers/stake_info +[ "7333333414212", + [ [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666747548" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ] ] ] + ./octez-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]' { "deactivated": false, "is_forbidden": false, "participation": diff --git a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus - destination).out b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus - destination).out index fb10de9b2e1c..555e2cdd0328 100644 --- a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus - destination).out +++ b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus - destination).out @@ -225,6 +225,24 @@ This sequence of operations was run: [PUBLIC_KEY_HASH] ... +ꜩ38000.025708 +./octez-client rpc get /chains/main/blocks/head/helpers/stake_info +[ "7333333414117", + [ [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666732452" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666681667" ] ] ] + ./octez-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]' { "deactivated": false, "is_forbidden": false, "participation": diff --git a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus -- destination).out b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus -- destination).out index 2539b321730e..41f10c2c00ba 100644 --- a/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus -- destination).out +++ b/tezt/tests/expected/consensus_key.ml/Alpha- Test drain delegate with (baker -- delegate - consensus -- destination).out @@ -225,6 +225,24 @@ This sequence of operations was run: [PUBLIC_KEY_HASH] ... +ꜩ38000.025708 +./octez-client rpc get /chains/main/blocks/head/helpers/stake_info +[ "7333333414117", + [ [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666732452" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666666666" ], + [ { "delegate": "[PUBLIC_KEY_HASH]", + "consensus_pkh": "[PUBLIC_KEY_HASH]" }, + "1466666681667" ] ] ] + ./octez-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]' { "deactivated": false, "is_forbidden": false, "participation": -- GitLab From fb605034a3c9c037b4041fdca16c9873fc07c44e Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 30 Jul 2025 13:45:48 +0200 Subject: [PATCH 8/9] Kaitai: update --- .../alpha__operation__data_and_metadata.ksy | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/client-libs/kaitai-struct-files/files/alpha__operation__data_and_metadata.ksy b/client-libs/kaitai-struct-files/files/alpha__operation__data_and_metadata.ksy index bba5364b9023..1511e6169c46 100644 --- a/client-libs/kaitai-struct-files/files/alpha__operation__data_and_metadata.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__operation__data_and_metadata.ksy @@ -1632,7 +1632,7 @@ types: type: public_key_hash doc: A Ed25519, Secp256k1, P256, or BLS public key hash - id: consensus_power - type: int31 + type: consensus_power committee_entries_1: seq: - id: committee_elt @@ -1645,6 +1645,12 @@ types: type: s4be - id: block_payload_hash size: 32 + consensus_power: + seq: + - id: slots + type: int31 + - id: stake + type: s8be contents: seq: - id: contents_entries @@ -2138,7 +2144,7 @@ types: type: public_key_hash doc: A Ed25519, Secp256k1, P256, or BLS public key hash - id: consensus_power - type: int31 + type: consensus_power - id: consensus_key type: public_key_hash doc: A Ed25519, Secp256k1, P256, or BLS public key hash @@ -2149,7 +2155,7 @@ types: - id: committee type: committee_2 - id: total_consensus_power - type: int31 + type: total_consensus_power metadata_1: seq: - id: punished_delegate @@ -3617,6 +3623,12 @@ types: type: n - id: ticket_updates type: ticket_updates_0 + total_consensus_power: + seq: + - id: slots + type: int31 + - id: stake + type: s8be transaction: seq: - id: source -- GitLab From 9d5326f8158ebd4ad3b92afb2bf979b83f57ca61 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Wed, 27 Nov 2024 11:23:15 +0100 Subject: [PATCH 9/9] =?UTF-8?q?ABA=C3=86B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WIP --- .../lib_delegate/baking_actions.ml | 11 +- .../lib_delegate/baking_actions.mli | 1 + src/proto_alpha/lib_delegate/baking_lib.ml | 22 +- .../lib_delegate/baking_scheduling.ml | 12 +- src/proto_alpha/lib_delegate/baking_state.ml | 83 ++++-- src/proto_alpha/lib_delegate/baking_state.mli | 31 +- .../client_baking_denunciation.ml | 25 +- src/proto_alpha/lib_delegate/node_rpc.ml | 4 +- src/proto_alpha/lib_delegate/node_rpc.mli | 2 +- .../lib_delegate/operation_worker.ml | 70 +++-- .../lib_delegate/operation_worker.mli | 8 +- .../lib_delegate/state_transitions.ml | 7 +- src/proto_alpha/lib_plugin/RPC.ml | 130 +++++++-- src/proto_alpha/lib_plugin/dal_services.ml | 6 +- src/proto_alpha/lib_plugin/mempool.ml | 2 +- src/proto_alpha/lib_protocol/alpha_context.ml | 9 +- .../lib_protocol/alpha_context.mli | 20 +- src/proto_alpha/lib_protocol/apply.ml | 19 +- src/proto_alpha/lib_protocol/baking.ml | 152 ++++++++-- src/proto_alpha/lib_protocol/baking.mli | 11 +- .../lib_protocol/delegate_cycles.ml | 42 ++- .../delegate_missed_attestations_storage.ml | 14 +- .../delegate_missed_attestations_storage.mli | 1 + .../lib_protocol/delegate_rewards.ml | 13 +- .../lib_protocol/delegate_rewards.mli | 6 + .../lib_protocol/delegate_sampler.ml | 40 ++- .../lib_protocol/delegate_sampler.mli | 19 +- src/proto_alpha/lib_protocol/main.ml | 16 +- src/proto_alpha/lib_protocol/raw_context.ml | 35 ++- src/proto_alpha/lib_protocol/raw_context.mli | 8 + .../lib_protocol/slash_percentage.ml | 13 +- .../lib_protocol/test/helpers/context.ml | 53 +++- .../lib_protocol/test/helpers/context.mli | 26 +- .../lib_protocol/test/helpers/op.ml | 1 + .../test/helpers/scenario_attestation.ml | 4 +- .../integration/consensus/test_aggregate.ml | 18 +- .../integration/consensus/test_attestation.ml | 48 +++- .../consensus/test_double_attestation.ml | 4 +- .../consensus/test_double_baking.ml | 4 +- .../consensus/test_participation.ml | 24 +- .../integration/test_scenario_slashing.ml | 2 +- .../integration/validate/validate_helpers.ml | 3 +- src/proto_alpha/lib_protocol/validate.ml | 57 ++-- .../lib_protocol/validate_errors.ml | 18 +- .../lib_protocol/validate_errors.mli | 6 +- tezt/lib_tezos/operation_core.ml | 38 ++- tezt/lib_tezos/operation_core.mli | 13 +- tezt/tests/double_consensus.ml | 265 ++++++++++-------- tezt/tests/prevalidator.ml | 13 +- 49 files changed, 1040 insertions(+), 389 deletions(-) diff --git a/src/proto_alpha/lib_delegate/baking_actions.ml b/src/proto_alpha/lib_delegate/baking_actions.ml index 5a8e2e584854..9057f61e4611 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.ml +++ b/src/proto_alpha/lib_delegate/baking_actions.ml @@ -150,6 +150,7 @@ and level_update = { next_level_delegate_slots:delegate_slots -> dal_attestable_slots:dal_attestable_slots -> next_level_dal_attestable_slots:dal_attestable_slots -> + consensus_threshold:int64 -> (state * action) Lwt.t; } @@ -1030,9 +1031,7 @@ let inject_block ?(force_injection = false) ?(asynchronous = true) state return new_state let prepare_waiting_for_quorum state = - let consensus_threshold = - state.global_state.constants.parametric.consensus_threshold_size - in + let consensus_threshold = state.level_state.consensus_threshold in let get_slot_voting_power ~slot = Delegate_slots.voting_power state.level_state.delegate_slots ~slot in @@ -1092,7 +1091,7 @@ let notice_delegates_without_slots all_delegates delegate_slots level = (fun {Baking_state_types.Key.id; _} -> not @@ List.exists - (fun ({delegate = {consensus_key; _}; _} : delegate_slot) -> + (fun ({delegate = {consensus_key; _}; _} : delegate_info) -> id = consensus_key.id) (Baking_state.Delegate_slots.own_delegates delegate_slots)) all_delegates @@ -1137,6 +1136,9 @@ let update_to_level state level_update = let*! () = notice_delegates_without_slots delegates delegate_slots new_level in + let* consensus_threshold = + Baking_state.compute_consensus_threshold ~level:new_level ~chain cctxt + in let round_durations = state.global_state.round_durations in let*? current_round = (compute_round @@ -1172,6 +1174,7 @@ let update_to_level state level_update = ~next_level_delegate_slots ~dal_attestable_slots ~next_level_dal_attestable_slots + ~consensus_threshold [@profiler.record_s {verbosity = Debug} "compute new state"]) in return new_state diff --git a/src/proto_alpha/lib_delegate/baking_actions.mli b/src/proto_alpha/lib_delegate/baking_actions.mli index 45cc85c6d6c1..249be7d1dce9 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.mli +++ b/src/proto_alpha/lib_delegate/baking_actions.mli @@ -58,6 +58,7 @@ and level_update = { next_level_delegate_slots:delegate_slots -> dal_attestable_slots:dal_attestable_slots -> next_level_dal_attestable_slots:dal_attestable_slots -> + consensus_threshold:int64 -> (state * action) Lwt.t; } diff --git a/src/proto_alpha/lib_delegate/baking_lib.ml b/src/proto_alpha/lib_delegate/baking_lib.ml index ccfe3895e162..fcd23c87daa9 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.ml +++ b/src/proto_alpha/lib_delegate/baking_lib.ml @@ -237,14 +237,14 @@ let attestations_attesting_power state attestations = match Delegate_slots.voting_power state.level_state.delegate_slots ~slot with - | None -> 0 (* cannot happen *) + | None -> 0L (* cannot happen *) | Some attesting_power -> attesting_power in List.sort_uniq compare attestations |> List.fold_left (fun power attestation -> - power + get_attestation_voting_power attestation) - 0 + Int64.add power (get_attestation_voting_power attestation)) + 0L let generic_attesting_power (filter : packed_operation list -> 'a list) (extract : 'a -> consensus_content) state = @@ -348,10 +348,8 @@ let propose_at_next_level ~minimal_timestamp state = let attestation_quorum state = let power, attestations = state_attesting_power state in - if - Compare.Int.( - power >= state.global_state.constants.parametric.consensus_threshold_size) - then Some (power, attestations) + if Compare.Int64.(power >= state.level_state.consensus_threshold) then + Some (power, attestations) else None (* Here's the sketch of the algorithm: @@ -708,14 +706,12 @@ let rec baking_minimal_timestamp ~count state own_attestations.unsigned_consensus_votes |> attestations_attesting_power state in - let consensus_threshold = - state.global_state.constants.parametric.consensus_threshold_size - in + let consensus_threshold = state.level_state.consensus_threshold in let* () = - if Compare.Int.(total_voting_power < consensus_threshold) then + if Compare.Int64.(total_voting_power < consensus_threshold) then cctxt#error - "Delegates do not have enough voting power. Only %d is available while \ - %d is required." + "Delegates do not have enough voting power. Only %Ld is available \ + while %Ld is required." total_voting_power consensus_threshold else return_unit diff --git a/src/proto_alpha/lib_delegate/baking_scheduling.ml b/src/proto_alpha/lib_delegate/baking_scheduling.ml index 3a65c8616a61..18e85f20a944 100644 --- a/src/proto_alpha/lib_delegate/baking_scheduling.ml +++ b/src/proto_alpha/lib_delegate/baking_scheduling.ml @@ -688,6 +688,9 @@ let create_initial_state cctxt ?dal_node_rpc_ctxt ?(synchronize = true) ~chain ~level:(Int32.succ current_level) ~chain in + let* consensus_threshold = + Baking_state.compute_consensus_threshold ~level:current_level ~chain cctxt + in let elected_block = if Baking_state.is_first_block_in_protocol current_proposal then (* If the last block is a protocol transition, we admit it as a @@ -730,6 +733,7 @@ let create_initial_state cctxt ?dal_node_rpc_ctxt ?(synchronize = true) ~chain next_level_latest_forge_request = None; dal_attestable_slots; next_level_dal_attestable_slots; + consensus_threshold; } in let* round_state = @@ -936,13 +940,7 @@ let try_resolve_consensus_keys cctxt key = | Error _ | Ok [] -> try_find_delegate_key (head_offset - 1) | Ok (Plugin.RPC.Validators. - { - delegate; - level = _; - consensus_key = _; - companion_key = _; - slots = _; - } + {delegate; level = _; consensus_key = _; companion_key = _; _} :: _) -> (* The primary registered key as delegate found. Return it. *) return delegate diff --git a/src/proto_alpha/lib_delegate/baking_state.ml b/src/proto_alpha/lib_delegate/baking_state.ml index fbf2fe724983..8b732de3c562 100644 --- a/src/proto_alpha/lib_delegate/baking_state.ml +++ b/src/proto_alpha/lib_delegate/baking_state.ml @@ -124,21 +124,21 @@ let block_info_encoding = module SlotMap : Map.S with type key = Slot.t = Map.Make (Slot) -type delegate_slot = { +type delegate_info = { delegate : Delegate.t; first_slot : Slot.t; - attesting_power : int; + attesting_power : int64; } module Delegate_slots = struct (* Note that we also use the delegate slots as proposal slots. *) type t = { - own_delegates : delegate_slot list; - own_delegate_slots : delegate_slot SlotMap.t; + own_delegates : delegate_info list; + own_delegate_rounds : delegate_info SlotMap.t; (* This map cannot have as keys just the first slot of delegates, because it is used in [round_proposer] for which we need all slots, as the round can be arbitrary. *) - all_delegate_voting_power : int SlotMap.t; + all_delegate_voting_power : int64 SlotMap.t; (* This is a map having as keys the first slot of all delegates, and as values their attesting power. This map contains just the first slot for a delegate, because it is @@ -153,19 +153,19 @@ module Delegate_slots = struct let own_delegates slots = slots.own_delegates - let own_slot_owner slots ~slot = SlotMap.find slot slots.own_delegate_slots + (* let own_slot_owner slots ~slot = SlotMap.find slot slots.own_delegate_rounds *) let find_first_slot_from slots ~slot = - SlotMap.find_first (fun s -> Slot.(s >= slot)) slots.own_delegate_slots + SlotMap.find_first (fun s -> Slot.(s >= slot)) slots.own_delegate_rounds - let min_slot slots = SlotMap.min_binding slots.own_delegate_slots + let min_slot slots = SlotMap.min_binding slots.own_delegate_rounds let own_round_owner slots ~committee_size ~round = let open Result_syntax in let* slot = Round.to_slot ~committee_size round |> Environment.wrap_tzresult in - return @@ SlotMap.find slot slots.own_delegate_slots + return @@ SlotMap.find slot slots.own_delegate_rounds let voting_power slots ~slot = SlotMap.find slot slots.all_delegate_voting_power @@ -254,6 +254,7 @@ type level_state = { next_level_latest_forge_request : Round.t option; dal_attestable_slots : dal_attestable_slots; next_level_dal_attestable_slots : dal_attestable_slots; + consensus_threshold : int64; } type phase = @@ -967,12 +968,34 @@ let may_load_attestable_data state = let delegate_slots attesting_rights delegates = let open Lwt_syntax in let known_keys = Key.Set.of_list delegates in - let* own_delegate_first_slots, own_delegate_slots, all_delegate_voting_power = + let* own_delegate_first_slots, own_delegate_rounds, all_delegate_voting_power + = Lwt_list.fold_left_s (fun (own_list, own_map, all_map) validator -> - let {Plugin.RPC.Validators.slots; _} = validator in - let first_slot = Stdlib.List.hd slots in - let attesting_power = List.length slots in + let { + Plugin.RPC.Validators.consensus_key; + companion_key; + delegate; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + staking_weight; + level = _; + } = + validator + in + let first_slot = + if use_legacy_for_attestation then Stdlib.List.hd slots_legacy + else + match attestation_slot with + | None -> assert false + | Some slot -> slot + in + let attesting_power = + if use_legacy_for_attestation then + Int64.of_int (List.length slots_legacy) + else staking_weight + in let all_map = SlotMap.add first_slot attesting_power all_map in let* own_list, own_map = let* delegate_opt = Delegate.of_validator ~known_keys validator in @@ -986,7 +1009,8 @@ let delegate_slots attesting_rights delegates = (fun own_map slot -> SlotMap.add slot attesting_slot own_map) own_map - slots ) + (* We use legacy regardless because it is used for rounds *) + slots_legacy ) in return (own_list, own_map, all_map)) ([], SlotMap.empty, SlotMap.empty) @@ -996,7 +1020,7 @@ let delegate_slots attesting_rights delegates = Delegate_slots. { own_delegates = own_delegate_first_slots; - own_delegate_slots; + own_delegate_rounds; all_delegate_voting_power; } @@ -1018,6 +1042,19 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) in return delegate_slots +let compute_consensus_threshold ?(block = `Head 0) ~level ~chain + (cctxt : Protocol_client_context.full) = + let open Lwt_result_syntax in + let*? level = Environment.wrap_tzresult (Raw_level.of_int32 level) in + let* consensus_threshold = + (Plugin.RPC.Validators.get_consensus_threshold + cctxt + level + (chain, block) + [@profiler.record_s {verbosity = Debug} "RPC: get consensus threshold"]) + in + return consensus_threshold + let round_proposer state ~level round = let slots = match level with @@ -1183,7 +1220,7 @@ let pp_elected_block fmt {proposal; attestation_qc} = let pp_delegate_slot fmt {delegate; first_slot; attesting_power} = Format.fprintf fmt - "slots: @[first_slot: %a@],@ delegate: %a,@ attesting_power: %d" + "slots: @[first_slot: %a@],@ delegate: %a,@ attesting_power: %Ld" Slot.pp first_slot Delegate.pp @@ -1206,7 +1243,7 @@ let delegate_slots_for_pp delegate_slot_map = |> SlotMap.map (fun {attester; all_slots} -> {attester; all_slots = List.rev all_slots}) -let pp_delegate_slots fmt Delegate_slots.{own_delegate_slots; _} = +let pp_delegate_slots fmt Delegate_slots.{own_delegate_rounds; _} = Format.fprintf fmt "@[%a@]" @@ -1224,7 +1261,7 @@ let pp_delegate_slots fmt Delegate_slots.{own_delegate_slots; _} = ~pp_sep:(fun fmt () -> Format.pp_print_string fmt ",") Slot.pp) (List.filteri (fun i _ -> i < 10) all_slots))) - (SlotMap.bindings (delegate_slots_for_pp own_delegate_slots)) + (SlotMap.bindings (delegate_slots_for_pp own_delegate_rounds)) let pp_prepared_block fmt {signed_block_header; delegate; _} = Format.fprintf @@ -1251,14 +1288,16 @@ let pp_level_state fmt next_level_latest_forge_request; dal_attestable_slots = _; next_level_dal_attestable_slots = _; + consensus_threshold; } = Format.fprintf fmt - "@[Level state:@ current level: %ld@ @[proposal (applied:%b):@ \ - %a@]@ locked round: %a@ attestable payload: %a@ elected block: %a@ @[own delegate slots:@ %a@]@ @[next level own delegate slots:@ %a@]@ \ - next level proposed round: %a@]" + "@[Level state:@ current level: %ld@ consensus threshold: %Ld @ @[proposal (applied:%b):@ %a@]@ locked round: %a@ attestable payload: %a@ \ + elected block: %a@ @[own delegate slots:@ %a@]@ @[next level \ + own delegate slots:@ %a@]@ next level proposed round: %a@]" current_level + consensus_threshold is_latest_proposal_applied pp_proposal latest_proposal diff --git a/src/proto_alpha/lib_delegate/baking_state.mli b/src/proto_alpha/lib_delegate/baking_state.mli index b16fc761fe6a..b480e6da3a52 100644 --- a/src/proto_alpha/lib_delegate/baking_state.mli +++ b/src/proto_alpha/lib_delegate/baking_state.mli @@ -29,13 +29,13 @@ open Baking_state_types (** A delegate slot consists of the delegate's consensus key, its public key hash, its first slot, and its attesting power at some level. *) -type delegate_slot = { +type delegate_info = { delegate : Delegate.t; first_slot : Slot.t; - attesting_power : int; + attesting_power : int64; } -val pp_delegate_slot : Format.formatter -> delegate_slot -> unit +val pp_delegate_slot : Format.formatter -> delegate_info -> unit module Delegate_slots : sig (** Information regarding the slot distribution at some level. *) @@ -43,30 +43,32 @@ module Delegate_slots : sig (** Returns the list of our own delegates that have at least a slot. There are no duplicates, the associated slot is the first one. *) - val own_delegates : t -> delegate_slot list + val own_delegates : t -> delegate_info list + (* (** Returns, among our *own* delegates, the delegate (together with its first attesting slot) that owns the given slot, if any (even if the given slot is not the delegate's first slot). *) - val own_slot_owner : t -> slot:Slot.t -> delegate_slot option + val own_slot_owner : t -> slot:Slot.t -> delegate_info option +*) (** Returns, among our *own* delegates, the delegate (together with its first attesting slot) that owns the given round, if any. *) val own_round_owner : - t -> committee_size:int -> round:Round.t -> delegate_slot option tzresult + t -> committee_size:int -> round:Round.t -> delegate_info option tzresult (** Returns the voting power of the delegate whose first slot is the given slot. Returns [None] if the slot is not the first slot of any delegate. *) - val voting_power : t -> slot:Slot.t -> int option + val voting_power : t -> slot:Slot.t -> int64 option (** Finds the first slot greater than or equal to [slot]. Returns the corresponding (slot, delegate) pair if found, or [None] if no such slot exist. *) - val find_first_slot_from : t -> slot:Slot.t -> (Slot.t * delegate_slot) option + val find_first_slot_from : t -> slot:Slot.t -> (Slot.t * delegate_info) option (** Returns the slot with the smallest index, along with its associated delegate. Returns [None] if the map is empty. *) - val min_slot : t -> (Slot.t * delegate_slot) option + val min_slot : t -> (Slot.t * delegate_info) option end type delegate_slots = Delegate_slots.t @@ -84,6 +86,13 @@ val compute_delegate_slots : Key.t list -> delegate_slots tzresult Lwt.t +val compute_consensus_threshold : + ?block:Block_services.block -> + level:int32 -> + chain:Shell_services.chain -> + Protocol_client_context.full -> + int64 tzresult Lwt.t + (** {2 Consensus operations types functions} *) (* An association list between delegates and promises for their DAL attestations @@ -298,6 +307,8 @@ type level_state = { a promise to obtain the attestable slots for that level. *) next_level_dal_attestable_slots : dal_attestable_slots; (** and similarly for the next level *) + consensus_threshold : int64; + (** The consensus threshold for the current level *) } val pp_level_state : Format.formatter -> level_state -> unit @@ -469,7 +480,7 @@ val update_current_phase : t -> phase -> t that has a proposer slot at the given round and the current or next level, if any. *) val round_proposer : - state -> level:[`Current | `Next] -> Round.t -> delegate_slot option + state -> level:[`Current | `Next] -> Round.t -> delegate_info option (** Memoization wrapper for {!Round.timestamp_of_round}. *) val timestamp_of_round : diff --git a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml index d2b1a462feb7..0319c319089c 100644 --- a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml +++ b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml @@ -184,6 +184,8 @@ let add_consensus_operation consensus_key op_kind recorded_operation map = | Preattestation -> Some {record with preattestation = recorded_operation}) map +(* This function returns the attestation slots for all the validators for the given level. + If/when all bakers attest, then we only use the attesting slot (if it exists). *) let get_validator_rights state cctxt level = let open Lwt_result_syntax in match Validators_cache.find_opt state.validators_rights level with @@ -193,11 +195,24 @@ let get_validator_rights state cctxt level = in let validators = List.fold_left - (fun acc ({consensus_key; slots; _} : RPC.Validators.t) -> - List.fold_left - (fun acc slot -> Slot.Map.add slot consensus_key acc) - acc - slots) + (fun acc + ({ + consensus_key; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + _; + } : + RPC.Validators.t) -> + if use_legacy_for_attestation then + List.fold_left + (fun acc slot -> Slot.Map.add slot consensus_key acc) + acc + slots_legacy + else + match attestation_slot with + | None -> acc + | Some slot -> Slot.Map.add slot consensus_key acc) Slot.Map.empty validators in diff --git a/src/proto_alpha/lib_delegate/node_rpc.ml b/src/proto_alpha/lib_delegate/node_rpc.ml index 309901416ea2..b7de1250e42d 100644 --- a/src/proto_alpha/lib_delegate/node_rpc.ml +++ b/src/proto_alpha/lib_delegate/node_rpc.ml @@ -424,9 +424,9 @@ let dal_attestable_slots (dal_node_rpc_ctxt : Tezos_rpc.Context.generic) ~attestation_level delegate_slots = let attested_level = Int32.succ attestation_level in List.map - (fun (delegate_slot : delegate_slot) -> + (fun (delegate_info : delegate_info) -> let delegate_id = - Baking_state_types.Delegate.delegate_id delegate_slot.delegate + Baking_state_types.Delegate.delegate_id delegate_info.delegate in ( delegate_id, get_attestable_slots dal_node_rpc_ctxt delegate_id ~attested_level )) diff --git a/src/proto_alpha/lib_delegate/node_rpc.mli b/src/proto_alpha/lib_delegate/node_rpc.mli index e8385790e861..4c1edc10ec99 100644 --- a/src/proto_alpha/lib_delegate/node_rpc.mli +++ b/src/proto_alpha/lib_delegate/node_rpc.mli @@ -93,7 +93,7 @@ val fetch_dal_config : val dal_attestable_slots : Tezos_rpc.Context.generic -> attestation_level:int32 -> - Baking_state.delegate_slot list -> + Baking_state.delegate_info list -> Baking_state.dal_attestable_slots (** [get_dal_profiles ctxt delegates] calls the DAL node RPC GET diff --git a/src/proto_alpha/lib_delegate/operation_worker.ml b/src/proto_alpha/lib_delegate/operation_worker.ml index 139c96af3c33..b053437d143b 100644 --- a/src/proto_alpha/lib_delegate/operation_worker.ml +++ b/src/proto_alpha/lib_delegate/operation_worker.ml @@ -36,6 +36,8 @@ module Events = struct let pp_int = Format.pp_print_int + let pp_int64 fmt t = Format.fprintf fmt "%s" (Int64.to_string t) + let loop_failed = declare_1 ~section @@ -61,8 +63,8 @@ module Events = struct ~msg: "prequorum reached (voting power: {voting_power}, {preattestations} \ preattestations)" - ~pp1:pp_int - ("voting_power", Data_encoding.int31) + ~pp1:pp_int64 + ("voting_power", Data_encoding.int64) ~pp2:pp_int ("preattestations", Data_encoding.int31) @@ -84,10 +86,10 @@ module Events = struct power: {voting_power}, {preattestations} preattestations)" ~pp1:pp_int ("count", Data_encoding.int31) - ~pp2:pp_int - ("delta_power", Data_encoding.int31) - ~pp3:pp_int - ("voting_power", Data_encoding.int31) + ~pp2:pp_int64 + ("delta_power", Data_encoding.int64) + ~pp3:pp_int64 + ("voting_power", Data_encoding.int64) ~pp4:pp_int ("preattestations", Data_encoding.int31) @@ -112,8 +114,8 @@ module Events = struct ~msg: "quorum reached (voting power: {voting_power}, {attestations} \ attestations)" - ~pp1:pp_int - ("voting_power", Data_encoding.int31) + ~pp1:pp_int64 + ("voting_power", Data_encoding.int64) ~pp2:pp_int ("attestations", Data_encoding.int31) @@ -127,10 +129,10 @@ module Events = struct power: {voting_power}, {attestations} attestations)" ~pp1:pp_int ("count", Data_encoding.int31) - ~pp2:pp_int - ("delta_power", Data_encoding.int31) - ~pp3:pp_int - ("voting_power", Data_encoding.int31) + ~pp2:pp_int64 + ("delta_power", Data_encoding.int64) + ~pp3:pp_int64 + ("voting_power", Data_encoding.int64) ~pp4:pp_int ("attestations", Data_encoding.int31) @@ -268,9 +270,9 @@ end) type pqc_watched = { candidate_watched : candidate; - get_slot_voting_power : slot:Slot.t -> int option; - consensus_threshold : int; - mutable current_voting_power : int; + get_slot_voting_power : slot:Slot.t -> int64 option; + consensus_threshold : int64; (* TODO Set with new power *) + mutable current_voting_power : int64; mutable preattestations_received : Preattestation_set.t; mutable preattestations_count : int; mutable previous_prequorum_progression : int; @@ -278,9 +280,9 @@ type pqc_watched = { type qc_watched = { candidate_watched : candidate; - get_slot_voting_power : slot:Slot.t -> int option; - consensus_threshold : int; - mutable current_voting_power : int; + get_slot_voting_power : slot:Slot.t -> int64 option; + consensus_threshold : int64; + mutable current_voting_power : int64; mutable attestations_received : Attestation_set.t; mutable attestations_count : int; mutable previous_quorum_progression : int; @@ -389,13 +391,13 @@ let reset_monitoring state = match state.proposal_watched with | None -> return_unit | Some (Pqc_watch pqc_watched) -> - pqc_watched.current_voting_power <- 0 ; + pqc_watched.current_voting_power <- 0L ; pqc_watched.preattestations_count <- 0 ; pqc_watched.preattestations_received <- Preattestation_set.empty ; pqc_watched.previous_prequorum_progression <- 0 ; return_unit | Some (Qc_watch qc_watched) -> - qc_watched.current_voting_power <- 0 ; + qc_watched.current_voting_power <- 0L ; qc_watched.attestations_count <- 0 ; qc_watched.attestations_received <- Attestation_set.empty ; qc_watched.previous_quorum_progression <- 0 ; @@ -441,14 +443,14 @@ let update_pqc_monitoring ~pqc_watched ops = pqc_watched.preattestations_count <- succ pqc_watched.preattestations_count ; pqc_watched.current_voting_power <- - pqc_watched.current_voting_power + op_power + Int64.add pqc_watched.current_voting_power op_power | None -> (* preattestations that do not use the first slot of a delegate are not added to the quorum *) ())) fresh_preattestations ; let additional_voting_power = - pqc_watched.current_voting_power - current_voting_power + Int64.sub pqc_watched.current_voting_power current_voting_power in let additional_preattestations_count = pqc_watched.preattestations_count - preattestations_count @@ -495,14 +497,14 @@ let update_qc_monitoring ~qc_watched ops = qc_watched.attestations_count <- succ qc_watched.attestations_count ; qc_watched.current_voting_power <- - qc_watched.current_voting_power + op_power + Int64.add qc_watched.current_voting_power op_power | None -> (* attestations that do not use the first slot of a delegate are not added to the quorum *) ())) fresh_attestations ; let additional_voting_power = - qc_watched.current_voting_power - current_voting_power + Int64.sub qc_watched.current_voting_power current_voting_power in let additional_attestations_count = qc_watched.attestations_count - attestations_count @@ -526,7 +528,9 @@ let update_monitoring ?(should_lock = true) state ops = let additional_voting_power, additional_preattestations_count = update_pqc_monitoring ~pqc_watched ops in - if pqc_watched.current_voting_power >= pqc_watched.consensus_threshold + if + Compare.Int64.( + pqc_watched.current_voting_power >= pqc_watched.consensus_threshold) then ( let* () = Events.( @@ -547,7 +551,11 @@ let update_monitoring ?(should_lock = true) state ops = else let* () = let current_ratio = - pqc_watched.current_voting_power * 100 / state.committee_size + Int64.( + div + (mul pqc_watched.current_voting_power 100L) + (of_int state.committee_size) + |> to_int) in (* We only want to output an event if the quorum progression has progressed of at least [quorum_progression_increment] *) @@ -595,7 +603,11 @@ let update_monitoring ?(should_lock = true) state ops = else let* () = let current_ratio = - qc_watched.current_voting_power * 100 / state.committee_size + Int64.( + div + (mul qc_watched.current_voting_power 100L) + (of_int state.committee_size) + |> to_int) in (* We only want to output an event if the quorum progression has progressed of at least [quorum_progression_increment] *) @@ -640,7 +652,7 @@ let monitor_preattestation_quorum state ~consensus_threshold candidate_watched; get_slot_voting_power; consensus_threshold; - current_voting_power = 0; + current_voting_power = 0L; preattestations_received = Preattestation_set.empty; preattestations_count = 0; previous_prequorum_progression = 0; @@ -657,7 +669,7 @@ let monitor_attestation_quorum state ~consensus_threshold ~get_slot_voting_power candidate_watched; get_slot_voting_power; consensus_threshold; - current_voting_power = 0; + current_voting_power = 0L; attestations_received = Attestation_set.empty; attestations_count = 0; previous_quorum_progression = 0; diff --git a/src/proto_alpha/lib_delegate/operation_worker.mli b/src/proto_alpha/lib_delegate/operation_worker.mli index 944bfbd54fb9..f50e6e66d7c9 100644 --- a/src/proto_alpha/lib_delegate/operation_worker.mli +++ b/src/proto_alpha/lib_delegate/operation_worker.mli @@ -86,8 +86,8 @@ val get_quorum_event_stream : t -> event Lwt_stream.t *) val monitor_preattestation_quorum : t -> - consensus_threshold:int -> - get_slot_voting_power:(slot:Slot.t -> int option) -> + consensus_threshold:int64 -> + get_slot_voting_power:(slot:Slot.t -> int64 option) -> candidate -> unit Lwt.t @@ -98,8 +98,8 @@ val monitor_preattestation_quorum : *) val monitor_attestation_quorum : t -> - consensus_threshold:int -> - get_slot_voting_power:(slot:Slot.t -> int option) -> + consensus_threshold:int64 -> + get_slot_voting_power:(slot:Slot.t -> int64 option) -> candidate -> unit Lwt.t diff --git a/src/proto_alpha/lib_delegate/state_transitions.ml b/src/proto_alpha/lib_delegate/state_transitions.ml index fff200ff237c..e03ee6e8d5b8 100644 --- a/src/proto_alpha/lib_delegate/state_transitions.ml +++ b/src/proto_alpha/lib_delegate/state_transitions.ml @@ -93,8 +93,8 @@ let make_consensus_vote_batch state proposal kind = let batch_content = {level; round; block_payload_hash} in let delegates_and_slots = List.map - (fun (delegate_slot : delegate_slot) -> - (delegate_slot.delegate, delegate_slot.first_slot)) + (fun (delegate_info : delegate_info) -> + (delegate_info.delegate, delegate_info.first_slot)) (Delegate_slots.own_delegates state.level_state.delegate_slots) in (* The branch is the latest finalized block. *) @@ -425,7 +425,7 @@ let rec handle_proposal ~is_proposal_applied state (new_proposal : proposal) = let new_level = new_proposal.block.shell.level in let compute_new_state ~current_round ~delegate_slots ~next_level_delegate_slots ~dal_attestable_slots - ~next_level_dal_attestable_slots = + ~next_level_dal_attestable_slots ~consensus_threshold = let round_state = { current_round; @@ -449,6 +449,7 @@ let rec handle_proposal ~is_proposal_applied state (new_proposal : proposal) = next_level_latest_forge_request = None; dal_attestable_slots; next_level_dal_attestable_slots; + consensus_threshold; } in (* recursive call with the up-to-date state to handle the new diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index cb5df08aaa88..5baddcdd321f 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -3797,7 +3797,7 @@ module Attestation_rights = struct delegate : Signature.Public_key_hash.t; consensus_key : Signature.Public_key_hash.t; first_slot : Slot.t; - attestation_power : int; + attestation_power : int64; } type t = { @@ -3816,7 +3816,7 @@ module Attestation_rights = struct (obj4 (req "delegate" Signature.Public_key_hash.encoding) (req "first_slot" Slot.encoding) - (req "attestation_power" uint16) + (req "attestation_power" int64) (req "consensus_key" Signature.Public_key_hash.encoding)) let encoding = @@ -3885,6 +3885,9 @@ module Attestation_rights = struct let current_level = Level.current ctxt in let current_timestamp = Timestamp.current ctxt in let round_durations = Alpha_context.Constants.round_durations ctxt in + let all_bakers_attest = + Stake_distribution.check_all_bakers_attest_at_level ctxt level + in let*? estimated_time = estimated_time round_durations @@ -3911,13 +3914,11 @@ module Attestation_rights = struct } : Consensus_key.power) acc -> - { - delegate; - consensus_key; - first_slot; - attestation_power = attestation_power.slots; - } - :: acc) + let attestation_power = + if all_bakers_attest then attestation_power.stake + else Int64.of_int attestation_power.slots + in + {delegate; consensus_key; first_slot; attestation_power} :: acc) rights [] in @@ -3977,27 +3978,65 @@ module Validators = struct delegate : Signature.Public_key_hash.t; consensus_key : Signature.public_key_hash; companion_key : Bls.Public_key_hash.t option; - slots : Slot.t list; + slots_legacy : Slot.t list; + attestation_slot : Slot.t option; + use_legacy_for_attestation : bool; + staking_weight : int64; } let encoding = let open Data_encoding in conv - (fun {level; delegate; consensus_key; companion_key; slots} -> - (level, delegate, slots, consensus_key, companion_key)) - (fun (level, delegate, slots, consensus_key, companion_key) -> - {level; delegate; consensus_key; companion_key; slots}) - (obj5 + (fun { + level; + delegate; + consensus_key; + companion_key; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + staking_weight; + } -> + ( level, + delegate, + slots_legacy, + attestation_slot, + use_legacy_for_attestation, + staking_weight, + consensus_key, + companion_key )) + (fun ( level, + delegate, + slots_legacy, + attestation_slot, + use_legacy_for_attestation, + staking_weight, + consensus_key, + companion_key ) -> + { + level; + delegate; + consensus_key; + companion_key; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + staking_weight; + }) + (obj8 (req "level" Raw_level.encoding) (req "delegate" Signature.Public_key_hash.encoding) - (req "slots" (list Slot.encoding)) + (req "slots_legacy" (list Slot.encoding)) + (req "attestation_slot" (option Slot.encoding)) + (req "use_legacy_for_attestation" bool) + (req "staking_weight" int64) (req "consensus_key" Signature.Public_key_hash.encoding) (opt "companion_key" Bls.Public_key_hash.encoding)) module S = struct open Data_encoding - let path = RPC_path.(path / "validators") + let validators_path = RPC_path.(path / "validators") type validators_query = { levels : Raw_level.t list; @@ -4031,7 +4070,24 @@ module Validators = struct used to restrict the results to the given consensus_keys.\n" ~query:validators_query ~output:(list encoding) - path + validators_path + + let threshold_path = RPC_path.(path / "consensus_threshold") + + type threshold_query = {level : Raw_level.t} + + let threshold_query = + let open RPC_query in + query (fun level -> {level = Raw_level.of_int32_exn level}) + |+ field "level" RPC_arg.int32 0l (fun t -> Raw_level.to_int32 t.level) + |> seal + + let consensus_threshold = + RPC_service.get_service + ~description:"Retrieves the consensus threshold for the given level." + ~query:threshold_query + ~output:Data_encoding.int64 + threshold_path end let add_attestation_slots_at_level (ctxt, acc) level = @@ -4040,13 +4096,32 @@ module Validators = struct let aggregate_attestation = Constants.aggregate_attestation ctxt in ( ctxt, Signature.Public_key_hash.Map.fold - (fun _pkh {Baking.delegate; consensus_key; companion_key; slots} acc -> + (fun _pkh + { + Baking.delegate; + consensus_key; + companion_key; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + staking_weight; + } + acc -> let companion_key = match consensus_key with | Bls _ when aggregate_attestation -> companion_key | _ -> None in - {level = level.level; delegate; consensus_key; companion_key; slots} + { + level = level.level; + delegate; + consensus_key; + companion_key; + slots_legacy; + attestation_slot; + use_legacy_for_attestation; + staking_weight; + } :: acc) rights acc ) @@ -4085,7 +4160,17 @@ module Validators = struct in List.filter is_requested rights in - rights) + rights) ; + Registration.register0 + ~chunked:false + S.consensus_threshold + (fun ctxt q () -> + let* _ctxt, level = + Stake_distribution.get_consensus_threshold + ctxt + (Level.from_raw ctxt q.level) + in + return level) let get ctxt ?(levels = []) ?(delegates = []) ?(consensus_keys = []) block = RPC_context.make_call0 @@ -4094,6 +4179,9 @@ module Validators = struct block {levels; delegates; consensus_keys} () + + let get_consensus_threshold ctxt level block = + RPC_context.make_call0 S.consensus_threshold ctxt block {level} () end let get_blocks_preservation_cycles ~get_context = diff --git a/src/proto_alpha/lib_plugin/dal_services.ml b/src/proto_alpha/lib_plugin/dal_services.ml index dd880a694adb..af44dd23ee0a 100644 --- a/src/proto_alpha/lib_plugin/dal_services.ml +++ b/src/proto_alpha/lib_plugin/dal_services.ml @@ -37,7 +37,11 @@ let shards ctxt ~level = let*? slots = Slot.Range.create ~min:0 ~count:number_of_shards in Slot.Range.rev_fold_es (fun (ctxt, map) slot -> - let* ctxt, consensus_pk = Stake_distribution.slot_owner ctxt level slot in + (* DAL shard ownership follows the random sampler order. Also see + [Baking.attesting_rights_by_first_slot] *) + let* ctxt, consensus_pk = + Stake_distribution.slot_owner_legacy ctxt level slot + in let slot = Slot.to_int slot in let map = Signature.Public_key_hash.Map.update diff --git a/src/proto_alpha/lib_plugin/mempool.ml b/src/proto_alpha/lib_plugin/mempool.ml index dbc2c72b3fa4..9301250c1c27 100644 --- a/src/proto_alpha/lib_plugin/mempool.ml +++ b/src/proto_alpha/lib_plugin/mempool.ml @@ -823,7 +823,7 @@ let get_context context ~(head : Tezos_base.Block_header.shell_header) = let sources_from_level_and_slot ctxt level slot = let open Lwt_syntax in - let* slot_owner = Stake_distribution.slot_owner ctxt level slot in + let* slot_owner = Stake_distribution.attestation_owner ctxt level slot in match slot_owner with | Ok ( _ctxt, diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 08c3eac02073..a04e3ca57bd5 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -630,14 +630,21 @@ module Stake_distribution = struct let baking_rights_owner = Delegate_sampler.baking_rights_owner - let slot_owner = Delegate_sampler.slot_owner + let slot_owner_legacy = Delegate_sampler.slot_owner_legacy + + let attestation_owner = Delegate_sampler.attestation_owner let stake_info = Delegate_sampler.stake_info + let get_consensus_threshold = Delegate_sampler.get_consensus_threshold + let load_sampler_for_cycle = Delegate_sampler.load_sampler_for_cycle let load_stake_info_for_cycle = Delegate_sampler.load_stake_info_for_cycle + let get_total_active_stake ctxt cycle = + Stake_storage.get_total_active_stake ctxt cycle + let get_total_frozen_stake ctxt cycle = let open Lwt_result_syntax in let* total_stake = Stake_storage.get_total_active_stake ctxt cycle in diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 4965c7c28b66..b3e3efb4819e 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -155,6 +155,10 @@ module Tez : sig (** See {!Tez_repr.mul_percentage}. *) val mul_percentage : rounding:[`Down | `Up] -> t -> Percentage.t -> t + + (** See {!Tez_repr.mul_ratio}. *) + val mul_ratio : + rounding:[`Down | `Up] -> t -> num:int64 -> den:int64 -> t tzresult end (** This module re-exports definitions from {!Staking_pseudotoken_repr}. *) @@ -2418,8 +2422,12 @@ module Delegate : sig val baking_reward_bonus_per_slot : t -> Tez.t tzresult + val baking_reward_bonus_max_per_block : t -> Tez.t tzresult + val attesting_reward_per_slot : t -> Tez.t tzresult + val attesting_reward_max_per_block : t -> Tez.t tzresult + val dal_attesting_reward_per_shard : t -> Tez.t tzresult val liquidity_baking_subsidy : t -> Tez.t tzresult @@ -2432,7 +2440,9 @@ module Delegate : sig type reward_kind = | Baking_reward_fixed_portion | Baking_reward_bonus_per_slot + | Baking_reward_bonus_max_per_block | Attesting_reward_per_slot + | Attesting_reward_max_per_block | Dal_attesting_reward_per_shard | Seed_nonce_revelation_tip | Vdf_revelation_tip @@ -5218,7 +5228,10 @@ module Stake_distribution : sig round:Round.t -> (context * Slot.t * Consensus_key.pk) tzresult Lwt.t - val slot_owner : + val slot_owner_legacy : + context -> Level.t -> Slot.t -> (context * Consensus_key.pk) tzresult Lwt.t + + val attestation_owner : context -> Level.t -> Slot.t -> (context * Consensus_key.pk) tzresult Lwt.t val stake_info : @@ -5226,12 +5239,17 @@ module Stake_distribution : sig Level.t -> (context * Int64.t * (Consensus_key.pk * Int64.t) list) tzresult Lwt.t + val get_consensus_threshold : + context -> Level.t -> (context * int64) tzresult Lwt.t + (** See {!Delegate_sampler.load_sampler_for_cycle}. *) val load_sampler_for_cycle : context -> Cycle.t -> context tzresult Lwt.t (** See {!Delegate_sampler.load_stake_info_for_cycle}. *) val load_stake_info_for_cycle : context -> Cycle.t -> context tzresult Lwt.t + val get_total_active_stake : context -> Cycle.t -> Stake_repr.t tzresult Lwt.t + val get_total_frozen_stake : context -> Cycle.t -> Tez.t tzresult Lwt.t module For_RPC : sig diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 6b82310a1ff3..d193119e8a88 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2340,7 +2340,7 @@ let record_preattestation ctxt (mode : mode) (content : consensus_content) : to finalize anyway in this mode. *) let* ctxt, consensus_key = let level = Level.from_raw ctxt content.level in - Stake_distribution.slot_owner ctxt level content.slot + Stake_distribution.attestation_owner ctxt level content.slot in return ( ctxt, @@ -2395,7 +2395,7 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) to finalize anyway in this mode. *) let* ctxt, consensus_key = let level = Level.from_raw ctxt consensus.level in - Stake_distribution.slot_owner ctxt level consensus.slot + Stake_distribution.attestation_owner ctxt level consensus.slot in return ( ctxt, @@ -2581,7 +2581,9 @@ let punish_double_consensus_operation (type kind) ctxt ~operation_hash Misbehaviour.{level; round; kind = Double_attesting} in let level = Level.from_raw ctxt misbehaviour.level in - let* ctxt, {delegate; _} = Stake_distribution.slot_owner ctxt level slot in + let* ctxt, {delegate; _} = + Stake_distribution.attestation_owner ctxt level slot + in let* ctxt, contents_result = punish_double_signing ctxt @@ -2694,7 +2696,7 @@ let apply_contents_list (type kind) ctxt chain_id (mode : mode) in let level = Level.from_raw ctxt level in let* ctxt, consensus_pk = - Stake_distribution.slot_owner ctxt level consensus_slot + Stake_distribution.attestation_owner ctxt level consensus_slot in let delegate = consensus_pk.delegate in let*! ctxt, _already_denounced = @@ -3235,7 +3237,7 @@ let finalize_application ctxt block_data_contents ~round ~predecessor_hash if Round.(round = zero) then Consecutive_round_zero.incr ctxt else Consecutive_round_zero.reset ctxt in - (* end of level *) + (* end of level *) let* ctxt = match block_data_contents.Block_header.seed_nonce_hash with | None -> return ctxt @@ -3249,11 +3251,10 @@ let finalize_application ctxt block_data_contents ~round ~predecessor_hash let all_bakers_attest_enabled = Stake_distribution.check_all_bakers_attest_at_level ctxt level in - let*? rewards_bonus = + let* rewards_bonus = if all_bakers_attest_enabled then - error Validate_errors.Consensus.All_bakers_attest_not_implemented - (* TODO ABAAB *) - else Baking.bonus_baking_reward ctxt ~attestation_power + Baking.bonus_baking_reward_precise ctxt ~attestation_weight + else Lwt.return @@ Baking.bonus_baking_reward ctxt ~attestation_power in return (ctxt, Some rewards_bonus) else return (ctxt, None) diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index fd49c03e5bb4..4ba6668b2b8a 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -32,6 +32,11 @@ type error += attestation_power : int; consensus_threshold : int; } + | (* `Permanent *) + Insufficient_attestation_weight of { + attestation_weight : int64; + consensus_threshold : int64; + } let () = register_error_kind @@ -56,7 +61,30 @@ let () = Some (attestation_power, consensus_threshold) | _ -> None) (fun (attestation_power, consensus_threshold) -> - Insufficient_attestation_power {attestation_power; consensus_threshold}) + Insufficient_attestation_power {attestation_power; consensus_threshold}) ; + register_error_kind + `Permanent + ~id:"baking.insufficient_attestation_weight" + ~title:"Insufficient attestation weight" + ~description: + "The attestation weight is insufficient to satisfy the consensus \ + threshold." + ~pp:(fun ppf (attestation_weight, consensus_threshold) -> + Format.fprintf + ppf + "The attestation weight (%Ld) is insufficient to satisfy the consensus \ + threshold (%Ld)." + attestation_weight + consensus_threshold) + Data_encoding.( + obj2 (req "attestation_weight" int64) (req "consensus_threshold" int64)) + (function + | Insufficient_attestation_weight + {attestation_weight; consensus_threshold} -> + Some (attestation_weight, consensus_threshold) + | _ -> None) + (fun (attestation_weight, consensus_threshold) -> + Insufficient_attestation_weight {attestation_weight; consensus_threshold}) let bonus_baking_reward ctxt ~attestation_power = let open Result_syntax in @@ -75,35 +103,103 @@ let bonus_baking_reward ctxt ~attestation_power = in Tez.(baking_reward_bonus_per_slot *? Int64.of_int extra_attestation_power) +let bonus_baking_reward_precise ctxt ~attestation_weight = + let open Lwt_result_syntax in + let*? baking_reward_bonus_max_per_block = + Delegate.Rewards.baking_reward_bonus_max_per_block ctxt + in + let* total_active_stake = + Stake_distribution.get_total_active_stake ctxt (Level.current ctxt).cycle + in + let total_active_stake_weight = + Stake_repr.staking_weight total_active_stake + in + (* The consensus threshold ratio is 2/3 *) + let consensus_threshold = Int64.(mul (div total_active_stake_weight 3L) 2L) in + let extra_attestation_weight = + Int64.sub attestation_weight consensus_threshold + in + let max_attestation_weight = + Int64.sub total_active_stake_weight consensus_threshold + in + let*? () = + error_when + Compare.Int64.(extra_attestation_weight < 0L) + (Insufficient_attestation_weight {attestation_weight; consensus_threshold}) + in + Lwt.return + @@ Tez.mul_ratio + ~rounding:`Up + baking_reward_bonus_max_per_block + ~num:extra_attestation_weight + ~den:max_attestation_weight + type ordered_slots = { delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; companion_key : Bls.Public_key_hash.t option; - slots : Slot.t list; + slots_legacy : Slot.t list; + attestation_slot : Slot.t option; + use_legacy_for_attestation : bool; + staking_weight : int64; } (* Slots returned by this function are assumed by consumers to be in increasing order, hence the use of [Slot.Range.rev_fold_es]. *) let attesting_rights (ctxt : t) level = - let consensus_committee_size = Constants.consensus_committee_size ctxt in let open Lwt_result_syntax in + let consensus_committee_size = Constants.consensus_committee_size ctxt in + let use_legacy_for_attestation = + not (Stake_distribution.check_all_bakers_attest_at_level ctxt level) + in + let* ctxt, _, attesters_list = Stake_distribution.stake_info ctxt level in + let* attestation_slot_map, _ = + List.fold_left_es + (fun (acc_map, i) ((consensus_pk : Consensus_key.pk), power) -> + let acc_map = + Signature.Public_key_hash.Map.add + consensus_pk.delegate + (i, power) + acc_map + in + let*? succ_i = Slot.succ i in + return (acc_map, succ_i)) + (Signature.Public_key_hash.Map.empty, Slot.zero) + attesters_list + in let*? slots = Slot.Range.create ~min:0 ~count:consensus_committee_size in Slot.Range.rev_fold_es (fun (ctxt, map) slot -> - let* ctxt, consensus_pk = Stake_distribution.slot_owner ctxt level slot in + let* ctxt, consensus_pk = + Stake_distribution.slot_owner_legacy ctxt level slot + in let map = Signature.Public_key_hash.Map.update consensus_pk.delegate (function | None -> + (* This should never return None *) + let attestation_slot, staking_weight = + match + Signature.Public_key_hash.Map.find + consensus_pk.delegate + attestation_slot_map + with + | None -> (None, 0L) + | Some (a, b) -> (Some a, b) + in Some { delegate = consensus_pk.delegate; consensus_key = consensus_pk.consensus_pkh; companion_key = consensus_pk.companion_pkh; - slots = [slot]; + slots_legacy = [slot]; + attestation_slot; + use_legacy_for_attestation; + staking_weight; } - | Some slots -> Some {slots with slots = slot :: slots.slots}) + | Some slots -> + Some {slots with slots_legacy = slot :: slots.slots_legacy}) map in return (ctxt, map)) @@ -121,7 +217,7 @@ let attesting_rights_by_first_slot ctxt level : Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> let+ ctxt, consensus_key = - Stake_distribution.slot_owner ctxt level slot + Stake_distribution.slot_owner_legacy ctxt level slot in let initial_slot, delegates_map = match @@ -176,27 +272,49 @@ let attesting_rights_by_first_slot ctxt level : (ctxt, (Signature.Public_key_hash.Map.empty, Slot.Map.empty)) slots in + let all_bakers_attest_enabled = + Stake_distribution.check_all_bakers_attest_at_level ctxt level + in let* ctxt, _, stake_info_list = Stake_distribution.stake_info ctxt level in - let slots_map = - List.fold_left - (fun acc ((consensus_pk, weight) : Consensus_key.pk * Int64.t) -> + let*? slots_map, _ = + let open Result_syntax in + List.fold_left_e + (fun (acc, i) ((consensus_key, weight) : Consensus_key.pk * Int64.t) -> match - Signature.Public_key_hash.Map.find consensus_pk.delegate delegates_map + Signature.Public_key_hash.Map.find + consensus_key.delegate + delegates_map with - | None -> acc + | None -> + if all_bakers_attest_enabled then + let* succ_i = Slot.succ i in + return + ( Slot.Map.add + i + { + Consensus_key.consensus_key; + attestation_power = {slots = 0; stake = weight}; + dal_power = 0; + } + acc, + succ_i ) + else return (acc, i) | Some slot -> ( match Slot.Map.find slot slots_map with - | None -> acc (* Impossible by construction *) + | None -> (acc, i) (* Impossible by construction *) | Some v -> - Slot.Map.add - slot + let v = { v with attestation_power = {v.attestation_power with stake = weight}; } - acc)) - Slot.Map.empty + in + if all_bakers_attest_enabled then + let* succ_i = Slot.succ i in + return (Slot.Map.add i v acc, succ_i) + else return (Slot.Map.add slot v acc, i))) + (Slot.Map.empty, Slot.zero) stake_info_list in return (ctxt, slots_map) diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index 20cee99b3835..fd724e89039a 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -37,7 +37,10 @@ type ordered_slots = private { delegate : Signature.public_key_hash; consensus_key : Signature.public_key_hash; companion_key : Bls.Public_key_hash.t option; - slots : Slot.t list; + slots_legacy : Slot.t list; + attestation_slot : Slot.t option; + use_legacy_for_attestation : bool; + staking_weight : int64; } (** For a given level computes who has the right to include an attestation in @@ -46,7 +49,7 @@ type ordered_slots = private { @return map from delegates with such rights to their attesting slots, in increasing order. - This function is only used by the 'validators' RPC. *) + This function must only be used by the 'validators' RPC. *) val attesting_rights : context -> Level.t -> @@ -64,3 +67,7 @@ val attesting_rights_by_first_slot : (** Computes the bonus baking reward depending on the attestation power. *) val bonus_baking_reward : context -> attestation_power:Attestation_power_repr.t -> Tez.t tzresult + +(** Computes the bonus baking reward depending on the attestation weight. *) +val bonus_baking_reward_precise : + context -> attestation_weight:int64 -> Tez.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index 38b0e96c9c64..5ea2174d485b 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -135,6 +135,9 @@ let distribute_attesting_rewards ctxt last_cycle unrevealed_nonces = let*? attesting_reward_per_slot = Delegate_rewards.attesting_reward_per_slot ctxt in + let*? attesting_reward_max_per_block = + Delegate_rewards.attesting_reward_max_per_block ctxt + in let unrevealed_nonces_set = List.fold_left (fun set {Storage.Seed.nonce_hash = _; delegate} -> @@ -159,6 +162,14 @@ let distribute_attesting_rewards ctxt last_cycle unrevealed_nonces = return @@ Some dal_attesting_reward_per_shard) in let* delegates = Stake_storage.get_selected_distribution ctxt last_cycle in + let all_bakers_attest_enabled = + Delegate_sampler.check_all_bakers_attest_at_level + ctxt + (Raw_context.current_level ctxt) + in + let blocks_per_cycle = + Int32.to_int (Constants_storage.blocks_per_cycle ctxt) + in List.fold_left_es (fun (ctxt, balance_updates) (delegate, active_stake) -> let* ctxt, sufficient_participation = @@ -171,14 +182,31 @@ let distribute_attesting_rewards ctxt last_cycle unrevealed_nonces = delegate_has_revealed_nonces delegate unrevealed_nonces_set in let active_stake_weight = Stake_repr.staking_weight active_stake in - let expected_slots = - Delegate_missed_attestations_storage - .expected_slots_for_given_active_stake - ctxt - ~total_active_stake_weight - ~active_stake_weight + let* rewards = + if all_bakers_attest_enabled then + (* This is the maximum amount of rewards distributed amongst all the delegates. + This should not overflow under regular conditions *) + let*? attesting_reward_max_per_cycle = + Tez_repr.( + attesting_reward_max_per_block *? Int64.of_int blocks_per_cycle) + in + Lwt.return + @@ Tez_repr.mul_ratio + ~rounding:`Up + attesting_reward_max_per_cycle + ~num:active_stake_weight + ~den:total_active_stake_weight + else + let expected_slots = + Delegate_missed_attestations_storage + .expected_slots_for_given_active_stake + ctxt + ~total_active_stake_weight + ~active_stake_weight + ~blocks_per_cycle + in + return @@ Tez_repr.mul_exn attesting_reward_per_slot expected_slots in - let rewards = Tez_repr.mul_exn attesting_reward_per_slot expected_slots in let gets_consensus_rewards = sufficient_participation && has_revealed_nonces in diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml index 6de36bb5fe2f..92e154e879cc 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.ml @@ -26,10 +26,7 @@ (*****************************************************************************) let expected_slots_for_given_active_stake ctxt ~total_active_stake_weight - ~active_stake_weight = - let blocks_per_cycle = - Int32.to_int (Constants_storage.blocks_per_cycle ctxt) - in + ~active_stake_weight ~blocks_per_cycle = let consensus_committee_size = Constants_storage.consensus_committee_size ctxt in @@ -97,11 +94,16 @@ let record_attesting_participation ctxt ~delegate ~participation assert (Compare.Int32.(level.cycle_position = 0l)) ; return ctxt | Some (_, active_stake_weight) -> + let blocks_per_cycle = + Int32.to_int (Constants_storage.blocks_per_cycle ctxt) + in + let expected_slots = expected_slots_for_given_active_stake ctxt ~total_active_stake_weight ~active_stake_weight + ~blocks_per_cycle in let Ratio_repr.{numerator; denominator} = Constants_storage.minimal_participation_ratio ctxt @@ -297,10 +299,14 @@ module For_RPC = struct let total_active_stake_weight = Stake_repr.staking_weight total_active_stake in + let blocks_per_cycle = + Int32.to_int (Constants_storage.blocks_per_cycle ctxt) + in expected_slots_for_given_active_stake ctxt ~total_active_stake_weight ~active_stake_weight + ~blocks_per_cycle in let Ratio_repr.{numerator; denominator} = Constants_storage.minimal_participation_ratio ctxt diff --git a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli index ef8366b62c10..fb3fb52c8c14 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_missed_attestations_storage.mli @@ -37,6 +37,7 @@ val expected_slots_for_given_active_stake : Raw_context.t -> total_active_stake_weight:int64 -> active_stake_weight:int64 -> + blocks_per_cycle:int -> int (** Computes the number of DAL shards that a delegate is expected to be diff --git a/src/proto_alpha/lib_protocol/delegate_rewards.ml b/src/proto_alpha/lib_protocol/delegate_rewards.ml index c020ca33982e..13098852671f 100644 --- a/src/proto_alpha/lib_protocol/delegate_rewards.ml +++ b/src/proto_alpha/lib_protocol/delegate_rewards.ml @@ -75,7 +75,9 @@ module M = struct type reward_kind = | Baking_reward_fixed_portion | Baking_reward_bonus_per_slot + | Baking_reward_bonus_max_per_block | Attesting_reward_per_slot + | Attesting_reward_max_per_block | Dal_attesting_reward_per_shard | Seed_nonce_revelation_tip | Vdf_revelation_tip @@ -89,9 +91,10 @@ module M = struct match reward_kind with | Baking_reward_fixed_portion -> issuance_weights.baking_reward_fixed_portion_weight - | Baking_reward_bonus_per_slot -> + | Baking_reward_bonus_per_slot | Baking_reward_bonus_max_per_block -> issuance_weights.baking_reward_bonus_weight - | Attesting_reward_per_slot -> issuance_weights.attesting_reward_weight + | Attesting_reward_per_slot | Attesting_reward_max_per_block -> + issuance_weights.attesting_reward_weight | Dal_attesting_reward_per_shard -> if dal_incentives_enabled then issuance_weights.dal_rewards_weight else 0 @@ -156,9 +159,15 @@ let baking_reward_fixed_portion ctxt = let baking_reward_bonus_per_slot ctxt = reward_from_context ~ctxt ~reward_kind:Baking_reward_bonus_per_slot +let baking_reward_bonus_max_per_block ctxt = + reward_from_context ~ctxt ~reward_kind:Baking_reward_bonus_max_per_block + let attesting_reward_per_slot ctxt = reward_from_context ~ctxt ~reward_kind:Attesting_reward_per_slot +let attesting_reward_max_per_block ctxt = + reward_from_context ~ctxt ~reward_kind:Attesting_reward_max_per_block + let dal_attesting_reward_per_shard ctxt = reward_from_context ~ctxt ~reward_kind:Dal_attesting_reward_per_shard diff --git a/src/proto_alpha/lib_protocol/delegate_rewards.mli b/src/proto_alpha/lib_protocol/delegate_rewards.mli index 0aa8300da781..f77bd210476a 100644 --- a/src/proto_alpha/lib_protocol/delegate_rewards.mli +++ b/src/proto_alpha/lib_protocol/delegate_rewards.mli @@ -29,8 +29,12 @@ val baking_reward_fixed_portion : Raw_context.t -> Tez_repr.t tzresult val baking_reward_bonus_per_slot : Raw_context.t -> Tez_repr.t tzresult +val baking_reward_bonus_max_per_block : Raw_context.t -> Tez_repr.t tzresult + val attesting_reward_per_slot : Raw_context.t -> Tez_repr.t tzresult +val attesting_reward_max_per_block : Raw_context.t -> Tez_repr.t tzresult + val dal_attesting_reward_per_shard : Raw_context.t -> Tez_repr.t tzresult val liquidity_baking_subsidy : Raw_context.t -> Tez_repr.t tzresult @@ -43,7 +47,9 @@ module For_RPC : sig type reward_kind = | Baking_reward_fixed_portion | Baking_reward_bonus_per_slot + | Baking_reward_bonus_max_per_block | Attesting_reward_per_slot + | Attesting_reward_max_per_block | Dal_attesting_reward_per_shard | Seed_nonce_revelation_tip | Vdf_revelation_tip diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index b271dec004aa..635ab3a74ba9 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -146,7 +146,8 @@ let check_all_bakers_attest_at_level ctxt level = | None -> false | Some act_level -> Raw_level_repr.(level.Level_repr.level >= act_level) -let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) +let slot_owner_legacy c level slot = + Random.owner c level (Slot_repr.to_int slot) let baking_rights_owner c (level : Level_repr.t) ~round = let open Lwt_result_syntax in @@ -198,6 +199,39 @@ let load_stake_info_for_cycle ctxt cycle = in return ctxt +let get_consensus_threshold ctxt level = + let open Lwt_result_syntax in + let all_bakers_attest_enabled = check_all_bakers_attest_at_level ctxt level in + let consensus_threshold_size = + Int64.of_int (Constants_storage.consensus_threshold_size ctxt) + in + if all_bakers_attest_enabled then + let* ctxt, total_weight, _info = stake_info ctxt level in + let consensus_committee_size = + Int64.of_int (Constants_storage.consensus_committee_size ctxt) + in + (* Since consensus_threshold < consensus_committee_size, we avoid overflowing *) + (* Since total_weight >= 6_000_000 > consensus_committee_size, roundings are + insignificant *) + return + ( ctxt, + Int64.( + mul + (div total_weight consensus_committee_size) + consensus_threshold_size) ) + else return (ctxt, consensus_threshold_size) + +let attestation_owner ctxt level slot = + let open Lwt_result_syntax in + let all_bakers_attest_enabled = check_all_bakers_attest_at_level ctxt level in + if all_bakers_attest_enabled then + let* ctxt, _, info = stake_info ctxt level in + let i = Slot_repr.to_int slot in + match List.nth info i with + | None -> failwith "TODO: make a proper error" + | Some (owner, _) -> return (ctxt, owner) + else Random.owner ctxt level (Slot_repr.to_int slot) + let get_delegate_stake_from_staking_balance ctxt delegate staking_balance = let open Lwt_result_syntax in let* staking_parameters = @@ -284,7 +318,9 @@ let clear_outdated_sampling_data ctxt ~new_cycle = let* ctxt = Delegate_sampler_state.remove_existing ctxt outdated_cycle in Seed_storage.remove_for_cycle ctxt outdated_cycle +(* This function MUST be called with legacy slots *) let attesting_rights_count ctxt level = + assert (not (check_all_bakers_attest_at_level ctxt level)) ; let consensus_committee_size = Constants_storage.consensus_committee_size ctxt in @@ -292,7 +328,7 @@ let attesting_rights_count ctxt level = let*? slots = Slot_repr.Range.create ~min:0 ~count:consensus_committee_size in Slot_repr.Range.fold_es (fun (ctxt, map) slot -> - let* ctxt, consensus_pk = slot_owner ctxt level slot in + let* ctxt, consensus_pk = slot_owner_legacy ctxt level slot in let map = Signature.Public_key_hash.Map.update consensus_pk.delegate diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.mli b/src/proto_alpha/lib_protocol/delegate_sampler.mli index 6dd5bd5f9adc..af2d0ed6afc9 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.mli +++ b/src/proto_alpha/lib_protocol/delegate_sampler.mli @@ -40,7 +40,13 @@ freeze or initialize the protocol while stitching. RPCs can use this function to predict an approximation of long term future slot allocations. It shouldn't be used in the baker. *) -val slot_owner : +val attestation_owner : + Raw_context.t -> + Level_repr.t -> + Slot_repr.t -> + (Raw_context.t * Delegate_consensus_key.pk) tzresult Lwt.t + +val slot_owner_legacy : Raw_context.t -> Level_repr.t -> Slot_repr.t -> @@ -56,7 +62,7 @@ val baking_rights_owner : sampler for [cycle] in [ctxt]. If the sampler was already cached, then [ctxt] is returned unchanged. - This function has the same effect on [ctxt] as {!slot_owner} and + This function has the same effect on [ctxt] as {!slot_owner_legacy} and {!baking_rights_owner}. *) val load_sampler_for_cycle : Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t @@ -72,7 +78,8 @@ val stake_info : for [cycle] in [ctxt]. If the stake info was already cached, then [ctxt] is returned unchanged. - This function has the same effect on [ctxt] as {!stake_info} *) + This function has the same effect on [ctxt] as {!stake_info} + and {!get_consensus_threshold}. *) val load_stake_info_for_cycle : Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t @@ -93,6 +100,12 @@ val attesting_rights_count : Level_repr.t -> (Raw_context.t * int Signature.Public_key_hash.Map.t) tzresult Lwt.t +(** [get_consensus_threshold ctxt level] returns the consensus threshold at a given level. + If [all_bakers_attest] is false for the given level, it returns the usual value + (4667 on mainnet). Otherwise it return 2/3rd of the total staking power. *) +val get_consensus_threshold : + Raw_context.t -> Level_repr.t -> (Raw_context.t * int64) tzresult Lwt.t + (** [check_all_bakers_attest_at_level ctxt level] checks that at the given level, all bakers were allowed (and expected) to attest. *) val check_all_bakers_attest_at_level : Raw_context.t -> Level_repr.t -> bool diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 4be507fadfec..1e42f650db86 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -132,14 +132,20 @@ let init_consensus_rights_for_block ctxt mode ~predecessor_level = let* ctxt, attestations_map = Baking.attesting_rights_by_first_slot ctxt predecessor_level in + let* ctxt, predecessor_consensus_threshold = + Stake_distribution.get_consensus_threshold ctxt predecessor_level + in let*? can_contain_preattestations = can_contain_preattestations mode in - let* ctxt, allowed_preattestations = + let* ctxt, allowed_preattestations, consensus_threshold = if can_contain_preattestations then let* ctxt, preattestations_map = Baking.attesting_rights_by_first_slot ctxt (Level.current ctxt) in - return (ctxt, Some preattestations_map) - else return (ctxt, None) + let* ctxt, consensus_threshold = + Stake_distribution.get_consensus_threshold ctxt (Level.current ctxt) + in + return (ctxt, Some preattestations_map, consensus_threshold) + else return (ctxt, None, 0L) in let ctxt = Consensus.initialize_consensus_operation @@ -147,6 +153,8 @@ let init_consensus_rights_for_block ctxt mode ~predecessor_level = ~allowed_attestations:(Some attestations_map) ~allowed_preattestations ~allowed_consensus:None + ~predecessor_consensus_threshold + ~consensus_threshold in return ctxt @@ -186,6 +194,8 @@ let init_consensus_rights_for_mempool ctxt ~predecessor_level = ~allowed_attestations:None ~allowed_preattestations:None ~allowed_consensus:(Some minimal_slots) + ~predecessor_consensus_threshold:0L + ~consensus_threshold:0L in return ctxt diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index b86be5abd6db..7a598b1e6363 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -122,6 +122,8 @@ module Raw_consensus = struct allowed_consensus : consensus_power Slot_repr.Map.t Level_repr.Map.t option; (** In mempool mode, hold delegates minimal slots for all allowed levels. [None] in all other modes. *) + predecessor_consensus_threshold : int64; (** TODO *) + consensus_threshold : int64; (** TODO *) forbidden_delegates : Signature.Public_key_hash.Set.t; (** Delegates that are not allowed to bake or attest blocks; i.e., delegates which have zero frozen deposit due to a previous @@ -155,6 +157,8 @@ module Raw_consensus = struct allowed_attestations = Some Slot_repr.Map.empty; allowed_preattestations = Some Slot_repr.Map.empty; allowed_consensus = None; + predecessor_consensus_threshold = 0L; + consensus_threshold = 0L; forbidden_delegates = Signature.Public_key_hash.Set.empty; attestations_seen = Slot_repr.Set.empty; preattestations_seen = Slot_repr.Set.empty; @@ -241,8 +245,16 @@ module Raw_consensus = struct | None -> {t with preattestations_quorum_round = Some round} let set_allowed_operations ~allowed_attestations ~allowed_preattestations - ~allowed_consensus t = - {t with allowed_attestations; allowed_preattestations; allowed_consensus} + ~allowed_consensus ~predecessor_consensus_threshold ~consensus_threshold t + = + { + t with + allowed_attestations; + allowed_preattestations; + allowed_consensus; + predecessor_consensus_threshold; + consensus_threshold; + } let locked_round_evidence t = t.locked_round_evidence @@ -2161,6 +2173,10 @@ module type CONSENSUS = sig val allowed_consensus : t -> consensus_power slot_map level_map option + val predecessor_consensus_threshold : t -> Int64.t + + val consensus_threshold : t -> Int64.t + val forbidden_delegates : t -> Signature.Public_key_hash.Set.t type error += Slot_map_not_found of {loc : string} @@ -2172,6 +2188,8 @@ module type CONSENSUS = sig allowed_attestations:consensus_power slot_map option -> allowed_preattestations:consensus_power slot_map option -> allowed_consensus:consensus_power slot_map level_map option -> + predecessor_consensus_threshold:Int64.t -> + consensus_threshold:Int64.t -> t val record_attestation : @@ -2226,6 +2244,12 @@ module Consensus : let[@inline] allowed_consensus ctxt = ctxt.back.consensus.allowed_consensus + let[@inline] predecessor_consensus_threshold ctxt = + ctxt.back.consensus.predecessor_consensus_threshold + + let[@inline] consensus_threshold ctxt = + ctxt.back.consensus.consensus_threshold + let[@inline] forbidden_delegates ctxt = ctxt.back.consensus.forbidden_delegates @@ -2242,13 +2266,16 @@ module Consensus : Raw_consensus.locked_round_evidence ctxt.back.consensus let[@inline] initialize_consensus_operation ctxt ~allowed_attestations - ~allowed_preattestations ~allowed_consensus = + ~allowed_preattestations ~allowed_consensus + ~predecessor_consensus_threshold ~consensus_threshold = update_consensus_with ctxt (Raw_consensus.set_allowed_operations ~allowed_attestations ~allowed_preattestations - ~allowed_consensus) + ~allowed_consensus + ~predecessor_consensus_threshold + ~consensus_threshold) let[@inline] record_preattestation ctxt ~initial_slot ~power round = update_consensus_with_tzresult diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 14d52d2ac6a7..a4492f559a72 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -391,6 +391,12 @@ module type CONSENSUS = sig (minimal_slot, voting_power, dal_power). See {!allowed_attestations} *) val allowed_consensus : t -> consensus_power slot_map level_map option + (** TODO *) + val predecessor_consensus_threshold : t -> Int64.t + + (** TODO *) + val consensus_threshold : t -> Int64.t + (** Returns the set of delegates that are not allowed to bake or attest blocks; i.e., delegates which have zero frozen deposit due to a previous slashing. *) @@ -411,6 +417,8 @@ module type CONSENSUS = sig allowed_attestations:consensus_power slot_map option -> allowed_preattestations:consensus_power slot_map option -> allowed_consensus:consensus_power slot_map level_map option -> + predecessor_consensus_threshold:Int64.t -> + consensus_threshold:Int64.t -> t (** [record_attestation ctx ~initial_slot ~power] records an diff --git a/src/proto_alpha/lib_protocol/slash_percentage.ml b/src/proto_alpha/lib_protocol/slash_percentage.ml index b7d6bc22433b..60c41ba1da6d 100644 --- a/src/proto_alpha/lib_protocol/slash_percentage.ml +++ b/src/proto_alpha/lib_protocol/slash_percentage.ml @@ -38,8 +38,17 @@ let get ctxt ~(kind : Misbehaviour_repr.kind) ~(level : Level_repr.t) match kind with | Double_baking -> return (ctxt, for_double_baking ctxt) | Double_attesting | Double_preattesting -> - let* ctxt, rights = Delegate_sampler.attesting_rights_count ctxt level in - return (ctxt, for_double_attestation ctxt rights denounced) + let all_bakers_attest_enabled = + Delegate_sampler.check_all_bakers_attest_at_level ctxt level + in + if all_bakers_attest_enabled then + assert false (* Fixed in another commit *) + else + (* Note: this is the only existing call to attesting_rights_count *) + let* ctxt, rights = + Delegate_sampler.attesting_rights_count ctxt level + in + return (ctxt, for_double_attestation ctxt rights denounced) module Internal_for_tests = struct let for_double_attestation = for_double_attestation diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index ce9dffd30a49..12fb1e4d8e32 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -158,18 +158,51 @@ let get_attester ?manager_pkh ctxt = |> WithExceptions.Option.get ~loc:__LOC__ |> return +let get_attesting_slots_from_attester (attester : Plugin.RPC.Validators.t) = + if attester.use_legacy_for_attestation then attester.slots_legacy + else + Option.value_f ~default:(fun () -> assert false) attester.attestation_slot + :: [] + +let get_attesting_power_from_attester (attester : Plugin.RPC.Validators.t) = + if attester.use_legacy_for_attestation then + Int64.of_int (List.length attester.slots_legacy) + else attester.staking_weight + let get_first_different_attesters ctxt = let open Lwt_result_syntax in let+ attesters = get_attesters ctxt in match attesters with x :: y :: _ -> (x, y) | _ -> assert false +(* +let get_attester ctxt = + let open Lwt_result_syntax in + let+ attesters = get_attesters ctxt in + let attester = WithExceptions.Option.get ~loc:__LOC__ @@ List.hd attesters in + let slots = get_attesting_slots_from_attester attester in + (attester.consensus_key, slots) +*) + +(* +let get_attester_slot ctxt pkh = + let open Lwt_result_syntax in + let+ attesters = get_attesters ctxt in + List.find_map + (function + | {Plugin.RPC.Validators.consensus_key; _} as attester -> + if Signature.Public_key_hash.(consensus_key = pkh) then + Some (get_attesting_slots_from_attester attester) + else None) + attesters *) + let get_attester_n ctxt n = let open Lwt_result_syntax in let+ attesters = Plugin.RPC.Validators.get rpc_ctxt ctxt in let attester = WithExceptions.Option.get ~loc:__LOC__ @@ List.nth attesters n in - (attester.consensus_key, attester.slots) + let slots = get_attesting_slots_from_attester attester in + (attester.consensus_key, slots) let attester_has_bls_key {consensus_key; _} = Signature.Public_key_hash.is_bls consensus_key @@ -187,25 +220,25 @@ let get_attester_with_bls_key ctxt = |> return let get_attesting_power_for_delegate ctxt ?level pkh = - let open Lwt_result_syntax in + let open Lwt_result_wrap_syntax in let levels = Option.map (fun level -> [level]) level in let* attesters = Plugin.RPC.Validators.get rpc_ctxt ?levels ctxt in - let rec find_slots_for_delegate = function - | [] -> return 0 - | {Plugin.RPC.Validators.delegate; slots; _} :: t -> + let rec find_info_for_delegate = function + | [] -> 0L + | ({Plugin.RPC.Validators.delegate; _} as attester) :: t -> if Signature.Public_key_hash.equal delegate pkh then - return (List.length slots) - else find_slots_for_delegate t + get_attesting_power_from_attester attester + else find_info_for_delegate t in - find_slots_for_delegate attesters + return @@ find_info_for_delegate attesters let get_cumulated_attesting_power_for_delegate ctxt ~levels pkh = let open Lwt_result_syntax in List.fold_left_es (fun accu level -> let+ power = get_attesting_power_for_delegate ctxt ~level pkh in - accu + power) - 0 + Int64.add accu power) + 0L levels let get_current_voting_power = Delegate_services.current_voting_power rpc_ctxt diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 9140e1c1d5b3..f6e6dd4125e3 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -63,6 +63,28 @@ val get_attester : ?manager_pkh:public_key_hash -> t -> attester tzresult Lwt.t val get_first_different_attesters : t -> (Plugin.RPC.Validators.t * Plugin.RPC.Validators.t) tzresult Lwt.t +(*(** Return the first element [delegate,slot] of the list returns by + [get_attesters], where [delegate] is the [consensus key] when + is set. *) + val get_attester : t -> (public_key_hash * Slot.t list) tzresult Lwt.t +*) + +(** Return the list of slots allocated to a delegate for attestation, from + to the return result of a call to {!Validators.get}, with respect to the status + of the [all_bakers_attest] feature *) +val get_attesting_slots_from_attester : Plugin.RPC.Validators.t -> Slot.t list + +(** TODO *) +val get_attesting_power_from_attester : Plugin.RPC.Validators.t -> int64 + +(* +(** Given a [delegate], and a context [ctxt], if [delegate] is in + [get_attesters ctxt] returns the [slots] of [delegate] otherwise + return [None]. *) +val get_attester_slot : + t -> public_key_hash -> Slot.t list option tzresult Lwt.t +*) + (** Return the [n]th element of the list returns by [get_attesters]. *) val get_attester_n : t -> int -> (public_key_hash * Slot.t list) tzresult Lwt.t @@ -81,12 +103,12 @@ val get_attester_with_bls_key : t -> attester tzresult Lwt.t in the requested level. If ommited, [level] defaults to the next level. *) val get_attesting_power_for_delegate : - t -> ?level:Raw_level.t -> public_key_hash -> int tzresult Lwt.t + t -> ?level:Raw_level.t -> public_key_hash -> int64 tzresult Lwt.t (** Sums the result of [get_attesting_power_for_delegate] over a list of levels. *) val get_cumulated_attesting_power_for_delegate : - t -> levels:Raw_level.t list -> public_key_hash -> int tzresult Lwt.t + t -> levels:Raw_level.t list -> public_key_hash -> int64 tzresult Lwt.t val get_current_voting_power : t -> public_key_hash -> int64 Environment.Error_monad.shell_tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.ml b/src/proto_alpha/lib_protocol/test/helpers/op.ml index b1708cde8ade..4113b388214d 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/op.ml @@ -83,6 +83,7 @@ let mk_block_payload_hash (b : Block.t) = type attesting_slot = {slot : Slot.t; consensus_pkh : public_key_hash} let attesting_slot_of_attester {Plugin.RPC.Validators.consensus_key; slots; _} = + (* TODO change that ABAAB *) let slot = List.hd slots |> WithExceptions.Option.get ~loc:__LOC__ in {slot; consensus_pkh = consensus_key} diff --git a/src/proto_alpha/lib_protocol/test/helpers/scenario_attestation.ml b/src/proto_alpha/lib_protocol/test/helpers/scenario_attestation.ml index 31cb50100bc8..25423f4fa810 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/scenario_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/scenario_attestation.ml @@ -370,8 +370,8 @@ let attest_with_all_ : t -> t tzresult Lwt.t = attestation_power; } -> Tezt.Check.( - (attestation_power > 0) - int + (attestation_power > 0L) + int64 ~__LOC__ ~error_msg:"Attestation power should be greater than 0, got %L") ; let* is_forbidden = 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 0cebaf6a384f..836f6bf0ff69 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 @@ -99,21 +99,21 @@ let check_aggregate_result (type kind) (kind : kind aggregate) ~attesters | [] -> return_unit | _ -> Test.fail "Unexpected non-empty balance updates list" in - (* Check total voting power *) + (* Check total attesting power *) let* () = - let voting_power = + let attesting_power = List.fold_left (fun acc (delegate : RPC.Validators.t) -> - List.length delegate.slots + acc) - 0 - attesters + Int64.add (Context.get_attesting_power_from_attester delegate) acc) + 0L + committee in - if voting_power = total_consensus_power.slots then return_unit + if Compare.Int64.(attesting_power = consensus_power) then return_unit else Test.fail - "Wrong voting power : expected %d, found %d" - voting_power - total_consensus_power.slots + "Wrong voting power : expected %Ld, found %Ld" + attesting_power + consensus_power in (* Check committee *) let expected_committee = diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml index 7ae0cad56f70..23cb4df5d63d 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml @@ -102,19 +102,26 @@ let test_fitness_gap () = Assert.equal_int32 ~loc:__LOC__ level_diff 1l (** Return a delegate and its second smallest slot for the level of [block]. *) -let delegate_and_second_slot block = +let delegate_and_second_slot_legacy block = let open Lwt_result_syntax in let* attesters = Context.get_attesters (B block) in - let delegate, slots = + let delegate, slots, legacy_mode = (* Find an attester with more than 1 slot. *) WithExceptions.Option.get ~loc:__LOC__ (List.find_map - (fun {RPC.Validators.delegate; slots; _} -> - if Compare.List_length_with.(slots > 1) then Some (delegate, slots) + (fun { + RPC.Validators.delegate; + slots_legacy; + use_legacy_for_attestation; + _; + } -> + if Compare.List_length_with.(slots_legacy > 1) then + Some (delegate, slots_legacy, use_legacy_for_attestation) else None) attesters) in + assert (legacy_mode = true) ; (* Check that the slots are sorted and have no duplicates. *) let rec check_sorted = function | [] | [_] -> true @@ -164,22 +171,32 @@ let test_negative_slot () = let test_not_smallest_slot () = let open Lwt_result_syntax in let* _genesis, b = init_genesis () in - let* attesting_slot = Op.get_non_canonical_attesting_slot ~attested_block:b in - Consensus_helpers.test_consensus_operation_all_modes_different_outcomes - ~loc:__LOC__ - ~attested_block:b - ~attesting_slot - ~application_error:error_wrong_slot - ~construction_error:error_wrong_slot - ~mempool_error:error_wrong_slot - Attestation + let* alpha_ctxt = Block.get_alpha_ctxt b in + let alpha_level = + Level.from_raw alpha_ctxt (Raw_level.of_int32_exn (Block.current_level b)) + in + if Stake_distribution.check_all_bakers_attest_at_level alpha_ctxt alpha_level + then return_unit + else + let* attesting_slot = + Op.get_non_canonical_attesting_slot ~attested_block:b + in + Consensus_helpers.test_consensus_operation_all_modes_different_outcomes + ~loc:__LOC__ + ~attested_block:b + ~attesting_slot + ~application_error:error_wrong_slot + ~construction_error:error_wrong_slot + ~mempool_error:error_wrong_slot + Attestation let attesting_slot_with_someone_elses_slot block = let open Lwt_result_syntax in let* attesters = Context.get_attesters (B block) in match attesters with | [] | [_] -> Test.fail ~__LOC__ "Expected at least two delegates with rights" - | {consensus_key = consensus_pkh; _} :: {slots; _} :: _ -> + | {consensus_key = consensus_pkh; _} :: someone_else :: _ -> + let slots = Context.get_attesting_slots_from_attester someone_else in let slot = WithExceptions.Option.get ~loc:__LOC__ (List.hd slots) in return {Op.slot; consensus_pkh} @@ -592,6 +609,8 @@ let test_attestation_threshold ~sufficient_threshold () = check that a block with attesting power [consensus_threshold_size - 1] won't be baked. *) let* genesis, _contracts = Context.init_n 10 () in + (* TODO adapt test for all_bakers_attest *) + assert (genesis.constants.all_bakers_attest_activation_level = None) ; let* b = Block.bake genesis in let* {parametric = {consensus_threshold_size; _}; _} = Context.get_constants (B b) @@ -601,6 +620,7 @@ let test_attestation_threshold ~sufficient_threshold () = let* _, attestations = List.fold_left_es (fun (counter, attestations) (attester : Context.attester) -> + let slots = Context.get_attesting_slots_from_attester attester in let new_counter = counter + List.length attester.slots in if (sufficient_threshold && counter < consensus_threshold_size) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml index 507a6062bc7b..de435c1e0311 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml @@ -256,6 +256,8 @@ let test_different_branch () = let test_different_slots () = let open Lwt_result_syntax in let* genesis, _contracts = Context.init2 ~consensus_threshold_size:0 () in + (* This test is irrelevant when all bakers attest, because all bakers would only have one valid slot *) + assert (genesis.constants.all_bakers_attest_activation_level = None) ; let* blk = Block.bake genesis in let* attesters = Context.get_attesters (B blk) in let consensus_pkh, slot1, slot2 = @@ -264,7 +266,7 @@ let test_different_slots () = ~loc:__LOC__ (List.find_map (fun (attester : RPC.Validators.t) -> - match attester.slots with + match Context.get_attesting_slots_from_attester attester.slots with | slot1 :: slot2 :: _ -> Some (attester.consensus_key, slot1, slot2) | _ -> None) attesters) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml index 4bcd4910b345..3df92e45e974 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml @@ -359,7 +359,9 @@ let test_payload_producer_gets_evidence_rewards () = let* preattesters = List.map_es (function - | {Plugin.RPC.Validators.delegate; slots; _} -> return (delegate, slots)) + | {Plugin.RPC.Validators.delegate; _} as attester -> + let slots = Context.get_attesting_slots_from_attester attester in + return (delegate, slots)) attesters in let* preattestations = diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_participation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_participation.ml index 5d60622bfa3c..b58663233dab 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_participation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_participation.ml @@ -75,7 +75,7 @@ let test_participation ~sufficient_participation () = let committee_size = csts.parametric.consensus_committee_size in let expected_nb_slots = blocks_per_cycle * committee_size / n_accounts in let minimal_nb_active_slots = - mpr.numerator * expected_nb_slots / mpr.denominator + Int64.of_int (mpr.numerator * expected_nb_slots / mpr.denominator) in let account1, account2 = match accounts with a1 :: a2 :: _ -> (a1, a2) | _ -> assert false @@ -87,6 +87,7 @@ let test_participation ~sufficient_participation () = consider baking rewards for [del2]. Delegate [del2] attests only if the target [minimal_nb_active_slots] is not reached; for the rest, it is [del1] that attests. *) + (* If [all_bakers_attest], [del2] attests iff [sufficient_participation] is true *) let* pred_b, b, _, last_del2_autostaked = List.fold_left_es (fun (b_pred, b_crt, attesting_power, _last_del2_autostaked) level -> @@ -95,11 +96,18 @@ let test_participation ~sufficient_participation () = let* attesting_power_for_level = Context.get_attesting_power_for_delegate (B b_crt) ~level del1 in + let* alpha_ctxt = Block.get_alpha_ctxt b_crt in + let all_bakers_attest = + Stake_distribution.check_all_bakers_attest_at_level + alpha_ctxt + (Level.from_raw alpha_ctxt level) + in let attester, new_attesting_power = if sufficient_participation - && attesting_power < minimal_nb_active_slots - then (del2, attesting_power + attesting_power_for_level) + && (Int64.compare attesting_power minimal_nb_active_slots < 0 + || all_bakers_attest) + then (del2, Int64.add attesting_power attesting_power_for_level) else (del1, attesting_power) in let* b, (metadata, _) = @@ -107,7 +115,7 @@ let test_participation ~sufficient_participation () = in let autostaked = Block.autostaked_opt del2 metadata in return (b_crt, b, new_attesting_power, autostaked)) - (b0, b1, 0, None) + (b0, b1, 0L, None) (2 -- (blocks_per_cycle - 1)) in let* bal2_at_pred_b = @@ -189,6 +197,8 @@ let test_participation_rpc () = } in let* b0, (account1, account2) = Context.init_with_constants2 constants in + (* TODO adapt this test with flag set to true *) + assert (b0.constants.all_bakers_attest_activation_level = None) ; let del1 = Context.Contract.pkh account1 in let del2 = Context.Contract.pkh account2 in let* csts = Context.get_constants (B b0) in @@ -238,7 +248,7 @@ let test_participation_rpc () = let* () = Assert.equal_int ~loc:__LOC__ info.missed_levels (level_int - 1) in - let missed_slots = total_attesting_power in + let missed_slots = Int64.to_int total_attesting_power in let* () = Assert.equal_int ~loc:__LOC__ info.missed_slots missed_slots in @@ -267,8 +277,8 @@ let test_participation_rpc () = let* attesting_power = Context.get_attesting_power_for_delegate (B b_crt) ~level del2 in - return (b_crt, b, total_attesting_power + attesting_power)) - (b0, b1, 0) + return (b_crt, b, Int64.add total_attesting_power attesting_power)) + (b0, b1, 0L) (1 -- (blocks_per_cycle - 2)) in return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml index d112f5179af5..4698d7569198 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml @@ -150,7 +150,7 @@ let check_has_no_slots ~loc baker_name = Test.fail ~__LOC__ "%s: delegate '%s'(%a) expected to have no attestation power, \ - has %i slots." + has %Li slots." loc baker_name Signature.Public_key_hash.pp diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/validate_helpers.ml b/src/proto_alpha/lib_protocol/test/integration/validate/validate_helpers.ml index c1f94115b43c..c78171c8ca77 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/validate_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/validate_helpers.ml @@ -373,7 +373,8 @@ let delegates_of_block block = let open Lwt_result_syntax in let+ validators = Context.get_attesters (B block) in List.map - (fun Plugin.RPC.Validators.{consensus_key; slots; _} -> + (fun (Plugin.RPC.Validators.{consensus_key; _} as attester) -> + let slots = Context.get_attesting_slots_from_attester attester in (consensus_key, slots)) validators diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index deae3b3cd691..126d11e2e86c 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -33,6 +33,8 @@ type consensus_info = { preattestation_slot_map : Consensus_key.power Slot.Map.t option; attestation_slot_map : Consensus_key.power Slot.Map.t option; consensus_slot_map : Consensus_key.power Slot.Map.t Level.Map.t option; + predecessor_consensus_threshold : int64; + consensus_threshold : int64; } let init_consensus_info ctxt (predecessor_level, predecessor_round) = @@ -42,6 +44,9 @@ let init_consensus_info ctxt (predecessor_level, predecessor_round) = preattestation_slot_map = Consensus.allowed_preattestations ctxt; attestation_slot_map = Consensus.allowed_attestations ctxt; consensus_slot_map = Consensus.allowed_consensus ctxt; + predecessor_consensus_threshold = + Consensus.predecessor_consensus_threshold ctxt; + consensus_threshold = Consensus.consensus_threshold ctxt; } (** Helper used to run all the pending checks after an operation validation. *) @@ -2167,7 +2172,7 @@ module Anonymous = struct check_denunciation_age vi (`Consensus_denounciation kind) level.level in let* ctxt, consensus_key = - Stake_distribution.slot_owner vi.ctxt level slot + Stake_distribution.attestation_owner vi.ctxt level slot in let delegate = consensus_key.delegate in let* already_slashed = @@ -2442,7 +2447,7 @@ module Anonymous = struct let*? () = check_denunciation_age vi `Dal_denounciation level in let level = Level.from_raw vi.ctxt level in let* ctxt, consensus_key = - Stake_distribution.slot_owner vi.ctxt level consensus_slot + Stake_distribution.attestation_owner vi.ctxt level consensus_slot in let delegate = consensus_key.delegate in let*! already_denounced = @@ -2473,7 +2478,7 @@ module Anonymous = struct in let* _ctxt, shard_owner = let*? tb_slot = Slot.of_int shard_index in - Stake_distribution.slot_owner vi.ctxt level tb_slot + Stake_distribution.attestation_owner vi.ctxt level tb_slot in let*? () = error_unless @@ -4027,12 +4032,24 @@ let check_attestation_power vi bs = in return Compare.Int32.(level_position_in_protocol > 1l) in + let all_bakers_attest = + Stake_distribution.check_all_bakers_attest_at_level vi.ctxt vi.current_level + in if are_attestations_required then - let required = Constants.consensus_threshold_size vi.ctxt in - (* TODO ABAÆB *) - let provided = bs.attestation_power.slots in + let*? consensus_info = + Option.value_e + ~error: + (trace_of_error + Validate_errors.Consensus.Consensus_operation_not_allowed) + vi.consensus_info + in + let required = consensus_info.predecessor_consensus_threshold in + let provided = + if all_bakers_attest then bs.attestation_power.stake + else bs.attestation_power.slots + in fail_unless - Compare.Int.(provided >= required) + Compare.Int64.(provided >= required) (Not_enough_attestations {required; provided}) else return_unit @@ -4076,21 +4093,27 @@ let check_preattestation_round_and_power vi vs round = (Locked_round_after_block_round {locked_round = preattestation_round; round}) in - let consensus_threshold = Constants.consensus_threshold_size vi.ctxt in + let* consensus_info = + Option.value_e + ~error: + (trace_of_error + Validate_errors.Consensus.Consensus_operation_not_allowed) + vi.consensus_info + in + let consensus_threshold = consensus_info.consensus_threshold in let all_bakers_attest_enabled = Stake_distribution.check_all_bakers_attest_at_level vi.ctxt vi.current_level in - if all_bakers_attest_enabled then - error Validate_errors.Consensus.All_bakers_attest_not_implemented - (* TODO ABAAB *) - else - let total_attesting_power = total_attesting_power.slots in - error_when - Compare.Int.(total_attesting_power < consensus_threshold) - (Insufficient_locked_round_evidence - {total_attesting_power; consensus_threshold}) + let total_attesting_power = + if all_bakers_attest_enabled then total_attesting_power.stake + else Int64.of_int total_attesting_power.slots + in + error_when + Compare.Int64.(total_attesting_power < consensus_threshold) + (Insufficient_locked_round_evidence + {total_attesting_power; consensus_threshold}) let check_payload_hash block_state ~predecessor_hash (block_header_contents : Block_header.contents) = diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index a89f60292688..36857f8a5248 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -1968,7 +1968,7 @@ let () = module Block = struct (* All block errors are permanent. *) type error += - | Not_enough_attestations of {required : int; provided : int} + | Not_enough_attestations of {required : int64; provided : int64} | Inconsistent_validation_passes_in_block of { expected : int; provided : int; @@ -1982,8 +1982,8 @@ module Block = struct round : Round.t; } | Insufficient_locked_round_evidence of { - total_attesting_power : int; - consensus_threshold : int; + total_attesting_power : int64; + consensus_threshold : int64; } let () = @@ -1997,10 +1997,10 @@ module Block = struct ~pp:(fun ppf (required, provided) -> Format.fprintf ppf - "Wrong number of attestations (%i), at least %i are expected" + "Wrong number of attestations (%Li), at least %Li are expected" provided required) - Data_encoding.(obj2 (req "required" int31) (req "provided" int31)) + Data_encoding.(obj2 (req "required" int64) (req "provided" int64)) (function | Not_enough_attestations {required; provided} -> Some (required, provided) @@ -2080,14 +2080,14 @@ module Block = struct ~pp:(fun ppf (total_attesting_power, consensus_threshold) -> Format.fprintf ppf - "The provided locked round evidence is not sufficient: provided %d \ - total attesting power but was expecting at least %d." + "The provided locked round evidence is not sufficient: provided %Ld \ + total attesting power but was expecting at least %Ld." total_attesting_power consensus_threshold) Data_encoding.( obj2 - (req "total_attesting_power" int31) - (req "consensus_threshold" int31)) + (req "total_attesting_power" int64) + (req "consensus_threshold" int64)) (function | Insufficient_locked_round_evidence {total_attesting_power; consensus_threshold} -> diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 1fd06f503ed2..c89c5329b1d2 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -290,7 +290,7 @@ type error += Failing_noop_error module Block : sig type error += - | Not_enough_attestations of {required : int; provided : int} + | Not_enough_attestations of {required : int64; provided : int64} | Inconsistent_validation_passes_in_block of { expected : int; provided : int; @@ -304,7 +304,7 @@ module Block : sig round : Round.t; } | Insufficient_locked_round_evidence of { - total_attesting_power : int; - consensus_threshold : int; + total_attesting_power : int64; + consensus_threshold : int64; } end diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index 9b48ad2b0f7c..f388204011a2 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -507,29 +507,47 @@ module Consensus = struct in inject ?request ?force ?error ~protocol op client - let get_slots ~level client = + let get_slots ~level ~protocol client = let* rpc_json = Client.RPC.call client @@ RPC.get_chain_block_helper_validators ~level () in let open JSON in + let slot_field = + match rpc_json |=> 0 |-> "use_legacy_for_attestation" |> as_bool_opt with + | None -> + assert (Protocol.(protocol < Alpha)) ; + "slots" + | Some b -> + assert (Protocol.(protocol >= Alpha)) ; + if b then "slots_legacy" else "attestation_slot" + in return @@ List.map (fun json -> let delegate = json |-> "delegate" |> as_string in - let slots = json |-> "slots" |> as_list |> List.map as_int in + let slots = json |-> slot_field |> as_list |> List.map as_int in (delegate, slots)) (as_list rpc_json) - let get_slots_by_consensus_key ~level client = + let get_slots_by_consensus_key ~level ~protocol client = let* rpc_json = Client.RPC.call client @@ RPC.get_chain_block_helper_validators ~level () in let open JSON in + let slot_field = + match rpc_json |=> 0 |-> "use_legacy_for_attestation" |> as_bool_opt with + | None -> + assert (Protocol.(protocol < Alpha)) ; + "slots" + | Some b -> + assert (Protocol.(protocol >= Alpha)) ; + if b then "slots_legacy" else "attestation_slot" + in return @@ List.map (fun json -> let consensus_key = json |-> "consensus_key" |> as_string in - let slots = json |-> "slots" |> as_list |> List.map as_int in + let slots = json |-> slot_field |> as_list |> List.map as_int in (consensus_key, slots)) (as_list rpc_json) @@ -538,6 +556,18 @@ module Consensus = struct | Some slots -> List.hd slots | None -> Test.fail "No slots found for %s" delegate.public_key_hash + let attestation_slot ~slots (delegate : Account.key) = + (* We assume only one level is in the given slots. + We also assume it is not empty (i.e, some delegate is in the list of attesters). *) + match + List.find_opt + (fun (delegate_rpc, _slots) -> + String.equal delegate_rpc delegate.public_key_hash) + slots + with + | Some (_, x :: _) -> x + | _ -> Test.fail "No slots found for %s" delegate.public_key_hash + let get_block_payload_hash ?block client = let* block_header = Client.RPC.call_via_endpoint client diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 8026409765fe..be644cf86b28 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -313,17 +313,24 @@ module Consensus : sig [GET /chains//blocks//helpers/validators] RPC. Returns an association list that maps a public key hash to the owned slot list *) - val get_slots : level:int -> Client.t -> (string * int list) list Lwt.t + val get_slots : + level:int -> + protocol:Protocol.t -> + Client.t -> + (string * int list) list Lwt.t (** Same as [get_slots] but maps a consensus key to the owned slot list. *) val get_slots_by_consensus_key : - level:int -> Client.t -> (string * int list) list Lwt.t + level:int -> + protocol:Protocol.t -> + Client.t -> + (string * int list) list Lwt.t (** Returns the first slot of the provided delegate in the [slots] association list that describes all attestation rights at some level. Causes the test to fail if the delegate is not found. *) - val first_slot : slots:(string * int list) list -> Account.key -> int + val attestation_slot : slots:(string * int list) list -> Account.key -> int (** Calls the [GET /chains//blocks//header] RPC and extracts the head block's payload hash from the result. *) diff --git a/tezt/tests/double_consensus.ml b/tezt/tests/double_consensus.ml index e04a88aa7794..627bf5b76bb1 100644 --- a/tezt/tests/double_consensus.ml +++ b/tezt/tests/double_consensus.ml @@ -101,11 +101,6 @@ let double_consensus_init ~delegate:Constant.bootstrap1.public_key_hash () in - let slots = - List.map - JSON.as_int - JSON.(List.hd JSON.(slots |> as_list) |-> "slots" |> as_list) - in Log.info "Inject valid %s." consensus_name ; let waiter = Node.wait_for_request ~request:`Inject node in let* () = @@ -127,6 +122,25 @@ let double_consensus_init in return ((client, accuser), (branch, level, round, slots, block_payload_hash)) +let check_version_and_get_slots slots protocol = + let open JSON in + match slots |=> 0 |-> "use_legacy_for_attestation" |> as_bool_opt with + | None -> + assert (Protocol.(protocol < Alpha)) ; + Some + (List.map + JSON.as_int + JSON.(List.hd JSON.(slots |> as_list) |-> "slots" |> as_list)) + | Some b -> + assert (Protocol.(protocol >= Alpha)) ; + if b then + Some + (List.map + JSON.as_int + JSON.( + List.hd JSON.(slots |> as_list) |-> "slots_legacy" |> as_list)) + else None + let attest_utils = ( Client.attest_for, (fun ~slot ~level ~round ~block_payload_hash -> @@ -145,52 +159,58 @@ let double_consensus_wrong_block_payload_hash let* (client, accuser), (branch, level, round, slots, _block_payload_hash) = double_consensus_init consensus_for consensus_name protocol () in - let* header = - Client.RPC.call client @@ RPC.get_chain_block_header ~block:"head~2" () - in - let block_payload_hash = JSON.(header |-> "payload_hash" |> as_string) in - Log.info "Inject an invalid %s and wait for denounciation" consensus_name ; - let op = - mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash - in - let waiter = consensus_waiter accuser in - let* _ = - Operation.Consensus.inject - ~force:true - ~protocol - ~branch - ~signer:Constant.bootstrap1 - op - client - in - let* () = waiter in - Log.info - "Inject another invalid %s and wait for already_denounced event" - consensus_name ; - let* header = - Client.RPC.call client @@ RPC.get_chain_block_header ~block:"head~3" () - in - let block_payload_hash = JSON.(header |-> "payload_hash" |> as_string) in - let op = - mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash - in - let* oph = - get_double_consensus_denounciation_hash protocol consensus_name client - in - let waiter_already_denounced = - double_consensus_already_denounced_waiter accuser oph - in - let* _ = - Operation.Consensus.inject - ~force:true - ~protocol - ~branch - ~signer:Constant.bootstrap1 - op - client - in - let* () = waiter_already_denounced in - unit + let slots = check_version_and_get_slots slots protocol in + match slots with + | None -> + Log.info "Legacy slots are not used in attestations. Aborting." ; + unit + | Some slots -> + let* header = + Client.RPC.call client @@ RPC.get_chain_block_header ~block:"head~2" () + in + let block_payload_hash = JSON.(header |-> "payload_hash" |> as_string) in + Log.info "Inject an invalid %s and wait for denounciation" consensus_name ; + let op = + mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash + in + let waiter = consensus_waiter accuser in + let* _ = + Operation.Consensus.inject + ~force:true + ~protocol + ~branch + ~signer:Constant.bootstrap1 + op + client + in + let* () = waiter in + Log.info + "Inject another invalid %s and wait for already_denounced event" + consensus_name ; + let* header = + Client.RPC.call client @@ RPC.get_chain_block_header ~block:"head~3" () + in + let block_payload_hash = JSON.(header |-> "payload_hash" |> as_string) in + let op = + mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash + in + let* oph = + get_double_consensus_denounciation_hash protocol consensus_name client + in + let waiter_already_denounced = + double_consensus_already_denounced_waiter accuser oph + in + let* _ = + Operation.Consensus.inject + ~force:true + ~protocol + ~branch + ~signer:Constant.bootstrap1 + op + client + in + let* () = waiter_already_denounced in + unit let double_attestation_wrong_block_payload_hash = Protocol.register_test @@ -219,46 +239,52 @@ let double_consensus_wrong_branch let* (client, accuser), (_branch, level, round, slots, block_payload_hash) = double_consensus_init consensus_for consensus_name protocol () in - let* branch = Operation.Manager.get_branch ~offset:4 client in - Log.info "Inject an invalid %s and wait for denounciation" consensus_name ; - let op = - mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash - in - let waiter = consensus_waiter accuser in - let* _ = - Operation.Consensus.inject - ~force:true - ~protocol - ~branch - ~signer:Constant.bootstrap1 - op - client - in - let* () = waiter in - Log.info - "Inject another invalid %s and wait for already_denounced event" - consensus_name ; - let* branch = Operation.Manager.get_branch ~offset:5 client in - let op = - mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash - in - let* oph = - get_double_consensus_denounciation_hash protocol consensus_name client - in - let waiter_already_denounced = - double_consensus_already_denounced_waiter accuser oph - in - let* _ = - Operation.Consensus.inject - ~force:true - ~protocol - ~branch - ~signer:Constant.bootstrap1 - op - client - in - let* () = waiter_already_denounced in - unit + let slots = check_version_and_get_slots slots protocol in + match slots with + | None -> + Log.info "Legacy slots are not used in attestations. Aborting." ; + unit + | Some slots -> + let* branch = Operation.Manager.get_branch ~offset:4 client in + Log.info "Inject an invalid %s and wait for denounciation" consensus_name ; + let op = + mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash + in + let waiter = consensus_waiter accuser in + let* _ = + Operation.Consensus.inject + ~force:true + ~protocol + ~branch + ~signer:Constant.bootstrap1 + op + client + in + let* () = waiter in + Log.info + "Inject another invalid %s and wait for already_denounced event" + consensus_name ; + let* branch = Operation.Manager.get_branch ~offset:5 client in + let op = + mk_consensus ~slot:(List.nth slots 0) ~level ~round ~block_payload_hash + in + let* oph = + get_double_consensus_denounciation_hash protocol consensus_name client + in + let waiter_already_denounced = + double_consensus_already_denounced_waiter accuser oph + in + let* _ = + Operation.Consensus.inject + ~force:true + ~protocol + ~branch + ~signer:Constant.bootstrap1 + op + client + in + let* () = waiter_already_denounced in + unit let double_attestation_wrong_branch = Protocol.register_test @@ -385,34 +411,35 @@ let operation_too_far_in_future = ~level () in - let slots = - List.map - JSON.as_int - JSON.(List.hd JSON.(slots |> as_list) |-> "slots" |> as_list) - in - Log.info - "Craft and inject an attestation 3 levels in the future and wait for \ - [consensus_operation_too_far_in_future.v0] event from the accuser." ; - let op = - Operation.Consensus.attestation - ~slot:(List.hd slots) - ~level - ~round:0 - ~block_payload_hash - () - in - let waiter = consensus_operation_too_far_in_future_waiter accuser in - let* _ = - Operation.Consensus.inject - ~force:true - ~protocol - ~branch - ~signer:Constant.bootstrap1 - op - client - in - let* () = waiter in - unit + let slots = check_version_and_get_slots slots protocol in + match slots with + | None -> + Log.info "Legacy slots are not used in attestations. Aborting." ; + unit + | Some slots -> + Log.info + "Craft and inject an attestation 3 levels in the future and wait for \ + [consensus_operation_too_far_in_future.v0] event from the accuser." ; + let op = + Operation.Consensus.attestation + ~slot:(List.hd slots) + ~level + ~round:0 + ~block_payload_hash + () + in + let waiter = consensus_operation_too_far_in_future_waiter accuser in + let* _ = + Operation.Consensus.inject + ~force:true + ~protocol + ~branch + ~signer:Constant.bootstrap1 + op + client + in + let* () = waiter in + unit let fetch_anonymous_operations ?block client = let* json = diff --git a/tezt/tests/prevalidator.ml b/tezt/tests/prevalidator.ml index 17c907e7cce8..c2e3fa1db13d 100644 --- a/tezt/tests/prevalidator.ml +++ b/tezt/tests/prevalidator.ml @@ -1908,11 +1908,11 @@ module Revamped = struct let* block_payload_hash = Operation.Consensus.get_block_payload_hash client in - let* slots = Operation.Consensus.get_slots ~level client in + let* slots = Operation.Consensus.get_slots ~level ~protocol client in let inject_attestation (delegate : Account.key) = Operation.Consensus.inject (Operation.Consensus.attestation - ~slot:(Operation.Consensus.first_slot ~slots delegate) + ~slot:(Operation.Consensus.attestation_slot ~slots delegate) ~level ~round:0 ~block_payload_hash @@ -2164,7 +2164,8 @@ module Revamped = struct let* op = Operation.Consensus.operation (Operation.Consensus.attestation - ~slot:(Operation.Consensus.first_slot ~slots delegate) + ~slot: + (Operation.Consensus.attestation_slot ~slots ~protocol delegate) ~level ~round:0 ~block_payload_hash @@ -2425,7 +2426,7 @@ module Revamped = struct let inject_attestation (account : Account.key) = Operation.Consensus.inject (Operation.Consensus.attestation - ~slot:(Operation.Consensus.first_slot ~slots account) + ~slot:(Operation.Consensus.attestation_slot ~slots ~protocol account) ~level ~round:0 ~block_payload_hash @@ -2538,7 +2539,7 @@ module Revamped = struct let inject_attestation (account : Account.key) = Operation.Consensus.inject (Operation.Consensus.attestation - ~slot:(Operation.Consensus.first_slot ~slots account) + ~slot:(Operation.Consensus.attestation_slot ~slots ~protocol account) ~level ~round:0 ~block_payload_hash @@ -2670,7 +2671,7 @@ module Revamped = struct let inject_attestation ~(account : Account.key) ~(signer : Account.key) = Operation.Consensus.inject (Operation.Consensus.attestation - ~slot:(Operation.Consensus.first_slot ~slots account) + ~slot:(Operation.Consensus.attestation_slot ~slots ~protocol account) ~level ~round:0 ~block_payload_hash -- GitLab