Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ledger-tool/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,8 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
.transaction_context
.get_current_instruction_context()
.unwrap(),
true, // copy_account_data
true, // copy_account_data
false, // is_abi_v2
)
.unwrap();

Expand Down
262 changes: 207 additions & 55 deletions program-runtime/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {
solana_pubkey::Pubkey,
solana_sbpf::{
aligned_memory::{AlignedMemory, Pod},
ebpf::{HOST_ALIGN, MM_INPUT_START},
ebpf::{HOST_ALIGN, MM_INPUT_START, MM_REGION_SIZE},
memory_region::{MemoryRegion, MemoryState},
},
solana_sdk_ids::bpf_loader_deprecated,
Expand Down Expand Up @@ -99,7 +99,8 @@ impl Serializer {
} else {
self.push_region(true);
let vaddr = self.vaddr;
self.push_account_data_region(account)?;
self.push_account_data_region(vaddr, account)?;
self.vaddr += account.get_data().len() as u64;
vaddr
};

Expand Down Expand Up @@ -128,19 +129,17 @@ impl Serializer {

fn push_account_data_region(
&mut self,
vaddr: u64,
account: &mut BorrowedAccount<'_>,
) -> Result<(), InstructionError> {
if !account.get_data().is_empty() {
let region = match account_data_region_memory_state(account) {
MemoryState::Readable => MemoryRegion::new_readonly(account.get_data(), self.vaddr),
MemoryState::Writable => {
MemoryRegion::new_writable(account.get_data_mut()?, self.vaddr)
}
MemoryState::Readable => MemoryRegion::new_readonly(account.get_data(), vaddr),
MemoryState::Writable => MemoryRegion::new_writable(account.get_data_mut()?, vaddr),
MemoryState::Cow(index_in_transaction) => {
MemoryRegion::new_cow(account.get_data(), self.vaddr, index_in_transaction)
MemoryRegion::new_cow(account.get_data(), vaddr, index_in_transaction)
}
};
self.vaddr += region.len;
self.regions.push(region);
}

Expand All @@ -165,8 +164,8 @@ impl Serializer {
self.vaddr += range.len() as u64;
}

fn finish(mut self) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
self.push_region(true);
fn finish(mut self, writable: bool) -> (AlignedMemory<HOST_ALIGN>, Vec<MemoryRegion>) {
self.push_region(writable);
debug_assert_eq!(self.region_start, self.buffer.len());
(self.buffer, self.regions)
}
Expand All @@ -189,6 +188,7 @@ pub fn serialize_parameters(
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
copy_account_data: bool,
is_abi_v2: bool,
) -> Result<
(
AlignedMemory<HOST_ALIGN>,
Expand All @@ -202,56 +202,62 @@ pub fn serialize_parameters(
return Err(InstructionError::MaxAccountsExceeded);
}

let (program_id, is_loader_deprecated) = {
let program_account =
instruction_context.try_borrow_last_program_account(transaction_context)?;
(
*program_account.get_key(),
*program_account.get_owner() == bpf_loader_deprecated::id(),
)
};

let accounts = (0..instruction_context.get_number_of_instruction_accounts())
.map(|instruction_account_index| {
if let Some(index) = instruction_context
.is_instruction_account_duplicate(instruction_account_index)
.unwrap()
{
SerializeAccount::Duplicate(index)
} else {
let account = instruction_context
.try_borrow_instruction_account(transaction_context, instruction_account_index)
.unwrap();
SerializeAccount::Account(instruction_account_index, account)
}
})
// fun fact: jemalloc is good at caching tiny allocations like this one,
// so collecting here is actually faster than passing the iterator
// around, since the iterator does the work to produce its items each
// time it's iterated on.
.collect::<Vec<_>>();

if is_loader_deprecated {
serialize_parameters_unaligned(
accounts,
instruction_context.get_instruction_data(),
&program_id,
copy_account_data,
)
if is_abi_v2 {
abiv2_serialize_instruction(transaction_context, instruction_context)
} else {
serialize_parameters_aligned(
accounts,
instruction_context.get_instruction_data(),
&program_id,
copy_account_data,
)
let (program_id, is_loader_deprecated) = {
let program_account =
instruction_context.try_borrow_last_program_account(transaction_context)?;
(
*program_account.get_key(),
*program_account.get_owner() == bpf_loader_deprecated::id(),
)
};
let accounts = (0..instruction_context.get_number_of_instruction_accounts())
.map(|instruction_account_index| {
if let Some(index) = instruction_context
.is_instruction_account_duplicate(instruction_account_index)
.unwrap()
{
SerializeAccount::Duplicate(index)
} else {
let account = instruction_context
.try_borrow_instruction_account(
transaction_context,
instruction_account_index,
)
.unwrap();
SerializeAccount::Account(instruction_account_index, account)
}
})
// fun fact: jemalloc is good at caching tiny allocations like this one,
// so collecting here is actually faster than passing the iterator
// around, since the iterator does the work to produce its items each
// time it's iterated on.
.collect::<Vec<_>>();
if is_loader_deprecated {
serialize_parameters_unaligned(
accounts,
instruction_context.get_instruction_data(),
&program_id,
copy_account_data,
)
} else {
serialize_parameters_aligned(
accounts,
instruction_context.get_instruction_data(),
&program_id,
copy_account_data,
)
}
}
}

pub fn deserialize_parameters(
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
copy_account_data: bool,
is_abi_v2: bool,
buffer: &[u8],
accounts_metadata: &[SerializedAccountMetadata],
) -> Result<(), InstructionError> {
Expand All @@ -260,7 +266,9 @@ pub fn deserialize_parameters(
.get_owner()
== bpf_loader_deprecated::id();
let account_lengths = accounts_metadata.iter().map(|a| a.original_data_len);
if is_loader_deprecated {
if is_abi_v2 {
Ok(())
} else if is_loader_deprecated {
deserialize_parameters_unaligned(
transaction_context,
instruction_context,
Expand Down Expand Up @@ -353,7 +361,7 @@ fn serialize_parameters_unaligned(
s.write_all(instruction_data);
s.write_all(program_id.as_ref());

let (mem, regions) = s.finish();
let (mem, regions) = s.finish(true);
Ok((mem, regions, accounts_metadata))
}

Expand Down Expand Up @@ -494,7 +502,7 @@ fn serialize_parameters_aligned(
s.write_all(instruction_data);
s.write_all(program_id.as_ref());

let (mem, regions) = s.finish();
let (mem, regions) = s.finish(true);
Ok((mem, regions, accounts_metadata))
}

Expand Down Expand Up @@ -605,6 +613,145 @@ fn deserialize_parameters_aligned<I: IntoIterator<Item = usize>>(
Ok(())
}

const TRANACTION_HEADER_VM_ADDRESS: u64 = MM_INPUT_START;
const SCRATCHPAD_DATA_VM_ADDRESS: u64 = MM_INPUT_START + MM_REGION_SIZE;
const INSTRUCTION_HEADER_VM_ADDRESS: u64 = MM_INPUT_START + MM_REGION_SIZE * 2;
const INSTRUCTION_DATA_VM_ADDRESS: u64 = MM_INPUT_START + MM_REGION_SIZE * 3;
const ACCOUNT_DATA_VM_ADDRESS: u64 = MM_INPUT_START + MM_REGION_SIZE * 4;

pub(crate) fn abiv2_serialize_transaction(
transaction_context: &TransactionContext,
) -> (AlignedMemory<HOST_ALIGN>, MemoryRegion) {
let size = 56 // size_of::<>()
+ transaction_context.get_number_of_accounts() as usize * 88; // size_of::<>()
let mut s = Serializer::new(size, TRANACTION_HEADER_VM_ADDRESS, true, false);
// s.write::<u32>(0x76494241u32.to_le());
// s.write::<u32>(0x00000002u32.to_le());
let scratchpad = transaction_context.get_return_data();
s.write_all(scratchpad.0.as_ref());
s.write::<u64>(SCRATCHPAD_DATA_VM_ADDRESS.to_le());
s.write::<u64>((scratchpad.1.len() as u64).to_le());
s.write::<u64>((transaction_context.get_number_of_accounts() as u64).to_le());
for index_in_transaction in 0..transaction_context.get_number_of_accounts() {
use solana_account::ReadableAccount;
let transaction_account = transaction_context
.get_account_at_index(index_in_transaction)
.unwrap()
.borrow();
let _vm_key_addr = s.write_all(
transaction_context
.get_key_of_account_at_index(index_in_transaction)
.unwrap()
.as_ref(),
);
let _vm_owner_addr = s.write_all(transaction_account.owner().as_ref());
let _vm_lamports_addr = s.write::<u64>(transaction_account.lamports().to_le());
let vm_data_addr = ACCOUNT_DATA_VM_ADDRESS + MM_REGION_SIZE * index_in_transaction as u64;
s.write::<u64>(vm_data_addr.to_le());
s.write::<u64>((transaction_account.data().len() as u64).to_le());
// s.write::<u64>((transaction_account.capacity() as u64).to_le());
}
let (mem, regions) = s.finish(false);
(mem, regions.into_iter().next().unwrap())
}

fn abiv2_serialize_instruction(
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
) -> Result<
(
AlignedMemory<HOST_ALIGN>,
Vec<MemoryRegion>,
Vec<SerializedAccountMetadata>,
),
InstructionError,
> {
/*let number_of_unique_instruction_accounts = accounts.iter().fold(0, |accumulator, account| {
if matches!(account, SerializeAccount::Account(_, _)) {
accumulator + 1
} else {
accumulator
}
});*/

let size = 20 // size_of::<>()
+ instruction_context.get_number_of_instruction_accounts() as usize * 4; // size_of::<>();
let mut s = Serializer::new(size, INSTRUCTION_HEADER_VM_ADDRESS, true, false);
let instruction_data = instruction_context.get_instruction_data();
s.write::<u64>(INSTRUCTION_DATA_VM_ADDRESS.to_le());
s.write::<u64>((instruction_data.len() as u64).to_le());
let program_account_index_in_transaction = instruction_context
.get_index_of_program_account_in_transaction(
instruction_context
.get_number_of_program_accounts()
.saturating_sub(1),
)
.unwrap();
s.write::<u16>(program_account_index_in_transaction.to_le());
s.write::<u16>(
instruction_context
.get_number_of_instruction_accounts()
.to_le(),
);
for index_in_instruction in 0..instruction_context.get_number_of_instruction_accounts() {
let mut borrowed_account = instruction_context
.try_borrow_instruction_account(transaction_context, index_in_instruction)
.unwrap();
let index_in_transaction = borrowed_account.get_index_in_transaction();
s.write::<u16>(index_in_transaction.to_le());
let mut flags = 0u16;
if borrowed_account.is_signer() {
flags |= 1 << 0;
}
if borrowed_account.is_writable() {
flags |= 1 << 1;
}
s.write::<u16>(flags.to_le());
if instruction_context
.is_instruction_account_duplicate(index_in_instruction)
.unwrap()
.is_none()
{
let vm_data_addr =
ACCOUNT_DATA_VM_ADDRESS + MM_REGION_SIZE * index_in_transaction as u64;
s.push_account_data_region(vm_data_addr, &mut borrowed_account)?;
}
}
let (mem, mut regions) = s.finish(false);

/*let mut index: IndexOfAccount = 0;
let mut deduplicated_account_indices = Vec::with_capacity(accounts.len());
for account in accounts.iter() {
match account {
SerializeAccount::Account(instruction_account_index, _) => {
deduplicated_account_indices.push(instruction_account_index - index);
}
SerializeAccount::Duplicate(position) => {
deduplicated_account_indices.push(
*deduplicated_account_indices
.get(*position as usize)
.unwrap(),
);
index += 1;
}
}
}
for deduplicated_account_index in deduplicated_account_indices {
s.write::<u16>(deduplicated_account_index.to_le());
}*/

let scratchpad = transaction_context.get_return_data();
regions.push(MemoryRegion::new_readonly(
scratchpad.1,
SCRATCHPAD_DATA_VM_ADDRESS,
));
regions.push(MemoryRegion::new_readonly(
instruction_data,
INSTRUCTION_DATA_VM_ADDRESS,
));
Ok((mem, regions, Vec::default()))
}

pub fn account_data_region_memory_state(account: &BorrowedAccount<'_>) -> MemoryState {
if account.can_data_be_changed().is_ok() {
if account.is_shared() {
Expand Down Expand Up @@ -729,6 +876,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false,
);
assert_eq!(
serialization_result.as_ref().err(),
Expand Down Expand Up @@ -883,6 +1031,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
)
.unwrap();

Expand Down Expand Up @@ -942,6 +1091,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
serialized.as_slice(),
&accounts_metadata,
)
Expand Down Expand Up @@ -974,6 +1124,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
)
.unwrap();
let mut serialized_regions = concat_regions(&regions);
Expand Down Expand Up @@ -1012,6 +1163,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
serialized.as_slice(),
&account_lengths,
)
Expand Down
1 change: 1 addition & 0 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub fn invoke_builtin_function(
transaction_context,
instruction_context,
true, // copy_account_data // There is no VM so direct mapping can not be implemented here
false, // is_abi_v2
)?;

// Deserialize data back into instruction params
Expand Down
Loading