#[cfg(feature = "conformance")]
use solana_account::ReadableAccount;
use {
crate::conformance::instr::context::InstrContext,
solana_account::Account,
solana_compute_budget::compute_budget::ComputeBudget,
solana_hash::Hash,
solana_instruction::Instruction,
solana_message::SanitizedMessage,
solana_program_runtime::{
execution_budget::{SVMTransactionExecutionBudget, SVMTransactionExecutionCost},
invoke_context::{EnvironmentConfig, mock_compile_message},
loaded_programs::{ProgramRuntimeEnvironment, ProgramRuntimeEnvironments},
sysvar_cache::SysvarCache,
},
solana_pubkey::Pubkey,
solana_rent::Rent,
solana_svm_callback::InvokeContextCallback,
solana_svm_feature_set::SVMFeatureSet,
solana_svm_log_collector::LogCollector,
solana_svm_transaction::svm_message::SVMStaticMessage,
solana_syscalls::create_program_runtime_environment,
solana_transaction_context::transaction::TransactionContext,
std::{cell::RefCell, rc::Rc},
};
pub(crate) struct InvokeContextFields<'a, 'ix_data> {
pub(crate) sanitized_message: SanitizedMessage,
pub(crate) transaction_context: TransactionContext<'ix_data>,
pub(crate) environment_config: EnvironmentConfig<'a>,
pub(crate) log_collector: Rc<RefCell<LogCollector>>,
pub(crate) execution_budget: SVMTransactionExecutionBudget,
pub(crate) execution_cost: SVMTransactionExecutionCost,
}
pub(crate) fn prepare_invoke_context_fields<'a, C: InvokeContextCallback>(
instr_context: &'a InstrContext,
callback: &'a C,
loader_key: &Pubkey,
sysvar_cache: &'a SysvarCache,
compute_budget: &ComputeBudget,
program_runtime_environments: &'a ProgramRuntimeEnvironments,
) -> InvokeContextFields<'a, 'a> {
let rent = sysvar_cache.get_rent().unwrap();
let (sanitized_message, transaction_context) = compile_transaction_context(
&instr_context.instruction,
&instr_context.accounts,
&instr_context.instruction.program_id,
loader_key,
compute_budget,
(*rent).clone(),
);
let (blockhash, blockhash_lamports_per_signature) = recent_blockhash(sysvar_cache);
let environment_config = EnvironmentConfig::new(
blockhash,
blockhash_lamports_per_signature,
false,
callback,
&instr_context.feature_set,
program_runtime_environments,
sysvar_cache,
);
let log_collector = LogCollector::new_ref();
let execution_budget = compute_budget.to_budget();
let execution_cost = compute_budget.to_cost();
InvokeContextFields {
sanitized_message,
transaction_context,
environment_config,
log_collector,
execution_budget,
execution_cost,
}
}
pub(crate) fn compute_budget(feature_set: &SVMFeatureSet) -> ComputeBudget {
let simd_0268_active = feature_set.raise_cpi_nesting_limit_to_8;
ComputeBudget::new_with_defaults(simd_0268_active)
}
#[cfg(feature = "conformance")]
pub(crate) fn program_loader_key(accounts: &[(Pubkey, Account)], program_id: &Pubkey) -> Pubkey {
accounts
.iter()
.find(|(key, _)| key == program_id)
.map(|(_, account)| account.owner)
.expect("program not found in accounts")
}
pub(crate) fn compile_transaction_context(
instruction: &Instruction,
accounts: &[(Pubkey, Account)],
program_id: &Pubkey,
loader_key: &Pubkey,
compute_budget: &ComputeBudget,
rent: Rent,
) -> (SanitizedMessage, TransactionContext<'static>) {
let (sanitized_message, transaction_accounts) =
mock_compile_message(instruction, accounts, program_id, loader_key);
let transaction_context = TransactionContext::new(
transaction_accounts,
rent,
compute_budget.max_instruction_stack_depth,
compute_budget.max_instruction_trace_length,
sanitized_message.num_instructions(),
);
(sanitized_message, transaction_context)
}
pub(crate) fn program_runtime_environments(
feature_set: &SVMFeatureSet,
compute_budget: &ComputeBudget,
) -> ProgramRuntimeEnvironments {
let environment = create_program_runtime_environment(
feature_set,
&compute_budget.to_budget(),
false,
false,
)
.unwrap();
ProgramRuntimeEnvironments::new(ProgramRuntimeEnvironment::clone(&environment), environment)
}
pub(crate) fn recent_blockhash(sysvar_cache: &SysvarCache) -> (Hash, u64) {
#[expect(deprecated)]
sysvar_cache
.get_recent_blockhashes()
.ok()
.and_then(|entries| entries.last().cloned())
.map(|entry| (entry.blockhash, entry.fee_calculator.lamports_per_signature))
.unwrap_or_default()
}
#[cfg(feature = "conformance")]
pub(crate) fn sysvar_cache_from_accounts(accounts: &[(Pubkey, Account)]) -> SysvarCache {
let mut cache = SysvarCache::default();
cache.fill_missing_entries(|pubkey, set_sysvar| {
if let Some(account) = accounts.iter().find(|(key, _)| key == pubkey)
&& account.1.lamports() > 0
{
set_sysvar(account.1.data());
}
});
cache
}