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
|
||||
- Fees
|
||||
- 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
|
||||
always _readonly_.
|
||||
|
@ -140,3 +141,18 @@ and de-activations per epoch. It is updated at the start of every epoch.
|
|||
- Address: `SysvarStakeHistory1111111111111111111111111`
|
||||
- Layout:
|
||||
[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 entrypoint;
|
||||
pub mod entrypoint_deprecated;
|
||||
pub mod epoch_rewards;
|
||||
pub mod epoch_schedule;
|
||||
pub mod feature;
|
||||
pub mod fee_calculator;
|
||||
|
|
|
@ -51,6 +51,10 @@ pub trait SyscallStubs: Sync + Send {
|
|||
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
fn sol_get_epoch_rewards_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||
UNSUPPORTED_SYSVAR
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
|
||||
// cannot be overlapping
|
||||
|
@ -197,6 +201,13 @@ pub(crate) fn sol_get_stack_height() -> u64 {
|
|||
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.
|
||||
///
|
||||
/// 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_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_get_epoch_rewards_sysvar(addr: *mut u8) -> u64);
|
||||
|
||||
#[cfg(target_feature = "static-syscalls")]
|
||||
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 epoch_rewards;
|
||||
pub mod epoch_schedule;
|
||||
pub mod fees;
|
||||
pub mod instructions;
|
||||
|
@ -111,6 +112,7 @@ lazy_static! {
|
|||
slot_history::id(),
|
||||
stake_history::id(),
|
||||
instructions::id(),
|
||||
epoch_rewards::id(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue