-
Notifications
You must be signed in to change notification settings - Fork 165
zero-copy slot hashes sysvar (with checked alternatives) #152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rustopian
merged 188 commits into
anza-xyz:main
from
rustopian:rustopian/slot-hashes-sysvar
Aug 9, 2025
Merged
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 3d1aeee
rename confusing term 'standard'
rustopian f7abda7
Merge pull request #1 from anza-xyz/main
rustopian 23d389e
sync for eisodos use
rustopian a3d4978
expose unchecked, test gate midpoint, more tests
rustopian bc835a3
fix exports
rustopian fc63e12
fmt, clippy
rustopian 43e6f6c
rm gating, for benching non-interpolated options
rustopian 42d1695
rm old test
rustopian 5bcd280
fmt
rustopian bb6c342
Cargo.toml
rustopian 605c3ed
Cargo.lock
rustopian d836130
clippy, deps
rustopian 97f7eae
rm unused test, import for std tests
rustopian 55c30c3
rm old helpers
rustopian 22de073
rm old midpoint fns
rustopian 992c692
replace double slice deref with single
rustopian 990c891
num_entries to 512 in tests, rm panics in tests, fmt
rustopian ba27a78
rm unused test import
rustopian 020844c
Cargo.toml
rustopian 7b449f0
Cargo.lock
rustopian 66a5b2d
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian d6e3ff3
Cargo.lock spaces
rustopian 2118d50
better fn names, rm data too small test for unchecked
rustopian 4bd29dc
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian 8e2b2e8
eliminate repetition, comments about passed in len and more
rustopian 54a90b4
quick b58 comparison function in test
rustopian fbf05b1
export const for benching
rustopian 54c93dd
try heuristic initial probe without subsequent heuristics
rustopian edffb2c
try heuristic initial probe without subsequent heuristics
rustopian 2b87a81
naive binary search
rustopian 4ffb3ea
fmt
rustopian b684c1b
cleanup
rustopian d912285
Rm outdated comment
rustopian d46caf7
wip radical simplification
rustopian cad0d80
merge
rustopian c6d2c25
deref improvement, clippy
rustopian cebad83
clippy
rustopian 90fe16e
trim comments
rustopian 2186e44
clippy on std
rustopian 55cdc31
rm old comments
rustopian 6da33b5
rm useless tests
rustopian 9ef87d7
some edge case tests
rustopian dce5e60
replace Data enum with generic
rustopian 8233618
unify impl
rustopian abdc303
rm now unneeded fns
rustopian 4d2f66c
rm possibly redundant conversion
rustopian eb4cce6
fmt
rustopian bfe9e51
clippy
rustopian caf7157
better error on invalid data
rustopian a434939
SlotHashEntry as safer u8;8 instead of u64
rustopian a2adf83
rm alignment check (svm invariant)
rustopian 80cec53
debug assert for tests/fuzz in new_unchecked()
rustopian 9c47e2f
test mock data
rustopian 1d8f22d
rm .gitmodules
rustopian e833baf
fetch, fetch_into, fetch_into_unchecked, plus some helper fns
rustopian 3f6ded6
Merge branch 'rustopian/slot-hashes-sysvar' of https://github.com/rus…
rustopian 5180689
correct old comment
rustopian 81d4efb
move standalone fns out of impl
rustopian b4ff56d
rm suspect inlines
rustopian 90edd03
inline hints
rustopian dfd105d
pub fn entries
rustopian b6c1094
adjust order, comments, test
rustopian 06b7111
offset validation in checked path
rustopian 0fd0f3b
group declarations
rustopian 822f280
fmt
rustopian af13901
move tests out
rustopian 4f94ef7
better align assert
rustopian 4cdf959
fmt
rustopian db58e78
nvm, bloats, rm Debug
rustopian 52da67b
rm submodule
rustopian 5b1f0d8
relocate std fetch(), test for it, rm some comments
rustopian d56b0c5
last entry test
rustopian 39462a3
rm unnecessary ref
rustopian 5e98f87
clean up unsafe
rustopian 6f4033f
rm old comment
rustopian cd4011b
rm dup check
rustopian 1d8ea80
inline compile-time assertion
rustopian c24ee2d
Merge pull request #3 from anza-xyz/main
rustopian 478a47d
cached pointer (pending benching)
rustopian ba4dd2a
rm unnecessary check
rustopian 049c7cc
cleanup
rustopian 32bfea7
reorg file structure
rustopian fc66be3
inline
rustopian 32934c5
AccountInfoWithBacking to make Miri happy in tests
rustopian a6b81f1
no pointer, re-derive from buffer every time (for benching)
rustopian 174fe52
Merge pull request #4 from rustopian/slot-hashes-sysvar-no-pointer
rustopian 20794fc
update fetch tests & rust version
rustopian b1f7542
misc improvements
rustopian d422291
more misc improvements
rustopian 4ac75cd
const pointer
rustopian 8d02e7f
revert; increases CUs in std
rustopian 7c773c5
move raw buffer helpers to own module
rustopian 3695f5e
miri
rustopian ced0e4c
clippy
rustopian c7c6438
make compiler happy until we can move platform-tools to >=1.82
rustopian d9ee730
fmt build.rs
rustopian 5fb0dad
Cargo.toml
rustopian 53c4261
appease clippy
rustopian f45d402
zero header for tests
rustopian 50b1c78
dup test fix
rustopian 616122d
replace tests with ci problems
rustopian 87aaf9e
rm assertion
rustopian 848af41
rust-toolchain
rustopian 37aed85
need more output to see why CI fails
rustopian 59a7cdb
debug prints to see why CI fails when local doesn't
rustopian 27946e7
debug prints to see why CI fails when local doesn't
rustopian c90d8b3
clippy
rustopian 6a5a67d
force output
rustopian 9b43b12
clippy
rustopian 6c478df
more output to debug CI discrepancy
rustopian e36e684
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian 63b1862
fmt
rustopian 4b4a2a9
update to new account info
rustopian 1b4322d
restore
rustopian bdca4bd
revert to test.mts without debug output
rustopian 4ae504c
rm debug prints
rustopian 0e22740
organize tests, rm a couple of unnecessary
rustopian 4a45832
helpers
rustopian bf7a241
miri
rustopian 0938580
alignment for test data
rustopian 50baf9e
rm dup attr
rustopian af5cf6e
remove len from type; 20_488 bytes is required
rustopian 7971e3b
clippy
rustopian 73c2c1d
dup bin search to prevent re-slicing
rustopian 4e35c48
revert, no CU effect
rustopian 57791f7
zero-cost custom errors, update comments
rustopian 21775b5
expect new errors in a few tests
rustopian bc48829
tiny test for coverage
rustopian 3589778
reuse test helpers
rustopian 6775834
rm dup attr
rustopian 22f2f07
rename misleading stub tests
rustopian 4dc29ac
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian d50117b
Update rust-toolchain.toml
rustopian 3ce2a07
rm old comment
rustopian 0b3f1cb
additional checks in debug mode
rustopian 2f6a4e5
use constant (again)
rustopian ff76d90
update to 1.84.1, rm conditional build
rustopian b7c15d2
update old format/lint toolchain instructions
rustopian 31b5586
comment on compile-time const assertion
rustopian d0875b4
relax to 1.84 for miri's sake
rustopian 7ddd4d7
back to standard errors
rustopian 3d5300c
roll back ff76d90 for now
rustopian 3ef7d37
rm wrap of as_entries_slice as entries
rustopian d832399
avoid a slice
rustopian 8ae18b8
don't require mainnet size in parse_and_validate_data
rustopian eb86bd2
fill_from_sysvar
rustopian 5cbf704
try 1.84.1 in CI again
rustopian 8aa9b83
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian 929ba51
Merge pull request #9 from rustopian/main
rustopian f66b356
rm build.rs
rustopian b343f15
CI demands 1.79
rustopian b69874d
1.79 compatible
rustopian 51e6000
move mod/use raw to beginning of file
rustopian 1f02774
rm unnecessary overflow check & test
rustopian 1ac41d2
make test name follow convention
rustopian 23791fe
align remaining test names to convention
rustopian 2aa6891
update comments for new parse_and_validate behavior
rustopian 9ad20fc
streamline allocate_and_fetch a bit
rustopian 73e3c4c
fuller doc comment
rustopian 7ea1ea5
mv imports, rm eprintln
rustopian f552de1
test ensures AccountLayout & Account are compat
rustopian 0cb5709
hunspell unhappy
rustopian 3ddaa2d
re-use AccountLayout
rustopian 06e6f3b
explicit alignment assertion
rustopian 655dd48
rm build dep rustc_version
rustopian 4ae8752
rm suppression of verbosity with more verbosity
rustopian f07ce8a
base58 lib instead of handroll
rustopian 672a541
align toolchain
rustopian 5eedd46
simple Hash type and log()
rustopian 1c18180
consolidate test
rustopian 46c5b03
Merge branch 'main' into rustopian/slot-hashes-sysvar
rustopian 16a254e
Cargo.lock
rustopian 71c0dca
Update sdk/pinocchio/Cargo.toml
rustopian b63b420
use five8_const
rustopian 1b21d13
Cargo.lock
rustopian d66f3d6
mark fn unsafe, fix five8 use
rustopian 2e52259
trim safety comment
rustopian 551b0f9
rm unnecessary max entries check
rustopian 9c56310
get_entry without building entries
rustopian b3fbc0e
Cargo.lock to merge from upstream
rustopian 99eb7da
Merge pull request #11 from anza-xyz/main
rustopian 2a17311
spelling
rustopian 1d9cab0
fix validate_buffer_size and tests
rustopian 06eede0
clarify
rustopian 8835c5b
hunspell
rustopian 63e667e
another caller responsibility for fetch_into_unchecked
rustopian adabaa9
slightly better name
rustopian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,4 +64,8 @@ RPC | |
| ed25519 | ||
| performant | ||
| syscall/S | ||
| bitmask | ||
| bitmask | ||
| pinocchio | ||
| mainnet | ||
| getters | ||
| PRNG | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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> { | ||
rustopian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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")] | ||
rustopian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) }) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.