1#![allow(clippy::arithmetic_side_effects)]
2
3use {
4 byteorder::{ByteOrder, LittleEndian},
5 miraland_program_runtime::invoke_context::SerializedAccountMetadata,
6 solana_rbpf::{
7 aligned_memory::{AlignedMemory, Pod},
8 ebpf::{HOST_ALIGN, MM_INPUT_START},
9 memory_region::{MemoryRegion, MemoryState},
10 },
11 miraland_sdk::{
12 bpf_loader_deprecated,
13 entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
14 feature_set::FeatureSet,
15 instruction::InstructionError,
16 pubkey::Pubkey,
17 system_instruction::MAX_PERMITTED_DATA_LENGTH,
18 transaction_context::{
19 BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
20 },
21 },
22 std::mem::{self, size_of},
23};
24
25const MAX_INSTRUCTION_ACCOUNTS: u8 = NON_DUP_MARKER;
28
29enum SerializeAccount<'a> {
30 Account(IndexOfAccount, BorrowedAccount<'a>),
31 Duplicate(IndexOfAccount),
32}
33
34struct Serializer {
35 pub buffer: AlignedMemory<HOST_ALIGN>,
36 regions: Vec<MemoryRegion>,
37 vaddr: u64,
38 region_start: usize,
39 aligned: bool,
40 copy_account_data: bool,
41}
42
43impl Serializer {
44 fn new(size: usize, start_addr: u64, aligned: bool, copy_account_data: bool) -> Serializer {
45 Serializer {
46 buffer: AlignedMemory::with_capacity(size),
47 regions: Vec::new(),
48 region_start: 0,
49 vaddr: start_addr,
50 aligned,
51 copy_account_data,
52 }
53 }
54
55 fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
56 self.buffer.fill_write(num, value)
57 }
58
59 pub fn write<T: Pod>(&mut self, value: T) -> u64 {
60 self.debug_assert_alignment::<T>();
61 let vaddr = self
62 .vaddr
63 .saturating_add(self.buffer.len() as u64)
64 .saturating_sub(self.region_start as u64);
65 unsafe {
74 self.buffer.write_unchecked(value);
75 }
76
77 vaddr
78 }
79
80 fn write_all(&mut self, value: &[u8]) -> u64 {
81 let vaddr = self
82 .vaddr
83 .saturating_add(self.buffer.len() as u64)
84 .saturating_sub(self.region_start as u64);
85 unsafe {
88 self.buffer.write_all_unchecked(value);
89 }
90
91 vaddr
92 }
93
94 fn write_account(
95 &mut self,
96 account: &mut BorrowedAccount<'_>,
97 feature_set: &FeatureSet,
98 ) -> Result<u64, InstructionError> {
99 let vm_data_addr = if self.copy_account_data {
100 let vm_data_addr = self.vaddr.saturating_add(self.buffer.len() as u64);
101 self.write_all(account.get_data());
102 vm_data_addr
103 } else {
104 self.push_region(true);
105 let vaddr = self.vaddr;
106 self.push_account_data_region(account, feature_set)?;
107 vaddr
108 };
109
110 if self.aligned {
111 let align_offset =
112 (account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
113 if self.copy_account_data {
114 self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
115 .map_err(|_| InstructionError::InvalidArgument)?;
116 } else {
117 self.fill_write(MAX_PERMITTED_DATA_INCREASE + BPF_ALIGN_OF_U128, 0)
123 .map_err(|_| InstructionError::InvalidArgument)?;
124 self.region_start += BPF_ALIGN_OF_U128.saturating_sub(align_offset);
125 self.push_region(account.can_data_be_changed(feature_set).is_ok());
127 }
128 }
129
130 Ok(vm_data_addr)
131 }
132
133 fn push_account_data_region(
134 &mut self,
135 account: &mut BorrowedAccount<'_>,
136 feature_set: &FeatureSet,
137 ) -> Result<(), InstructionError> {
138 if !account.get_data().is_empty() {
139 let region = match account_data_region_memory_state(account, feature_set) {
140 MemoryState::Readable => MemoryRegion::new_readonly(account.get_data(), self.vaddr),
141 MemoryState::Writable => {
142 MemoryRegion::new_writable(account.get_data_mut(feature_set)?, self.vaddr)
143 }
144 MemoryState::Cow(index_in_transaction) => {
145 MemoryRegion::new_cow(account.get_data(), self.vaddr, index_in_transaction)
146 }
147 };
148 self.vaddr += region.len;
149 self.regions.push(region);
150 }
151
152 Ok(())
153 }
154
155 fn push_region(&mut self, writable: bool) {
156 let range = self.region_start..self.buffer.len();
157 let region = if writable {
158 MemoryRegion::new_writable(
159 self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
160 self.vaddr,
161 )
162 } else {
163 MemoryRegion::new_readonly(
164 self.buffer.as_slice().get(range.clone()).unwrap(),
165 self.vaddr,
166 )
167 };
168 self.regions.push(region);
169 self.region_start = range.end;
170 self.vaddr += range.len() as u64;
171 }
172
173 fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
174 self.push_region(true);
175 debug_assert_eq!(self.region_start, self.buffer.len());
176 (self.buffer, self.regions)
177 }
178
179 fn debug_assert_alignment<T>(&self) {
180 debug_assert!(
181 !self.aligned
182 || self
183 .buffer
184 .as_slice()
185 .as_ptr_range()
186 .end
187 .align_offset(mem::align_of::<T>())
188 == 0
189 );
190 }
191}
192
193pub fn serialize_parameters(
194 transaction_context: &TransactionContext,
195 instruction_context: &InstructionContext,
196 copy_account_data: bool,
197 feature_set: &FeatureSet,
198) -> Result<
199 (
200 AlignedMemory<HOST_ALIGN>,
201 Vec<MemoryRegion>,
202 Vec<SerializedAccountMetadata>,
203 ),
204 InstructionError,
205> {
206 let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
207 if num_ix_accounts > MAX_INSTRUCTION_ACCOUNTS as IndexOfAccount {
208 return Err(InstructionError::MaxAccountsExceeded);
209 }
210
211 let (program_id, is_loader_deprecated) = {
212 let program_account =
213 instruction_context.try_borrow_last_program_account(transaction_context)?;
214 (
215 *program_account.get_key(),
216 *program_account.get_owner() == bpf_loader_deprecated::id(),
217 )
218 };
219
220 let accounts = (0..instruction_context.get_number_of_instruction_accounts())
221 .map(|instruction_account_index| {
222 if let Some(index) = instruction_context
223 .is_instruction_account_duplicate(instruction_account_index)
224 .unwrap()
225 {
226 SerializeAccount::Duplicate(index)
227 } else {
228 let account = instruction_context
229 .try_borrow_instruction_account(transaction_context, instruction_account_index)
230 .unwrap();
231 SerializeAccount::Account(instruction_account_index, account)
232 }
233 })
234 .collect::<Vec<_>>();
239
240 if is_loader_deprecated {
241 serialize_parameters_unaligned(
242 accounts,
243 instruction_context.get_instruction_data(),
244 &program_id,
245 copy_account_data,
246 feature_set,
247 )
248 } else {
249 serialize_parameters_aligned(
250 accounts,
251 instruction_context.get_instruction_data(),
252 &program_id,
253 copy_account_data,
254 feature_set,
255 )
256 }
257}
258
259pub fn deserialize_parameters(
260 transaction_context: &TransactionContext,
261 instruction_context: &InstructionContext,
262 copy_account_data: bool,
263 buffer: &[u8],
264 accounts_metadata: &[SerializedAccountMetadata],
265 feature_set: &FeatureSet,
266) -> Result<(), InstructionError> {
267 let is_loader_deprecated = *instruction_context
268 .try_borrow_last_program_account(transaction_context)?
269 .get_owner()
270 == bpf_loader_deprecated::id();
271 let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
272 if is_loader_deprecated {
273 deserialize_parameters_unaligned(
274 transaction_context,
275 instruction_context,
276 copy_account_data,
277 buffer,
278 account_lengths,
279 feature_set,
280 )
281 } else {
282 deserialize_parameters_aligned(
283 transaction_context,
284 instruction_context,
285 copy_account_data,
286 buffer,
287 account_lengths,
288 feature_set,
289 )
290 }
291}
292
293fn serialize_parameters_unaligned(
294 accounts: Vec<SerializeAccount>,
295 instruction_data: &[u8],
296 program_id: &Pubkey,
297 copy_account_data: bool,
298 feature_set: &FeatureSet,
299) -> Result<
300 (
301 AlignedMemory<HOST_ALIGN>,
302 Vec<MemoryRegion>,
303 Vec<SerializedAccountMetadata>,
304 ),
305 InstructionError,
306> {
307 let mut size = size_of::<u64>();
309 for account in &accounts {
310 size += 1; match account {
312 SerializeAccount::Duplicate(_) => {}
313 SerializeAccount::Account(_, account) => {
314 size += size_of::<u8>() + size_of::<u8>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); if copy_account_data {
323 size += account.get_data().len();
324 }
325 }
326 }
327 }
328 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, false, copy_account_data);
333
334 let mut accounts_metadata: Vec<SerializedAccountMetadata> = Vec::with_capacity(accounts.len());
335 s.write::<u64>((accounts.len() as u64).to_le());
336 for account in accounts {
337 match account {
338 SerializeAccount::Duplicate(position) => {
339 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
340 s.write(position as u8);
341 }
342 SerializeAccount::Account(_, mut account) => {
343 s.write::<u8>(NON_DUP_MARKER);
344 s.write::<u8>(account.is_signer() as u8);
345 s.write::<u8>(account.is_writable() as u8);
346 let vm_key_addr = s.write_all(account.get_key().as_ref());
347 let vm_lamports_addr = s.write::<u64>(account.get_lamports().to_le());
348 s.write::<u64>((account.get_data().len() as u64).to_le());
349 let vm_data_addr = s.write_account(&mut account, feature_set)?;
350 let vm_owner_addr = s.write_all(account.get_owner().as_ref());
351 s.write::<u8>(account.is_executable(feature_set) as u8);
352 s.write::<u64>((account.get_rent_epoch()).to_le());
353 accounts_metadata.push(SerializedAccountMetadata {
354 original_data_len: account.get_data().len(),
355 vm_key_addr,
356 vm_lamports_addr,
357 vm_owner_addr,
358 vm_data_addr,
359 });
360 }
361 };
362 }
363 s.write::<u64>((instruction_data.len() as u64).to_le());
364 s.write_all(instruction_data);
365 s.write_all(program_id.as_ref());
366
367 let (mem, regions) = s.finish();
368 Ok((mem, regions, accounts_metadata))
369}
370
371pub fn deserialize_parameters_unaligned<I: IntoIterator<Item = usize>>(
372 transaction_context: &TransactionContext,
373 instruction_context: &InstructionContext,
374 copy_account_data: bool,
375 buffer: &[u8],
376 account_lengths: I,
377 feature_set: &FeatureSet,
378) -> Result<(), InstructionError> {
379 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in (0..instruction_context
381 .get_number_of_instruction_accounts())
382 .zip(account_lengths.into_iter())
383 {
384 let duplicate =
385 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
386 start += 1; if duplicate.is_none() {
388 let mut borrowed_account = instruction_context
389 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
390 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); let lamports = LittleEndian::read_u64(
394 buffer
395 .get(start..)
396 .ok_or(InstructionError::InvalidArgument)?,
397 );
398 if borrowed_account.get_lamports() != lamports {
399 borrowed_account.set_lamports(lamports, feature_set)?;
400 }
401 start += size_of::<u64>() + size_of::<u64>(); if copy_account_data {
404 let data = buffer
405 .get(start..start + pre_len)
406 .ok_or(InstructionError::InvalidArgument)?;
407 match borrowed_account
409 .can_data_be_resized(data.len())
410 .and_then(|_| borrowed_account.can_data_be_changed(feature_set))
411 {
412 Ok(()) => borrowed_account.set_data_from_slice(data, feature_set)?,
413 Err(err) if borrowed_account.get_data() != data => return Err(err),
414 _ => {}
415 }
416 start += pre_len; }
418 start += size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
422 }
423 Ok(())
424}
425
426fn serialize_parameters_aligned(
427 accounts: Vec<SerializeAccount>,
428 instruction_data: &[u8],
429 program_id: &Pubkey,
430 copy_account_data: bool,
431 feature_set: &FeatureSet,
432) -> Result<
433 (
434 AlignedMemory<HOST_ALIGN>,
435 Vec<MemoryRegion>,
436 Vec<SerializedAccountMetadata>,
437 ),
438 InstructionError,
439> {
440 let mut accounts_metadata = Vec::with_capacity(accounts.len());
441 let mut size = size_of::<u64>();
443 for account in &accounts {
444 size += 1; match account {
446 SerializeAccount::Duplicate(_) => size += 7, SerializeAccount::Account(_, account) => {
448 let data_len = account.get_data().len();
449 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>() + MAX_PERMITTED_DATA_INCREASE
458 + size_of::<u64>(); if copy_account_data {
460 size += data_len + (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
461 } else {
462 size += BPF_ALIGN_OF_U128;
463 }
464 }
465 }
466 }
467 size += size_of::<u64>() + instruction_data.len()
469 + size_of::<Pubkey>(); let mut s = Serializer::new(size, MM_INPUT_START, true, copy_account_data);
472
473 s.write::<u64>((accounts.len() as u64).to_le());
475 for account in accounts {
476 match account {
477 SerializeAccount::Account(_, mut borrowed_account) => {
478 s.write::<u8>(NON_DUP_MARKER);
479 s.write::<u8>(borrowed_account.is_signer() as u8);
480 s.write::<u8>(borrowed_account.is_writable() as u8);
481 s.write::<u8>(borrowed_account.is_executable(feature_set) as u8);
482 s.write_all(&[0u8, 0, 0, 0]);
483 let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
484 let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
485 let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
486 s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
487 let vm_data_addr = s.write_account(&mut borrowed_account, feature_set)?;
488 s.write::<u64>((borrowed_account.get_rent_epoch()).to_le());
489 accounts_metadata.push(SerializedAccountMetadata {
490 original_data_len: borrowed_account.get_data().len(),
491 vm_key_addr,
492 vm_owner_addr,
493 vm_lamports_addr,
494 vm_data_addr,
495 });
496 }
497 SerializeAccount::Duplicate(position) => {
498 accounts_metadata.push(accounts_metadata.get(position as usize).unwrap().clone());
499 s.write::<u8>(position as u8);
500 s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
501 }
502 };
503 }
504 s.write::<u64>((instruction_data.len() as u64).to_le());
505 s.write_all(instruction_data);
506 s.write_all(program_id.as_ref());
507
508 let (mem, regions) = s.finish();
509 Ok((mem, regions, accounts_metadata))
510}
511
512pub fn deserialize_parameters_aligned<I: IntoIterator<Item = usize>>(
513 transaction_context: &TransactionContext,
514 instruction_context: &InstructionContext,
515 copy_account_data: bool,
516 buffer: &[u8],
517 account_lengths: I,
518 feature_set: &FeatureSet,
519) -> Result<(), InstructionError> {
520 let mut start = size_of::<u64>(); for (instruction_account_index, pre_len) in (0..instruction_context
522 .get_number_of_instruction_accounts())
523 .zip(account_lengths.into_iter())
524 {
525 let duplicate =
526 instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
527 start += size_of::<u8>(); if duplicate.is_some() {
529 start += 7; } else {
531 let mut borrowed_account = instruction_context
532 .try_borrow_instruction_account(transaction_context, instruction_account_index)?;
533 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + size_of::<u32>() + size_of::<Pubkey>(); let owner = buffer
539 .get(start..start + size_of::<Pubkey>())
540 .ok_or(InstructionError::InvalidArgument)?;
541 start += size_of::<Pubkey>(); let lamports = LittleEndian::read_u64(
543 buffer
544 .get(start..)
545 .ok_or(InstructionError::InvalidArgument)?,
546 );
547 if borrowed_account.get_lamports() != lamports {
548 borrowed_account.set_lamports(lamports, feature_set)?;
549 }
550 start += size_of::<u64>(); let post_len = LittleEndian::read_u64(
552 buffer
553 .get(start..)
554 .ok_or(InstructionError::InvalidArgument)?,
555 ) as usize;
556 start += size_of::<u64>(); if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
558 || post_len > MAX_PERMITTED_DATA_LENGTH as usize
559 {
560 return Err(InstructionError::InvalidRealloc);
561 }
562 let alignment_offset = (pre_len as *const u8).align_offset(BPF_ALIGN_OF_U128);
564 if copy_account_data {
565 let data = buffer
566 .get(start..start + post_len)
567 .ok_or(InstructionError::InvalidArgument)?;
568 match borrowed_account
569 .can_data_be_resized(post_len)
570 .and_then(|_| borrowed_account.can_data_be_changed(feature_set))
571 {
572 Ok(()) => borrowed_account.set_data_from_slice(data, feature_set)?,
573 Err(err) if borrowed_account.get_data() != data => return Err(err),
574 _ => {}
575 }
576 start += pre_len; } else {
578 start += BPF_ALIGN_OF_U128.saturating_sub(alignment_offset);
581 let data = buffer
582 .get(start..start + MAX_PERMITTED_DATA_INCREASE)
583 .ok_or(InstructionError::InvalidArgument)?;
584 match borrowed_account
585 .can_data_be_resized(post_len)
586 .and_then(|_| borrowed_account.can_data_be_changed(feature_set))
587 {
588 Ok(()) => {
589 borrowed_account.set_data_length(post_len, feature_set)?;
590 let allocated_bytes = post_len.saturating_sub(pre_len);
591 if allocated_bytes > 0 {
592 borrowed_account
593 .get_data_mut(feature_set)?
594 .get_mut(pre_len..pre_len.saturating_add(allocated_bytes))
595 .ok_or(InstructionError::InvalidArgument)?
596 .copy_from_slice(
597 data.get(0..allocated_bytes)
598 .ok_or(InstructionError::InvalidArgument)?,
599 );
600 }
601 }
602 Err(err) if borrowed_account.get_data().len() != post_len => return Err(err),
603 _ => {}
604 }
605 }
606 start += MAX_PERMITTED_DATA_INCREASE;
607 start += alignment_offset;
608 start += size_of::<u64>(); if borrowed_account.get_owner().to_bytes() != owner {
610 borrowed_account.set_owner(owner, feature_set)?;
612 }
613 }
614 }
615 Ok(())
616}
617
618pub(crate) fn account_data_region_memory_state(
619 account: &BorrowedAccount<'_>,
620 feature_set: &FeatureSet,
621) -> MemoryState {
622 if account.can_data_be_changed(feature_set).is_ok() {
623 if account.is_shared() {
624 MemoryState::Cow(account.get_index_in_transaction() as u64)
625 } else {
626 MemoryState::Writable
627 }
628 } else {
629 MemoryState::Readable
630 }
631}
632
633#[cfg(test)]
634#[allow(clippy::indexing_slicing)]
635mod tests {
636 use {
637 super::*,
638 miraland_program_runtime::with_mock_invoke_context,
639 miraland_sdk::{
640 account::{Account, AccountSharedData, WritableAccount},
641 account_info::AccountInfo,
642 bpf_loader,
643 entrypoint::deserialize,
644 transaction_context::InstructionAccount,
645 },
646 std::{
647 cell::RefCell,
648 mem::transmute,
649 rc::Rc,
650 slice::{self, from_raw_parts, from_raw_parts_mut},
651 },
652 };
653
654 #[test]
655 fn test_serialize_parameters_with_many_accounts() {
656 struct TestCase {
657 num_ix_accounts: usize,
658 append_dup_account: bool,
659 expected_err: Option<InstructionError>,
660 name: &'static str,
661 }
662
663 for copy_account_data in [true] {
664 for TestCase {
665 num_ix_accounts,
666 append_dup_account,
667 expected_err,
668 name,
669 } in [
670 TestCase {
671 name: "serialize max accounts with cap",
672 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
673 append_dup_account: false,
674 expected_err: None,
675 },
676 TestCase {
677 name: "serialize too many accounts with cap",
678 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS) + 1,
679 append_dup_account: false,
680 expected_err: Some(InstructionError::MaxAccountsExceeded),
681 },
682 TestCase {
683 name: "serialize too many accounts and append dup with cap",
684 num_ix_accounts: usize::from(MAX_INSTRUCTION_ACCOUNTS),
685 append_dup_account: true,
686 expected_err: Some(InstructionError::MaxAccountsExceeded),
687 },
688 ] {
689 let program_id = miraland_sdk::pubkey::new_rand();
690 let mut transaction_accounts = vec![(
691 program_id,
692 AccountSharedData::from(Account {
693 lamports: 0,
694 data: vec![],
695 owner: bpf_loader::id(),
696 executable: true,
697 rent_epoch: 0,
698 }),
699 )];
700 for _ in 0..num_ix_accounts {
701 transaction_accounts.push((
702 Pubkey::new_unique(),
703 AccountSharedData::from(Account {
704 lamports: 0,
705 data: vec![],
706 owner: program_id,
707 executable: false,
708 rent_epoch: 0,
709 }),
710 ));
711 }
712 let mut instruction_accounts: Vec<_> = (0..num_ix_accounts as IndexOfAccount)
713 .map(|index_in_callee| InstructionAccount {
714 index_in_transaction: index_in_callee + 1,
715 index_in_caller: index_in_callee + 1,
716 index_in_callee,
717 is_signer: false,
718 is_writable: false,
719 })
720 .collect();
721 if append_dup_account {
722 instruction_accounts.push(instruction_accounts.last().cloned().unwrap());
723 }
724 let program_indices = [0];
725 let instruction_data = vec![];
726
727 with_mock_invoke_context!(
728 invoke_context,
729 transaction_context,
730 transaction_accounts
731 );
732 invoke_context
733 .transaction_context
734 .get_next_instruction_context()
735 .unwrap()
736 .configure(&program_indices, &instruction_accounts, &instruction_data);
737 invoke_context.push().unwrap();
738 let instruction_context = invoke_context
739 .transaction_context
740 .get_current_instruction_context()
741 .unwrap();
742
743 let serialization_result = serialize_parameters(
744 invoke_context.transaction_context,
745 instruction_context,
746 copy_account_data,
747 &invoke_context.feature_set,
748 );
749 assert_eq!(
750 serialization_result.as_ref().err(),
751 expected_err.as_ref(),
752 "{name} test case failed",
753 );
754 if expected_err.is_some() {
755 continue;
756 }
757
758 let (mut serialized, regions, _account_lengths) = serialization_result.unwrap();
759 let mut serialized_regions = concat_regions(®ions);
760 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
761 deserialize(
762 if copy_account_data {
763 serialized.as_slice_mut()
764 } else {
765 serialized_regions.as_slice_mut()
766 }
767 .first_mut()
768 .unwrap() as *mut u8,
769 )
770 };
771 assert_eq!(de_program_id, &program_id);
772 assert_eq!(de_instruction_data, &instruction_data);
773 for account_info in de_accounts {
774 let index_in_transaction = invoke_context
775 .transaction_context
776 .find_index_of_account(account_info.key)
777 .unwrap();
778 let account = invoke_context
779 .transaction_context
780 .get_account_at_index(index_in_transaction)
781 .unwrap()
782 .borrow();
783 assert_eq!(account.lamports(), account_info.lamports());
784 assert_eq!(account.data(), &account_info.data.borrow()[..]);
785 assert_eq!(account.owner(), account_info.owner);
786 assert_eq!(account.executable(), account_info.executable);
787 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
788 }
789 }
790 }
791 }
792
793 #[test]
794 fn test_serialize_parameters() {
795 for copy_account_data in [false, true] {
796 let program_id = miraland_sdk::pubkey::new_rand();
797 let transaction_accounts = vec![
798 (
799 program_id,
800 AccountSharedData::from(Account {
801 lamports: 0,
802 data: vec![],
803 owner: bpf_loader::id(),
804 executable: true,
805 rent_epoch: 0,
806 }),
807 ),
808 (
809 miraland_sdk::pubkey::new_rand(),
810 AccountSharedData::from(Account {
811 lamports: 1,
812 data: vec![1u8, 2, 3, 4, 5],
813 owner: bpf_loader::id(),
814 executable: false,
815 rent_epoch: 100,
816 }),
817 ),
818 (
819 miraland_sdk::pubkey::new_rand(),
820 AccountSharedData::from(Account {
821 lamports: 2,
822 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
823 owner: bpf_loader::id(),
824 executable: true,
825 rent_epoch: 200,
826 }),
827 ),
828 (
829 miraland_sdk::pubkey::new_rand(),
830 AccountSharedData::from(Account {
831 lamports: 3,
832 data: vec![],
833 owner: bpf_loader::id(),
834 executable: false,
835 rent_epoch: 3100,
836 }),
837 ),
838 (
839 miraland_sdk::pubkey::new_rand(),
840 AccountSharedData::from(Account {
841 lamports: 4,
842 data: vec![1u8, 2, 3, 4, 5],
843 owner: bpf_loader::id(),
844 executable: false,
845 rent_epoch: 100,
846 }),
847 ),
848 (
849 miraland_sdk::pubkey::new_rand(),
850 AccountSharedData::from(Account {
851 lamports: 5,
852 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
853 owner: bpf_loader::id(),
854 executable: true,
855 rent_epoch: 200,
856 }),
857 ),
858 (
859 miraland_sdk::pubkey::new_rand(),
860 AccountSharedData::from(Account {
861 lamports: 6,
862 data: vec![],
863 owner: bpf_loader::id(),
864 executable: false,
865 rent_epoch: 3100,
866 }),
867 ),
868 ];
869 let instruction_accounts: Vec<InstructionAccount> = [1, 1, 2, 3, 4, 4, 5, 6]
870 .into_iter()
871 .enumerate()
872 .map(
873 |(index_in_instruction, index_in_transaction)| InstructionAccount {
874 index_in_transaction,
875 index_in_caller: index_in_transaction,
876 index_in_callee: index_in_transaction - 1,
877 is_signer: false,
878 is_writable: index_in_instruction >= 4,
879 },
880 )
881 .collect();
882 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
883 let program_indices = [0];
884 let mut original_accounts = transaction_accounts.clone();
885 with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
886 invoke_context
887 .transaction_context
888 .get_next_instruction_context()
889 .unwrap()
890 .configure(&program_indices, &instruction_accounts, &instruction_data);
891 invoke_context.push().unwrap();
892 let instruction_context = invoke_context
893 .transaction_context
894 .get_current_instruction_context()
895 .unwrap();
896
897 let (mut serialized, regions, accounts_metadata) = serialize_parameters(
899 invoke_context.transaction_context,
900 instruction_context,
901 copy_account_data,
902 &invoke_context.feature_set,
903 )
904 .unwrap();
905
906 let mut serialized_regions = concat_regions(®ions);
907 if copy_account_data {
908 assert_eq!(serialized.as_slice(), serialized_regions.as_slice());
909 }
910 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
911 deserialize(
912 if copy_account_data {
913 serialized.as_slice_mut()
914 } else {
915 serialized_regions.as_slice_mut()
916 }
917 .first_mut()
918 .unwrap() as *mut u8,
919 )
920 };
921
922 assert_eq!(&program_id, de_program_id);
923 assert_eq!(instruction_data, de_instruction_data);
924 assert_eq!(
925 (de_instruction_data.first().unwrap() as *const u8).align_offset(BPF_ALIGN_OF_U128),
926 0
927 );
928 for account_info in de_accounts {
929 let index_in_transaction = invoke_context
930 .transaction_context
931 .find_index_of_account(account_info.key)
932 .unwrap();
933 let account = invoke_context
934 .transaction_context
935 .get_account_at_index(index_in_transaction)
936 .unwrap()
937 .borrow();
938 assert_eq!(account.lamports(), account_info.lamports());
939 assert_eq!(account.data(), &account_info.data.borrow()[..]);
940 assert_eq!(account.owner(), account_info.owner);
941 assert!(account_info.executable);
942 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
943
944 assert_eq!(
945 (*account_info.lamports.borrow() as *const u64).align_offset(BPF_ALIGN_OF_U128),
946 0
947 );
948 assert_eq!(
949 account_info
950 .data
951 .borrow()
952 .as_ptr()
953 .align_offset(BPF_ALIGN_OF_U128),
954 0
955 );
956 }
957
958 deserialize_parameters(
959 invoke_context.transaction_context,
960 instruction_context,
961 copy_account_data,
962 serialized.as_slice(),
963 &accounts_metadata,
964 &invoke_context.feature_set,
965 )
966 .unwrap();
967 for (index_in_transaction, (_key, original_account)) in
968 original_accounts.iter().enumerate()
969 {
970 let account = invoke_context
971 .transaction_context
972 .get_account_at_index(index_in_transaction as IndexOfAccount)
973 .unwrap()
974 .borrow();
975 assert_eq!(&*account, original_account);
976 }
977
978 original_accounts
980 .first_mut()
981 .unwrap()
982 .1
983 .set_owner(bpf_loader_deprecated::id());
984 invoke_context
985 .transaction_context
986 .get_account_at_index(0)
987 .unwrap()
988 .borrow_mut()
989 .set_owner(bpf_loader_deprecated::id());
990
991 let (mut serialized, regions, account_lengths) = serialize_parameters(
992 invoke_context.transaction_context,
993 instruction_context,
994 copy_account_data,
995 &invoke_context.feature_set,
996 )
997 .unwrap();
998 let mut serialized_regions = concat_regions(®ions);
999
1000 let (de_program_id, de_accounts, de_instruction_data) = unsafe {
1001 deserialize_unaligned(
1002 if copy_account_data {
1003 serialized.as_slice_mut()
1004 } else {
1005 serialized_regions.as_slice_mut()
1006 }
1007 .first_mut()
1008 .unwrap() as *mut u8,
1009 )
1010 };
1011 assert_eq!(&program_id, de_program_id);
1012 assert_eq!(instruction_data, de_instruction_data);
1013 for account_info in de_accounts {
1014 let index_in_transaction = invoke_context
1015 .transaction_context
1016 .find_index_of_account(account_info.key)
1017 .unwrap();
1018 let account = invoke_context
1019 .transaction_context
1020 .get_account_at_index(index_in_transaction)
1021 .unwrap()
1022 .borrow();
1023 assert_eq!(account.lamports(), account_info.lamports());
1024 assert_eq!(account.data(), &account_info.data.borrow()[..]);
1025 assert_eq!(account.owner(), account_info.owner);
1026 assert!(account_info.executable);
1027 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
1028 }
1029
1030 deserialize_parameters(
1031 invoke_context.transaction_context,
1032 instruction_context,
1033 copy_account_data,
1034 serialized.as_slice(),
1035 &account_lengths,
1036 &invoke_context.feature_set,
1037 )
1038 .unwrap();
1039 for (index_in_transaction, (_key, original_account)) in
1040 original_accounts.iter().enumerate()
1041 {
1042 let account = invoke_context
1043 .transaction_context
1044 .get_account_at_index(index_in_transaction as IndexOfAccount)
1045 .unwrap()
1046 .borrow();
1047 assert_eq!(&*account, original_account);
1048 }
1049 }
1050 }
1051
1052 #[deny(unsafe_op_in_unsafe_fn)]
1054 pub unsafe fn deserialize_unaligned<'a>(
1055 input: *mut u8,
1056 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
1057 struct Ptr<T>(std::marker::PhantomData<T>);
1059 impl<T> Ptr<T> {
1060 const COULD_BE_UNALIGNED: bool = std::mem::align_of::<T>() > 1;
1061
1062 #[inline(always)]
1063 fn read_possibly_unaligned(input: *mut u8, offset: usize) -> T {
1064 unsafe {
1065 let src = input.add(offset) as *const T;
1066 if Self::COULD_BE_UNALIGNED {
1067 src.read_unaligned()
1068 } else {
1069 src.read()
1070 }
1071 }
1072 }
1073
1074 #[inline(always)]
1083 fn ref_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a T {
1084 #[allow(clippy::transmute_ptr_to_ref)]
1085 unsafe {
1086 transmute(input.add(offset) as *const T)
1087 }
1088 }
1089
1090 #[inline(always)]
1092 fn mut_possibly_unaligned<'a>(input: *mut u8, offset: usize) -> &'a mut T {
1093 #[allow(clippy::transmute_ptr_to_ref)]
1094 unsafe {
1095 transmute(input.add(offset) as *mut T)
1096 }
1097 }
1098 }
1099
1100 let mut offset: usize = 0;
1101
1102 let num_accounts = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1105 offset += size_of::<u64>();
1106
1107 let mut accounts = Vec::with_capacity(num_accounts);
1110 for _ in 0..num_accounts {
1111 let dup_info = Ptr::<u8>::read_possibly_unaligned(input, offset);
1112 offset += size_of::<u8>();
1113 if dup_info == NON_DUP_MARKER {
1114 let is_signer = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1115 offset += size_of::<u8>();
1116
1117 let is_writable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1118 offset += size_of::<u8>();
1119
1120 let key = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1121 offset += size_of::<Pubkey>();
1122
1123 let lamports = Rc::new(RefCell::new(Ptr::mut_possibly_unaligned(input, offset)));
1124 offset += size_of::<u64>();
1125
1126 let data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1127 offset += size_of::<u64>();
1128
1129 let data = Rc::new(RefCell::new(unsafe {
1130 from_raw_parts_mut(input.add(offset), data_len)
1131 }));
1132 offset += data_len;
1133
1134 let owner: &Pubkey = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1135 offset += size_of::<Pubkey>();
1136
1137 let executable = Ptr::<u8>::read_possibly_unaligned(input, offset) != 0;
1138 offset += size_of::<u8>();
1139
1140 let rent_epoch = Ptr::<u64>::read_possibly_unaligned(input, offset);
1141 offset += size_of::<u64>();
1142
1143 accounts.push(AccountInfo {
1144 key,
1145 is_signer,
1146 is_writable,
1147 lamports,
1148 data,
1149 owner,
1150 executable,
1151 rent_epoch,
1152 });
1153 } else {
1154 accounts.push(accounts.get(dup_info as usize).unwrap().clone());
1156 }
1157 }
1158
1159 let instruction_data_len = Ptr::<u64>::read_possibly_unaligned(input, offset) as usize;
1162 offset += size_of::<u64>();
1163
1164 let instruction_data = unsafe { from_raw_parts(input.add(offset), instruction_data_len) };
1165 offset += instruction_data_len;
1166
1167 let program_id = Ptr::<Pubkey>::ref_possibly_unaligned(input, offset);
1170
1171 (program_id, accounts, instruction_data)
1172 }
1173
1174 fn concat_regions(regions: &[MemoryRegion]) -> AlignedMemory<HOST_ALIGN> {
1175 let len = regions.iter().fold(0, |len, region| len + region.len) as usize;
1176 let mut mem = AlignedMemory::zero_filled(len);
1177 for region in regions {
1178 let host_slice = unsafe {
1179 slice::from_raw_parts(region.host_addr.get() as *const u8, region.len as usize)
1180 };
1181 mem.as_slice_mut()[(region.vm_addr - MM_INPUT_START) as usize..][..region.len as usize]
1182 .copy_from_slice(host_slice)
1183 }
1184 mem
1185 }
1186}