Skip to content

Commit 1c0e142

Browse files
authored
harness: refactor account compilation step (#157)
1 parent 0c01e45 commit 1c0e142

File tree

5 files changed

+152
-70
lines changed

5 files changed

+152
-70
lines changed

harness/src/compile_accounts.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ pub struct CompiledAccounts {
2121
pub transaction_accounts: Vec<TransactionAccount>,
2222
}
2323

24-
pub fn compile_accounts(
24+
pub fn compile_accounts<'a>(
2525
instruction: &Instruction,
26-
accounts: &[(Pubkey, Account)],
26+
accounts: impl Iterator<Item = &'a (Pubkey, Account)>,
2727
loader_key: Pubkey,
2828
) -> CompiledAccounts {
2929
let stub_out_program_account = move || {

harness/src/fuzz/firedancer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn build_fixture_context(
7373
instruction_accounts,
7474
transaction_accounts,
7575
..
76-
} = compile_accounts(instruction, accounts, loader_key);
76+
} = compile_accounts(instruction, accounts.iter(), loader_key);
7777

7878
let accounts = transaction_accounts
7979
.into_iter()

harness/src/lib.rs

Lines changed: 98 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,6 @@ pub use mollusk_svm_result as result;
454454
use mollusk_svm_result::Compare;
455455
#[cfg(feature = "precompiles")]
456456
use solana_precompile_error::PrecompileError;
457-
#[cfg(feature = "invocation-inspect-callback")]
458-
use solana_transaction_context::InstructionAccount;
459457
use {
460458
crate::{
461459
account_store::AccountStore, compile_accounts::CompiledAccounts, epoch_stake::EpochStake,
@@ -464,7 +462,7 @@ use {
464462
agave_feature_set::FeatureSet,
465463
mollusk_svm_error::error::{MolluskError, MolluskPanic},
466464
mollusk_svm_result::{Check, CheckContext, Config, InstructionResult},
467-
solana_account::Account,
465+
solana_account::{Account, AccountSharedData},
468466
solana_compute_budget::compute_budget::ComputeBudget,
469467
solana_hash::Hash,
470468
solana_instruction::{AccountMeta, Instruction},
@@ -473,7 +471,7 @@ use {
473471
solana_svm_callback::InvokeContextCallback,
474472
solana_svm_log_collector::LogCollector,
475473
solana_svm_timings::ExecuteTimings,
476-
solana_transaction_context::TransactionContext,
474+
solana_transaction_context::{InstructionAccount, TransactionContext},
477475
std::{cell::RefCell, collections::HashSet, iter::once, rc::Rc},
478476
};
479477

@@ -680,32 +678,28 @@ impl Mollusk {
680678
self.sysvars.warp_to_slot(slot)
681679
}
682680

683-
/// Process an instruction using the minified Solana Virtual Machine (SVM)
684-
/// environment. Simply returns the result.
685-
pub fn process_instruction(
681+
fn get_loader_key(&self, program_id: &Pubkey) -> Pubkey {
682+
if crate::program::precompile_keys::is_precompile(program_id) {
683+
crate::program::loader_keys::NATIVE_LOADER
684+
} else {
685+
self.program_cache
686+
.load_program(program_id)
687+
.or_panic_with(MolluskError::ProgramNotCached(program_id))
688+
.account_owner()
689+
}
690+
}
691+
692+
fn process_instruction_inner(
686693
&self,
687694
instruction: &Instruction,
688695
accounts: &[(Pubkey, Account)],
696+
program_id_index: u16,
697+
instruction_accounts: Vec<InstructionAccount>,
698+
transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
689699
) -> InstructionResult {
690700
let mut compute_units_consumed = 0;
691701
let mut timings = ExecuteTimings::default();
692702

693-
let loader_key = if crate::program::precompile_keys::is_precompile(&instruction.program_id)
694-
{
695-
crate::program::loader_keys::NATIVE_LOADER
696-
} else {
697-
self.program_cache
698-
.load_program(&instruction.program_id)
699-
.or_panic_with(MolluskError::ProgramNotCached(&instruction.program_id))
700-
.account_owner()
701-
};
702-
703-
let CompiledAccounts {
704-
program_id_index,
705-
instruction_accounts,
706-
transaction_accounts,
707-
} = crate::compile_accounts::compile_accounts(instruction, accounts, loader_key);
708-
709703
let mut transaction_context = TransactionContext::new(
710704
transaction_accounts,
711705
self.sysvars.rent.clone(),
@@ -805,6 +799,30 @@ impl Mollusk {
805799
}
806800
}
807801

802+
/// Process an instruction using the minified Solana Virtual Machine (SVM)
803+
/// environment. Simply returns the result.
804+
pub fn process_instruction(
805+
&self,
806+
instruction: &Instruction,
807+
accounts: &[(Pubkey, Account)],
808+
) -> InstructionResult {
809+
let loader_key = self.get_loader_key(&instruction.program_id);
810+
811+
let CompiledAccounts {
812+
program_id_index,
813+
instruction_accounts,
814+
transaction_accounts,
815+
} = crate::compile_accounts::compile_accounts(instruction, accounts.iter(), loader_key);
816+
817+
self.process_instruction_inner(
818+
instruction,
819+
accounts,
820+
program_id_index,
821+
instruction_accounts,
822+
transaction_accounts,
823+
)
824+
}
825+
808826
/// Process a chain of instructions using the minified Solana Virtual
809827
/// Machine (SVM) environment. The returned result is an
810828
/// `InstructionResult`, containing:
@@ -820,22 +838,36 @@ impl Mollusk {
820838
instructions: &[Instruction],
821839
accounts: &[(Pubkey, Account)],
822840
) -> InstructionResult {
823-
let mut result = InstructionResult {
841+
let mut composite_result = InstructionResult {
824842
resulting_accounts: accounts.to_vec(),
825843
..Default::default()
826844
};
827845

828846
for instruction in instructions {
829-
let this_result = self.process_instruction(instruction, &result.resulting_accounts);
847+
let loader_key = self.get_loader_key(&instruction.program_id);
848+
849+
let CompiledAccounts {
850+
program_id_index,
851+
instruction_accounts,
852+
transaction_accounts,
853+
} = crate::compile_accounts::compile_accounts(instruction, accounts.iter(), loader_key);
854+
855+
let this_result = self.process_instruction_inner(
856+
instruction,
857+
accounts,
858+
program_id_index,
859+
instruction_accounts,
860+
transaction_accounts,
861+
);
830862

831-
result.absorb(this_result);
863+
composite_result.absorb(this_result);
832864

833-
if result.program_result.is_err() {
865+
if composite_result.program_result.is_err() {
834866
break;
835867
}
836868
}
837869

838-
result
870+
composite_result
839871
}
840872

841873
/// Process an instruction using the minified Solana Virtual Machine (SVM)
@@ -866,7 +898,21 @@ impl Mollusk {
866898
accounts: &[(Pubkey, Account)],
867899
checks: &[Check],
868900
) -> InstructionResult {
869-
let result = self.process_instruction(instruction, accounts);
901+
let loader_key = self.get_loader_key(&instruction.program_id);
902+
903+
let CompiledAccounts {
904+
program_id_index,
905+
instruction_accounts,
906+
transaction_accounts,
907+
} = crate::compile_accounts::compile_accounts(instruction, accounts.iter(), loader_key);
908+
909+
let result = self.process_instruction_inner(
910+
instruction,
911+
accounts,
912+
program_id_index,
913+
instruction_accounts,
914+
transaction_accounts,
915+
);
870916

871917
#[cfg(any(feature = "fuzz", feature = "fuzz-fd"))]
872918
fuzz::generate_fixtures_from_mollusk_test(self, instruction, accounts, &result);
@@ -904,26 +950,42 @@ impl Mollusk {
904950
instructions: &[(&Instruction, &[Check])],
905951
accounts: &[(Pubkey, Account)],
906952
) -> InstructionResult {
907-
let mut result = InstructionResult {
953+
let mut composite_result = InstructionResult {
908954
resulting_accounts: accounts.to_vec(),
909955
..Default::default()
910956
};
911957

912958
for (instruction, checks) in instructions.iter() {
913-
let this_result = self.process_and_validate_instruction(
959+
let loader_key = self.get_loader_key(&instruction.program_id);
960+
let accounts = &composite_result.resulting_accounts;
961+
962+
let CompiledAccounts {
963+
program_id_index,
964+
instruction_accounts,
965+
transaction_accounts,
966+
} = crate::compile_accounts::compile_accounts(instruction, accounts.iter(), loader_key);
967+
968+
let this_result = self.process_instruction_inner(
914969
instruction,
915-
&result.resulting_accounts,
916-
checks,
970+
accounts,
971+
program_id_index,
972+
instruction_accounts,
973+
transaction_accounts,
917974
);
918975

919-
result.absorb(this_result);
976+
#[cfg(any(feature = "fuzz", feature = "fuzz-fd"))]
977+
fuzz::generate_fixtures_from_mollusk_test(self, instruction, accounts, &this_result);
978+
979+
this_result.run_checks(checks, &self.config, self);
920980

921-
if result.program_result.is_err() {
981+
composite_result.absorb(this_result);
982+
983+
if composite_result.program_result.is_err() {
922984
break;
923985
}
924986
}
925987

926-
result
988+
composite_result
927989
}
928990

929991
#[cfg(feature = "fuzz")]

keys/src/accounts.rs

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
33
use {
44
crate::keys::KeyMap,
5-
mollusk_svm_error::error::MolluskError,
5+
mollusk_svm_error::error::{MolluskError, MolluskPanic},
66
solana_account::{Account, AccountSharedData},
77
solana_instruction::Instruction,
88
solana_pubkey::Pubkey,
99
solana_transaction_context::{IndexOfAccount, InstructionAccount, TransactionAccount},
10+
std::collections::HashMap,
1011
};
1112

1213
// Helper struct to avoid cloning instruction data.
@@ -47,60 +48,64 @@ pub fn compile_instruction_accounts(
4748
.collect()
4849
}
4950

50-
pub fn compile_transaction_accounts_for_instruction(
51+
pub fn compile_transaction_accounts_for_instruction<'a>(
5152
key_map: &KeyMap,
5253
instruction: &Instruction,
53-
accounts: &[(Pubkey, Account)],
54+
accounts: impl Iterator<Item = &'a (Pubkey, Account)>,
5455
stub_out_program_account: Option<Box<dyn Fn() -> Account>>,
5556
) -> Vec<TransactionAccount> {
57+
let len = key_map.len();
58+
let mut by_key: HashMap<Pubkey, AccountSharedData> = HashMap::with_capacity(len);
59+
60+
for (key, account) in accounts {
61+
if key_map.contains_key(key) {
62+
by_key.insert(*key, AccountSharedData::from(account.clone()));
63+
}
64+
}
65+
5666
key_map
5767
.keys()
5868
.map(|key| {
59-
let account = accounts
60-
.iter()
61-
.find(|(k, _)| k == key)
62-
.map(|(_, account)| AccountSharedData::from(account.clone()));
63-
64-
if let Some(account) = account {
65-
(*key, account)
66-
} else if let Some(stub_out_program_account) = &stub_out_program_account {
69+
if let Some(stub_out_program_account) = &stub_out_program_account {
6770
if instruction.program_id == *key {
68-
(*key, stub_out_program_account().into())
69-
} else {
70-
panic!("{}", MolluskError::AccountMissing(key))
71+
return (*key, stub_out_program_account().into());
7172
}
72-
} else {
73-
panic!("{}", MolluskError::AccountMissing(key))
7473
}
74+
let account = by_key
75+
.remove(key)
76+
.or_panic_with(MolluskError::AccountMissing(key));
77+
(*key, account)
7578
})
7679
.collect()
7780
}
7881

79-
pub fn compile_transaction_accounts(
82+
pub fn compile_transaction_accounts<'a>(
8083
key_map: &KeyMap,
8184
instructions: &[Instruction],
82-
accounts: &[(Pubkey, Account)],
85+
accounts: impl Iterator<Item = &'a (Pubkey, Account)>,
8386
stub_out_program_account: Option<Box<dyn Fn() -> Account>>,
8487
) -> Vec<TransactionAccount> {
88+
let len = key_map.len();
89+
let mut by_key: HashMap<Pubkey, AccountSharedData> = HashMap::with_capacity(len);
90+
91+
for (key, account) in accounts {
92+
if key_map.contains_key(key) {
93+
by_key.insert(*key, AccountSharedData::from(account.clone()));
94+
}
95+
}
96+
8597
key_map
8698
.keys()
8799
.map(|key| {
88-
let account = accounts
89-
.iter()
90-
.find(|(k, _)| k == key)
91-
.map(|(_, account)| AccountSharedData::from(account.clone()));
92-
93-
if let Some(account) = account {
94-
(*key, account)
95-
} else if let Some(stub_out_program_account) = &stub_out_program_account {
100+
if let Some(stub_out_program_account) = &stub_out_program_account {
96101
if instructions.iter().any(|ix| ix.program_id == *key) {
97-
(*key, stub_out_program_account().into())
98-
} else {
99-
panic!("{}", MolluskError::AccountMissing(key))
102+
return (*key, stub_out_program_account().into());
100103
}
101-
} else {
102-
panic!("{}", MolluskError::AccountMissing(key))
103104
}
105+
let account = by_key
106+
.remove(key)
107+
.or_panic_with(MolluskError::AccountMissing(key));
108+
(*key, account)
104109
})
105110
.collect()
106111
}

keys/src/keys.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,21 @@ impl KeyMap {
181181
pub fn position(&self, key: &Pubkey) -> Option<usize> {
182182
self.map.keys().position(|k| k == key)
183183
}
184+
185+
/// Return whether or not the map contains a key.
186+
pub fn contains_key(&self, key: &Pubkey) -> bool {
187+
self.map.contains_key(key)
188+
}
189+
190+
/// Get the length of the key map.
191+
pub fn len(&self) -> usize {
192+
self.map.len()
193+
}
194+
195+
/// Return whether or not the key map is empty.
196+
pub fn is_empty(&self) -> bool {
197+
self.map.is_empty()
198+
}
184199
}
185200

186201
#[cfg(test)]

0 commit comments

Comments
 (0)