//! Example Rust-based BPF realloc test program #![cfg(feature = "program")] extern crate solana_program; use { crate::instructions::*, solana_bpf_rust_realloc::instructions::*, solana_program::{ account_info::AccountInfo, entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE}, instruction::{AccountMeta, Instruction}, msg, program::invoke, pubkey::Pubkey, system_instruction, system_program, }, std::convert::TryInto, }; solana_program::entrypoint!(process_instruction); #[allow(clippy::unnecessary_wraps)] fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { let account = &accounts[0]; let invoke_program_id = accounts[1].key; let pre_len = account.data_len(); let mut bump = 0; match instruction_data[0] { INVOKE_REALLOC_ZERO_RO => { msg!("invoke realloc to zero of ro account"); // Realloc RO account let mut instruction = realloc(invoke_program_id, account.key, 0, &mut bump); instruction.accounts[0].is_writable = false; invoke(&instruction, accounts)?; } INVOKE_REALLOC_ZERO => { msg!("invoke realloc to zero"); invoke( &realloc(invoke_program_id, account.key, 0, &mut bump), accounts, )?; assert_eq!(0, account.data_len()); } INVOKE_REALLOC_MAX_PLUS_ONE => { msg!("invoke realloc max + 1"); invoke( &realloc( invoke_program_id, account.key, MAX_PERMITTED_DATA_INCREASE + 1, &mut bump, ), accounts, )?; } INVOKE_REALLOC_EXTEND_MAX => { msg!("invoke realloc max"); invoke( &realloc_extend( invoke_program_id, account.key, MAX_PERMITTED_DATA_INCREASE, &mut bump, ), accounts, )?; assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len()); } INVOKE_REALLOC_MAX_TWICE => { msg!("invoke realloc max twice"); invoke( &realloc( invoke_program_id, account.key, MAX_PERMITTED_DATA_INCREASE, &mut bump, ), accounts, )?; let new_len = pre_len + MAX_PERMITTED_DATA_INCREASE; assert_eq!(new_len, account.data_len()); account.realloc(new_len + MAX_PERMITTED_DATA_INCREASE, false)?; assert_eq!(new_len + MAX_PERMITTED_DATA_INCREASE, account.data_len()); } INVOKE_REALLOC_AND_ASSIGN => { msg!("invoke realloc and assign"); invoke( &Instruction::new_with_bytes( *invoke_program_id, &[REALLOC_AND_ASSIGN], vec![AccountMeta::new(*account.key, false)], ), accounts, )?; assert_eq!(pre_len + MAX_PERMITTED_DATA_INCREASE, account.data_len()); assert_eq!(*account.owner, system_program::id()); } INVOKE_REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM => { msg!("invoke realloc and assign to self via system program"); invoke( &Instruction::new_with_bytes( *accounts[1].key, &[REALLOC_AND_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM], vec![ AccountMeta::new(*account.key, true), AccountMeta::new_readonly(*accounts[2].key, false), ], ), accounts, )?; } INVOKE_ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC => { msg!("invoke assign to self and realloc via system program"); invoke( &Instruction::new_with_bytes( *accounts[1].key, &[ASSIGN_TO_SELF_VIA_SYSTEM_PROGRAM_AND_REALLOC], vec![ AccountMeta::new(*account.key, true), AccountMeta::new_readonly(*accounts[2].key, false), ], ), accounts, )?; } INVOKE_REALLOC_INVOKE_CHECK => { msg!("realloc invoke check size"); account.realloc(100, false)?; assert_eq!(100, account.data_len()); account.try_borrow_mut_data()?[pre_len..].fill(2); invoke( &Instruction::new_with_bytes( *accounts[1].key, &[CHECK], vec![AccountMeta::new(*account.key, false)], ), accounts, )?; } INVOKE_REALLOC_TO => { let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); msg!("realloc to {}", new_len); account.realloc(new_len, false)?; assert_eq!(new_len, account.data_len()); if pre_len < new_len { account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); } } INVOKE_REALLOC_RECURSIVE => { msg!("realloc invoke recursive"); let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); let new_len = usize::from_le_bytes(bytes.try_into().unwrap()); account.realloc(new_len, false)?; assert_eq!(new_len, account.data_len()); account.try_borrow_mut_data()?[pre_len..].fill(instruction_data[1]); let final_len: usize = 200; let mut new_instruction_data = vec![]; new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); invoke( &Instruction::new_with_bytes( *program_id, &new_instruction_data, vec![ AccountMeta::new(*account.key, false), AccountMeta::new_readonly(*accounts[1].key, false), ], ), accounts, )?; assert_eq!(final_len, account.data_len()); let data = account.try_borrow_mut_data()?; for i in 0..new_len { assert_eq!(data[i], instruction_data[1]); } for i in new_len..final_len { assert_eq!(data[i], new_instruction_data[1]); } } INVOKE_CREATE_ACCOUNT_REALLOC_CHECK => { msg!("Create new account, realloc, and check"); let pre_len: usize = 100; invoke( &system_instruction::create_account( accounts[0].key, accounts[1].key, 1, pre_len as u64, program_id, ), accounts, )?; assert_eq!(pre_len, accounts[1].data_len()); accounts[1].realloc(pre_len + 1, false)?; assert_eq!(pre_len + 1, accounts[1].data_len()); assert_eq!(accounts[1].owner, program_id); let final_len: usize = 200; let mut new_instruction_data = vec![]; new_instruction_data.extend_from_slice(&[INVOKE_REALLOC_TO, 2]); new_instruction_data.extend_from_slice(&final_len.to_le_bytes()); invoke( &Instruction::new_with_bytes( *program_id, &new_instruction_data, vec![ AccountMeta::new(*accounts[1].key, false), AccountMeta::new_readonly(*accounts[3].key, false), ], ), accounts, )?; assert_eq!(final_len, accounts[1].data_len()); } INVOKE_DEALLOC_AND_ASSIGN => { msg!("realloc zerod"); let (bytes, _) = instruction_data[2..].split_at(std::mem::size_of::()); let pre_len = usize::from_le_bytes(bytes.try_into().unwrap()); let new_len = pre_len * 2; assert_eq!(pre_len, 100); { let data = account.try_borrow_mut_data()?; for i in 0..pre_len { assert_eq!(data[i], instruction_data[1]); } } invoke( &Instruction::new_with_bytes( *accounts[2].key, &[DEALLOC_AND_ASSIGN_TO_CALLER], vec![ AccountMeta::new(*account.key, false), AccountMeta::new_readonly(*accounts[1].key, false), ], ), accounts, )?; assert_eq!(account.owner, program_id); assert_eq!(account.data_len(), 0); account.realloc(new_len, false)?; assert_eq!(account.data_len(), new_len); { let data = account.try_borrow_mut_data()?; for i in 0..new_len { assert_eq!(data[i], 0); } } } INVOKE_REALLOC_MAX_INVOKE_MAX => { msg!("invoke realloc max invoke max"); assert_eq!(0, account.data_len()); account.realloc(MAX_PERMITTED_DATA_INCREASE, false)?; assert_eq!(MAX_PERMITTED_DATA_INCREASE, account.data_len()); account.assign(invoke_program_id); assert_eq!(account.owner, invoke_program_id); invoke( &realloc_extend( invoke_program_id, account.key, MAX_PERMITTED_DATA_INCREASE, &mut bump, ), accounts, )?; } INVOKE_INVOKE_MAX_TWICE => { msg!("invoke invoke max twice"); assert_eq!(0, account.data_len()); account.assign(accounts[2].key); assert_eq!(account.owner, accounts[2].key); invoke( &realloc( accounts[2].key, account.key, MAX_PERMITTED_DATA_INCREASE, &mut bump, ), accounts, )?; invoke( &realloc_extend( accounts[2].key, account.key, MAX_PERMITTED_DATA_INCREASE, &mut bump, ), accounts, )?; panic!("last invoke should fail"); } _ => panic!(), } Ok(()) }