Add SystemInstruction::CreateAccount support to CPI (#11649)
This commit is contained in:
parent
f1ba2387d3
commit
e9b610b8df
|
@ -36,21 +36,62 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||
|
||||
switch (params.data[0]) {
|
||||
case TEST_SUCCESS: {
|
||||
sol_log("Call system program");
|
||||
sol_log("Call system program create account");
|
||||
{
|
||||
sol_assert(*accounts[FROM_INDEX].lamports = 43);
|
||||
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41);
|
||||
uint64_t from_lamports = *accounts[FROM_INDEX].lamports;
|
||||
uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports;
|
||||
SolAccountMeta arguments[] = {
|
||||
{accounts[FROM_INDEX].key, false, true},
|
||||
{accounts[ARGUMENT_INDEX].key, false, false}};
|
||||
{accounts[FROM_INDEX].key, true, true},
|
||||
{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};
|
||||
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
|
||||
arguments, SOL_ARRAY_SIZE(arguments),
|
||||
data, SOL_ARRAY_SIZE(data)};
|
||||
sol_assert(SUCCESS ==
|
||||
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
|
||||
sol_assert(*accounts[FROM_INDEX].lamports = 42);
|
||||
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42);
|
||||
sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 1);
|
||||
sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 1);
|
||||
}
|
||||
|
||||
sol_log("Test data translation");
|
||||
|
@ -92,8 +133,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
|
||||
{&nonce1, 1}};
|
||||
SolPubkey address;
|
||||
sol_assert(SUCCESS == sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
|
||||
params.program_id, &address));
|
||||
sol_assert(SUCCESS ==
|
||||
sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
|
||||
params.program_id, &address));
|
||||
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use solana_bpf_rust_invoked::instruction::*;
|
|||
use solana_sdk::{
|
||||
account_info::AccountInfo,
|
||||
entrypoint,
|
||||
entrypoint::ProgramResult,
|
||||
entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE},
|
||||
info,
|
||||
program::{invoke, invoke_signed},
|
||||
program_error::ProgramError,
|
||||
|
@ -46,18 +46,52 @@ fn process_instruction(
|
|||
|
||||
match instruction_data[0] {
|
||||
TEST_SUCCESS => {
|
||||
info!("Call system program");
|
||||
info!("Call system program create account");
|
||||
{
|
||||
assert_eq!(accounts[FROM_INDEX].lamports(), 43);
|
||||
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41);
|
||||
let from_lamports = accounts[FROM_INDEX].lamports();
|
||||
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(
|
||||
accounts[FROM_INDEX].key,
|
||||
accounts[ARGUMENT_INDEX].key,
|
||||
accounts[DERIVED_KEY1_INDEX].key,
|
||||
1,
|
||||
);
|
||||
invoke(&instruction, accounts)?;
|
||||
assert_eq!(accounts[FROM_INDEX].lamports(), 42);
|
||||
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42);
|
||||
assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1);
|
||||
assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 1);
|
||||
}
|
||||
|
||||
info!("Test data translation");
|
||||
|
|
|
@ -14,6 +14,7 @@ use solana_sdk::{
|
|||
bpf_loader,
|
||||
client::SyncClient,
|
||||
clock::DEFAULT_SLOTS_PER_EPOCH,
|
||||
entrypoint::MAX_PERMITTED_DATA_INCREASE,
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
message::Message,
|
||||
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 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);
|
||||
|
||||
let invoked_argument_keypair = Keypair::new();
|
||||
|
@ -353,7 +354,7 @@ fn test_program_bpf_invoke() {
|
|||
bank.store_account(&invoked_argument_keypair.pubkey(), &account);
|
||||
|
||||
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);
|
||||
|
||||
let (derived_key1, nonce1) =
|
||||
|
@ -443,5 +444,16 @@ fn test_program_bpf_invoke() {
|
|||
.unwrap(),
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||
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::{
|
||||
io::prelude::*,
|
||||
mem::{self, align_of},
|
||||
mem::{align_of, size_of},
|
||||
};
|
||||
|
||||
/// Look for a duplicate account and return its position if found
|
||||
|
@ -47,7 +48,7 @@ pub fn serialize_parameters_unaligned(
|
|||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<Vec<u8>, InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
assert_eq!(32, size_of::<Pubkey>());
|
||||
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
|
@ -84,29 +85,29 @@ pub fn deserialize_parameters_unaligned(
|
|||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> 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() {
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
start += 1; // is_dup
|
||||
if !is_dup {
|
||||
start += mem::size_of::<u8>(); // is_signer
|
||||
start += mem::size_of::<u8>(); // is_writable
|
||||
start += mem::size_of::<Pubkey>(); // pubkey
|
||||
start += size_of::<u8>(); // is_signer
|
||||
start += size_of::<u8>(); // is_writable
|
||||
start += size_of::<Pubkey>(); // pubkey
|
||||
keyed_account.try_account_ref_mut()?.lamports =
|
||||
LittleEndian::read_u64(&buffer[start..]);
|
||||
start += mem::size_of::<u64>() // lamports
|
||||
+ mem::size_of::<u64>(); // data length
|
||||
start += size_of::<u64>() // lamports
|
||||
+ 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
|
||||
+ mem::size_of::<Pubkey>() // owner
|
||||
+ mem::size_of::<u8>() // executable
|
||||
+ mem::size_of::<u64>(); // rent_epoch
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u64>(); // rent_epoch
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -117,14 +118,12 @@ pub fn serialize_parameters_aligned(
|
|||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
) -> 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();
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
.unwrap();
|
||||
|
||||
// TODO panic?
|
||||
if v.as_ptr().align_offset(align_of::<u128>()) != 0 {
|
||||
panic!();
|
||||
}
|
||||
|
@ -148,7 +147,9 @@ pub fn serialize_parameters_aligned(
|
|||
.unwrap();
|
||||
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
|
||||
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,
|
||||
);
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
|
@ -166,33 +167,39 @@ pub fn deserialize_parameters_aligned(
|
|||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> 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() {
|
||||
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
start += 1; // is_dup
|
||||
if !is_dup {
|
||||
start += mem::size_of::<u8>() // is_signer
|
||||
+ 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
|
||||
start += size_of::<u8>(); // position
|
||||
if is_dup {
|
||||
start += 7; // padding to 64-bit aligned
|
||||
} 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(())
|
||||
|
@ -206,7 +213,6 @@ mod tests {
|
|||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
mem::size_of,
|
||||
rc::Rc,
|
||||
// Hide Result from bindgen gets confused about generics in non-generic type declarations
|
||||
slice::{from_raw_parts, from_raw_parts_mut},
|
||||
|
|
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
|||
account::KeyedAccount,
|
||||
account_info::AccountInfo,
|
||||
bpf_loader,
|
||||
entrypoint::SUCCESS,
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
entrypoint_native::{InvokeContext, Logger},
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
message::Message,
|
||||
|
@ -382,7 +382,14 @@ pub fn syscall_create_program_address(
|
|||
|
||||
// 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
|
||||
trait SyscallProcessInstruction<'a> {
|
||||
|
@ -470,27 +477,43 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
|||
for account_info in account_infos.iter() {
|
||||
let key = translate_type!(Pubkey, account_info.key as *const _, ro_regions)?;
|
||||
if account_key == key {
|
||||
let lamports_ref = {
|
||||
let lamports = {
|
||||
// Double translate lamports out of RefCell
|
||||
let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_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
|
||||
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 {
|
||||
lamports: *lamports_ref,
|
||||
lamports: *lamports,
|
||||
data: data.to_vec(),
|
||||
executable: account_info.executable,
|
||||
owner: *owner,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -582,7 +605,7 @@ struct SolAccountMeta {
|
|||
struct SolAccountInfo {
|
||||
key_addr: u64,
|
||||
lamports_addr: u64,
|
||||
data_len: usize,
|
||||
data_len: u64,
|
||||
data_addr: u64,
|
||||
owner_addr: u64,
|
||||
rent_epoch: u64,
|
||||
|
@ -672,24 +695,36 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessSolInstructionC<'a> {
|
|||
for account_info in account_infos.iter() {
|
||||
let key = translate_type!(Pubkey, account_info.key_addr, ro_regions)?;
|
||||
if account_key == key {
|
||||
let lamports_ref =
|
||||
let lamports =
|
||||
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!(
|
||||
u8,
|
||||
account_info.data_addr,
|
||||
account_info.data_len,
|
||||
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 {
|
||||
lamports: *lamports_ref,
|
||||
lamports: *lamports,
|
||||
data: data.to_vec(),
|
||||
executable: account_info.executable,
|
||||
owner: *owner,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -826,7 +861,7 @@ fn call<'a>(
|
|||
let message = Message::new(&[instruction], None);
|
||||
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 (accounts, refs) = syscall.translate_accounts(
|
||||
let (accounts, account_refs) = syscall.translate_accounts(
|
||||
&message,
|
||||
account_infos_addr,
|
||||
account_infos_len as usize,
|
||||
|
@ -860,17 +895,31 @@ fn call<'a>(
|
|||
}
|
||||
|
||||
// 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();
|
||||
if message.is_writable(i) && !account.executable {
|
||||
*lamport_ref = account.lamports;
|
||||
if data.len() != account.data.len() {
|
||||
return Err(SyscallError::InstructionError(
|
||||
InstructionError::AccountDataSizeChanged,
|
||||
)
|
||||
.into());
|
||||
*account_ref.lamports = account.lamports;
|
||||
*account_ref.owner = account.owner;
|
||||
if account_ref.data.len() != account.data.len() {
|
||||
*account_ref.ref_to_len_in_vm = account.data.len() as u64;
|
||||
*account_ref.serialized_len_ptr = account.data.len() as u64;
|
||||
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(),
|
||||
);
|
||||
}
|
||||
if account.data.len() > account_ref.data.len() + MAX_PERMITTED_DATA_INCREASE {
|
||||
return Err(
|
||||
SyscallError::InstructionError(InstructionError::InvalidRealloc).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
data.clone_from_slice(&account.data);
|
||||
account_ref
|
||||
.data
|
||||
.clone_from_slice(&account.data[0..account_ref.data.len()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
type BankStatusCache = StatusCache<Result<()>>;
|
||||
#[frozen_abi(digest = "3bFpd1M1YHHKFASfjUU5L9dUkg87TKuMzwUoUebwa8Pu")]
|
||||
#[frozen_abi(digest = "9qMSqhQjkhvvVL4bSoGek2VAnF9KdM9ARMXYA3G2f3iG")]
|
||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||
type TransactionAccountRefCells = Vec<Rc<RefCell<Account>>>;
|
||||
type TransactionLoaderRefCells = Vec<Vec<(Pubkey, RefCell<Account>)>>;
|
||||
|
|
|
@ -63,7 +63,7 @@ impl PreAccount {
|
|||
|
||||
// An account not assigned to the program cannot have its balance decrease.
|
||||
if *program_id != self.owner // line coverage used to get branch coverage
|
||||
&& self.lamports > post.lamports
|
||||
&& self.lamports > post.lamports
|
||||
{
|
||||
return Err(InstructionError::ExternalAccountLamportSpend);
|
||||
}
|
||||
|
@ -127,6 +127,7 @@ impl PreAccount {
|
|||
|
||||
pub fn update(&mut self, account: &Account) {
|
||||
self.lamports = account.lamports;
|
||||
self.owner = account.owner;
|
||||
if self.data.len() != account.data.len() {
|
||||
// Only system account can change data size, copy with alloc
|
||||
self.data = account.data.clone();
|
||||
|
|
|
@ -256,6 +256,11 @@ typedef struct {
|
|||
const SolPubkey *program_id; /** program_id of the currently executing program */
|
||||
} 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
|
||||
*
|
||||
|
@ -297,6 +302,7 @@ static bool sol_deserialize(
|
|||
uint64_t data_len = *(uint64_t *) input;
|
||||
input += sizeof(uint64_t);
|
||||
input += data_len;
|
||||
input += MAX_PERMITTED_DATA_INCREASE;
|
||||
input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding
|
||||
input += sizeof(uint64_t);
|
||||
}
|
||||
|
@ -334,6 +340,7 @@ static bool sol_deserialize(
|
|||
input += sizeof(uint64_t);
|
||||
params->ka[i].data = (uint8_t *) input;
|
||||
input += params->ka[i].data_len;
|
||||
input += MAX_PERMITTED_DATA_INCREASE;
|
||||
input = (uint8_t*)(((uint64_t)input + 8 - 1) & ~(8 - 1)); // padding
|
||||
|
||||
// rent epoch
|
||||
|
|
|
@ -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
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -99,7 +102,7 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
|
|||
let data = Rc::new(RefCell::new({
|
||||
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
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
|
|
|
@ -159,6 +159,10 @@ pub enum InstructionError {
|
|||
/// Provided seeds do not result in a valid address
|
||||
#[error("Provided seeds do not result in a valid address")]
|
||||
InvalidSeeds,
|
||||
|
||||
// Failed to reallocate account data of this length
|
||||
#[error("Failed to reallocate account data")]
|
||||
InvalidRealloc,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
|
Loading…
Reference in New Issue