Skip to content

Commit 4aa0cb2

Browse files
authored
feat: make mollusk context api return InstructionResult (#152)
1 parent 43ff949 commit 4aa0cb2

File tree

4 files changed

+85
-101
lines changed

4 files changed

+85
-101
lines changed

harness/src/lib.rs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ use {
461461
},
462462
agave_feature_set::FeatureSet,
463463
mollusk_svm_error::error::{MolluskError, MolluskPanic},
464-
mollusk_svm_result::{Check, CheckContext, Config, ContextResult, InstructionResult},
464+
mollusk_svm_result::{Check, CheckContext, Config, InstructionResult},
465465
solana_account::Account,
466466
solana_compute_budget::compute_budget::ComputeBudget,
467467
solana_hash::Hash,
@@ -1213,46 +1213,34 @@ impl<AS: AccountStore> MolluskContext<AS> {
12131213
accounts
12141214
}
12151215

1216-
fn consume_mollusk_result(&self, result: InstructionResult) -> ContextResult {
1217-
let InstructionResult {
1218-
compute_units_consumed,
1219-
execution_time,
1220-
program_result,
1221-
raw_result,
1222-
return_data,
1223-
resulting_accounts,
1224-
} = result;
1225-
1226-
let mut store = self.account_store.borrow_mut();
1227-
for (pubkey, account) in resulting_accounts {
1228-
store.store_account(pubkey, account);
1229-
}
1230-
1231-
ContextResult {
1232-
compute_units_consumed,
1233-
execution_time,
1234-
program_result,
1235-
raw_result,
1236-
return_data,
1216+
fn consume_mollusk_result(&self, result: &InstructionResult) {
1217+
if result.program_result.is_ok() {
1218+
// Only store resulting accounts if the result was success.
1219+
let mut store = self.account_store.borrow_mut();
1220+
for (pubkey, account) in result.resulting_accounts.iter() {
1221+
store.store_account(*pubkey, account.clone());
1222+
}
12371223
}
12381224
}
12391225

12401226
/// Process an instruction using the minified Solana Virtual Machine (SVM)
12411227
/// environment. Simply returns the result.
1242-
pub fn process_instruction(&self, instruction: &Instruction) -> ContextResult {
1228+
pub fn process_instruction(&self, instruction: &Instruction) -> InstructionResult {
12431229
let accounts = self.load_accounts_for_instructions(once(instruction));
12441230
let result = self.mollusk.process_instruction(instruction, &accounts);
1245-
self.consume_mollusk_result(result)
1231+
self.consume_mollusk_result(&result);
1232+
result
12461233
}
12471234

12481235
/// Process a chain of instructions using the minified Solana Virtual
12491236
/// Machine (SVM) environment.
1250-
pub fn process_instruction_chain(&self, instructions: &[Instruction]) -> ContextResult {
1237+
pub fn process_instruction_chain(&self, instructions: &[Instruction]) -> InstructionResult {
12511238
let accounts = self.load_accounts_for_instructions(instructions.iter());
12521239
let result = self
12531240
.mollusk
12541241
.process_instruction_chain(instructions, &accounts);
1255-
self.consume_mollusk_result(result)
1242+
self.consume_mollusk_result(&result);
1243+
result
12561244
}
12571245

12581246
/// Process an instruction using the minified Solana Virtual Machine (SVM)
@@ -1261,26 +1249,28 @@ impl<AS: AccountStore> MolluskContext<AS> {
12611249
&self,
12621250
instruction: &Instruction,
12631251
checks: &[Check],
1264-
) -> ContextResult {
1252+
) -> InstructionResult {
12651253
let accounts = self.load_accounts_for_instructions(once(instruction));
12661254
let result = self
12671255
.mollusk
12681256
.process_and_validate_instruction(instruction, &accounts, checks);
1269-
self.consume_mollusk_result(result)
1257+
self.consume_mollusk_result(&result);
1258+
result
12701259
}
12711260

12721261
/// Process a chain of instructions using the minified Solana Virtual
12731262
/// Machine (SVM) environment, then perform checks on the result.
12741263
pub fn process_and_validate_instruction_chain(
12751264
&self,
12761265
instructions: &[(&Instruction, &[Check])],
1277-
) -> ContextResult {
1266+
) -> InstructionResult {
12781267
let accounts = self.load_accounts_for_instructions(
12791268
instructions.iter().map(|(instruction, _)| *instruction),
12801269
);
12811270
let result = self
12821271
.mollusk
12831272
.process_and_validate_instruction_chain(instructions, &accounts);
1284-
self.consume_mollusk_result(result)
1273+
self.consume_mollusk_result(&result);
1274+
result
12851275
}
12861276
}

harness/tests/account_store.rs

Lines changed: 58 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use {
22
mollusk_svm::{result::Check, Mollusk},
33
solana_account::{Account, ReadableAccount},
4+
solana_instruction::{AccountMeta, Instruction},
45
solana_program_error::ProgramError,
56
solana_pubkey::Pubkey,
67
solana_system_interface::error::SystemError,
@@ -127,69 +128,73 @@ fn test_multiple_transfers_with_persistent_state() {
127128
}
128129

129130
#[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();
131+
fn test_account_store_sysvars_and_programs() {
132+
std::env::set_var("SBF_OUT_DIR", "../target/deploy");
133+
134+
let program_id = Pubkey::new_unique();
135+
let mollusk = Mollusk::new(&program_id, "test_program_primary");
136+
let mut context = mollusk.with_context(HashMap::new());
137+
138+
// `with_context` will already create program accounts, so assert our
139+
// main program already has an account in the store.
140+
{
141+
let store = context.account_store.borrow();
142+
let main_program_account = store
143+
.get(&program_id)
144+
.expect("Main program account should exist");
145+
assert_eq!(
146+
main_program_account.owner,
147+
solana_sdk_ids::bpf_loader_upgradeable::id()
148+
);
149+
assert!(main_program_account.executable);
150+
}
151+
152+
// Add another test program to the test environment.
153+
let other_program_id = Pubkey::new_unique();
154+
context.mollusk.add_program(
155+
&other_program_id,
156+
"test_program_cpi_target",
157+
&mollusk_svm::program::loader_keys::LOADER_V3,
158+
);
137159

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-
&[],
160+
// Use the "close account" test from our BPF program.
161+
let key = Pubkey::new_unique();
162+
context
163+
.account_store
164+
.borrow_mut()
165+
.insert(key, Account::new(50_000_000, 50, &program_id));
166+
let instruction = Instruction::new_with_bytes(
167+
program_id,
168+
&[3],
142169
vec![
143-
solana_instruction::AccountMeta::new_readonly(clock_pubkey, false),
144-
solana_instruction::AccountMeta::new(recipient, false),
170+
AccountMeta::new(key, true),
171+
AccountMeta::new(solana_sdk_ids::incinerator::id(), false),
172+
AccountMeta::new_readonly(solana_sdk_ids::system_program::id(), false),
173+
// Arbitrarily include the `Clock` sysvar account
174+
AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false),
175+
// Also include our additional program account
176+
AccountMeta::new_readonly(other_program_id, false),
145177
],
146178
);
179+
context.process_and_validate_instruction(&instruction, &[Check::success()]);
147180

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.
152181
let store = context.account_store.borrow();
153-
let clock_account = store.get(&clock_pubkey).expect("Clock sysvar should exist");
154182

155-
// Verify it has the expected owner.
183+
// Verify clock sysvar was loaded.
184+
let clock_account = store
185+
.get(&solana_sdk_ids::sysvar::clock::id())
186+
.expect("Clock sysvar should exist");
156187
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();
166188

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-
],
189+
// Verify our additional program was loaded.
190+
let additional_program_account = store
191+
.get(&other_program_id)
192+
.expect("Additional program account should exist");
193+
assert_eq!(
194+
additional_program_account.owner,
195+
mollusk_svm::program::loader_keys::LOADER_V3
178196
);
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);
197+
assert!(additional_program_account.executable);
193198
}
194199

195200
#[test]

result/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@ pub use {
4141
check::{AccountCheckBuilder, Check},
4242
compare::Compare,
4343
config::{CheckContext, Config},
44-
types::{ContextResult, InstructionResult, ProgramResult},
44+
types::{InstructionResult, ProgramResult},
4545
};

result/src/types.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,14 @@ pub enum ProgramResult {
1717
}
1818

1919
impl ProgramResult {
20+
/// Returns `true` if the program succeeded.
21+
pub fn is_ok(&self) -> bool {
22+
matches!(self, ProgramResult::Success)
23+
}
24+
2025
/// Returns `true` if the program returned an error.
2126
pub fn is_err(&self) -> bool {
22-
!matches!(self, ProgramResult::Success)
27+
!self.is_ok()
2328
}
2429
}
2530

@@ -90,19 +95,3 @@ impl InstructionResult {
9095
self.resulting_accounts = other.resulting_accounts;
9196
}
9297
}
93-
94-
/// The same return type as `InstructionResult`, but without the
95-
/// `resulting_accounts`. When working with the `MolluskContext`,
96-
/// developers can access resulting accounts from the account store directly.
97-
pub struct ContextResult {
98-
/// The number of compute units consumed by the instruction.
99-
pub compute_units_consumed: u64,
100-
/// The time taken to execute the instruction.
101-
pub execution_time: u64,
102-
/// The result code of the program's execution.
103-
pub program_result: ProgramResult,
104-
/// The raw result of the program's execution.
105-
pub raw_result: Result<(), InstructionError>,
106-
/// The return data produced by the instruction, if any.
107-
pub return_data: Vec<u8>,
108-
}

0 commit comments

Comments
 (0)