11use crate :: {
2- compact:: parse_compact_instructions ,
2+ compact:: { parse_compact_instructions_ref_with_len , CompactInstructionRef } ,
33 error:: AuthError ,
44 state:: { deferred:: DeferredExecAccount , AccountDiscriminator } ,
55} ;
@@ -99,14 +99,14 @@ pub fn process(
9999 return Err ( AuthError :: DeferredAuthorizationExpired . into ( ) ) ;
100100 }
101101
102- // Parse compact instructions
103- let compact_instructions = parse_compact_instructions ( instruction_data) ?;
102+ // Parse compact instructions and track consumed length. We hash the
103+ // raw instruction_data[..consumed] directly — the parse/encode format
104+ // is byte-identical, so there's no need to re-serialize.
105+ let ( compact_instructions, compact_len) =
106+ parse_compact_instructions_ref_with_len ( instruction_data) ?;
104107
105- // Serialize compact instructions to compute hash
106- let compact_bytes = crate :: compact:: serialize_compact_instructions ( & compact_instructions) ;
107-
108- // Verify instructions hash
109- let instructions_hash = compute_sha256 ( & compact_bytes) ;
108+ // Verify instructions hash against the exact bytes we parsed from
109+ let instructions_hash = compute_sha256 ( & instruction_data[ ..compact_len] ) ;
110110 if instructions_hash != deferred. instructions_hash {
111111 return Err ( AuthError :: DeferredHashMismatch . into ( ) ) ;
112112 }
@@ -140,46 +140,47 @@ pub fn process(
140140 let close_data = unsafe { deferred_pda. borrow_mut_data_unchecked ( ) } ;
141141 close_data. fill ( 0 ) ;
142142
143+ // Reuse Vecs across inner CPI iterations — allocated once, cleared +
144+ // repushed each iteration. Same optimisation as execute::immediate.
145+ const MAX_INNER_ACCOUNTS : usize = 32 ;
146+ let mut account_metas: Vec < AccountMeta > = Vec :: with_capacity ( MAX_INNER_ACCOUNTS ) ;
147+ let mut cpi_accounts: Vec < Account > = Vec :: with_capacity ( MAX_INNER_ACCOUNTS ) ;
148+
149+ let vault_bump_arr = [ vault_bump] ;
150+ let seeds = [
151+ Seed :: from ( b"vault" ) ,
152+ Seed :: from ( wallet_pda. key ( ) . as_ref ( ) ) ,
153+ Seed :: from ( & vault_bump_arr) ,
154+ ] ;
155+
143156 // Execute each compact instruction via CPI with vault PDA signing
144157 for compact_ix in & compact_instructions {
145158 let decompressed = compact_ix. decompress ( accounts) ?;
146159
147- // Build AccountMeta array
148- let account_metas: Vec < AccountMeta > = decompressed
149- . accounts
150- . iter ( )
151- . map ( |acc| AccountMeta {
152- pubkey : acc. key ( ) ,
153- is_signer : acc. is_signer ( ) || acc. key ( ) == vault_pda. key ( ) ,
154- is_writable : acc. is_writable ( ) ,
155- } )
156- . collect ( ) ;
157-
158160 // Prevent self-reentrancy
159161 if decompressed. program_id . as_ref ( ) == program_id. as_ref ( ) {
160162 return Err ( AuthError :: SelfReentrancyNotAllowed . into ( ) ) ;
161163 }
162164
165+ account_metas. clear ( ) ;
166+ cpi_accounts. clear ( ) ;
167+ for & acc in & decompressed. accounts {
168+ account_metas. push ( AccountMeta {
169+ pubkey : acc. key ( ) ,
170+ is_signer : acc. is_signer ( ) || acc. key ( ) == vault_pda. key ( ) ,
171+ is_writable : acc. is_writable ( ) ,
172+ } ) ;
173+ cpi_accounts. push ( Account :: from ( acc) ) ;
174+ }
175+
163176 let ix = Instruction {
164177 program_id : decompressed. program_id ,
165178 accounts : & account_metas,
166- data : & decompressed. data ,
179+ data : decompressed. data ,
167180 } ;
168181
169- let vault_bump_arr = [ vault_bump] ;
170- let seeds = [
171- Seed :: from ( b"vault" ) ,
172- Seed :: from ( wallet_pda. key ( ) . as_ref ( ) ) ,
173- Seed :: from ( & vault_bump_arr) ,
174- ] ;
175182 let signer: Signer = ( & seeds) . into ( ) ;
176183
177- let cpi_accounts: Vec < Account > = decompressed
178- . accounts
179- . iter ( )
180- . map ( |acc| Account :: from ( * acc) )
181- . collect ( ) ;
182-
183184 unsafe {
184185 invoke_signed_unchecked ( & ix, & cpi_accounts, & [ signer] ) ;
185186 }
@@ -209,26 +210,26 @@ fn compute_sha256(data: &[u8]) -> [u8; 32] {
209210}
210211
211212/// Compute SHA256 hash of all account pubkeys referenced by compact instructions.
212- /// Same logic as execute.rs ::compute_accounts_hash.
213+ /// Matches execute::immediate ::compute_accounts_hash.
213214fn compute_accounts_hash (
214215 accounts : & [ AccountInfo ] ,
215- compact_instructions : & [ crate :: compact :: CompactInstruction ] ,
216+ compact_instructions : & [ CompactInstructionRef < ' _ > ] ,
216217) -> Result < [ u8 ; 32 ] , ProgramError > {
217- let mut pubkeys_data = Vec :: new ( ) ;
218+ let mut refs : Vec < & [ u8 ] > = Vec :: with_capacity ( compact_instructions . len ( ) * 4 ) ;
218219
219220 for ix in compact_instructions {
220221 let program_idx = ix. program_id_index as usize ;
221222 if program_idx >= accounts. len ( ) {
222223 return Err ( ProgramError :: InvalidInstructionData ) ;
223224 }
224- pubkeys_data . extend_from_slice ( accounts[ program_idx] . key ( ) . as_ref ( ) ) ;
225+ refs . push ( accounts[ program_idx] . key ( ) . as_ref ( ) ) ;
225226
226- for & acc_idx in & ix. accounts {
227+ for & acc_idx in ix. accounts {
227228 let idx = acc_idx as usize ;
228229 if idx >= accounts. len ( ) {
229230 return Err ( ProgramError :: InvalidInstructionData ) ;
230231 }
231- pubkeys_data . extend_from_slice ( accounts[ idx] . key ( ) . as_ref ( ) ) ;
232+ refs . push ( accounts[ idx] . key ( ) . as_ref ( ) ) ;
232233 }
233234 }
234235
@@ -237,15 +238,15 @@ fn compute_accounts_hash(
237238 #[ cfg( target_os = "solana" ) ]
238239 unsafe {
239240 pinocchio:: syscalls:: sol_sha256 (
240- [ pubkeys_data . as_slice ( ) ] . as_ptr ( ) as * const u8 ,
241- 1 ,
241+ refs . as_ptr ( ) as * const u8 ,
242+ refs . len ( ) as u64 ,
242243 hash. as_mut_ptr ( ) ,
243244 ) ;
244245 }
245246 #[ cfg( not( target_os = "solana" ) ) ]
246247 {
247248 hash = [ 0xAA ; 32 ] ;
248- let _ = pubkeys_data ;
249+ let _ = refs ;
249250 }
250251
251252 Ok ( hash)
0 commit comments