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

Commit e9b610b

Browse files
authored
Add SystemInstruction::CreateAccount support to CPI (#11649)
1 parent f1ba238 commit e9b610b

File tree

10 files changed

+243
-85
lines changed

10 files changed

+243
-85
lines changed

programs/bpf/c/src/invoke/invoke.c

+51-9
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,62 @@ extern uint64_t entrypoint(const uint8_t *input) {
3636

3737
switch (params.data[0]) {
3838
case TEST_SUCCESS: {
39-
sol_log("Call system program");
39+
sol_log("Call system program create account");
4040
{
41-
sol_assert(*accounts[FROM_INDEX].lamports = 43);
42-
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 41);
41+
uint64_t from_lamports = *accounts[FROM_INDEX].lamports;
42+
uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports;
4343
SolAccountMeta arguments[] = {
44-
{accounts[FROM_INDEX].key, false, true},
45-
{accounts[ARGUMENT_INDEX].key, false, false}};
44+
{accounts[FROM_INDEX].key, true, true},
45+
{accounts[DERIVED_KEY1_INDEX].key, true, true}};
46+
uint8_t data[4 + 8 + 8 + 32];
47+
*(uint64_t *)(data + 4) = 42;
48+
*(uint64_t *)(data + 4 + 8) = MAX_PERMITTED_DATA_INCREASE;
49+
sol_memcpy(data + 4 + 8 + 8, params.program_id, SIZE_PUBKEY);
50+
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
51+
arguments, SOL_ARRAY_SIZE(arguments),
52+
data, SOL_ARRAY_SIZE(data)};
53+
uint8_t seed1[] = {'Y', 'o', 'u', ' ', 'p', 'a', 's', 's',
54+
' ', 'b', 'u', 't', 't', 'e', 'r'};
55+
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
56+
{&nonce1, 1}};
57+
const SolSignerSeeds signers_seeds[] = {{seeds1, SOL_ARRAY_SIZE(seeds1)}};
58+
sol_assert(SUCCESS == sol_invoke_signed(&instruction, accounts,
59+
SOL_ARRAY_SIZE(accounts),
60+
signers_seeds,
61+
SOL_ARRAY_SIZE(signers_seeds)));
62+
sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 42);
63+
sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 42);
64+
sol_assert(SolPubkey_same(accounts[DERIVED_KEY1_INDEX].owner,
65+
params.program_id));
66+
sol_assert(accounts[DERIVED_KEY1_INDEX].data_len ==
67+
MAX_PERMITTED_DATA_INCREASE);
68+
sol_assert(
69+
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] ==
70+
0);
71+
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f;
72+
sol_assert(
73+
accounts[DERIVED_KEY1_INDEX].data[MAX_PERMITTED_DATA_INCREASE - 1] ==
74+
0x0f);
75+
for (uint8_t i = 0; i < 20; i++) {
76+
accounts[DERIVED_KEY1_INDEX].data[i] = i;
77+
}
78+
}
79+
80+
sol_log("Call system program transfer");
81+
{
82+
uint64_t from_lamports = *accounts[FROM_INDEX].lamports;
83+
uint64_t to_lamports = *accounts[DERIVED_KEY1_INDEX].lamports;
84+
SolAccountMeta arguments[] = {
85+
{accounts[FROM_INDEX].key, true, true},
86+
{accounts[DERIVED_KEY1_INDEX].key, true, false}};
4687
uint8_t data[] = {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0};
4788
const SolInstruction instruction = {accounts[SYSTEM_PROGRAM_INDEX].key,
4889
arguments, SOL_ARRAY_SIZE(arguments),
4990
data, SOL_ARRAY_SIZE(data)};
5091
sol_assert(SUCCESS ==
5192
sol_invoke(&instruction, accounts, SOL_ARRAY_SIZE(accounts)));
52-
sol_assert(*accounts[FROM_INDEX].lamports = 42);
53-
sol_assert(*accounts[ARGUMENT_INDEX].lamports = 42);
93+
sol_assert(*accounts[FROM_INDEX].lamports == from_lamports - 1);
94+
sol_assert(*accounts[DERIVED_KEY1_INDEX].lamports == to_lamports + 1);
5495
}
5596

5697
sol_log("Test data translation");
@@ -92,8 +133,9 @@ extern uint64_t entrypoint(const uint8_t *input) {
92133
const SolSignerSeed seeds1[] = {{seed1, SOL_ARRAY_SIZE(seed1)},
93134
{&nonce1, 1}};
94135
SolPubkey address;
95-
sol_assert(SUCCESS == sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
96-
params.program_id, &address));
136+
sol_assert(SUCCESS ==
137+
sol_create_program_address(seeds1, SOL_ARRAY_SIZE(seeds1),
138+
params.program_id, &address));
97139
sol_assert(SolPubkey_same(&address, accounts[DERIVED_KEY1_INDEX].key));
98140
}
99141

programs/bpf/rust/invoke/src/lib.rs

+41-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use solana_bpf_rust_invoked::instruction::*;
88
use solana_sdk::{
99
account_info::AccountInfo,
1010
entrypoint,
11-
entrypoint::ProgramResult,
11+
entrypoint::{ProgramResult, MAX_PERMITTED_DATA_INCREASE},
1212
info,
1313
program::{invoke, invoke_signed},
1414
program_error::ProgramError,
@@ -46,18 +46,52 @@ fn process_instruction(
4646

4747
match instruction_data[0] {
4848
TEST_SUCCESS => {
49-
info!("Call system program");
49+
info!("Call system program create account");
5050
{
51-
assert_eq!(accounts[FROM_INDEX].lamports(), 43);
52-
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 41);
51+
let from_lamports = accounts[FROM_INDEX].lamports();
52+
let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports();
53+
assert_eq!(accounts[DERIVED_KEY1_INDEX].data_len(), 0);
54+
assert!(solana_sdk::system_program::check_id(
55+
accounts[DERIVED_KEY1_INDEX].owner
56+
));
57+
58+
let instruction = system_instruction::create_account(
59+
accounts[FROM_INDEX].key,
60+
accounts[DERIVED_KEY1_INDEX].key,
61+
42,
62+
MAX_PERMITTED_DATA_INCREASE as u64,
63+
program_id,
64+
);
65+
invoke_signed(&instruction, accounts, &[&[b"You pass butter", &[nonce1]]])?;
66+
67+
assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 42);
68+
assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 42);
69+
assert_eq!(program_id, accounts[DERIVED_KEY1_INDEX].owner);
70+
assert_eq!(
71+
accounts[DERIVED_KEY1_INDEX].data_len(),
72+
MAX_PERMITTED_DATA_INCREASE
73+
);
74+
let mut data = accounts[DERIVED_KEY1_INDEX].try_borrow_mut_data()?;
75+
assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0);
76+
data[MAX_PERMITTED_DATA_INCREASE - 1] = 0x0f;
77+
assert_eq!(data[MAX_PERMITTED_DATA_INCREASE - 1], 0x0f);
78+
for i in 0..20 {
79+
data[i] = i as u8;
80+
}
81+
}
82+
83+
info!("Call system program transfer");
84+
{
85+
let from_lamports = accounts[FROM_INDEX].lamports();
86+
let to_lamports = accounts[DERIVED_KEY1_INDEX].lamports();
5387
let instruction = system_instruction::transfer(
5488
accounts[FROM_INDEX].key,
55-
accounts[ARGUMENT_INDEX].key,
89+
accounts[DERIVED_KEY1_INDEX].key,
5690
1,
5791
);
5892
invoke(&instruction, accounts)?;
59-
assert_eq!(accounts[FROM_INDEX].lamports(), 42);
60-
assert_eq!(accounts[ARGUMENT_INDEX].lamports(), 42);
93+
assert_eq!(accounts[FROM_INDEX].lamports(), from_lamports - 1);
94+
assert_eq!(accounts[DERIVED_KEY1_INDEX].lamports(), to_lamports + 1);
6195
}
6296

6397
info!("Test data translation");

programs/bpf/tests/programs.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use solana_sdk::{
1414
bpf_loader,
1515
client::SyncClient,
1616
clock::DEFAULT_SLOTS_PER_EPOCH,
17+
entrypoint::MAX_PERMITTED_DATA_INCREASE,
1718
instruction::{AccountMeta, Instruction, InstructionError},
1819
message::Message,
1920
pubkey::Pubkey,
@@ -345,15 +346,15 @@ fn test_program_bpf_invoke() {
345346
let invoked_program_id = load_bpf_program(&bank_client, &mint_keypair, program.1);
346347

347348
let argument_keypair = Keypair::new();
348-
let account = Account::new(41, 100, &invoke_program_id);
349+
let account = Account::new(42, 100, &invoke_program_id);
349350
bank.store_account(&argument_keypair.pubkey(), &account);
350351

351352
let invoked_argument_keypair = Keypair::new();
352353
let account = Account::new(10, 10, &invoked_program_id);
353354
bank.store_account(&invoked_argument_keypair.pubkey(), &account);
354355

355356
let from_keypair = Keypair::new();
356-
let account = Account::new(43, 0, &solana_sdk::system_program::id());
357+
let account = Account::new(84, 0, &solana_sdk::system_program::id());
357358
bank.store_account(&from_keypair.pubkey(), &account);
358359

359360
let (derived_key1, nonce1) =
@@ -443,5 +444,16 @@ fn test_program_bpf_invoke() {
443444
.unwrap(),
444445
TransactionError::InstructionError(0, InstructionError::Custom(194969602))
445446
);
447+
448+
assert_eq!(43, bank.get_balance(&derived_key1));
449+
let account = bank.get_account(&derived_key1).unwrap();
450+
assert_eq!(invoke_program_id, account.owner);
451+
assert_eq!(
452+
MAX_PERMITTED_DATA_INCREASE,
453+
bank.get_account(&derived_key1).unwrap().data.len()
454+
);
455+
for i in 0..20 {
456+
assert_eq!(i as u8, account.data[i]);
457+
}
446458
}
447459
}

programs/bpf_loader/src/serialization.rs

+47-41
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
22
use solana_sdk::{
3-
account::KeyedAccount, bpf_loader_deprecated, instruction::InstructionError, pubkey::Pubkey,
3+
account::KeyedAccount, bpf_loader_deprecated, entrypoint::MAX_PERMITTED_DATA_INCREASE,
4+
instruction::InstructionError, pubkey::Pubkey,
45
};
56
use std::{
67
io::prelude::*,
7-
mem::{self, align_of},
8+
mem::{align_of, size_of},
89
};
910

1011
/// Look for a duplicate account and return its position if found
@@ -47,7 +48,7 @@ pub fn serialize_parameters_unaligned(
4748
keyed_accounts: &[KeyedAccount],
4849
instruction_data: &[u8],
4950
) -> Result<Vec<u8>, InstructionError> {
50-
assert_eq!(32, mem::size_of::<Pubkey>());
51+
assert_eq!(32, size_of::<Pubkey>());
5152

5253
let mut v: Vec<u8> = Vec::new();
5354
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
@@ -84,29 +85,29 @@ pub fn deserialize_parameters_unaligned(
8485
keyed_accounts: &[KeyedAccount],
8586
buffer: &[u8],
8687
) -> Result<(), InstructionError> {
87-
assert_eq!(32, mem::size_of::<Pubkey>());
88+
assert_eq!(32, size_of::<Pubkey>());
8889

89-
let mut start = mem::size_of::<u64>(); // number of accounts
90+
let mut start = size_of::<u64>(); // number of accounts
9091
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
9192
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
9293
start += 1; // is_dup
9394
if !is_dup {
94-
start += mem::size_of::<u8>(); // is_signer
95-
start += mem::size_of::<u8>(); // is_writable
96-
start += mem::size_of::<Pubkey>(); // pubkey
95+
start += size_of::<u8>(); // is_signer
96+
start += size_of::<u8>(); // is_writable
97+
start += size_of::<Pubkey>(); // pubkey
9798
keyed_account.try_account_ref_mut()?.lamports =
9899
LittleEndian::read_u64(&buffer[start..]);
99-
start += mem::size_of::<u64>() // lamports
100-
+ mem::size_of::<u64>(); // data length
100+
start += size_of::<u64>() // lamports
101+
+ size_of::<u64>(); // data length
101102
let end = start + keyed_account.data_len()?;
102103
keyed_account
103104
.try_account_ref_mut()?
104105
.data
105106
.clone_from_slice(&buffer[start..end]);
106107
start += keyed_account.data_len()? // data
107-
+ mem::size_of::<Pubkey>() // owner
108-
+ mem::size_of::<u8>() // executable
109-
+ mem::size_of::<u64>(); // rent_epoch
108+
+ size_of::<Pubkey>() // owner
109+
+ size_of::<u8>() // executable
110+
+ size_of::<u64>(); // rent_epoch
110111
}
111112
}
112113
Ok(())
@@ -117,14 +118,12 @@ pub fn serialize_parameters_aligned(
117118
keyed_accounts: &[KeyedAccount],
118119
instruction_data: &[u8],
119120
) -> Result<Vec<u8>, InstructionError> {
120-
assert_eq!(32, mem::size_of::<Pubkey>());
121+
assert_eq!(32, size_of::<Pubkey>());
121122

122-
// TODO use with capacity would be nice, but don't know account data sizes...
123123
let mut v: Vec<u8> = Vec::new();
124124
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
125125
.unwrap();
126126

127-
// TODO panic?
128127
if v.as_ptr().align_offset(align_of::<u128>()) != 0 {
129128
panic!();
130129
}
@@ -148,7 +147,9 @@ pub fn serialize_parameters_aligned(
148147
.unwrap();
149148
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
150149
v.resize(
151-
v.len() + (v.len() as *const u8).align_offset(align_of::<u128>()),
150+
v.len()
151+
+ MAX_PERMITTED_DATA_INCREASE
152+
+ (v.len() as *const u8).align_offset(align_of::<u128>()),
152153
0,
153154
);
154155
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
@@ -166,33 +167,39 @@ pub fn deserialize_parameters_aligned(
166167
keyed_accounts: &[KeyedAccount],
167168
buffer: &[u8],
168169
) -> Result<(), InstructionError> {
169-
assert_eq!(32, mem::size_of::<Pubkey>());
170+
assert_eq!(32, size_of::<Pubkey>());
170171

171-
let mut start = mem::size_of::<u64>(); // number of accounts
172+
let mut start = size_of::<u64>(); // number of accounts
172173
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
173174
let (is_dup, _) = is_dup(&keyed_accounts[..i], keyed_account);
174-
start += 1; // is_dup
175-
if !is_dup {
176-
start += mem::size_of::<u8>() // is_signer
177-
+ mem::size_of::<u8>() // is_writable
178-
+ mem::size_of::<u8>() // executable
179-
+ 4 // padding
180-
+ mem::size_of::<Pubkey>() // pubkey
181-
+ mem::size_of::<Pubkey>(); // owner
182-
keyed_account.try_account_ref_mut()?.lamports =
183-
LittleEndian::read_u64(&buffer[start..]);
184-
start += mem::size_of::<u64>() // lamports
185-
+ mem::size_of::<u64>(); // data length
186-
let end = start + keyed_account.data_len()?;
187-
keyed_account
188-
.try_account_ref_mut()?
189-
.data
190-
.clone_from_slice(&buffer[start..end]);
191-
start += keyed_account.data_len()?; // data
192-
start += (start as *const u8).align_offset(align_of::<u128>());
193-
start += mem::size_of::<u64>(); // rent_epoch
175+
start += size_of::<u8>(); // position
176+
if is_dup {
177+
start += 7; // padding to 64-bit aligned
194178
} else {
195-
start += 7; // padding
179+
let mut account = keyed_account.try_account_ref_mut()?;
180+
start += size_of::<u8>() // is_signer
181+
+ size_of::<u8>() // is_writable
182+
+ size_of::<u8>() // executable
183+
+ 4 // padding to 128-bit aligned
184+
+ size_of::<Pubkey>(); // key
185+
account.owner = Pubkey::new(&buffer[start..start + size_of::<Pubkey>()]);
186+
start += size_of::<Pubkey>(); // owner
187+
account.lamports = LittleEndian::read_u64(&buffer[start..]);
188+
start += size_of::<u64>(); // lamports
189+
let pre_len = account.data.len();
190+
let post_len = LittleEndian::read_u64(&buffer[start..]) as usize;
191+
start += size_of::<u64>(); // data length
192+
let mut data_end = start + pre_len;
193+
if post_len != pre_len
194+
&& (post_len.saturating_sub(pre_len)) <= MAX_PERMITTED_DATA_INCREASE
195+
{
196+
account.data.resize(post_len, 0);
197+
data_end = start + post_len;
198+
}
199+
account.data.clone_from_slice(&buffer[start..data_end]);
200+
start += pre_len + MAX_PERMITTED_DATA_INCREASE; // data
201+
start += (start as *const u8).align_offset(align_of::<u128>());
202+
start += size_of::<u64>(); // rent_epoch
196203
}
197204
}
198205
Ok(())
@@ -206,7 +213,6 @@ mod tests {
206213
};
207214
use std::{
208215
cell::RefCell,
209-
mem::size_of,
210216
rc::Rc,
211217
// Hide Result from bindgen gets confused about generics in non-generic type declarations
212218
slice::{from_raw_parts, from_raw_parts_mut},

0 commit comments

Comments
 (0)