Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
188 commits
Select commit Hold shift + click to select a range
002832c
create slot_hashes.rs
rustopian May 1, 2025
3d1aeee
rename confusing term 'standard'
rustopian May 1, 2025
f7abda7
Merge pull request #1 from anza-xyz/main
rustopian May 1, 2025
23d389e
sync for eisodos use
rustopian May 4, 2025
a3d4978
expose unchecked, test gate midpoint, more tests
rustopian May 5, 2025
bc835a3
fix exports
rustopian May 5, 2025
fc63e12
fmt, clippy
rustopian May 5, 2025
43e6f6c
rm gating, for benching non-interpolated options
rustopian May 5, 2025
42d1695
rm old test
rustopian May 5, 2025
5bcd280
fmt
rustopian May 5, 2025
bb6c342
Cargo.toml
rustopian May 5, 2025
605c3ed
Cargo.lock
rustopian May 5, 2025
d836130
clippy, deps
rustopian May 6, 2025
97f7eae
rm unused test, import for std tests
rustopian May 6, 2025
55c30c3
rm old helpers
rustopian May 6, 2025
22de073
rm old midpoint fns
rustopian May 6, 2025
992c692
replace double slice deref with single
rustopian May 6, 2025
990c891
num_entries to 512 in tests, rm panics in tests, fmt
rustopian May 6, 2025
ba27a78
rm unused test import
rustopian May 6, 2025
020844c
Cargo.toml
rustopian May 6, 2025
7b449f0
Cargo.lock
rustopian May 6, 2025
66a5b2d
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian May 6, 2025
d6e3ff3
Cargo.lock spaces
rustopian May 6, 2025
2118d50
better fn names, rm data too small test for unchecked
rustopian May 6, 2025
4bd29dc
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian May 6, 2025
8e2b2e8
eliminate repetition, comments about passed in len and more
rustopian May 7, 2025
54a90b4
quick b58 comparison function in test
rustopian May 7, 2025
fbf05b1
export const for benching
rustopian May 7, 2025
54c93dd
try heuristic initial probe without subsequent heuristics
rustopian May 7, 2025
edffb2c
try heuristic initial probe without subsequent heuristics
rustopian May 7, 2025
2b87a81
naive binary search
rustopian May 7, 2025
4ffb3ea
fmt
rustopian May 7, 2025
b684c1b
cleanup
rustopian May 8, 2025
d912285
Rm outdated comment
rustopian May 11, 2025
d46caf7
wip radical simplification
rustopian May 15, 2025
cad0d80
merge
rustopian May 15, 2025
c6d2c25
deref improvement, clippy
rustopian May 15, 2025
cebad83
clippy
rustopian May 15, 2025
90fe16e
trim comments
rustopian May 15, 2025
2186e44
clippy on std
rustopian May 15, 2025
55cdc31
rm old comments
rustopian May 15, 2025
6da33b5
rm useless tests
rustopian May 15, 2025
9ef87d7
some edge case tests
rustopian May 15, 2025
dce5e60
replace Data enum with generic
rustopian May 27, 2025
8233618
unify impl
rustopian May 27, 2025
abdc303
rm now unneeded fns
rustopian May 27, 2025
4d2f66c
rm possibly redundant conversion
rustopian May 27, 2025
eb4cce6
fmt
rustopian May 27, 2025
bfe9e51
clippy
rustopian May 27, 2025
caf7157
better error on invalid data
rustopian May 27, 2025
a434939
SlotHashEntry as safer u8;8 instead of u64
rustopian May 27, 2025
a2adf83
rm alignment check (svm invariant)
rustopian May 27, 2025
80cec53
debug assert for tests/fuzz in new_unchecked()
rustopian May 28, 2025
9c47e2f
test mock data
rustopian May 28, 2025
1d8f22d
rm .gitmodules
rustopian May 28, 2025
e833baf
fetch, fetch_into, fetch_into_unchecked, plus some helper fns
rustopian May 28, 2025
3f6ded6
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian May 28, 2025
5180689
correct old comment
rustopian May 28, 2025
81d4efb
move standalone fns out of impl
rustopian May 28, 2025
b4ff56d
rm suspect inlines
rustopian May 28, 2025
90edd03
inline hints
rustopian May 29, 2025
dfd105d
pub fn entries
rustopian May 29, 2025
b6c1094
adjust order, comments, test
rustopian May 29, 2025
06b7111
offset validation in checked path
rustopian May 29, 2025
0fd0f3b
group declarations
rustopian May 29, 2025
822f280
fmt
rustopian May 29, 2025
af13901
move tests out
rustopian May 29, 2025
4f94ef7
better align assert
rustopian May 29, 2025
4cdf959
fmt
rustopian May 29, 2025
db58e78
nvm, bloats, rm Debug
rustopian May 29, 2025
52da67b
rm submodule
rustopian May 29, 2025
5b1f0d8
relocate std fetch(), test for it, rm some comments
rustopian May 29, 2025
d56b0c5
last entry test
rustopian May 29, 2025
39462a3
rm unnecessary ref
rustopian May 29, 2025
5e98f87
clean up unsafe
rustopian May 29, 2025
6f4033f
rm old comment
rustopian May 30, 2025
cd4011b
rm dup check
rustopian May 30, 2025
1d8ea80
inline compile-time assertion
rustopian May 31, 2025
c24ee2d
Merge pull request #3 from anza-xyz/main
rustopian Jun 12, 2025
478a47d
cached pointer (pending benching)
rustopian Jun 17, 2025
ba4dd2a
rm unnecessary check
rustopian Jun 17, 2025
049c7cc
cleanup
rustopian Jun 18, 2025
32bfea7
reorg file structure
rustopian Jun 18, 2025
fc66be3
inline
rustopian Jun 18, 2025
32934c5
AccountInfoWithBacking to make Miri happy in tests
rustopian Jun 18, 2025
a6b81f1
no pointer, re-derive from buffer every time (for benching)
rustopian Jun 20, 2025
174fe52
Merge pull request #4 from rustopian/slot-hashes-sysvar-no-pointer
rustopian Jun 20, 2025
20794fc
update fetch tests & rust version
rustopian Jun 20, 2025
b1f7542
misc improvements
rustopian Jun 21, 2025
d422291
more misc improvements
rustopian Jun 21, 2025
4ac75cd
const pointer
rustopian Jun 21, 2025
8d02e7f
revert; increases CUs in std
rustopian Jun 21, 2025
7c773c5
move raw buffer helpers to own module
rustopian Jun 21, 2025
3695f5e
miri
rustopian Jun 21, 2025
ced0e4c
clippy
rustopian Jun 21, 2025
c7c6438
make compiler happy until we can move platform-tools to >=1.82
rustopian Jun 22, 2025
d9ee730
fmt build.rs
rustopian Jun 22, 2025
5fb0dad
Cargo.toml
rustopian Jun 22, 2025
53c4261
appease clippy
rustopian Jun 22, 2025
f45d402
zero header for tests
rustopian Jun 22, 2025
50b1c78
dup test fix
rustopian Jun 22, 2025
616122d
replace tests with ci problems
rustopian Jun 22, 2025
87aaf9e
rm assertion
rustopian Jun 22, 2025
848af41
rust-toolchain
rustopian Jun 22, 2025
37aed85
need more output to see why CI fails
rustopian Jun 22, 2025
59a7cdb
debug prints to see why CI fails when local doesn't
rustopian Jun 22, 2025
27946e7
debug prints to see why CI fails when local doesn't
rustopian Jun 22, 2025
c90d8b3
clippy
rustopian Jun 22, 2025
6a5a67d
force output
rustopian Jun 22, 2025
9b43b12
clippy
rustopian Jun 22, 2025
6c478df
more output to debug CI discrepancy
rustopian Jun 23, 2025
e36e684
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian Jun 23, 2025
63b1862
fmt
rustopian Jun 23, 2025
4b4a2a9
update to new account info
rustopian Jun 23, 2025
1b4322d
restore
rustopian Jun 23, 2025
bdca4bd
revert to test.mts without debug output
rustopian Jun 23, 2025
4ae504c
rm debug prints
rustopian Jun 23, 2025
0e22740
organize tests, rm a couple of unnecessary
rustopian Jun 23, 2025
4a45832
helpers
rustopian Jun 23, 2025
bf7a241
miri
rustopian Jun 23, 2025
0938580
alignment for test data
rustopian Jun 23, 2025
50baf9e
rm dup attr
rustopian Jun 23, 2025
af5cf6e
remove len from type; 20_488 bytes is required
rustopian Jun 24, 2025
7971e3b
clippy
rustopian Jun 24, 2025
73c2c1d
dup bin search to prevent re-slicing
rustopian Jun 24, 2025
4e35c48
revert, no CU effect
rustopian Jun 24, 2025
57791f7
zero-cost custom errors, update comments
rustopian Jun 24, 2025
21775b5
expect new errors in a few tests
rustopian Jun 24, 2025
bc48829
tiny test for coverage
rustopian Jun 24, 2025
3589778
reuse test helpers
rustopian Jun 24, 2025
6775834
rm dup attr
rustopian Jun 24, 2025
22f2f07
rename misleading stub tests
rustopian Jun 24, 2025
4dc29ac
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian Jun 24, 2025
d50117b
Update rust-toolchain.toml
rustopian Jun 24, 2025
3ce2a07
rm old comment
rustopian Jun 24, 2025
0b3f1cb
additional checks in debug mode
rustopian Jun 25, 2025
2f6a4e5
use constant (again)
rustopian Jun 28, 2025
ff76d90
update to 1.84.1, rm conditional build
rustopian Jun 28, 2025
b7c15d2
update old format/lint toolchain instructions
rustopian Jun 28, 2025
31b5586
comment on compile-time const assertion
rustopian Jun 28, 2025
d0875b4
relax to 1.84 for miri's sake
rustopian Jun 28, 2025
7ddd4d7
back to standard errors
rustopian Jun 28, 2025
3d5300c
roll back ff76d90 for now
rustopian Jun 28, 2025
3ef7d37
rm wrap of as_entries_slice as entries
rustopian Jul 9, 2025
d832399
avoid a slice
rustopian Jul 9, 2025
8ae18b8
don't require mainnet size in parse_and_validate_data
rustopian Jul 9, 2025
eb86bd2
fill_from_sysvar
rustopian Jul 9, 2025
5cbf704
try 1.84.1 in CI again
rustopian Jul 9, 2025
8aa9b83
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian Jul 9, 2025
929ba51
Merge pull request #9 from rustopian/main
rustopian Jul 12, 2025
f66b356
rm build.rs
rustopian Jul 15, 2025
b343f15
CI demands 1.79
rustopian Jul 15, 2025
b69874d
1.79 compatible
rustopian Jul 15, 2025
51e6000
move mod/use raw to beginning of file
rustopian Jul 15, 2025
1f02774
rm unnecessary overflow check & test
rustopian Jul 15, 2025
1ac41d2
make test name follow convention
rustopian Jul 15, 2025
23791fe
align remaining test names to convention
rustopian Jul 15, 2025
2aa6891
update comments for new parse_and_validate behavior
rustopian Jul 16, 2025
9ad20fc
streamline allocate_and_fetch a bit
rustopian Jul 16, 2025
73e3c4c
fuller doc comment
rustopian Jul 16, 2025
7ea1ea5
mv imports, rm eprintln
rustopian Jul 17, 2025
f552de1
test ensures AccountLayout & Account are compat
rustopian Jul 17, 2025
0cb5709
hunspell unhappy
rustopian Jul 17, 2025
3ddaa2d
re-use AccountLayout
rustopian Jul 17, 2025
06e6f3b
explicit alignment assertion
rustopian Jul 17, 2025
655dd48
rm build dep rustc_version
rustopian Jul 17, 2025
4ae8752
rm suppression of verbosity with more verbosity
rustopian Jul 17, 2025
f07ce8a
base58 lib instead of handroll
rustopian Jul 17, 2025
672a541
align toolchain
rustopian Jul 17, 2025
5eedd46
simple Hash type and log()
rustopian Jul 17, 2025
1c18180
consolidate test
rustopian Jul 17, 2025
46c5b03
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian Jul 21, 2025
16a254e
Cargo.lock
rustopian Jul 21, 2025
71c0dca
Update sdk/pinocchio/Cargo.toml
rustopian Jul 24, 2025
b63b420
use five8_const
rustopian Jul 24, 2025
1b21d13
Cargo.lock
rustopian Jul 24, 2025
d66f3d6
mark fn unsafe, fix five8 use
rustopian Jul 24, 2025
2e52259
trim safety comment
rustopian Jul 25, 2025
551b0f9
rm unnecessary max entries check
rustopian Jul 25, 2025
9c56310
get_entry without building entries
rustopian Jul 25, 2025
b3fbc0e
Cargo.lock to merge from upstream
rustopian Jul 25, 2025
99eb7da
Merge pull request #11 from anza-xyz/main
rustopian Jul 25, 2025
2a17311
spelling
rustopian Jul 25, 2025
1d9cab0
fix validate_buffer_size and tests
rustopian Jul 26, 2025
06eede0
clarify
rustopian Jul 26, 2025
8835c5b
hunspell
rustopian Jul 26, 2025
63e667e
another caller responsibility for fetch_into_unchecked
rustopian Jul 26, 2025
adabaa9
slightly better name
rustopian Jul 28, 2025
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion scripts/setup/solana.dic
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ RPC
ed25519
performant
syscall/S
bitmask
bitmask
pinocchio
mainnet
getters
PRNG
3 changes: 3 additions & 0 deletions sdk/pinocchio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ unexpected_cfgs = { level = "warn", check-cfg = [

[features]
std = []

[dev-dependencies]
five8_const = { workspace = true }
1 change: 1 addition & 0 deletions sdk/pinocchio/src/sysvars/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod clock;
pub mod fees;
pub mod instructions;
pub mod rent;
pub mod slot_hashes;

/// Return value indicating that the `offset + length` is greater than the length of
/// the sysvar data.
Expand Down
333 changes: 333 additions & 0 deletions sdk/pinocchio/src/sysvars/slot_hashes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
//! Efficient, zero-copy access to `SlotHashes` sysvar data.

pub mod raw;
#[doc(inline)]
pub use raw::{fetch_into, fetch_into_unchecked, validate_fetch_offset};

#[cfg(test)]
mod test;
#[cfg(test)]
mod test_edge;
#[cfg(test)]
mod test_raw;
#[cfg(test)]
mod test_utils;

use crate::{
account_info::{AccountInfo, Ref},
program_error::ProgramError,
pubkey::Pubkey,
sysvars::clock::Slot,
};
use core::{mem, ops::Deref, slice::from_raw_parts};
#[cfg(feature = "std")]
use std::boxed::Box;

/// `SysvarS1otHashes111111111111111111111111111`
pub const SLOTHASHES_ID: Pubkey = [
6, 167, 213, 23, 25, 47, 10, 175, 198, 242, 101, 227, 251, 119, 204, 122, 218, 130, 197, 41,
208, 190, 59, 19, 110, 45, 0, 85, 32, 0, 0, 0,
];
/// Number of bytes in a hash.
pub const HASH_BYTES: usize = 32;
/// Sysvar data is:
/// `len` (8 bytes): little-endian entry count (`≤ 512`)
/// `entries`(`len × 40 bytes`): consecutive `(u64 slot, [u8;32] hash)` pairs
/// Size of the entry count field at the beginning of sysvar data.
pub const NUM_ENTRIES_SIZE: usize = mem::size_of::<u64>();
/// Size of a slot number in bytes.
pub const SLOT_SIZE: usize = mem::size_of::<Slot>();
/// Size of a single slot hash entry.
pub const ENTRY_SIZE: usize = SLOT_SIZE + HASH_BYTES;
/// Maximum number of slot hash entries that can be stored in the sysvar.
pub const MAX_ENTRIES: usize = 512;
/// Max size of the sysvar data in bytes. 20488. Golden on mainnet (never smaller)
pub const MAX_SIZE: usize = NUM_ENTRIES_SIZE + MAX_ENTRIES * ENTRY_SIZE;
/// A single hash.
pub type Hash = [u8; HASH_BYTES];

/// A single entry in the `SlotHashes` sysvar.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(C)]
pub struct SlotHashEntry {
/// The slot number stored as little-endian bytes.
slot_le: [u8; 8],
/// The hash corresponding to the slot.
pub hash: Hash,
}

// Fail compilation if `SlotHashEntry` is not byte-aligned.
const _: [(); 1] = [(); mem::align_of::<SlotHashEntry>()];

/// `SlotHashes` provides read-only, zero-copy access to `SlotHashes` sysvar bytes.
pub struct SlotHashes<T: Deref<Target = [u8]>> {
data: T,
}

/// Log a `Hash` from a program.
pub fn log(hash: &Hash) {
crate::pubkey::log(hash);
}

/// Reads the entry count from the first 8 bytes of data.
/// Returns None if the data is too short.
#[inline(always)]
pub(crate) fn read_entry_count_from_bytes(data: &[u8]) -> Option<usize> {
if data.len() < NUM_ENTRIES_SIZE {
return None;
}
Some(unsafe {
// SAFETY: `data` is guaranteed to be at least `NUM_ENTRIES_SIZE` bytes long by the
// preceding length check, so it is sound to read the first 8 bytes and interpret
// them as a little-endian `u64`.
u64::from_le_bytes(*(data.as_ptr() as *const [u8; NUM_ENTRIES_SIZE]))
} as usize)
}

/// Reads the entry count from the first 8 bytes of data.
///
/// # Safety
/// Caller must ensure data has at least `NUM_ENTRIES_SIZE` bytes.
#[inline(always)]
pub(crate) unsafe fn read_entry_count_from_bytes_unchecked(data: &[u8]) -> usize {
u64::from_le_bytes(*(data.as_ptr() as *const [u8; NUM_ENTRIES_SIZE])) as usize
}

/// Validates `SlotHashes` data format.
///
/// The function checks:
/// 1. The buffer is large enough to contain the entry count.
/// 2. The buffer length is sufficient to hold the declared number of entries.
///
/// It returns `Ok(())` if the data is well-formed, otherwise an appropriate
/// `ProgramError` describing the issue.
#[inline]
fn parse_and_validate_data(data: &[u8]) -> Result<(), ProgramError> {
if data.len() < NUM_ENTRIES_SIZE {
return Err(ProgramError::AccountDataTooSmall);
}

// SAFETY: We've confirmed that data has enough bytes to read the entry count.
let num_entries = unsafe { read_entry_count_from_bytes_unchecked(data) };

let min_size = NUM_ENTRIES_SIZE + num_entries * ENTRY_SIZE;
if data.len() < min_size {
return Err(ProgramError::AccountDataTooSmall);
}

Ok(())
}

impl SlotHashEntry {
/// Returns the slot number as a `u64`.
#[inline(always)]
pub fn slot(&self) -> Slot {
u64::from_le_bytes(self.slot_le)
}
}

impl<T: Deref<Target = [u8]>> SlotHashes<T> {
/// Creates a `SlotHashes` instance with validation of the entry count and buffer size.
///
/// This constructor validates that the buffer has at least enough bytes to contain
/// the declared number of entries. The buffer can be any size above the minimum required,
/// making it suitable for both full `MAX_SIZE` buffers and smaller test data.
/// Does not validate that entries are sorted in descending order.
#[inline(always)]
pub fn new(data: T) -> Result<Self, ProgramError> {
parse_and_validate_data(&data)?;
// SAFETY: `parse_and_validate_data` verifies that the data slice has at least
// `NUM_ENTRIES_SIZE` bytes for the entry count and enough additional bytes to
// contain the declared number of entries, thus upholding all invariants required
// by `SlotHashes::new_unchecked`.
Ok(unsafe { Self::new_unchecked(data) })
}

/// Creates a `SlotHashes` instance without validation.
///
/// This is an unsafe constructor that bypasses all validation checks for performance.
/// In debug builds, it still runs `parse_and_validate_data` as a sanity check.
///
/// # Safety
///
/// This function is unsafe because it does not validate the data size or format.
/// The caller must ensure:
/// 1. The underlying byte slice in `data` represents valid `SlotHashes` data
/// (length prefix plus entries, where entries are sorted in descending order by slot).
/// 2. The data slice has at least `NUM_ENTRIES_SIZE + (declared_entries * ENTRY_SIZE)` bytes.
/// 3. The first 8 bytes contain a valid entry count in little-endian format.
///
#[inline(always)]
pub unsafe fn new_unchecked(data: T) -> Self {
if cfg!(debug_assertions) {
parse_and_validate_data(&data)
.expect("`data` matches all the same requirements as for `new()`");
}

SlotHashes { data }
}

/// Returns the number of `SlotHashEntry` items accessible.
#[inline(always)]
pub fn len(&self) -> usize {
// SAFETY: `SlotHashes::new` and `new_unchecked` guarantee that `self.data` has at
// least `NUM_ENTRIES_SIZE` bytes, so reading the entry count without additional
// checks is safe.
unsafe { read_entry_count_from_bytes_unchecked(&self.data) }
}

/// Returns if the sysvar is empty.
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns a `&[SlotHashEntry]` view into the underlying data.
///
/// Call once and reuse the slice if you need many look-ups.
///
/// The constructor (in the safe path that called `parse_and_validate_data`)
/// or caller (if unsafe `new_unchecked` path) is responsible for ensuring
/// the slice is big enough and properly aligned.
#[inline(always)]
pub fn entries(&self) -> &[SlotHashEntry] {
unsafe {
// SAFETY: The slice begins `NUM_ENTRIES_SIZE` bytes into `self.data`, which
// is guaranteed by parse_and_validate_data() to have at least `len * ENTRY_SIZE`
// additional bytes. The pointer is properly aligned for `SlotHashEntry` (which
// a compile-time assertion ensures is alignment 1).
from_raw_parts(
self.data.as_ptr().add(NUM_ENTRIES_SIZE) as *const SlotHashEntry,
self.len(),
)
}
}

/// Gets a reference to the entry at `index` or `None` if out of bounds.
#[inline(always)]
pub fn get_entry(&self, index: usize) -> Option<&SlotHashEntry> {
if index >= self.len() {
return None;
}
Some(unsafe { self.get_entry_unchecked(index) })
}

/// Finds the hash for a specific slot using binary search.
///
/// Returns the hash if the slot is found, or `None` if not found.
/// Assumes entries are sorted by slot in descending order.
/// If calling repeatedly, prefer getting `entries()` in caller
/// to avoid repeated slice construction.
#[inline(always)]
pub fn get_hash(&self, target_slot: Slot) -> Option<&Hash> {
self.position(target_slot)
.map(|index| unsafe { &self.get_entry_unchecked(index).hash })
}

/// Finds the position (index) of a specific slot using binary search.
///
/// Returns the index if the slot is found, or `None` if not found.
/// Assumes entries are sorted by slot in descending order.
/// If calling repeatedly, prefer getting `entries()` in caller
/// to avoid repeated slice construction.
#[inline(always)]
pub fn position(&self, target_slot: Slot) -> Option<usize> {
self.entries()
.binary_search_by(|probe_entry| probe_entry.slot().cmp(&target_slot).reverse())
.ok()
}

/// Returns a reference to the entry at `index` **without** bounds checking.
///
/// # Safety
/// Caller must guarantee that `index < self.len()`.
#[inline(always)]
pub unsafe fn get_entry_unchecked(&self, index: usize) -> &SlotHashEntry {
debug_assert!(index < self.len());
// SAFETY: Caller guarantees `index < self.len()`. The data pointer is valid
// and aligned for `SlotHashEntry`. The offset calculation points to a
// valid entry within the allocated data.
let entries_ptr = self.data.as_ptr().add(NUM_ENTRIES_SIZE) as *const SlotHashEntry;
&*entries_ptr.add(index)
}
}

impl<'a, T: Deref<Target = [u8]>> IntoIterator for &'a SlotHashes<T> {
type Item = &'a SlotHashEntry;
type IntoIter = core::slice::Iter<'a, SlotHashEntry>;

fn into_iter(self) -> Self::IntoIter {
self.entries().iter()
}
}

impl<'a> SlotHashes<Ref<'a, [u8]>> {
/// Creates a `SlotHashes` instance by safely borrowing data from an `AccountInfo`.
///
/// This function verifies that:
/// - The account key matches the `SLOTHASHES_ID`
/// - The account data can be successfully borrowed
///
/// Returns a `SlotHashes` instance that borrows the account's data for zero-copy access.
/// The returned instance is valid for the lifetime of the borrow.
///
/// # Errors
/// - `ProgramError::InvalidArgument` if the account key doesn't match the `SlotHashes` sysvar ID
/// - `ProgramError::AccountBorrowFailed` if the account data is already mutably borrowed
#[inline(always)]
pub fn from_account_info(account_info: &'a AccountInfo) -> Result<Self, ProgramError> {
if account_info.key() != &SLOTHASHES_ID {
return Err(ProgramError::InvalidArgument);
}

let data_ref = account_info.try_borrow_data()?;

// SAFETY: The account was validated to be the `SlotHashes` sysvar.
Ok(unsafe { SlotHashes::new_unchecked(data_ref) })
}
}

#[cfg(feature = "std")]
impl SlotHashes<Box<[u8]>> {
/// Fills the provided buffer with the full `SlotHashes` sysvar data.
///
/// # Safety
/// The caller must ensure the buffer pointer is valid for `MAX_SIZE` bytes.
/// The syscall will write exactly `MAX_SIZE` bytes to the buffer.
#[inline(always)]
unsafe fn fill_from_sysvar(buffer_ptr: *mut u8) -> Result<(), ProgramError> {
crate::sysvars::get_sysvar_unchecked(buffer_ptr, &SLOTHASHES_ID, 0, MAX_SIZE)?;

// For tests on builds that don't actually fill the buffer.
#[cfg(not(target_os = "solana"))]
core::ptr::write_bytes(buffer_ptr, 0, NUM_ENTRIES_SIZE);

Ok(())
}

/// Allocates an optimal buffer for the sysvar data based on available features.
#[inline(always)]
fn allocate_and_fetch() -> Result<Box<[u8]>, ProgramError> {
let mut buf = std::vec::Vec::with_capacity(MAX_SIZE);
unsafe {
// SAFETY: `buf` was allocated with capacity `MAX_SIZE` so its
// pointer is valid for exactly that many bytes. `fill_from_sysvar`
// writes `MAX_SIZE` bytes, and we immediately set the length to
// `MAX_SIZE`, marking the entire buffer as initialized before it is
// turned into a boxed slice.
Self::fill_from_sysvar(buf.as_mut_ptr())?;
buf.set_len(MAX_SIZE);
}
Ok(buf.into_boxed_slice())
}

/// Fetches the `SlotHashes` sysvar data directly via syscall. This copies
/// the full sysvar data (`MAX_SIZE` bytes).
#[inline(always)]
pub fn fetch() -> Result<Self, ProgramError> {
let data_init = Self::allocate_and_fetch()?;

// SAFETY: The data was initialized by the syscall.
Ok(unsafe { SlotHashes::new_unchecked(data_init) })
}
}
Loading