Add SystemInstruction::CreateAccount support to CPI (#11649)

This commit is contained in:
Jack May
2020-08-17 13:38:42 -07:00
committed by GitHub
parent f1ba2387d3
commit e9b610b8df
10 changed files with 243 additions and 85 deletions

View File

@ -36,21 +36,62 @@ extern uint64_t entrypoint(const uint8_t *input) {
switch (params.data[0]) { switch (params.data[0]) {
case TEST_SUCCESS: { case TEST_SUCCESS: {
sol_log("Call system program"); sol_log("Call system program create account");
{ {
sol_assert(*accounts[FROM_INDEX].lamports = 43); uint64_t from_lamports = *accounts[FROM_INDEX].lamports;
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41); uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports;
SolAccountMeta arguments[] = { SolAccountMeta arguments[] = {
{accounts[FROM_INDEX].key, false, true}, {accounts[FROM_INDEX].key, true, true},
{accounts[ARGUMENT_INDEX].key, false, false}}; {accounts[DERIVED_KEY1_INDEX].key, true, true}};
uint8_t data[4 + 8 + 8 + 32];
*(uint64_t *)(data + 4) = 42;
*(uint64_t *)(data + 4 + 8) = MAX_PERMITTED_DATA_INCREASE;
sol_memcpy(data + 4 + 8 + 8, params.program_id, SIZE_PUBKEY);
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)};
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
' ', 'b', 'u', 't', 't', 'e', 'r'};
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
{&nonce1, 1}};
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}};
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
SOL_ARRAY_SIZE(accounts),
signers_seeds,
SOL_ARRAY_SIZE(signers_seeds)));
sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 42);
sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 42);
sol_assert(SolPubkey_same(accounts[DERIVED_KEY1_INDEX].owner,
params.program_id));
sol_assert(accounts[DERIVED_KEY1_INDEX].data_len ==
MAX_PERMITTED_DATA_INCREASE);
sol_assert(
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] ==
0);
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f;
sol_assert(
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] ==
0x0f);
for (uint8_t i = 0; i < 20; i++) {
accounts[DERIVED_KEY1_INDEX].data[i] = i;
}
}
sol_log("Call system program transfer");
{
uint64_t from_lamports = *accounts[FROM_INDEX].lamports;
uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports;
SolAccountMeta arguments[] = {
{accounts[FROM_INDEX].key, true, true},
{accounts[DERIVED_KEY1_INDEX].key, true, false}};
uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}; uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key, const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
arguments, SOL_ARRAY_SIZE(arguments), arguments, SOL_ARRAY_SIZE(arguments),
data, SOL_ARRAY_SIZE(data)}; data, SOL_ARRAY_SIZE(data)};
sol_assert(SUCCESS == sol_assert(SUCCESS ==
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts))); sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
sol_assert(*accounts[FROM_INDEX].lamports = 42); sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 1);
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42); sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 1);
} }
sol_log("Test data translation"); sol_log("Test data translation");
@ -92,7 +133,8 @@ extern uint64_t entrypoint(const uint8_t *input) {
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)}, const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
{&nonce1, 1}}; {&nonce1, 1}};
SolPubkey address; SolPubkey address;
sol_assert(SUCCESS == sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1), sol_assert(SUCCESS ==
sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
params.program_id, &address)); params.program_id, &address));
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key)); sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
} }

View File

@ -8,7 +8,7 @@ use solana_bpf_rust_invoked::instruction::*;
use solana_sdk::{ use solana_sdk::{
account_info::AccountInfo, account_info::AccountInfo,
entrypoint, entrypoint,
entrypoint::ProgramResult, entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE},
info, info,
program::{invoke, invoke_signed}, program::{invoke, invoke_signed},
program_error::ProgramError, program_error::ProgramError,
@ -46,18 +46,52 @@ fn process_instruction(
match instruction_data[0] { match instruction_data[0] {
TEST_SUCCESS => { TEST_SUCCESS => {
info!("Call system program"); info!("Call system program create account");
{ {
assert_eq!(accounts[FROM_INDEX].lamports(), 43); let from_lamports = accounts[FROM_INDEX].lamports();
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41); let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports();
assert_eq!(accounts[DERIVED_KEY1_INDEX].data_len(), 0);
assert!(solana_sdk::system_program::check_id(
accounts[DERIVED_KEY1_INDEX].owner
));
let instruction = system_instruction::create_account(
accounts[FROM_INDEX].key,
accounts[DERIVED_KEY1_INDEX].key,
42,
MAX_PERMITTED_DATA_INCREASE as u64,
program_id,
);
invoke_signed(&instruction, accounts, &[&[b"You pass butter", &[nonce1]]])?;
assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 42);
assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 42);
assert_eq!(program_id, accounts[DERIVED_KEY1_INDEX].owner);
assert_eq!(
accounts[DERIVED_KEY1_INDEX].data_len(),
MAX_PERMITTED_DATA_INCREASE
);
let mut data = accounts[DERIVED_KEY1_INDEX].try_borrow_mut_data()?;
assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0);
data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f;
assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0x0f);
for i in 0..20 {
data[i] = i as u8;
}
}
info!("Call system program transfer");
{
let from_lamports = accounts[FROM_INDEX].lamports();
let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports();
let instruction = system_instruction::transfer( let instruction = system_instruction::transfer(
accounts[FROM_INDEX].key, accounts[FROM_INDEX].key,
accounts[ARGUMENT_INDEX].key, accounts[DERIVED_KEY1_INDEX].key,
1, 1,
); );
invoke(&instruction, accounts)?; invoke(&instruction, accounts)?;
assert_eq!(accounts[FROM_INDEX].lamports(), 42); assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1);
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42); assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 1);
} }
info!("Test data translation"); info!("Test data translation");

View File

@ -14,6 +14,7 @@ use solana_sdk::{
bpf_loader, bpf_loader,
client::SyncClient, client::SyncClient,
clock::DEFAULT_SLOTS_PER_EPOCH, clock::DEFAULT_SLOTS_PER_EPOCH,
entrypoint::MAX_PERMITTED_DATA_INCREASE,
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
@ -345,7 +346,7 @@ fn test_program_bpf_invoke() {
let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1); let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1);
let argument_keypair = Keypair::new(); let argument_keypair = Keypair::new();
let account = Account::new(41, 100, &invoke_program_id); let account = Account::new(42, 100, &invoke_program_id);
bank.store_account(&argument_keypair.pubkey(), &account); bank.store_account(&argument_keypair.pubkey(), &account);
let invoked_argument_keypair = Keypair::new(); let invoked_argument_keypair = Keypair::new();
@ -353,7 +354,7 @@ fn test_program_bpf_invoke() {
bank.store_account(&invoked_argument_keypair.pubkey(), &account); bank.store_account(&invoked_argument_keypair.pubkey(), &account);
let from_keypair = Keypair::new(); let from_keypair = Keypair::new();
let account = Account::new(43, 0, &solana_sdk::system_program::id()); let account = Account::new(84, 0, &solana_sdk::system_program::id());
bank.store_account(&from_keypair.pubkey(), &account); bank.store_account(&from_keypair.pubkey(), &account);
let (derived_key1, nonce1) = let (derived_key1, nonce1) =
@ -443,5 +444,16 @@ fn test_program_bpf_invoke() {
.unwrap(), .unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(194969602)) TransactionError::InstructionError(0, InstructionError::Custom(194969602))
); );
assert_eq!(43, bank.get_balance(&derived_key1));
let account = bank.get_account(&derived_key1).unwrap();
assert_eq!(invoke_program_id, account.owner);
assert_eq!(
MAX_PERMITTED_DATA_INCREASE,
bank.get_account(&derived_key1).unwrap().data.len()
);
for i in 0..20 {
assert_eq!(i as u8, account.data[i]);
}
} }
} }

View File

@ -1,10 +1,11 @@
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use solana_sdk::{ use solana_sdk::{
account::KeyedAccount, bpf_loader_deprecated, instruction::InstructionError, pubkey::Pubkey, account::KeyedAccount, bpf_loader_deprecated, entrypoint::MAX_PERMITTED_DATA_INCREASE,
instruction::InstructionError, pubkey::Pubkey,
}; };
use std::{ use std::{
io::prelude::*, io::prelude::*,
mem::{self, align_of}, mem::{align_of, size_of},
}; };
/// Look for a duplicate account and return its position if found /// Look for a duplicate account and return its position if found
@ -47,7 +48,7 @@ pub fn serialize_parameters_unaligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
instruction_data: &[u8], instruction_data: &[u8],
) -> Result<Vec<u8>, InstructionError> { ) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>()); assert_eq!(32, size_of::<Pubkey>());
let mut v: Vec<u8> = Vec::new(); let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64) v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
@ -84,29 +85,29 @@ pub fn deserialize_parameters_unaligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
buffer: &[u8], buffer: &[u8],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>()); assert_eq!(32, size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts let mut start = size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() { for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup start += 1; // is_dup
if !is_dup { if !is_dup {
start += mem::size_of::<u8>(); // is_signer start += size_of::<u8>(); // is_signer
start += mem::size_of::<u8>(); // is_writable start += size_of::<u8>(); // is_writable
start += mem::size_of::<Pubkey>(); // pubkey start += size_of::<Pubkey>(); // pubkey
keyed_account.try_account_ref_mut()?.lamports = keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]); LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports start += size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length + size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?; let end = start + keyed_account.data_len()?;
keyed_account keyed_account
.try_account_ref_mut()? .try_account_ref_mut()?
.data .data
.clone_from_slice(&buffer[start..end]); .clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()? // data start += keyed_account.data_len()? // data
+ mem::size_of::<Pubkey>() // owner + size_of::<Pubkey>() // owner
+ mem::size_of::<u8>() // executable + size_of::<u8>() // executable
+ mem::size_of::<u64>(); // rent_epoch + size_of::<u64>(); // rent_epoch
} }
} }
Ok(()) Ok(())
@ -117,14 +118,12 @@ pub fn serialize_parameters_aligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
instruction_data: &[u8], instruction_data: &[u8],
) -> Result<Vec<u8>, InstructionError> { ) -> Result<Vec<u8>, InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>()); assert_eq!(32, size_of::<Pubkey>());
// TODO use with capacity would be nice, but don't know account data sizes...
let mut v: Vec<u8> = Vec::new(); let mut v: Vec<u8> = Vec::new();
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64) v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
.unwrap(); .unwrap();
// TODO panic?
if v.as_ptr().align_offset(align_of::<u128>()) != 0 { if v.as_ptr().align_offset(align_of::<u128>()) != 0 {
panic!(); panic!();
} }
@ -148,7 +147,9 @@ pub fn serialize_parameters_aligned(
.unwrap(); .unwrap();
v.write_all(&keyed_account.try_account_ref()?.data).unwrap(); v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
v.resize( v.resize(
v.len() + (v.len() as *const u8).align_offset(align_of::<u128>()), v.len()
+ MAX_PERMITTED_DATA_INCREASE
+ (v.len() as *const u8).align_offset(align_of::<u128>()),
0, 0,
); );
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64) v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
@ -166,33 +167,39 @@ pub fn deserialize_parameters_aligned(
keyed_accounts: &[KeyedAccount], keyed_accounts: &[KeyedAccount],
buffer: &[u8], buffer: &[u8],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
assert_eq!(32, mem::size_of::<Pubkey>()); assert_eq!(32, size_of::<Pubkey>());
let mut start = mem::size_of::<u64>(); // number of accounts let mut start = size_of::<u64>(); // number of accounts
for (i, keyed_account) in keyed_accounts.iter().enumerate() { for (i, keyed_account) in keyed_accounts.iter().enumerate() {
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account); let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
start += 1; // is_dup start += size_of::<u8>(); // position
if !is_dup { if is_dup {
start += mem::size_of::<u8>() // is_signer start += 7; // padding to 64-bit aligned
+ mem::size_of::<u8>() // is_writable
+ mem::size_of::<u8>() // executable
+ 4 // padding
+ mem::size_of::<Pubkey>() // pubkey
+ mem::size_of::<Pubkey>(); // owner
keyed_account.try_account_ref_mut()?.lamports =
LittleEndian::read_u64(&buffer[start..]);
start += mem::size_of::<u64>() // lamports
+ mem::size_of::<u64>(); // data length
let end = start + keyed_account.data_len()?;
keyed_account
.try_account_ref_mut()?
.data
.clone_from_slice(&buffer[start..end]);
start += keyed_account.data_len()?; // data
start += (start as *const u8).align_offset(align_of::<u128>());
start += mem::size_of::<u64>(); // rent_epoch
} else { } else {
start += 7; // padding let mut account = keyed_account.try_account_ref_mut()?;
start += size_of::<u8>() // is_signer
+ size_of::<u8>() // is_writable
+ size_of::<u8>() // executable
+ 4 // padding to 128-bit aligned
+ size_of::<Pubkey>(); // key
account.owner = Pubkey::new(&buffer[start..start + size_of::<Pubkey>()]);
start += size_of::<Pubkey>(); // owner
account.lamports = LittleEndian::read_u64(&buffer[start..]);
start += size_of::<u64>(); // lamports
let pre_len = account.data.len();
let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
start += size_of::<u64>(); // data length
let mut data_end = start + pre_len;
if post_len != pre_len
&& (post_len.saturating_sub(pre_len)) <= MAX_PERMITTED_DATA_INCREASE
{
account.data.resize(post_len, 0);
data_end = start + post_len;
}
account.data.clone_from_slice(&buffer[start..data_end]);
start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data
start += (start as *const u8).align_offset(align_of::<u128>());
start += size_of::<u64>(); // rent_epoch
} }
} }
Ok(()) Ok(())
@ -206,7 +213,6 @@ mod tests {
}; };
use std::{ use std::{
cell::RefCell, cell::RefCell,
mem::size_of,
rc::Rc, rc::Rc,
// Hide Result from bindgen gets confused about generics in non-generic type declarations // Hide Result from bindgen gets confused about generics in non-generic type declarations
slice::{from_raw_parts, from_raw_parts_mut}, slice::{from_raw_parts, from_raw_parts_mut},

View File

@ -11,7 +11,7 @@ use solana_sdk::{
account::KeyedAccount, account::KeyedAccount,
account_info::AccountInfo, account_info::AccountInfo,
bpf_loader, bpf_loader,
entrypoint::SUCCESS, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
entrypoint_native::{InvokeContext, Logger}, entrypoint_native::{InvokeContext, Logger},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
message::Message, message::Message,
@ -382,7 +382,14 @@ pub fn syscall_create_program_address(
// Cross-program invocation syscalls // Cross-program invocation syscalls
pub type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<(&'a mut u64, &'a mut [u8])>); struct AccountReferences<'a> {
lamports: &'a mut u64,
owner: &'a mut Pubkey,
data: &'a mut [u8],
ref_to_len_in_vm: &'a mut u64,
serialized_len_ptr: &'a mut u64,
}
type TranslatedAccounts<'a> = (Vec<Rc<RefCell<Account>>>, Vec<AccountReferences<'a>>);
/// Implemented by language specific data structure translators /// Implemented by language specific data structure translators
trait SyscallProcessInstruction<'a> { trait SyscallProcessInstruction<'a> {
@ -470,27 +477,43 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
for account_info in account_infos.iter() { for account_info in account_infos.iter() {
let key = translate_type!(Pubkey, account_info.key as *const _, ro_regions)?; let key = translate_type!(Pubkey, account_info.key as *const _, ro_regions)?;
if account_key == key { if account_key == key {
let lamports_ref = { let lamports = {
// Double translate lamports out of RefCell // Double translate lamports out of RefCell
let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_regions)?; let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_regions)?;
translate_type_mut!(u64, *ptr, rw_regions)? translate_type_mut!(u64, *ptr, rw_regions)?
}; };
let data = { let owner =
translate_type_mut!(Pubkey, account_info.owner as *const _, ro_regions)?;
let (data, ref_to_len_in_vm, serialized_len_ptr) = {
// Double translate data out of RefCell // Double translate data out of RefCell
let data = *translate_type!(&[u8], account_info.data.as_ptr(), ro_regions)?; let data = *translate_type!(&[u8], account_info.data.as_ptr(), ro_regions)?;
translate_slice_mut!(u8, data.as_ptr(), data.len(), rw_regions)? let translated =
translate!(account_info.data.as_ptr(), 8, ro_regions)? as *mut u64;
let ref_to_len_in_vm = unsafe { &mut *translated.offset(1) };
let ref_of_len_in_input_buffer = unsafe { data.as_ptr().offset(-8) };
let serialized_len_ptr =
translate_type_mut!(u64, ref_of_len_in_input_buffer, rw_regions)?;
(
translate_slice_mut!(u8, data.as_ptr(), data.len(), rw_regions)?,
ref_to_len_in_vm,
serialized_len_ptr,
)
}; };
let owner =
translate_type!(Pubkey, account_info.owner as *const _, ro_regions)?;
accounts.push(Rc::new(RefCell::new(Account { accounts.push(Rc::new(RefCell::new(Account {
lamports: *lamports_ref, lamports: *lamports,
data: data.to_vec(), data: data.to_vec(),
executable: account_info.executable, executable: account_info.executable,
owner: *owner, owner: *owner,
rent_epoch: account_info.rent_epoch, rent_epoch: account_info.rent_epoch,
}))); })));
refs.push((lamports_ref, data)); refs.push(AccountReferences {
lamports,
owner,
data,
ref_to_len_in_vm,
serialized_len_ptr,
});
continue 'root; continue 'root;
} }
} }
@ -582,7 +605,7 @@ struct SolAccountMeta {
struct SolAccountInfo { struct SolAccountInfo {
key_addr: u64, key_addr: u64,
lamports_addr: u64, lamports_addr: u64,
data_len: usize, data_len: u64,
data_addr: u64, data_addr: u64,
owner_addr: u64, owner_addr: u64,
rent_epoch: u64, rent_epoch: u64,
@ -672,24 +695,36 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
for account_info in account_infos.iter() { for account_info in account_infos.iter() {
let key = translate_type!(Pubkey, account_info.key_addr, ro_regions)?; let key = translate_type!(Pubkey, account_info.key_addr, ro_regions)?;
if account_key == key { if account_key == key {
let lamports_ref = let lamports =
translate_type_mut!(u64, account_info.lamports_addr, rw_regions)?; translate_type_mut!(u64, account_info.lamports_addr, rw_regions)?;
let owner = translate_type_mut!(Pubkey, account_info.owner_addr, ro_regions)?;
let data = translate_slice_mut!( let data = translate_slice_mut!(
u8, u8,
account_info.data_addr, account_info.data_addr,
account_info.data_len, account_info.data_len,
rw_regions rw_regions
)?; )?;
let owner = translate_type!(Pubkey, account_info.owner_addr, ro_regions)?; let ref_to_len_in_vm =
unsafe { &mut *(&account_info.data_len as *const u64 as u64 as *mut u64) };
let ref_of_len_in_input_buffer =
unsafe { (account_info.data_addr as *mut u8).offset(-8) };
let serialized_len_ptr =
translate_type_mut!(u64, ref_of_len_in_input_buffer, rw_regions)?;
accounts.push(Rc::new(RefCell::new(Account { accounts.push(Rc::new(RefCell::new(Account {
lamports: *lamports_ref, lamports: *lamports,
data: data.to_vec(), data: data.to_vec(),
executable: account_info.executable, executable: account_info.executable,
owner: *owner, owner: *owner,
rent_epoch: account_info.rent_epoch, rent_epoch: account_info.rent_epoch,
}))); })));
refs.push((lamports_ref, data)); refs.push(AccountReferences {
lamports,
owner,
data,
ref_to_len_in_vm,
serialized_len_ptr,
});
continue 'root; continue 'root;
} }
} }
@ -826,7 +861,7 @@ fn call<'a>(
let message = Message::new(&[instruction], None); let message = Message::new(&[instruction], None);
let callee_program_id_index = message.instructions[0].program_id_index as usize; let callee_program_id_index = message.instructions[0].program_id_index as usize;
let callee_program_id = message.account_keys[callee_program_id_index]; let callee_program_id = message.account_keys[callee_program_id_index];
let (accounts, refs) = syscall.translate_accounts( let (accounts, account_refs) = syscall.translate_accounts(
&message, &message,
account_infos_addr, account_infos_addr,
account_infos_len as usize, account_infos_len as usize,
@ -860,17 +895,31 @@ fn call<'a>(
} }
// Copy results back into caller's AccountInfos // Copy results back into caller's AccountInfos
for (i, (account, (lamport_ref, data))) in accounts.iter().zip(refs).enumerate() {
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
let account = account.borrow(); let account = account.borrow();
if message.is_writable(i) && !account.executable { if message.is_writable(i) && !account.executable {
*lamport_ref = account.lamports; *account_ref.lamports = account.lamports;
if data.len() != account.data.len() { *account_ref.owner = account.owner;
return Err(SyscallError::InstructionError( if account_ref.data.len() != account.data.len() {
InstructionError::AccountDataSizeChanged, *account_ref.ref_to_len_in_vm = account.data.len() as u64;
) *account_ref.serialized_len_ptr = account.data.len() as u64;
.into()); if !account_ref.data.is_empty() {
// Only support for `CreateAccount` at this time.
// Need a way to limit total realloc size accross multiple CPI calls
return Err(
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
);
} }
data.clone_from_slice(&account.data); if account.data.len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
return Err(
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
);
}
}
account_ref
.data
.clone_from_slice(&account.data[0..account_ref.data.len()]);
} }
} }

View File

@ -76,7 +76,7 @@ pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0;
pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5;
type BankStatusCache = StatusCache<Result<()>>; type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "3bFpd1M1YHHKFASfjUU5L9dUkg87TKuMzwUoUebwa8Pu")] #[frozen_abi(digest = "9qMSqhQjkhvvVL4bSoGek2VAnF9KdM9ARMXYA3G2f3iG")]
pub type BankSlotDelta = SlotDelta<Result<()>>; pub type BankSlotDelta = SlotDelta<Result<()>>;
type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>; type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>;
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>; type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>;

View File

@ -127,6 +127,7 @@ impl PreAccount {
pub fn update(&mut self, account: &Account) { pub fn update(&mut self, account: &Account) {
self.lamports = account.lamports; self.lamports = account.lamports;
self.owner = account.owner;
if self.data.len() != account.data.len() { if self.data.len() != account.data.len() {
// Only system account can change data size, copy with alloc // Only system account can change data size, copy with alloc
self.data = account.data.clone(); self.data = account.data.clone();

View File

@ -256,6 +256,11 @@ typedef struct {
const SolPubkey *program_id; /** program_id of the currently executing program */ const SolPubkey *program_id; /** program_id of the currently executing program */
} SolParameters; } SolParameters;
/**
* Maximum number of bytes a program may add to an account during a single realloc
*/
#define MAX_PERMITTED_DATA_INCREASE (1024 * 10)
/** /**
* De-serializes the input parameters into usable types * De-serializes the input parameters into usable types
* *
@ -297,6 +302,7 @@ static bool sol_deserialize(
uint64_t data_len = *(uint64_t *) input; uint64_t data_len = *(uint64_t *) input;
input += sizeof(uint64_t); input += sizeof(uint64_t);
input += data_len; input += data_len;
input += MAX_PERMITTED_DATA_INCREASE;
input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding
input += sizeof(uint64_t); input += sizeof(uint64_t);
} }
@ -334,6 +340,7 @@ static bool sol_deserialize(
input += sizeof(uint64_t); input += sizeof(uint64_t);
params->ka[i].data = (uint8_t *) input; params->ka[i].data = (uint8_t *) input;
input += params->ka[i].data_len; input += params->ka[i].data_len;
input += MAX_PERMITTED_DATA_INCREASE;
input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding
// rent epoch // rent epoch

View File

@ -48,6 +48,9 @@ macro_rules! entrypoint {
}; };
} }
/// Maximum number of bytes a program may add to an account during a single realloc
pub const MAX_PERMITTED_DATA_INCREASE: usize = 1_024 * 10;
/// Deserialize the input arguments /// Deserialize the input arguments
/// ///
/// # Safety /// # Safety
@ -99,7 +102,7 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
let data = Rc::new(RefCell::new({ let data = Rc::new(RefCell::new({
from_raw_parts_mut(input.add(offset), data_len) from_raw_parts_mut(input.add(offset), data_len)
})); }));
offset += data_len; offset += data_len + MAX_PERMITTED_DATA_INCREASE;
offset += (offset as *const u8).align_offset(align_of::<u128>()); // padding offset += (offset as *const u8).align_offset(align_of::<u128>()); // padding
#[allow(clippy::cast_ptr_alignment)] #[allow(clippy::cast_ptr_alignment)]

View File

@ -159,6 +159,10 @@ pub enum InstructionError {
/// Provided seeds do not result in a valid address /// Provided seeds do not result in a valid address
#[error("Provided seeds do not result in a valid address")] #[error("Provided seeds do not result in a valid address")]
InvalidSeeds, InvalidSeeds,
// Failed to reallocate account data of this length
#[error("Failed to reallocate account data")]
InvalidRealloc,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]