Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Add more unit tests to SVM (#35383)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasSte authored Mar 1, 2024
1 parent 7c87897 commit 532b806
Show file tree
Hide file tree
Showing 5 changed files with 483 additions and 0 deletions.
2 changes: 2 additions & 0 deletions svm/src/account_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 31 additions & 0 deletions svm/src/account_overrides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
67 changes: 67 additions & 0 deletions svm/src/account_rent_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
169 changes: 169 additions & 0 deletions svm/src/transaction_account_state_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use {
},
};

#[derive(PartialEq, Debug)]
pub struct TransactionAccountStateInfo {
rent_state: Option<RentState>, // None: readonly account
}
Expand Down Expand Up @@ -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 })
);
}
}
Loading

0 comments on commit 532b806

Please sign in to comment.