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
 | ||
|  | }
 |