From ec63b813f58e0564920a5ab81e81eb0a048a5b07 Mon Sep 17 00:00:00 2001 From: Lucas Steuernagel <38472950+LucasSte@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:44:26 -0300 Subject: [PATCH] Move account filter test to SVM (#35304) --- runtime/src/bank/tests.rs | 183 ------------------------- svm/tests/mock_bank.rs | 48 +++++++ svm/tests/mod.rs | 1 - svm/tests/rent_state.rs | 36 +---- svm/tests/transaction_processor.rs | 206 +++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 217 deletions(-) create mode 100644 svm/tests/mock_bank.rs delete mode 100644 svm/tests/mod.rs create mode 100644 svm/tests/transaction_processor.rs diff --git a/runtime/src/bank/tests.rs b/runtime/src/bank/tests.rs index 005233531..02bb7f5c0 100644 --- a/runtime/src/bank/tests.rs +++ b/runtime/src/bank/tests.rs @@ -13585,189 +13585,6 @@ fn test_last_restart_slot() { assert_eq!(get_last_restart_slot(&bank7), Some(6)); } -#[test] -fn test_filter_executable_program_accounts() { - let keypair1 = Keypair::new(); - let keypair2 = Keypair::new(); - - let non_program_pubkey1 = Pubkey::new_unique(); - let non_program_pubkey2 = Pubkey::new_unique(); - let program1_pubkey = Pubkey::new_unique(); - let program2_pubkey = Pubkey::new_unique(); - let account1_pubkey = Pubkey::new_unique(); - let account2_pubkey = Pubkey::new_unique(); - let account3_pubkey = Pubkey::new_unique(); - let account4_pubkey = Pubkey::new_unique(); - - let account5_pubkey = Pubkey::new_unique(); - - let (genesis_config, _mint_keypair) = create_genesis_config(10); - let bank = Bank::new_for_tests(&genesis_config); - bank.store_account( - &non_program_pubkey1, - &AccountSharedData::new(1, 10, &account5_pubkey), - ); - bank.store_account( - &non_program_pubkey2, - &AccountSharedData::new(1, 10, &account5_pubkey), - ); - bank.store_account( - &program1_pubkey, - &AccountSharedData::new(40, 1, &account5_pubkey), - ); - bank.store_account( - &program2_pubkey, - &AccountSharedData::new(40, 1, &account5_pubkey), - ); - bank.store_account( - &account1_pubkey, - &AccountSharedData::new(1, 10, &non_program_pubkey1), - ); - bank.store_account( - &account2_pubkey, - &AccountSharedData::new(1, 10, &non_program_pubkey2), - ); - bank.store_account( - &account3_pubkey, - &AccountSharedData::new(40, 1, &program1_pubkey), - ); - bank.store_account( - &account4_pubkey, - &AccountSharedData::new(40, 1, &program2_pubkey), - ); - - let tx1 = Transaction::new_with_compiled_instructions( - &[&keypair1], - &[non_program_pubkey1], - Hash::new_unique(), - vec![account1_pubkey, account2_pubkey, account3_pubkey], - vec![CompiledInstruction::new(1, &(), vec![0])], - ); - let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1); - - let tx2 = Transaction::new_with_compiled_instructions( - &[&keypair2], - &[non_program_pubkey2], - Hash::new_unique(), - vec![account4_pubkey, account3_pubkey, account2_pubkey], - vec![CompiledInstruction::new(1, &(), vec![0])], - ); - let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2); - - let owners = &[program1_pubkey, program2_pubkey]; - let programs = TransactionBatchProcessor::::filter_executable_program_accounts( - &bank, - &[sanitized_tx1, sanitized_tx2], - &mut [(Ok(()), None, Some(0)), (Ok(()), None, Some(0))], - owners, - ); - - // The result should contain only account3_pubkey, and account4_pubkey as the program accounts - assert_eq!(programs.len(), 2); - assert_eq!( - programs - .get(&account3_pubkey) - .expect("failed to find the program account"), - &(&program1_pubkey, 2) - ); - assert_eq!( - programs - .get(&account4_pubkey) - .expect("failed to find the program account"), - &(&program2_pubkey, 1) - ); -} - -#[test] -fn test_filter_executable_program_accounts_invalid_blockhash() { - let keypair1 = Keypair::new(); - let keypair2 = Keypair::new(); - - let non_program_pubkey1 = Pubkey::new_unique(); - let non_program_pubkey2 = Pubkey::new_unique(); - let program1_pubkey = Pubkey::new_unique(); - let program2_pubkey = Pubkey::new_unique(); - let account1_pubkey = Pubkey::new_unique(); - let account2_pubkey = Pubkey::new_unique(); - let account3_pubkey = Pubkey::new_unique(); - let account4_pubkey = Pubkey::new_unique(); - - let account5_pubkey = Pubkey::new_unique(); - - let (genesis_config, _mint_keypair) = create_genesis_config(10); - let bank = Bank::new_for_tests(&genesis_config); - bank.store_account( - &non_program_pubkey1, - &AccountSharedData::new(1, 10, &account5_pubkey), - ); - bank.store_account( - &non_program_pubkey2, - &AccountSharedData::new(1, 10, &account5_pubkey), - ); - bank.store_account( - &program1_pubkey, - &AccountSharedData::new(40, 1, &account5_pubkey), - ); - bank.store_account( - &program2_pubkey, - &AccountSharedData::new(40, 1, &account5_pubkey), - ); - bank.store_account( - &account1_pubkey, - &AccountSharedData::new(1, 10, &non_program_pubkey1), - ); - bank.store_account( - &account2_pubkey, - &AccountSharedData::new(1, 10, &non_program_pubkey2), - ); - bank.store_account( - &account3_pubkey, - &AccountSharedData::new(40, 1, &program1_pubkey), - ); - bank.store_account( - &account4_pubkey, - &AccountSharedData::new(40, 1, &program2_pubkey), - ); - - let tx1 = Transaction::new_with_compiled_instructions( - &[&keypair1], - &[non_program_pubkey1], - Hash::new_unique(), - vec![account1_pubkey, account2_pubkey, account3_pubkey], - vec![CompiledInstruction::new(1, &(), vec![0])], - ); - let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1); - - let tx2 = Transaction::new_with_compiled_instructions( - &[&keypair2], - &[non_program_pubkey2], - Hash::new_unique(), - vec![account4_pubkey, account3_pubkey, account2_pubkey], - vec![CompiledInstruction::new(1, &(), vec![0])], - ); - // Let's not register blockhash from tx2. This should cause the tx2 to fail - let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2); - - let owners = &[program1_pubkey, program2_pubkey]; - let mut lock_results = vec![(Ok(()), None, Some(0)), (Ok(()), None, None)]; - let programs = TransactionBatchProcessor::::filter_executable_program_accounts( - &bank, - &[sanitized_tx1, sanitized_tx2], - &mut lock_results, - owners, - ); - - // The result should contain only account3_pubkey as the program accounts - assert_eq!(programs.len(), 1); - assert_eq!( - programs - .get(&account3_pubkey) - .expect("failed to find the program account"), - &(&program1_pubkey, 1) - ); - assert_eq!(lock_results[1].0, Err(TransactionError::BlockhashNotFound)); -} - /// Test that rehashing works with skipped rewrites /// /// Since `bank_to_xxx_snapshot_archive()` calls `Bank::rehash()`, we must ensure that rehashing diff --git a/svm/tests/mock_bank.rs b/svm/tests/mock_bank.rs new file mode 100644 index 000000000..3548b5fba --- /dev/null +++ b/svm/tests/mock_bank.rs @@ -0,0 +1,48 @@ +use { + solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + feature_set::FeatureSet, + hash::Hash, + pubkey::Pubkey, + rent_collector::RentCollector, + }, + solana_svm::transaction_processor::TransactionProcessingCallback, + std::{collections::HashMap, sync::Arc}, +}; + +#[derive(Default)] +pub struct MockBankCallback { + rent_collector: RentCollector, + feature_set: Arc, + pub account_shared_data: HashMap, +} + +impl TransactionProcessingCallback for MockBankCallback { + fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { + if let Some(data) = self.account_shared_data.get(account) { + if data.lamports() == 0 { + None + } else { + owners.iter().position(|entry| data.owner() == entry) + } + } else { + None + } + } + + fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { + self.account_shared_data.get(pubkey).cloned() + } + + fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64) { + todo!() + } + + fn get_rent_collector(&self) -> &RentCollector { + &self.rent_collector + } + + fn get_feature_set(&self) -> Arc { + self.feature_set.clone() + } +} diff --git a/svm/tests/mod.rs b/svm/tests/mod.rs deleted file mode 100644 index d1932e125..000000000 --- a/svm/tests/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod rent_state; diff --git a/svm/tests/rent_state.rs b/svm/tests/rent_state.rs index a97ee64ab..d24a32ac3 100644 --- a/svm/tests/rent_state.rs +++ b/svm/tests/rent_state.rs @@ -7,14 +7,12 @@ use { }, solana_sdk::{ account::{AccountSharedData, WritableAccount}, - feature_set::FeatureSet, fee::FeeStructure, hash::Hash, native_loader, native_token::sol_to_lamports, pubkey::Pubkey, rent::Rent, - rent_collector::RentCollector, signature::{Keypair, Signer}, system_transaction, transaction::SanitizedTransaction, @@ -23,44 +21,16 @@ use { solana_svm::{ account_loader::load_accounts, transaction_account_state_info::TransactionAccountStateInfo, transaction_error_metrics::TransactionErrorMetrics, - transaction_processor::TransactionProcessingCallback, }, - std::{collections::HashMap, sync::Arc}, + std::collections::HashMap, }; -#[derive(Default)] -struct MockBankCallback { - rent_collector: RentCollector, - feature_set: Arc, - account_shared_data: HashMap, -} - -impl TransactionProcessingCallback for MockBankCallback { - fn account_matches_owners(&self, _account: &Pubkey, _owners: &[Pubkey]) -> Option { - todo!() - } - - fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { - self.account_shared_data.get(pubkey).cloned() - } - - fn get_last_blockhash_and_lamports_per_signature(&self) -> (Hash, u64) { - todo!() - } - - fn get_rent_collector(&self) -> &RentCollector { - &self.rent_collector - } - - fn get_feature_set(&self) -> Arc { - self.feature_set.clone() - } -} +mod mock_bank; #[test] fn test_rent_state_list_len() { let mint_keypair = Keypair::new(); - let mut bank = MockBankCallback::default(); + let mut bank = mock_bank::MockBankCallback::default(); let recipient = Pubkey::new_unique(); let last_block_hash = Hash::new_unique(); diff --git a/svm/tests/transaction_processor.rs b/svm/tests/transaction_processor.rs new file mode 100644 index 000000000..170405424 --- /dev/null +++ b/svm/tests/transaction_processor.rs @@ -0,0 +1,206 @@ +#![cfg(test)] + +use { + solana_program_runtime::loaded_programs::{BlockRelation, ForkGraph}, + solana_sdk::{ + account::AccountSharedData, + clock::Slot, + hash::Hash, + instruction::CompiledInstruction, + pubkey::Pubkey, + signature::Keypair, + transaction::{SanitizedTransaction, Transaction, TransactionError}, + }, + solana_svm::transaction_processor::TransactionBatchProcessor, +}; + +mod mock_bank; + +struct MockForkGraph {} + +impl ForkGraph for MockForkGraph { + fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { + todo!() + } +} + +#[test] +fn test_filter_executable_program_accounts() { + let keypair1 = Keypair::new(); + let keypair2 = Keypair::new(); + + let non_program_pubkey1 = Pubkey::new_unique(); + let non_program_pubkey2 = Pubkey::new_unique(); + let program1_pubkey = Pubkey::new_unique(); + let program2_pubkey = Pubkey::new_unique(); + let account1_pubkey = Pubkey::new_unique(); + let account2_pubkey = Pubkey::new_unique(); + let account3_pubkey = Pubkey::new_unique(); + let account4_pubkey = Pubkey::new_unique(); + + let account5_pubkey = Pubkey::new_unique(); + + let mut bank = mock_bank::MockBankCallback::default(); + bank.account_shared_data.insert( + non_program_pubkey1, + AccountSharedData::new(1, 10, &account5_pubkey), + ); + bank.account_shared_data.insert( + non_program_pubkey2, + AccountSharedData::new(1, 10, &account5_pubkey), + ); + bank.account_shared_data.insert( + program1_pubkey, + AccountSharedData::new(40, 1, &account5_pubkey), + ); + bank.account_shared_data.insert( + program2_pubkey, + AccountSharedData::new(40, 1, &account5_pubkey), + ); + bank.account_shared_data.insert( + account1_pubkey, + AccountSharedData::new(1, 10, &non_program_pubkey1), + ); + bank.account_shared_data.insert( + account2_pubkey, + AccountSharedData::new(1, 10, &non_program_pubkey2), + ); + bank.account_shared_data.insert( + account3_pubkey, + AccountSharedData::new(40, 1, &program1_pubkey), + ); + bank.account_shared_data.insert( + account4_pubkey, + AccountSharedData::new(40, 1, &program2_pubkey), + ); + + let tx1 = Transaction::new_with_compiled_instructions( + &[&keypair1], + &[non_program_pubkey1], + Hash::new_unique(), + vec![account1_pubkey, account2_pubkey, account3_pubkey], + vec![CompiledInstruction::new(1, &(), vec![0])], + ); + let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1); + + let tx2 = Transaction::new_with_compiled_instructions( + &[&keypair2], + &[non_program_pubkey2], + Hash::new_unique(), + vec![account4_pubkey, account3_pubkey, account2_pubkey], + vec![CompiledInstruction::new(1, &(), vec![0])], + ); + let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2); + + let owners = &[program1_pubkey, program2_pubkey]; + let programs = TransactionBatchProcessor::::filter_executable_program_accounts( + &bank, + &[sanitized_tx1, sanitized_tx2], + &mut [(Ok(()), None, Some(0)), (Ok(()), None, Some(0))], + owners, + ); + + // The result should contain only account3_pubkey, and account4_pubkey as the program accounts + assert_eq!(programs.len(), 2); + assert_eq!( + programs + .get(&account3_pubkey) + .expect("failed to find the program account"), + &(&program1_pubkey, 2) + ); + assert_eq!( + programs + .get(&account4_pubkey) + .expect("failed to find the program account"), + &(&program2_pubkey, 1) + ); +} + +#[test] +fn test_filter_executable_program_accounts_invalid_blockhash() { + let keypair1 = Keypair::new(); + let keypair2 = Keypair::new(); + + let non_program_pubkey1 = Pubkey::new_unique(); + let non_program_pubkey2 = Pubkey::new_unique(); + let program1_pubkey = Pubkey::new_unique(); + let program2_pubkey = Pubkey::new_unique(); + let account1_pubkey = Pubkey::new_unique(); + let account2_pubkey = Pubkey::new_unique(); + let account3_pubkey = Pubkey::new_unique(); + let account4_pubkey = Pubkey::new_unique(); + + let account5_pubkey = Pubkey::new_unique(); + + let mut bank = mock_bank::MockBankCallback::default(); + bank.account_shared_data.insert( + non_program_pubkey1, + AccountSharedData::new(1, 10, &account5_pubkey), + ); + bank.account_shared_data.insert( + non_program_pubkey2, + AccountSharedData::new(1, 10, &account5_pubkey), + ); + bank.account_shared_data.insert( + program1_pubkey, + AccountSharedData::new(40, 1, &account5_pubkey), + ); + bank.account_shared_data.insert( + program2_pubkey, + AccountSharedData::new(40, 1, &account5_pubkey), + ); + bank.account_shared_data.insert( + account1_pubkey, + AccountSharedData::new(1, 10, &non_program_pubkey1), + ); + bank.account_shared_data.insert( + account2_pubkey, + AccountSharedData::new(1, 10, &non_program_pubkey2), + ); + bank.account_shared_data.insert( + account3_pubkey, + AccountSharedData::new(40, 1, &program1_pubkey), + ); + bank.account_shared_data.insert( + account4_pubkey, + AccountSharedData::new(40, 1, &program2_pubkey), + ); + + let tx1 = Transaction::new_with_compiled_instructions( + &[&keypair1], + &[non_program_pubkey1], + Hash::new_unique(), + vec![account1_pubkey, account2_pubkey, account3_pubkey], + vec![CompiledInstruction::new(1, &(), vec![0])], + ); + let sanitized_tx1 = SanitizedTransaction::from_transaction_for_tests(tx1); + + let tx2 = Transaction::new_with_compiled_instructions( + &[&keypair2], + &[non_program_pubkey2], + Hash::new_unique(), + vec![account4_pubkey, account3_pubkey, account2_pubkey], + vec![CompiledInstruction::new(1, &(), vec![0])], + ); + // Let's not register blockhash from tx2. This should cause the tx2 to fail + let sanitized_tx2 = SanitizedTransaction::from_transaction_for_tests(tx2); + + let owners = &[program1_pubkey, program2_pubkey]; + let mut lock_results = vec![(Ok(()), None, Some(0)), (Ok(()), None, None)]; + let programs = TransactionBatchProcessor::::filter_executable_program_accounts( + &bank, + &[sanitized_tx1, sanitized_tx2], + &mut lock_results, + owners, + ); + + // The result should contain only account3_pubkey as the program accounts + assert_eq!(programs.len(), 1); + assert_eq!( + programs + .get(&account3_pubkey) + .expect("failed to find the program account"), + &(&program1_pubkey, 1) + ); + assert_eq!(lock_results[1].0, Err(TransactionError::BlockhashNotFound)); +}