11pub mod runner;
22
3+ // Bring crates into scope
34use mollusk_svm:: { program:: keyed_account_for_system_program, Mollusk } ;
45use solana_account:: Account ;
56use solana_instruction:: { AccountMeta , Instruction } ;
7+ use solana_program;
8+ // Imports needed for SlotHashes construction
9+ // Use correct paths for 1.18
10+ use solana_program:: clock:: Slot ;
11+ use solana_program:: hash:: Hash ;
12+ // SlotHash is a type alias (Slot, Hash)
13+ use solana_program:: slot_hashes:: SlotHash ;
14+ // Use Sysvar ID from solana_program
15+ use solana_program:: sysvar:: ID as SYSVAR_PROGRAM_ID ;
616use solana_pubkey:: Pubkey ;
717use std:: vec;
818
19+ // Enum to control slot decrement behavior in mock data generation
20+ #[ derive( Clone , Copy , Debug ) ]
21+ enum DecrementStrategy {
22+ Strictly1 ,
23+ Average1_05 ,
24+ Average2 ,
25+ }
26+
927pub const BASE_LAMPORTS : u64 = 2_000_000_000u64 ;
28+ const NUM_BENCH_SLOT_HASH_ENTRIES : usize = 512 ;
29+ const BENCH_SLOT_HASH_START_SLOT : u64 = 10000 ;
30+
31+ // Simple deterministic PRNG for varied decrements
32+ // Using a basic Lehmer / MINSTD generator approach
33+ fn simple_prng ( seed : u64 ) -> u64 {
34+ const A : u64 = 16807 ; // Multiplier
35+ const M : u64 = 2147483647 ; // Modulus (2^31 - 1)
36+ let initial_state = if seed == 0 { 1 } else { seed } ;
37+ ( A . wrapping_mul ( initial_state) ) % M
38+ }
1039
1140/// Create a new Mollusk instance for the given program ID and name.
1241pub fn setup ( program_id : & Pubkey , name : & ' static str ) -> Mollusk {
@@ -17,12 +46,23 @@ pub fn setup(program_id: &Pubkey, name: &'static str) -> Mollusk {
1746}
1847
1948/// Instructions on the program to be executed.
49+ #[ derive( Clone , Copy , Debug ) ]
2050pub enum ProgramInstruction {
2151 Ping ,
2252 Log ,
2353 Account { expected : u64 } ,
2454 CreateAccount ,
2555 Transfer ,
56+ // --- SlotHashes (Safe/Checked Path - Primarily for SDK/Nostd now) ---
57+ SlotHashesGetEntryChecked , // ID 5
58+ SlotHashesGetHashChecked , /* ID 6 (Treat Interpolated/Midpoint same for SDK/Nostd manual
59+ * search) */
60+ SlotHashesPositionChecked , // ID 7
61+ // --- SlotHashes (Unsafe/Unchecked Path - For Pinocchio Benchmarking) ---
62+ SlotHashesGetEntryUnchecked , // ID 8
63+ SlotHashesGetHashInterpolatedUnchecked , // ID 9
64+ SlotHashesPositionInterpolatedUnchecked { target_slot : Slot } , // ID 10 <- Takes Slot
65+ SlotHashesPositionNaiveUnchecked { target_slot : Slot } , // ID 11 <- Takes Slot
2666}
2767
2868/// Returns the instruction data for the given instruction.
@@ -38,6 +78,27 @@ pub fn instruction_data(instruction: ProgramInstruction) -> Vec<u8> {
3878 }
3979 ProgramInstruction :: CreateAccount => vec ! [ 3 ] ,
4080 ProgramInstruction :: Transfer => vec ! [ 4 ] ,
81+ // Checked path instructions (match SDK/Nostd implementation)
82+ ProgramInstruction :: SlotHashesGetEntryChecked => vec ! [ 5 ] ,
83+ ProgramInstruction :: SlotHashesGetHashChecked => vec ! [ 6 ] ,
84+ ProgramInstruction :: SlotHashesPositionChecked => vec ! [ 7 ] ,
85+ // Unchecked path instructions (Pinocchio specific tests)
86+ ProgramInstruction :: SlotHashesGetEntryUnchecked => vec ! [ 8 ] ,
87+ ProgramInstruction :: SlotHashesGetHashInterpolatedUnchecked => vec ! [ 9 ] ,
88+ ProgramInstruction :: SlotHashesPositionInterpolatedUnchecked { target_slot } => {
89+ let mut data = Vec :: with_capacity ( 1 + 8 ) ;
90+ data. push ( 10 ) ;
91+ data. extend_from_slice ( & target_slot. to_le_bytes ( ) ) ;
92+ data
93+ }
94+ ProgramInstruction :: SlotHashesPositionNaiveUnchecked { target_slot } => {
95+ let mut data = Vec :: with_capacity ( 1 + 8 ) ;
96+ data. push ( 11 ) ;
97+ data. extend_from_slice ( & target_slot. to_le_bytes ( ) ) ;
98+ data
99+ }
100+ // Default/Error case for unused IDs - or handle in processor
101+ _ => vec ! [ 255 ] , // Or panic, or specific error instruction
41102 }
42103}
43104
@@ -50,6 +111,47 @@ pub fn generate_pubkeys(count: usize) -> Vec<Pubkey> {
50111 keys
51112}
52113
114+ /// Helper function to generate more realistic SlotHashes data
115+ fn generate_mock_slot_hashes_data ( strategy : DecrementStrategy ) -> Vec < ( u64 , [ u8 ; 32 ] ) > {
116+ let mut entries = Vec :: with_capacity ( NUM_BENCH_SLOT_HASH_ENTRIES ) ;
117+ let mut current_slot = BENCH_SLOT_HASH_START_SLOT ;
118+
119+ for i in 0 ..NUM_BENCH_SLOT_HASH_ENTRIES {
120+ let hash_byte = ( i % 256 ) as u8 ;
121+ let hash = [ hash_byte; 32 ] ;
122+ entries. push ( ( current_slot, hash) ) ;
123+
124+ let random_val = simple_prng ( i as u64 ) ;
125+ let decrement = match strategy {
126+ DecrementStrategy :: Strictly1 => 1 ,
127+ DecrementStrategy :: Average1_05 => {
128+ if random_val % 20 == 0 {
129+ 2
130+ } else {
131+ 1
132+ }
133+ }
134+ DecrementStrategy :: Average2 => {
135+ if random_val % 2 == 0 {
136+ 1
137+ } else {
138+ 3
139+ }
140+ }
141+ } ;
142+
143+ // Calculate next slot and check for saturation/no change
144+ let next_slot = current_slot. saturating_sub ( decrement) ;
145+ if next_slot == current_slot {
146+ // If next slot is same as current (i.e., saturated at 0 or decrement was 0),
147+ // stop generating to ensure strict monotonicity.
148+ break ;
149+ }
150+ current_slot = next_slot; // Update for the *next* iteration
151+ }
152+ entries
153+ }
154+
53155/// Generates the instruction data and accounts for the
54156/// `ProgramInstruction::Account` instruction.
55157fn generate_account ( program_id : Pubkey , expected : u64 ) -> ( Instruction , Vec < ( Pubkey , Account ) > ) {
@@ -154,3 +256,99 @@ fn generate_transfer(program_id: Pubkey) -> (Instruction, Vec<(Pubkey, Account)>
154256 accounts,
155257 )
156258}
259+
260+ /// Generates the instruction data and accounts for the SlotHashes instructions
261+ /// (SDK version).
262+ fn generate_sdk_slot_hashes_ix (
263+ program_id : Pubkey ,
264+ ix_type : ProgramInstruction ,
265+ strategy : DecrementStrategy ,
266+ ) -> ( Instruction , Vec < ( Pubkey , Account ) > ) {
267+ // Use the well-known ID directly to avoid sdk dependency
268+ let sysvar_id = solana_pubkey:: Pubkey :: new_from_array ( [
269+ 6 , 167 , 213 , 23 , 25 , 47 , 10 , 175 , 198 , 242 , 101 , 227 , 251 , 119 , 204 , 122 , 218 , 130 , 197 ,
270+ 41 , 208 , 190 , 59 , 19 , 110 , 45 , 0 , 85 , 32 , 0 , 0 , 0 ,
271+ ] ) ;
272+
273+ // Generate realistic mock SlotHashes data
274+ let mock_entries_raw = generate_mock_slot_hashes_data ( strategy) ;
275+
276+ // Manually serialize mock data according to layout: u64 len + [(u64 slot, [u8;
277+ // 32] hash)] (Using u64 len for consistency, as prefix didn't cause the
278+ // UnsupportedSysvar error)
279+ let num_entries = mock_entries_raw. len ( ) as u64 ;
280+ let mut data = Vec :: with_capacity ( 8 + mock_entries_raw. len ( ) * ( 8 + 32 ) ) ; // Use 8 for u64 len
281+ data. extend_from_slice ( & ( num_entries as u64 ) . to_le_bytes ( ) ) ;
282+ for ( slot, hash) in & mock_entries_raw {
283+ data. extend_from_slice ( & slot. to_le_bytes ( ) ) ;
284+ data. extend_from_slice ( hash) ;
285+ }
286+
287+ // Create the sysvar account owned by the Sysvar Program ID
288+ let mut sysvar_account = Account :: new ( 1 , data. len ( ) , & SYSVAR_PROGRAM_ID ) ;
289+ sysvar_account. data = data;
290+ sysvar_account. executable = false ; // Sysvars aren't executable
291+
292+ let accounts = vec ! [ ( sysvar_id, sysvar_account) ] ;
293+
294+ let account_metas = vec ! [ AccountMeta :: new_readonly( sysvar_id, false ) ] ;
295+
296+ (
297+ Instruction {
298+ program_id,
299+ accounts : account_metas,
300+ data : instruction_data ( ix_type) ,
301+ } ,
302+ accounts,
303+ )
304+ }
305+
306+ /// Generates the instruction data and accounts for the SlotHashes instructions
307+ /// (Pinocchio version).
308+ fn generate_pinocchio_slot_hashes_ix (
309+ program_id : Pubkey ,
310+ // ix_type now includes the target slot if needed
311+ ix_variant : ProgramInstruction ,
312+ strategy : DecrementStrategy ,
313+ // Pass the specific target_slot value IF the instruction needs it.
314+ // We'll determine this target_slot in the runner.rs logic.
315+ // Let's keep the function signature simpler for now and derive target inside if needed,
316+ // or modify runner.rs to pass it only when ix_variant requires it.
317+ // Simpler: Let runner.rs handle providing the full ix_variant including the target slot.
318+ ) -> ( Instruction , Vec < ( Pubkey , Account ) > ) {
319+ let sysvar_id = solana_pubkey:: Pubkey :: new_from_array ( [
320+ 6 , 167 , 213 , 23 , 25 , 47 , 10 , 175 , 198 , 242 , 101 , 227 , 251 , 119 , 204 , 122 , 218 , 130 , 197 ,
321+ 41 , 208 , 190 , 59 , 19 , 110 , 45 , 0 , 85 , 32 , 0 , 0 , 0 ,
322+ ] ) ;
323+
324+ // Generate realistic mock SlotHashes data - needed for account setup
325+ let mock_entries = generate_mock_slot_hashes_data ( strategy) ;
326+
327+ let num_entries = mock_entries. len ( ) as u64 ;
328+ let mut account_data = Vec :: with_capacity ( 8 + mock_entries. len ( ) * ( 8 + 32 ) ) ;
329+ account_data. extend_from_slice ( & ( num_entries as u64 ) . to_le_bytes ( ) ) ;
330+ for ( slot, hash) in & mock_entries {
331+ account_data. extend_from_slice ( & slot. to_le_bytes ( ) ) ;
332+ account_data. extend_from_slice ( hash) ;
333+ }
334+
335+ // Create the sysvar account
336+ let mut sysvar_account = Account :: new ( 1 , account_data. len ( ) , & SYSVAR_PROGRAM_ID ) ;
337+ sysvar_account. data = account_data;
338+ sysvar_account. executable = false ;
339+
340+ let accounts = vec ! [ ( sysvar_id, sysvar_account) ] ;
341+ let account_metas = vec ! [ AccountMeta :: new_readonly( sysvar_id, false ) ] ;
342+
343+ // Generate instruction data using the provided full ix_variant
344+ let instruction_bytes = instruction_data ( ix_variant) ;
345+
346+ (
347+ Instruction {
348+ program_id,
349+ accounts : account_metas,
350+ data : instruction_bytes,
351+ } ,
352+ accounts,
353+ )
354+ }
0 commit comments