Align host addresses (#11384)
* Align host addresses * support new program abi * update epoch rollout * Enforce aligned pointers in cross-program invocations
This commit is contained in:
parent
697a0e2947
commit
9290e561e1
|
@ -871,6 +871,9 @@ impl TestValidator {
|
|||
genesis_config
|
||||
.native_instruction_processors
|
||||
.push(solana_bpf_loader_program!());
|
||||
genesis_config
|
||||
.native_instruction_processors
|
||||
.push(solana_bpf_loader_deprecated_program!());
|
||||
|
||||
genesis_config.rent.lamports_per_byte_year = 1;
|
||||
genesis_config.rent.exemption_threshold = 1.0;
|
||||
|
|
|
@ -36,9 +36,8 @@ pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option<Infl
|
|||
OperatingMode::Stable => match epoch {
|
||||
// No inflation at epoch 0
|
||||
0 => Some(Inflation::new_disabled()),
|
||||
// Inflation starts
|
||||
// The epoch of Epoch::MAX is a placeholder and is expected to be reduced in
|
||||
// a future hard fork.
|
||||
// Inflation starts The epoch of Epoch::MAX is a placeholder and is
|
||||
// expected to be reduced in a future hard fork.
|
||||
Epoch::MAX => Some(Inflation::default()),
|
||||
_ => None,
|
||||
},
|
||||
|
@ -49,38 +48,36 @@ pub fn get_programs(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(
|
|||
match operating_mode {
|
||||
OperatingMode::Development => {
|
||||
if epoch == 0 {
|
||||
// Programs used for testing
|
||||
Some(vec![
|
||||
// Enable all Stable programs
|
||||
solana_bpf_loader_program!(),
|
||||
solana_bpf_loader_deprecated_program!(),
|
||||
solana_vest_program!(),
|
||||
// Programs that are only available in Development mode
|
||||
solana_budget_program!(),
|
||||
solana_exchange_program!(),
|
||||
])
|
||||
} else if epoch == std::u64::MAX {
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected
|
||||
// to be reduced in a future network update.
|
||||
Some(vec![solana_bpf_loader_program!()])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
OperatingMode::Stable => {
|
||||
if epoch == std::u64::MAX - 1 {
|
||||
// The epoch of std::u64::MAX - 1 is a placeholder and is expected to be reduced in
|
||||
// a future hard fork.
|
||||
Some(vec![solana_bpf_loader_program!()])
|
||||
} else if epoch == std::u64::MAX {
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected to be reduced in a
|
||||
// future hard fork.
|
||||
Some(vec![solana_vest_program!()])
|
||||
if epoch == std::u64::MAX {
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected
|
||||
// to be reduced in a future network update.
|
||||
Some(vec![solana_bpf_loader_program!(), solana_vest_program!()])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
OperatingMode::Preview => {
|
||||
if epoch == 0 {
|
||||
Some(vec![solana_bpf_loader_program!()])
|
||||
} else if epoch == std::u64::MAX {
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected to be reduced in a
|
||||
// future hard fork.
|
||||
Some(vec![solana_vest_program!()])
|
||||
if epoch == std::u64::MAX {
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected
|
||||
// to be reduced in a future network update.
|
||||
Some(vec![solana_bpf_loader_program!(), solana_vest_program!()])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -138,7 +135,7 @@ mod tests {
|
|||
fn test_development_programs() {
|
||||
assert_eq!(
|
||||
get_programs(OperatingMode::Development, 0).unwrap().len(),
|
||||
4
|
||||
5
|
||||
);
|
||||
assert_eq!(get_programs(OperatingMode::Development, 1), None);
|
||||
}
|
||||
|
@ -159,7 +156,6 @@ mod tests {
|
|||
#[test]
|
||||
fn test_softlaunch_programs() {
|
||||
assert_eq!(get_programs(OperatingMode::Stable, 1), None);
|
||||
assert!(get_programs(OperatingMode::Stable, std::u64::MAX - 1).is_some());
|
||||
assert!(get_programs(OperatingMode::Stable, std::u64::MAX).is_some());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -705,7 +705,13 @@ fn test_stable_operating_mode() {
|
|||
}
|
||||
|
||||
// Programs that are not available at epoch 0
|
||||
for program_id in [&solana_sdk::bpf_loader::id(), &solana_vest_program::id()].iter() {
|
||||
for program_id in [
|
||||
&solana_sdk::bpf_loader::id(),
|
||||
&solana_sdk::bpf_loader_deprecated::id(),
|
||||
&solana_vest_program::id(),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
assert_eq!(
|
||||
(
|
||||
program_id,
|
||||
|
|
|
@ -23,6 +23,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
|
|||
sol_assert(sol_deserialize(input, ¶ms, 4));
|
||||
|
||||
SolPubkey bpf_loader_id =
|
||||
(SolPubkey){.x = {2, 168, 246, 145, 78, 136, 161, 110, 57, 90, 225,
|
||||
40, 148, 143, 250, 105, 86, 147, 55, 104, 24, 221,
|
||||
71, 67, 82, 33, 243, 198, 0, 0, 0, 0}};
|
||||
|
||||
SolPubkey bpf_loader_deprecated_id =
|
||||
(SolPubkey){.x = {2, 168, 246, 145, 78, 136, 161, 107, 189, 35, 149,
|
||||
133, 95, 100, 4, 217, 180, 244, 86, 183, 130, 27,
|
||||
176, 20, 87, 73, 66, 140, 0, 0, 0, 0}};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use crate::process_instruction;
|
||||
|
||||
solana_sdk::declare_loader!(
|
||||
solana_sdk::bpf_loader_deprecated::ID,
|
||||
solana_bpf_loader_deprecated_program,
|
||||
process_instruction,
|
||||
solana_bpf_loader_program,
|
||||
deprecated::id
|
||||
);
|
|
@ -1,10 +1,15 @@
|
|||
pub mod alloc;
|
||||
pub mod allocator_bump;
|
||||
pub mod bpf_verifier;
|
||||
pub mod deprecated;
|
||||
pub mod serialization;
|
||||
pub mod syscalls;
|
||||
|
||||
use crate::{bpf_verifier::VerifierError, syscalls::SyscallError};
|
||||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||
use crate::{
|
||||
bpf_verifier::VerifierError,
|
||||
serialization::{deserialize_parameters, serialize_parameters},
|
||||
syscalls::SyscallError,
|
||||
};
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use solana_rbpf::{
|
||||
ebpf::{EbpfError, UserDefinedError},
|
||||
|
@ -13,7 +18,7 @@ use solana_rbpf::{
|
|||
};
|
||||
use solana_sdk::{
|
||||
account::{is_executable, next_keyed_account, KeyedAccount},
|
||||
bpf_loader,
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
decode_error::DecodeError,
|
||||
entrypoint::SUCCESS,
|
||||
entrypoint_native::InvokeContext,
|
||||
|
@ -22,13 +27,13 @@ use solana_sdk::{
|
|||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use std::{io::prelude::*, mem};
|
||||
use thiserror::Error;
|
||||
|
||||
solana_sdk::declare_loader!(
|
||||
solana_sdk::bpf_loader::ID,
|
||||
solana_bpf_loader_program,
|
||||
process_instruction
|
||||
process_instruction,
|
||||
solana_bpf_loader_program
|
||||
);
|
||||
|
||||
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
|
@ -86,75 +91,6 @@ pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool,
|
|||
(false, 0)
|
||||
}
|
||||
|
||||
pub fn serialize_parameters(
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
.unwrap();
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
if is_dup {
|
||||
v.write_u8(position as u8).unwrap();
|
||||
} else {
|
||||
v.write_u8(std::u8::MAX).unwrap();
|
||||
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
||||
.unwrap();
|
||||
v.write_u8(keyed_account.is_writable() as u8).unwrap();
|
||||
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
|
||||
.unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
|
||||
.unwrap();
|
||||
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
|
||||
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
|
||||
v.write_u8(keyed_account.executable()? as u8).unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
v.write_u64::<LittleEndian>(data.len() as u64).unwrap();
|
||||
v.write_all(data).unwrap();
|
||||
v.write_all(program_id.as_ref()).unwrap();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
|
||||
let mut start = mem::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
|
||||
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
|
||||
+ mem::size_of::<Pubkey>() // owner
|
||||
+ mem::size_of::<u8>() // executable
|
||||
+ mem::size_of::<u64>(); // rent_epoch
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! log{
|
||||
($logger:ident, $message:expr) => {
|
||||
if let Ok(mut logger) = $logger.try_borrow_mut() {
|
||||
|
@ -176,7 +112,7 @@ pub fn process_instruction(
|
|||
instruction_data: &[u8],
|
||||
invoke_context: &mut dyn InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
debug_assert!(bpf_loader::check_id(program_id));
|
||||
debug_assert!(bpf_loader::check_id(program_id) || bpf_loader_deprecated::check_id(program_id));
|
||||
|
||||
let logger = invoke_context.get_logger();
|
||||
|
||||
|
@ -191,6 +127,7 @@ pub fn process_instruction(
|
|||
|
||||
let parameter_accounts = keyed_accounts_iter.as_slice();
|
||||
let parameter_bytes = serialize_parameters(
|
||||
program_id,
|
||||
program.unsigned_key(),
|
||||
parameter_accounts,
|
||||
&instruction_data,
|
||||
|
@ -236,7 +173,7 @@ pub fn process_instruction(
|
|||
}
|
||||
}
|
||||
}
|
||||
deserialize_parameters(parameter_accounts, ¶meter_bytes)?;
|
||||
deserialize_parameters(program_id, parameter_accounts, ¶meter_bytes)?;
|
||||
log!(logger, "BPF program {} success", program.unsigned_key());
|
||||
} else if !keyed_accounts.is_empty() {
|
||||
match limited_deserialize(instruction_data)? {
|
||||
|
@ -349,6 +286,7 @@ mod tests {
|
|||
// Ensure that we can invoke this macro from the same crate
|
||||
// where it is defined.
|
||||
solana_bpf_loader_program!();
|
||||
solana_bpf_loader_deprecated_program!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -440,7 +378,7 @@ mod tests {
|
|||
fn test_bpf_loader_finalize() {
|
||||
let program_id = Pubkey::new_rand();
|
||||
let program_key = Pubkey::new_rand();
|
||||
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
||||
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
let rent = Rent::default();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
@ -506,7 +444,7 @@ mod tests {
|
|||
let program_key = Pubkey::new_rand();
|
||||
|
||||
// Create program account
|
||||
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
||||
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
let program_account = Account::new_ref(1, 0, &program_id);
|
||||
|
@ -580,6 +518,94 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bpf_loader_serialize_unaligned() {
|
||||
let program_id = Pubkey::new_rand();
|
||||
let program_key = Pubkey::new_rand();
|
||||
|
||||
// Create program account
|
||||
let mut file = File::open("test_elfs/noop_unaligned.so").expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
let program_account = Account::new_ref(1, 0, &program_id);
|
||||
program_account.borrow_mut().data = elf;
|
||||
program_account.borrow_mut().executable = true;
|
||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
||||
|
||||
// Case: With program and parameter account
|
||||
let parameter_account = Account::new_ref(1, 0, &program_id);
|
||||
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
process_instruction(
|
||||
&bpf_loader_deprecated::id(),
|
||||
&keyed_accounts,
|
||||
&[],
|
||||
&mut MockInvokeContext::default()
|
||||
)
|
||||
);
|
||||
|
||||
// Case: With duplicate accounts
|
||||
let duplicate_key = Pubkey::new_rand();
|
||||
let parameter_account = Account::new_ref(1, 0, &program_id);
|
||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
||||
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
||||
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
process_instruction(
|
||||
&bpf_loader_deprecated::id(),
|
||||
&keyed_accounts,
|
||||
&[],
|
||||
&mut MockInvokeContext::default()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bpf_loader_serialize_aligned() {
|
||||
let program_id = Pubkey::new_rand();
|
||||
let program_key = Pubkey::new_rand();
|
||||
|
||||
// Create program account
|
||||
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
let program_account = Account::new_ref(1, 0, &program_id);
|
||||
program_account.borrow_mut().data = elf;
|
||||
program_account.borrow_mut().executable = true;
|
||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
||||
|
||||
// Case: With program and parameter account
|
||||
let parameter_account = Account::new_ref(1, 0, &program_id);
|
||||
keyed_accounts.push(KeyedAccount::new(&program_key, false, ¶meter_account));
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
process_instruction(
|
||||
&bpf_loader::id(),
|
||||
&keyed_accounts,
|
||||
&[],
|
||||
&mut MockInvokeContext::default()
|
||||
)
|
||||
);
|
||||
|
||||
// Case: With duplicate accounts
|
||||
let duplicate_key = Pubkey::new_rand();
|
||||
let parameter_account = Account::new_ref(1, 0, &program_id);
|
||||
let mut keyed_accounts = vec![KeyedAccount::new(&program_key, false, &program_account)];
|
||||
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
||||
keyed_accounts.push(KeyedAccount::new(&duplicate_key, false, ¶meter_account));
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
process_instruction(
|
||||
&bpf_loader::id(),
|
||||
&keyed_accounts,
|
||||
&[],
|
||||
&mut MockInvokeContext::default()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// fuzzing utility function
|
||||
fn fuzz<F>(
|
||||
bytes: &[u8],
|
||||
|
@ -610,7 +636,7 @@ mod tests {
|
|||
let program_key = Pubkey::new_rand();
|
||||
|
||||
// Create program account
|
||||
let mut file = File::open("test_elfs/noop.so").expect("file open failed");
|
||||
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
||||
use solana_sdk::{
|
||||
account::KeyedAccount, bpf_loader_deprecated, instruction::InstructionError, pubkey::Pubkey,
|
||||
};
|
||||
use std::{
|
||||
io::prelude::*,
|
||||
mem::{self, align_of},
|
||||
};
|
||||
|
||||
/// Look for a duplicate account and return its position if found
|
||||
pub fn is_dup(accounts: &[KeyedAccount], keyed_account: &KeyedAccount) -> (bool, usize) {
|
||||
for (i, account) in accounts.iter().enumerate() {
|
||||
if account == keyed_account {
|
||||
return (true, i);
|
||||
}
|
||||
}
|
||||
(false, 0)
|
||||
}
|
||||
|
||||
pub fn serialize_parameters(
|
||||
loader_id: &Pubkey,
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, InstructionError> {
|
||||
if *loader_id == bpf_loader_deprecated::id() {
|
||||
serialize_parameters_unaligned(program_id, keyed_accounts, data)
|
||||
} else {
|
||||
serialize_parameters_aligned(program_id, keyed_accounts, data)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters(
|
||||
loader_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
if *loader_id == bpf_loader_deprecated::id() {
|
||||
deserialize_parameters_unaligned(keyed_accounts, buffer)
|
||||
} else {
|
||||
deserialize_parameters_aligned(keyed_accounts, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_parameters_unaligned(
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<Vec<u8>, InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
|
||||
let mut v: Vec<u8> = Vec::new();
|
||||
v.write_u64::<LittleEndian>(keyed_accounts.len() as u64)
|
||||
.unwrap();
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
if is_dup {
|
||||
v.write_u8(position as u8).unwrap();
|
||||
} else {
|
||||
v.write_u8(std::u8::MAX).unwrap();
|
||||
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
||||
.unwrap();
|
||||
v.write_u8(keyed_account.is_writable() as u8).unwrap();
|
||||
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
|
||||
.unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
|
||||
.unwrap();
|
||||
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
|
||||
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
|
||||
v.write_u8(keyed_account.executable()? as u8).unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
|
||||
.unwrap();
|
||||
v.write_all(instruction_data).unwrap();
|
||||
v.write_all(program_id.as_ref()).unwrap();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_unaligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
|
||||
let mut start = mem::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
|
||||
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
|
||||
+ mem::size_of::<Pubkey>() // owner
|
||||
+ mem::size_of::<u8>() // executable
|
||||
+ mem::size_of::<u64>(); // rent_epoch
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize_parameters_aligned(
|
||||
program_id: &Pubkey,
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
instruction_data: &[u8],
|
||||
) -> Result<Vec<u8>, InstructionError> {
|
||||
assert_eq!(32, mem::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!();
|
||||
}
|
||||
for (i, keyed_account) in keyed_accounts.iter().enumerate() {
|
||||
let (is_dup, position) = is_dup(&keyed_accounts[..i], keyed_account);
|
||||
if is_dup {
|
||||
v.write_u8(position as u8).unwrap();
|
||||
v.write_all(&[0u8, 0, 0, 0, 0, 0, 0]).unwrap(); // 7 bytes of padding to make 64-bit aligned
|
||||
} else {
|
||||
v.write_u8(std::u8::MAX).unwrap();
|
||||
v.write_u8(keyed_account.signer_key().is_some() as u8)
|
||||
.unwrap();
|
||||
v.write_u8(keyed_account.is_writable() as u8).unwrap();
|
||||
v.write_u8(keyed_account.executable()? as u8).unwrap();
|
||||
v.write_all(&[0u8, 0, 0, 0]).unwrap(); // 4 bytes of padding to make 128-bit aligned
|
||||
v.write_all(keyed_account.unsigned_key().as_ref()).unwrap();
|
||||
v.write_all(keyed_account.owner()?.as_ref()).unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.lamports()?)
|
||||
.unwrap();
|
||||
v.write_u64::<LittleEndian>(keyed_account.data_len()? as u64)
|
||||
.unwrap();
|
||||
v.write_all(&keyed_account.try_account_ref()?.data).unwrap();
|
||||
for _ in 0..16 - (v.len() % 16) {
|
||||
v.write_u8(0).unwrap(); // 128 bit aligned again
|
||||
}
|
||||
v.write_u64::<LittleEndian>(keyed_account.rent_epoch()? as u64)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_data.len() as u64)
|
||||
.unwrap();
|
||||
v.write_all(instruction_data).unwrap();
|
||||
v.write_all(program_id.as_ref()).unwrap();
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_aligned(
|
||||
keyed_accounts: &[KeyedAccount],
|
||||
buffer: &[u8],
|
||||
) -> Result<(), InstructionError> {
|
||||
assert_eq!(32, mem::size_of::<Pubkey>());
|
||||
|
||||
let mut start = mem::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 += 16 - (start % 16); // padding
|
||||
start += mem::size_of::<u64>(); // rent_epoch
|
||||
} else {
|
||||
start += 7; // padding
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_sdk::{
|
||||
account::Account, account_info::AccountInfo, bpf_loader, entrypoint::deserialize,
|
||||
};
|
||||
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},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_serialize_parameters() {
|
||||
let program_id = Pubkey::new_rand();
|
||||
let dup_key = Pubkey::new_rand();
|
||||
let keys = vec![dup_key, dup_key, Pubkey::new_rand(), Pubkey::new_rand()];
|
||||
let accounts = [
|
||||
RefCell::new(Account {
|
||||
lamports: 1,
|
||||
data: vec![1u8, 2, 3, 4, 5],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
}),
|
||||
// dup of first
|
||||
RefCell::new(Account {
|
||||
lamports: 1,
|
||||
data: vec![1u8, 2, 3, 4, 5],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 100,
|
||||
}),
|
||||
RefCell::new(Account {
|
||||
lamports: 2,
|
||||
data: vec![11u8, 12, 13, 14, 15, 16, 17, 18, 19],
|
||||
owner: bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 200,
|
||||
}),
|
||||
RefCell::new(Account {
|
||||
lamports: 3,
|
||||
data: vec![],
|
||||
owner: bpf_loader::id(),
|
||||
executable: false,
|
||||
rent_epoch: 3100,
|
||||
}),
|
||||
];
|
||||
|
||||
let keyed_accounts: Vec<_> = keys
|
||||
.iter()
|
||||
.zip(&accounts)
|
||||
.map(|(key, account)| KeyedAccount::new(&key, false, &account))
|
||||
.collect();
|
||||
let instruction_data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
|
||||
// check serialize_parameters_aligned
|
||||
|
||||
let mut serialized = serialize_parameters(
|
||||
&bpf_loader::id(),
|
||||
&program_id,
|
||||
&keyed_accounts,
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let (de_program_id, de_accounts, de_instruction_data) =
|
||||
unsafe { deserialize(&mut serialized[0] as *mut u8) };
|
||||
|
||||
assert_eq!(&program_id, de_program_id);
|
||||
assert_eq!(instruction_data, de_instruction_data);
|
||||
assert_eq!(
|
||||
(&de_instruction_data[0] as *const u8).align_offset(align_of::<u128>()),
|
||||
0
|
||||
);
|
||||
for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys.clone()) {
|
||||
assert_eq!(key, *account_info.key);
|
||||
let account = account.borrow();
|
||||
assert_eq!(account.lamports, account_info.lamports());
|
||||
assert_eq!(&account.data[..], &account_info.data.borrow()[..]);
|
||||
assert_eq!(&account.owner, account_info.owner);
|
||||
assert_eq!(account.executable, account_info.executable);
|
||||
assert_eq!(account.rent_epoch, account_info.rent_epoch);
|
||||
|
||||
assert_eq!(
|
||||
(*account_info.lamports.borrow() as *const u64).align_offset(align_of::<u64>()),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
account_info
|
||||
.data
|
||||
.borrow()
|
||||
.as_ptr()
|
||||
.align_offset(align_of::<u128>()),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// check serialize_parameters_unaligned
|
||||
|
||||
let mut serialized = serialize_parameters(
|
||||
&bpf_loader_deprecated::id(),
|
||||
&program_id,
|
||||
&keyed_accounts,
|
||||
&instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let (de_program_id, de_accounts, de_instruction_data) =
|
||||
unsafe { deserialize_unaligned(&mut serialized[0] as *mut u8) };
|
||||
|
||||
assert_eq!(&program_id, de_program_id);
|
||||
assert_eq!(instruction_data, de_instruction_data);
|
||||
for ((account, account_info), key) in accounts.iter().zip(de_accounts).zip(keys) {
|
||||
assert_eq!(key, *account_info.key);
|
||||
let account = account.borrow();
|
||||
assert_eq!(account.lamports, account_info.lamports());
|
||||
assert_eq!(&account.data[..], &account_info.data.borrow()[..]);
|
||||
assert_eq!(&account.owner, account_info.owner);
|
||||
assert_eq!(account.executable, account_info.executable);
|
||||
assert_eq!(account.rent_epoch, account_info.rent_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
// the old bpf_loader in-program deserializer bpf_loader::id()
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub unsafe fn deserialize_unaligned<'a>(
|
||||
input: *mut u8,
|
||||
) -> (&'a Pubkey, Vec<AccountInfo<'a>>, &'a [u8]) {
|
||||
let mut offset: usize = 0;
|
||||
|
||||
// number of accounts present
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let num_accounts = *(input.add(offset) as *const u64) as usize;
|
||||
offset += size_of::<u64>();
|
||||
|
||||
// account Infos
|
||||
|
||||
let mut accounts = Vec::with_capacity(num_accounts);
|
||||
for _ in 0..num_accounts {
|
||||
let dup_info = *(input.add(offset) as *const u8);
|
||||
offset += size_of::<u8>();
|
||||
if dup_info == std::u8::MAX {
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let is_signer = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let is_writable = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
|
||||
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
offset += size_of::<Pubkey>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
||||
offset += size_of::<u64>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let data_len = *(input.add(offset) as *const u64) as usize;
|
||||
offset += size_of::<u64>();
|
||||
|
||||
let data = Rc::new(RefCell::new({
|
||||
from_raw_parts_mut(input.add(offset), data_len)
|
||||
}));
|
||||
offset += data_len;
|
||||
|
||||
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
offset += size_of::<Pubkey>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let executable = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let rent_epoch = *(input.add(offset) as *const u64);
|
||||
offset += size_of::<u64>();
|
||||
|
||||
accounts.push(AccountInfo {
|
||||
is_signer,
|
||||
is_writable,
|
||||
key,
|
||||
lamports,
|
||||
data,
|
||||
owner,
|
||||
executable,
|
||||
rent_epoch,
|
||||
});
|
||||
} else {
|
||||
// duplicate account, clone the original
|
||||
accounts.push(accounts[dup_info as usize].clone());
|
||||
}
|
||||
}
|
||||
|
||||
// instruction data
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let instruction_data_len = *(input.add(offset) as *const u64) as usize;
|
||||
offset += size_of::<u64>();
|
||||
|
||||
let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
|
||||
offset += instruction_data_len;
|
||||
|
||||
// program Id
|
||||
|
||||
let program_id: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
|
||||
(program_id, accounts, instruction_data)
|
||||
}
|
||||
}
|
|
@ -50,6 +50,8 @@ pub enum SyscallError {
|
|||
InstructionError(InstructionError),
|
||||
#[error("Cross-program invocation with unauthorized signer or writable account")]
|
||||
PrivilegeEscalation,
|
||||
#[error("Unaligned pointer")]
|
||||
UnalignedPointer,
|
||||
}
|
||||
impl From<SyscallError> for EbpfError<BPFError> {
|
||||
fn from(error: SyscallError) -> Self {
|
||||
|
@ -141,7 +143,10 @@ macro_rules! translate {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! translate_type_mut {
|
||||
($t:ty, $vm_addr:expr, $regions:expr) => {
|
||||
($t:ty, $vm_addr:expr, $regions:expr) => {{
|
||||
if ($vm_addr as u64 as *mut $t).align_offset(align_of::<$t>()) != 0 {
|
||||
Err(SyscallError::UnalignedPointer.into())
|
||||
} else {
|
||||
unsafe {
|
||||
match translate_addr::<BPFError>(
|
||||
$vm_addr as u64,
|
||||
|
@ -154,7 +159,8 @@ macro_rules! translate_type_mut {
|
|||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! translate_type {
|
||||
|
@ -168,7 +174,10 @@ macro_rules! translate_type {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! translate_slice_mut {
|
||||
($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => {
|
||||
($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => {{
|
||||
if ($vm_addr as u64 as *mut $t).align_offset(align_of::<$t>()) != 0 {
|
||||
Err(SyscallError::UnalignedPointer.into())
|
||||
} else {
|
||||
match translate_addr::<BPFError>(
|
||||
$vm_addr as u64,
|
||||
$len as usize * size_of::<$t>(),
|
||||
|
@ -179,7 +188,8 @@ macro_rules! translate_slice_mut {
|
|||
Ok(value) => Ok(unsafe { from_raw_parts_mut(value as *mut $t, $len as usize) }),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
};
|
||||
}
|
||||
}};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! translate_slice {
|
||||
|
@ -456,7 +466,7 @@ impl<'a> SyscallProcessInstruction<'a> for SyscallProcessInstructionRust<'a> {
|
|||
let lamports_ref = {
|
||||
// Double translate lamports out of RefCell
|
||||
let ptr = translate_type!(u64, account_info.lamports.as_ptr(), ro_regions)?;
|
||||
translate_type_mut!(u64, *(ptr as *const u64), rw_regions)?
|
||||
translate_type_mut!(u64, *ptr, rw_regions)?
|
||||
};
|
||||
let data = {
|
||||
// Double translate data out of RefCell
|
||||
|
@ -918,13 +928,17 @@ mod tests {
|
|||
vec![AccountMeta::new(Pubkey::new_rand(), false)],
|
||||
);
|
||||
let addr = &instruction as *const _ as u64;
|
||||
let regions = vec![MemoryRegion {
|
||||
let mut regions = vec![MemoryRegion {
|
||||
addr_host: addr,
|
||||
addr_vm: 100,
|
||||
addr_vm: 96,
|
||||
len: std::mem::size_of::<Instruction>() as u64,
|
||||
}];
|
||||
let translated_instruction = translate_type!(Instruction, 100, ®ions).unwrap();
|
||||
let translated_instruction = translate_type!(Instruction, 96, ®ions).unwrap();
|
||||
assert_eq!(instruction, *translated_instruction);
|
||||
regions[0].len = 1;
|
||||
assert!(translate_type!(Instruction, 100, ®ions).is_err());
|
||||
regions[0].len = std::mem::size_of::<Instruction>() as u64 * 2;
|
||||
assert!(translate_type!(Instruction, 100, ®ions).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Binary file not shown.
|
@ -289,12 +289,15 @@ static bool sol_deserialize(
|
|||
if (dup_info == UINT8_MAX) {
|
||||
input += sizeof(uint8_t);
|
||||
input += sizeof(uint8_t);
|
||||
input += sizeof(SolPubkey);
|
||||
input += sizeof(uint64_t);
|
||||
input += *(uint64_t *) input;
|
||||
input += sizeof(uint64_t);
|
||||
input += sizeof(SolPubkey);
|
||||
input += sizeof(uint8_t);
|
||||
input += 4; // padding
|
||||
input += sizeof(SolPubkey);
|
||||
input += sizeof(SolPubkey);
|
||||
input += sizeof(uint64_t);
|
||||
uint64_t data_len = *(uint64_t *) input;
|
||||
input += sizeof(uint64_t);
|
||||
input += data_len;
|
||||
input += 16 - (data_len % 16); // padding
|
||||
input += sizeof(uint64_t);
|
||||
}
|
||||
continue;
|
||||
|
@ -308,10 +311,20 @@ static bool sol_deserialize(
|
|||
params->ka[i].is_writable = *(uint8_t *) input != 0;
|
||||
input += sizeof(uint8_t);
|
||||
|
||||
// executable?
|
||||
params->ka[i].executable = *(uint8_t *) input;
|
||||
input += sizeof(uint8_t);
|
||||
|
||||
input += 4; // padding
|
||||
|
||||
// key
|
||||
params->ka[i].key = (SolPubkey *) input;
|
||||
input += sizeof(SolPubkey);
|
||||
|
||||
// owner
|
||||
params->ka[i].owner = (SolPubkey *) input;
|
||||
input += sizeof(SolPubkey);
|
||||
|
||||
// lamports
|
||||
params->ka[i].lamports = (uint64_t *) input;
|
||||
input += sizeof(uint64_t);
|
||||
|
@ -322,26 +335,22 @@ static bool sol_deserialize(
|
|||
params->ka[i].data = (uint8_t *) input;
|
||||
input += params->ka[i].data_len;
|
||||
|
||||
// owner
|
||||
params->ka[i].owner = (SolPubkey *) input;
|
||||
input += sizeof(SolPubkey);
|
||||
|
||||
// executable?
|
||||
params->ka[i].executable = *(uint8_t *) input;
|
||||
input += sizeof(uint8_t);
|
||||
input += 16 - (params->ka[i].data_len % 16); // padding
|
||||
|
||||
// rent epoch
|
||||
params->ka[i].rent_epoch = *(uint64_t *) input;
|
||||
input += sizeof(uint64_t);
|
||||
} else {
|
||||
params->ka[i].is_signer = params->ka[dup_info].is_signer;
|
||||
params->ka[i].is_writable = params->ka[dup_info].is_writable;
|
||||
params->ka[i].executable = params->ka[dup_info].executable;
|
||||
params->ka[i].key = params->ka[dup_info].key;
|
||||
params->ka[i].owner = params->ka[dup_info].owner;
|
||||
params->ka[i].lamports = params->ka[dup_info].lamports;
|
||||
params->ka[i].data_len = params->ka[dup_info].data_len;
|
||||
params->ka[i].data = params->ka[dup_info].data;
|
||||
params->ka[i].owner = params->ka[dup_info].owner;
|
||||
params->ka[i].executable = params->ka[dup_info].executable;
|
||||
params->ka[i].rent_epoch = params->ka[dup_info].rent_epoch;
|
||||
input += 7; // padding
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
crate::declare_id!("BPFLoader1111111111111111111111111111111111");
|
||||
crate::declare_id!("BPFLoader2111111111111111111111111111111111");
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
crate::declare_id!("BPFLoader1111111111111111111111111111111111");
|
|
@ -76,9 +76,18 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
|
|||
let is_writable = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let executable = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
|
||||
offset += 4; // padding
|
||||
|
||||
let key: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
offset += size_of::<Pubkey>();
|
||||
|
||||
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
offset += size_of::<Pubkey>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let lamports = Rc::new(RefCell::new(&mut *(input.add(offset) as *mut u64)));
|
||||
offset += size_of::<u64>();
|
||||
|
@ -92,12 +101,7 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
|
|||
}));
|
||||
offset += data_len;
|
||||
|
||||
let owner: &Pubkey = &*(input.add(offset) as *const Pubkey);
|
||||
offset += size_of::<Pubkey>();
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let executable = *(input.add(offset) as *const u8) != 0;
|
||||
offset += size_of::<u8>();
|
||||
offset += 16 - (offset % 16); // padding
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let rent_epoch = *(input.add(offset) as *const u64);
|
||||
|
@ -114,6 +118,8 @@ pub unsafe fn deserialize<'a>(input: *mut u8) -> (&'a Pubkey, Vec<AccountInfo<'a
|
|||
rent_epoch,
|
||||
});
|
||||
} else {
|
||||
offset += 7; // padding
|
||||
|
||||
// Duplicate account, clone the original
|
||||
accounts.push(accounts[dup_info as usize].clone());
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ pub type LoaderEntrypoint = unsafe extern "C" fn(
|
|||
#[rustversion::since(1.46.0)]
|
||||
#[macro_export]
|
||||
macro_rules! declare_name {
|
||||
($name:ident) => {
|
||||
($name:ident, $filename:ident, $id:path) => {
|
||||
#[macro_export]
|
||||
macro_rules! $name {
|
||||
() => {
|
||||
|
@ -66,8 +66,8 @@ macro_rules! declare_name {
|
|||
// `respan!` respans the path `$crate::id`, which we then call (hence the extra
|
||||
// parens)
|
||||
(
|
||||
stringify!($name).to_string(),
|
||||
::solana_sdk::respan!($crate::id, $name)(),
|
||||
stringify!($filename).to_string(),
|
||||
::solana_sdk::respan!($crate::$id, $name)(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -77,11 +77,11 @@ macro_rules! declare_name {
|
|||
#[rustversion::not(since(1.46.0))]
|
||||
#[macro_export]
|
||||
macro_rules! declare_name {
|
||||
($name:ident) => {
|
||||
($name:ident, $filename:ident, $id:path) => {
|
||||
#[macro_export]
|
||||
macro_rules! $name {
|
||||
() => {
|
||||
(stringify!($name).to_string(), $crate::id())
|
||||
(stringify!($filename).to_string(), $crate::$id())
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -90,8 +90,10 @@ macro_rules! declare_name {
|
|||
/// Convenience macro to declare a native program
|
||||
///
|
||||
/// bs58_string: bs58 string representation the program's id
|
||||
/// name: Name of the program, must match the library name in Cargo.toml
|
||||
/// name: Name of the program
|
||||
/// filename: must match the library name in Cargo.toml
|
||||
/// entrypoint: Program's entrypoint, must be of `type Entrypoint`
|
||||
/// id: Path to the program id access function, used if not called in `src/lib`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -159,7 +161,7 @@ macro_rules! declare_name {
|
|||
macro_rules! declare_program(
|
||||
($bs58_string:expr, $name:ident, $entrypoint:expr) => (
|
||||
$crate::declare_id!($bs58_string);
|
||||
$crate::declare_name!($name);
|
||||
$crate::declare_name!($name, $name, id);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $name(
|
||||
|
@ -174,12 +176,16 @@ macro_rules! declare_program(
|
|||
|
||||
/// Same as declare_program but for native loaders
|
||||
#[macro_export]
|
||||
macro_rules! declare_loader(
|
||||
($bs58_string:expr, $name:ident, $entrypoint:expr) => (
|
||||
macro_rules! declare_loader {
|
||||
($bs58_string:expr, $name:ident, $entrypoint:expr) => {
|
||||
$crate::declare_loader!($bs58_string, $name, $entrypoint, $name, id);
|
||||
};
|
||||
($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident) => {
|
||||
$crate::declare_loader!($bs58_string, $name, $entrypoint, $filename, id);
|
||||
};
|
||||
($bs58_string:expr, $name:ident, $entrypoint:expr, $filename:ident, $id:path) => {
|
||||
$crate::declare_id!($bs58_string);
|
||||
$crate::declare_name!($name);
|
||||
|
||||
|
||||
$crate::declare_name!($name, $filename, $id);
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $name(
|
||||
|
@ -190,8 +196,8 @@ macro_rules! declare_loader(
|
|||
) -> Result<(), $crate::instruction::InstructionError> {
|
||||
$entrypoint(program_id, keyed_accounts, instruction_data, invoke_context)
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ pub mod abi_example;
|
|||
pub mod account;
|
||||
pub mod account_utils;
|
||||
pub mod bpf_loader;
|
||||
pub mod bpf_loader_deprecated;
|
||||
pub mod clock;
|
||||
pub mod commitment_config;
|
||||
pub mod decode_error;
|
||||
|
|
Loading…
Reference in New Issue