Skip to content

Commit 5f4d642

Browse files
authored
feat: account-store: load sysvar and program accounts automatically (#150)
1 parent 215d6f0 commit 5f4d642

File tree

4 files changed

+115
-3
lines changed

4 files changed

+115
-3
lines changed

harness/src/lib.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,9 +1195,17 @@ impl<AS: AccountStore> MolluskContext<AS> {
11951195
.iter()
11961196
.for_each(|AccountMeta { pubkey, .. }| {
11971197
if seen.insert(*pubkey) {
1198-
let account = store
1199-
.get_account(pubkey)
1200-
.unwrap_or_else(|| store.default_account(pubkey));
1198+
let account = store.get_account(pubkey).unwrap_or_else(|| {
1199+
self.mollusk
1200+
.sysvars
1201+
.maybe_create_sysvar_account(pubkey)
1202+
.unwrap_or_else(|| {
1203+
self.mollusk
1204+
.program_cache
1205+
.maybe_create_program_account(pubkey)
1206+
.unwrap_or_else(|| store.default_account(pubkey))
1207+
})
1208+
});
12011209
accounts.push((*pubkey, account));
12021210
}
12031211
});

harness/src/program.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,24 @@ impl ProgramCache {
164164
})
165165
.collect()
166166
}
167+
168+
pub(crate) fn maybe_create_program_account(&self, pubkey: &Pubkey) -> Option<Account> {
169+
// If it's found in the entries cache, create the proper program account based
170+
// on the loader key.
171+
self.entries_cache
172+
.borrow()
173+
.get(pubkey)
174+
.map(|loader_key| match *loader_key {
175+
loader_keys::NATIVE_LOADER => {
176+
create_keyed_account_for_builtin_program(pubkey, "I'm a stub!").1
177+
}
178+
loader_keys::LOADER_V1 => create_program_account_loader_v1(&[]),
179+
loader_keys::LOADER_V2 => create_program_account_loader_v2(&[]),
180+
loader_keys::LOADER_V3 => create_program_account_loader_v3(pubkey),
181+
loader_keys::LOADER_V4 => create_program_account_loader_v4(&[]),
182+
_ => panic!("Invalid loader key: {}", loader_key),
183+
})
184+
}
167185
}
168186

169187
pub struct Builtin {

harness/src/sysvar.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,26 @@ impl Sysvars {
7272
(T::id(), account)
7373
}
7474

75+
pub(crate) fn maybe_create_sysvar_account(&self, pubkey: &Pubkey) -> Option<Account> {
76+
if pubkey.eq(&Clock::id()) {
77+
Some(self.sysvar_account(&self.clock).1)
78+
} else if pubkey.eq(&EpochRewards::id()) {
79+
Some(self.sysvar_account(&self.epoch_rewards).1)
80+
} else if pubkey.eq(&EpochSchedule::id()) {
81+
Some(self.sysvar_account(&self.epoch_schedule).1)
82+
} else if pubkey.eq(&LastRestartSlot::id()) {
83+
Some(self.sysvar_account(&self.last_restart_slot).1)
84+
} else if pubkey.eq(&Rent::id()) {
85+
Some(self.sysvar_account(&self.rent).1)
86+
} else if pubkey.eq(&SlotHashes::id()) {
87+
Some(self.sysvar_account(&self.slot_hashes).1)
88+
} else if pubkey.eq(&StakeHistory::id()) {
89+
Some(self.sysvar_account(&self.stake_history).1)
90+
} else {
91+
None
92+
}
93+
}
94+
7595
/// Get the key and account for the clock sysvar.
7696
pub fn keyed_account_for_clock_sysvar(&self) -> (Pubkey, Account) {
7797
self.sysvar_account(&self.clock)

harness/tests/account_store.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,72 @@ fn test_multiple_transfers_with_persistent_state() {
126126
);
127127
}
128128

129+
#[test]
130+
fn test_account_store_sysvar_account() {
131+
let mollusk = Mollusk::default();
132+
let context = mollusk.with_context(HashMap::new());
133+
134+
// Use Clock sysvar as an example.
135+
let clock_pubkey = solana_sdk_ids::sysvar::clock::id();
136+
let recipient = Pubkey::new_unique();
137+
138+
// Create an instruction that references the Clock sysvar.
139+
let instruction = solana_instruction::Instruction::new_with_bytes(
140+
solana_sdk_ids::system_program::id(),
141+
&[],
142+
vec![
143+
solana_instruction::AccountMeta::new_readonly(clock_pubkey, false),
144+
solana_instruction::AccountMeta::new(recipient, false),
145+
],
146+
);
147+
148+
// Process the instruction - this should load the Clock sysvar account.
149+
context.process_instruction(&instruction);
150+
151+
// Verify the Clock sysvar was loaded correctly.
152+
let store = context.account_store.borrow();
153+
let clock_account = store.get(&clock_pubkey).expect("Clock sysvar should exist");
154+
155+
// Verify it has the expected owner.
156+
assert_eq!(clock_account.owner, solana_sdk_ids::sysvar::id());
157+
// Verify it has data (Clock sysvar should have serialized Clock data).
158+
assert!(!clock_account.data.is_empty());
159+
}
160+
161+
#[test]
162+
fn test_account_store_program_account() {
163+
// Use the System Program as an example.
164+
let program_id = solana_sdk_ids::system_program::id();
165+
let mollusk = Mollusk::default();
166+
167+
let context = mollusk.with_context(HashMap::new());
168+
let recipient = Pubkey::new_unique();
169+
170+
// Create an instruction that references the program account.
171+
let instruction = solana_instruction::Instruction::new_with_bytes(
172+
solana_sdk_ids::bpf_loader_upgradeable::id(),
173+
&[],
174+
vec![
175+
solana_instruction::AccountMeta::new_readonly(program_id, false),
176+
solana_instruction::AccountMeta::new(recipient, false),
177+
],
178+
);
179+
180+
// Process the instruction - this should load the program account.
181+
context.process_instruction(&instruction);
182+
183+
// Verify the program account was loaded correctly
184+
let store = context.account_store.borrow();
185+
let program_account = store
186+
.get(&program_id)
187+
.expect("Program account should exist");
188+
189+
// Verify it has the expected owner (native loader for builtins).
190+
assert_eq!(program_account.owner, solana_sdk_ids::native_loader::id());
191+
// Verify it's marked as executable.
192+
assert!(program_account.executable);
193+
}
194+
129195
#[test]
130196
fn test_account_store_default_account() {
131197
let mollusk = Mollusk::default();

0 commit comments

Comments
 (0)