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