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

Commit 898d5c9

Browse files
committed
[wip]: Using positional accounts
1 parent 297c49e commit 898d5c9

File tree

1 file changed

+68
-27
lines changed

1 file changed

+68
-27
lines changed

program/src/processor/batch.rs

Lines changed: 68 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,94 @@
1+
use core::mem::MaybeUninit;
2+
13
use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
24

35
use crate::entrypoint::inner_process_instruction;
46

5-
/// The size of the batch instruction header.
6-
///
7-
/// The header of each instruction consists of two `u8` values:
8-
/// * number of the accounts
9-
/// * length of the instruction data
10-
const IX_HEADER_SIZE: usize = 2;
11-
12-
pub fn process_batch(mut accounts: &[AccountInfo], mut instruction_data: &[u8]) -> ProgramResult {
13-
loop {
14-
// Validates the instruction data and accounts offset.
7+
macro_rules! write_account {
8+
( $index_source:expr, $source:ident, $index_target:literal, $target:ident ) => {
9+
// TODO: need to validate that the indices are within bounds.
10+
unsafe {
11+
$target
12+
.get_unchecked_mut($index_target)
13+
.write($source.get_unchecked($index_source).clone())
14+
}
15+
};
16+
}
1517

16-
if instruction_data.len() < IX_HEADER_SIZE {
17-
// The instruction data must have at least two bytes.
18-
return Err(ProgramError::InvalidInstructionData);
18+
macro_rules! fill_accounts {
19+
( $indices:ident, $accounts:ident, $instruction_accounts:ident ) => {
20+
match $indices.len() {
21+
1 => {
22+
write_account!($indices[0] as usize, $accounts, 0, $instruction_accounts);
23+
}
24+
2 => {
25+
write_account!($indices[0] as usize, $accounts, 0, $instruction_accounts);
26+
write_account!($indices[1] as usize, $accounts, 1, $instruction_accounts);
27+
}
28+
3 => {
29+
write_account!($indices[0] as usize, $accounts, 0, $instruction_accounts);
30+
write_account!($indices[1] as usize, $accounts, 1, $instruction_accounts);
31+
write_account!($indices[2] as usize, $accounts, 2, $instruction_accounts);
32+
}
33+
4 => {
34+
write_account!($indices[0] as usize, $accounts, 0, $instruction_accounts);
35+
write_account!($indices[1] as usize, $accounts, 1, $instruction_accounts);
36+
write_account!($indices[2] as usize, $accounts, 2, $instruction_accounts);
37+
write_account!($indices[3] as usize, $accounts, 3, $instruction_accounts);
38+
}
39+
// TODO: Add more cases up to 15.
40+
_ => return Err(ProgramError::NotEnoughAccountKeys),
1941
}
42+
};
43+
}
44+
45+
pub fn process_batch(accounts: &[AccountInfo], mut instruction_data: &[u8]) -> ProgramResult {
46+
const UNINIT_ACCOUNT: MaybeUninit<AccountInfo> = MaybeUninit::<AccountInfo>::uninit();
47+
// Instructions take at most 15 accounts.
48+
let mut instruction_accounts: [MaybeUninit<AccountInfo>; 15] = [UNINIT_ACCOUNT; 15];
2049

21-
// SAFETY: The instruction data is guaranteed to have at least two bytes (header)
22-
// + one byte (discriminator).
50+
if instruction_data.is_empty() {
51+
return Err(ProgramError::InvalidInstructionData);
52+
}
53+
54+
loop {
2355
let expected_accounts = unsafe { *instruction_data.get_unchecked(0) as usize };
24-
let data_offset = IX_HEADER_SIZE + unsafe { *instruction_data.get_unchecked(1) as usize };
56+
// There must be at least:
57+
// - 1 byte for the number of accounts.
58+
// - `expected_accounts` bytes for instruction accounts indices.
59+
// - 1 byte for the length of the instruction data.
60+
let data_offset = expected_accounts + 2;
2561

26-
if instruction_data.len() < data_offset || data_offset == 0 {
62+
if instruction_data.len() < data_offset {
2763
return Err(ProgramError::InvalidInstructionData);
2864
}
2965

30-
if accounts.len() < expected_accounts {
31-
return Err(ProgramError::NotEnoughAccountKeys);
32-
}
66+
let indices = unsafe { instruction_data.get_unchecked(1..1 + expected_accounts) };
67+
fill_accounts!(indices, accounts, instruction_accounts);
3368

34-
// Process the instruction.
69+
let expected_data =
70+
data_offset + unsafe { *instruction_data.get_unchecked(data_offset - 1) as usize };
71+
72+
if instruction_data.len() < expected_data || expected_data == 0 {
73+
return Err(ProgramError::InvalidInstructionData);
74+
}
3575

3676
// SAFETY: The instruction data and accounts lengths are already validated so all
3777
// the slices are guaranteed to be valid.
3878
inner_process_instruction(
39-
unsafe { accounts.get_unchecked(..expected_accounts) },
40-
unsafe { instruction_data.get_unchecked(IX_HEADER_SIZE + 1..data_offset) },
41-
unsafe { *instruction_data.get_unchecked(IX_HEADER_SIZE) },
79+
unsafe {
80+
core::slice::from_raw_parts(instruction_accounts.as_ptr() as _, expected_accounts)
81+
},
82+
unsafe { instruction_data.get_unchecked(data_offset + 1..expected_data) },
83+
unsafe { *instruction_data.get_unchecked(data_offset) },
4284
)?;
4385

44-
if data_offset == instruction_data.len() {
86+
if expected_data == instruction_data.len() {
4587
// The batch is complete.
4688
break;
4789
}
4890

49-
accounts = &accounts[expected_accounts..];
50-
instruction_data = &instruction_data[data_offset..];
91+
instruction_data = &instruction_data[expected_data..];
5192
}
5293

5394
Ok(())

0 commit comments

Comments
 (0)