SDK: Add sysvar to expose recent block hashes to programs (#6663)
* SDK: Add sysvar to expose recent block hashes to programs * Blockhashes is one word * Missed one * Avoid allocs on update * unwrap_or_else * Use iterators * Add microbench * Revert "unwrap_or_else" This reverts commita8f8c3bfbe
. * Revert "Avoid allocs on update" This reverts commit486f01790c
.
This commit is contained in:
@ -9,6 +9,7 @@ use solana_runtime::loader_utils::create_invoke_instruction;
|
||||
use solana_sdk::account::KeyedAccount;
|
||||
use solana_sdk::client::AsyncClient;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::clock::MAX_RECENT_BLOCKHASHES;
|
||||
use solana_sdk::genesis_block::create_genesis_block;
|
||||
use solana_sdk::instruction::InstructionError;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
@ -173,3 +174,26 @@ fn bench_bank_async_process_builtin_transactions(bencher: &mut Bencher) {
|
||||
fn bench_bank_async_process_native_loader_transactions(bencher: &mut Bencher) {
|
||||
do_bench_transactions(bencher, &async_bencher, &create_native_loader_transactions);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[ignore]
|
||||
fn bench_bank_update_recent_blockhashes(bencher: &mut Bencher) {
|
||||
let (genesis_block, _mint_keypair) = create_genesis_block(100);
|
||||
let mut bank = Arc::new(Bank::new(&genesis_block));
|
||||
goto_end_of_slot(Arc::get_mut(&mut bank).unwrap());
|
||||
let genesis_blockhash = bank.last_blockhash();
|
||||
// Prime blockhash_queue
|
||||
for i in 0..(MAX_RECENT_BLOCKHASHES + 1) {
|
||||
bank = Arc::new(Bank::new_from_parent(
|
||||
&bank,
|
||||
&Pubkey::default(),
|
||||
(i + 1) as u64,
|
||||
));
|
||||
goto_end_of_slot(Arc::get_mut(&mut bank).unwrap());
|
||||
}
|
||||
// Verify blockhash_queue is full (genesis blockhash has been kicked out)
|
||||
assert!(!bank.check_hash_age(&genesis_blockhash, MAX_RECENT_BLOCKHASHES));
|
||||
bencher.iter(|| {
|
||||
bank.update_recent_blockhashes();
|
||||
});
|
||||
}
|
||||
|
@ -291,6 +291,7 @@ impl Bank {
|
||||
bank.update_clock();
|
||||
bank.update_rent();
|
||||
bank.update_epoch_schedule();
|
||||
bank.update_recent_blockhashes();
|
||||
bank
|
||||
}
|
||||
|
||||
@ -385,6 +386,7 @@ impl Bank {
|
||||
new.update_stake_history(Some(parent.epoch()));
|
||||
new.update_clock();
|
||||
new.update_fees();
|
||||
new.update_recent_blockhashes();
|
||||
new
|
||||
}
|
||||
|
||||
@ -539,6 +541,15 @@ impl Bank {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update_recent_blockhashes(&self) {
|
||||
let blockhash_queue = self.blockhash_queue.read().unwrap();
|
||||
let recent_blockhash_iter = blockhash_queue.get_recent_blockhashes();
|
||||
self.store_account(
|
||||
&sysvar::recent_blockhashes::id(),
|
||||
&sysvar::recent_blockhashes::create_account_with_data(1, recent_blockhash_iter),
|
||||
);
|
||||
}
|
||||
|
||||
// If the point values are not `normal`, bring them back into range and
|
||||
// set them to the last value or 0.
|
||||
fn check_point_values(
|
||||
@ -1599,6 +1610,18 @@ impl Drop for Bank {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn goto_end_of_slot(bank: &mut Bank) {
|
||||
let mut tick_hash = bank.last_blockhash();
|
||||
loop {
|
||||
tick_hash = hashv(&[&tick_hash.as_ref(), &[42]]);
|
||||
bank.register_tick(&tick_hash);
|
||||
if tick_hash == bank.last_blockhash() {
|
||||
bank.freeze();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -2023,18 +2046,6 @@ mod tests {
|
||||
assert_eq!(bank.get_balance(&key.pubkey()), 1);
|
||||
}
|
||||
|
||||
fn goto_end_of_slot(bank: &mut Bank) {
|
||||
let mut tick_hash = bank.last_blockhash();
|
||||
loop {
|
||||
tick_hash = hashv(&[&tick_hash.as_ref(), &[42]]);
|
||||
bank.register_tick(&tick_hash);
|
||||
if tick_hash == bank.last_blockhash() {
|
||||
bank.freeze();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_tx_fee() {
|
||||
let arbitrary_transfer_amount = 42;
|
||||
@ -3322,4 +3333,22 @@ mod tests {
|
||||
// Non-native loader accounts can not be used for instruction processing
|
||||
bank.add_instruction_processor(mint_keypair.pubkey(), mock_ix_processor);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recent_blockhashes_sysvar() {
|
||||
let (genesis_block, _mint_keypair) = create_genesis_block(500);
|
||||
let mut bank = Arc::new(Bank::new(&genesis_block));
|
||||
for i in 1..5 {
|
||||
let bhq_account = bank.get_account(&sysvar::recent_blockhashes::id()).unwrap();
|
||||
let recent_blockhashes =
|
||||
sysvar::recent_blockhashes::RecentBlockhashes::from_account(&bhq_account).unwrap();
|
||||
// Check length
|
||||
assert_eq!(recent_blockhashes.len(), i);
|
||||
let most_recent_hash = recent_blockhashes.iter().nth(0).unwrap();
|
||||
// Check order
|
||||
assert!(bank.check_hash_age(most_recent_hash, 0));
|
||||
goto_end_of_slot(Arc::get_mut(&mut bank).unwrap());
|
||||
bank = Arc::new(new_from_parent(&bank));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,11 +114,16 @@ impl BlockhashQueue {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_recent_blockhashes(&self) -> impl Iterator<Item = (u64, &Hash)> {
|
||||
(&self.ages).iter().map(|(k, v)| (v.hash_height, k))
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bincode::serialize;
|
||||
use solana_sdk::clock::MAX_RECENT_BLOCKHASHES;
|
||||
use solana_sdk::hash::hash;
|
||||
|
||||
#[test]
|
||||
@ -150,4 +155,21 @@ mod tests {
|
||||
assert_eq!(last_hash, hash_queue.last_hash());
|
||||
assert!(hash_queue.check_hash_age(&last_hash, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_recent_blockhashes() {
|
||||
let mut blockhash_queue = BlockhashQueue::new(MAX_RECENT_BLOCKHASHES);
|
||||
let recent_blockhashes = blockhash_queue.get_recent_blockhashes();
|
||||
// Sanity-check an empty BlockhashQueue
|
||||
assert_eq!(recent_blockhashes.count(), 0);
|
||||
for i in 0..MAX_RECENT_BLOCKHASHES {
|
||||
let hash = hash(&serialize(&i).unwrap());
|
||||
blockhash_queue.register_hash(&hash, &FeeCalculator::default());
|
||||
}
|
||||
let recent_blockhashes = blockhash_queue.get_recent_blockhashes();
|
||||
// Verify that the returned hashes are most recent
|
||||
for (_slot, hash) in recent_blockhashes {
|
||||
assert!(blockhash_queue.check_hash_age(hash, MAX_RECENT_BLOCKHASHES));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user