1use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
2use gemachain_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN};
3use gemachain_sdk::{
4 account::{ReadableAccount, WritableAccount},
5 bpf_loader_deprecated,
6 entrypoint::MAX_PERMITTED_DATA_INCREASE,
7 instruction::InstructionError,
8 keyed_account::KeyedAccount,
9 pubkey::Pubkey,
10};
11use std::{
12 io::prelude::*,
13 mem::{align_of, size_of},
14};
15
16pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
18 for (i, account) in accounts.iter().enumerate() {
19 if account == keyed_account {
20 return (true, i);
21 }
22 }
23 (false, 0)
24}
25
26pub fn serialize_parameters(
27 loader_id: &Pubkey,
28 program_id: &Pubkey,
29 keyed_accounts: &[KeyedAccount],
30 data: &[u8],
31) -> Result<(AlignedMemory, Vec<usize>), InstructionError> {
32 if *loader_id == bpf_loader_deprecated::id() {
33 serialize_parameters_unaligned(program_id, keyed_accounts, data)
34 } else {
35 serialize_parameters_aligned(program_id, keyed_accounts, data)
36 }
37 .and_then(|buffer| {
38 let account_lengths = keyed_accounts
39 .iter()
40 .map(|keyed_account| keyed_account.data_len())
41 .collect::<Result<Vec<usize>, InstructionError>>()?;
42 Ok((buffer, account_lengths))
43 })
44}
45
46pub fn deserialize_parameters(
47 loader_id: &Pubkey,
48 keyed_accounts: &[KeyedAccount],
49 buffer: &[u8],
50 account_lengths: &[usize],
51) -> Result<(), InstructionError> {
52 if *loader_id == bpf_loader_deprecated::id() {
53 deserialize_parameters_unaligned(keyed_accounts, buffer, account_lengths)
54 } else {
55 deserialize_parameters_aligned(keyed_accounts, buffer, account_lengths)
56 }
57}
58
59pub fn get_serialized_account_size_unaligned(
60 keyed_account: &KeyedAccount,
61) -> Result<usize, InstructionError> {
62 let data_len = keyed_account.data_len()?;
63 Ok(
64 size_of::<u8>() + size_of::<u8>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + data_len + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(), )
74}
75
76pub fn serialize_parameters_unaligned(
77 program_id: &Pubkey,
78 keyed_accounts: &[KeyedAccount],
79 instruction_data: &[u8],
80) -> Result<AlignedMemory, InstructionError> {
81 let mut size = size_of::<u64>();
83 for (i, keyed_account) in keyed_accounts.iter().enumerate() {
84 let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
85 size += 1; if !is_dup {
87 size += get_serialized_account_size_unaligned(keyed_account)?;
88 }
89 }
90 size += size_of::<u64>() + instruction_data.len() + size_of::<Pubkey>(); let mut v = AlignedMemory::new(size, HOST_ALIGN);
94
95 v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
96 .map_err(|_| InstructionError::InvalidArgument)?;
97 for (i, keyed_account) in keyed_accounts.iter().enumerate() {
98 let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
99 if is_dup {
100 v.write_u8(position as u8)
101 .map_err(|_| InstructionError::InvalidArgument)?;
102 } else {
103 v.write_u8(std::u8::MAX)
104 .map_err(|_| InstructionError::InvalidArgument)?;
105 v.write_u8(keyed_account.signer_key().is_some() as u8)
106 .map_err(|_| InstructionError::InvalidArgument)?;
107 v.write_u8(keyed_account.is_writable() as u8)
108 .map_err(|_| InstructionError::InvalidArgument)?;
109 v.write_all(keyed_account.unsigned_key().as_ref())
110 .map_err(|_| InstructionError::InvalidArgument)?;
111 v.write_u64::<LittleEndian>(keyed_account.carats()?)
112 .map_err(|_| InstructionError::InvalidArgument)?;
113 v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
114 .map_err(|_| InstructionError::InvalidArgument)?;
115 v.write_all(keyed_account.try_account_ref()?.data())
116 .map_err(|_| InstructionError::InvalidArgument)?;
117 v.write_all(keyed_account.owner()?.as_ref())
118 .map_err(|_| InstructionError::InvalidArgument)?;
119 v.write_u8(keyed_account.executable()? as u8)
120 .map_err(|_| InstructionError::InvalidArgument)?;
121 v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
122 .map_err(|_| InstructionError::InvalidArgument)?;
123 }
124 }
125 v.write_u64::<LittleEndian>(instruction_data.len() as u64)
126 .map_err(|_| InstructionError::InvalidArgument)?;
127 v.write_all(instruction_data)
128 .map_err(|_| InstructionError::InvalidArgument)?;
129 v.write_all(program_id.as_ref())
130 .map_err(|_| InstructionError::InvalidArgument)?;
131 Ok(v)
132}
133
134pub fn deserialize_parameters_unaligned(
135 keyed_accounts: &[KeyedAccount],
136 buffer: &[u8],
137 account_lengths: &[usize],
138) -> Result<(), InstructionError> {
139 let mut start = size_of::<u64>(); for (i, (keyed_account, _pre_len)) in keyed_accounts
141 .iter()
142 .zip(account_lengths.iter())
143 .enumerate()
144 {
145 let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
146 start += 1; if !is_dup {
148 start += size_of::<u8>(); start += size_of::<u8>(); start += size_of::<Pubkey>(); keyed_account
152 .try_account_ref_mut()?
153 .set_carats(LittleEndian::read_u64(&buffer[start..]));
154 start += size_of::<u64>() + size_of::<u64>(); let end = start + keyed_account.data_len()?;
157 keyed_account
158 .try_account_ref_mut()?
159 .set_data_from_slice(&buffer[start..end]);
160 start += keyed_account.data_len()? + size_of::<Pubkey>() + size_of::<u8>() + size_of::<u64>(); }
165 }
166 Ok(())
167}
168
169pub fn get_serialized_account_size_aligned(
170 keyed_account: &KeyedAccount,
171) -> Result<usize, InstructionError> {
172 let data_len = keyed_account.data_len()?;
173 Ok(
174 size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + 4 + size_of::<Pubkey>() + size_of::<Pubkey>() + size_of::<u64>() + size_of::<u64>() + data_len
183 + MAX_PERMITTED_DATA_INCREASE
184 + (data_len as *const u8).align_offset(align_of::<u128>())
185 + size_of::<u64>(), )
187}
188
189pub fn serialize_parameters_aligned(
190 program_id: &Pubkey,
191 keyed_accounts: &[KeyedAccount],
192 instruction_data: &[u8],
193) -> Result<AlignedMemory, InstructionError> {
194 let mut size = size_of::<u64>();
196 for (i, keyed_account) in keyed_accounts.iter().enumerate() {
197 let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
198 size += 1; if is_dup {
200 size += 7; } else {
202 size += get_serialized_account_size_aligned(keyed_account)?;
203 }
204 }
205 size += size_of::<u64>() + instruction_data.len()
207 + size_of::<Pubkey>(); let mut v = AlignedMemory::new(size, HOST_ALIGN);
209
210 v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
212 .map_err(|_| InstructionError::InvalidArgument)?;
213 for (i, keyed_account) in keyed_accounts.iter().enumerate() {
214 let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
215 if is_dup {
216 v.write_u8(position as u8)
217 .map_err(|_| InstructionError::InvalidArgument)?;
218 v.write_all(&[0u8, 0, 0, 0, 0, 0, 0])
219 .map_err(|_| InstructionError::InvalidArgument)?; } else {
221 v.write_u8(std::u8::MAX)
222 .map_err(|_| InstructionError::InvalidArgument)?;
223 v.write_u8(keyed_account.signer_key().is_some() as u8)
224 .map_err(|_| InstructionError::InvalidArgument)?;
225 v.write_u8(keyed_account.is_writable() as u8)
226 .map_err(|_| InstructionError::InvalidArgument)?;
227 v.write_u8(keyed_account.executable()? as u8)
228 .map_err(|_| InstructionError::InvalidArgument)?;
229 v.write_all(&[0u8, 0, 0, 0])
230 .map_err(|_| InstructionError::InvalidArgument)?; v.write_all(keyed_account.unsigned_key().as_ref())
232 .map_err(|_| InstructionError::InvalidArgument)?;
233 v.write_all(keyed_account.owner()?.as_ref())
234 .map_err(|_| InstructionError::InvalidArgument)?;
235 v.write_u64::<LittleEndian>(keyed_account.carats()?)
236 .map_err(|_| InstructionError::InvalidArgument)?;
237 v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
238 .map_err(|_| InstructionError::InvalidArgument)?;
239 v.write_all(keyed_account.try_account_ref()?.data())
240 .map_err(|_| InstructionError::InvalidArgument)?;
241 v.resize(
242 MAX_PERMITTED_DATA_INCREASE
243 + (v.write_index() as *const u8).align_offset(align_of::<u128>()),
244 0,
245 )
246 .map_err(|_| InstructionError::InvalidArgument)?;
247 v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
248 .map_err(|_| InstructionError::InvalidArgument)?;
249 }
250 }
251 v.write_u64::<LittleEndian>(instruction_data.len() as u64)
252 .map_err(|_| InstructionError::InvalidArgument)?;
253 v.write_all(instruction_data)
254 .map_err(|_| InstructionError::InvalidArgument)?;
255 v.write_all(program_id.as_ref())
256 .map_err(|_| InstructionError::InvalidArgument)?;
257 Ok(v)
258}
259
260pub fn deserialize_parameters_aligned(
261 keyed_accounts: &[KeyedAccount],
262 buffer: &[u8],
263 account_lengths: &[usize],
264) -> Result<(), InstructionError> {
265 let mut start = size_of::<u64>(); for (i, (keyed_account, pre_len)) in keyed_accounts
267 .iter()
268 .zip(account_lengths.iter())
269 .enumerate()
270 {
271 let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
272 start += size_of::<u8>(); if is_dup {
274 start += 7; } else {
276 let mut account = keyed_account.try_account_ref_mut()?;
277 start += size_of::<u8>() + size_of::<u8>() + size_of::<u8>() + 4 + size_of::<Pubkey>(); account.copy_into_owner_from_slice(&buffer[start..start + size_of::<Pubkey>()]);
283 start += size_of::<Pubkey>(); account.set_carats(LittleEndian::read_u64(&buffer[start..]));
285 start += size_of::<u64>(); let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
287 start += size_of::<u64>(); let mut data_end = start + *pre_len;
289 if post_len != *pre_len
290 && (post_len.saturating_sub(*pre_len)) <= MAX_PERMITTED_DATA_INCREASE
291 {
292 data_end = start + post_len;
293 }
294
295 account.set_data_from_slice(&buffer[start..data_end]);
296 start += *pre_len + MAX_PERMITTED_DATA_INCREASE; start += (start as *const u8).align_offset(align_of::<u128>());
298 start += size_of::<u64>(); }
300 }
301 Ok(())
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use gemachain_sdk::{
308 account::{Account, AccountSharedData},
309 account_info::AccountInfo,
310 bpf_loader,
311 entrypoint::deserialize,
312 };
313 use std::{
314 cell::RefCell,
315 rc::Rc,
316 slice::{from_raw_parts, from_raw_parts_mut},
318 };
319
320 #[test]
321 fn test_serialize_parameters() {
322 let program_id = gemachain_sdk::pubkey::new_rand();
323 let dup_key = gemachain_sdk::pubkey::new_rand();
324 let dup_key2 = gemachain_sdk::pubkey::new_rand();
325 let keys = vec![
326 dup_key,
327 dup_key,
328 gemachain_sdk::pubkey::new_rand(),
329 gemachain_sdk::pubkey::new_rand(),
330 dup_key2,
331 dup_key2,
332 gemachain_sdk::pubkey::new_rand(),
333 gemachain_sdk::pubkey::new_rand(),
334 ];
335 let accounts = [
336 RefCell::new(AccountSharedData::from(Account {
337 carats: 1,
338 data: vec![1u8, 2, 3, 4, 5],
339 owner: bpf_loader::id(),
340 executable: false,
341 rent_epoch: 100,
342 })),
343 RefCell::new(AccountSharedData::from(Account {
345 carats: 1,
346 data: vec![1u8, 2, 3, 4, 5],
347 owner: bpf_loader::id(),
348 executable: false,
349 rent_epoch: 100,
350 })),
351 RefCell::new(AccountSharedData::from(Account {
352 carats: 2,
353 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
354 owner: bpf_loader::id(),
355 executable: true,
356 rent_epoch: 200,
357 })),
358 RefCell::new(AccountSharedData::from(Account {
359 carats: 3,
360 data: vec![],
361 owner: bpf_loader::id(),
362 executable: false,
363 rent_epoch: 3100,
364 })),
365 RefCell::new(AccountSharedData::from(Account {
366 carats: 4,
367 data: vec![1u8, 2, 3, 4, 5],
368 owner: bpf_loader::id(),
369 executable: false,
370 rent_epoch: 100,
371 })),
372 RefCell::new(AccountSharedData::from(Account {
374 carats: 4,
375 data: vec![1u8, 2, 3, 4, 5],
376 owner: bpf_loader::id(),
377 executable: false,
378 rent_epoch: 100,
379 })),
380 RefCell::new(AccountSharedData::from(Account {
381 carats: 5,
382 data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
383 owner: bpf_loader::id(),
384 executable: true,
385 rent_epoch: 200,
386 })),
387 RefCell::new(AccountSharedData::from(Account {
388 carats: 6,
389 data: vec![],
390 owner: bpf_loader::id(),
391 executable: false,
392 rent_epoch: 3100,
393 })),
394 ];
395
396 let keyed_accounts: Vec<_> = keys
397 .iter()
398 .zip(&accounts)
399 .enumerate()
400 .map(|(i, (key, account))| {
401 if i <= accounts.len() / 2 {
402 KeyedAccount::new_readonly(key, false, account)
403 } else {
404 KeyedAccount::new(key, false, account)
405 }
406 })
407 .collect();
408 let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
409
410 let (mut serialized, account_lengths) = serialize_parameters(
413 &bpf_loader::id(),
414 &program_id,
415 &keyed_accounts,
416 &instruction_data,
417 )
418 .unwrap();
419
420 let (de_program_id, de_accounts, de_instruction_data) =
421 unsafe { deserialize(&mut serialized.as_slice_mut()[0] as *mut u8) };
422
423 assert_eq!(&program_id, de_program_id);
424 assert_eq!(instruction_data, de_instruction_data);
425 assert_eq!(
426 (&de_instruction_data[0] as *const u8).align_offset(align_of::<u128>()),
427 0
428 );
429 for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys.clone()) {
430 assert_eq!(key, *account_info.key);
431 let account = account.borrow();
432 assert_eq!(account.carats(), account_info.carats());
433 assert_eq!(account.data(), &account_info.data.borrow()[..]);
434 assert_eq!(account.owner(), account_info.owner);
435 assert_eq!(account.executable(), account_info.executable);
436 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
437
438 assert_eq!(
439 (*account_info.carats.borrow() as *const u64).align_offset(align_of::<u64>()),
440 0
441 );
442 assert_eq!(
443 account_info
444 .data
445 .borrow()
446 .as_ptr()
447 .align_offset(align_of::<u128>()),
448 0
449 );
450 }
451
452 let de_accounts = accounts.clone();
453 let de_keyed_accounts: Vec<_> = keys
454 .iter()
455 .zip(&de_accounts)
456 .enumerate()
457 .map(|(i, (key, account))| {
458 if i <= accounts.len() / 2 {
459 KeyedAccount::new_readonly(key, false, account)
460 } else {
461 KeyedAccount::new(key, false, account)
462 }
463 })
464 .collect();
465 deserialize_parameters(
466 &bpf_loader::id(),
467 &de_keyed_accounts,
468 serialized.as_slice(),
469 &account_lengths,
470 )
471 .unwrap();
472 for ((account, de_keyed_account), key) in
473 accounts.iter().zip(de_keyed_accounts).zip(keys.clone())
474 {
475 assert_eq!(key, *de_keyed_account.unsigned_key());
476 let account = account.borrow();
477 assert_eq!(account.executable(), de_keyed_account.executable().unwrap());
478 assert_eq!(account.rent_epoch(), de_keyed_account.rent_epoch().unwrap());
479 }
480
481 let (mut serialized, account_lengths) = serialize_parameters(
484 &bpf_loader_deprecated::id(),
485 &program_id,
486 &keyed_accounts,
487 &instruction_data,
488 )
489 .unwrap();
490
491 let (de_program_id, de_accounts, de_instruction_data) =
492 unsafe { deserialize_unaligned(&mut serialized.as_slice_mut()[0] as *mut u8) };
493 assert_eq!(&program_id, de_program_id);
494 assert_eq!(instruction_data, de_instruction_data);
495 for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys.clone()) {
496 assert_eq!(key, *account_info.key);
497 let account = account.borrow();
498 assert_eq!(account.carats(), account_info.carats());
499 assert_eq!(account.data(), &account_info.data.borrow()[..]);
500 assert_eq!(account.owner(), account_info.owner);
501 assert_eq!(account.executable(), account_info.executable);
502 assert_eq!(account.rent_epoch(), account_info.rent_epoch);
503 }
504
505 let de_accounts = accounts.clone();
506 let de_keyed_accounts: Vec<_> = keys
507 .iter()
508 .zip(&de_accounts)
509 .enumerate()
510 .map(|(i, (key, account))| {
511 if i < accounts.len() / 2 {
512 KeyedAccount::new_readonly(key, false, account)
513 } else {
514 KeyedAccount::new(key, false, account)
515 }
516 })
517 .collect();
518 deserialize_parameters(
519 &bpf_loader_deprecated::id(),
520 &de_keyed_accounts,
521 serialized.as_slice(),
522 &account_lengths,
523 )
524 .unwrap();
525 for ((account, de_keyed_account), key) in
526 accounts.iter().zip(de_keyed_accounts).zip(keys.clone())
527 {
528 assert_eq!(key, *de_keyed_account.unsigned_key());
529 let account = account.borrow();
530 assert_eq!(account.carats(), de_keyed_account.carats().unwrap());
531 assert_eq!(
532 account.data(),
533 de_keyed_account.try_account_ref().unwrap().data()
534 );
535 assert_eq!(*account.owner(), de_keyed_account.owner().unwrap());
536 assert_eq!(account.executable(), de_keyed_account.executable().unwrap());
537 assert_eq!(account.rent_epoch(), de_keyed_account.rent_epoch().unwrap());
538 }
539 }
540
541 #[allow(clippy::type_complexity)]
543 pub unsafe fn deserialize_unaligned<'a>(
544 input: *mut u8,
545 ) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
546 let mut offset: usize = 0;
547
548 #[allow(clippy::cast_ptr_alignment)]
551 let num_accounts = *(input.add(offset) as *const u64) as usize;
552 offset += size_of::<u64>();
553
554 let mut accounts = Vec::with_capacity(num_accounts);
557 for _ in 0..num_accounts {
558 let dup_info = *(input.add(offset) as *const u8);
559 offset += size_of::<u8>();
560 if dup_info == std::u8::MAX {
561 #[allow(clippy::cast_ptr_alignment)]
562 let is_signer = *(input.add(offset) as *const u8) != 0;
563 offset += size_of::<u8>();
564
565 #[allow(clippy::cast_ptr_alignment)]
566 let is_writable = *(input.add(offset) as *const u8) != 0;
567 offset += size_of::<u8>();
568
569 let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
570 offset += size_of::<Pubkey>();
571
572 #[allow(clippy::cast_ptr_alignment)]
573 let carats = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
574 offset += size_of::<u64>();
575
576 #[allow(clippy::cast_ptr_alignment)]
577 let data_len = *(input.add(offset) as *const u64) as usize;
578 offset += size_of::<u64>();
579
580 let data = Rc::new(RefCell::new({
581 from_raw_parts_mut(input.add(offset), data_len)
582 }));
583 offset += data_len;
584
585 let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
586 offset += size_of::<Pubkey>();
587
588 #[allow(clippy::cast_ptr_alignment)]
589 let executable = *(input.add(offset) as *const u8) != 0;
590 offset += size_of::<u8>();
591
592 #[allow(clippy::cast_ptr_alignment)]
593 let rent_epoch = *(input.add(offset) as *const u64);
594 offset += size_of::<u64>();
595
596 accounts.push(AccountInfo {
597 key,
598 is_signer,
599 is_writable,
600 carats,
601 data,
602 owner,
603 executable,
604 rent_epoch,
605 });
606 } else {
607 accounts.push(accounts[dup_info as usize].clone());
609 }
610 }
611
612 #[allow(clippy::cast_ptr_alignment)]
615 let instruction_data_len = *(input.add(offset) as *const u64) as usize;
616 offset += size_of::<u64>();
617
618 let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
619 offset += instruction_data_len;
620
621 let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
624
625 (program_id, accounts, instruction_data)
626 }
627}