Skip to content

Commit cee1e4c

Browse files
authored
transfer-lamports: Also read instruction data (#11)
#### Problem The transfer-lamports example is limited in usefulness since it doesn't read instruction data. This means it only tests a portion of the program entrypoint. #### Summary of changes Add instruction data! Programs interpet 8 bytes of instruction data as a little-endian u64. Also, update the numbers for the test.
1 parent 45d9595 commit cee1e4c

File tree

7 files changed

+45
-26
lines changed

7 files changed

+45
-26
lines changed

README.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,20 @@ Since this is just doing a syscall, all the languages behave the same. The only
157157
difference is that the Assembly version *doesn't* set the return code to 0, and
158158
lets the VM assume it worked.
159159

160-
* Transfer-Lamports: moves 5 lamports from a source account to a destination
160+
* Transfer-Lamports: moves lamports from a source account to a destination, with
161+
the amount given by a little-endian u64 in instruction data.
161162

162163
| Language | CU Usage |
163164
| --- | --- |
164-
| Rust | 464 |
165-
| Zig | 43 |
166-
| C | 103 |
167-
| Assembly | 22 |
168-
| Rust (pinocchio) | 23 |
165+
| Rust | 459 |
166+
| Zig | 44 |
167+
| C | 104 |
168+
| Assembly | 31 |
169+
| Rust (pinocchio) | 32 |
169170

170171
This one starts to get interesting since it requires parsing the instruction
171172
input. Since the assembly version knows exactly where to find everything, it can
172-
be hyper-optimized. The C version is also very performant.
173-
174-
Zig's version should perform the same as C, but there are some inefficiencies that
175-
are currently being fixed.
173+
be hyper-optimized.
176174

177175
* CPI: allocates a PDA given by the seed "You pass butter" and a bump seed in
178176
the instruction data. This requires a call to `create_program_address` to check

transfer-lamports/asm/main.s

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,38 @@
22
entrypoint:
33
ldxdw r2, [r1 + 0] # get number of accounts
44
jne r2, 2, error # error if not 2 accounts
5+
56
ldxb r2, [r1 + 8] # get first account
6-
jne r2, 0xff, error # shouldn't be a duplicate, but check
7+
# can check this, but isn't necessary
8+
# jne r2, 0xff, error
79
ldxdw r2, [r1 + 8 + 8 + 32 + 32] # get source lamports
810
ldxdw r3, [r1 + 8 + 8 + 32 + 32 + 8] # get account data size
911
mov64 r4, r1
1012
add64 r4, 8 + 8 + 32 + 32 + 8 + 8 + 10240 + 8 # calculate end of account data
1113
add64 r4, r3
1214
mov64 r5, r4 # check how much padding we need to add
13-
and64 r5, 7 # clear high bits
14-
jeq r5, 0, 1 # no low bits set, jump ahead
15+
and64 r5, -8 # clear low bits
16+
jeq r5, r4, 1 # no low bits set, jump ahead
1517
add64 r4, 8 # add 8 for truncation if needed
1618
and64 r4, -8 # clear low bits
19+
1720
ldxb r5, [r4 + 0] # get second account
1821
jne r5, 0xff, error # we don't allow duplicates
1922
ldxdw r5, [r4 + 8 + 32 + 32] # get destination lamports
20-
sub64 r2, 5 # subtract lamports
21-
add64 r5, 5 # add lamports
23+
ldxdw r6, [r4 + 8 + 32 + 32 + 8] # get account data size
24+
mov64 r7, r4
25+
add64 r7, 8 + 32 + 32 + 8 + 8 + 10240 + 8 # calculate end of account data
26+
add64 r7, r6
27+
mov64 r8, r7 # check how much padding we need to add
28+
and64 r8, -8 # clear low bits
29+
jeq r8, r7, 1 # no low bits set, jump ahead
30+
add64 r7, 8 # add 8 for truncation if low bits are set
31+
ldxdw r8, [r7 + 0] # get instruction data size
32+
jne r8, 0x08, error # need 8 bytes of instruction data
33+
ldxdw r8, [r7 + 8] # get instruction data as little-endian u64
34+
35+
sub64 r2, r8 # subtract lamports
36+
add64 r5, r8 # add lamports
2237
stxdw [r1 + 8 + 8 + 32 + 32], r2 # write the new values back
2338
stxdw [r4 + 8 + 32 + 32], r5
2439
exit

transfer-lamports/c/src/main.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ extern uint64_t entrypoint(const uint8_t *input) {
1111
return ERROR_INVALID_ARGUMENT;
1212
}
1313

14+
uint64_t transfer_amount = *(uint64_t *) params.data;
1415
SolAccountInfo source_account = params.ka[0];
1516
SolAccountInfo destination_account = params.ka[1];
16-
*source_account.lamports -= 5;
17-
*destination_account.lamports += 5;
17+
*source_account.lamports -= transfer_amount;
18+
*destination_account.lamports += transfer_amount;
1819

1920
return 0;
2021
}

transfer-lamports/pinocchio/src/entrypoint.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ fn process_instruction(mut context: InstructionContext) -> ProgramResult {
3838
// accounts are different, so we can safely ignore the case when the account is
3939
// duplicated.
4040
if let MaybeAccount::Account(destination_info) = context.next_account_unchecked() {
41+
let (instruction_data, _) = context.instruction_data_unchecked();
42+
let transfer_amount = u64::from_le_bytes(instruction_data.try_into().unwrap());
4143
// withdraw five lamports
42-
*source_info.borrow_mut_lamports_unchecked() -= 5;
44+
*source_info.borrow_mut_lamports_unchecked() -= transfer_amount;
4345
// deposit five lamports
44-
*destination_info.borrow_mut_lamports_unchecked() += 5;
46+
*destination_info.borrow_mut_lamports_unchecked() += transfer_amount;
4547
}
4648
}
4749

transfer-lamports/src/processor.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,21 @@ use solana_program::{
1111
pub fn process_instruction(
1212
_program_id: &Pubkey,
1313
accounts: &[AccountInfo],
14-
_instruction_data: &[u8],
14+
instruction_data: &[u8],
1515
) -> ProgramResult {
1616
// Create an iterator to safely reference accounts in the slice
1717
let account_info_iter = &mut accounts.iter();
18+
let transfer_amount = u64::from_le_bytes(instruction_data.try_into().unwrap());
1819

1920
// As part of the program specification the first account is the source
2021
// account and the second is the destination account
2122
let source_info = next_account_info(account_info_iter)?;
2223
let destination_info = next_account_info(account_info_iter)?;
2324

2425
// Withdraw five lamports from the source
25-
**source_info.try_borrow_mut_lamports()? -= 5;
26+
**source_info.try_borrow_mut_lamports()? -= transfer_amount;
2627
// Deposit five lamports into the destination
27-
**destination_info.try_borrow_mut_lamports()? += 5;
28+
**destination_info.try_borrow_mut_lamports()? += transfer_amount;
2829

2930
Ok(())
3031
}

transfer-lamports/tests/functional.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ async fn test_lamport_transfer() {
2020
None,
2121
);
2222

23-
let source_lamports = 5;
23+
let source_lamports = 555_555;
2424
let destination_lamports = 890_875;
2525
program_test.add_account(
2626
source_pubkey,
@@ -43,7 +43,7 @@ async fn test_lamport_transfer() {
4343
let mut transaction = Transaction::new_with_payer(
4444
&[Instruction::new_with_bincode(
4545
program_id,
46-
&(),
46+
&source_lamports.to_le_bytes(),
4747
vec![
4848
AccountMeta::new(source_pubkey, false),
4949
AccountMeta::new(destination_pubkey, false),

transfer-lamports/zig/main.zig

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
const std = @import("std");
12
const sol = @import("solana-program-sdk");
23

34
export fn entrypoint(input: [*]u8) u64 {
45
const context = sol.Context.load(input) catch return 1;
56
const source = context.accounts[0];
67
const destination = context.accounts[1];
7-
source.lamports().* -= 5;
8-
destination.lamports().* += 5;
8+
const transfer_amount = std.mem.bytesToValue(u64, context.data[0..@sizeOf(u64)]);
9+
source.lamports().* -= transfer_amount;
10+
destination.lamports().* += transfer_amount;
911
return 0;
1012
}

0 commit comments

Comments
 (0)