223 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use solana_program::{
 | |
|     account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, program::invoke,
 | |
|     program_error::ProgramError, pubkey::Pubkey, system_instruction,
 | |
| };
 | |
| 
 | |
| #[derive(Debug)]
 | |
| #[repr(C)]
 | |
| struct SolInstruction {
 | |
|     program_id_addr: u64,
 | |
|     accounts_addr: u64,
 | |
|     accounts_len: usize,
 | |
|     data_addr: u64,
 | |
|     data_len: usize,
 | |
| }
 | |
| 
 | |
| /// Rust representation of C's SolAccountMeta
 | |
| #[derive(Debug)]
 | |
| #[repr(C)]
 | |
| struct SolAccountMeta {
 | |
|     pubkey_addr: u64,
 | |
|     is_writable: bool,
 | |
|     is_signer: bool,
 | |
| }
 | |
| 
 | |
| /// Rust representation of C's SolAccountInfo
 | |
| #[derive(Debug, Clone)]
 | |
| #[repr(C)]
 | |
| struct SolAccountInfo {
 | |
|     key_addr: u64,
 | |
|     lamports_addr: u64,
 | |
|     data_len: u64,
 | |
|     data_addr: u64,
 | |
|     owner_addr: u64,
 | |
|     rent_epoch: u64,
 | |
|     is_signer: bool,
 | |
|     is_writable: bool,
 | |
|     executable: bool,
 | |
| }
 | |
| 
 | |
| /// Rust representation of C's SolSignerSeed
 | |
| #[derive(Debug)]
 | |
| #[repr(C)]
 | |
| struct SolSignerSeedC {
 | |
|     addr: u64,
 | |
|     len: u64,
 | |
| }
 | |
| 
 | |
| /// Rust representation of C's SolSignerSeeds
 | |
| #[derive(Debug)]
 | |
| #[repr(C)]
 | |
| struct SolSignerSeedsC {
 | |
|     addr: u64,
 | |
|     len: u64,
 | |
| }
 | |
| 
 | |
| extern "C" {
 | |
|     fn sol_invoke_signed_c(
 | |
|         instruction_addr: *const SolInstruction,
 | |
|         account_infos_addr: *const SolAccountInfo,
 | |
|         account_infos_len: u64,
 | |
|         signers_seeds_addr: *const SolSignerSeedsC,
 | |
|         signers_seeds_len: u64,
 | |
|     ) -> u64;
 | |
| }
 | |
| 
 | |
| const READONLY_ACCOUNTS: &[SolAccountInfo] = &[
 | |
|     SolAccountInfo {
 | |
|         is_signer: false,
 | |
|         is_writable: false,
 | |
|         executable: true,
 | |
|         key_addr: 0x400000010,
 | |
|         owner_addr: 0x400000030,
 | |
|         lamports_addr: 0x400000050,
 | |
|         rent_epoch: 0,
 | |
|         data_addr: 0x400000060,
 | |
|         data_len: 14,
 | |
|     },
 | |
|     SolAccountInfo {
 | |
|         is_signer: true,
 | |
|         is_writable: true,
 | |
|         executable: false,
 | |
|         key_addr: 0x400002880,
 | |
|         owner_addr: 0x4000028A0,
 | |
|         lamports_addr: 0x4000028c0,
 | |
|         rent_epoch: 0,
 | |
|         data_addr: 0x4000028d0,
 | |
|         data_len: 0,
 | |
|     },
 | |
| ];
 | |
| 
 | |
| const PUBKEY: Pubkey = Pubkey::new_from_array([
 | |
|     0_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | |
|     0,
 | |
| ]);
 | |
| 
 | |
| fn check_preconditions(
 | |
|     in_infos: &[AccountInfo],
 | |
|     static_infos: &[SolAccountInfo],
 | |
| ) -> Result<(), ProgramError> {
 | |
|     for (in_info, static_info) in in_infos.iter().zip(static_infos) {
 | |
|         check!(in_info.key.as_ref().as_ptr() as u64, static_info.key_addr);
 | |
|         check!(
 | |
|             in_info.owner.as_ref().as_ptr() as u64,
 | |
|             static_info.owner_addr
 | |
|         );
 | |
|         check!(
 | |
|             unsafe { *in_info.lamports.as_ptr() as *const u64 as u64 },
 | |
|             static_info.lamports_addr
 | |
|         );
 | |
|         check!(
 | |
|             in_info.try_borrow_data()?.as_ptr() as u64,
 | |
|             static_info.data_addr
 | |
|         );
 | |
|         check!(in_info.data_len() as u64, static_info.data_len);
 | |
|     }
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| entrypoint!(process_instruction);
 | |
| fn process_instruction(
 | |
|     program_id: &Pubkey,
 | |
|     accounts: &[AccountInfo],
 | |
|     instruction_data: &[u8],
 | |
| ) -> ProgramResult {
 | |
|     check_preconditions(accounts, READONLY_ACCOUNTS)?;
 | |
| 
 | |
|     match instruction_data[0] {
 | |
|         1 => {
 | |
|             let system_instruction = system_instruction::allocate(accounts[1].key, 42);
 | |
|             let metas = &[SolAccountMeta {
 | |
|                 is_signer: true,
 | |
|                 is_writable: true,
 | |
|                 pubkey_addr: accounts[1].key as *const _ as u64,
 | |
|             }];
 | |
|             let instruction = SolInstruction {
 | |
|                 accounts_addr: metas.as_ptr() as u64,
 | |
|                 accounts_len: metas.len(),
 | |
|                 data_addr: system_instruction.data.as_ptr() as u64,
 | |
|                 data_len: system_instruction.data.len(),
 | |
|                 program_id_addr: accounts[0].key as *const Pubkey as u64,
 | |
|             };
 | |
|             unsafe {
 | |
|                 check!(
 | |
|                     0,
 | |
|                     sol_invoke_signed_c(
 | |
|                         &instruction as *const _,
 | |
|                         READONLY_ACCOUNTS.as_ptr(),
 | |
|                         READONLY_ACCOUNTS.len() as u64,
 | |
|                         std::ptr::null::<SolSignerSeedsC>(),
 | |
|                         0,
 | |
|                     )
 | |
|                 );
 | |
|             }
 | |
|             let ptr = &READONLY_ACCOUNTS[1].data_len as *const _ as u64 as *mut u64;
 | |
|             check!(42, unsafe { read_val(ptr) });
 | |
|         }
 | |
|         2 => {
 | |
|             // Not sure how to get a const data length in an Rc<RefCell<&mut [u8]>>
 | |
|         }
 | |
|         3 => {
 | |
|             let mut new_accounts =
 | |
|                 &mut [READONLY_ACCOUNTS[0].clone(), READONLY_ACCOUNTS[1].clone()];
 | |
|             new_accounts[1].owner_addr = &PUBKEY as *const _ as u64;
 | |
|             let system_instruction = system_instruction::assign(accounts[1].key, program_id);
 | |
|             let metas = &[SolAccountMeta {
 | |
|                 is_signer: true,
 | |
|                 is_writable: true,
 | |
|                 pubkey_addr: accounts[1].key as *const _ as u64,
 | |
|             }];
 | |
|             let instruction = SolInstruction {
 | |
|                 accounts_addr: metas.as_ptr() as u64,
 | |
|                 accounts_len: metas.len(),
 | |
|                 data_addr: system_instruction.data.as_ptr() as u64,
 | |
|                 data_len: system_instruction.data.len(),
 | |
|                 program_id_addr: accounts[0].key as *const Pubkey as u64,
 | |
|             };
 | |
|             unsafe {
 | |
|                 check!(
 | |
|                     0,
 | |
|                     sol_invoke_signed_c(
 | |
|                         &instruction as *const _,
 | |
|                         new_accounts.as_ptr(),
 | |
|                         new_accounts.len() as u64,
 | |
|                         std::ptr::null::<SolSignerSeedsC>(),
 | |
|                         0,
 | |
|                     )
 | |
|                 );
 | |
|             }
 | |
|         }
 | |
|         4 => {
 | |
|             let mut new_account = accounts[1].clone();
 | |
|             new_account.owner = &PUBKEY;
 | |
|             let instruction = system_instruction::assign(accounts[1].key, program_id);
 | |
|             invoke(&instruction, &[accounts[0].clone(), new_account])?;
 | |
|         }
 | |
|         _ => check!(0, 1),
 | |
|     }
 | |
| 
 | |
|     Ok(())
 | |
| }
 | |
| 
 | |
| #[macro_export]
 | |
| macro_rules! check {
 | |
|     ($left:expr, $right:expr) => {
 | |
|         if $left != $right {
 | |
|             msg!(
 | |
|                 "Condition failure: {:?} != {:?} at line {:?}",
 | |
|                 $left,
 | |
|                 $right,
 | |
|                 line!()
 | |
|             );
 | |
|             return Err(ProgramError::Custom(0));
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| /// Skirt the compiler and force a read from a const value
 | |
| /// # Safety
 | |
| #[inline(never)]
 | |
| pub unsafe fn read_val<T: Copy>(ptr: *mut T) -> T {
 | |
|     *ptr
 | |
| }
 |