Add SystemInstruction::CreateAccount support to CPI (#11649)
This commit is contained in:
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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},
|
||||||
|
@ -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()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>)>>;
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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)]
|
||||||
|
@ -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)]
|
||||||
|
Reference in New Issue
Block a user