diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 722dda9b60ec493262d8eedf0fc907ede2f7df4d..01737f08314c69d6868da1fc15735ca1c1cd1bf8 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -33,9 +33,20 @@ Breaking Changes RPC Changes ----------- -- The new ``current_voting_power`` RPC computes the voting power of a - delegate based on its current stake (as opposed to reading it from - the vote listings as the ``voting_power`` does) (MR :gl:`!9329`) +- Three new variants of the ``voting_power`` RPC (which returns the + voting power of a delegate based on the stake it had when voting + snapshot was taken) have been added: + + - ``current_voting_power`` the voting power of a delegate based on + its current stake (MR :gl:`!9329`) + + - ``baking_power`` computes the baking power of a delegate based on + the stake snapshot selected for the current cycle (MR + :gl:`!9350`) + + - ``current_baking_power`` computes the baking power of a delegate + based on its current stake (MR :gl:`!9350`) + Operation receipts ------------------ diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 5779d0061c0e0fa451a9172bd9f1f3e5cba3a058..035e94c78e8d8726023b90fb279807bd27a171ba 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -527,6 +527,8 @@ module Stake_distribution = struct let open Lwt_result_syntax in let* total_stake = Stake_storage.get_total_active_stake ctxt cycle in return (Stake_repr.get_frozen total_stake) + + module For_RPC = Delegate_sampler.For_RPC end module Nonce = Nonce_storage diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 311987624be9c2b234d93904ac4d3841c55bbd3c..f5e2274213fb3c38914a1a7f0d6cc70a85b8ebae 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -4586,6 +4586,14 @@ module Stake_distribution : sig val load_sampler_for_cycle : context -> Cycle.t -> context tzresult Lwt.t val get_total_frozen_stake : context -> Cycle.t -> Tez.t tzresult Lwt.t + + module For_RPC : sig + val delegate_baking_power_for_cycle : + context -> Cycle.t -> Signature.public_key_hash -> int64 tzresult Lwt.t + + val delegate_current_baking_power : + context -> Signature.public_key_hash -> int64 tzresult Lwt.t + end end (** This module re-exports definitions from {!Commitment_repr} and, diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index 3a4227ecbd9955d42eaf28fa3679c43406fe2417..69f1693c0c2e634da71cf043c8e5dabca2a02253 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -151,6 +151,65 @@ let load_sampler_for_cycle ctxt cycle = in return ctxt +let get_delegate_stake_from_staking_balance ctxt + ~staking_over_baking_global_limit_millionth ~delegation_over_baking_limit + delegate staking_balance = + let open Lwt_result_syntax in + let delegate_contract = Contract_repr.Implicit delegate in + let* delegate_own_pseudotokens = + Staking_pseudotokens_storage.costaking_pseudotokens_balance + ctxt + delegate_contract + in + let* delegate_own_frozen_deposits = + Staking_pseudotokens_storage.tez_of_frozen_deposits_pseudotokens + ctxt + delegate + delegate_own_pseudotokens + in + let* {staking_over_baking_limit_millionth; _} = + Delegate_staking_parameters.of_delegate ctxt delegate + in + let staking_over_baking_limit_millionth = + let delegate_staking_over_baking_limit_millionth = + Int64.of_int32 staking_over_baking_limit_millionth + in + Compare.Int64.min + staking_over_baking_global_limit_millionth + delegate_staking_over_baking_limit_millionth + in + let staking_over_baking_limit_plus_1_millionth = + Int64.add 1_000_000L staking_over_baking_limit_millionth + in + let open Tez_repr in + let* {current_amount = all_frozen_deposits; initial_amount = _} = + Frozen_deposits_storage.get ctxt delegate_contract + in + let frozen = + match + mul_ratio + delegate_own_frozen_deposits + ~num:staking_over_baking_limit_plus_1_millionth + ~den:1_000_000L + with + | Ok max_allowed_frozen_deposits -> + min all_frozen_deposits max_allowed_frozen_deposits + (* Over-co-staked frozen deposits counts towards delegated stake. *) + | Error _max_allowed_frozen_deposits_overflows -> all_frozen_deposits + in + (* This subtraction may result in a negative value if tez were frozen + after the snapshot. This is fine, they are then taken into account as + frozen stake rather than delegated. *) + let available_delegated = + sub_opt staking_balance frozen |> Option.value ~default:zero + in + let delegated = + match delegate_own_frozen_deposits *? delegation_over_baking_limit with + | Ok max_allowed_delegated -> min max_allowed_delegated available_delegated + | Error _max_allowed_delegated_overflows -> available_delegated + in + return (Stake_repr.make ~frozen ~delegated) + let get_stakes_for_selected_index ctxt index = let open Lwt_result_syntax in let delegation_over_baking_limit = @@ -169,61 +228,14 @@ let get_stakes_for_selected_index ctxt index = ctxt ~index ~f:(fun (delegate, staking_balance) (acc, total_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - let* delegate_own_pseudotokens = - Staking_pseudotokens_storage.costaking_pseudotokens_balance - ctxt - delegate_contract - in - let* delegate_own_frozen_deposits = - Staking_pseudotokens_storage.tez_of_frozen_deposits_pseudotokens + let* stake_for_cycle = + get_delegate_stake_from_staking_balance + ~staking_over_baking_global_limit_millionth + ~delegation_over_baking_limit ctxt delegate - delegate_own_pseudotokens - in - let* {staking_over_baking_limit_millionth; _} = - Delegate_staking_parameters.of_delegate ctxt delegate - in - let staking_over_baking_limit_millionth = - let delegate_staking_over_baking_limit_millionth = - Int64.of_int32 staking_over_baking_limit_millionth - in - Compare.Int64.min - staking_over_baking_global_limit_millionth - delegate_staking_over_baking_limit_millionth - in - let staking_over_baking_limit_plus_1_millionth = - Int64.add 1_000_000L staking_over_baking_limit_millionth - in - let open Tez_repr in - let* {current_amount = all_frozen_deposits; initial_amount = _} = - Frozen_deposits_storage.get ctxt delegate_contract - in - let frozen = - match - mul_ratio - delegate_own_frozen_deposits - ~num:staking_over_baking_limit_plus_1_millionth - ~den:1_000_000L - with - | Ok max_allowed_frozen_deposits -> - min all_frozen_deposits max_allowed_frozen_deposits - (* Over-co-staked frozen deposits counts towards delegated stake. *) - | Error _max_allowed_frozen_deposits_overflows -> all_frozen_deposits - in - (* This subtraction may result in a negative value if tez were frozen - after the snapshot. This is fine, they are then taken into account as - frozen stake rather than delegated. *) - let available_delegated = - sub_opt staking_balance frozen |> Option.value ~default:zero - in - let delegated = - match delegate_own_frozen_deposits *? delegation_over_baking_limit with - | Ok max_allowed_delegated -> - min max_allowed_delegated available_delegated - | Error _max_allowed_delegated_overflows -> available_delegated + staking_balance in - let stake_for_cycle = Stake_repr.make ~frozen ~delegated in let*? total_stake = Stake_repr.(total_stake +? stake_for_cycle) in return ((delegate, stake_for_cycle) :: acc, total_stake)) ~init:([], Stake_repr.zero) @@ -263,6 +275,30 @@ let select_distribution_for_cycle ctxt cycle = (* pre-allocate the sampler *) Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) +let delegate_baking_power_from_staking_balance ctxt delegate staking_balance = + let open Lwt_result_syntax in + let delegation_over_baking_limit = + Int64.of_int (Constants_storage.delegation_over_baking_limit ctxt) + in + let staking_over_baking_global_limit_millionth = + Int64.( + mul + 1_000_000L + (of_int + (Constants_storage + .adaptive_inflation_staking_over_baking_global_limit + ctxt))) + in + let+ stake = + get_delegate_stake_from_staking_balance + ctxt + ~staking_over_baking_global_limit_millionth + ~delegation_over_baking_limit + delegate + staking_balance + in + Stake_context.staking_weight ctxt stake + let select_new_distribution_at_cycle_end ctxt ~new_cycle = let preserved = Constants_storage.preserved_cycles ctxt in let for_cycle = Cycle_repr.add new_cycle preserved in @@ -275,3 +311,22 @@ let clear_outdated_sampling_data ctxt ~new_cycle = | Some outdated_cycle -> Delegate_sampler_state.remove_existing ctxt outdated_cycle >>=? fun ctxt -> Seed_storage.remove_for_cycle ctxt outdated_cycle + +module For_RPC = struct + let delegate_baking_power_for_cycle ctxt cycle delegate = + let open Lwt_result_syntax in + let* max_snapshot_index = Stake_storage.max_snapshot_index ctxt in + let* seed = Seed_storage.raw_for_cycle ctxt cycle in + let* selected_index = + compute_snapshot_index_for_seed ~max_snapshot_index seed + in + let* staking_balance = + Storage.Stake.Staking_balance.Snapshot.get ctxt (selected_index, delegate) + in + delegate_baking_power_from_staking_balance ctxt delegate staking_balance + + let delegate_current_baking_power ctxt delegate = + let open Lwt_result_syntax in + let* staking_balance = Stake_storage.get_staking_balance ctxt delegate in + delegate_baking_power_from_staking_balance ctxt delegate staking_balance +end diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.mli b/src/proto_alpha/lib_protocol/delegate_sampler.mli index 864cea84b717b210ee152974a589fe2dbe4e0575..506cc04ee2d3107a0012b7bc58e481c158f3702c 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.mli +++ b/src/proto_alpha/lib_protocol/delegate_sampler.mli @@ -77,3 +77,18 @@ val clear_outdated_sampling_data : val select_distribution_for_cycle : Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +module For_RPC : sig + (** The baking power for a given delegate from the selected stake + snapshot of the current cycle. *) + val delegate_baking_power_for_cycle : + Raw_context.t -> + Cycle_repr.t -> + Signature.public_key_hash -> + int64 tzresult Lwt.t + + (** The baking power for a given delegate computed from its current + stake. *) + val delegate_current_baking_power : + Raw_context.t -> Signature.public_key_hash -> int64 tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index ebd5605f73239c72599585c50440bfdf9783137d..2613434a8e8e6e33dce8dce5bc5a30122a04028c 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -387,6 +387,29 @@ module S = struct ~output:Data_encoding.int64 RPC_path.(path / "voting_power") + let baking_power = + RPC_service.get_service + ~description: + "The baking power for a given delegate from the selected stake \ + snapshot of the current cycle. This is the baking power which will be \ + used preserved_cycles after the current cycle to compute the baking \ + and endorsing rights." + ~query:RPC_query.empty + ~output:Data_encoding.int64 + RPC_path.(path / "baking_power") + + let current_baking_power = + RPC_service.get_service + ~description: + "The baking power of a delegate, as computed from its current stake. \ + Contrary to the value returned by the baking_power endpoint, this \ + value is not used for computing baking rights but only reflects the \ + baking power that the delegate would have if a snapshot was taken at \ + the current block." + ~query:RPC_query.empty + ~output:Data_encoding.int64 + RPC_path.(path / "current_baking_power") + let voting_info = RPC_service.get_service ~description: @@ -543,6 +566,13 @@ let register () = register1 ~chunked:false S.voting_power (fun ctxt pkh () () -> check_delegate_registered ctxt pkh >>=? fun () -> Vote.get_voting_power_free ctxt pkh) ; + register1 ~chunked:false S.baking_power (fun ctxt pkh () () -> + check_delegate_registered ctxt pkh >>=? fun () -> + let cycle = (Level.current ctxt).cycle in + Stake_distribution.For_RPC.delegate_baking_power_for_cycle ctxt cycle pkh) ; + register1 ~chunked:false S.current_baking_power (fun ctxt pkh () () -> + check_delegate_registered ctxt pkh >>=? fun () -> + Stake_distribution.For_RPC.delegate_current_baking_power ctxt pkh) ; register1 ~chunked:false S.voting_info (fun ctxt pkh () () -> check_delegate_registered ctxt pkh >>=? fun () -> Vote.get_delegate_info ctxt pkh) ; @@ -609,6 +639,12 @@ let voting_power ctxt block pkh = let current_voting_power ctxt block pkh = RPC_context.make_call1 S.current_voting_power ctxt block pkh () () +let baking_power ctxt block pkh = + RPC_context.make_call1 S.baking_power ctxt block pkh () () + +let current_baking_power ctxt block pkh = + RPC_context.make_call1 S.current_baking_power ctxt block pkh () () + let voting_info ctxt block pkh = RPC_context.make_call1 S.voting_info ctxt block pkh () () diff --git a/src/proto_alpha/lib_protocol/delegate_services.mli b/src/proto_alpha/lib_protocol/delegate_services.mli index 4d0de3b058c2c2f276ee36f8a6d71ce9ad56b950..2dcfb1d21f2dfb53b237b1c5aff42ed95667d7a7 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.mli +++ b/src/proto_alpha/lib_protocol/delegate_services.mli @@ -137,6 +137,12 @@ val current_voting_power : val voting_power : 'a #RPC_context.simple -> 'a -> public_key_hash -> int64 shell_tzresult Lwt.t +val baking_power : + 'a #RPC_context.simple -> 'a -> public_key_hash -> int64 shell_tzresult Lwt.t + +val current_baking_power : + 'a #RPC_context.simple -> 'a -> public_key_hash -> int64 shell_tzresult Lwt.t + val voting_info : 'a #RPC_context.simple -> 'a -> diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index ab17cd2a43ed11ec3771017708e654b149b6bd38..be42b172141ae0d25aa1e580fdeaa6b203df566f 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -174,6 +174,10 @@ let get_voting_power = Delegate_services.voting_power rpc_ctxt let get_total_voting_power = Alpha_services.Voting.total_voting_power rpc_ctxt +let get_baking_power = Delegate_services.baking_power rpc_ctxt + +let get_current_baking_power = Delegate_services.current_baking_power rpc_ctxt + let get_bakers ?filter ?cycle ctxt = Plugin.RPC.Baking_rights.get rpc_ctxt ?cycle ctxt >|=? fun bakers -> (match filter with None -> bakers | Some f -> List.filter f bakers) diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index c4c76d85962a92bbe05401f3c050641b3a7c32eb..a88bf12259ca043edf161c935d918af06c69f715 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -80,6 +80,12 @@ val get_voting_power : val get_total_voting_power : t -> int64 Environment.Error_monad.shell_tzresult Lwt.t +val get_baking_power : + t -> public_key_hash -> int64 Environment.Error_monad.shell_tzresult Lwt.t + +val get_current_baking_power : + t -> public_key_hash -> int64 Environment.Error_monad.shell_tzresult Lwt.t + val get_bakers : ?filter:(Plugin.RPC.Baking_rights.t -> bool) -> ?cycle:Cycle.t ->