Add epoch_reward sysvar (#32049)
* Add epoch_reward sysvar * Apply suggestions from code review Co-authored-by: Brooks <brooks@prumo.org> * remove new from EpochRewards * remove copy test * Apply suggestions from code review Co-authored-by: Jon Cinque <joncinque@pm.me> Co-authored-by: Tyera <teulberg@gmail.com> * reviews --------- Co-authored-by: HaoranYi <haoran.yi@solana.com> Co-authored-by: Brooks <brooks@prumo.org> Co-authored-by: Jon Cinque <joncinque@pm.me> Co-authored-by: Tyera <teulberg@gmail.com>
This commit is contained in:
parent
3b0b0ba07d
commit
ffe4c06a19
|
@ -22,6 +22,7 @@ The following sysvars support `get`:
|
||||||
- EpochSchedule
|
- EpochSchedule
|
||||||
- Fees
|
- Fees
|
||||||
- Rent
|
- Rent
|
||||||
|
- EpochRewards
|
||||||
|
|
||||||
The second is to pass the sysvar to the program as an account by including its address as one of the accounts in the `Instruction` and then deserializing the data during execution. Access to sysvars accounts is
|
The second is to pass the sysvar to the program as an account by including its address as one of the accounts in the `Instruction` and then deserializing the data during execution. Access to sysvars accounts is
|
||||||
always _readonly_.
|
always _readonly_.
|
||||||
|
@ -140,3 +141,18 @@ and de-activations per epoch. It is updated at the start of every epoch.
|
||||||
- Address: `SysvarStakeHistory1111111111111111111111111`
|
- Address: `SysvarStakeHistory1111111111111111111111111`
|
||||||
- Layout:
|
- Layout:
|
||||||
[StakeHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/stake_history/struct.StakeHistory.html)
|
[StakeHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/stake_history/struct.StakeHistory.html)
|
||||||
|
|
||||||
|
## EpochRewards
|
||||||
|
|
||||||
|
The EpochRewards sysvar tracks the progress of epoch rewards distribution. The
|
||||||
|
sysvar is created in the first block of the epoch, and lasts for several blocks
|
||||||
|
while paying out the rewards. When all rewards have been distributed, the sysvar
|
||||||
|
is deleted. Unlike other sysvars, which almost always exist on-chain,
|
||||||
|
EpochRewards sysvar only exists during the reward period. Therefore, calling
|
||||||
|
`EpochRewards::get()` on blocks that are outside of the reward period will
|
||||||
|
return an error, i.e. `UnsupportedSysvar`. This can serve as a method for
|
||||||
|
determining whether epoch rewards distribution has finished.
|
||||||
|
|
||||||
|
- Address: `SysvarEpochRewards1111111111111111111111111`
|
||||||
|
- Layout:
|
||||||
|
[EpochRewards](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_rewards/struct.EpochRewards.html)
|
|
@ -0,0 +1,75 @@
|
||||||
|
//! A type to hold data for the [`EpochRewards` sysvar][sv].
|
||||||
|
//!
|
||||||
|
//! [sv]: https://docs.solana.com/developing/runtime-facilities/sysvars#EpochRewards
|
||||||
|
//!
|
||||||
|
//! The sysvar ID is declared in [`sysvar::epoch_rewards`].
|
||||||
|
//!
|
||||||
|
//! [`sysvar::epoch_rewards`]: crate::sysvar::epoch_rewards
|
||||||
|
|
||||||
|
use std::ops::AddAssign;
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, Clone, Copy, AbiExample)]
|
||||||
|
pub struct EpochRewards {
|
||||||
|
/// total rewards for the current epoch, in lamports
|
||||||
|
pub total_rewards: u64,
|
||||||
|
|
||||||
|
/// distributed rewards for the current epoch, in lamports
|
||||||
|
pub distributed_rewards: u64,
|
||||||
|
|
||||||
|
/// distribution of all staking rewards for the current
|
||||||
|
/// epoch will be completed at this block height
|
||||||
|
pub distribution_complete_block_height: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EpochRewards {
|
||||||
|
pub fn distribute(&mut self, amount: u64) {
|
||||||
|
assert!(self.distributed_rewards.saturating_add(amount) <= self.total_rewards);
|
||||||
|
|
||||||
|
self.distributed_rewards.add_assign(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl EpochRewards {
|
||||||
|
pub fn new(
|
||||||
|
total_rewards: u64,
|
||||||
|
distributed_rewards: u64,
|
||||||
|
distribution_complete_block_height: u64,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
total_rewards,
|
||||||
|
distributed_rewards,
|
||||||
|
distribution_complete_block_height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_epoch_rewards_new() {
|
||||||
|
let epoch_rewards = EpochRewards::new(100, 0, 64);
|
||||||
|
|
||||||
|
assert_eq!(epoch_rewards.total_rewards, 100);
|
||||||
|
assert_eq!(epoch_rewards.distributed_rewards, 0);
|
||||||
|
assert_eq!(epoch_rewards.distribution_complete_block_height, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_epoch_rewards_distribute() {
|
||||||
|
let mut epoch_rewards = EpochRewards::new(100, 0, 64);
|
||||||
|
epoch_rewards.distribute(100);
|
||||||
|
|
||||||
|
assert_eq!(epoch_rewards.total_rewards, 100);
|
||||||
|
assert_eq!(epoch_rewards.distributed_rewards, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(
|
||||||
|
expected = "assertion failed: self.distributed_rewards.saturating_add(amount) <= self.total_rewards"
|
||||||
|
)]
|
||||||
|
fn test_epoch_rewards_distribute_panic() {
|
||||||
|
let mut epoch_rewards = EpochRewards::new(100, 0, 64);
|
||||||
|
epoch_rewards.distribute(200);
|
||||||
|
}
|
||||||
|
}
|
|
@ -486,6 +486,7 @@ pub mod decode_error;
|
||||||
pub mod ed25519_program;
|
pub mod ed25519_program;
|
||||||
pub mod entrypoint;
|
pub mod entrypoint;
|
||||||
pub mod entrypoint_deprecated;
|
pub mod entrypoint_deprecated;
|
||||||
|
pub mod epoch_rewards;
|
||||||
pub mod epoch_schedule;
|
pub mod epoch_schedule;
|
||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod fee_calculator;
|
pub mod fee_calculator;
|
||||||
|
|
|
@ -51,6 +51,10 @@ pub trait SyscallStubs: Sync + Send {
|
||||||
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
UNSUPPORTED_SYSVAR
|
UNSUPPORTED_SYSVAR
|
||||||
}
|
}
|
||||||
|
fn sol_get_epoch_rewards_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
|
UNSUPPORTED_SYSVAR
|
||||||
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||||
// cannot be overlapping
|
// cannot be overlapping
|
||||||
|
@ -197,6 +201,13 @@ pub(crate) fn sol_get_stack_height() -> u64 {
|
||||||
SYSCALL_STUBS.read().unwrap().sol_get_stack_height()
|
SYSCALL_STUBS.read().unwrap().sol_get_stack_height()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sol_get_epoch_rewards_sysvar(var_addr: *mut u8) -> u64 {
|
||||||
|
SYSCALL_STUBS
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.sol_get_epoch_rewards_sysvar(var_addr)
|
||||||
|
}
|
||||||
|
|
||||||
/// Check that two regions do not overlap.
|
/// Check that two regions do not overlap.
|
||||||
///
|
///
|
||||||
/// Hidden to share with bpf_loader without being part of the API surface.
|
/// Hidden to share with bpf_loader without being part of the API surface.
|
||||||
|
|
|
@ -67,6 +67,7 @@ define_syscall!(fn sol_curve_multiscalar_mul(curve_id: u64, scalars_addr: *const
|
||||||
define_syscall!(fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result: *mut u8) -> u64);
|
define_syscall!(fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result: *mut u8) -> u64);
|
||||||
define_syscall!(fn sol_alt_bn128_group_op(group_op: u64, input: *const u8, input_size: u64, result: *mut u8) -> u64);
|
define_syscall!(fn sol_alt_bn128_group_op(group_op: u64, input: *const u8, input_size: u64, result: *mut u8) -> u64);
|
||||||
define_syscall!(fn sol_big_mod_exp(params: *const u8, result: *mut u8) -> u64);
|
define_syscall!(fn sol_big_mod_exp(params: *const u8, result: *mut u8) -> u64);
|
||||||
|
define_syscall!(fn sol_get_epoch_rewards_sysvar(addr: *mut u8) -> u64);
|
||||||
|
|
||||||
#[cfg(target_feature = "static-syscalls")]
|
#[cfg(target_feature = "static-syscalls")]
|
||||||
pub const fn sys_hash(name: &str) -> usize {
|
pub const fn sys_hash(name: &str) -> usize {
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
//! Epoch rewards for current epoch
|
||||||
|
//!
|
||||||
|
//! The _epoch rewards_ sysvar provides access to the [`EpochRewards`] type,
|
||||||
|
//! which tracks the progress of epoch rewards distribution. It includes the
|
||||||
|
//! - total rewards for the current epoch, in lamports
|
||||||
|
//! - rewards for the current epoch distributed so far, in lamports
|
||||||
|
//! - distribution completed block height, i.e. distribution of all staking rewards for the current
|
||||||
|
//! epoch will be completed at this block height
|
||||||
|
//!
|
||||||
|
//! [`EpochRewards`] implements [`Sysvar::get`] and can be loaded efficiently without
|
||||||
|
//! passing the sysvar account ID to the program.
|
||||||
|
//!
|
||||||
|
//! See also the Solana [documentation on the epoch rewards sysvar][sdoc].
|
||||||
|
//!
|
||||||
|
//! [sdoc]: https://docs.solana.com/developing/runtime-facilities/sysvars#epochrewards
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! Accessing via on-chain program directly:
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! # use solana_program::{
|
||||||
|
//! # account_info::{AccountInfo, next_account_info},
|
||||||
|
//! # entrypoint::ProgramResult,
|
||||||
|
//! # msg,
|
||||||
|
//! # program_error::ProgramError,
|
||||||
|
//! # pubkey::Pubkey,
|
||||||
|
//! # sysvar::epoch_rewards::{self, EpochRewards},
|
||||||
|
//! # sysvar::Sysvar,
|
||||||
|
//! # };
|
||||||
|
//! #
|
||||||
|
//! fn process_instruction(
|
||||||
|
//! program_id: &Pubkey,
|
||||||
|
//! accounts: &[AccountInfo],
|
||||||
|
//! instruction_data: &[u8],
|
||||||
|
//! ) -> ProgramResult {
|
||||||
|
//!
|
||||||
|
//! let epoch_rewards = EpochRewards::get()?;
|
||||||
|
//! msg!("epoch_rewards: {:#?}", epoch_rewards);
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! #
|
||||||
|
//! # use solana_program::sysvar::SysvarId;
|
||||||
|
//! # let p = EpochRewards::id();
|
||||||
|
//! # let l = &mut 1120560;
|
||||||
|
//! # let d = &mut vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
//! # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0);
|
||||||
|
//! # let accounts = &[a.clone(), a];
|
||||||
|
//! # process_instruction(
|
||||||
|
//! # &Pubkey::new_unique(),
|
||||||
|
//! # accounts,
|
||||||
|
//! # &[],
|
||||||
|
//! # )?;
|
||||||
|
//! # Ok::<(), ProgramError>(())
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Accessing via on-chain program's account parameters:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use solana_program::{
|
||||||
|
//! # account_info::{AccountInfo, next_account_info},
|
||||||
|
//! # entrypoint::ProgramResult,
|
||||||
|
//! # msg,
|
||||||
|
//! # pubkey::Pubkey,
|
||||||
|
//! # sysvar::epoch_rewards::{self, EpochRewards},
|
||||||
|
//! # sysvar::Sysvar,
|
||||||
|
//! # };
|
||||||
|
//! # use solana_program::program_error::ProgramError;
|
||||||
|
//! #
|
||||||
|
//! fn process_instruction(
|
||||||
|
//! program_id: &Pubkey,
|
||||||
|
//! accounts: &[AccountInfo],
|
||||||
|
//! instruction_data: &[u8],
|
||||||
|
//! ) -> ProgramResult {
|
||||||
|
//! let account_info_iter = &mut accounts.iter();
|
||||||
|
//! let epoch_rewards_account_info = next_account_info(account_info_iter)?;
|
||||||
|
//!
|
||||||
|
//! assert!(epoch_rewards::check_id(epoch_rewards_account_info.key));
|
||||||
|
//!
|
||||||
|
//! let epoch_rewards = EpochRewards::from_account_info(epoch_rewards_account_info)?;
|
||||||
|
//! msg!("epoch_rewards: {:#?}", epoch_rewards);
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! #
|
||||||
|
//! # use solana_program::sysvar::SysvarId;
|
||||||
|
//! # let p = EpochRewards::id();
|
||||||
|
//! # let l = &mut 1120560;
|
||||||
|
//! # let d = &mut vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
//! # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0);
|
||||||
|
//! # let accounts = &[a.clone(), a];
|
||||||
|
//! # process_instruction(
|
||||||
|
//! # &Pubkey::new_unique(),
|
||||||
|
//! # accounts,
|
||||||
|
//! # &[],
|
||||||
|
//! # )?;
|
||||||
|
//! # Ok::<(), ProgramError>(())
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Accessing via the RPC client:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use solana_program::example_mocks::solana_sdk;
|
||||||
|
//! # use solana_program::example_mocks::solana_rpc_client;
|
||||||
|
//! # use solana_sdk::account::Account;
|
||||||
|
//! # use solana_rpc_client::rpc_client::RpcClient;
|
||||||
|
//! # use solana_sdk::sysvar::epoch_rewards::{self, EpochRewards};
|
||||||
|
//! # use anyhow::Result;
|
||||||
|
//! #
|
||||||
|
//! fn print_sysvar_epoch_rewards(client: &RpcClient) -> Result<()> {
|
||||||
|
//! # client.set_get_account_response(epoch_rewards::ID, Account {
|
||||||
|
//! # lamports: 1120560,
|
||||||
|
//! # data: vec![0, 202, 154, 59, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
//! # owner: solana_sdk::system_program::ID,
|
||||||
|
//! # executable: false,
|
||||||
|
//! # rent_epoch: 307,
|
||||||
|
//! # });
|
||||||
|
//! #
|
||||||
|
//! let epoch_rewards = client.get_account(&epoch_rewards::ID)?;
|
||||||
|
//! let data: EpochRewards = bincode::deserialize(&epoch_rewards.data)?;
|
||||||
|
//!
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! #
|
||||||
|
//! # let client = RpcClient::new(String::new());
|
||||||
|
//! # print_sysvar_epoch_rewards(&client)?;
|
||||||
|
//! #
|
||||||
|
//! # Ok::<(), anyhow::Error>(())
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
pub use crate::epoch_rewards::EpochRewards;
|
||||||
|
use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar};
|
||||||
|
|
||||||
|
crate::declare_sysvar_id!("SysvarEpochRewards1111111111111111111111111", EpochRewards);
|
||||||
|
|
||||||
|
impl Sysvar for EpochRewards {
|
||||||
|
impl_sysvar_get!(sol_get_epoch_rewards_sysvar);
|
||||||
|
}
|
|
@ -87,6 +87,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
|
pub mod epoch_rewards;
|
||||||
pub mod epoch_schedule;
|
pub mod epoch_schedule;
|
||||||
pub mod fees;
|
pub mod fees;
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
|
@ -111,6 +112,7 @@ lazy_static! {
|
||||||
slot_history::id(),
|
slot_history::id(),
|
||||||
stake_history::id(),
|
stake_history::id(),
|
||||||
instructions::id(),
|
instructions::id(),
|
||||||
|
epoch_rewards::id(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue