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

Commit edd379b

Browse files
committed
wip: Add structs with stable layouts for types that are passed to runtime
1 parent 10dde65 commit edd379b

File tree

16 files changed

+396
-23
lines changed

16 files changed

+396
-23
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

program-runtime/src/invoke_context.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use {
2222
pubkey::Pubkey,
2323
rent::Rent,
2424
saturating_add_assign,
25+
stable_layout::stable_instruction::StableInstruction,
2526
transaction_context::{
2627
IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
2728
},
@@ -482,6 +483,7 @@ impl<'a> InvokeContext<'a> {
482483
instruction: Instruction,
483484
signers: &[Pubkey],
484485
) -> Result<(), InstructionError> {
486+
let instruction = StableInstruction::from(instruction);
485487
let (instruction_accounts, program_indices) =
486488
self.prepare_instruction(&instruction, signers)?;
487489
let mut compute_units_consumed = 0;
@@ -499,7 +501,7 @@ impl<'a> InvokeContext<'a> {
499501
#[allow(clippy::type_complexity)]
500502
pub fn prepare_instruction(
501503
&mut self,
502-
instruction: &Instruction,
504+
instruction: &StableInstruction,
503505
signers: &[Pubkey],
504506
) -> Result<(Vec<InstructionAccount>, Vec<IndexOfAccount>), InstructionError> {
505507
// Finds the index of each account in the instruction by its pubkey.
@@ -1334,6 +1336,7 @@ mod tests {
13341336
},
13351337
metas.clone(),
13361338
);
1339+
let inner_instruction = StableInstruction::from(inner_instruction);
13371340
let (inner_instruction_accounts, program_indices) = invoke_context
13381341
.prepare_instruction(&inner_instruction, &[])
13391342
.unwrap();

program-test/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ use {
3333
fee_calculator::{FeeCalculator, FeeRateGovernor, DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE},
3434
genesis_config::{ClusterType, GenesisConfig},
3535
hash::Hash,
36-
instruction::{Instruction, InstructionError},
36+
instruction::InstructionError,
3737
native_token::sol_to_lamports,
3838
poh_config::PohConfig,
3939
program_error::{ProgramError, UNSUPPORTED_SYSVAR},
4040
pubkey::Pubkey,
4141
rent::Rent,
4242
signature::{Keypair, Signer},
43+
stable_layout::stable_instruction::StableInstruction,
4344
sysvar::{Sysvar, SysvarId},
4445
},
4546
solana_vote_program::vote_state::{self, VoteState, VoteStateVersions},
@@ -223,7 +224,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
223224

224225
fn sol_invoke_signed(
225226
&self,
226-
instruction: &Instruction,
227+
instruction: &StableInstruction,
227228
account_infos: &[AccountInfo],
228229
signers_seeds: &[&[&[u8]]],
229230
) -> ProgramResult {

programs/bpf_loader/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.16.0" }
2222
solana_rbpf = "=0.2.38"
2323
thiserror = "1.0"
2424

25+
[dev-dependencies]
26+
memoffset = "0.8"
27+
2528
[lib]
2629
crate-type = ["lib"]
2730
name = "solana_bpf_loader_program"

programs/bpf_loader/src/syscalls/cpi.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use {
33
crate::declare_syscall,
44
solana_sdk::{
55
feature_set::enable_bpf_loader_set_authority_checked_ix,
6+
stable_layout::{stable_instruction::StableInstruction, stable_vec::StableVec},
67
syscalls::{
78
MAX_CPI_ACCOUNT_INFOS, MAX_CPI_INSTRUCTION_ACCOUNTS, MAX_CPI_INSTRUCTION_DATA_LEN,
89
},
@@ -211,7 +212,7 @@ trait SyscallInvokeSigned {
211212
addr: u64,
212213
memory_mapping: &mut MemoryMapping,
213214
invoke_context: &mut InvokeContext,
214-
) -> Result<Instruction, EbpfError>;
215+
) -> Result<StableInstruction, EbpfError>;
215216
fn translate_accounts<'a>(
216217
instruction_accounts: &[InstructionAccount],
217218
program_indices: &[IndexOfAccount],
@@ -258,8 +259,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
258259
addr: u64,
259260
memory_mapping: &mut MemoryMapping,
260261
invoke_context: &mut InvokeContext,
261-
) -> Result<Instruction, EbpfError> {
262-
let ix = translate_type::<Instruction>(
262+
) -> Result<StableInstruction, EbpfError> {
263+
let ix = translate_type::<StableInstruction>(
263264
memory_mapping,
264265
addr,
265266
invoke_context.get_check_aligned(),
@@ -275,6 +276,7 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
275276
invoke_context.get_check_size(),
276277
)?
277278
.to_vec();
279+
let accounts = StableVec::from(accounts);
278280

279281
let ix_data_len = ix.data.len() as u64;
280282
if invoke_context
@@ -296,10 +298,12 @@ impl SyscallInvokeSigned for SyscallInvokeSignedRust {
296298
invoke_context.get_check_size(),
297299
)?
298300
.to_vec();
299-
Ok(Instruction {
300-
program_id: ix.program_id,
301+
let data = StableVec::from(data);
302+
303+
Ok(StableInstruction {
301304
accounts,
302305
data,
306+
program_id: ix.program_id,
303307
})
304308
}
305309

@@ -469,7 +473,7 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC {
469473
addr: u64,
470474
memory_mapping: &mut MemoryMapping,
471475
invoke_context: &mut InvokeContext,
472-
) -> Result<Instruction, EbpfError> {
476+
) -> Result<StableInstruction, EbpfError> {
473477
let ix_c = translate_type::<SolInstruction>(
474478
memory_mapping,
475479
addr,
@@ -514,6 +518,8 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC {
514518
invoke_context.get_check_size(),
515519
)?
516520
.to_vec();
521+
let data = StableVec::from(data);
522+
517523
let accounts = meta_cs
518524
.iter()
519525
.map(|meta_c| {
@@ -529,11 +535,12 @@ impl SyscallInvokeSigned for SyscallInvokeSignedC {
529535
})
530536
})
531537
.collect::<Result<Vec<AccountMeta>, EbpfError>>()?;
538+
let accounts = StableVec::from(accounts);
532539

533-
Ok(Instruction {
534-
program_id: *program_id,
540+
Ok(StableInstruction {
535541
accounts,
536542
data,
543+
program_id: *program_id,
537544
})
538545
}
539546

@@ -1128,6 +1135,7 @@ mod tests {
11281135
solana_sdk::{
11291136
account::{Account, AccountSharedData},
11301137
clock::Epoch,
1138+
instruction::Instruction,
11311139
rent::Rent,
11321140
transaction_context::{TransactionAccount, TransactionContext},
11331141
},
@@ -1220,8 +1228,8 @@ mod tests {
12201228
)
12211229
.unwrap();
12221230
assert_eq!(ins.program_id, program_id);
1223-
assert_eq!(ins.accounts, accounts);
1224-
assert_eq!(ins.data, data);
1231+
assert_eq!(ins.accounts, StableVec::from(accounts));
1232+
assert_eq!(ins.data, StableVec::from(data));
12251233
}
12261234

12271235
#[test]

programs/bpf_loader/src/syscalls/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use {
4444
},
4545
hash::{Hasher, HASH_BYTES},
4646
instruction::{
47-
AccountMeta, Instruction, InstructionError, ProcessedSiblingInstruction,
47+
AccountMeta, InstructionError, ProcessedSiblingInstruction,
4848
TRANSACTION_LEVEL_STACK_HEIGHT,
4949
},
5050
keccak, native_loader,
@@ -1821,6 +1821,7 @@ mod tests {
18211821
bpf_loader,
18221822
fee_calculator::FeeCalculator,
18231823
hash::hashv,
1824+
instruction::Instruction,
18241825
program::check_type_assumptions,
18251826
sysvar::{clock::Clock, epoch_schedule::EpochSchedule, rent::Rent},
18261827
transaction_context::TransactionContext,

sdk/program/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ pub mod serialize_utils;
521521
pub mod short_vec;
522522
pub mod slot_hashes;
523523
pub mod slot_history;
524+
pub mod stable_layout;
524525
pub mod stake;
525526
pub mod stake_history;
526527
pub mod syscalls;

sdk/program/src/program.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
use crate::{
1212
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction, pubkey::Pubkey,
13+
stable_layout::stable_instruction::StableInstruction,
1314
};
1415

1516
/// Invoke a cross-program instruction.
@@ -290,11 +291,14 @@ pub fn invoke_signed_unchecked(
290291
account_infos: &[AccountInfo],
291292
signers_seeds: &[&[&[u8]]],
292293
) -> ProgramResult {
294+
// bprumo TODO: it would be nice to not clone `instruction`... Can this fn take a StableInstruction instead?
295+
let instruction = StableInstruction::from(instruction.clone());
296+
293297
#[cfg(target_os = "solana")]
294298
{
295299
let result = unsafe {
296300
crate::syscalls::sol_invoke_signed_rust(
297-
instruction as *const _ as *const u8,
301+
&instruction as *const _ as *const u8,
298302
account_infos as *const _ as *const u8,
299303
account_infos.len() as u64,
300304
signers_seeds as *const _ as *const u8,
@@ -308,7 +312,7 @@ pub fn invoke_signed_unchecked(
308312
}
309313

310314
#[cfg(not(target_os = "solana"))]
311-
crate::program_stubs::sol_invoke_signed(instruction, account_infos, signers_seeds)
315+
crate::program_stubs::sol_invoke_signed(&instruction, account_infos, signers_seeds)
312316
}
313317

314318
/// Maximum size that can be set using [`set_return_data`].
@@ -432,17 +436,18 @@ pub fn check_type_assumptions() {
432436
accounts: vec![account_meta1.clone(), account_meta2.clone()],
433437
data: data.clone(),
434438
};
439+
let instruction = StableInstruction::from(instruction);
435440
let instruction_addr = &instruction as *const _ as u64;
436441

437442
// program id
438-
assert_eq!(offset_of!(Instruction, program_id), 48);
443+
assert_eq!(offset_of!(StableInstruction, program_id), 48);
439444
let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
440445
unsafe {
441446
assert_eq!(*pubkey_ptr, pubkey1);
442447
}
443448

444449
// accounts
445-
assert_eq!(offset_of!(Instruction, accounts), 0);
450+
assert_eq!(offset_of!(StableInstruction, accounts), 0);
446451
let accounts_ptr = (instruction_addr) as *const *const AccountMeta;
447452
let accounts_cap = (instruction_addr + 8) as *const usize;
448453
let accounts_len = (instruction_addr + 16) as *const usize;
@@ -455,7 +460,7 @@ pub fn check_type_assumptions() {
455460
}
456461

457462
// data
458-
assert_eq!(offset_of!(Instruction, data), 24);
463+
assert_eq!(offset_of!(StableInstruction, data), 24);
459464
let data_ptr = (instruction_addr + 24) as *const *const [u8; 5];
460465
let data_cap = (instruction_addr + 24 + 8) as *const usize;
461466
let data_len = (instruction_addr + 24 + 16) as *const usize;

sdk/program/src/program_stubs.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
crate::{
77
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
88
program_error::UNSUPPORTED_SYSVAR, pubkey::Pubkey,
9+
stable_layout::stable_instruction::StableInstruction,
910
},
1011
itertools::Itertools,
1112
std::sync::{Arc, RwLock},
@@ -31,7 +32,7 @@ pub trait SyscallStubs: Sync + Send {
3132
}
3233
fn sol_invoke_signed(
3334
&self,
34-
_instruction: &Instruction,
35+
_instruction: &StableInstruction,
3536
_account_infos: &[AccountInfo],
3637
_signers_seeds: &[&[&[u8]]],
3738
) -> ProgramResult {
@@ -117,7 +118,7 @@ pub(crate) fn sol_log_compute_units() {
117118
}
118119

119120
pub(crate) fn sol_invoke_signed(
120-
instruction: &Instruction,
121+
instruction: &StableInstruction,
121122
account_infos: &[AccountInfo],
122123
signers_seeds: &[&[&[u8]]],
123124
) -> ProgramResult {

sdk/program/src/stable_layout.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//! bprumo TODO: doc, or hide doc?
2+
3+
pub mod stable_instruction;
4+
pub mod stable_rc;
5+
pub mod stable_ref_cell;
6+
pub mod stable_slice;
7+
pub mod stable_vec;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! Instruction, with a stable memory layout
2+
3+
use {
4+
crate::{
5+
instruction::{AccountMeta, Instruction},
6+
pubkey::Pubkey,
7+
stable_layout::stable_vec::StableVec,
8+
},
9+
std::fmt::Debug,
10+
};
11+
12+
#[derive(Debug)]
13+
#[repr(C)]
14+
pub struct StableInstruction {
15+
pub accounts: StableVec<AccountMeta>,
16+
pub data: StableVec<u8>,
17+
pub program_id: Pubkey,
18+
}
19+
20+
impl From<Instruction> for StableInstruction {
21+
fn from(other: Instruction) -> Self {
22+
Self {
23+
accounts: StableVec::from(other.accounts),
24+
data: StableVec::from(other.data),
25+
program_id: other.program_id,
26+
}
27+
}
28+
}
29+
30+
#[cfg(test)]
31+
mod tests {
32+
use {
33+
super::*,
34+
memoffset::offset_of,
35+
std::mem::{align_of, size_of},
36+
};
37+
38+
#[test]
39+
fn test_memory_layout() {
40+
assert_eq!(offset_of!(StableInstruction, accounts), 0);
41+
assert_eq!(offset_of!(StableInstruction, data), 24);
42+
assert_eq!(offset_of!(StableInstruction, program_id), 48);
43+
assert_eq!(align_of::<StableInstruction>(), 8);
44+
assert_eq!(size_of::<StableInstruction>(), 24 + 24 + 32);
45+
46+
let program_id = Pubkey::new_unique();
47+
let account_meta1 = AccountMeta {
48+
pubkey: Pubkey::new_unique(),
49+
is_signer: true,
50+
is_writable: false,
51+
};
52+
let account_meta2 = AccountMeta {
53+
pubkey: Pubkey::new_unique(),
54+
is_signer: false,
55+
is_writable: true,
56+
};
57+
let accounts = vec![account_meta1, account_meta2];
58+
let data = vec![1, 2, 3, 4, 5];
59+
let instruction = Instruction {
60+
program_id,
61+
accounts: accounts.clone(),
62+
data: data.clone(),
63+
};
64+
let instruction = StableInstruction::from(instruction);
65+
66+
let instruction_addr = &instruction as *const _ as u64;
67+
68+
let accounts_ptr = instruction_addr as *const &[AccountMeta; 2];
69+
assert_eq!(unsafe { *accounts_ptr }, accounts.as_slice());
70+
71+
let data_ptr = (instruction_addr + 24) as *const &[u8; 5];
72+
assert_eq!(unsafe { *data_ptr }, data.as_slice());
73+
74+
let pubkey_ptr = (instruction_addr + 48) as *const Pubkey;
75+
assert_eq!(unsafe { *pubkey_ptr }, program_id);
76+
}
77+
}

0 commit comments

Comments
 (0)