diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index e865bbb9a3..a818fe97e8 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -23,11 +23,12 @@ use solana_sdk::signature::{Keypair, Signature}; use solana_sdk::storage_program; use solana_sdk::system_program; use solana_sdk::system_transaction::SystemTransaction; -use solana_sdk::timing::{duration_as_us, MAX_RECENT_TICK_HASHES, NUM_TICKS_PER_SECOND}; +use solana_sdk::timing::{duration_as_us, MAX_RECENT_BLOCK_HASHES, NUM_TICKS_PER_SECOND}; use solana_sdk::token_program; use solana_sdk::transaction::Transaction; use solana_sdk::vote_program::{self, VoteState}; use std::result; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Instant; @@ -85,7 +86,7 @@ pub struct Bank { status_cache: RwLock, /// FIFO queue of `last_id` items - tick_hash_queue: RwLock, + block_hash_queue: RwLock, /// Previous checkpoint of this bank parent: RwLock>>, @@ -121,7 +122,7 @@ pub struct Bank { impl Default for HashQueue { fn default() -> Self { - Self::new(MAX_RECENT_TICK_HASHES) + Self::new(MAX_RECENT_BLOCK_HASHES) } } @@ -151,7 +152,7 @@ impl Bank { parent.freeze(); let mut bank = Self::default(); - bank.tick_hash_queue = RwLock::new(parent.tick_hash_queue.read().unwrap().clone()); + bank.block_hash_queue = RwLock::new(parent.block_hash_queue.read().unwrap().clone()); bank.tick_height .store(parent.tick_height.load(Ordering::SeqCst), Ordering::SeqCst); bank.ticks_per_slot = parent.ticks_per_slot; @@ -263,7 +264,7 @@ impl Bank { &bootstrap_leader_vote_account, ); - self.tick_hash_queue + self.block_hash_queue .write() .unwrap() .genesis_hash(&genesis_block.hash()); @@ -289,7 +290,7 @@ impl Bank { /// Return the last entry ID registered. pub fn last_id(&self) -> Hash { - self.tick_hash_queue.read().unwrap().last_hash() + self.block_hash_queue.read().unwrap().last_hash() } /// Forget all signatures. Useful for benchmarking. @@ -324,8 +325,7 @@ impl Bank { slots_and_stakes.sort_by(|a, b| a.0.cmp(&b.0)); let max_slot = self.slot_height(); - let min_slot = - max_slot.saturating_sub(MAX_RECENT_TICK_HASHES as u64 / self.ticks_per_slot()); + let min_slot = max_slot.saturating_sub(MAX_RECENT_BLOCK_HASHES as u64); let mut total_stake = 0; for (slot, stake) in slots_and_stakes.iter() { @@ -333,10 +333,10 @@ impl Bank { total_stake += stake; if total_stake > supermajority_stake { return self - .tick_hash_queue + .block_hash_queue .read() .unwrap() - .hash_height_to_timestamp(slot * self.ticks_per_slot()); + .hash_height_to_timestamp(*slot); } } } @@ -360,12 +360,12 @@ impl Bank { self.tick_height.fetch_add(1, Ordering::SeqCst); self.tick_height.load(Ordering::SeqCst) as u64 }; + inc_new_counter_info!("bank-register_tick-registered", 1); - { - let mut tick_hash_queue = self.tick_hash_queue.write().unwrap(); - inc_new_counter_info!("bank-register_tick-registered", 1); - tick_hash_queue.register_hash(hash); - assert_eq!(current_tick_height, tick_hash_queue.hash_height()) + // Register a new block hash if at the last tick in the slot + if current_tick_height % self.ticks_per_slot == self.ticks_per_slot - 1 { + let mut block_hash_queue = self.block_hash_queue.write().unwrap(); + block_hash_queue.register_hash(hash); } if current_tick_height % NUM_TICKS_PER_SECOND == 0 { @@ -411,7 +411,7 @@ impl Bank { max_age: usize, error_counters: &mut ErrorCounters, ) -> Vec> { - let hash_queue = self.tick_hash_queue.read().unwrap(); + let hash_queue = self.block_hash_queue.read().unwrap(); txs.iter() .zip(lock_results.into_iter()) .map(|(tx, lock_res)| { @@ -617,7 +617,7 @@ impl Bank { pub fn process_transactions(&self, txs: &[Transaction]) -> Vec> { let lock_results = self.lock_accounts(txs); let results = - self.load_execute_and_commit_transactions(txs, lock_results, MAX_RECENT_TICK_HASHES); + self.load_execute_and_commit_transactions(txs, lock_results, MAX_RECENT_BLOCK_HASHES); self.unlock_accounts(txs, &results); results } @@ -747,7 +747,7 @@ impl Bank { pub fn tick_height(&self) -> u64 { // tick_height is using an AtomicUSize because AtomicU64 is not yet a stable API. // Until we can switch to AtomicU64, fail if usize is not the same as u64 - assert_eq!(std::usize::MAX, 0xFFFFFFFFFFFFFFFF); + assert_eq!(std::usize::MAX, 0xFFFF_FFFF_FFFF_FFFF); self.tick_height.load(Ordering::SeqCst) as u64 } @@ -1229,7 +1229,7 @@ mod tests { let results_alice = bank.load_execute_and_commit_transactions( &pay_alice, lock_result, - MAX_RECENT_TICK_HASHES, + MAX_RECENT_BLOCK_HASHES, ); assert_eq!(results_alice[0], Ok(())); diff --git a/runtime/src/hash_queue.rs b/runtime/src/hash_queue.rs index 6acbe577f7..de5e771b7a 100644 --- a/runtime/src/hash_queue.rs +++ b/runtime/src/hash_queue.rs @@ -32,6 +32,7 @@ impl HashQueue { } } + #[allow(dead_code)] pub fn hash_height(&self) -> u64 { self.hash_height } @@ -78,7 +79,6 @@ impl HashQueue { self.entries .retain(|_, entry| hash_height - entry.hash_height <= max_entries as u64); } - self.entries.insert( *hash, HashQueueEntry { diff --git a/sdk/src/timing.rs b/sdk/src/timing.rs index 396767652a..eb4eb4405f 100644 --- a/sdk/src/timing.rs +++ b/sdk/src/timing.rs @@ -18,6 +18,8 @@ pub const DEFAULT_SLOTS_PER_EPOCH: u64 = 64; pub const MAX_HASH_AGE_IN_SECONDS: usize = 120; pub const MAX_RECENT_TICK_HASHES: usize = NUM_TICKS_PER_SECOND as usize * MAX_HASH_AGE_IN_SECONDS; +pub const MAX_RECENT_BLOCK_HASHES: usize = + MAX_RECENT_TICK_HASHES / (DEFAULT_TICKS_PER_SLOT as usize); pub fn duration_as_us(d: &Duration) -> u64 { (d.as_secs() * 1000 * 1000) + (u64::from(d.subsec_nanos()) / 1_000) diff --git a/src/banking_stage.rs b/src/banking_stage.rs index d2570e744a..b23fd4cb4a 100644 --- a/src/banking_stage.rs +++ b/src/banking_stage.rs @@ -391,7 +391,8 @@ mod tests { #[test] fn test_banking_stage_tick() { - let (genesis_block, _mint_keypair) = GenesisBlock::new(2); + let (mut genesis_block, _mint_keypair) = GenesisBlock::new(2); + genesis_block.ticks_per_slot = 4; let bank = Arc::new(Bank::new(&genesis_block)); let start_hash = bank.last_id(); let (verified_sender, verified_receiver) = channel(); @@ -400,17 +401,17 @@ mod tests { &bank, &poh_recorder, verified_receiver, - DEFAULT_TICKS_PER_SLOT, + genesis_block.ticks_per_slot - 1, genesis_block.bootstrap_leader_id, ); - sleep(Duration::from_millis(500)); + sleep(Duration::from_millis(600)); drop(verified_sender); let entries: Vec<_> = entry_receiver .iter() .flat_map(|x| x.into_iter().map(|e| e.0)) .collect(); - assert!(entries.len() != 0); + assert_eq!(entries.len(), genesis_block.ticks_per_slot as usize - 1); assert!(entries.verify(&start_hash)); assert_eq!(entries[entries.len() - 1].hash, bank.last_id()); banking_stage.join().unwrap(); diff --git a/src/blocktree_processor.rs b/src/blocktree_processor.rs index be45c9cbb4..f51502f602 100644 --- a/src/blocktree_processor.rs +++ b/src/blocktree_processor.rs @@ -426,11 +426,19 @@ mod tests { #[test] fn test_process_empty_entry_is_registered() { + solana_logger::setup(); + let (genesis_block, mint_keypair) = GenesisBlock::new(2); let bank = Bank::new(&genesis_block); let keypair = Keypair::new(); - let entry = next_entry(&genesis_block.hash(), 1, vec![]); - let tx = SystemTransaction::new_account(&mint_keypair, keypair.pubkey(), 1, entry.hash, 0); + let slot_entries = create_ticks(genesis_block.ticks_per_slot - 1, genesis_block.hash()); + let tx = SystemTransaction::new_account( + &mint_keypair, + keypair.pubkey(), + 1, + slot_entries.last().unwrap().hash, + 0, + ); // First, ensure the TX is rejected because of the unregistered last ID assert_eq!( @@ -439,19 +447,20 @@ mod tests { ); // Now ensure the TX is accepted despite pointing to the ID of an empty entry. - par_process_entries(&bank, &[entry]).unwrap(); + par_process_entries(&bank, &slot_entries).unwrap(); assert_eq!(bank.process_transaction(&tx), Ok(())); } #[test] fn test_process_ledger_simple() { + solana_logger::setup(); let leader_pubkey = Keypair::new().pubkey(); let (genesis_block, mint_keypair) = GenesisBlock::new_with_leader(100, leader_pubkey, 50); - let (ledger_path, last_id) = create_new_tmp_ledger!(&genesis_block); + let (ledger_path, mut last_entry_hash) = create_new_tmp_ledger!(&genesis_block); debug!("ledger_path: {:?}", ledger_path); let mut entries = vec![]; - let mut last_entry_hash = last_id; + let last_id = genesis_block.hash(); for _ in 0..3 { // Transfer one token from the mint to a random account let keypair = Keypair::new(); @@ -522,9 +531,10 @@ mod tests { let bank = Bank::new(&genesis_block); // ensure bank can process a tick + assert_eq!(bank.tick_height(), 0); let tick = next_entry(&genesis_block.hash(), 1, vec![]); assert_eq!(par_process_entries(&bank, &[tick.clone()]), Ok(())); - assert_eq!(bank.last_id(), tick.hash); + assert_eq!(bank.tick_height(), 1); } #[test] @@ -652,12 +662,15 @@ mod tests { assert_eq!(bank.process_transaction(&tx), Ok(())); let last_id = bank.last_id(); + while last_id == bank.last_id() { + bank.register_tick(&Hash::default()); + } // ensure bank can process 2 entries that do not have a common account and tick is registered - let tx = SystemTransaction::new_account(&keypair2, keypair3.pubkey(), 1, bank.last_id(), 0); + let tx = SystemTransaction::new_account(&keypair2, keypair3.pubkey(), 1, last_id, 0); let entry_1 = next_entry(&last_id, 1, vec![tx]); let tick = next_entry(&entry_1.hash, 1, vec![]); - let tx = SystemTransaction::new_account(&keypair1, keypair4.pubkey(), 1, tick.hash, 0); + let tx = SystemTransaction::new_account(&keypair1, keypair4.pubkey(), 1, bank.last_id(), 0); let entry_2 = next_entry(&tick.hash, 1, vec![tx]); assert_eq!( par_process_entries(&bank, &[entry_1.clone(), tick.clone(), entry_2.clone()]), @@ -665,9 +678,9 @@ mod tests { ); assert_eq!(bank.get_balance(&keypair3.pubkey()), 1); assert_eq!(bank.get_balance(&keypair4.pubkey()), 1); - assert_eq!(bank.last_id(), tick.hash); + // ensure that an error is returned for an empty account (keypair2) - let tx = SystemTransaction::new_account(&keypair2, keypair3.pubkey(), 1, tick.hash, 0); + let tx = SystemTransaction::new_account(&keypair2, keypair3.pubkey(), 1, bank.last_id(), 0); let entry_3 = next_entry(&entry_2.hash, 1, vec![tx]); assert_eq!( par_process_entries(&bank, &[entry_3]), diff --git a/src/leader_confirmation_service.rs b/src/leader_confirmation_service.rs index 57d1a0af39..6cff3f8025 100644 --- a/src/leader_confirmation_service.rs +++ b/src/leader_confirmation_service.rs @@ -147,14 +147,14 @@ mod tests { let (genesis_block, mint_keypair) = GenesisBlock::new(1234); let bank = Arc::new(Bank::new(&genesis_block)); - // generate 10 validators, but only vote for the first 6 validators - let ids: Vec<_> = (0..10 * bank.ticks_per_slot()) - .map(|i| { - let last_id = hash(&serialize(&i).unwrap()); // Unique hash - bank.register_tick(&last_id); - last_id - }) - .collect(); + + // Move the bank up 10 slots + let mut tick_hash = genesis_block.hash(); + while bank.slot_height() < 10 { + tick_hash = hash(&serialize(&tick_hash).unwrap()); + bank.register_tick(&tick_hash); + } + let last_id = bank.last_id(); // Create a total of 10 vote accounts, each will have a balance of 1 (after giving 1 to // their vote account), for a total staking pool of 10 tokens. @@ -162,7 +162,6 @@ mod tests { .map(|i| { // Create new validator to vote let validator_keypair = Arc::new(Keypair::new()); - let last_id = ids[i]; let voting_keypair = VotingKeypair::new_local(&validator_keypair); let voting_pubkey = voting_keypair.pubkey(); @@ -185,10 +184,11 @@ mod tests { genesis_block.bootstrap_leader_id, &mut last_confirmation_time, ); + assert_eq!(last_confirmation_time, 0); // Get another validator to vote, so we now have 2/3 consensus let voting_keypair = &vote_accounts[7].0; - let vote_tx = VoteTransaction::new_vote(voting_keypair, 7, ids[6], 0); + let vote_tx = VoteTransaction::new_vote(voting_keypair, 7, last_id, 0); bank.process_transaction(&vote_tx).unwrap(); LeaderConfirmationService::compute_confirmation(