diff --git a/Cargo.lock b/Cargo.lock index 9623771f1e867e..0af3476557e8f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5624,6 +5624,7 @@ dependencies = [ "solana-poseidon", "solana-program-runtime", "solana-sdk", + "solana-vote", "solana-zk-token-sdk", "solana_rbpf", "test-case", @@ -6739,6 +6740,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk", + "solana-vote", "solana_rbpf", "test-case", "thiserror", @@ -7344,6 +7346,7 @@ dependencies = [ "solana-program-runtime", "solana-sdk", "solana-system-program", + "solana-vote", ] [[package]] diff --git a/program-runtime/Cargo.toml b/program-runtime/Cargo.toml index a3d25a9cb37f03..b7c68d933be2b9 100644 --- a/program-runtime/Cargo.toml +++ b/program-runtime/Cargo.toml @@ -28,6 +28,7 @@ solana-frozen-abi-macro = { workspace = true, optional = true } solana-measure = { workspace = true } solana-metrics = { workspace = true } solana-sdk = { workspace = true } +solana-vote = { workspace = true } solana_rbpf = { workspace = true } thiserror = { workspace = true } diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index ace5e0de11475f..293214a870f13a 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -36,6 +36,7 @@ use { IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext, }, }, + solana_vote::vote_account::VoteAccountsHashMap, std::{ alloc::Layout, cell::RefCell, @@ -146,6 +147,8 @@ impl BpfAllocator { pub struct EnvironmentConfig<'a> { pub blockhash: Hash, + epoch_total_stake: Option, + epoch_vote_accounts: Option<&'a VoteAccountsHashMap>, pub feature_set: Arc, pub lamports_per_signature: u64, sysvar_cache: &'a SysvarCache, @@ -153,12 +156,16 @@ pub struct EnvironmentConfig<'a> { impl<'a> EnvironmentConfig<'a> { pub fn new( blockhash: Hash, + epoch_total_stake: Option, + epoch_vote_accounts: Option<&'a VoteAccountsHashMap>, feature_set: Arc, lamports_per_signature: u64, sysvar_cache: &'a SysvarCache, ) -> Self { Self { blockhash, + epoch_total_stake, + epoch_vote_accounts, feature_set, lamports_per_signature, sysvar_cache, @@ -614,6 +621,16 @@ impl<'a> InvokeContext<'a> { self.environment_config.sysvar_cache } + /// Get cached epoch total stake. + pub fn get_epoch_total_stake(&self) -> Option { + self.environment_config.epoch_total_stake + } + + /// Get cached epoch vote accounts. + pub fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + self.environment_config.epoch_vote_accounts + } + // Should alignment be enforced during user pointer translation pub fn get_check_aligned(&self) -> bool { self.transaction_context @@ -710,6 +727,8 @@ macro_rules! with_mock_invoke_context { }); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 6d3147d1e6bf03..aac0f10e7cd57e 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -29,6 +29,7 @@ assert_matches = { workspace = true } memoffset = { workspace = true } rand = { workspace = true } solana-sdk = { workspace = true, features = ["dev-context-only-utils"] } +solana-vote = { workspace = true } test-case = { workspace = true } [lib] diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 425cbf8d56e820..3d530cccc58848 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -39,7 +39,8 @@ use { self, abort_on_invalid_curve, blake3_syscall_enabled, curve25519_syscall_enabled, disable_deploy_of_alloc_free_syscall, disable_fees_sysvar, enable_alt_bn128_compression_syscall, enable_alt_bn128_syscall, - enable_big_mod_exp_syscall, enable_partitioned_epoch_reward, enable_poseidon_syscall, + enable_big_mod_exp_syscall, enable_get_epoch_stake_syscall, + enable_partitioned_epoch_reward, enable_poseidon_syscall, error_on_syscall_bpf_function_hash_collisions, get_sysvar_syscall_enabled, last_restart_slot_sysvar, reject_callx_r10, remaining_compute_units_syscall_enabled, switch_to_new_elf_parser, @@ -50,7 +51,7 @@ use { precompiles::is_precompile, program::MAX_RETURN_DATA, program_stubs::is_nonoverlapping, - pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN}, + pubkey::{Pubkey, PubkeyError, MAX_SEEDS, MAX_SEED_LEN, PUBKEY_BYTES}, secp256k1_recover::{ Secp256k1RecoverError, SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH, }, @@ -282,6 +283,8 @@ pub fn create_program_runtime_environment_v1<'a>( let remaining_compute_units_syscall_enabled = feature_set.is_active(&remaining_compute_units_syscall_enabled::id()); let get_sysvar_syscall_enabled = feature_set.is_active(&get_sysvar_syscall_enabled::id()); + let enable_get_epoch_stake_syscall = + feature_set.is_active(&enable_get_epoch_stake_syscall::id()); // !!! ATTENTION !!! // When adding new features for RBPF here, // also add them to `Bank::apply_builtin_program_feature_transitions()`. @@ -476,6 +479,14 @@ pub fn create_program_runtime_environment_v1<'a>( SyscallGetSysvar::vm, )?; + // Get Epoch Stake + register_feature_gated_function!( + result, + enable_get_epoch_stake_syscall, + *b"sol_get_epoch_stake", + SyscallGetEpochStake::vm, + )?; + // Log data result.register_function_hashed(*b"sol_log_data", SyscallLogData::vm)?; @@ -2009,6 +2020,83 @@ declare_builtin_function!( } ); +declare_builtin_function!( + // Get Epoch Stake Syscall + SyscallGetEpochStake, + fn rust( + invoke_context: &mut InvokeContext, + var_addr: u64, + _arg2: u64, + _arg3: u64, + _arg4: u64, + _arg5: u64, + memory_mapping: &mut MemoryMapping, + ) -> Result { + let compute_budget = invoke_context.get_compute_budget(); + + if var_addr == 0 { + // As specified by SIMD-0133: If `var_addr` is a null pointer: + // + // Compute units: + // + // ``` + // syscall_base + // ``` + let compute_units = compute_budget.syscall_base_cost; + consume_compute_meter(invoke_context, compute_units)?; + // + // Control flow: + // + // - The syscall aborts the virtual machine if: + // - Compute budget is exceeded. + // - Otherwise, the syscall returns a `u64` integer representing the total active + // stake on the cluster for the current epoch. + Ok(invoke_context.get_epoch_total_stake().unwrap_or(0)) + } else { + // As specified by SIMD-0133: If `var_addr` is _not_ a null pointer: + // + // Compute units: + // + // ``` + // syscall_base + floor(PUBKEY_BYTES/cpi_bytes_per_unit) + mem_op_base + // ``` + let compute_units = compute_budget + .syscall_base_cost + .saturating_add( + (PUBKEY_BYTES as u64) + .checked_div(compute_budget.cpi_bytes_per_unit) + .unwrap_or(u64::MAX), + ) + .saturating_add(compute_budget.mem_op_base_cost); + consume_compute_meter(invoke_context, compute_units)?; + // + // Control flow: + // + // - The syscall aborts the virtual machine if: + // - Not all bytes in VM memory range `[vote_addr, vote_addr + 32)` are + // readable. + // - Compute budget is exceeded. + // - Otherwise, the syscall returns a `u64` integer representing the total active + // stake delegated to the vote account at the provided address. + // If the provided vote address corresponds to an account that is not a vote + // account or does not exist, the syscall will return `0` for active stake. + let check_aligned = invoke_context.get_check_aligned(); + let vote_address = translate_type::(memory_mapping, var_addr, check_aligned)?; + + Ok( + if let Some(vote_accounts) = invoke_context.get_epoch_vote_accounts() { + vote_accounts + .get(vote_address) + .map(|(stake, _)| *stake) + .unwrap_or(0) + } else { + 0 + }, + ) + } + } +); + #[cfg(test)] #[allow(clippy::arithmetic_side_effects)] #[allow(clippy::indexing_slicing)] @@ -2039,7 +2127,8 @@ mod tests { last_restart_slot::LastRestartSlot, }, }, - std::{mem, str::FromStr}, + solana_vote::vote_account::VoteAccount, + std::{collections::HashMap, mem, str::FromStr}, test_case::test_case, }; @@ -4679,6 +4768,185 @@ mod tests { } } + #[test] + fn test_syscall_get_epoch_stake_total_stake() { + let config = Config::default(); + let mut compute_budget = ComputeBudget::default(); + let sysvar_cache = Arc::::default(); + + let expected_total_stake = 200_000_000_000_000u64; + // Compute units, as specified by SIMD-0133. + // cu = syscall_base_cost + let expected_cus = compute_budget.syscall_base_cost; + + // Set the compute budget to the expected CUs to ensure the syscall + // doesn't exceed the expected usage. + compute_budget.compute_unit_limit = expected_cus; + + with_mock_invoke_context!(invoke_context, transaction_context, vec![]); + invoke_context.environment_config = EnvironmentConfig::new( + Hash::default(), + Some(expected_total_stake), + None, // Vote accounts are not needed for this test. + Arc::::default(), + 0, + &sysvar_cache, + ); + + let null_pointer_var = std::ptr::null::() as u64; + + let mut memory_mapping = MemoryMapping::new(vec![], &config, &SBPFVersion::V2).unwrap(); + + let result = SyscallGetEpochStake::rust( + &mut invoke_context, + null_pointer_var, + 0, + 0, + 0, + 0, + &mut memory_mapping, + ) + .unwrap(); + + assert_eq!(result, expected_total_stake); + } + + #[test] + fn test_syscall_get_epoch_stake_vote_account_stake() { + let config = Config::default(); + let mut compute_budget = ComputeBudget::default(); + let sysvar_cache = Arc::::default(); + + let expected_epoch_stake = 55_000_000_000u64; + // Compute units, as specified by SIMD-0133. + // cu = syscall_base_cost + // + floor(32/cpi_bytes_per_unit) + // + mem_op_base_cost + let expected_cus = compute_budget.syscall_base_cost + + (PUBKEY_BYTES as u64) / compute_budget.cpi_bytes_per_unit + + compute_budget.mem_op_base_cost; + + // Set the compute budget to the expected CUs to ensure the syscall + // doesn't exceed the expected usage. + compute_budget.compute_unit_limit = expected_cus; + + let vote_address = Pubkey::new_unique(); + let mut vote_accounts_map = HashMap::new(); + vote_accounts_map.insert( + vote_address, + ( + expected_epoch_stake, + VoteAccount::try_from(AccountSharedData::new( + 0, + 0, + &solana_sdk::vote::program::id(), + )) + .unwrap(), + ), + ); + + with_mock_invoke_context!(invoke_context, transaction_context, vec![]); + invoke_context.environment_config = EnvironmentConfig::new( + Hash::default(), + None, // Total stake is not needed for this test. + Some(&vote_accounts_map), + Arc::::default(), + 0, + &sysvar_cache, + ); + + { + // The syscall aborts the virtual machine if not all bytes in VM + // memory range `[vote_addr, vote_addr + 32)` are readable. + let vote_address_var = 0x100000000; + + let mut memory_mapping = MemoryMapping::new( + vec![ + // Invalid read-only memory region. + MemoryRegion::new_readonly(&[2; 31], vote_address_var), + ], + &config, + &SBPFVersion::V2, + ) + .unwrap(); + + let result = SyscallGetEpochStake::rust( + &mut invoke_context, + vote_address_var, + 0, + 0, + 0, + 0, + &mut memory_mapping, + ); + + assert_access_violation!(result, vote_address_var, 32); + } + + invoke_context.mock_set_remaining(compute_budget.compute_unit_limit); + { + // Otherwise, the syscall returns a `u64` integer representing the + // total active stake delegated to the vote account at the provided + // address. + let vote_address_var = 0x100000000; + + let mut memory_mapping = MemoryMapping::new( + vec![MemoryRegion::new_readonly( + bytes_of(&vote_address), + vote_address_var, + )], + &config, + &SBPFVersion::V2, + ) + .unwrap(); + + let result = SyscallGetEpochStake::rust( + &mut invoke_context, + vote_address_var, + 0, + 0, + 0, + 0, + &mut memory_mapping, + ) + .unwrap(); + + assert_eq!(result, expected_epoch_stake); + } + + invoke_context.mock_set_remaining(compute_budget.compute_unit_limit); + { + // If the provided vote address corresponds to an account that is + // not a vote account or does not exist, the syscall will write + // `0` for active stake. + let vote_address_var = 0x100000000; + let not_a_vote_address = Pubkey::new_unique(); // Not a vote account. + + let mut memory_mapping = MemoryMapping::new( + vec![MemoryRegion::new_readonly( + bytes_of(¬_a_vote_address), + vote_address_var, + )], + &config, + &SBPFVersion::V2, + ) + .unwrap(); + + let result = SyscallGetEpochStake::rust( + &mut invoke_context, + vote_address_var, + 0, + 0, + 0, + 0, + &mut memory_mapping, + ) + .unwrap(); + + assert_eq!(result, 0); // `0` for active stake. + } + } + #[test] fn test_check_type_assumptions() { check_type_assumptions(); diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index f836c77f8a2980..4233f527504b12 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5410,6 +5410,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-sdk", + "solana-vote", "solana_rbpf", "thiserror", ] @@ -6321,6 +6322,7 @@ dependencies = [ "solana-program-runtime", "solana-sdk", "solana-system-program", + "solana-vote", ] [[package]] diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 94e4a3ef550d89..df8f4caf83783b 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -6155,6 +6155,13 @@ impl Bank { Some(self.epoch_stakes.get(&epoch)?.stakes().staked_nodes()) } + /// Get the total epoch stake for the given epoch. + pub fn epoch_total_stake(&self, epoch: Epoch) -> Option { + self.epoch_stakes + .get(&epoch) + .map(|epoch_stakes| epoch_stakes.total_stake()) + } + /// vote accounts for the specific epoch along with the stake /// attributed to each account pub fn epoch_vote_accounts(&self, epoch: Epoch) -> Option<&VoteAccountsHashMap> { @@ -6838,6 +6845,14 @@ impl TransactionProcessingCallback for Bank { self.feature_set.clone() } + fn get_epoch_total_stake(&self) -> Option { + self.epoch_total_stake(self.epoch()) + } + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + self.epoch_vote_accounts(self.epoch()) + } + fn get_program_match_criteria(&self, program: &Pubkey) -> ProgramCacheMatchCriteria { if self.check_program_modification_slot { self.program_modification_slot(program) diff --git a/runtime/src/bank/builtins/core_bpf_migration/mod.rs b/runtime/src/bank/builtins/core_bpf_migration/mod.rs index 613256f7c634cb..69f1fbdbd50c70 100644 --- a/runtime/src/bank/builtins/core_bpf_migration/mod.rs +++ b/runtime/src/bank/builtins/core_bpf_migration/mod.rs @@ -203,7 +203,14 @@ impl Bank { let mut dummy_invoke_context = InvokeContext::new( &mut dummy_transaction_context, &program_cache_for_tx_batch, - EnvironmentConfig::new(Hash::default(), self.feature_set.clone(), 0, &sysvar_cache), + EnvironmentConfig::new( + Hash::default(), + None, + None, + self.feature_set.clone(), + 0, + &sysvar_cache, + ), None, compute_budget, &mut programs_modified, diff --git a/sdk/program/src/epoch_stake.rs b/sdk/program/src/epoch_stake.rs new file mode 100644 index 00000000000000..cf8efd2eb6a2f5 --- /dev/null +++ b/sdk/program/src/epoch_stake.rs @@ -0,0 +1,30 @@ +//! API for retrieving epoch stake information. +//! +//! On-chain programs can use this API to retrieve the total stake for the +//! current epoch or the stake for a specific vote account using the +//! `sol_get_epoch_stake` syscall. + +use crate::pubkey::Pubkey; + +fn get_epoch_stake(var_addr: *const u8) -> u64 { + #[cfg(target_os = "solana")] + let result = unsafe { crate::syscalls::sol_get_epoch_stake(var_addr) }; + + #[cfg(not(target_os = "solana"))] + let result = crate::program_stubs::sol_get_epoch_stake(var_addr); + + result +} + +/// Get the current epoch's total stake. +pub fn get_epoch_total_stake() -> u64 { + get_epoch_stake(std::ptr::null::() as *const u8) +} + +/// Get the current epoch stake for a given vote address. +/// +/// If the provided vote address corresponds to an account that is not a vote +/// account or does not exist, returns `0` for active stake. +pub fn get_epoch_stake_for_vote_account(vote_address: &Pubkey) -> u64 { + get_epoch_stake(vote_address as *const _ as *const u8) +} diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 8731b6f6bc2785..017ac3a067744d 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -493,6 +493,7 @@ pub mod entrypoint; pub mod entrypoint_deprecated; pub mod epoch_rewards; pub mod epoch_schedule; +pub mod epoch_stake; pub mod feature; pub mod fee_calculator; pub mod hash; diff --git a/sdk/program/src/program_stubs.rs b/sdk/program/src/program_stubs.rs index 2b06ecbee8646c..7fd31358090118 100644 --- a/sdk/program/src/program_stubs.rs +++ b/sdk/program/src/program_stubs.rs @@ -70,6 +70,9 @@ pub trait SyscallStubs: Sync + Send { fn sol_get_last_restart_slot(&self, _var_addr: *mut u8) -> u64 { UNSUPPORTED_SYSVAR } + fn sol_get_epoch_stake(&self, _vote_address: *const u8) -> u64 { + 0 + } /// # Safety unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) { // cannot be overlapping @@ -193,6 +196,13 @@ pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 { .sol_get_last_restart_slot(var_addr) } +pub(crate) fn sol_get_epoch_stake(vote_address: *const u8) -> u64 { + SYSCALL_STUBS + .read() + .unwrap() + .sol_get_epoch_stake(vote_address) +} + pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) { unsafe { SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n); diff --git a/sdk/program/src/syscalls/definitions.rs b/sdk/program/src/syscalls/definitions.rs index f83e9d7091ea56..bdae7960b33da3 100644 --- a/sdk/program/src/syscalls/definitions.rs +++ b/sdk/program/src/syscalls/definitions.rs @@ -66,6 +66,7 @@ define_syscall!(fn sol_big_mod_exp(params: *const u8, result: *mut u8) -> u64); define_syscall!(fn sol_remaining_compute_units() -> u64); define_syscall!(fn sol_alt_bn128_compression(op: u64, input: *const u8, input_size: u64, result: *mut u8) -> u64); define_syscall!(fn sol_get_sysvar(sysvar_id_addr: *const u8, result: *mut u8, offset: u64, length: u64) -> u64); +define_syscall!(fn sol_get_epoch_stake(vote_address: *const u8) -> u64); // these are to be deprecated once they are superceded by sol_get_sysvar define_syscall!(fn sol_get_clock_sysvar(addr: *mut u8) -> u64); diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index ed3a7ff5162341..ee30a1a459150a 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -817,6 +817,10 @@ pub mod migrate_config_program_to_core_bpf { solana_sdk::declare_id!("2Fr57nzzkLYXW695UdDxDeR5fhnZWSttZeZYemrnpGFV"); } +pub mod enable_get_epoch_stake_syscall { + solana_sdk::declare_id!("7mScTYkJXsbdrcwTQRs7oeCSXoJm4WjzBsRyf8bCU3Np"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -1016,6 +1020,7 @@ lazy_static! { (migrate_feature_gate_program_to_core_bpf::id(), "Migrate Feature Gate program to Core BPF (programify) #1003"), (vote_only_full_fec_sets::id(), "vote only full fec sets"), (migrate_config_program_to_core_bpf::id(), "Migrate Config program to Core BPF #1378"), + (enable_get_epoch_stake_syscall::id(), "Enable syscall: sol_get_epoch_stake #884"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/svm/Cargo.toml b/svm/Cargo.toml index ebe845cd130837..d86759cc7db09d 100644 --- a/svm/Cargo.toml +++ b/svm/Cargo.toml @@ -25,6 +25,7 @@ solana-metrics = { workspace = true } solana-program-runtime = { workspace = true } solana-sdk = { workspace = true } solana-system-program = { workspace = true } +solana-vote = { workspace = true } [lib] crate-type = ["lib"] diff --git a/svm/src/account_loader.rs b/svm/src/account_loader.rs index 2a14d0cc774f1e..a6af292e94c88d 100644 --- a/svm/src/account_loader.rs +++ b/svm/src/account_loader.rs @@ -513,6 +513,7 @@ mod tests { transaction::{Result, SanitizedTransaction, Transaction, TransactionError}, transaction_context::{TransactionAccount, TransactionContext}, }, + solana_vote::vote_account::VoteAccountsHashMap, std::{borrow::Cow, collections::HashMap, convert::TryFrom, sync::Arc}, }; @@ -543,6 +544,14 @@ mod tests { fn get_feature_set(&self) -> Arc { self.feature_set.clone() } + + fn get_epoch_total_stake(&self) -> Option { + None + } + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + None + } } fn load_accounts_with_fee_and_rent( diff --git a/svm/src/message_processor.rs b/svm/src/message_processor.rs index da9498d59533b8..eb442a23266064 100644 --- a/svm/src/message_processor.rs +++ b/svm/src/message_processor.rs @@ -278,6 +278,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -332,6 +334,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -376,6 +380,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -511,6 +517,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -550,6 +558,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -586,6 +596,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, @@ -683,6 +695,8 @@ mod tests { let mut programs_modified_by_tx = ProgramCacheForTxBatch::default(); let environment_config = EnvironmentConfig::new( Hash::default(), + None, + None, Arc::new(FeatureSet::all_enabled()), 0, &sysvar_cache, diff --git a/svm/src/program_loader.rs b/svm/src/program_loader.rs index 8920340d4a2544..3b8f35923b5055 100644 --- a/svm/src/program_loader.rs +++ b/svm/src/program_loader.rs @@ -232,6 +232,7 @@ mod tests { account::WritableAccount, bpf_loader, bpf_loader_upgradeable, feature_set::FeatureSet, hash::Hash, rent_collector::RentCollector, }, + solana_vote::vote_account::VoteAccountsHashMap, std::{ cell::RefCell, collections::HashMap, @@ -285,6 +286,14 @@ mod tests { self.feature_set.clone() } + fn get_epoch_total_stake(&self) -> Option { + None + } + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + None + } + fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { let mut account_data = AccountSharedData::default(); account_data.set_data(name.as_bytes().to_vec()); diff --git a/svm/src/transaction_processing_callback.rs b/svm/src/transaction_processing_callback.rs index ded481eda8ea5f..f92dc6e2299b39 100644 --- a/svm/src/transaction_processing_callback.rs +++ b/svm/src/transaction_processing_callback.rs @@ -4,6 +4,7 @@ use { account::AccountSharedData, feature_set::FeatureSet, hash::Hash, pubkey::Pubkey, rent_collector::RentCollector, }, + solana_vote::vote_account::VoteAccountsHashMap, std::sync::Arc, }; @@ -19,6 +20,10 @@ pub trait TransactionProcessingCallback { fn get_feature_set(&self) -> Arc; + fn get_epoch_total_stake(&self) -> Option; + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap>; + fn get_program_match_criteria(&self, _program: &Pubkey) -> ProgramCacheMatchCriteria { ProgramCacheMatchCriteria::NoCriteria } diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index d9d1182dd55899..9b7c5f517802d0 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -631,6 +631,8 @@ impl TransactionBatchProcessor { program_cache_for_tx_batch, EnvironmentConfig::new( blockhash, + callback.get_epoch_total_stake(), + callback.get_epoch_vote_accounts(), callback.get_feature_set(), lamports_per_signature, sysvar_cache, @@ -866,6 +868,7 @@ mod tests { transaction::{SanitizedTransaction, Transaction, TransactionError}, transaction_context::TransactionContext, }, + solana_vote::vote_account::VoteAccountsHashMap, std::{ env, fs::{self, File}, @@ -929,6 +932,14 @@ mod tests { self.feature_set.clone() } + fn get_epoch_total_stake(&self) -> Option { + None + } + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + None + } + fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { let mut account_data = AccountSharedData::default(); account_data.set_data(name.as_bytes().to_vec()); diff --git a/svm/tests/conformance.rs b/svm/tests/conformance.rs index 7245684c4b7222..8c916fea444a70 100644 --- a/svm/tests/conformance.rs +++ b/svm/tests/conformance.rs @@ -463,6 +463,8 @@ fn execute_fixture_as_instr( let sysvar_cache = &batch_processor.sysvar_cache.read().unwrap(); let env_config = EnvironmentConfig::new( mock_bank.blockhash, + None, + None, mock_bank.feature_set.clone(), mock_bank.lamports_per_sginature, sysvar_cache, diff --git a/svm/tests/mock_bank.rs b/svm/tests/mock_bank.rs index addbc07cc7e64f..7ee17547577957 100644 --- a/svm/tests/mock_bank.rs +++ b/svm/tests/mock_bank.rs @@ -11,6 +11,7 @@ use { slot_hashes::Slot, }, solana_svm::transaction_processing_callback::TransactionProcessingCallback, + solana_vote::vote_account::VoteAccountsHashMap, std::{cell::RefCell, cmp::Ordering, collections::HashMap, sync::Arc}, }; @@ -69,6 +70,14 @@ impl TransactionProcessingCallback for MockBankCallback { self.feature_set.clone() } + fn get_epoch_total_stake(&self) -> Option { + None + } + + fn get_epoch_vote_accounts(&self) -> Option<&VoteAccountsHashMap> { + None + } + fn add_builtin_account(&self, name: &str, program_id: &Pubkey) { let account_data = native_loader::create_loadable_account_with_fields(name, (5000, 0));