Przeglądaj źródła

Epoch stake callback (#5597)

* svm-callback: define separate trait for epoch stake

* invoke-context: use callback for epoch stake

* svm-callback: respect semver
Joe C 7 miesięcy temu
rodzic
commit
03d0db840e

+ 5 - 0
Cargo.lock

@@ -249,6 +249,7 @@ dependencies = [
  "solana-stake-program",
  "solana-stake-program",
  "solana-storage-bigtable",
  "solana-storage-bigtable",
  "solana-streamer",
  "solana-streamer",
+ "solana-svm-callback",
  "solana-transaction-context 2.3.0",
  "solana-transaction-context 2.3.0",
  "solana-transaction-status",
  "solana-transaction-status",
  "solana-type-overrides",
  "solana-type-overrides",
@@ -6927,6 +6928,7 @@ dependencies = [
  "solana-sha256-hasher",
  "solana-sha256-hasher",
  "solana-slot-hashes",
  "solana-slot-hashes",
  "solana-stable-layout",
  "solana-stable-layout",
+ "solana-svm-callback",
  "solana-system-interface",
  "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-sysvar-id",
@@ -9175,6 +9177,7 @@ dependencies = [
  "solana-sdk-ids",
  "solana-sdk-ids",
  "solana-slot-hashes",
  "solana-slot-hashes",
  "solana-stable-layout",
  "solana-stable-layout",
+ "solana-svm-callback",
  "solana-system-interface",
  "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-sysvar-id",
@@ -10183,6 +10186,7 @@ dependencies = [
  "solana-rent",
  "solana-rent",
  "solana-sdk-ids",
  "solana-sdk-ids",
  "solana-stake-interface",
  "solana-stake-interface",
+ "solana-svm-callback",
  "solana-sysvar",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-sysvar-id",
  "solana-transaction-context 2.3.0",
  "solana-transaction-context 2.3.0",
@@ -10490,6 +10494,7 @@ dependencies = [
  "solana-sdk",
  "solana-sdk",
  "solana-sdk-ids",
  "solana-sdk-ids",
  "solana-sha256-hasher",
  "solana-sha256-hasher",
+ "solana-svm-callback",
  "solana-system-interface",
  "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar",
  "solana-transaction-context 2.3.0",
  "solana-transaction-context 2.3.0",

+ 1 - 0
ledger-tool/Cargo.toml

@@ -54,6 +54,7 @@ solana-sdk = { version = "=2.2.2", features = ["openssl-vendored"] }
 solana-stake-program = { workspace = true }
 solana-stake-program = { workspace = true }
 solana-storage-bigtable = { workspace = true }
 solana-storage-bigtable = { workspace = true }
 solana-streamer = { workspace = true }
 solana-streamer = { workspace = true }
+solana-svm-callback = { workspace = true }
 solana-transaction-context = { workspace = true }
 solana-transaction-context = { workspace = true }
 solana-transaction-status = { workspace = true }
 solana-transaction-status = { workspace = true }
 solana-type-overrides = { workspace = true }
 solana-type-overrides = { workspace = true }

+ 1 - 0
program-runtime/Cargo.toml

@@ -44,6 +44,7 @@ solana-sbpf = { workspace = true }
 solana-sdk-ids = { workspace = true }
 solana-sdk-ids = { workspace = true }
 solana-slot-hashes = { workspace = true }
 solana-slot-hashes = { workspace = true }
 solana-stable-layout = { workspace = true }
 solana-stable-layout = { workspace = true }
+solana-svm-callback = { workspace = true }
 solana-system-interface = { workspace = true }
 solana-system-interface = { workspace = true }
 solana-sysvar = { workspace = true }
 solana-sysvar = { workspace = true }
 solana-sysvar-id = { workspace = true }
 solana-sysvar-id = { workspace = true }

+ 18 - 14
program-runtime/src/invoke_context.rs

@@ -31,6 +31,7 @@ use {
         bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader, sysvar,
         bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader, sysvar,
     },
     },
     solana_stable_layout::stable_instruction::StableInstruction,
     solana_stable_layout::stable_instruction::StableInstruction,
+    solana_svm_callback::EpochStakeCallback,
     solana_timings::{ExecuteDetailsTimings, ExecuteTimings},
     solana_timings::{ExecuteDetailsTimings, ExecuteTimings},
     solana_transaction_context::{
     solana_transaction_context::{
         IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
         IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
@@ -146,8 +147,7 @@ impl BpfAllocator {
 pub struct EnvironmentConfig<'a> {
 pub struct EnvironmentConfig<'a> {
     pub blockhash: Hash,
     pub blockhash: Hash,
     pub blockhash_lamports_per_signature: u64,
     pub blockhash_lamports_per_signature: u64,
-    epoch_total_stake: u64,
-    get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
+    epoch_stake_callback: &'a dyn EpochStakeCallback,
     pub feature_set: Arc<FeatureSet>,
     pub feature_set: Arc<FeatureSet>,
     sysvar_cache: &'a SysvarCache,
     sysvar_cache: &'a SysvarCache,
 }
 }
@@ -155,16 +155,14 @@ impl<'a> EnvironmentConfig<'a> {
     pub fn new(
     pub fn new(
         blockhash: Hash,
         blockhash: Hash,
         blockhash_lamports_per_signature: u64,
         blockhash_lamports_per_signature: u64,
-        epoch_total_stake: u64,
-        get_epoch_vote_account_stake_callback: &'a dyn Fn(&'a Pubkey) -> u64,
+        epoch_stake_callback: &'a dyn EpochStakeCallback,
         feature_set: Arc<FeatureSet>,
         feature_set: Arc<FeatureSet>,
         sysvar_cache: &'a SysvarCache,
         sysvar_cache: &'a SysvarCache,
     ) -> Self {
     ) -> Self {
         Self {
         Self {
             blockhash,
             blockhash,
             blockhash_lamports_per_signature,
             blockhash_lamports_per_signature,
-            epoch_total_stake,
-            get_epoch_vote_account_stake_callback,
+            epoch_stake_callback,
             feature_set,
             feature_set,
             sysvar_cache,
             sysvar_cache,
         }
         }
@@ -666,15 +664,17 @@ impl<'a> InvokeContext<'a> {
     }
     }
 
 
     /// Get cached epoch total stake.
     /// Get cached epoch total stake.
-    pub fn get_epoch_total_stake(&self) -> u64 {
-        self.environment_config.epoch_total_stake
+    pub fn get_epoch_stake(&self) -> u64 {
+        self.environment_config
+            .epoch_stake_callback
+            .get_epoch_stake()
     }
     }
 
 
     /// Get cached stake for the epoch vote account.
     /// Get cached stake for the epoch vote account.
-    pub fn get_epoch_vote_account_stake(&self, pubkey: &'a Pubkey) -> u64 {
-        (self
-            .environment_config
-            .get_epoch_vote_account_stake_callback)(pubkey)
+    pub fn get_epoch_stake_for_vote_account(&self, pubkey: &'a Pubkey) -> u64 {
+        self.environment_config
+            .epoch_stake_callback
+            .get_epoch_stake_for_vote_account(pubkey)
     }
     }
 
 
     // Should alignment be enforced during user pointer translation
     // Should alignment be enforced during user pointer translation
@@ -735,6 +735,7 @@ macro_rules! with_mock_invoke_context {
         use {
         use {
             agave_feature_set::FeatureSet,
             agave_feature_set::FeatureSet,
             solana_log_collector::LogCollector,
             solana_log_collector::LogCollector,
+            solana_svm_callback::EpochStakeCallback,
             solana_type_overrides::sync::Arc,
             solana_type_overrides::sync::Arc,
             $crate::{
             $crate::{
                 __private::{Hash, ReadableAccount, Rent, TransactionContext},
                 __private::{Hash, ReadableAccount, Rent, TransactionContext},
@@ -744,6 +745,10 @@ macro_rules! with_mock_invoke_context {
                 sysvar_cache::SysvarCache,
                 sysvar_cache::SysvarCache,
             },
             },
         };
         };
+
+        struct MockEpochStakeCallback {}
+        impl EpochStakeCallback for MockEpochStakeCallback {}
+
         let compute_budget = SVMTransactionExecutionBudget::default();
         let compute_budget = SVMTransactionExecutionBudget::default();
         let mut $transaction_context = TransactionContext::new(
         let mut $transaction_context = TransactionContext::new(
             $transaction_accounts,
             $transaction_accounts,
@@ -772,8 +777,7 @@ macro_rules! with_mock_invoke_context {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockEpochStakeCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );

+ 1 - 0
programs/bpf_loader/Cargo.toml

@@ -64,6 +64,7 @@ solana-program = { workspace = true }
 solana-pubkey = { workspace = true, features = ["rand"] }
 solana-pubkey = { workspace = true, features = ["rand"] }
 solana-rent = { workspace = true }
 solana-rent = { workspace = true }
 solana-slot-hashes = { workspace = true }
 solana-slot-hashes = { workspace = true }
+solana-svm-callback = { workspace = true }
 solana-transaction-context = { workspace = true, features = ["dev-context-only-utils"] }
 solana-transaction-context = { workspace = true, features = ["dev-context-only-utils"] }
 static_assertions = { workspace = true }
 static_assertions = { workspace = true }
 test-case = { workspace = true }
 test-case = { workspace = true }

+ 32 - 19
programs/bpf_loader/src/syscalls/mod.rs

@@ -2200,7 +2200,7 @@ declare_builtin_function!(
             //     - Compute budget is exceeded.
             //     - Compute budget is exceeded.
             // - Otherwise, the syscall returns a `u64` integer representing the total active
             // - Otherwise, the syscall returns a `u64` integer representing the total active
             //   stake on the cluster for the current epoch.
             //   stake on the cluster for the current epoch.
-            Ok(invoke_context.get_epoch_total_stake())
+            Ok(invoke_context.get_epoch_stake())
         } else {
         } else {
             // As specified by SIMD-0133: If `var_addr` is _not_ a null pointer:
             // As specified by SIMD-0133: If `var_addr` is _not_ a null pointer:
             //
             //
@@ -2232,7 +2232,7 @@ declare_builtin_function!(
             let check_aligned = invoke_context.get_check_aligned();
             let check_aligned = invoke_context.get_check_aligned();
             let vote_address = translate_type::<Pubkey>(memory_mapping, var_addr, check_aligned)?;
             let vote_address = translate_type::<Pubkey>(memory_mapping, var_addr, check_aligned)?;
 
 
-            Ok(invoke_context.get_epoch_vote_account_stake(vote_address))
+            Ok(invoke_context.get_epoch_stake_for_vote_account(vote_address))
         }
         }
     }
     }
 );
 );
@@ -4912,7 +4912,16 @@ mod tests {
         let mut compute_budget = SVMTransactionExecutionBudget::default();
         let mut compute_budget = SVMTransactionExecutionBudget::default();
         let sysvar_cache = Arc::<SysvarCache>::default();
         let sysvar_cache = Arc::<SysvarCache>::default();
 
 
-        let expected_total_stake = 200_000_000_000_000u64;
+        const EXPECTED_TOTAL_STAKE: u64 = 200_000_000_000_000;
+
+        struct MockCallback {}
+        impl EpochStakeCallback for MockCallback {
+            fn get_epoch_stake(&self) -> u64 {
+                EXPECTED_TOTAL_STAKE
+            }
+            // Vote accounts are not needed for this test.
+        }
+
         // Compute units, as specified by SIMD-0133.
         // Compute units, as specified by SIMD-0133.
         // cu = syscall_base_cost
         // cu = syscall_base_cost
         let expected_cus = compute_cost.syscall_base_cost;
         let expected_cus = compute_cost.syscall_base_cost;
@@ -4925,8 +4934,7 @@ mod tests {
         invoke_context.environment_config = EnvironmentConfig::new(
         invoke_context.environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            expected_total_stake,
-            &|_| 0, // Vote accounts are not needed for this test.
+            &MockCallback {},
             Arc::<FeatureSet>::default(),
             Arc::<FeatureSet>::default(),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -4946,7 +4954,7 @@ mod tests {
         )
         )
         .unwrap();
         .unwrap();
 
 
-        assert_eq!(result, expected_total_stake);
+        assert_eq!(result, EXPECTED_TOTAL_STAKE);
     }
     }
 
 
     #[test]
     #[test]
@@ -4956,7 +4964,21 @@ mod tests {
         let compute_cost = SVMTransactionExecutionCost::default();
         let compute_cost = SVMTransactionExecutionCost::default();
         let sysvar_cache = Arc::<SysvarCache>::default();
         let sysvar_cache = Arc::<SysvarCache>::default();
 
 
-        let expected_epoch_stake = 55_000_000_000u64;
+        const TARGET_VOTE_ADDRESS: Pubkey = Pubkey::new_from_array([2; 32]);
+        const EXPECTED_EPOCH_STAKE: u64 = 55_000_000_000;
+
+        struct MockCallback {}
+        impl EpochStakeCallback for MockCallback {
+            // Total stake is not needed for this test.
+            fn get_epoch_stake_for_vote_account(&self, vote_address: &Pubkey) -> u64 {
+                if *vote_address == TARGET_VOTE_ADDRESS {
+                    EXPECTED_EPOCH_STAKE
+                } else {
+                    0
+                }
+            }
+        }
+
         // Compute units, as specified by SIMD-0133.
         // Compute units, as specified by SIMD-0133.
         // cu = syscall_base_cost
         // cu = syscall_base_cost
         //     + floor(32/cpi_bytes_per_unit)
         //     + floor(32/cpi_bytes_per_unit)
@@ -4969,20 +4991,11 @@ mod tests {
         // doesn't exceed the expected usage.
         // doesn't exceed the expected usage.
         compute_budget.compute_unit_limit = expected_cus;
         compute_budget.compute_unit_limit = expected_cus;
 
 
-        let vote_address = Pubkey::new_unique();
         with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
         with_mock_invoke_context!(invoke_context, transaction_context, vec![]);
-        let callback = |pubkey: &Pubkey| {
-            if *pubkey == vote_address {
-                expected_epoch_stake
-            } else {
-                0
-            }
-        };
         invoke_context.environment_config = EnvironmentConfig::new(
         invoke_context.environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0, // Total stake is not needed for this test.
-            &callback,
+            &MockCallback {},
             Arc::<FeatureSet>::default(),
             Arc::<FeatureSet>::default(),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -5024,7 +5037,7 @@ mod tests {
 
 
             let mut memory_mapping = MemoryMapping::new(
             let mut memory_mapping = MemoryMapping::new(
                 vec![MemoryRegion::new_readonly(
                 vec![MemoryRegion::new_readonly(
-                    bytes_of(&vote_address),
+                    bytes_of(&TARGET_VOTE_ADDRESS),
                     vote_address_var,
                     vote_address_var,
                 )],
                 )],
                 &config,
                 &config,
@@ -5043,7 +5056,7 @@ mod tests {
             )
             )
             .unwrap();
             .unwrap();
 
 
-            assert_eq!(result, expected_epoch_stake);
+            assert_eq!(result, EXPECTED_EPOCH_STAKE);
         }
         }
 
 
         invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);
         invoke_context.mock_set_remaining(compute_budget.compute_unit_limit);

+ 2 - 0
programs/sbf/Cargo.lock

@@ -7175,6 +7175,7 @@ dependencies = [
  "solana-sdk-ids",
  "solana-sdk-ids",
  "solana-slot-hashes",
  "solana-slot-hashes",
  "solana-stable-layout",
  "solana-stable-layout",
+ "solana-svm-callback",
  "solana-system-interface",
  "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-sysvar-id",
@@ -7709,6 +7710,7 @@ dependencies = [
  "solana-sbpf",
  "solana-sbpf",
  "solana-sdk",
  "solana-sdk",
  "solana-svm",
  "solana-svm",
+ "solana-svm-callback",
  "solana-svm-transaction",
  "solana-svm-transaction",
  "solana-timings",
  "solana-timings",
  "solana-transaction-context 2.3.0",
  "solana-transaction-context 2.3.0",

+ 2 - 0
programs/sbf/Cargo.toml

@@ -66,6 +66,7 @@ solana-sdk = "=2.2.2"
 solana-sbpf = "=0.10.0"
 solana-sbpf = "=0.10.0"
 solana-secp256k1-recover = "=2.2.1"
 solana-secp256k1-recover = "=2.2.1"
 solana-svm = { path = "../../svm", version = "=2.3.0" }
 solana-svm = { path = "../../svm", version = "=2.3.0" }
+solana-svm-callback = { path = "../../svm-callback", version = "=2.3.0" }
 solana-svm-transaction = { path = "../../svm-transaction", version = "=2.3.0" }
 solana-svm-transaction = { path = "../../svm-transaction", version = "=2.3.0" }
 solana-timings = { path = "../../timings", version = "=2.3.0" }
 solana-timings = { path = "../../timings", version = "=2.3.0" }
 solana-transaction-context = { path = "../../transaction-context", version = "=2.3.0" }
 solana-transaction-context = { path = "../../transaction-context", version = "=2.3.0" }
@@ -140,6 +141,7 @@ solana-sbf-rust-realloc-invoke-dep = { workspace = true }
 solana-sbpf = { workspace = true }
 solana-sbpf = { workspace = true }
 solana-sdk = { workspace = true, features = ["dev-context-only-utils"] }
 solana-sdk = { workspace = true, features = ["dev-context-only-utils"] }
 solana-svm = { workspace = true }
 solana-svm = { workspace = true }
+solana-svm-callback = { workspace = true }
 solana-svm-transaction = { workspace = true }
 solana-svm-transaction = { workspace = true }
 solana-timings = { workspace = true }
 solana-timings = { workspace = true }
 solana-transaction-context = { workspace = true }
 solana-transaction-context = { workspace = true }

+ 1 - 0
programs/stake/Cargo.toml

@@ -40,6 +40,7 @@ solana-compute-budget = { workspace = true }
 solana-epoch-rewards = { workspace = true }
 solana-epoch-rewards = { workspace = true }
 solana-epoch-schedule = { workspace = true }
 solana-epoch-schedule = { workspace = true }
 solana-pubkey = { workspace = true, features = ["rand"] }
 solana-pubkey = { workspace = true, features = ["rand"] }
+solana-svm-callback = { workspace = true }
 solana-sysvar-id = { workspace = true }
 solana-sysvar-id = { workspace = true }
 solana-vote-program = { workspace = true, default-features = false }
 solana-vote-program = { workspace = true, default-features = false }
 test-case = { workspace = true }
 test-case = { workspace = true }

+ 1 - 0
programs/system/Cargo.toml

@@ -39,6 +39,7 @@ solana-nonce-account = { workspace = true }
 solana-rent = { workspace = true }
 solana-rent = { workspace = true }
 solana-sdk = { workspace = true }
 solana-sdk = { workspace = true }
 solana-sha256-hasher = { workspace = true }
 solana-sha256-hasher = { workspace = true }
+solana-svm-callback = { workspace = true }
 
 
 [lib]
 [lib]
 crate-type = ["lib"]
 crate-type = ["lib"]

+ 14 - 8
runtime/src/bank.rs

@@ -171,7 +171,7 @@ use {
             TransactionProcessingConfig, TransactionProcessingEnvironment,
             TransactionProcessingConfig, TransactionProcessingEnvironment,
         },
         },
     },
     },
-    solana_svm_callback::{AccountState, TransactionProcessingCallback},
+    solana_svm_callback::{AccountState, EpochStakeCallback, TransactionProcessingCallback},
     solana_svm_transaction::svm_message::SVMMessage,
     solana_svm_transaction::svm_message::SVMMessage,
     solana_timings::{ExecuteTimingType, ExecuteTimings},
     solana_timings::{ExecuteTimingType, ExecuteTimings},
     solana_transaction_context::{TransactionAccount, TransactionReturnData},
     solana_transaction_context::{TransactionAccount, TransactionReturnData},
@@ -6940,6 +6940,19 @@ impl Bank {
     }
     }
 }
 }
 
 
+impl EpochStakeCallback for Bank {
+    fn get_epoch_stake(&self) -> u64 {
+        self.get_current_epoch_total_stake()
+    }
+
+    fn get_epoch_stake_for_vote_account(&self, vote_address: &Pubkey) -> u64 {
+        self.get_current_epoch_vote_accounts()
+            .get(vote_address)
+            .map(|(stake, _)| (*stake))
+            .unwrap_or(0)
+    }
+}
+
 impl TransactionProcessingCallback for Bank {
 impl TransactionProcessingCallback for Bank {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
         self.rc
         self.rc
@@ -7000,13 +7013,6 @@ impl TransactionProcessingCallback for Bank {
             self.inspect_account_for_accounts_lt_hash(address, &account_state, is_writable);
             self.inspect_account_for_accounts_lt_hash(address, &account_state, is_writable);
         }
         }
     }
     }
-
-    fn get_current_epoch_vote_account_stake(&self, vote_address: &Pubkey) -> u64 {
-        self.get_current_epoch_vote_accounts()
-            .get(vote_address)
-            .map(|(stake, _)| (*stake))
-            .unwrap_or(0)
-    }
 }
 }
 
 
 #[cfg(feature = "dev-context-only-utils")]
 #[cfg(feature = "dev-context-only-utils")]

+ 5 - 2
runtime/src/bank/builtins/core_bpf_migration/mod.rs

@@ -20,6 +20,7 @@ use {
         instruction::InstructionError,
         instruction::InstructionError,
         pubkey::Pubkey,
         pubkey::Pubkey,
     },
     },
+    solana_svm_callback::EpochStakeCallback,
     solana_transaction_context::TransactionContext,
     solana_transaction_context::TransactionContext,
     source_buffer::SourceBuffer,
     source_buffer::SourceBuffer,
     std::{cmp::Ordering, sync::atomic::Ordering::Relaxed},
     std::{cmp::Ordering, sync::atomic::Ordering::Relaxed},
@@ -158,14 +159,16 @@ impl Bank {
                 compute_budget.max_instruction_trace_length,
                 compute_budget.max_instruction_trace_length,
             );
             );
 
 
+            struct MockCallback {}
+            impl EpochStakeCallback for MockCallback {}
+
             let mut dummy_invoke_context = InvokeContext::new(
             let mut dummy_invoke_context = InvokeContext::new(
                 &mut dummy_transaction_context,
                 &mut dummy_transaction_context,
                 &mut program_cache_for_tx_batch,
                 &mut program_cache_for_tx_batch,
                 EnvironmentConfig::new(
                 EnvironmentConfig::new(
                     Hash::default(),
                     Hash::default(),
                     0,
                     0,
-                    0,
-                    &|_| 0,
+                    &MockCallback {},
                     self.feature_set.clone(),
                     self.feature_set.clone(),
                     &sysvar_cache,
                     &sysvar_cache,
                 ),
                 ),

+ 20 - 3
svm-callback/src/lib.rs

@@ -1,7 +1,20 @@
 use {solana_account::AccountSharedData, solana_pubkey::Pubkey};
 use {solana_account::AccountSharedData, solana_pubkey::Pubkey};
 
 
+/// Callback for obtaining the cluster's current epoch stake.
+pub trait EpochStakeCallback {
+    /// Returns the total current epoch stake for the network.
+    fn get_epoch_stake(&self) -> u64 {
+        0
+    }
+
+    /// Returns the current epoch stake for the given vote account.
+    fn get_epoch_stake_for_vote_account(&self, _vote_address: &Pubkey) -> u64 {
+        0
+    }
+}
+
 /// Runtime callbacks for transaction processing.
 /// Runtime callbacks for transaction processing.
-pub trait TransactionProcessingCallback {
+pub trait TransactionProcessingCallback: EpochStakeCallback {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize>;
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize>;
 
 
     fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData>;
     fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData>;
@@ -11,8 +24,12 @@ pub trait TransactionProcessingCallback {
     fn inspect_account(&self, _address: &Pubkey, _account_state: AccountState, _is_writable: bool) {
     fn inspect_account(&self, _address: &Pubkey, _account_state: AccountState, _is_writable: bool) {
     }
     }
 
 
-    fn get_current_epoch_vote_account_stake(&self, _vote_address: &Pubkey) -> u64 {
-        0
+    #[deprecated(
+        since = "2.3.0",
+        note = "Use `get_epoch_stake_for_vote_account` on the `EpochStakeCallback` trait instead"
+    )]
+    fn get_current_epoch_vote_account_stake(&self, vote_address: &Pubkey) -> u64 {
+        Self::get_epoch_stake_for_vote_account(self, vote_address)
     }
     }
 }
 }
 
 

+ 1 - 0
svm/examples/Cargo.lock

@@ -6999,6 +6999,7 @@ dependencies = [
  "solana-sdk-ids",
  "solana-sdk-ids",
  "solana-slot-hashes",
  "solana-slot-hashes",
  "solana-stable-layout",
  "solana-stable-layout",
+ "solana-svm-callback",
  "solana-system-interface",
  "solana-system-interface",
  "solana-sysvar",
  "solana-sysvar",
  "solana-sysvar-id",
  "solana-sysvar-id",

+ 3 - 1
svm/examples/json-rpc/server/src/svm_bridge.rs

@@ -31,7 +31,7 @@ use {
         transaction_processing_result::TransactionProcessingResult,
         transaction_processing_result::TransactionProcessingResult,
         transaction_processor::TransactionBatchProcessor,
         transaction_processor::TransactionBatchProcessor,
     },
     },
-    solana_svm_callback::TransactionProcessingCallback,
+    solana_svm_callback::{EpochStakeCallback, TransactionProcessingCallback},
     std::{
     std::{
         collections::HashMap,
         collections::HashMap,
         sync::{Arc, RwLock},
         sync::{Arc, RwLock},
@@ -59,6 +59,8 @@ pub struct MockBankCallback {
     pub account_shared_data: RwLock<HashMap<Pubkey, AccountSharedData>>,
     pub account_shared_data: RwLock<HashMap<Pubkey, AccountSharedData>>,
 }
 }
 
 
+impl EpochStakeCallback for MockBankCallback {}
+
 impl TransactionProcessingCallback for MockBankCallback {
 impl TransactionProcessingCallback for MockBankCallback {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
         if let Some(data) = self.account_shared_data.read().unwrap().get(account) {
         if let Some(data) = self.account_shared_data.read().unwrap().get(account) {

+ 2 - 1
svm/examples/paytube/src/loader.rs

@@ -11,7 +11,7 @@ use {
         account::{AccountSharedData, ReadableAccount},
         account::{AccountSharedData, ReadableAccount},
         pubkey::Pubkey,
         pubkey::Pubkey,
     },
     },
-    solana_svm_callback::TransactionProcessingCallback,
+    solana_svm_callback::{EpochStakeCallback, TransactionProcessingCallback},
     std::{collections::HashMap, sync::RwLock},
     std::{collections::HashMap, sync::RwLock},
 };
 };
 
 
@@ -39,6 +39,7 @@ impl<'a> PayTubeAccountLoader<'a> {
 /// ability to load accounts.
 /// ability to load accounts.
 ///
 ///
 /// In the Agave validator, this implementation is Bank, powered by AccountsDB.
 /// In the Agave validator, this implementation is Bank, powered by AccountsDB.
+impl EpochStakeCallback for PayTubeAccountLoader<'_> {}
 impl TransactionProcessingCallback for PayTubeAccountLoader<'_> {
 impl TransactionProcessingCallback for PayTubeAccountLoader<'_> {
     fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
     fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
         if let Some(account) = self.cache.read().unwrap().get(pubkey) {
         if let Some(account) = self.cache.read().unwrap().get(pubkey) {

+ 3 - 1
svm/src/account_loader.rs

@@ -697,7 +697,7 @@ mod tests {
         },
         },
         solana_signature::Signature,
         solana_signature::Signature,
         solana_signer::Signer,
         solana_signer::Signer,
-        solana_svm_callback::TransactionProcessingCallback,
+        solana_svm_callback::{EpochStakeCallback, TransactionProcessingCallback},
         solana_system_transaction::transfer,
         solana_system_transaction::transfer,
         solana_transaction::{sanitized::SanitizedTransaction, Transaction},
         solana_transaction::{sanitized::SanitizedTransaction, Transaction},
         solana_transaction_context::{TransactionAccount, TransactionContext},
         solana_transaction_context::{TransactionAccount, TransactionContext},
@@ -713,6 +713,8 @@ mod tests {
             RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>,
             RefCell<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>,
     }
     }
 
 
+    impl EpochStakeCallback for TestCallbacks {}
+
     impl TransactionProcessingCallback for TestCallbacks {
     impl TransactionProcessingCallback for TestCallbacks {
         fn account_matches_owners(&self, _account: &Pubkey, _owners: &[Pubkey]) -> Option<usize> {
         fn account_matches_owners(&self, _account: &Pubkey, _owners: &[Pubkey]) -> Option<usize> {
             None
             None

+ 11 - 14
svm/src/message_processor.rs

@@ -142,10 +142,14 @@ mod tests {
         solana_sdk_ids::{ed25519_program, native_loader, secp256k1_program, system_program},
         solana_sdk_ids::{ed25519_program, native_loader, secp256k1_program, system_program},
         solana_secp256k1_program::new_secp256k1_instruction,
         solana_secp256k1_program::new_secp256k1_instruction,
         solana_secp256r1_program::new_secp256r1_instruction,
         solana_secp256r1_program::new_secp256r1_instruction,
+        solana_svm_callback::EpochStakeCallback,
         solana_transaction_context::TransactionContext,
         solana_transaction_context::TransactionContext,
         std::sync::Arc,
         std::sync::Arc,
     };
     };
 
 
+    struct MockCallback {}
+    impl EpochStakeCallback for MockCallback {}
+
     fn create_loadable_account_for_test(name: &str) -> AccountSharedData {
     fn create_loadable_account_for_test(name: &str) -> AccountSharedData {
         let (lamports, rent_epoch) = DUMMY_INHERITABLE_ACCOUNT_FIELDS;
         let (lamports, rent_epoch) = DUMMY_INHERITABLE_ACCOUNT_FIELDS;
         AccountSharedData::from(Account {
         AccountSharedData::from(Account {
@@ -254,8 +258,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -309,8 +312,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -354,8 +356,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -490,8 +491,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -530,8 +530,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -567,8 +566,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );
@@ -671,8 +669,7 @@ mod tests {
         let environment_config = EnvironmentConfig::new(
         let environment_config = EnvironmentConfig::new(
             Hash::default(),
             Hash::default(),
             0,
             0,
-            0,
-            &|_| 0,
+            &MockCallback {},
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
             &sysvar_cache,
             &sysvar_cache,
         );
         );

+ 3 - 0
svm/src/program_loader.rs

@@ -257,6 +257,7 @@ mod tests {
             solana_sbpf::program::BuiltinProgram,
             solana_sbpf::program::BuiltinProgram,
         },
         },
         solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
         solana_sdk_ids::{bpf_loader, bpf_loader_upgradeable},
+        solana_svm_callback::EpochStakeCallback,
         std::{
         std::{
             cell::RefCell,
             cell::RefCell,
             collections::HashMap,
             collections::HashMap,
@@ -279,6 +280,8 @@ mod tests {
         pub(crate) account_shared_data: RefCell<HashMap<Pubkey, AccountSharedData>>,
         pub(crate) account_shared_data: RefCell<HashMap<Pubkey, AccountSharedData>>,
     }
     }
 
 
+    impl EpochStakeCallback for MockBankCallback {}
+
     impl TransactionProcessingCallback for MockBankCallback {
     impl TransactionProcessingCallback for MockBankCallback {
         fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
         fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
             if let Some(data) = self.account_shared_data.borrow().get(account) {
             if let Some(data) = self.account_shared_data.borrow().get(account) {

+ 4 - 5
svm/src/transaction_processor.rs

@@ -844,8 +844,6 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
 
 
         let mut executed_units = 0u64;
         let mut executed_units = 0u64;
         let sysvar_cache = &self.sysvar_cache.read().unwrap();
         let sysvar_cache = &self.sysvar_cache.read().unwrap();
-        let epoch_vote_account_stake_callback =
-            |pubkey| callback.get_current_epoch_vote_account_stake(pubkey);
 
 
         let mut invoke_context = InvokeContext::new(
         let mut invoke_context = InvokeContext::new(
             &mut transaction_context,
             &mut transaction_context,
@@ -853,8 +851,7 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
             EnvironmentConfig::new(
             EnvironmentConfig::new(
                 environment.blockhash,
                 environment.blockhash,
                 environment.blockhash_lamports_per_signature,
                 environment.blockhash_lamports_per_signature,
-                environment.epoch_total_stake,
-                &epoch_vote_account_stake_callback,
+                callback,
                 Arc::clone(&environment.feature_set),
                 Arc::clone(&environment.feature_set),
                 sysvar_cache,
                 sysvar_cache,
             ),
             ),
@@ -1094,7 +1091,7 @@ mod tests {
         solana_rent_debits::RentDebits,
         solana_rent_debits::RentDebits,
         solana_sdk_ids::{bpf_loader, system_program, sysvar},
         solana_sdk_ids::{bpf_loader, system_program, sysvar},
         solana_signature::Signature,
         solana_signature::Signature,
-        solana_svm_callback::AccountState,
+        solana_svm_callback::{AccountState, EpochStakeCallback},
         solana_transaction::{sanitized::SanitizedTransaction, Transaction},
         solana_transaction::{sanitized::SanitizedTransaction, Transaction},
         solana_transaction_context::TransactionContext,
         solana_transaction_context::TransactionContext,
         solana_transaction_error::{TransactionError, TransactionError::DuplicateInstruction},
         solana_transaction_error::{TransactionError, TransactionError::DuplicateInstruction},
@@ -1124,6 +1121,8 @@ mod tests {
             Arc<RwLock<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>>,
             Arc<RwLock<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>>,
     }
     }
 
 
+    impl EpochStakeCallback for MockBankCallback {}
+
     impl TransactionProcessingCallback for MockBankCallback {
     impl TransactionProcessingCallback for MockBankCallback {
         fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
         fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
             if let Some(data) = self.account_shared_data.read().unwrap().get(account) {
             if let Some(data) = self.account_shared_data.read().unwrap().get(account) {

+ 1 - 2
svm/tests/conformance.rs

@@ -351,8 +351,7 @@ fn execute_fixture_as_instr(
     let env_config = EnvironmentConfig::new(
     let env_config = EnvironmentConfig::new(
         blockhash,
         blockhash,
         lamports_per_signature,
         lamports_per_signature,
-        0,
-        &|_| 0,
+        mock_bank,
         mock_bank.feature_set.clone(),
         mock_bank.feature_set.clone(),
         sysvar_cache,
         sysvar_cache,
     );
     );

+ 3 - 1
svm/tests/mock_bank.rs

@@ -30,7 +30,7 @@ use {
         sysvar::SysvarId,
         sysvar::SysvarId,
     },
     },
     solana_svm::transaction_processor::TransactionBatchProcessor,
     solana_svm::transaction_processor::TransactionBatchProcessor,
-    solana_svm_callback::{AccountState, TransactionProcessingCallback},
+    solana_svm_callback::{AccountState, EpochStakeCallback, TransactionProcessingCallback},
     solana_svm_transaction::svm_message::SVMMessage,
     solana_svm_transaction::svm_message::SVMMessage,
     solana_type_overrides::sync::{Arc, RwLock},
     solana_type_overrides::sync::{Arc, RwLock},
     std::{
     std::{
@@ -67,6 +67,8 @@ pub struct MockBankCallback {
         Arc<RwLock<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>>,
         Arc<RwLock<HashMap<Pubkey, Vec<(Option<AccountSharedData>, /* is_writable */ bool)>>>>,
 }
 }
 
 
+impl EpochStakeCallback for MockBankCallback {}
+
 impl TransactionProcessingCallback for MockBankCallback {
 impl TransactionProcessingCallback for MockBankCallback {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
     fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
         if let Some(data) = self.account_shared_data.read().unwrap().get(account) {
         if let Some(data) = self.account_shared_data.read().unwrap().get(account) {