From 4ed647d8eca24d09307323297ed2263c0697ebcc Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Sat, 16 Apr 2022 00:30:20 +0800 Subject: [PATCH] Test that tick slot hashes update the recent blockhash queue (#24242) --- ledger/src/blockstore_processor.rs | 135 ++++++++++++++++++++++++++++- runtime/src/bank.rs | 16 ++-- runtime/src/blockhash_queue.rs | 10 ++- 3 files changed, 150 insertions(+), 11 deletions(-) diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index fd5b3a35d..aa25c5e5b 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -892,7 +892,7 @@ pub fn confirm_slot( ) -> result::Result<(), BlockstoreProcessorError> { let slot = bank.slot(); - let (entries, num_shreds, slot_full) = { + let slot_entries_load_result = { let mut load_elapsed = Measure::start("load_elapsed"); let load_result = blockstore .get_slot_entries_with_shred_info(slot, progress.num_shreds, allow_dead_slots) @@ -906,6 +906,35 @@ pub fn confirm_slot( load_result }?; + confirm_slot_entries( + bank, + slot_entries_load_result, + timing, + progress, + skip_verification, + transaction_status_sender, + replay_vote_sender, + transaction_cost_metrics_sender, + entry_callback, + recyclers, + ) +} + +#[allow(clippy::too_many_arguments)] +fn confirm_slot_entries( + bank: &Arc, + slot_entries_load_result: (Vec, u64, bool), + timing: &mut ConfirmationTiming, + progress: &mut ConfirmationProgress, + skip_verification: bool, + transaction_status_sender: Option<&TransactionStatusSender>, + replay_vote_sender: Option<&ReplayVoteSender>, + transaction_cost_metrics_sender: Option<&TransactionCostMetricsSender>, + entry_callback: Option<&ProcessCallback>, + recyclers: &VerifyRecyclers, +) -> result::Result<(), BlockstoreProcessorError> { + let slot = bank.slot(); + let (entries, num_shreds, slot_full) = slot_entries_load_result; let num_entries = entries.len(); let num_txs = entries.iter().map(|e| e.transactions.len()).sum::(); trace!( @@ -3880,4 +3909,108 @@ pub mod tests { 8 ); } + + fn confirm_slot_entries_for_tests( + bank: &Arc, + slot_entries: Vec, + slot_full: bool, + prev_entry_hash: Hash, + ) -> result::Result<(), BlockstoreProcessorError> { + confirm_slot_entries( + bank, + (slot_entries, 0, slot_full), + &mut ConfirmationTiming::default(), + &mut ConfirmationProgress::new(prev_entry_hash), + false, + None, + None, + None, + None, + &VerifyRecyclers::default(), + ) + } + + #[test] + fn test_confirm_slot_entries() { + const HASHES_PER_TICK: u64 = 10; + const TICKS_PER_SLOT: u64 = 2; + + let collector_id = Pubkey::new_unique(); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair, + .. + } = create_genesis_config(10_000); + genesis_config.poh_config.hashes_per_tick = Some(HASHES_PER_TICK); + genesis_config.ticks_per_slot = TICKS_PER_SLOT; + let genesis_hash = genesis_config.hash(); + + let slot_0_bank = Arc::new(Bank::new_for_tests(&genesis_config)); + assert_eq!(slot_0_bank.slot(), 0); + assert_eq!(slot_0_bank.tick_height(), 0); + assert_eq!(slot_0_bank.max_tick_height(), 2); + assert_eq!(slot_0_bank.last_blockhash(), genesis_hash); + assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(0)); + + let slot_0_entries = entry::create_ticks(TICKS_PER_SLOT, HASHES_PER_TICK, genesis_hash); + let slot_0_hash = slot_0_entries.last().unwrap().hash; + confirm_slot_entries_for_tests(&slot_0_bank, slot_0_entries, true, genesis_hash).unwrap(); + assert_eq!(slot_0_bank.tick_height(), slot_0_bank.max_tick_height()); + assert_eq!(slot_0_bank.last_blockhash(), slot_0_hash); + assert_eq!(slot_0_bank.get_hash_age(&genesis_hash), Some(1)); + assert_eq!(slot_0_bank.get_hash_age(&slot_0_hash), Some(0)); + + let slot_2_bank = Arc::new(Bank::new_from_parent(&slot_0_bank, &collector_id, 2)); + assert_eq!(slot_2_bank.slot(), 2); + assert_eq!(slot_2_bank.tick_height(), 2); + assert_eq!(slot_2_bank.max_tick_height(), 6); + assert_eq!(slot_2_bank.last_blockhash(), slot_0_hash); + + let slot_1_entries = entry::create_ticks(TICKS_PER_SLOT, HASHES_PER_TICK, slot_0_hash); + let slot_1_hash = slot_1_entries.last().unwrap().hash; + confirm_slot_entries_for_tests(&slot_2_bank, slot_1_entries, false, slot_0_hash).unwrap(); + assert_eq!(slot_2_bank.tick_height(), 4); + assert_eq!(slot_2_bank.last_blockhash(), slot_1_hash); + assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(2)); + assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(1)); + assert_eq!(slot_2_bank.get_hash_age(&slot_1_hash), Some(0)); + + // Check that slot 2 transactions can use any previous slot hash, including the + // hash for slot 1 which is just ticks. + let slot_2_entries = { + let to_pubkey = Pubkey::new_unique(); + let mut prev_entry_hash = slot_1_hash; + let mut remaining_entry_hashes = HASHES_PER_TICK; + let mut entries: Vec = [genesis_hash, slot_0_hash, slot_1_hash] + .into_iter() + .map(|recent_hash| { + let tx = + system_transaction::transfer(&mint_keypair, &to_pubkey, 1, recent_hash); + remaining_entry_hashes = remaining_entry_hashes.checked_sub(1).unwrap(); + next_entry_mut(&mut prev_entry_hash, 1, vec![tx]) + }) + .collect(); + + entries.push(next_entry_mut( + &mut prev_entry_hash, + remaining_entry_hashes, + vec![], + )); + entries.push(next_entry_mut( + &mut prev_entry_hash, + HASHES_PER_TICK, + vec![], + )); + entries + }; + let slot_2_hash = slot_2_entries.last().unwrap().hash; + confirm_slot_entries_for_tests(&slot_2_bank, slot_2_entries, true, slot_1_hash).unwrap(); + assert_eq!(slot_2_bank.tick_height(), slot_2_bank.max_tick_height()); + assert_eq!(slot_2_bank.last_blockhash(), slot_2_hash); + assert_eq!(slot_2_bank.get_hash_age(&genesis_hash), Some(3)); + assert_eq!(slot_2_bank.get_hash_age(&slot_0_hash), Some(2)); + assert_eq!(slot_2_bank.get_hash_age(&slot_1_hash), Some(1)); + assert_eq!(slot_2_bank.get_hash_age(&slot_2_hash), Some(0)); + } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 140ff4e10..1712878ba 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -93,8 +93,8 @@ use { account_utils::StateMut, clock::{ BankId, Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, - INITIAL_RENT_EPOCH, MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, - MAX_TRANSACTION_FORWARDING_DELAY, SECONDS_PER_DAY, + INITIAL_RENT_EPOCH, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY, + SECONDS_PER_DAY, }, ed25519_program, epoch_info::EpochInfo, @@ -1252,12 +1252,6 @@ pub struct Bank { pub fee_structure: FeeStructure, } -impl Default for BlockhashQueue { - fn default() -> Self { - Self::new(MAX_RECENT_BLOCKHASHES) - } -} - struct VoteWithStakeDelegations { vote_state: Arc, vote_account: AccountSharedData, @@ -3730,6 +3724,10 @@ impl Bank { .collect() } + pub fn get_hash_age(&self, hash: &Hash) -> Option { + self.blockhash_queue.read().unwrap().get_hash_age(hash) + } + pub fn check_hash_age(&self, hash: &Hash, max_age: usize) -> Option { self.blockhash_queue .read() @@ -6854,7 +6852,7 @@ pub(crate) mod tests { solana_sdk::{ account::Account, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, - clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_TICKS_PER_SLOT}, + clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_TICKS_PER_SLOT, MAX_RECENT_BLOCKHASHES}, compute_budget::ComputeBudgetInstruction, epoch_schedule::MINIMUM_SLOTS_PER_EPOCH, feature::Feature, diff --git a/runtime/src/blockhash_queue.rs b/runtime/src/blockhash_queue.rs index 5be334fa3..f3247c4ef 100644 --- a/runtime/src/blockhash_queue.rs +++ b/runtime/src/blockhash_queue.rs @@ -2,7 +2,9 @@ use solana_sdk::sysvar::recent_blockhashes; use { serde::{Deserialize, Serialize}, - solana_sdk::{fee_calculator::FeeCalculator, hash::Hash, timing::timestamp}, + solana_sdk::{ + clock::MAX_RECENT_BLOCKHASHES, fee_calculator::FeeCalculator, hash::Hash, timing::timestamp, + }, std::collections::HashMap, }; @@ -29,6 +31,12 @@ pub struct BlockhashQueue { max_age: usize, } +impl Default for BlockhashQueue { + fn default() -> Self { + Self::new(MAX_RECENT_BLOCKHASHES) + } +} + impl BlockhashQueue { pub fn new(max_age: usize) -> Self { Self {