Add more unit tests to SVM (#35383)
This commit is contained in:
parent
7c878973e2
commit
532b806bef
|
@ -106,6 +106,8 @@ pub fn load_accounts<CB: TransactionProcessingCallback>(
|
|||
&loaded_transaction.rent_debits,
|
||||
) {
|
||||
Ok(nonce) => Some(nonce),
|
||||
// This error branch is never reached, because `load_transaction_accounts`
|
||||
// already validates the fee payer account.
|
||||
Err(e) => return (Err(e), None),
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -29,3 +29,34 @@ impl AccountOverrides {
|
|||
self.accounts.get(pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
crate::account_overrides::AccountOverrides,
|
||||
solana_sdk::{account::AccountSharedData, pubkey::Pubkey, sysvar},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_set_account() {
|
||||
let mut accounts = AccountOverrides::default();
|
||||
let data = AccountSharedData::default();
|
||||
let key = Pubkey::new_unique();
|
||||
accounts.set_account(&key, Some(data.clone()));
|
||||
assert_eq!(accounts.get(&key), Some(&data));
|
||||
|
||||
accounts.set_account(&key, None);
|
||||
assert!(accounts.get(&key).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_history() {
|
||||
let mut accounts = AccountOverrides::default();
|
||||
let data = AccountSharedData::default();
|
||||
|
||||
assert_eq!(accounts.get(&sysvar::slot_history::id()), None);
|
||||
accounts.set_slot_history(Some(data.clone()));
|
||||
|
||||
assert_eq!(accounts.get(&sysvar::slot_history::id()), Some(&data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,4 +237,71 @@ mod tests {
|
|||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_rent_state_with_account() {
|
||||
let pre_rent_state = RentState::RentPaying {
|
||||
data_size: 2,
|
||||
lamports: 3,
|
||||
};
|
||||
|
||||
let post_rent_state = RentState::RentPaying {
|
||||
data_size: 2,
|
||||
lamports: 5,
|
||||
};
|
||||
let account_index = 2 as IndexOfAccount;
|
||||
let key = Pubkey::new_unique();
|
||||
let result = RentState::check_rent_state_with_account(
|
||||
&pre_rent_state,
|
||||
&post_rent_state,
|
||||
&key,
|
||||
&AccountSharedData::default(),
|
||||
account_index,
|
||||
);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(TransactionError::InsufficientFundsForRent {
|
||||
account_index: account_index as u8
|
||||
})
|
||||
);
|
||||
|
||||
let result = RentState::check_rent_state_with_account(
|
||||
&pre_rent_state,
|
||||
&post_rent_state,
|
||||
&solana_sdk::incinerator::id(),
|
||||
&AccountSharedData::default(),
|
||||
account_index,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_rent_state() {
|
||||
let context = TransactionContext::new(
|
||||
vec![(Pubkey::new_unique(), AccountSharedData::default())],
|
||||
Rent::default(),
|
||||
20,
|
||||
20,
|
||||
);
|
||||
|
||||
let pre_rent_state = RentState::RentPaying {
|
||||
data_size: 2,
|
||||
lamports: 3,
|
||||
};
|
||||
|
||||
let post_rent_state = RentState::RentPaying {
|
||||
data_size: 2,
|
||||
lamports: 5,
|
||||
};
|
||||
|
||||
let result =
|
||||
RentState::check_rent_state(Some(&pre_rent_state), Some(&post_rent_state), &context, 0);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(TransactionError::InsufficientFundsForRent { account_index: 0 })
|
||||
);
|
||||
|
||||
let result = RentState::check_rent_state(None, Some(&post_rent_state), &context, 0);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct TransactionAccountStateInfo {
|
||||
rent_state: Option<RentState>, // None: readonly account
|
||||
}
|
||||
|
@ -67,3 +68,171 @@ impl TransactionAccountStateInfo {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {
|
||||
crate::{
|
||||
account_rent_state::RentState,
|
||||
transaction_account_state_info::TransactionAccountStateInfo,
|
||||
},
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
hash::Hash,
|
||||
instruction::CompiledInstruction,
|
||||
message::{LegacyMessage, Message, MessageHeader, SanitizedMessage},
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
transaction::TransactionError,
|
||||
transaction_context::TransactionContext,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
let rent = Rent::default();
|
||||
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 transaction_accounts = vec![
|
||||
(key1.pubkey(), AccountSharedData::default()),
|
||||
(key2.pubkey(), AccountSharedData::default()),
|
||||
(key3.pubkey(), AccountSharedData::default()),
|
||||
];
|
||||
|
||||
let context = TransactionContext::new(transaction_accounts, rent.clone(), 20, 20);
|
||||
let result = TransactionAccountStateInfo::new(&rent, &context, &sanitized_message);
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized)
|
||||
},
|
||||
TransactionAccountStateInfo { rent_state: None },
|
||||
TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized)
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "message and transaction context out of sync, fatal")]
|
||||
fn test_new_panic() {
|
||||
let rent = Rent::default();
|
||||
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(), key3.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 transaction_accounts = vec![
|
||||
(key1.pubkey(), AccountSharedData::default()),
|
||||
(key2.pubkey(), AccountSharedData::default()),
|
||||
(key3.pubkey(), AccountSharedData::default()),
|
||||
];
|
||||
|
||||
let context = TransactionContext::new(transaction_accounts, rent.clone(), 20, 20);
|
||||
let _result = TransactionAccountStateInfo::new(&rent, &context, &sanitized_message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_changes() {
|
||||
let key1 = Keypair::new();
|
||||
let key2 = Keypair::new();
|
||||
let pre_rent_state = vec![
|
||||
TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized),
|
||||
},
|
||||
TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized),
|
||||
},
|
||||
];
|
||||
let post_rent_state = vec![TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized),
|
||||
}];
|
||||
|
||||
let transaction_accounts = vec![
|
||||
(key1.pubkey(), AccountSharedData::default()),
|
||||
(key2.pubkey(), AccountSharedData::default()),
|
||||
];
|
||||
|
||||
let context = TransactionContext::new(transaction_accounts, Rent::default(), 20, 20);
|
||||
|
||||
let result = TransactionAccountStateInfo::verify_changes(
|
||||
&pre_rent_state,
|
||||
&post_rent_state,
|
||||
&context,
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let pre_rent_state = vec![TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::Uninitialized),
|
||||
}];
|
||||
let post_rent_state = vec![TransactionAccountStateInfo {
|
||||
rent_state: Some(RentState::RentPaying {
|
||||
data_size: 2,
|
||||
lamports: 5,
|
||||
}),
|
||||
}];
|
||||
|
||||
let transaction_accounts = vec![
|
||||
(key1.pubkey(), AccountSharedData::default()),
|
||||
(key2.pubkey(), AccountSharedData::default()),
|
||||
];
|
||||
|
||||
let context = TransactionContext::new(transaction_accounts, Rent::default(), 20, 20);
|
||||
let result = TransactionAccountStateInfo::verify_changes(
|
||||
&pre_rent_state,
|
||||
&post_rent_state,
|
||||
&context,
|
||||
);
|
||||
assert_eq!(
|
||||
result.err(),
|
||||
Some(TransactionError::InsufficientFundsForRent { account_index: 0 })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
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)]
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue