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

Add example of deserializing the SlotHash sysvar while remaining with… #3440

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 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
95 changes: 95 additions & 0 deletions examples/rust/sysvar/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,98 @@ pub fn process_instruction(

Ok(())
}


//Code for deserializing the SlotHash sysvar while remaining within the BPF compute limit

//entrypoint!(process_instruction2);
fn process_instruction2(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was to have all this code happen after line 43, in the normal process_instruction function. Written this way, the processor will never be triggered

let accounts_iter = &mut accounts.iter();
let sysvar_slot_history = next_account_info(accounts_iter)?;

/*
Decoding the SlotHashes sysvar using `from_account_info` is too expensive.
For example this statement will exceed the current BPF compute unit budget:

let slot_hashes = SlotHashes::from_account_info(&sysvar_slot_history).unwrap();

Instead manually decode the sysvar.
*/

if *sysvar_slot_history.key != sysvar::slot_hashes::id() {
msg!("Invalid SlotHashes sysvar");
return Err(ProgramError::InvalidArgument);
}

let data = sysvar_slot_history.try_borrow_data()?;

let num_slot_hashes = u64::from_le_bytes(data[0..8].try_into().unwrap());
let mut pos = 8;

for _i in 0..num_slot_hashes {
let slot = u64::from_le_bytes(data[pos..pos + 8].try_into().unwrap());
pos += 8;
let hash = &data[pos..pos + 32];
pos += 32;

if slot == 54943128 {
msg!("Found slot {}, hash {}", slot, Hash::new(hash));
}
}

Ok(())
}

#[cfg(test)]
mod test {
use {
super::*,
assert_matches::*,
solana_program::{
instruction::{AccountMeta, Instruction},
native_token::sol_to_lamports,
sysvar,
},
solana_program_test::*,
solana_sdk::{signature::Signer, transaction::Transaction},
};

#[tokio::test]
async fn test_transaction() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is to merge this test with the one in examples/rust/sysvar/tests/functional.rs since they exercise the same code underneath. You can change the transaction to also include the slot hashes sysvar and rest should work very nicely

let program_id = Pubkey::new_unique();

let mut program_test = ProgramTest::new(
"bpf_program_template",
program_id,
processor!(process_instruction2),
);

// Replace the SlotHashes sysvar will a fully populated version that was grabbed off Mainnet
// Beta by running:
// solana account SysvarS1otHashes111111111111111111111111111 -o slot_hashes.bin
program_test.add_account_with_file_data(
sysvar::slot_hashes::id(),
sol_to_lamports(1.),
Pubkey::default(),
"slot_hashes.bin",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this test is moved to examples/rust/sysvar/tests/functional.rs, then you can move the slot hashes file to examples/rust/sysvar/tests/fixtures/slot_hashes.bin

);

let (mut banks_client, payer, recent_blockhash) = program_test.start().await;

let mut transaction = Transaction::new_with_payer(
&[Instruction {
program_id,
accounts: vec![AccountMeta::new(sysvar::slot_hashes::id(), false)],
data: vec![1, 2, 3],
}],
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);

assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
}
}
Binary file added examples/rust/sysvar/src/slot_hashes.bin
Binary file not shown.