1#![allow(clippy::integer_arithmetic)]
2
3use {
4 byteorder::{ByteOrder, LittleEndian},
5 solana_rbpf::{
6 aligned_memory::{AlignedMemory, Pod},
7 ebpf::{HOST_ALIGN, MM_INPUT_START},
8 memory_region::MemoryRegion,
9 },
10 solana_sdk::{
11 bpf_loader_deprecated,
12 entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
13 instruction::InstructionError,
14 pubkey::Pubkey,
15 system_instruction::MAX_PERMITTED_DATA_LENGTH,
16 transaction_context::{
17 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
18 },
19 },
20 std::{mem, mem::size_of},
21};
22
23const MAX_INSTRUCTION_ACCOUNTS: u8 = NON_DUP_MARKER;
26
27enum SerializeAccount<'a> {
28 Account(IndexOfAccount, BorrowedAccount<'a>),
29 Duplicate(IndexOfAccount),
30}
31
32struct Serializer {
33 pub buffer: AlignedMemory<HOST_ALIGN>,
34 regions: Vec<MemoryRegion>,
35 vaddr: u64,
36 region_start: usize,
37 aligned: bool,
38}
39
40impl Serializer {
41 fn new(size: usize, start_addr: u64, aligned: bool) -> Serializer {
42 Serializer {
43 buffer: AlignedMemory::with_capacity(size),
44 regions: Vec::new(),
45 region_start: 0,
46 vaddr: start_addr,
47 aligned,
48 }
49 }
50
51 fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
52 self.buffer.fill_write(num, value)
53 }
54
55 pub fn write<T: Pod>(&mut self, value: T) {
56 self.debug_assert_alignment::<T>();
57 unsafe {
66 self.buffer.write_unchecked(value);
67 }
68 }
69
70 fn write_all(&mut self, value: &[u8]) {
71 unsafe {
74 self.buffer.write_all_unchecked(value);
75 }
76 }
77
78 fn write_account(&mut self, account: &BorrowedAccount<'_>) -> Result<(), InstructionError> {
79 self.write_all(account.get_data());
80
81 if self.aligned {
82 let align_offset =
83 (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
84 self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
85 .map_err(|_| InstructionError::InvalidArgument)?;
86 }
87
88 Ok(())
89 }
90
91 fn push_region(&mut self) {
92 let range = self.region_start..self.buffer.len();
93 let region = MemoryRegion::new_writable(
94 self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
95 self.vaddr,
96 );
97 self.regions.push(region);
98 self.region_start = range.end;
99 self.vaddr += range.len() as u64;
100 }
101
102 fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
103 self.push_region();
104 debug_assert_eq!(self.region_start, self.buffer.len());
105 (self.buffer, self.regions)
106 }
107
108 fn debug_assert_alignment<T>(&self) {
109 debug_assert!(
110 !self.aligned
111 || self
112 .buffer
113 .as_slice()
114 .as_ptr_range()
115 .end
116 .align_offset(mem::align_of::<T>())
117 == 0
118 );
119 }
120}
121
122pub fn serialize_parameters(
123 transaction_context: &TransactionContext,
124 instruction_context: &InstructionContext,
125 should_cap_ix_accounts: bool,
126) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>, Vec<usize>), InstructionError> {
127 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
128 if should_cap_ix_accounts && num_ix_accounts > MAX_INSTRUCTION_ACCOUNTS as IndexOfAccount {
129 return Err(InstructionError::MaxAccountsExceeded);
130 }
131 let is_loader_deprecated = *instruction_context
132 .try_borrow_last_program_account(transaction_context)?
133 .get_owner()
134 == bpf_loader_deprecated::id();
135
136 let num_accounts = instruction_context.get_number_of_instruction_accounts() as usize;
137 let mut accounts = Vec::with_capacity(num_accounts);
138 let mut account_lengths: Vec<usize> = Vec::with_capacity(num_accounts);
139 for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
140 if let Some(index) =
141 instruction_context.is_instruction_account_duplicate(instruction_account_index)?
142 {
143 accounts.push(SerializeAccount::Duplicate(index));
144 account_lengths.push(*account_lengths.get(index as usize).unwrap());
147 } else {
148 let account = instruction_context
149 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
150 account_lengths.push(account.get_data().len());
151 accounts.push(SerializeAccount::Account(
152 instruction_account_index,
153 account,
154 ));
155 };
156 }
157
158 if is_loader_deprecated {
159 serialize_parameters_unaligned(transaction_context, instruction_context, accounts)
160 } else {
161 serialize_parameters_aligned(transaction_context, instruction_context, accounts)
162 }
163 .map(|(buffer, regions)| (buffer, regions, account_lengths))
164}
165
166pub fn deserialize_parameters(
167 transaction_context: &TransactionContext,
168 instruction_context: &InstructionContext,
169 buffer: &[u8],
170 account_lengths: &[usize],
171) -> Result<(), InstructionError> {
172 let is_loader_deprecated = *instruction_context
173 .try_borrow_last_program_account(transaction_context)?
174 .get_owner()
175 == bpf_loader_deprecated::id();
176 if is_loader_deprecated {
177 deserialize_parameters_unaligned(
178 transaction_context,
179 instruction_context,
180 buffer,
181 account_lengths,
182 )
183 } else {
184 deserialize_parameters_aligned(
185 transaction_context,
186 instruction_context,
187 buffer,
188 account_lengths,
189 )
190 }
191}
192
193fn serialize_parameters_unaligned(
194 transaction_context: &TransactionContext,
195 instruction_context: &InstructionContext,
196 accounts: Vec<SerializeAccount>,
197) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
198 let mut size = size_of::<u64>();
200 for account in &accounts {
201 size += 1; match account {
203 SerializeAccount::Duplicate(_) => {}
204 SerializeAccount::Account(_, account) => {
205 size += size_of::<u8>() + size_of::<u8>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + account.get_data().len() + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
215 }
216 }
217 size += size_of::<u64>() + instruction_context.get_instruction_data().len() + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, false);
222
223 s.write::<u64>((accounts.len() as u64).to_le());
224 for account in accounts {
225 match account {
226 SerializeAccount::Duplicate(position) => s.write(position as u8),
227 SerializeAccount::Account(_, account) => {
228 s.write::<u8>(NON_DUP_MARKER);
229 s.write::<u8>(account.is_signer() as u8);
230 s.write::<u8>(account.is_writable() as u8);
231 s.write_all(account.get_key().as_ref());
232 s.write::<u64>(account.get_lamports().to_le());
233 s.write::<u64>((account.get_data().len() as u64).to_le());
234 s.write_account(&account)
235 .map_err(|_| InstructionError::InvalidArgument)?;
236 s.write_all(account.get_owner().as_ref());
237 s.write::<u8>(account.is_executable() as u8);
238 s.write::<u64>((account.get_rent_epoch()).to_le());
239 }
240 };
241 }
242 s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
243 s.write_all(instruction_context.get_instruction_data());
244 s.write_all(
245 instruction_context
246 .try_borrow_last_program_account(transaction_context)?
247 .get_key()
248 .as_ref(),
249 );
250
251 Ok(s.finish())
252}
253
254pub fn deserialize_parameters_unaligned(
255 transaction_context: &TransactionContext,
256 instruction_context: &InstructionContext,
257 buffer: &[u8],
258 account_lengths: &[usize],
259) -> Result<(), InstructionError> {
260 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
262 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths.iter())
263 {
264 let duplicate =
265 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
266 start += 1; if duplicate.is_none() {
268 let mut borrowed_account = instruction_context
269 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
270 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = LittleEndian::read_u64(
274 buffer
275 .get(start..)
276 .ok_or(InstructionError::InvalidArgument)?,
277 );
278 if borrowed_account.get_lamports() != lamports {
279 borrowed_account.set_lamports(lamports)?;
280 }
281 start += size_of::<u64>() + size_of::<u64>(); let data = buffer
284 .get(start..start + pre_len)
285 .ok_or(InstructionError::InvalidArgument)?;
286 match borrowed_account
288 .can_data_be_resized(data.len())
289 .and_then(|_| borrowed_account.can_data_be_changed())
290 {
291 Ok(()) => borrowed_account.set_data_from_slice(data)?,
292 Err(err) if borrowed_account.get_data() != data => return Err(err),
293 _ => {}
294 }
295 start += pre_len + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
300 }
301 Ok(())
302}
303
304fn serialize_parameters_aligned(
305 transaction_context: &TransactionContext,
306 instruction_context: &InstructionContext,
307 accounts: Vec<SerializeAccount>,
308) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
309 let mut size = size_of::<u64>();
311 for account in &accounts {
312 size += 1; match account {
314 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
316 let data_len = account.get_data().len();
317 size += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + data_len
326 + MAX_PERMITTED_DATA_INCREASE
327 + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
328 + size_of::<u64>(); }
330 }
331 }
332 size += size_of::<u64>() + instruction_context.get_instruction_data().len()
334 + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, true);
337
338 s.write::<u64>((accounts.len() as u64).to_le());
340 for account in accounts {
341 match account {
342 SerializeAccount::Account(_, borrowed_account) => {
343 s.write::<u8>(NON_DUP_MARKER);
344 s.write::<u8>(borrowed_account.is_signer() as u8);
345 s.write::<u8>(borrowed_account.is_writable() as u8);
346 s.write::<u8>(borrowed_account.is_executable() as u8);
347 s.write_all(&[0u8, 0, 0, 0]);
348 s.write_all(borrowed_account.get_key().as_ref());
349 s.write_all(borrowed_account.get_owner().as_ref());
350 s.write::<u64>(borrowed_account.get_lamports().to_le());
351 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
352 s.write_account(&borrowed_account)
353 .map_err(|_| InstructionError::InvalidArgument)?;
354 s.write::<u64>((borrowed_account.get_rent_epoch()).to_le());
355 }
356 SerializeAccount::Duplicate(position) => {
357 s.write::<u8>(position as u8);
358 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
359 }
360 };
361 }
362 s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
363 s.write_all(instruction_context.get_instruction_data());
364 s.write_all(
365 instruction_context
366 .try_borrow_last_program_account(transaction_context)?
367 .get_key()
368 .as_ref(),
369 );
370
371 Ok(s.finish())
372}
373
374pub fn deserialize_parameters_aligned(
375 transaction_context: &TransactionContext,
376 instruction_context: &InstructionContext,
377 buffer: &[u8],
378 account_lengths: &[usize],
379) -> Result<(), InstructionError> {
380 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in
382 (0..instruction_context.get_number_of_instruction_accounts()).zip(account_lengths.iter())
383 {
384 let duplicate =
385 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
386 start += size_of::<u8>(); if duplicate.is_some() {
388 start += 7; } else {
390 let mut borrowed_account = instruction_context
391 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
392 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
398 .get(start..start + size_of::<Pubkey>())
399 .ok_or(InstructionError::InvalidArgument)?;
400 start += size_of::<Pubkey>(); let lamports = LittleEndian::read_u64(
402 buffer
403 .get(start..)
404 .ok_or(InstructionError::InvalidArgument)?,
405 );
406 if borrowed_account.get_lamports() != lamports {
407 borrowed_account.set_lamports(lamports)?;
408 }
409 start += size_of::<u64>(); let post_len = LittleEndian::read_u64(
411 buffer
412 .get(start..)
413 .ok_or(InstructionError::InvalidArgument)?,
414 ) as usize;
415 start += size_of::<u64>(); if post_len.saturating_sub(*pre_len) > MAX_PERMITTED_DATA_INCREASE
417 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
418 {
419 return Err(InstructionError::InvalidRealloc);
420 }
421 let data_end = start + post_len;
422 let data = buffer
423 .get(start..data_end)
424 .ok_or(InstructionError::InvalidArgument)?;
425 match borrowed_account
427 .can_data_be_resized(data.len())
428 .and_then(|_| borrowed_account.can_data_be_changed())
429 {
430 Ok(()) => borrowed_account.set_data_from_slice(data)?,
431 Err(err) if borrowed_account.get_data() != data => return Err(err),
432 _ => {}
433 }
434 start += *pre_len + MAX_PERMITTED_DATA_INCREASE; start += (start as *const u8).align_offset(BPF_ALIGN_OF_U128);
436 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
438 borrowed_account.set_owner(owner)?;
440 }
441 }
442 }
443 Ok(())
444}
445
446#[cfg(test)]
447mod tests {
448 use {
449 super::*,
450 solana_program_runtime::invoke_context::{prepare_mock_invoke_context, InvokeContext},
451 solana_sdk::{
452 account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
453 account_info::AccountInfo,
454 bpf_loader,
455 entrypoint::deserialize,
456 instruction::AccountMeta,
457 sysvar::rent::Rent,
458 },
459 std::{
460 cell::RefCell,
461 rc::Rc,
462 slice::{from_raw_parts, from_raw_parts_mut},
463 },
464 };
465
466 #[test]
467 fn test_serialize_parameters_with_many_accounts() {
468 struct TestCase {
469 num_ix_accounts: usize,
470 append_dup_account: bool,
471 should_cap_ix_accounts: bool,
472 expected_err: Option<InstructionError>,
473 name: &'static str,
474 }
475
476 for TestCase {
477 num_ix_accounts,
478 append_dup_account,
479 should_cap_ix_accounts,
480 expected_err,
481 name,
482 } in [
483 TestCase {
484 name: "serialize max accounts without cap",
485 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
486 should_cap_ix_accounts: false,
487 append_dup_account: false,
488 expected_err: None,
489 },
490 TestCase {
491 name: "serialize max accounts and append dup without cap",
492 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
493 should_cap_ix_accounts: false,
494 append_dup_account: true,
495 expected_err: None,
496 },
497 TestCase {
498 name: "serialize max accounts with cap",
499 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
500 should_cap_ix_accounts: true,
501 append_dup_account: false,
502 expected_err: None,
503 },
504 TestCase {
505 name: "serialize too many accounts with cap",
506 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
507 should_cap_ix_accounts: true,
508 append_dup_account: false,
509 expected_err: Some(InstructionError::MaxAccountsExceeded),
510 },
511 TestCase {
512 name: "serialize too many accounts and append dup with cap",
513 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
514 should_cap_ix_accounts: true,
515 append_dup_account: true,
516 expected_err: Some(InstructionError::MaxAccountsExceeded),
517 },
518 ] {
529 let program_id = solana_sdk::pubkey::new_rand();
530 let mut transaction_accounts = vec![(
531 program_id,
532 AccountSharedData::from(Account {
533 lamports: 0,
534 data: vec![],
535 owner: bpf_loader::id(),
536 executable: true,
537 rent_epoch: 0,
538 }),
539 )];
540
541 let instruction_account_keys: Vec<Pubkey> =
542 (0..num_ix_accounts).map(|_| Pubkey::new_unique()).collect();
543
544 for key in &instruction_account_keys {
545 transaction_accounts.push((
546 *key,
547 AccountSharedData::from(Account {
548 lamports: 0,
549 data: vec![],
550 owner: program_id,
551 executable: false,
552 rent_epoch: 0,
553 }),
554 ));
555 }
556
557 let mut instruction_account_metas: Vec<_> = instruction_account_keys
558 .iter()
559 .map(|key| AccountMeta::new_readonly(*key, false))
560 .collect();
561 if append_dup_account {
562 instruction_account_metas.push(instruction_account_metas.last().cloned().unwrap());
563 }
564
565 let program_indices = [0];
566 let instruction_accounts = prepare_mock_invoke_context(
567 transaction_accounts.clone(),
568 instruction_account_metas,
569 &program_indices,
570 )
571 .instruction_accounts;
572 let instruction_data = vec![];
573
574 let mut transaction_context =
575 TransactionContext::new(transaction_accounts, Some(Rent::default()), 1, 1);
576 transaction_context
577 .get_next_instruction_context()
578 .unwrap()
579 .configure(&program_indices, &instruction_accounts, &instruction_data);
580 transaction_context.push().unwrap();
581 let instruction_context = transaction_context
582 .get_instruction_context_at_index_in_trace(0)
583 .unwrap();
584
585 let serialization_result = serialize_parameters(
586 &transaction_context,
587 instruction_context,
588 should_cap_ix_accounts,
589 );
590 assert_eq!(
591 serialization_result.as_ref().err(),
592 expected_err.as_ref(),
593 "{name} test case failed"
594 );
595 if expected_err.is_some() {
596 continue;
597 }
598
599 let (mut serialized, _regions, _account_lengths) = serialization_result.unwrap();
600 let (de_program_id, de_accounts, de_instruction_data) =
601 unsafe { deserialize(serialized.as_slice_mut().first_mut().unwrap() as *mut u8) };
602 assert_eq!(de_program_id, &program_id);
603 assert_eq!(de_instruction_data, &instruction_data);
604 for (index, account_info) in de_accounts.into_iter().enumerate() {
605 let ix_account = &instruction_accounts.get(index).unwrap();
606 assert_eq!(
607 account_info.key,
608 transaction_context
609 .get_key_of_account_at_index(ix_account.index_in_transaction)
610 .unwrap()
611 );
612 assert_eq!(account_info.owner, &program_id);
613 assert!(!account_info.executable);
614 assert!(account_info.data_is_empty());
615 assert!(!account_info.is_writable);
616 assert!(!account_info.is_signer);
617 }
618 }
619 }
620
621 #[test]
622 fn test_serialize_parameters() {
623 let program_id = solana_sdk::pubkey::new_rand();
624 let transaction_accounts = vec![
625 (
626 program_id,
627 AccountSharedData::from(Account {
628 lamports: 0,
629 data: vec![],
630 owner: bpf_loader::id(),
631 executable: true,
632 rent_epoch: 0,
633 }),
634 ),
635 (
636 solana_sdk::pubkey::new_rand(),
637 AccountSharedData::from(Account {
638 lamports: 1,
639 data: vec![1u8, 2, 3, 4, 5],
640 owner: bpf_loader::id(),
641 executable: false,
642 rent_epoch: 100,
643 }),
644 ),
645 (
646 solana_sdk::pubkey::new_rand(),
647 AccountSharedData::from(Account {
648 lamports: 2,
649 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
650 owner: bpf_loader::id(),
651 executable: true,
652 rent_epoch: 200,
653 }),
654 ),
655 (
656 solana_sdk::pubkey::new_rand(),
657 AccountSharedData::from(Account {
658 lamports: 3,
659 data: vec![],
660 owner: bpf_loader::id(),
661 executable: false,
662 rent_epoch: 3100,
663 }),
664 ),
665 (
666 solana_sdk::pubkey::new_rand(),
667 AccountSharedData::from(Account {
668 lamports: 4,
669 data: vec![1u8, 2, 3, 4, 5],
670 owner: bpf_loader::id(),
671 executable: false,
672 rent_epoch: 100,
673 }),
674 ),
675 (
676 solana_sdk::pubkey::new_rand(),
677 AccountSharedData::from(Account {
678 lamports: 5,
679 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
680 owner: bpf_loader::id(),
681 executable: true,
682 rent_epoch: 200,
683 }),
684 ),
685 (
686 solana_sdk::pubkey::new_rand(),
687 AccountSharedData::from(Account {
688 lamports: 6,
689 data: vec![],
690 owner: bpf_loader::id(),
691 executable: false,
692 rent_epoch: 3100,
693 }),
694 ),
695 ];
696 let instruction_accounts = [1, 1, 2, 3, 4, 4, 5, 6]
697 .into_iter()
698 .enumerate()
699 .map(
700 |(instruction_account_index, index_in_transaction)| AccountMeta {
701 pubkey: transaction_accounts.get(index_in_transaction).unwrap().0,
702 is_signer: false,
703 is_writable: instruction_account_index >= 4,
704 },
705 )
706 .collect();
707 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
708 let program_indices = [0];
709 let mut original_accounts = transaction_accounts.clone();
710 let preparation = prepare_mock_invoke_context(
711 transaction_accounts,
712 instruction_accounts,
713 &program_indices,
714 );
715 let mut transaction_context = TransactionContext::new(
716 preparation.transaction_accounts,
717 Some(Rent::default()),
718 1,
719 1,
720 );
721 let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
722 invoke_context
723 .transaction_context
724 .get_next_instruction_context()
725 .unwrap()
726 .configure(
727 &program_indices,
728 &preparation.instruction_accounts,
729 &instruction_data,
730 );
731 invoke_context.push().unwrap();
732 let instruction_context = invoke_context
733 .transaction_context
734 .get_current_instruction_context()
735 .unwrap();
736
737 let (mut serialized, _regions, account_lengths) = serialize_parameters(
740 invoke_context.transaction_context,
741 instruction_context,
742 true,
743 )
744 .unwrap();
745
746 let (de_program_id, de_accounts, de_instruction_data) =
747 unsafe { deserialize(serialized.as_slice_mut().first_mut().unwrap() as *mut u8) };
748
749 assert_eq!(&program_id, de_program_id);
750 assert_eq!(instruction_data, de_instruction_data);
751 assert_eq!(
752 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
753 0
754 );
755 for account_info in de_accounts {
756 let index_in_transaction = invoke_context
757 .transaction_context
758 .find_index_of_account(account_info.key)
759 .unwrap();
760 let account = invoke_context
761 .transaction_context
762 .get_account_at_index(index_in_transaction)
763 .unwrap()
764 .borrow();
765 assert_eq!(account.lamports(), account_info.lamports());
766 assert_eq!(account.data(), &account_info.data.borrow()[..]);
767 assert_eq!(account.owner(), account_info.owner);
768 assert_eq!(account.executable(), account_info.executable);
769 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
770
771 assert_eq!(
772 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
773 0
774 );
775 assert_eq!(
776 account_info
777 .data
778 .borrow()
779 .as_ptr()
780 .align_offset(BPF_ALIGN_OF_U128),
781 0
782 );
783 }
784
785 deserialize_parameters(
786 invoke_context.transaction_context,
787 instruction_context,
788 serialized.as_slice(),
789 &account_lengths,
790 )
791 .unwrap();
792 for (index_in_transaction, (_key, original_account)) in original_accounts.iter().enumerate()
793 {
794 let account = invoke_context
795 .transaction_context
796 .get_account_at_index(index_in_transaction as IndexOfAccount)
797 .unwrap()
798 .borrow();
799 assert_eq!(&*account, original_account);
800 }
801
802 original_accounts
804 .first_mut()
805 .unwrap()
806 .1
807 .set_owner(bpf_loader_deprecated::id());
808 invoke_context
809 .transaction_context
810 .get_account_at_index(0)
811 .unwrap()
812 .borrow_mut()
813 .set_owner(bpf_loader_deprecated::id());
814
815 let (mut serialized, _regions, account_lengths) = serialize_parameters(
816 invoke_context.transaction_context,
817 instruction_context,
818 true,
819 )
820 .unwrap();
821
822 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
823 deserialize_unaligned(serialized.as_slice_mut().first_mut().unwrap() as *mut u8)
824 };
825 assert_eq!(&program_id, de_program_id);
826 assert_eq!(instruction_data, de_instruction_data);
827 for account_info in de_accounts {
828 let index_in_transaction = invoke_context
829 .transaction_context
830 .find_index_of_account(account_info.key)
831 .unwrap();
832 let account = invoke_context
833 .transaction_context
834 .get_account_at_index(index_in_transaction)
835 .unwrap()
836 .borrow();
837 assert_eq!(account.lamports(), account_info.lamports());
838 assert_eq!(account.data(), &account_info.data.borrow()[..]);
839 assert_eq!(account.owner(), account_info.owner);
840 assert_eq!(account.executable(), account_info.executable);
841 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
842 }
843
844 deserialize_parameters(
845 invoke_context.transaction_context,
846 instruction_context,
847 serialized.as_slice(),
848 &account_lengths,
849 )
850 .unwrap();
851 for (index_in_transaction, (_key, original_account)) in original_accounts.iter().enumerate()
852 {
853 let account = invoke_context
854 .transaction_context
855 .get_account_at_index(index_in_transaction as IndexOfAccount)
856 .unwrap()
857 .borrow();
858 assert_eq!(&*account, original_account);
859 }
860 }
861
862 #[allow(clippy::type_complexity)]
864 pub unsafe fn deserialize_unaligned<'a>(
865 input: *mut u8,
866 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
867 let mut offset: usize = 0;
868
869 #[allow(clippy::cast_ptr_alignment)]
872 let num_accounts = *(input.add(offset) as *const u64) as usize;
873 offset += size_of::<u64>();
874
875 let mut accounts = Vec::with_capacity(num_accounts);
878 for _ in 0..num_accounts {
879 let dup_info = *(input.add(offset) as *const u8);
880 offset += size_of::<u8>();
881 if dup_info == NON_DUP_MARKER {
882 #[allow(clippy::cast_ptr_alignment)]
883 let is_signer = *(input.add(offset) as *const u8) != 0;
884 offset += size_of::<u8>();
885
886 #[allow(clippy::cast_ptr_alignment)]
887 let is_writable = *(input.add(offset) as *const u8) != 0;
888 offset += size_of::<u8>();
889
890 let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
891 offset += size_of::<Pubkey>();
892
893 #[allow(clippy::cast_ptr_alignment)]
894 let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
895 offset += size_of::<u64>();
896
897 #[allow(clippy::cast_ptr_alignment)]
898 let data_len = *(input.add(offset) as *const u64) as usize;
899 offset += size_of::<u64>();
900
901 let data = Rc::new(RefCell::new({
902 from_raw_parts_mut(input.add(offset), data_len)
903 }));
904 offset += data_len;
905
906 let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
907 offset += size_of::<Pubkey>();
908
909 #[allow(clippy::cast_ptr_alignment)]
910 let executable = *(input.add(offset) as *const u8) != 0;
911 offset += size_of::<u8>();
912
913 #[allow(clippy::cast_ptr_alignment)]
914 let rent_epoch = *(input.add(offset) as *const u64);
915 offset += size_of::<u64>();
916
917 accounts.push(AccountInfo {
918 key,
919 is_signer,
920 is_writable,
921 lamports,
922 data,
923 owner,
924 executable,
925 rent_epoch,
926 });
927 } else {
928 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
930 }
931 }
932
933 #[allow(clippy::cast_ptr_alignment)]
936 let instruction_data_len = *(input.add(offset) as *const u64) as usize;
937 offset += size_of::<u64>();
938
939 let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
940 offset += instruction_data_len;
941
942 let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
945
946 (program_id, accounts, instruction_data)
947 }
948}