Skip to content

Commit ddadaf6

Browse files
authored
refactor: account compilation: fallback accounts (#187)
1 parent 6dfa58a commit ddadaf6

File tree

4 files changed

+123
-186
lines changed

4 files changed

+123
-186
lines changed

harness/src/compile_accounts.rs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ use {
55
mollusk_svm_keys::{
66
accounts::{
77
compile_instruction_accounts, compile_instruction_without_data,
8-
compile_transaction_accounts_for_instruction,
8+
compile_transaction_accounts,
99
},
1010
keys::KeyMap,
1111
},
12-
solana_account::{Account, AccountSharedData, WritableAccount},
12+
solana_account::{Account, AccountSharedData},
1313
solana_instruction::Instruction,
1414
solana_pubkey::Pubkey,
1515
solana_transaction_context::InstructionAccount,
16+
std::collections::HashMap,
1617
};
1718

1819
pub struct CompiledAccounts {
@@ -21,41 +22,15 @@ pub struct CompiledAccounts {
2122
pub transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
2223
}
2324

24-
pub fn compile_accounts<'a, 'b, I>(
25-
instruction_index: usize,
26-
all_instructions: I,
25+
pub fn compile_accounts<'a>(
26+
instruction: &Instruction,
2727
accounts: impl Iterator<Item = &'a (Pubkey, Account)>,
28-
loader_key: Pubkey,
29-
) -> CompiledAccounts
30-
where
31-
I: IntoIterator<Item = &'b Instruction>,
32-
{
33-
// Collect instruction references for the instructions sysvar.
34-
let instruction_refs: Vec<&Instruction> = all_instructions.into_iter().collect();
35-
let instruction = instruction_refs[instruction_index];
36-
37-
let stub_out_program_account = move || {
38-
let mut program_account = Account::default();
39-
program_account.set_owner(loader_key);
40-
program_account.set_executable(true);
41-
program_account
42-
};
43-
44-
let fallback_to_instructions_sysvar = |pubkey: &Pubkey| -> Option<Account> {
45-
(pubkey == &solana_instructions_sysvar::ID)
46-
.then(|| crate::instructions_sysvar::keyed_account(instruction_refs.iter().copied()).1)
47-
};
48-
28+
fallback_accounts: &HashMap<Pubkey, Account>,
29+
) -> CompiledAccounts {
4930
let key_map = KeyMap::compile_from_instruction(instruction);
5031
let compiled_instruction = compile_instruction_without_data(&key_map, instruction);
5132
let instruction_accounts = compile_instruction_accounts(&key_map, &compiled_instruction);
52-
let transaction_accounts = compile_transaction_accounts_for_instruction(
53-
&key_map,
54-
instruction,
55-
accounts,
56-
Some(stub_out_program_account),
57-
Some(fallback_to_instructions_sysvar),
58-
);
33+
let transaction_accounts = compile_transaction_accounts(&key_map, accounts, fallback_accounts);
5934

6035
CompiledAccounts {
6136
program_id_index: compiled_instruction.program_id_index as u16,

harness/src/fuzz/firedancer.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,28 @@ fn build_fixture_context(
6363
instruction: &Instruction,
6464
slot: u64,
6565
) -> FuzzContext {
66-
const INDEX: usize = 0;
67-
6866
let loader_key = if BUILTIN_PROGRAM_IDS.contains(&instruction.program_id) {
6967
solana_sdk_ids::native_loader::id()
7068
} else {
7169
DEFAULT_LOADER_KEY
7270
};
7371

72+
let fallbacks = [(
73+
instruction.program_id,
74+
Account {
75+
owner: loader_key,
76+
executable: true,
77+
..Default::default()
78+
},
79+
)]
80+
.into_iter()
81+
.collect();
82+
7483
let CompiledAccounts {
7584
instruction_accounts,
7685
transaction_accounts,
7786
..
78-
} = compile_accounts(
79-
INDEX,
80-
std::iter::once(instruction),
81-
accounts.iter(),
82-
loader_key,
83-
);
87+
} = compile_accounts(instruction, accounts.iter(), &fallbacks);
8488

8589
let accounts = transaction_accounts
8690
.into_iter()

harness/src/lib.rs

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,13 @@ use {
483483
solana_svm_log_collector::LogCollector,
484484
solana_svm_timings::ExecuteTimings,
485485
solana_transaction_context::{InstructionAccount, TransactionContext},
486-
std::{cell::RefCell, collections::HashSet, iter::once, rc::Rc, sync::Arc},
486+
std::{
487+
cell::RefCell,
488+
collections::{HashMap, HashSet},
489+
iter::once,
490+
rc::Rc,
491+
sync::Arc,
492+
},
487493
};
488494
#[cfg(feature = "inner-instructions")]
489495
use {
@@ -772,6 +778,44 @@ impl Mollusk {
772778
}
773779
}
774780

781+
// Determine the accounts to fallback to during account compilation.
782+
fn get_account_fallbacks<'a>(
783+
&self,
784+
all_program_ids: impl Iterator<Item = &'a Pubkey>,
785+
all_instructions: impl Iterator<Item = &'a Instruction>,
786+
accounts: &[(Pubkey, Account)],
787+
) -> HashMap<Pubkey, Account> {
788+
// Use a HashSet for fast lookups.
789+
let account_keys: HashSet<&Pubkey> = accounts.iter().map(|(key, _)| key).collect();
790+
791+
let mut fallbacks = HashMap::new();
792+
793+
// Top-level target programs.
794+
all_program_ids.for_each(|program_id| {
795+
if !account_keys.contains(program_id) {
796+
// Fallback to a stub.
797+
fallbacks.insert(
798+
*program_id,
799+
Account {
800+
owner: self.get_loader_key(program_id),
801+
executable: true,
802+
..Default::default()
803+
},
804+
);
805+
}
806+
});
807+
808+
// Instructions sysvar.
809+
if !account_keys.contains(&solana_instructions_sysvar::ID) {
810+
// Fallback to the actual implementation of the sysvar.
811+
let (ix_sysvar_id, ix_sysvar_acct) =
812+
crate::instructions_sysvar::keyed_account(all_instructions);
813+
fallbacks.insert(ix_sysvar_id, ix_sysvar_acct);
814+
}
815+
816+
fallbacks
817+
}
818+
775819
#[cfg(feature = "inner-instructions")]
776820
fn deconstruct_transaction(
777821
transaction_context: &mut TransactionContext,
@@ -958,17 +1002,20 @@ impl Mollusk {
9581002
) -> InstructionResult {
9591003
const INDEX: usize = 0;
9601004

961-
let loader_key = self.get_loader_key(&instruction.program_id);
1005+
let fallback_accounts = self.get_account_fallbacks(
1006+
std::iter::once(&instruction.program_id),
1007+
std::iter::once(instruction),
1008+
accounts,
1009+
);
9621010

9631011
let CompiledAccounts {
9641012
program_id_index,
9651013
instruction_accounts,
9661014
transaction_accounts,
9671015
} = crate::compile_accounts::compile_accounts(
968-
INDEX,
969-
std::iter::once(instruction),
1016+
instruction,
9701017
accounts.iter(),
971-
loader_key,
1018+
&fallback_accounts,
9721019
);
9731020

9741021
self.process_instruction_inner(
@@ -1001,18 +1048,21 @@ impl Mollusk {
10011048
..Default::default()
10021049
};
10031050

1004-
for (index, instruction) in instructions.iter().enumerate() {
1005-
let loader_key = self.get_loader_key(&instruction.program_id);
1051+
let fallback_accounts = self.get_account_fallbacks(
1052+
instructions.iter().map(|ix| &ix.program_id),
1053+
instructions.iter(),
1054+
accounts,
1055+
);
10061056

1057+
for (index, instruction) in instructions.iter().enumerate() {
10071058
let CompiledAccounts {
10081059
program_id_index,
10091060
instruction_accounts,
10101061
transaction_accounts,
10111062
} = crate::compile_accounts::compile_accounts(
1012-
index,
1013-
instructions.iter(),
1063+
instruction,
10141064
accounts.iter(),
1015-
loader_key,
1065+
&fallback_accounts,
10161066
);
10171067

10181068
let this_result = self.process_instruction_inner(
@@ -1064,17 +1114,20 @@ impl Mollusk {
10641114
) -> InstructionResult {
10651115
const INDEX: usize = 0;
10661116

1067-
let loader_key = self.get_loader_key(&instruction.program_id);
1117+
let fallback_accounts = self.get_account_fallbacks(
1118+
std::iter::once(&instruction.program_id),
1119+
std::iter::once(instruction),
1120+
accounts,
1121+
);
10681122

10691123
let CompiledAccounts {
10701124
program_id_index,
10711125
instruction_accounts,
10721126
transaction_accounts,
10731127
} = crate::compile_accounts::compile_accounts(
1074-
INDEX,
1075-
std::iter::once(instruction),
1128+
instruction,
10761129
accounts.iter(),
1077-
loader_key,
1130+
&fallback_accounts,
10781131
);
10791132

10801133
let result = self.process_instruction_inner(
@@ -1127,19 +1180,23 @@ impl Mollusk {
11271180
..Default::default()
11281181
};
11291182

1183+
let fallback_accounts = self.get_account_fallbacks(
1184+
instructions.iter().map(|(ix, _)| &ix.program_id),
1185+
instructions.iter().map(|(ix, _)| *ix),
1186+
accounts,
1187+
);
1188+
11301189
for (index, (instruction, checks)) in instructions.iter().enumerate() {
1131-
let loader_key = self.get_loader_key(&instruction.program_id);
11321190
let accounts = &composite_result.resulting_accounts;
11331191

11341192
let CompiledAccounts {
11351193
program_id_index,
11361194
instruction_accounts,
11371195
transaction_accounts,
11381196
} = crate::compile_accounts::compile_accounts(
1139-
index,
1140-
instructions.iter().map(|(ix, _)| *ix),
1197+
instruction,
11411198
accounts.iter(),
1142-
loader_key,
1199+
&fallback_accounts,
11431200
);
11441201

11451202
let this_result = self.process_instruction_inner(

0 commit comments

Comments
 (0)