diff --git a/account-decoder/src/parse_sysvar.rs b/account-decoder/src/parse_sysvar.rs index 7f7469490f..35746949c7 100644 --- a/account-decoder/src/parse_sysvar.rs +++ b/account-decoder/src/parse_sysvar.rs @@ -15,7 +15,9 @@ use { slot_hashes::SlotHashes, slot_history::{self, SlotHistory}, stake_history::{StakeHistory, StakeHistoryEntry}, - sysvar::{self, last_restart_slot::LastRestartSlot, rewards::Rewards}, + sysvar::{ + self, epoch_rewards::EpochRewards, last_restart_slot::LastRestartSlot, rewards::Rewards, + }, }, }; @@ -89,6 +91,10 @@ pub fn parse_sysvar(data: &[u8], pubkey: &Pubkey) -> Result(data) + .ok() + .map(SysvarAccountType::EpochRewards) } else { None } @@ -113,6 +119,7 @@ pub enum SysvarAccountType { SlotHistory(UiSlotHistory), StakeHistory(Vec), LastRestartSlot(UiLastRestartSlot), + EpochRewards(EpochRewards), } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)] @@ -363,5 +370,16 @@ mod test { last_restart_slot: 1282 }) ); + + let epoch_rewards = EpochRewards { + total_rewards: 100, + distributed_rewards: 20, + distribution_complete_block_height: 42, + }; + let epoch_rewards_sysvar = create_account_for_test(&epoch_rewards); + assert_eq!( + parse_sysvar(&epoch_rewards_sysvar.data, &sysvar::epoch_rewards::id()).unwrap(), + SysvarAccountType::EpochRewards(epoch_rewards), + ); } } diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 90f3015c1d..1033fb8cf7 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -5,8 +5,8 @@ pub use self::{ }, mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset}, sysvar::{ - SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar, - SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar, + SyscallGetClockSysvar, SyscallGetEpochRewardsSysvar, SyscallGetEpochScheduleSysvar, + SyscallGetFeesSysvar, SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar, }, }; #[allow(deprecated)] @@ -36,7 +36,7 @@ use { self, blake3_syscall_enabled, curve25519_syscall_enabled, disable_cpi_setting_executable_and_rent_epoch, disable_deploy_of_alloc_free_syscall, disable_fees_sysvar, enable_alt_bn128_syscall, enable_big_mod_exp_syscall, - enable_early_verification_of_account_modifications, + enable_early_verification_of_account_modifications, enable_partitioned_epoch_reward, error_on_syscall_bpf_function_hash_collisions, last_restart_slot_sysvar, libsecp256k1_0_5_upgrade_enabled, reject_callx_r10, stop_sibling_instruction_search_at_parent, stop_truncating_strings_in_syscalls, @@ -186,6 +186,8 @@ pub fn create_program_runtime_environment<'a>( let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id()); let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id()); let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id()); + let epoch_rewards_syscall_enabled = + feature_set.is_active(&enable_partitioned_epoch_reward::id()); let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs && feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id()); let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id()); @@ -272,6 +274,13 @@ pub fn create_program_runtime_environment<'a>( SyscallGetLastRestartSlotSysvar::call, )?; + register_feature_gated_function!( + result, + epoch_rewards_syscall_enabled, + b"sol_get_epoch_rewrds_sysvar", + SyscallGetEpochRewardsSysvar::call, + )?; + // Memory ops result.register_function(b"sol_memcpy_", SyscallMemcpy::call)?; result.register_function(b"sol_memmove_", SyscallMemmove::call)?; @@ -1807,7 +1816,9 @@ mod tests { instruction::Instruction, program::check_type_assumptions, stable_layout::stable_instruction::StableInstruction, - sysvar::{self, clock::Clock, epoch_schedule::EpochSchedule}, + sysvar::{ + self, clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule, + }, }, std::{mem, str::FromStr}, }; @@ -3177,11 +3188,17 @@ mod tests { src_rent.exemption_threshold = 2.0; src_rent.burn_percent = 3; + let mut src_rewards = create_filled_type::(false); + src_rewards.total_rewards = 100; + src_rewards.distributed_rewards = 10; + src_rewards.distribution_complete_block_height = 42; + let mut sysvar_cache = SysvarCache::default(); sysvar_cache.set_clock(src_clock.clone()); sysvar_cache.set_epoch_schedule(src_epochschedule); sysvar_cache.set_fees(src_fees.clone()); sysvar_cache.set_rent(src_rent); + sysvar_cache.set_epoch_rewards(src_rewards); let transaction_accounts = vec![ ( @@ -3200,6 +3217,10 @@ mod tests { sysvar::rent::id(), create_account_shared_data_for_test(&src_rent), ), + ( + sysvar::epoch_rewards::id(), + create_account_shared_data_for_test(&src_rewards), + ), ]; with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts); @@ -3345,6 +3366,42 @@ mod tests { clean_rent.burn_percent = src_rent.burn_percent; assert!(are_bytes_equal(&got_rent, &clean_rent)); } + + // Test epoch rewards sysvar + { + let mut got_rewards = create_filled_type::(true); + let got_rewards_va = 0x100000000; + + let mut memory_mapping = MemoryMapping::new( + vec![MemoryRegion::new_writable( + bytes_of_mut(&mut got_rewards), + got_rewards_va, + )], + &config, + ) + .unwrap(); + + let mut result = ProgramResult::Ok(0); + SyscallGetEpochRewardsSysvar::call( + &mut invoke_context, + got_rewards_va, + 0, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + result.unwrap(); + assert_eq!(got_rewards, src_rewards); + + let mut clean_rewards = create_filled_type::(true); + clean_rewards.total_rewards = src_rewards.total_rewards; + clean_rewards.distributed_rewards = src_rewards.distributed_rewards; + clean_rewards.distribution_complete_block_height = + src_rewards.distribution_complete_block_height; + assert!(are_bytes_equal(&got_rewards, &clean_rewards)); + } } fn call_program_address_common<'a, 'b: 'a>( diff --git a/programs/bpf_loader/src/syscalls/sysvar.rs b/programs/bpf_loader/src/syscalls/sysvar.rs index 03973820e4..d86402b340 100644 --- a/programs/bpf_loader/src/syscalls/sysvar.rs +++ b/programs/bpf_loader/src/syscalls/sysvar.rs @@ -66,6 +66,28 @@ declare_syscall!( } ); +declare_syscall!( + /// Get a EpochRewards sysvar + SyscallGetEpochRewardsSysvar, + fn inner_call( + invoke_context: &mut InvokeContext, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + ) -> Result { + get_sysvar( + invoke_context.get_sysvar_cache().get_epoch_rewards(), + var_addr, + invoke_context.get_check_aligned(), + memory_mapping, + invoke_context, + ) + } +); + declare_syscall!( /// Get a Fees sysvar SyscallGetFeesSysvar, diff --git a/programs/sbf/rust/sysvar/src/lib.rs b/programs/sbf/rust/sysvar/src/lib.rs index bd577dd3ca..c4b51360de 100644 --- a/programs/sbf/rust/sysvar/src/lib.rs +++ b/programs/sbf/rust/sysvar/src/lib.rs @@ -13,8 +13,9 @@ use solana_program::{ program_error::ProgramError, pubkey::Pubkey, sysvar::{ - self, clock::Clock, epoch_schedule::EpochSchedule, instructions, rent::Rent, - slot_hashes::SlotHashes, slot_history::SlotHistory, stake_history::StakeHistory, Sysvar, + self, clock::Clock, epoch_rewards::EpochRewards, epoch_schedule::EpochSchedule, + instructions, rent::Rent, slot_hashes::SlotHashes, slot_history::SlotHistory, + stake_history::StakeHistory, Sysvar, }, }; @@ -69,6 +70,7 @@ pub fn process_instruction( AccountMeta::new_readonly(*accounts[8].key, false), AccountMeta::new_readonly(*accounts[9].key, false), AccountMeta::new_readonly(*accounts[10].key, false), + AccountMeta::new_readonly(*accounts[11].key, false), ], ) ); @@ -123,5 +125,18 @@ pub fn process_instruction( assert_eq!(fees, got_fees); } + // Epoch Rewards + { + msg!("EpochRewards identifier:"); + sysvar::epoch_rewards::id().log(); + let epoch_rewards = EpochRewards::from_account_info(&accounts[11]); + // epoch_rewards sysvar should only be valid during epoch reward period. In this test case, + // the test bank is outside reward period. Therefore, we expect that the epoch_rewards + // sysvar doesn't exist. + assert!(epoch_rewards.is_err()); + let got_epoch_rewards = EpochRewards::get(); + assert!(got_epoch_rewards.is_err()); + } + Ok(()) } diff --git a/programs/sbf/rust/sysvar/tests/lib.rs b/programs/sbf/rust/sysvar/tests/lib.rs index 9294ccdc0b..980171e39c 100644 --- a/programs/sbf/rust/sysvar/tests/lib.rs +++ b/programs/sbf/rust/sysvar/tests/lib.rs @@ -9,8 +9,8 @@ use { pubkey::Pubkey, signature::Signer, sysvar::{ - clock, epoch_schedule, fees, instructions, recent_blockhashes, rent, slot_hashes, - slot_history, stake_history, + clock, epoch_rewards, epoch_schedule, fees, instructions, recent_blockhashes, rent, + slot_hashes, slot_history, stake_history, }, transaction::Transaction, }, @@ -45,6 +45,7 @@ async fn test_sysvars() { AccountMeta::new_readonly(stake_history::id(), false), #[allow(deprecated)] AccountMeta::new_readonly(fees::id(), false), + AccountMeta::new_readonly(epoch_rewards::id(), false), ], )], Some(&payer.pubkey()), @@ -78,6 +79,7 @@ async fn test_sysvars() { AccountMeta::new_readonly(stake_history::id(), false), #[allow(deprecated)] AccountMeta::new_readonly(fees::id(), false), + AccountMeta::new_readonly(epoch_rewards::id(), false), ], )], Some(&payer.pubkey()),