Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
Add SystemInstruction::CreateAccount support to CPI (#11649)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackcmay authored Aug 17, 2020
1 parent f1ba238 commit e9b610b
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 85 deletions.
60 changes: 51 additions & 9 deletions programs/bpf/c/src/invoke/invoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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));
}

Expand Down
48 changes: 41 additions & 7 deletions programs/bpf/rust/invoke/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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");
Expand Down
16 changes: 14 additions & 2 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -345,15 +346,15 @@ 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();
let account = Account::new(10, 10, &invoked_program_id);
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) =
Expand Down Expand Up @@ -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]);
}
}
}
88 changes: 47 additions & 41 deletions programs/bpf_loader/src/serialization.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(())
Expand All @@ -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!();
}
Expand All @@ -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)
Expand All @@ -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(())
Expand All @@ -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},
Expand Down
Loading

0 comments on commit e9b610b

Please sign in to comment.