Serialization refactor (#28251)
* Use infallible, unchecked methods to write into the serialization buffer We serialize in two steps: first we compute the size of the buffer, then we write into it. Therefore there's no need to check if each individual write fits the buffer - we know it does we just computed the required size. * serialize_parameters: remove extra loop/borrows Remove one extra loop over accounts to gather account lengths. Also gather all accounts at once and avoid temporary borrows. * Move creating MemoryRegions for serialized parameters from create_vm to serialize_parameters This is in preparation of using multiple MemoryRegions once we land direct account mapping. * bpf_loader: introduce internal API to build serialization buffer/regions This is prep work for landing the direct_mapping feature, which maps account data in their own memory regions. * serialization: fix after API changes
This commit is contained in:
parent
a400178744
commit
f6fee4ac3a
|
@ -118,7 +118,7 @@ pub fn builtin_process_instruction(
|
|||
let deduplicated_indices: HashSet<IndexOfAccount> = instruction_account_indices.collect();
|
||||
|
||||
// Serialize entrypoint parameters with BPF ABI
|
||||
let (mut parameter_bytes, _account_lengths) = serialize_parameters(
|
||||
let (mut parameter_bytes, _regions, _account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
|
|
|
@ -14,7 +14,9 @@ use {
|
|||
solana_measure::measure::Measure,
|
||||
solana_program_runtime::invoke_context::with_mock_invoke_context,
|
||||
solana_rbpf::{
|
||||
ebpf::MM_INPUT_START,
|
||||
elf::Executable,
|
||||
memory_region::MemoryRegion,
|
||||
verifier::RequisiteVerifier,
|
||||
vm::{Config, InstructionMeter, SyscallRegistry, VerifiedExecutable},
|
||||
},
|
||||
|
@ -31,7 +33,6 @@ use {
|
|||
instruction::{AccountMeta, Instruction},
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
},
|
||||
std::{env, fs::File, io::Read, mem, path::PathBuf, sync::Arc},
|
||||
|
@ -124,7 +125,7 @@ fn bench_program_alu(bencher: &mut Bencher) {
|
|||
let mut instruction_meter = ThisInstructionMeter { compute_meter };
|
||||
let mut vm = create_vm(
|
||||
&verified_executable,
|
||||
&mut inner_iter,
|
||||
vec![MemoryRegion::new_writable(&mut inner_iter, MM_INPUT_START)],
|
||||
vec![],
|
||||
invoke_context,
|
||||
)
|
||||
|
@ -224,7 +225,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
|
|||
.mock_set_remaining(BUDGET);
|
||||
|
||||
// Serialize account data
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
let (_serialized, regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
|
@ -250,7 +251,7 @@ fn bench_create_vm(bencher: &mut Bencher) {
|
|||
bencher.iter(|| {
|
||||
let _ = create_vm(
|
||||
&verified_executable,
|
||||
serialized.as_slice_mut(),
|
||||
regions.clone(),
|
||||
account_lengths.clone(),
|
||||
invoke_context,
|
||||
)
|
||||
|
@ -271,7 +272,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
|
|||
.mock_set_remaining(BUDGET);
|
||||
|
||||
// Serialize account data
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
let (_serialized, regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
|
@ -298,7 +299,7 @@ fn bench_instruction_count_tuner(_bencher: &mut Bencher) {
|
|||
let mut instruction_meter = ThisInstructionMeter { compute_meter };
|
||||
let mut vm = create_vm(
|
||||
&verified_executable,
|
||||
serialized.as_slice_mut(),
|
||||
regions,
|
||||
account_lengths,
|
||||
invoke_context,
|
||||
)
|
||||
|
|
|
@ -224,16 +224,6 @@ fn run_program(name: &str) -> u64 {
|
|||
file.read_to_end(&mut data).unwrap();
|
||||
let loader_id = bpf_loader::id();
|
||||
with_mock_invoke_context(loader_id, 0, false, |invoke_context| {
|
||||
let (parameter_bytes, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap(),
|
||||
true, // should_cap_ix_accounts
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let compute_meter = invoke_context.get_compute_meter();
|
||||
let mut instruction_meter = ThisInstructionMeter { compute_meter };
|
||||
let config = Config {
|
||||
|
@ -278,11 +268,21 @@ fn run_program(name: &str) -> u64 {
|
|||
transaction_context
|
||||
.set_return_data(caller, Vec::new())
|
||||
.unwrap();
|
||||
let mut parameter_bytes = parameter_bytes.clone();
|
||||
|
||||
let (parameter_bytes, regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
.get_current_instruction_context()
|
||||
.unwrap(),
|
||||
true, // should_cap_ix_accounts
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let mut vm = create_vm(
|
||||
&verified_executable,
|
||||
parameter_bytes.as_slice_mut(),
|
||||
regions,
|
||||
account_lengths.clone(),
|
||||
invoke_context,
|
||||
)
|
||||
|
|
|
@ -29,7 +29,7 @@ use {
|
|||
},
|
||||
solana_rbpf::{
|
||||
aligned_memory::AlignedMemory,
|
||||
ebpf::{HOST_ALIGN, MM_HEAP_START, MM_INPUT_START},
|
||||
ebpf::{HOST_ALIGN, MM_HEAP_START},
|
||||
elf::Executable,
|
||||
error::{EbpfError, UserDefinedError},
|
||||
memory_region::MemoryRegion,
|
||||
|
@ -290,7 +290,7 @@ fn check_loader_id(id: &Pubkey) -> bool {
|
|||
/// Create the BPF virtual machine
|
||||
pub fn create_vm<'a, 'b>(
|
||||
program: &'a VerifiedExecutable<RequisiteVerifier, ThisInstructionMeter>,
|
||||
parameter_bytes: &mut [u8],
|
||||
regions: Vec<MemoryRegion>,
|
||||
orig_account_lengths: Vec<usize>,
|
||||
invoke_context: &'a mut InvokeContext<'b>,
|
||||
) -> Result<EbpfVm<'a, RequisiteVerifier, ThisInstructionMeter>, EbpfError> {
|
||||
|
@ -303,13 +303,8 @@ pub fn create_vm<'a, 'b>(
|
|||
);
|
||||
let mut heap =
|
||||
AlignedMemory::<HOST_ALIGN>::zero_filled(compute_budget.heap_size.unwrap_or(HEAP_LENGTH));
|
||||
let parameter_region = MemoryRegion::new_writable(parameter_bytes, MM_INPUT_START);
|
||||
let vm = EbpfVm::new(
|
||||
program,
|
||||
invoke_context,
|
||||
heap.as_slice_mut(),
|
||||
vec![parameter_region],
|
||||
)?;
|
||||
|
||||
let vm = EbpfVm::new(program, invoke_context, heap.as_slice_mut(), regions)?;
|
||||
let check_aligned = bpf_loader_deprecated::id()
|
||||
!= invoke_context
|
||||
.transaction_context
|
||||
|
@ -1331,7 +1326,7 @@ impl Executor for BpfExecutor {
|
|||
let program_id = *instruction_context.get_last_program_key(transaction_context)?;
|
||||
|
||||
let mut serialize_time = Measure::start("serialize");
|
||||
let (mut parameter_bytes, account_lengths) = serialize_parameters(
|
||||
let (parameter_bytes, regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
instruction_context,
|
||||
invoke_context
|
||||
|
@ -1345,7 +1340,7 @@ impl Executor for BpfExecutor {
|
|||
let execution_result = {
|
||||
let mut vm = match create_vm(
|
||||
&self.verified_executable,
|
||||
parameter_bytes.as_slice_mut(),
|
||||
regions,
|
||||
account_lengths,
|
||||
invoke_context,
|
||||
) {
|
||||
|
@ -1480,7 +1475,7 @@ mod tests {
|
|||
super::*,
|
||||
rand::Rng,
|
||||
solana_program_runtime::invoke_context::mock_process_instruction,
|
||||
solana_rbpf::{verifier::Verifier, vm::SyscallRegistry},
|
||||
solana_rbpf::{ebpf::MM_INPUT_START, verifier::Verifier, vm::SyscallRegistry},
|
||||
solana_runtime::{bank::Bank, bank_client::BankClient},
|
||||
solana_sdk::{
|
||||
account::{
|
||||
|
|
|
@ -1,53 +1,166 @@
|
|||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
use {
|
||||
byteorder::{ByteOrder, LittleEndian, WriteBytesExt},
|
||||
solana_rbpf::{aligned_memory::AlignedMemory, ebpf::HOST_ALIGN},
|
||||
byteorder::{ByteOrder, LittleEndian},
|
||||
solana_rbpf::{
|
||||
aligned_memory::{AlignedMemory, Pod},
|
||||
ebpf::{HOST_ALIGN, MM_INPUT_START},
|
||||
memory_region::MemoryRegion,
|
||||
},
|
||||
solana_sdk::{
|
||||
bpf_loader_deprecated,
|
||||
entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
|
||||
instruction::InstructionError,
|
||||
pubkey::Pubkey,
|
||||
system_instruction::MAX_PERMITTED_DATA_LENGTH,
|
||||
transaction_context::{IndexOfAccount, InstructionContext, TransactionContext},
|
||||
transaction_context::{
|
||||
BorrowedAccount, IndexOfAccount, InstructionContext, TransactionContext,
|
||||
},
|
||||
},
|
||||
std::{io::prelude::*, mem::size_of},
|
||||
std::{mem, mem::size_of},
|
||||
};
|
||||
|
||||
/// Maximum number of instruction accounts that can be serialized into the
|
||||
/// BPF VM.
|
||||
const MAX_INSTRUCTION_ACCOUNTS: u8 = NON_DUP_MARKER;
|
||||
|
||||
enum SerializeAccount<'a> {
|
||||
Account(IndexOfAccount, BorrowedAccount<'a>),
|
||||
Duplicate(IndexOfAccount),
|
||||
}
|
||||
|
||||
struct Serializer {
|
||||
pub buffer: AlignedMemory<HOST_ALIGN>,
|
||||
regions: Vec<MemoryRegion>,
|
||||
vaddr: u64,
|
||||
region_start: usize,
|
||||
aligned: bool,
|
||||
}
|
||||
|
||||
impl Serializer {
|
||||
fn new(size: usize, start_addr: u64, aligned: bool) -> Serializer {
|
||||
Serializer {
|
||||
buffer: AlignedMemory::with_capacity(size),
|
||||
regions: Vec::new(),
|
||||
region_start: 0,
|
||||
vaddr: start_addr,
|
||||
aligned,
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> {
|
||||
self.buffer.fill_write(num, value)
|
||||
}
|
||||
|
||||
pub fn write<T: Pod>(&mut self, value: T) {
|
||||
self.debug_assert_alignment::<T>();
|
||||
// Safety:
|
||||
// in serialize_parameters_(aligned|unaligned) first we compute the
|
||||
// required size then we write into the newly allocated buffer. There's
|
||||
// no need to check bounds at every write.
|
||||
//
|
||||
// AlignedMemory::write_unchecked _does_ debug_assert!() that the capacity
|
||||
// is enough, so in the unlikely case we introduce a bug in the size
|
||||
// computation, tests will abort.
|
||||
unsafe {
|
||||
self.buffer.write_unchecked(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_all(&mut self, value: &[u8]) {
|
||||
// Safety:
|
||||
// see write() - the buffer is guaranteed to be large enough
|
||||
unsafe {
|
||||
self.buffer.write_all_unchecked(value);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_account(&mut self, account: &BorrowedAccount<'_>) -> Result<(), InstructionError> {
|
||||
self.write_all(account.get_data());
|
||||
|
||||
if self.aligned {
|
||||
let align_offset =
|
||||
(account.get_data().len() as *const u8).align_offset(BPF_ALIGN_OF_U128);
|
||||
self.fill_write(MAX_PERMITTED_DATA_INCREASE + align_offset, 0)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_region(&mut self) {
|
||||
let range = self.region_start..self.buffer.len();
|
||||
let region = MemoryRegion::new_writable(
|
||||
self.buffer.as_slice_mut().get_mut(range.clone()).unwrap(),
|
||||
self.vaddr,
|
||||
);
|
||||
self.regions.push(region);
|
||||
self.region_start = range.end;
|
||||
self.vaddr += range.len() as u64;
|
||||
}
|
||||
|
||||
fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
|
||||
self.push_region();
|
||||
debug_assert_eq!(self.region_start, self.buffer.len());
|
||||
(self.buffer, self.regions)
|
||||
}
|
||||
|
||||
fn debug_assert_alignment<T>(&self) {
|
||||
debug_assert!(
|
||||
!self.aligned
|
||||
|| self
|
||||
.buffer
|
||||
.as_slice()
|
||||
.as_ptr_range()
|
||||
.end
|
||||
.align_offset(mem::align_of::<T>())
|
||||
== 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_parameters(
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
should_cap_ix_accounts: bool,
|
||||
) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<usize>), InstructionError> {
|
||||
) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>, Vec<usize>), InstructionError> {
|
||||
let num_ix_accounts = instruction_context.get_number_of_instruction_accounts();
|
||||
if should_cap_ix_accounts && num_ix_accounts > MAX_INSTRUCTION_ACCOUNTS as IndexOfAccount {
|
||||
return Err(InstructionError::MaxAccountsExceeded);
|
||||
}
|
||||
|
||||
let is_loader_deprecated = *instruction_context
|
||||
.try_borrow_last_program_account(transaction_context)?
|
||||
.get_owner()
|
||||
== bpf_loader_deprecated::id();
|
||||
if is_loader_deprecated {
|
||||
serialize_parameters_unaligned(transaction_context, instruction_context)
|
||||
} else {
|
||||
serialize_parameters_aligned(transaction_context, instruction_context)
|
||||
|
||||
let num_accounts = instruction_context.get_number_of_instruction_accounts() as usize;
|
||||
let mut accounts = Vec::with_capacity(num_accounts);
|
||||
let mut account_lengths: Vec<usize> = Vec::with_capacity(num_accounts);
|
||||
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
|
||||
if let Some(index) =
|
||||
instruction_context.is_instruction_account_duplicate(instruction_account_index)?
|
||||
{
|
||||
accounts.push(SerializeAccount::Duplicate(index));
|
||||
// unwrap here is safe: if an account is a duplicate, we must have
|
||||
// seen the original already
|
||||
account_lengths.push(*account_lengths.get(index as usize).unwrap());
|
||||
} else {
|
||||
let account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?;
|
||||
account_lengths.push(account.get_data().len());
|
||||
accounts.push(SerializeAccount::Account(
|
||||
instruction_account_index,
|
||||
account,
|
||||
));
|
||||
};
|
||||
}
|
||||
.and_then(|buffer| {
|
||||
let account_lengths = (0..instruction_context.get_number_of_instruction_accounts())
|
||||
.map(|instruction_account_index| {
|
||||
Ok(instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?
|
||||
.get_data()
|
||||
.len())
|
||||
})
|
||||
.collect::<Result<Vec<usize>, InstructionError>>()?;
|
||||
Ok((buffer, account_lengths))
|
||||
})
|
||||
|
||||
if is_loader_deprecated {
|
||||
serialize_parameters_unaligned(transaction_context, instruction_context, accounts)
|
||||
} else {
|
||||
serialize_parameters_aligned(transaction_context, instruction_context, accounts)
|
||||
}
|
||||
.map(|(buffer, regions)| (buffer, regions, account_lengths))
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters(
|
||||
|
@ -77,82 +190,65 @@ pub fn deserialize_parameters(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn serialize_parameters_unaligned(
|
||||
fn serialize_parameters_unaligned(
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
) -> Result<AlignedMemory<HOST_ALIGN>, InstructionError> {
|
||||
accounts: Vec<SerializeAccount>,
|
||||
) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
|
||||
// Calculate size in order to alloc once
|
||||
let mut size = size_of::<u64>();
|
||||
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
|
||||
let duplicate =
|
||||
instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
|
||||
for account in &accounts {
|
||||
size += 1; // dup
|
||||
if duplicate.is_none() {
|
||||
let data_len = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?
|
||||
.get_data()
|
||||
.len();
|
||||
size += size_of::<u8>() // is_signer
|
||||
match account {
|
||||
SerializeAccount::Duplicate(_) => {}
|
||||
SerializeAccount::Account(_, account) => {
|
||||
size += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<Pubkey>() // key
|
||||
+ size_of::<u64>() // lamports
|
||||
+ size_of::<u64>() // data len
|
||||
+ data_len // data
|
||||
+ account.get_data().len() // data
|
||||
+ size_of::<Pubkey>() // owner
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u64>(); // rent_epoch
|
||||
}
|
||||
}
|
||||
}
|
||||
size += size_of::<u64>() // instruction data len
|
||||
+ instruction_context.get_instruction_data().len() // instruction data
|
||||
+ size_of::<Pubkey>(); // program id
|
||||
let mut v = AlignedMemory::<HOST_ALIGN>::with_capacity(size);
|
||||
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
|
||||
let duplicate =
|
||||
instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
|
||||
if let Some(position) = duplicate {
|
||||
v.write_u8(position as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
} else {
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?;
|
||||
v.write_u8(NON_DUP_MARKER)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_signer() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_writable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(borrowed_account.get_key().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(borrowed_account.get_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(borrowed_account.get_owner().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_executable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
}
|
||||
let mut s = Serializer::new(size, MM_INPUT_START, false);
|
||||
|
||||
s.write::<u64>((accounts.len() as u64).to_le());
|
||||
for account in accounts {
|
||||
match account {
|
||||
SerializeAccount::Duplicate(position) => s.write(position as u8),
|
||||
SerializeAccount::Account(_, account) => {
|
||||
s.write::<u8>(NON_DUP_MARKER);
|
||||
s.write::<u8>(account.is_signer() as u8);
|
||||
s.write::<u8>(account.is_writable() as u8);
|
||||
s.write_all(account.get_key().as_ref());
|
||||
s.write::<u64>(account.get_lamports().to_le());
|
||||
s.write::<u64>((account.get_data().len() as u64).to_le());
|
||||
s.write_account(&account)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
s.write_all(account.get_owner().as_ref());
|
||||
s.write::<u8>(account.is_executable() as u8);
|
||||
s.write::<u64>((account.get_rent_epoch() as u64).to_le());
|
||||
}
|
||||
};
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(instruction_context.get_instruction_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(
|
||||
s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
|
||||
s.write_all(instruction_context.get_instruction_data());
|
||||
s.write_all(
|
||||
instruction_context
|
||||
.try_borrow_last_program_account(transaction_context)?
|
||||
.get_key()
|
||||
.as_ref(),
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
Ok(v)
|
||||
);
|
||||
|
||||
Ok(s.finish())
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_unaligned(
|
||||
|
@ -205,24 +301,20 @@ pub fn deserialize_parameters_unaligned(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize_parameters_aligned(
|
||||
fn serialize_parameters_aligned(
|
||||
transaction_context: &TransactionContext,
|
||||
instruction_context: &InstructionContext,
|
||||
) -> Result<AlignedMemory<HOST_ALIGN>, InstructionError> {
|
||||
accounts: Vec<SerializeAccount>,
|
||||
) -> Result<(AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>), InstructionError> {
|
||||
// Calculate size in order to alloc once
|
||||
let mut size = size_of::<u64>();
|
||||
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
|
||||
let duplicate =
|
||||
instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
|
||||
for account in &accounts {
|
||||
size += 1; // dup
|
||||
if duplicate.is_some() {
|
||||
size += 7; // padding to 64-bit aligned
|
||||
} else {
|
||||
let data_len = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?
|
||||
.get_data()
|
||||
.len();
|
||||
size += size_of::<u8>() // is_signer
|
||||
match account {
|
||||
SerializeAccount::Duplicate(_) => size += 7, // padding to 64-bit aligned
|
||||
SerializeAccount::Account(_, account) => {
|
||||
let data_len = account.get_data().len();
|
||||
size += size_of::<u8>() // is_signer
|
||||
+ size_of::<u8>() // is_writable
|
||||
+ size_of::<u8>() // executable
|
||||
+ size_of::<u32>() // original_data_len
|
||||
|
@ -234,69 +326,49 @@ pub fn serialize_parameters_aligned(
|
|||
+ MAX_PERMITTED_DATA_INCREASE
|
||||
+ (data_len as *const u8).align_offset(BPF_ALIGN_OF_U128)
|
||||
+ size_of::<u64>(); // rent epoch
|
||||
}
|
||||
}
|
||||
}
|
||||
size += size_of::<u64>() // data len
|
||||
+ instruction_context.get_instruction_data().len()
|
||||
+ size_of::<Pubkey>(); // program id;
|
||||
let mut v = AlignedMemory::<HOST_ALIGN>::with_capacity(size);
|
||||
|
||||
let mut s = Serializer::new(size, MM_INPUT_START, true);
|
||||
|
||||
// Serialize into the buffer
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_number_of_instruction_accounts() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
for instruction_account_index in 0..instruction_context.get_number_of_instruction_accounts() {
|
||||
let duplicate =
|
||||
instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
|
||||
if let Some(position) = duplicate {
|
||||
v.write_u8(position as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(&[0u8, 0, 0, 0, 0, 0, 0])
|
||||
.map_err(|_| InstructionError::InvalidArgument)?; // 7 bytes of padding to make 64-bit aligned
|
||||
} else {
|
||||
let borrowed_account = instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, instruction_account_index)?;
|
||||
v.write_u8(NON_DUP_MARKER)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_signer() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_writable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u8(borrowed_account.is_executable() as u8)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(&[0u8, 0, 0, 0])
|
||||
.map_err(|_| InstructionError::InvalidArgument)?; // 4 bytes of padding to make 128-bit aligned
|
||||
v.write_all(borrowed_account.get_key().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(borrowed_account.get_owner().as_ref())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_lamports())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(borrowed_account.get_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.fill_write(
|
||||
MAX_PERMITTED_DATA_INCREASE
|
||||
+ (v.write_index() as *const u8).align_offset(BPF_ALIGN_OF_U128),
|
||||
0,
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_u64::<LittleEndian>(borrowed_account.get_rent_epoch() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
}
|
||||
s.write::<u64>((accounts.len() as u64).to_le());
|
||||
for account in accounts {
|
||||
match account {
|
||||
SerializeAccount::Account(_, borrowed_account) => {
|
||||
s.write::<u8>(NON_DUP_MARKER);
|
||||
s.write::<u8>(borrowed_account.is_signer() as u8);
|
||||
s.write::<u8>(borrowed_account.is_writable() as u8);
|
||||
s.write::<u8>(borrowed_account.is_executable() as u8);
|
||||
s.write_all(&[0u8, 0, 0, 0]);
|
||||
s.write_all(borrowed_account.get_key().as_ref());
|
||||
s.write_all(borrowed_account.get_owner().as_ref());
|
||||
s.write::<u64>(borrowed_account.get_lamports().to_le());
|
||||
s.write::<u64>((borrowed_account.get_data().len() as u64).to_le());
|
||||
s.write_account(&borrowed_account)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
s.write::<u64>((borrowed_account.get_rent_epoch() as u64).to_le());
|
||||
}
|
||||
SerializeAccount::Duplicate(position) => {
|
||||
s.write::<u8>(position as u8);
|
||||
s.write_all(&[0u8, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
};
|
||||
}
|
||||
v.write_u64::<LittleEndian>(instruction_context.get_instruction_data().len() as u64)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(instruction_context.get_instruction_data())
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
v.write_all(
|
||||
s.write::<u64>((instruction_context.get_instruction_data().len() as u64).to_le());
|
||||
s.write_all(instruction_context.get_instruction_data());
|
||||
s.write_all(
|
||||
instruction_context
|
||||
.try_borrow_last_program_account(transaction_context)?
|
||||
.get_key()
|
||||
.as_ref(),
|
||||
)
|
||||
.map_err(|_| InstructionError::InvalidArgument)?;
|
||||
Ok(v)
|
||||
);
|
||||
|
||||
Ok(s.finish())
|
||||
}
|
||||
|
||||
pub fn deserialize_parameters_aligned(
|
||||
|
@ -525,7 +597,7 @@ mod tests {
|
|||
continue;
|
||||
}
|
||||
|
||||
let (mut serialized, _account_lengths) = serialization_result.unwrap();
|
||||
let (mut serialized, _regions, _account_lengths) = serialization_result.unwrap();
|
||||
let (de_program_id, de_accounts, de_instruction_data) =
|
||||
unsafe { deserialize(serialized.as_slice_mut().first_mut().unwrap() as *mut u8) };
|
||||
assert_eq!(de_program_id, &program_id);
|
||||
|
@ -665,7 +737,7 @@ mod tests {
|
|||
|
||||
// check serialize_parameters_aligned
|
||||
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
let (mut serialized, _regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
instruction_context,
|
||||
true,
|
||||
|
@ -741,7 +813,7 @@ mod tests {
|
|||
.borrow_mut()
|
||||
.set_owner(bpf_loader_deprecated::id());
|
||||
|
||||
let (mut serialized, account_lengths) = serialize_parameters(
|
||||
let (mut serialized, _regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
instruction_context,
|
||||
true,
|
||||
|
|
|
@ -233,7 +233,7 @@ native machine code before execting it in the virtual machine.",
|
|||
&instruction_data,
|
||||
);
|
||||
invoke_context.push().unwrap();
|
||||
let (mut parameter_bytes, account_lengths) = serialize_parameters(
|
||||
let (_parameter_bytes, regions, account_lengths) = serialize_parameters(
|
||||
invoke_context.transaction_context,
|
||||
invoke_context
|
||||
.transaction_context
|
||||
|
@ -292,7 +292,7 @@ native machine code before execting it in the virtual machine.",
|
|||
|
||||
let mut vm = create_vm(
|
||||
&verified_executable,
|
||||
parameter_bytes.as_slice_mut(),
|
||||
regions,
|
||||
account_lengths,
|
||||
&mut invoke_context,
|
||||
)
|
||||
|
|
|
@ -700,11 +700,13 @@ pub struct BorrowedAccount<'a> {
|
|||
|
||||
impl<'a> BorrowedAccount<'a> {
|
||||
/// Returns the index of this account (transaction wide)
|
||||
#[inline]
|
||||
pub fn get_index_in_transaction(&self) -> IndexOfAccount {
|
||||
self.index_in_transaction
|
||||
}
|
||||
|
||||
/// Returns the public key of this account (transaction wide)
|
||||
#[inline]
|
||||
pub fn get_key(&self) -> &Pubkey {
|
||||
self.transaction_context
|
||||
.get_key_of_account_at_index(self.index_in_transaction)
|
||||
|
@ -712,6 +714,7 @@ impl<'a> BorrowedAccount<'a> {
|
|||
}
|
||||
|
||||
/// Returns the owner of this account (transaction wide)
|
||||
#[inline]
|
||||
pub fn get_owner(&self) -> &Pubkey {
|
||||
self.account.owner()
|
||||
}
|
||||
|
@ -760,6 +763,7 @@ impl<'a> BorrowedAccount<'a> {
|
|||
}
|
||||
|
||||
/// Returns the number of lamports of this account (transaction wide)
|
||||
#[inline]
|
||||
pub fn get_lamports(&self) -> u64 {
|
||||
self.account.lamports()
|
||||
}
|
||||
|
@ -824,6 +828,7 @@ impl<'a> BorrowedAccount<'a> {
|
|||
}
|
||||
|
||||
/// Returns a read-only slice of the account data (transaction wide)
|
||||
#[inline]
|
||||
pub fn get_data(&self) -> &[u8] {
|
||||
self.account.data()
|
||||
}
|
||||
|
@ -936,6 +941,7 @@ impl<'a> BorrowedAccount<'a> {
|
|||
}
|
||||
|
||||
/// Returns whether this account is executable (transaction wide)
|
||||
#[inline]
|
||||
pub fn is_executable(&self) -> bool {
|
||||
self.account.executable()
|
||||
}
|
||||
|
@ -982,6 +988,7 @@ impl<'a> BorrowedAccount<'a> {
|
|||
|
||||
/// Returns the rent epoch of this account (transaction wide)
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
#[inline]
|
||||
pub fn get_rent_epoch(&self) -> u64 {
|
||||
self.account.rent_epoch()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue