SVM: Add doc comments, restrict visibility of some xfaces to crate (#136)
This commit is contained in:
parent
bf0a3684eb
commit
d88050cda3
|
@ -7499,7 +7499,7 @@ impl Bank {
|
||||||
effective_epoch: Epoch,
|
effective_epoch: Epoch,
|
||||||
) -> Arc<LoadedProgram> {
|
) -> Arc<LoadedProgram> {
|
||||||
self.transaction_processor
|
self.transaction_processor
|
||||||
.load_program(self, pubkey, reload, effective_epoch)
|
.load_program_with_pubkey(self, pubkey, reload, effective_epoch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,11 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
// for the load instructions
|
// for the load instructions
|
||||||
pub type TransactionRent = u64;
|
pub(crate) type TransactionRent = u64;
|
||||||
pub type TransactionProgramIndices = Vec<Vec<IndexOfAccount>>;
|
pub(crate) type TransactionProgramIndices = Vec<Vec<IndexOfAccount>>;
|
||||||
|
pub type TransactionCheckResult = (transaction::Result<()>, Option<NoncePartial>, Option<u64>);
|
||||||
|
pub type TransactionLoadResult = (Result<LoadedTransaction>, Option<NonceFull>);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct LoadedTransaction {
|
pub struct LoadedTransaction {
|
||||||
pub accounts: Vec<TransactionAccount>,
|
pub accounts: Vec<TransactionAccount>,
|
||||||
|
@ -48,10 +51,66 @@ pub struct LoadedTransaction {
|
||||||
pub rent_debits: RentDebits,
|
pub rent_debits: RentDebits,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type TransactionLoadResult = (Result<LoadedTransaction>, Option<NonceFull>);
|
/// Check whether the payer_account is capable of paying the fee. The
|
||||||
pub type TransactionCheckResult = (transaction::Result<()>, Option<NoncePartial>, Option<u64>);
|
/// side effect is to subtract the fee amount from the payer_account
|
||||||
|
/// balance of lamports. If the payer_acount is not able to pay the
|
||||||
|
/// fee, the error_counters is incremented, and a specific error is
|
||||||
|
/// returned.
|
||||||
|
pub fn validate_fee_payer(
|
||||||
|
payer_address: &Pubkey,
|
||||||
|
payer_account: &mut AccountSharedData,
|
||||||
|
payer_index: IndexOfAccount,
|
||||||
|
error_counters: &mut TransactionErrorMetrics,
|
||||||
|
rent_collector: &RentCollector,
|
||||||
|
fee: u64,
|
||||||
|
) -> Result<()> {
|
||||||
|
if payer_account.lamports() == 0 {
|
||||||
|
error_counters.account_not_found += 1;
|
||||||
|
return Err(TransactionError::AccountNotFound);
|
||||||
|
}
|
||||||
|
let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
|
||||||
|
error_counters.invalid_account_for_fee += 1;
|
||||||
|
TransactionError::InvalidAccountForFee
|
||||||
|
})?;
|
||||||
|
let min_balance = match system_account_kind {
|
||||||
|
SystemAccountKind::System => 0,
|
||||||
|
SystemAccountKind::Nonce => {
|
||||||
|
// Should we ever allow a fees charge to zero a nonce account's
|
||||||
|
// balance. The state MUST be set to uninitialized in that case
|
||||||
|
rent_collector.rent.minimum_balance(NonceState::size())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub fn load_accounts<CB: TransactionProcessingCallback>(
|
payer_account
|
||||||
|
.lamports()
|
||||||
|
.checked_sub(min_balance)
|
||||||
|
.and_then(|v| v.checked_sub(fee))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error_counters.insufficient_funds += 1;
|
||||||
|
TransactionError::InsufficientFundsForFee
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let payer_pre_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
||||||
|
payer_account
|
||||||
|
.checked_sub_lamports(fee)
|
||||||
|
.map_err(|_| TransactionError::InsufficientFundsForFee)?;
|
||||||
|
|
||||||
|
let payer_post_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
||||||
|
RentState::check_rent_state_with_account(
|
||||||
|
&payer_pre_rent_state,
|
||||||
|
&payer_post_rent_state,
|
||||||
|
payer_address,
|
||||||
|
payer_account,
|
||||||
|
payer_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect information about accounts used in txs transactions and
|
||||||
|
/// return vector of tuples, one for each transaction in the
|
||||||
|
/// batch. Each tuple contains struct of information about accounts as
|
||||||
|
/// its first element and an optional transaction nonce info as its
|
||||||
|
/// second element.
|
||||||
|
pub(crate) fn load_accounts<CB: TransactionProcessingCallback>(
|
||||||
callbacks: &CB,
|
callbacks: &CB,
|
||||||
txs: &[SanitizedTransaction],
|
txs: &[SanitizedTransaction],
|
||||||
lock_results: &[TransactionCheckResult],
|
lock_results: &[TransactionCheckResult],
|
||||||
|
@ -399,55 +458,6 @@ fn accumulate_and_check_loaded_account_data_size(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_fee_payer(
|
|
||||||
payer_address: &Pubkey,
|
|
||||||
payer_account: &mut AccountSharedData,
|
|
||||||
payer_index: IndexOfAccount,
|
|
||||||
error_counters: &mut TransactionErrorMetrics,
|
|
||||||
rent_collector: &RentCollector,
|
|
||||||
fee: u64,
|
|
||||||
) -> Result<()> {
|
|
||||||
if payer_account.lamports() == 0 {
|
|
||||||
error_counters.account_not_found += 1;
|
|
||||||
return Err(TransactionError::AccountNotFound);
|
|
||||||
}
|
|
||||||
let system_account_kind = get_system_account_kind(payer_account).ok_or_else(|| {
|
|
||||||
error_counters.invalid_account_for_fee += 1;
|
|
||||||
TransactionError::InvalidAccountForFee
|
|
||||||
})?;
|
|
||||||
let min_balance = match system_account_kind {
|
|
||||||
SystemAccountKind::System => 0,
|
|
||||||
SystemAccountKind::Nonce => {
|
|
||||||
// Should we ever allow a fees charge to zero a nonce account's
|
|
||||||
// balance. The state MUST be set to uninitialized in that case
|
|
||||||
rent_collector.rent.minimum_balance(NonceState::size())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
payer_account
|
|
||||||
.lamports()
|
|
||||||
.checked_sub(min_balance)
|
|
||||||
.and_then(|v| v.checked_sub(fee))
|
|
||||||
.ok_or_else(|| {
|
|
||||||
error_counters.insufficient_funds += 1;
|
|
||||||
TransactionError::InsufficientFundsForFee
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let payer_pre_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
|
||||||
payer_account
|
|
||||||
.checked_sub_lamports(fee)
|
|
||||||
.map_err(|_| TransactionError::InsufficientFundsForFee)?;
|
|
||||||
|
|
||||||
let payer_post_rent_state = RentState::from_account(payer_account, &rent_collector.rent);
|
|
||||||
RentState::check_rent_state_with_account(
|
|
||||||
&payer_pre_rent_state,
|
|
||||||
&payer_post_rent_state,
|
|
||||||
payer_address,
|
|
||||||
payer_account,
|
|
||||||
payer_index,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn construct_instructions_account(message: &SanitizedMessage) -> AccountSharedData {
|
fn construct_instructions_account(message: &SanitizedMessage) -> AccountSharedData {
|
||||||
AccountSharedData::from(Account {
|
AccountSharedData::from(Account {
|
||||||
data: construct_instructions_data(&message.decompile_instructions()),
|
data: construct_instructions_data(&message.decompile_instructions()),
|
||||||
|
@ -460,11 +470,15 @@ fn construct_instructions_account(message: &SanitizedMessage) -> AccountSharedDa
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
crate::transaction_processor::TransactionProcessingCallback,
|
crate::{
|
||||||
|
transaction_account_state_info::TransactionAccountStateInfo,
|
||||||
|
transaction_processor::TransactionProcessingCallback,
|
||||||
|
},
|
||||||
nonce::state::Versions as NonceVersions,
|
nonce::state::Versions as NonceVersions,
|
||||||
solana_program_runtime::{
|
solana_program_runtime::{
|
||||||
|
compute_budget::ComputeBudget,
|
||||||
compute_budget_processor,
|
compute_budget_processor,
|
||||||
loaded_programs::LoadedProgram,
|
loaded_programs::{LoadedProgram, LoadedProgramsForTxBatch},
|
||||||
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
|
||||||
},
|
},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
|
@ -473,22 +487,27 @@ mod tests {
|
||||||
compute_budget::ComputeBudgetInstruction,
|
compute_budget::ComputeBudgetInstruction,
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
feature_set::FeatureSet,
|
feature_set::FeatureSet,
|
||||||
|
fee::FeeStructure,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
message::{
|
message::{
|
||||||
v0::{LoadedAddresses, LoadedMessage},
|
v0::{LoadedAddresses, LoadedMessage},
|
||||||
LegacyMessage, Message, MessageHeader, SanitizedMessage,
|
LegacyMessage, Message, MessageHeader, SanitizedMessage,
|
||||||
},
|
},
|
||||||
|
native_loader,
|
||||||
|
native_token::sol_to_lamports,
|
||||||
nonce,
|
nonce,
|
||||||
|
nonce_info::{NonceFull, NoncePartial},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
rent_collector::RentCollector,
|
rent_collector::{RentCollector, RENT_EXEMPT_RENT_EPOCH},
|
||||||
|
rent_debits::RentDebits,
|
||||||
signature::{Keypair, Signature, Signer},
|
signature::{Keypair, Signature, Signer},
|
||||||
system_program, sysvar,
|
system_program, system_transaction, sysvar,
|
||||||
transaction::{Result, Transaction, TransactionError},
|
transaction::{Result, SanitizedTransaction, Transaction, TransactionError},
|
||||||
transaction_context::TransactionAccount,
|
transaction_context::{TransactionAccount, TransactionContext},
|
||||||
},
|
},
|
||||||
std::{borrow::Cow, convert::TryFrom, sync::Arc},
|
std::{borrow::Cow, collections::HashMap, convert::TryFrom, sync::Arc},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -2017,4 +2036,248 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rent_state_list_len() {
|
||||||
|
let mint_keypair = Keypair::new();
|
||||||
|
let mut bank = TestCallbacks::default();
|
||||||
|
let recipient = Pubkey::new_unique();
|
||||||
|
let last_block_hash = Hash::new_unique();
|
||||||
|
|
||||||
|
let mut system_data = AccountSharedData::default();
|
||||||
|
system_data.set_executable(true);
|
||||||
|
system_data.set_owner(native_loader::id());
|
||||||
|
bank.accounts_map
|
||||||
|
.insert(Pubkey::new_from_array([0u8; 32]), system_data);
|
||||||
|
|
||||||
|
let mut mint_data = AccountSharedData::default();
|
||||||
|
mint_data.set_lamports(2);
|
||||||
|
bank.accounts_map.insert(mint_keypair.pubkey(), mint_data);
|
||||||
|
|
||||||
|
bank.accounts_map
|
||||||
|
.insert(recipient, AccountSharedData::default());
|
||||||
|
|
||||||
|
let tx = system_transaction::transfer(
|
||||||
|
&mint_keypair,
|
||||||
|
&recipient,
|
||||||
|
sol_to_lamports(1.),
|
||||||
|
last_block_hash,
|
||||||
|
);
|
||||||
|
let num_accounts = tx.message().account_keys.len();
|
||||||
|
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
||||||
|
let mut error_counters = TransactionErrorMetrics::default();
|
||||||
|
let loaded_txs = load_accounts(
|
||||||
|
&bank,
|
||||||
|
&[sanitized_tx.clone()],
|
||||||
|
&[(Ok(()), None, Some(0))],
|
||||||
|
&mut error_counters,
|
||||||
|
&FeeStructure::default(),
|
||||||
|
None,
|
||||||
|
&HashMap::new(),
|
||||||
|
&LoadedProgramsForTxBatch::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let compute_budget = ComputeBudget::new(u64::from(
|
||||||
|
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||||
|
));
|
||||||
|
let transaction_context = TransactionContext::new(
|
||||||
|
loaded_txs[0].0.as_ref().unwrap().accounts.clone(),
|
||||||
|
Rent::default(),
|
||||||
|
compute_budget.max_invoke_stack_height,
|
||||||
|
compute_budget.max_instruction_trace_length,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
TransactionAccountStateInfo::new(
|
||||||
|
&Rent::default(),
|
||||||
|
&transaction_context,
|
||||||
|
sanitized_tx.message()
|
||||||
|
)
|
||||||
|
.len(),
|
||||||
|
num_accounts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_accounts_success() {
|
||||||
|
let key1 = Keypair::new();
|
||||||
|
let key2 = Keypair::new();
|
||||||
|
let key3 = Keypair::new();
|
||||||
|
let key4 = Keypair::new();
|
||||||
|
|
||||||
|
let message = Message {
|
||||||
|
account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
|
||||||
|
header: MessageHeader::default(),
|
||||||
|
instructions: vec![
|
||||||
|
CompiledInstruction {
|
||||||
|
program_id_index: 1,
|
||||||
|
accounts: vec![0],
|
||||||
|
data: vec![],
|
||||||
|
},
|
||||||
|
CompiledInstruction {
|
||||||
|
program_id_index: 1,
|
||||||
|
accounts: vec![2],
|
||||||
|
data: vec![],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
recent_blockhash: Hash::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let legacy = LegacyMessage::new(message);
|
||||||
|
let sanitized_message = SanitizedMessage::Legacy(legacy);
|
||||||
|
let mut mock_bank = TestCallbacks::default();
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_executable(true);
|
||||||
|
account_data.set_owner(key3.pubkey());
|
||||||
|
mock_bank.accounts_map.insert(key1.pubkey(), account_data);
|
||||||
|
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_lamports(200);
|
||||||
|
mock_bank.accounts_map.insert(key2.pubkey(), account_data);
|
||||||
|
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_executable(true);
|
||||||
|
account_data.set_owner(native_loader::id());
|
||||||
|
mock_bank.accounts_map.insert(key3.pubkey(), account_data);
|
||||||
|
|
||||||
|
let mut error_counter = TransactionErrorMetrics::default();
|
||||||
|
let loaded_programs = LoadedProgramsForTxBatch::default();
|
||||||
|
|
||||||
|
let sanitized_transaction = SanitizedTransaction::new_for_tests(
|
||||||
|
sanitized_message,
|
||||||
|
vec![Signature::new_unique()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let lock_results =
|
||||||
|
(Ok(()), Some(NoncePartial::default()), Some(20u64)) as TransactionCheckResult;
|
||||||
|
|
||||||
|
let results = load_accounts(
|
||||||
|
&mock_bank,
|
||||||
|
&[sanitized_transaction],
|
||||||
|
&[lock_results],
|
||||||
|
&mut error_counter,
|
||||||
|
&FeeStructure::default(),
|
||||||
|
None,
|
||||||
|
&HashMap::new(),
|
||||||
|
&loaded_programs,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut account_data = AccountSharedData::default();
|
||||||
|
account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
|
||||||
|
|
||||||
|
assert_eq!(results.len(), 1);
|
||||||
|
let (loaded_result, nonce) = results[0].clone();
|
||||||
|
assert_eq!(
|
||||||
|
loaded_result.unwrap(),
|
||||||
|
LoadedTransaction {
|
||||||
|
accounts: vec![
|
||||||
|
(
|
||||||
|
key2.pubkey(),
|
||||||
|
mock_bank.accounts_map[&key2.pubkey()].clone()
|
||||||
|
),
|
||||||
|
(
|
||||||
|
key1.pubkey(),
|
||||||
|
mock_bank.accounts_map[&key1.pubkey()].clone()
|
||||||
|
),
|
||||||
|
(key4.pubkey(), account_data),
|
||||||
|
(
|
||||||
|
key3.pubkey(),
|
||||||
|
mock_bank.accounts_map[&key3.pubkey()].clone()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
program_indices: vec![vec![3, 1], vec![3, 1]],
|
||||||
|
rent: 0,
|
||||||
|
rent_debits: RentDebits::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
nonce.unwrap(),
|
||||||
|
NonceFull::new(
|
||||||
|
Pubkey::from([0; 32]),
|
||||||
|
AccountSharedData::default(),
|
||||||
|
Some(mock_bank.accounts_map[&key2.pubkey()].clone())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_accounts_error() {
|
||||||
|
let mock_bank = TestCallbacks::default();
|
||||||
|
let message = Message {
|
||||||
|
account_keys: vec![Pubkey::new_from_array([0; 32])],
|
||||||
|
header: MessageHeader::default(),
|
||||||
|
instructions: vec![CompiledInstruction {
|
||||||
|
program_id_index: 0,
|
||||||
|
accounts: vec![],
|
||||||
|
data: vec![],
|
||||||
|
}],
|
||||||
|
recent_blockhash: Hash::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let legacy = LegacyMessage::new(message);
|
||||||
|
let sanitized_message = SanitizedMessage::Legacy(legacy);
|
||||||
|
let sanitized_transaction = SanitizedTransaction::new_for_tests(
|
||||||
|
sanitized_message,
|
||||||
|
vec![Signature::new_unique()],
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let lock_results = (Ok(()), Some(NoncePartial::default()), None) as TransactionCheckResult;
|
||||||
|
let fee_structure = FeeStructure::default();
|
||||||
|
|
||||||
|
let result = load_accounts(
|
||||||
|
&mock_bank,
|
||||||
|
&[sanitized_transaction.clone()],
|
||||||
|
&[lock_results],
|
||||||
|
&mut TransactionErrorMetrics::default(),
|
||||||
|
&fee_structure,
|
||||||
|
None,
|
||||||
|
&HashMap::new(),
|
||||||
|
&LoadedProgramsForTxBatch::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![(Err(TransactionError::BlockhashNotFound), None)]
|
||||||
|
);
|
||||||
|
|
||||||
|
let lock_results =
|
||||||
|
(Ok(()), Some(NoncePartial::default()), Some(20u64)) as TransactionCheckResult;
|
||||||
|
|
||||||
|
let result = load_accounts(
|
||||||
|
&mock_bank,
|
||||||
|
&[sanitized_transaction.clone()],
|
||||||
|
&[lock_results.clone()],
|
||||||
|
&mut TransactionErrorMetrics::default(),
|
||||||
|
&fee_structure,
|
||||||
|
None,
|
||||||
|
&HashMap::new(),
|
||||||
|
&LoadedProgramsForTxBatch::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result, vec![(Err(TransactionError::AccountNotFound), None)]);
|
||||||
|
|
||||||
|
let lock_results = (
|
||||||
|
Err(TransactionError::InvalidWritableAccount),
|
||||||
|
Some(NoncePartial::default()),
|
||||||
|
Some(20u64),
|
||||||
|
) as TransactionCheckResult;
|
||||||
|
|
||||||
|
let result = load_accounts(
|
||||||
|
&mock_bank,
|
||||||
|
&[sanitized_transaction.clone()],
|
||||||
|
&[lock_results],
|
||||||
|
&mut TransactionErrorMetrics::default(),
|
||||||
|
&fee_structure,
|
||||||
|
None,
|
||||||
|
&HashMap::new(),
|
||||||
|
&LoadedProgramsForTxBatch::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![(Err(TransactionError::InvalidWritableAccount), None)]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub struct AccountOverrides {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountOverrides {
|
impl AccountOverrides {
|
||||||
|
/// Insert or remove an account with a given pubkey to/from the list of overrides.
|
||||||
pub fn set_account(&mut self, pubkey: &Pubkey, account: Option<AccountSharedData>) {
|
pub fn set_account(&mut self, pubkey: &Pubkey, account: Option<AccountSharedData>) {
|
||||||
match account {
|
match account {
|
||||||
Some(account) => self.accounts.insert(*pubkey, account),
|
Some(account) => self.accounts.insert(*pubkey, account),
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub enum RentState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RentState {
|
impl RentState {
|
||||||
|
/// Return a new RentState instance for a given account and rent.
|
||||||
pub fn from_account(account: &AccountSharedData, rent: &Rent) -> Self {
|
pub fn from_account(account: &AccountSharedData, rent: &Rent) -> Self {
|
||||||
if account.lamports() == 0 {
|
if account.lamports() == 0 {
|
||||||
Self::Uninitialized
|
Self::Uninitialized
|
||||||
|
@ -36,6 +37,8 @@ impl RentState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether a transition from the pre_rent_state to this
|
||||||
|
/// state is valid.
|
||||||
pub fn transition_allowed_from(&self, pre_rent_state: &RentState) -> bool {
|
pub fn transition_allowed_from(&self, pre_rent_state: &RentState) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Uninitialized | Self::RentExempt => true,
|
Self::Uninitialized | Self::RentExempt => true,
|
||||||
|
@ -57,21 +60,6 @@ impl RentState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_rent_state_metrics(pre_rent_state: &Self, post_rent_state: &Self) {
|
|
||||||
match (pre_rent_state, post_rent_state) {
|
|
||||||
(&RentState::Uninitialized, &RentState::RentPaying { .. }) => {
|
|
||||||
inc_new_counter_info!("rent_paying_err-new_account", 1);
|
|
||||||
}
|
|
||||||
(&RentState::RentPaying { .. }, &RentState::RentPaying { .. }) => {
|
|
||||||
inc_new_counter_info!("rent_paying_ok-legacy", 1);
|
|
||||||
}
|
|
||||||
(_, &RentState::RentPaying { .. }) => {
|
|
||||||
inc_new_counter_info!("rent_paying_err-other", 1);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn check_rent_state(
|
pub(crate) fn check_rent_state(
|
||||||
pre_rent_state: Option<&Self>,
|
pre_rent_state: Option<&Self>,
|
||||||
post_rent_state: Option<&Self>,
|
post_rent_state: Option<&Self>,
|
||||||
|
@ -118,6 +106,21 @@ impl RentState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn submit_rent_state_metrics(pre_rent_state: &Self, post_rent_state: &Self) {
|
||||||
|
match (pre_rent_state, post_rent_state) {
|
||||||
|
(&RentState::Uninitialized, &RentState::RentPaying { .. }) => {
|
||||||
|
inc_new_counter_info!("rent_paying_err-new_account", 1);
|
||||||
|
}
|
||||||
|
(&RentState::RentPaying { .. }, &RentState::RentPaying { .. }) => {
|
||||||
|
inc_new_counter_info!("rent_paying_ok-legacy", 1);
|
||||||
|
}
|
||||||
|
(_, &RentState::RentPaying { .. }) => {
|
||||||
|
inc_new_counter_info!("rent_paying_err-other", 1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -11,12 +11,12 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct TransactionAccountStateInfo {
|
pub(crate) struct TransactionAccountStateInfo {
|
||||||
rent_state: Option<RentState>, // None: readonly account
|
rent_state: Option<RentState>, // None: readonly account
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionAccountStateInfo {
|
impl TransactionAccountStateInfo {
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
rent: &Rent,
|
rent: &Rent,
|
||||||
transaction_context: &TransactionContext,
|
transaction_context: &TransactionContext,
|
||||||
message: &SanitizedMessage,
|
message: &SanitizedMessage,
|
||||||
|
|
|
@ -195,6 +195,7 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Main entrypoint to the SVM.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn load_and_execute_sanitized_transactions<'a, CB: TransactionProcessingCallback>(
|
pub fn load_and_execute_sanitized_transactions<'a, CB: TransactionProcessingCallback>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -377,6 +378,112 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load program with a specific pubkey from loaded programs
|
||||||
|
/// cache, and update the program's access slot as a side-effect.
|
||||||
|
pub fn load_program_with_pubkey<CB: TransactionProcessingCallback>(
|
||||||
|
&self,
|
||||||
|
callbacks: &CB,
|
||||||
|
pubkey: &Pubkey,
|
||||||
|
reload: bool,
|
||||||
|
effective_epoch: Epoch,
|
||||||
|
) -> Arc<LoadedProgram> {
|
||||||
|
let loaded_programs_cache = self.loaded_programs_cache.read().unwrap();
|
||||||
|
let environments = loaded_programs_cache.get_environments_for_epoch(effective_epoch);
|
||||||
|
let mut load_program_metrics = LoadProgramMetrics {
|
||||||
|
program_id: pubkey.to_string(),
|
||||||
|
..LoadProgramMetrics::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut loaded_program =
|
||||||
|
match self.load_program_accounts(callbacks, pubkey, environments) {
|
||||||
|
ProgramAccountLoadResult::AccountNotFound => Ok(LoadedProgram::new_tombstone(
|
||||||
|
self.slot,
|
||||||
|
LoadedProgramType::Closed,
|
||||||
|
)),
|
||||||
|
|
||||||
|
ProgramAccountLoadResult::InvalidAccountData(env) => Err((self.slot, env)),
|
||||||
|
|
||||||
|
ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account) => {
|
||||||
|
Self::load_program_from_bytes(
|
||||||
|
&mut load_program_metrics,
|
||||||
|
program_account.data(),
|
||||||
|
program_account.owner(),
|
||||||
|
program_account.data().len(),
|
||||||
|
0,
|
||||||
|
environments.program_runtime_v1.clone(),
|
||||||
|
reload,
|
||||||
|
)
|
||||||
|
.map_err(|_| (0, environments.program_runtime_v1.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramAccountLoadResult::ProgramOfLoaderV3(
|
||||||
|
program_account,
|
||||||
|
programdata_account,
|
||||||
|
slot,
|
||||||
|
) => programdata_account
|
||||||
|
.data()
|
||||||
|
.get(UpgradeableLoaderState::size_of_programdata_metadata()..)
|
||||||
|
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||||
|
.and_then(|programdata| {
|
||||||
|
Self::load_program_from_bytes(
|
||||||
|
&mut load_program_metrics,
|
||||||
|
programdata,
|
||||||
|
program_account.owner(),
|
||||||
|
program_account
|
||||||
|
.data()
|
||||||
|
.len()
|
||||||
|
.saturating_add(programdata_account.data().len()),
|
||||||
|
slot,
|
||||||
|
environments.program_runtime_v1.clone(),
|
||||||
|
reload,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(|_| (slot, environments.program_runtime_v1.clone())),
|
||||||
|
|
||||||
|
ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => {
|
||||||
|
program_account
|
||||||
|
.data()
|
||||||
|
.get(LoaderV4State::program_data_offset()..)
|
||||||
|
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
||||||
|
.and_then(|elf_bytes| {
|
||||||
|
Self::load_program_from_bytes(
|
||||||
|
&mut load_program_metrics,
|
||||||
|
elf_bytes,
|
||||||
|
&loader_v4::id(),
|
||||||
|
program_account.data().len(),
|
||||||
|
slot,
|
||||||
|
environments.program_runtime_v2.clone(),
|
||||||
|
reload,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map_err(|_| (slot, environments.program_runtime_v2.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unwrap_or_else(|(slot, env)| {
|
||||||
|
LoadedProgram::new_tombstone(slot, LoadedProgramType::FailedVerification(env))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut timings = ExecuteDetailsTimings::default();
|
||||||
|
load_program_metrics.submit_datapoint(&mut timings);
|
||||||
|
if !Arc::ptr_eq(
|
||||||
|
&environments.program_runtime_v1,
|
||||||
|
&loaded_programs_cache.environments.program_runtime_v1,
|
||||||
|
) || !Arc::ptr_eq(
|
||||||
|
&environments.program_runtime_v2,
|
||||||
|
&loaded_programs_cache.environments.program_runtime_v2,
|
||||||
|
) {
|
||||||
|
// There can be two entries per program when the environment changes.
|
||||||
|
// One for the old environment before the epoch boundary and one for the new environment after the epoch boundary.
|
||||||
|
// These two entries have the same deployment slot, so they must differ in their effective slot instead.
|
||||||
|
// This is done by setting the effective slot of the entry for the new environment to the epoch boundary.
|
||||||
|
loaded_program.effective_slot = loaded_program
|
||||||
|
.effective_slot
|
||||||
|
.max(self.epoch_schedule.get_first_slot_in_epoch(effective_epoch));
|
||||||
|
}
|
||||||
|
loaded_program.update_access_slot(self.slot);
|
||||||
|
Arc::new(loaded_program)
|
||||||
|
}
|
||||||
|
|
||||||
fn replenish_program_cache<CB: TransactionProcessingCallback>(
|
fn replenish_program_cache<CB: TransactionProcessingCallback>(
|
||||||
&self,
|
&self,
|
||||||
callback: &CB,
|
callback: &CB,
|
||||||
|
@ -454,7 +561,7 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
||||||
|
|
||||||
if let Some((key, count)) = program_to_load {
|
if let Some((key, count)) = program_to_load {
|
||||||
// Load, verify and compile one program.
|
// Load, verify and compile one program.
|
||||||
let program = self.load_program(callback, &key, false, self.epoch);
|
let program = self.load_program_with_pubkey(callback, &key, false, self.epoch);
|
||||||
program.tx_usage_counter.store(count, Ordering::Relaxed);
|
program.tx_usage_counter.store(count, Ordering::Relaxed);
|
||||||
program_to_store = Some((key, program));
|
program_to_store = Some((key, program));
|
||||||
} else if missing_programs.is_empty() {
|
} else if missing_programs.is_empty() {
|
||||||
|
@ -683,110 +790,6 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_program<CB: TransactionProcessingCallback>(
|
|
||||||
&self,
|
|
||||||
callbacks: &CB,
|
|
||||||
pubkey: &Pubkey,
|
|
||||||
reload: bool,
|
|
||||||
effective_epoch: Epoch,
|
|
||||||
) -> Arc<LoadedProgram> {
|
|
||||||
let loaded_programs_cache = self.loaded_programs_cache.read().unwrap();
|
|
||||||
let environments = loaded_programs_cache.get_environments_for_epoch(effective_epoch);
|
|
||||||
let mut load_program_metrics = LoadProgramMetrics {
|
|
||||||
program_id: pubkey.to_string(),
|
|
||||||
..LoadProgramMetrics::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut loaded_program =
|
|
||||||
match self.load_program_accounts(callbacks, pubkey, environments) {
|
|
||||||
ProgramAccountLoadResult::AccountNotFound => Ok(LoadedProgram::new_tombstone(
|
|
||||||
self.slot,
|
|
||||||
LoadedProgramType::Closed,
|
|
||||||
)),
|
|
||||||
|
|
||||||
ProgramAccountLoadResult::InvalidAccountData(env) => Err((self.slot, env)),
|
|
||||||
|
|
||||||
ProgramAccountLoadResult::ProgramOfLoaderV1orV2(program_account) => {
|
|
||||||
Self::load_program_from_bytes(
|
|
||||||
&mut load_program_metrics,
|
|
||||||
program_account.data(),
|
|
||||||
program_account.owner(),
|
|
||||||
program_account.data().len(),
|
|
||||||
0,
|
|
||||||
environments.program_runtime_v1.clone(),
|
|
||||||
reload,
|
|
||||||
)
|
|
||||||
.map_err(|_| (0, environments.program_runtime_v1.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
ProgramAccountLoadResult::ProgramOfLoaderV3(
|
|
||||||
program_account,
|
|
||||||
programdata_account,
|
|
||||||
slot,
|
|
||||||
) => programdata_account
|
|
||||||
.data()
|
|
||||||
.get(UpgradeableLoaderState::size_of_programdata_metadata()..)
|
|
||||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
|
||||||
.and_then(|programdata| {
|
|
||||||
Self::load_program_from_bytes(
|
|
||||||
&mut load_program_metrics,
|
|
||||||
programdata,
|
|
||||||
program_account.owner(),
|
|
||||||
program_account
|
|
||||||
.data()
|
|
||||||
.len()
|
|
||||||
.saturating_add(programdata_account.data().len()),
|
|
||||||
slot,
|
|
||||||
environments.program_runtime_v1.clone(),
|
|
||||||
reload,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map_err(|_| (slot, environments.program_runtime_v1.clone())),
|
|
||||||
|
|
||||||
ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot) => {
|
|
||||||
program_account
|
|
||||||
.data()
|
|
||||||
.get(LoaderV4State::program_data_offset()..)
|
|
||||||
.ok_or(Box::new(InstructionError::InvalidAccountData).into())
|
|
||||||
.and_then(|elf_bytes| {
|
|
||||||
Self::load_program_from_bytes(
|
|
||||||
&mut load_program_metrics,
|
|
||||||
elf_bytes,
|
|
||||||
&loader_v4::id(),
|
|
||||||
program_account.data().len(),
|
|
||||||
slot,
|
|
||||||
environments.program_runtime_v2.clone(),
|
|
||||||
reload,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.map_err(|_| (slot, environments.program_runtime_v2.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.unwrap_or_else(|(slot, env)| {
|
|
||||||
LoadedProgram::new_tombstone(slot, LoadedProgramType::FailedVerification(env))
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut timings = ExecuteDetailsTimings::default();
|
|
||||||
load_program_metrics.submit_datapoint(&mut timings);
|
|
||||||
if !Arc::ptr_eq(
|
|
||||||
&environments.program_runtime_v1,
|
|
||||||
&loaded_programs_cache.environments.program_runtime_v1,
|
|
||||||
) || !Arc::ptr_eq(
|
|
||||||
&environments.program_runtime_v2,
|
|
||||||
&loaded_programs_cache.environments.program_runtime_v2,
|
|
||||||
) {
|
|
||||||
// There can be two entries per program when the environment changes.
|
|
||||||
// One for the old environment before the epoch boundary and one for the new environment after the epoch boundary.
|
|
||||||
// These two entries have the same deployment slot, so they must differ in their effective slot instead.
|
|
||||||
// This is done by setting the effective slot of the entry for the new environment to the epoch boundary.
|
|
||||||
loaded_program.effective_slot = loaded_program
|
|
||||||
.effective_slot
|
|
||||||
.max(self.epoch_schedule.get_first_slot_in_epoch(effective_epoch));
|
|
||||||
}
|
|
||||||
loaded_program.update_access_slot(self.slot);
|
|
||||||
Arc::new(loaded_program)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_program_from_bytes(
|
fn load_program_from_bytes(
|
||||||
load_program_metrics: &mut LoadProgramMetrics,
|
load_program_metrics: &mut LoadProgramMetrics,
|
||||||
programdata: &[u8],
|
programdata: &[u8],
|
||||||
|
@ -1242,7 +1245,7 @@ mod tests {
|
||||||
let key = Pubkey::new_unique();
|
let key = Pubkey::new_unique();
|
||||||
let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
|
let batch_processor = TransactionBatchProcessor::<TestForkGraph>::default();
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 50);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 50);
|
||||||
|
|
||||||
let loaded_program = LoadedProgram::new_tombstone(0, LoadedProgramType::Closed);
|
let loaded_program = LoadedProgram::new_tombstone(0, LoadedProgramType::Closed);
|
||||||
assert_eq!(result, Arc::new(loaded_program));
|
assert_eq!(result, Arc::new(loaded_program));
|
||||||
|
@ -1259,7 +1262,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20);
|
||||||
|
|
||||||
let loaded_program = LoadedProgram::new_tombstone(
|
let loaded_program = LoadedProgram::new_tombstone(
|
||||||
0,
|
0,
|
||||||
|
@ -1288,7 +1291,7 @@ mod tests {
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
// This should return an error
|
// This should return an error
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20);
|
||||||
let loaded_program = LoadedProgram::new_tombstone(
|
let loaded_program = LoadedProgram::new_tombstone(
|
||||||
0,
|
0,
|
||||||
LoadedProgramType::FailedVerification(
|
LoadedProgramType::FailedVerification(
|
||||||
|
@ -1316,7 +1319,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20);
|
||||||
|
|
||||||
let environments = ProgramRuntimeEnvironments::default();
|
let environments = ProgramRuntimeEnvironments::default();
|
||||||
let expected = TransactionBatchProcessor::<TestForkGraph>::load_program_from_bytes(
|
let expected = TransactionBatchProcessor::<TestForkGraph>::load_program_from_bytes(
|
||||||
|
@ -1361,7 +1364,7 @@ mod tests {
|
||||||
.insert(key2, account_data2.clone());
|
.insert(key2, account_data2.clone());
|
||||||
|
|
||||||
// This should return an error
|
// This should return an error
|
||||||
let result = batch_processor.load_program(&mock_bank, &key1, false, 0);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key1, false, 0);
|
||||||
let loaded_program = LoadedProgram::new_tombstone(
|
let loaded_program = LoadedProgram::new_tombstone(
|
||||||
0,
|
0,
|
||||||
LoadedProgramType::FailedVerification(
|
LoadedProgramType::FailedVerification(
|
||||||
|
@ -1399,7 +1402,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key2, account_data.clone());
|
.insert(key2, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key1, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key1, false, 20);
|
||||||
|
|
||||||
let data = account_data.data();
|
let data = account_data.data();
|
||||||
account_data
|
account_data
|
||||||
|
@ -1441,7 +1444,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 0);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 0);
|
||||||
let loaded_program = LoadedProgram::new_tombstone(
|
let loaded_program = LoadedProgram::new_tombstone(
|
||||||
0,
|
0,
|
||||||
LoadedProgramType::FailedVerification(
|
LoadedProgramType::FailedVerification(
|
||||||
|
@ -1475,7 +1478,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20);
|
||||||
|
|
||||||
let data = account_data.data()[LoaderV4State::program_data_offset()..].to_vec();
|
let data = account_data.data()[LoaderV4State::program_data_offset()..].to_vec();
|
||||||
account_data.set_data(data);
|
account_data.set_data(data);
|
||||||
|
@ -1513,7 +1516,7 @@ mod tests {
|
||||||
.account_shared_data
|
.account_shared_data
|
||||||
.insert(key, account_data.clone());
|
.insert(key, account_data.clone());
|
||||||
|
|
||||||
let result = batch_processor.load_program(&mock_bank, &key, false, 20);
|
let result = batch_processor.load_program_with_pubkey(&mock_bank, &key, false, 20);
|
||||||
|
|
||||||
let slot = batch_processor.epoch_schedule.get_first_slot_in_epoch(20);
|
let slot = batch_processor.epoch_schedule.get_first_slot_in_epoch(20);
|
||||||
assert_eq!(result.effective_slot, slot);
|
assert_eq!(result.effective_slot, slot);
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
use {
|
|
||||||
crate::mock_bank::MockBankCallback,
|
|
||||||
solana_program_runtime::loaded_programs::LoadedProgramsForTxBatch,
|
|
||||||
solana_sdk::{
|
|
||||||
account::{AccountSharedData, WritableAccount},
|
|
||||||
fee::FeeStructure,
|
|
||||||
hash::Hash,
|
|
||||||
instruction::CompiledInstruction,
|
|
||||||
message::{LegacyMessage, Message, MessageHeader, SanitizedMessage},
|
|
||||||
native_loader,
|
|
||||||
nonce_info::{NonceFull, NoncePartial},
|
|
||||||
pubkey::Pubkey,
|
|
||||||
rent_collector::RENT_EXEMPT_RENT_EPOCH,
|
|
||||||
rent_debits::RentDebits,
|
|
||||||
signature::{Keypair, Signature, Signer},
|
|
||||||
transaction::{SanitizedTransaction, TransactionError},
|
|
||||||
},
|
|
||||||
solana_svm::{
|
|
||||||
account_loader::{load_accounts, LoadedTransaction, TransactionCheckResult},
|
|
||||||
transaction_error_metrics::TransactionErrorMetrics,
|
|
||||||
},
|
|
||||||
std::collections::HashMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod mock_bank;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_load_accounts_success() {
|
|
||||||
let key1 = Keypair::new();
|
|
||||||
let key2 = Keypair::new();
|
|
||||||
let key3 = Keypair::new();
|
|
||||||
let key4 = Keypair::new();
|
|
||||||
|
|
||||||
let message = Message {
|
|
||||||
account_keys: vec![key2.pubkey(), key1.pubkey(), key4.pubkey()],
|
|
||||||
header: MessageHeader::default(),
|
|
||||||
instructions: vec![
|
|
||||||
CompiledInstruction {
|
|
||||||
program_id_index: 1,
|
|
||||||
accounts: vec![0],
|
|
||||||
data: vec![],
|
|
||||||
},
|
|
||||||
CompiledInstruction {
|
|
||||||
program_id_index: 1,
|
|
||||||
accounts: vec![2],
|
|
||||||
data: vec![],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
recent_blockhash: Hash::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let legacy = LegacyMessage::new(message);
|
|
||||||
let sanitized_message = SanitizedMessage::Legacy(legacy);
|
|
||||||
let mut mock_bank = MockBankCallback::default();
|
|
||||||
let mut account_data = AccountSharedData::default();
|
|
||||||
account_data.set_executable(true);
|
|
||||||
account_data.set_owner(key3.pubkey());
|
|
||||||
mock_bank
|
|
||||||
.account_shared_data
|
|
||||||
.insert(key1.pubkey(), account_data);
|
|
||||||
|
|
||||||
let mut account_data = AccountSharedData::default();
|
|
||||||
account_data.set_lamports(200);
|
|
||||||
mock_bank
|
|
||||||
.account_shared_data
|
|
||||||
.insert(key2.pubkey(), account_data);
|
|
||||||
|
|
||||||
let mut account_data = AccountSharedData::default();
|
|
||||||
account_data.set_executable(true);
|
|
||||||
account_data.set_owner(native_loader::id());
|
|
||||||
mock_bank
|
|
||||||
.account_shared_data
|
|
||||||
.insert(key3.pubkey(), account_data);
|
|
||||||
|
|
||||||
let mut error_counter = TransactionErrorMetrics::default();
|
|
||||||
let loaded_programs = LoadedProgramsForTxBatch::default();
|
|
||||||
|
|
||||||
let sanitized_transaction = SanitizedTransaction::new_for_tests(
|
|
||||||
sanitized_message,
|
|
||||||
vec![Signature::new_unique()],
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
let lock_results =
|
|
||||||
(Ok(()), Some(NoncePartial::default()), Some(20u64)) as TransactionCheckResult;
|
|
||||||
|
|
||||||
let results = load_accounts(
|
|
||||||
&mock_bank,
|
|
||||||
&[sanitized_transaction],
|
|
||||||
&[lock_results],
|
|
||||||
&mut error_counter,
|
|
||||||
&FeeStructure::default(),
|
|
||||||
None,
|
|
||||||
&HashMap::new(),
|
|
||||||
&loaded_programs,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut account_data = AccountSharedData::default();
|
|
||||||
account_data.set_rent_epoch(RENT_EXEMPT_RENT_EPOCH);
|
|
||||||
|
|
||||||
assert_eq!(results.len(), 1);
|
|
||||||
let (loaded_result, nonce) = results[0].clone();
|
|
||||||
assert_eq!(
|
|
||||||
loaded_result.unwrap(),
|
|
||||||
LoadedTransaction {
|
|
||||||
accounts: vec![
|
|
||||||
(
|
|
||||||
key2.pubkey(),
|
|
||||||
mock_bank.account_shared_data[&key2.pubkey()].clone()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
key1.pubkey(),
|
|
||||||
mock_bank.account_shared_data[&key1.pubkey()].clone()
|
|
||||||
),
|
|
||||||
(key4.pubkey(), account_data),
|
|
||||||
(
|
|
||||||
key3.pubkey(),
|
|
||||||
mock_bank.account_shared_data[&key3.pubkey()].clone()
|
|
||||||
),
|
|
||||||
],
|
|
||||||
program_indices: vec![vec![3, 1], vec![3, 1]],
|
|
||||||
rent: 0,
|
|
||||||
rent_debits: RentDebits::default()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
nonce.unwrap(),
|
|
||||||
NonceFull::new(
|
|
||||||
Pubkey::from([0; 32]),
|
|
||||||
AccountSharedData::default(),
|
|
||||||
Some(mock_bank.account_shared_data[&key2.pubkey()].clone())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_load_accounts_error() {
|
|
||||||
let mock_bank = MockBankCallback::default();
|
|
||||||
let message = Message {
|
|
||||||
account_keys: vec![Pubkey::new_from_array([0; 32])],
|
|
||||||
header: MessageHeader::default(),
|
|
||||||
instructions: vec![CompiledInstruction {
|
|
||||||
program_id_index: 0,
|
|
||||||
accounts: vec![],
|
|
||||||
data: vec![],
|
|
||||||
}],
|
|
||||||
recent_blockhash: Hash::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let legacy = LegacyMessage::new(message);
|
|
||||||
let sanitized_message = SanitizedMessage::Legacy(legacy);
|
|
||||||
let sanitized_transaction = SanitizedTransaction::new_for_tests(
|
|
||||||
sanitized_message,
|
|
||||||
vec![Signature::new_unique()],
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let lock_results = (Ok(()), Some(NoncePartial::default()), None) as TransactionCheckResult;
|
|
||||||
let fee_structure = FeeStructure::default();
|
|
||||||
|
|
||||||
let result = load_accounts(
|
|
||||||
&mock_bank,
|
|
||||||
&[sanitized_transaction.clone()],
|
|
||||||
&[lock_results],
|
|
||||||
&mut TransactionErrorMetrics::default(),
|
|
||||||
&fee_structure,
|
|
||||||
None,
|
|
||||||
&HashMap::new(),
|
|
||||||
&LoadedProgramsForTxBatch::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
result,
|
|
||||||
vec![(Err(TransactionError::BlockhashNotFound), None)]
|
|
||||||
);
|
|
||||||
|
|
||||||
let lock_results =
|
|
||||||
(Ok(()), Some(NoncePartial::default()), Some(20u64)) as TransactionCheckResult;
|
|
||||||
|
|
||||||
let result = load_accounts(
|
|
||||||
&mock_bank,
|
|
||||||
&[sanitized_transaction.clone()],
|
|
||||||
&[lock_results.clone()],
|
|
||||||
&mut TransactionErrorMetrics::default(),
|
|
||||||
&fee_structure,
|
|
||||||
None,
|
|
||||||
&HashMap::new(),
|
|
||||||
&LoadedProgramsForTxBatch::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(result, vec![(Err(TransactionError::AccountNotFound), None)]);
|
|
||||||
|
|
||||||
let lock_results = (
|
|
||||||
Err(TransactionError::InvalidWritableAccount),
|
|
||||||
Some(NoncePartial::default()),
|
|
||||||
Some(20u64),
|
|
||||||
) as TransactionCheckResult;
|
|
||||||
|
|
||||||
let result = load_accounts(
|
|
||||||
&mock_bank,
|
|
||||||
&[sanitized_transaction.clone()],
|
|
||||||
&[lock_results],
|
|
||||||
&mut TransactionErrorMetrics::default(),
|
|
||||||
&fee_structure,
|
|
||||||
None,
|
|
||||||
&HashMap::new(),
|
|
||||||
&LoadedProgramsForTxBatch::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
result,
|
|
||||||
vec![(Err(TransactionError::InvalidWritableAccount), None)]
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use {
|
|
||||||
solana_program_runtime::{
|
|
||||||
compute_budget::ComputeBudget, compute_budget_processor,
|
|
||||||
loaded_programs::LoadedProgramsForTxBatch,
|
|
||||||
},
|
|
||||||
solana_sdk::{
|
|
||||||
account::{AccountSharedData, WritableAccount},
|
|
||||||
fee::FeeStructure,
|
|
||||||
hash::Hash,
|
|
||||||
native_loader,
|
|
||||||
native_token::sol_to_lamports,
|
|
||||||
pubkey::Pubkey,
|
|
||||||
rent::Rent,
|
|
||||||
signature::{Keypair, Signer},
|
|
||||||
system_transaction,
|
|
||||||
transaction::SanitizedTransaction,
|
|
||||||
transaction_context::TransactionContext,
|
|
||||||
},
|
|
||||||
solana_svm::{
|
|
||||||
account_loader::load_accounts, transaction_account_state_info::TransactionAccountStateInfo,
|
|
||||||
transaction_error_metrics::TransactionErrorMetrics,
|
|
||||||
},
|
|
||||||
std::collections::HashMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod mock_bank;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rent_state_list_len() {
|
|
||||||
let mint_keypair = Keypair::new();
|
|
||||||
let mut bank = mock_bank::MockBankCallback::default();
|
|
||||||
let recipient = Pubkey::new_unique();
|
|
||||||
let last_block_hash = Hash::new_unique();
|
|
||||||
|
|
||||||
let mut system_data = AccountSharedData::default();
|
|
||||||
system_data.set_executable(true);
|
|
||||||
system_data.set_owner(native_loader::id());
|
|
||||||
bank.account_shared_data
|
|
||||||
.insert(Pubkey::new_from_array([0u8; 32]), system_data);
|
|
||||||
|
|
||||||
let mut mint_data = AccountSharedData::default();
|
|
||||||
mint_data.set_lamports(2);
|
|
||||||
bank.account_shared_data
|
|
||||||
.insert(mint_keypair.pubkey(), mint_data);
|
|
||||||
|
|
||||||
bank.account_shared_data
|
|
||||||
.insert(recipient, AccountSharedData::default());
|
|
||||||
|
|
||||||
let tx = system_transaction::transfer(
|
|
||||||
&mint_keypair,
|
|
||||||
&recipient,
|
|
||||||
sol_to_lamports(1.),
|
|
||||||
last_block_hash,
|
|
||||||
);
|
|
||||||
let num_accounts = tx.message().account_keys.len();
|
|
||||||
let sanitized_tx = SanitizedTransaction::from_transaction_for_tests(tx);
|
|
||||||
let mut error_counters = TransactionErrorMetrics::default();
|
|
||||||
let loaded_txs = load_accounts(
|
|
||||||
&bank,
|
|
||||||
&[sanitized_tx.clone()],
|
|
||||||
&[(Ok(()), None, Some(0))],
|
|
||||||
&mut error_counters,
|
|
||||||
&FeeStructure::default(),
|
|
||||||
None,
|
|
||||||
&HashMap::new(),
|
|
||||||
&LoadedProgramsForTxBatch::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let compute_budget = ComputeBudget::new(u64::from(
|
|
||||||
compute_budget_processor::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
|
||||||
));
|
|
||||||
let transaction_context = TransactionContext::new(
|
|
||||||
loaded_txs[0].0.as_ref().unwrap().accounts.clone(),
|
|
||||||
Rent::default(),
|
|
||||||
compute_budget.max_invoke_stack_height,
|
|
||||||
compute_budget.max_instruction_trace_length,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
TransactionAccountStateInfo::new(
|
|
||||||
&Rent::default(),
|
|
||||||
&transaction_context,
|
|
||||||
sanitized_tx.message()
|
|
||||||
)
|
|
||||||
.len(),
|
|
||||||
num_accounts,
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue