Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - ABIv2 #3110

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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 @@ -543,7 +543,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
1 change: 1 addition & 0 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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
18 changes: 12 additions & 6 deletions programs/bpf_loader/benches/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ fn bench_serialize_unaligned(bencher: &mut Bencher) {
.get_current_instruction_context()
.unwrap();
bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, false).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, false, false).unwrap();
});
}

Expand All @@ -137,7 +138,8 @@ fn bench_serialize_unaligned_copy_account_data(bencher: &mut Bencher) {
.get_current_instruction_context()
.unwrap();
bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, true).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, true, false).unwrap();
});
}

Expand All @@ -149,7 +151,8 @@ fn bench_serialize_aligned(bencher: &mut Bencher) {
.unwrap();

bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, false).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, false, false).unwrap();
});
}

Expand All @@ -161,7 +164,8 @@ fn bench_serialize_aligned_copy_account_data(bencher: &mut Bencher) {
.unwrap();

bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, true).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, true, false).unwrap();
});
}

Expand All @@ -172,7 +176,8 @@ fn bench_serialize_unaligned_max_accounts(bencher: &mut Bencher) {
.get_current_instruction_context()
.unwrap();
bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, false).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, false, false).unwrap();
});
}

Expand All @@ -184,6 +189,7 @@ fn bench_serialize_aligned_max_accounts(bencher: &mut Bencher) {
.unwrap();

bencher.iter(|| {
let _ = serialize_parameters(&transaction_context, instruction_context, false).unwrap();
let _ =
serialize_parameters(&transaction_context, instruction_context, false, false).unwrap();
});
}
2 changes: 2 additions & 0 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,7 @@ pub fn execute<'a, 'b: 'a>(
invoke_context.transaction_context,
instruction_context,
!direct_mapping,
false,
)?;
serialize_time.stop();

Expand Down Expand Up @@ -1488,6 +1489,7 @@ pub fn execute<'a, 'b: 'a>(
.transaction_context
.get_current_instruction_context()?,
copy_account_data,
false,
parameter_bytes,
&invoke_context.get_syscall_context()?.accounts_metadata,
)
Expand Down
202 changes: 190 additions & 12 deletions programs/bpf_loader/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ use {
solana_program_runtime::invoke_context::SerializedAccountMetadata,
solana_rbpf::{
aligned_memory::{AlignedMemory, Pod},
ebpf::{HOST_ALIGN, MM_INPUT_START},
ebpf::{HOST_ALIGN, MM_INPUT_START, MM_PROGRAM_START},
memory_region::{MemoryRegion, MemoryState},
},
solana_sdk::{
bpf_loader_deprecated,
entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER},
entrypoint::{
ABIv2InstructionAccount, ABIv2InstructionContext, BPF_ALIGN_OF_U128,
MAX_PERMITTED_DATA_INCREASE, NON_DUP_MARKER,
},
instruction::InstructionError,
pubkey::Pubkey,
system_instruction::MAX_PERMITTED_DATA_LENGTH,
Expand Down Expand Up @@ -102,7 +105,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 All @@ -129,21 +133,19 @@ impl Serializer {
Ok(vm_data_addr)
}

fn push_account_data_region(
pub 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 Down Expand Up @@ -192,6 +194,7 @@ pub fn serialize_parameters(
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
copy_account_data: bool,
is_abi_v2: bool,
) -> Result<
(
AlignedMemory<HOST_ALIGN>,
Expand Down Expand Up @@ -234,7 +237,13 @@ pub fn serialize_parameters(
// time it's iterated on.
.collect::<Vec<_>>();

if is_loader_deprecated {
if is_abi_v2 {
serialize_parameters_v2(
accounts,
instruction_context.get_instruction_data(),
&program_id,
)
} else if is_loader_deprecated {
serialize_parameters_unaligned(
accounts,
instruction_context.get_instruction_data(),
Expand All @@ -255,6 +264,7 @@ 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 @@ -263,7 +273,14 @@ 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 {
deserialize_parameters_v2(
transaction_context,
instruction_context,
buffer,
account_lengths,
)
} else if is_loader_deprecated {
deserialize_parameters_unaligned(
transaction_context,
instruction_context,
Expand Down Expand Up @@ -603,6 +620,162 @@ pub fn deserialize_parameters_aligned<I: IntoIterator<Item = usize>>(
Ok(())
}

fn serialize_parameters_v2(
mut accounts: Vec<SerializeAccount>,
instruction_data: &[u8],
program_id: &Pubkey,
) -> Result<
(
AlignedMemory<HOST_ALIGN>,
Vec<MemoryRegion>,
Vec<SerializedAccountMetadata>,
),
InstructionError,
> {
let mut accounts_metadata = Vec::with_capacity(accounts.len());
// Calculate size in order to alloc once
let number_of_unique_instruction_accounts = accounts.iter().fold(0, |accumulator, account| {
if matches!(account, SerializeAccount::Account(_, _)) {
accumulator + 1
} else {
accumulator
}
});
let size = size_of::<ABIv2InstructionContext>()
+ size_of::<ABIv2InstructionAccount>() * number_of_unique_instruction_accounts
+ size_of::<u16>() * accounts.len(); // instruction_context.get_number_of_instruction_accounts();

// Serialize into the buffer
const INSTRUCTION_DATA_VM_ADDRESS: u64 = MM_INPUT_START + MM_PROGRAM_START;
let mut s = Serializer::new(size, MM_INPUT_START, true, false);
s.write::<u32>(0x76494241u32.to_le());
s.write::<u32>(0x00000002u32.to_le());
s.write::<u64>(INSTRUCTION_DATA_VM_ADDRESS.to_le());
s.write::<u32>((instruction_data.len() as u32).to_le());
s.write::<u16>((number_of_unique_instruction_accounts as u16).to_le());
s.write::<u16>((accounts.len() as u16).to_le());
s.write_all(program_id.as_ref());
let mut index: IndexOfAccount = 2;
for account in accounts.iter_mut() {
if let SerializeAccount::Account(_, ref mut borrowed_account) = account {
let vm_key_addr = s.write_all(borrowed_account.get_key().as_ref());
let vm_owner_addr = s.write_all(borrowed_account.get_owner().as_ref());
let mut flags = 0u64;
if borrowed_account.is_signer() {
flags |= 1 << 8;
}
if borrowed_account.is_writable() {
flags |= 1 << 16;
}
s.write::<u64>(flags.to_le());
let vm_lamports_addr = s.write::<u64>(borrowed_account.get_lamports().to_le());
let vm_data_addr = MM_INPUT_START + MM_PROGRAM_START * index as u64;
s.push_account_data_region(vm_data_addr, borrowed_account)?;
s.write::<u64>(vm_data_addr.to_le());
s.write::<u32>((borrowed_account.get_data().len() as u32).to_le());
s.write::<u32>((borrowed_account.capacity() as u32).to_le());
accounts_metadata.push(SerializedAccountMetadata {
original_data_len: borrowed_account.get_data().len(),
vm_key_addr,
vm_owner_addr,
vm_lamports_addr,
vm_data_addr,
});
index += 1;
}
}
index = 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 (mem, mut regions) = s.finish();
regions.push(MemoryRegion::new_readonly(
instruction_data,
INSTRUCTION_DATA_VM_ADDRESS,
));
Ok((mem, regions, accounts_metadata))
}

pub fn deserialize_parameters_v2<I: IntoIterator<Item = usize>>(
transaction_context: &TransactionContext,
instruction_context: &InstructionContext,
buffer: &[u8],
account_lengths: I,
) -> Result<(), InstructionError> {
let mut start = size_of::<ABIv2InstructionContext>();
for (instruction_account_index, pre_len) in (0..instruction_context
.get_number_of_instruction_accounts())
.zip(account_lengths.into_iter())
{
let duplicate =
instruction_context.is_instruction_account_duplicate(instruction_account_index)?;
if duplicate.is_none() {
let mut borrowed_account = instruction_context
.try_borrow_instruction_account(transaction_context, instruction_account_index)?;
start += size_of::<Pubkey>(); // key
let owner = buffer
.get(start..start + size_of::<Pubkey>())
.ok_or(InstructionError::InvalidArgument)?;
start += size_of::<Pubkey>(); // owner
start += size_of::<u64>(); // flags
let lamports = LittleEndian::read_u64(
buffer
.get(start..)
.ok_or(InstructionError::InvalidArgument)?,
);
if borrowed_account.get_lamports() != lamports {
borrowed_account.set_lamports(lamports)?;
}
start += size_of::<u64>(); // lamports
start += size_of::<u64>(); // payload_vm_address
let post_len = LittleEndian::read_u32(
buffer
.get(start..)
.ok_or(InstructionError::InvalidArgument)?,
) as usize;
start += size_of::<u32>(); // payload_length
start += size_of::<u32>(); // payload_capacity
match borrowed_account
.can_data_be_resized(post_len)
.and_then(|_| borrowed_account.can_data_be_changed())
{
Ok(()) => {
if post_len.saturating_sub(pre_len) > MAX_PERMITTED_DATA_INCREASE
|| post_len > MAX_PERMITTED_DATA_LENGTH as usize
{
return Err(InstructionError::InvalidRealloc);
}
borrowed_account.set_data_length(post_len)?;
}
Err(err) if borrowed_account.get_data().len() != post_len => return Err(err),
_ => {}
}
if borrowed_account.get_owner().to_bytes() != owner {
// Change the owner at the end so that we are allowed to change the lamports and data before
borrowed_account.set_owner(owner)?;
}
}
}
Ok(())
}

pub(crate) 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 +902,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 +1057,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
)
.unwrap();

Expand Down Expand Up @@ -942,6 +1117,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 +1150,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 +1189,7 @@ mod tests {
invoke_context.transaction_context,
instruction_context,
copy_account_data,
false, // is_abi_v2
serialized.as_slice(),
&account_lengths,
)
Expand Down
Loading
Loading