Skip to content

Commit 187cc80

Browse files
committed
Example of deserializing the SlotHash sysvar while remaining within the BPF compute limit
1 parent 56addc9 commit 187cc80

File tree

2 files changed

+66
-16
lines changed

2 files changed

+66
-16
lines changed

src/lib.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,54 @@
11
use solana_program::{
2-
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
2+
account_info::{next_account_info, AccountInfo},
3+
entrypoint,
4+
entrypoint::ProgramResult,
5+
hash::Hash,
6+
msg,
7+
program_error::ProgramError,
8+
pubkey::Pubkey,
9+
sysvar,
310
};
11+
use std::convert::TryInto;
412

513
entrypoint!(process_instruction);
614
fn process_instruction(
7-
program_id: &Pubkey,
15+
_program_id: &Pubkey,
816
accounts: &[AccountInfo],
9-
instruction_data: &[u8],
17+
_instruction_data: &[u8],
1018
) -> ProgramResult {
11-
msg!(
12-
"process_instruction: {}: {} accounts, data={:?}",
13-
program_id,
14-
accounts.len(),
15-
instruction_data
16-
);
19+
let accounts_iter = &mut accounts.iter();
20+
let sysvar_slot_history = next_account_info(accounts_iter)?;
21+
22+
/*
23+
Decoding the SlotHashes sysvar using `from_account_info` is too expensive.
24+
For example this statement will exceed the current BPF compute unit budget:
25+
26+
let slot_hashes = SlotHashes::from_account_info(&sysvar_slot_history).unwrap();
27+
28+
Instead manually decode the sysvar.
29+
*/
30+
31+
if *sysvar_slot_history.key != sysvar::slot_hashes::id() {
32+
msg!("Invalid SlotHashes sysvar");
33+
return Err(ProgramError::InvalidArgument);
34+
}
35+
36+
let data = sysvar_slot_history.try_borrow_data()?;
37+
38+
let num_slot_hashes = u64::from_le_bytes(data[0..8].try_into().unwrap());
39+
let mut pos = 8;
40+
41+
for _i in 0..num_slot_hashes {
42+
let slot = u64::from_le_bytes(data[pos..pos + 8].try_into().unwrap());
43+
pos += 8;
44+
let hash = &data[pos..pos + 32];
45+
pos += 32;
46+
47+
if slot == 54943128 {
48+
msg!("Found slot {}, hash {}", slot, Hash::new(hash));
49+
}
50+
}
51+
1752
Ok(())
1853
}
1954

@@ -22,7 +57,11 @@ mod test {
2257
use {
2358
super::*,
2459
assert_matches::*,
25-
solana_program::instruction::{AccountMeta, Instruction},
60+
solana_program::{
61+
instruction::{AccountMeta, Instruction},
62+
native_token::sol_to_lamports,
63+
sysvar,
64+
},
2665
solana_program_test::*,
2766
solana_sdk::{signature::Signer, transaction::Transaction},
2867
};
@@ -31,19 +70,30 @@ mod test {
3170
async fn test_transaction() {
3271
let program_id = Pubkey::new_unique();
3372

34-
let (mut banks_client, payer, recent_blockhash) = ProgramTest::new(
73+
let mut program_test = ProgramTest::new(
3574
"bpf_program_template",
3675
program_id,
3776
processor!(process_instruction),
38-
)
39-
.start()
40-
.await;
77+
);
78+
79+
// Replace the SlotHashes sysvar will a full populated version that was grabbed off Mainnet
80+
// Beta by running:
81+
// solana account SysvarS1otHashes111111111111111111111111111 -o slot_hashes.bin
82+
program_test.add_account_with_file_data(
83+
sysvar::slot_hashes::id(),
84+
sol_to_lamports(1.),
85+
Pubkey::default(),
86+
"slot_hashes.bin",
87+
);
88+
89+
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;
4190

4291
let mut transaction = Transaction::new_with_payer(
4392
&[Instruction {
4493
program_id,
45-
accounts: vec![AccountMeta::new(payer.pubkey(), false)],
46-
data: vec![1, 2, 3],
94+
accounts: vec![AccountMeta::new(sysvar::slot_hashes::id(), false)],
95+
//accounts: vec![AccountMeta::new(fake_slot_hashes, false)],
96+
data: vec![],
4797
}],
4898
Some(&payer.pubkey()),
4999
);

tests/fixtures/slot_hashes.bin

20 KB
Binary file not shown.

0 commit comments

Comments
 (0)