diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index b3d47f88a6..8a1a3c9ac0 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -18,7 +18,7 @@ use solana_sdk::native_loader; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, Signature}; use solana_sdk::system_transaction::SystemTransaction; -use solana_sdk::timing::{duration_as_us, MAX_RECENT_BLOCKHASHES, NUM_TICKS_PER_SECOND}; +use solana_sdk::timing::{duration_as_us, MAX_RECENT_BLOCKHASHES}; use solana_sdk::transaction::{Transaction, TransactionError}; use solana_vote_api::vote_instruction::Vote; use solana_vote_api::vote_state::{Lockout, VoteState}; @@ -105,7 +105,7 @@ impl EpochSchedule { pub type Result = result::Result; -type BankStatusCache = StatusCache; +type BankStatusCache = StatusCache>; /// Manager for the state of all accounts and programs after processing its entries. #[derive(Default)] @@ -117,7 +117,7 @@ pub struct Bank { accounts_id: u64, /// A cache of signature statuses - status_cache: RwLock, + status_cache: Arc>, /// FIFO queue of `recent_blockhash` items blockhash_queue: RwLock, @@ -173,14 +173,12 @@ impl Bank { let mut bank = Self::default(); bank.accounts = Arc::new(Accounts::new(bank.slot, paths)); bank.process_genesis_block(genesis_block); - // genesis needs stakes for all epochs up to the epoch implied by // slot = 0 and genesis configuration let vote_accounts: HashMap<_, _> = bank.vote_accounts().collect(); for i in 0..=bank.get_stakers_epoch(bank.slot) { bank.epoch_vote_accounts.insert(i, vote_accounts.clone()); } - bank } @@ -191,6 +189,8 @@ impl Bank { let mut bank = Self::default(); bank.blockhash_queue = RwLock::new(parent.blockhash_queue.read().unwrap().clone()); + bank.status_cache = parent.status_cache.clone(); + bank.tick_height .store(parent.tick_height.load(Ordering::SeqCst), Ordering::SeqCst); bank.ticks_per_slot = parent.ticks_per_slot; @@ -246,7 +246,6 @@ impl Bank { // freeze is a one-way trip, idempotent *hash = self.hash_internal_state(); } - // self.status_cache.write().unwrap().freeze(); } /// squash the parent's state up into this Bank, @@ -259,15 +258,9 @@ impl Bank { self.accounts.squash(self.accounts_id); - let parent_caches: Vec<_> = parents + parents .iter() - .map(|p| { - let mut parent = p.status_cache.write().unwrap(); - parent.freeze(); - parent - }) - .collect(); - self.status_cache.write().unwrap().squash(&parent_caches); + .for_each(|p| self.status_cache.write().unwrap().add_root(p.slot())); } /// Return the more recent checkpoint of this bank instance. @@ -355,7 +348,7 @@ impl Bank { /// Forget all signatures. Useful for benchmarking. pub fn clear_signatures(&self) { - self.status_cache.write().unwrap().clear(); + self.status_cache.write().unwrap().clear_signatures(); } fn update_transaction_statuses(&self, txs: &[Transaction], res: &[Result<()>]) { @@ -364,7 +357,12 @@ impl Bank { match &res[i] { Ok(_) => { if !tx.signatures.is_empty() { - status_cache.add(&tx.signatures[0]); + status_cache.insert( + &tx.recent_blockhash, + &tx.signatures[0], + self.slot(), + Ok(()), + ); } } Err(TransactionError::BlockhashNotFound) => (), @@ -372,8 +370,12 @@ impl Bank { Err(TransactionError::AccountNotFound) => (), Err(e) => { if !tx.signatures.is_empty() { - status_cache.add(&tx.signatures[0]); - status_cache.save_failure_status(&tx.signatures[0], e.clone()); + status_cache.insert( + &tx.recent_blockhash, + &tx.signatures[0], + self.slot(), + Err(e.clone()), + ); } } } @@ -430,12 +432,7 @@ impl Bank { // 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 blockhash_queue = self.blockhash_queue.write().unwrap(); - blockhash_queue.register_hash(hash); - } - - if current_tick_height % NUM_TICKS_PER_SECOND == 0 { - self.status_cache.write().unwrap().new_cache(hash); + self.blockhash_queue.write().unwrap().register_hash(hash); } } @@ -515,16 +512,24 @@ impl Bank { lock_results: Vec>, error_counters: &mut ErrorCounters, ) -> Vec> { - let parents = self.parents(); - let mut caches = vec![self.status_cache.read().unwrap()]; - caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap())); + let mut ancestors = HashMap::new(); + ancestors.insert(self.slot(), 0); + self.parents().iter().enumerate().for_each(|(i, p)| { + ancestors.insert(p.slot(), i + 1); + }); + + let rcache = self.status_cache.read().unwrap(); txs.iter() .zip(lock_results.into_iter()) .map(|(tx, lock_res)| { if tx.signatures.is_empty() { return lock_res; } - if lock_res.is_ok() && StatusCache::has_signature_all(&caches, &tx.signatures[0]) { + if lock_res.is_ok() + && rcache + .get_signature_status(&tx.signatures[0], &tx.recent_blockhash, &ancestors) + .is_some() + { error_counters.duplicate_signature += 1; Err(TransactionError::DuplicateSignature) } else { @@ -793,10 +798,13 @@ impl Bank { &self, signature: &Signature, ) -> Option<(usize, Result<()>)> { - let parents = self.parents(); - let mut caches = vec![self.status_cache.read().unwrap()]; - caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap())); - StatusCache::get_signature_status_all(&caches, signature) + let mut ancestors = HashMap::new(); + ancestors.insert(self.slot(), 0); + self.parents().iter().enumerate().for_each(|(i, p)| { + ancestors.insert(p.slot(), i + 1); + }); + let rcache = self.status_cache.read().unwrap(); + rcache.get_signature_status_slow(signature, &ancestors) } pub fn get_signature_status(&self, signature: &Signature) -> Option> { @@ -805,10 +813,7 @@ impl Bank { } pub fn has_signature(&self, signature: &Signature) -> bool { - let parents = self.parents(); - let mut caches = vec![self.status_cache.read().unwrap()]; - caches.extend(parents.iter().map(|b| b.status_cache.read().unwrap())); - StatusCache::has_signature_all(&caches, signature) + self.get_signature_confirmation_status(signature).is_some() } /// Hash the `accounts` HashMap. This represents a validator's interpretation @@ -1432,6 +1437,7 @@ mod tests { /// Verifies that last ids and accounts are correctly referenced from parent #[test] fn test_bank_squash() { + solana_logger::setup(); let (genesis_block, mint_keypair) = GenesisBlock::new(2); let key1 = Keypair::new(); let key2 = Keypair::new(); @@ -1439,10 +1445,23 @@ mod tests { let tx_move_mint_to_1 = SystemTransaction::new_move(&mint_keypair, &key1.pubkey(), 1, genesis_block.hash(), 0); + trace!("parent process tx "); assert_eq!(parent.process_transaction(&tx_move_mint_to_1), Ok(())); + trace!("done parent process tx "); assert_eq!(parent.transaction_count(), 1); + assert_eq!( + parent.get_signature_status(&tx_move_mint_to_1.signatures[0]), + Some(Ok(())) + ); + trace!("new form parent"); let bank = new_from_parent(&parent); + trace!("done new form parent"); + assert_eq!( + bank.get_signature_status(&tx_move_mint_to_1.signatures[0]), + Some(Ok(())) + ); + assert_eq!(bank.transaction_count(), parent.transaction_count()); let tx_move_1_to_2 = SystemTransaction::new_move(&key1, &key2.pubkey(), 1, genesis_block.hash(), 0); @@ -1459,6 +1478,7 @@ mod tests { assert_eq!(bank.get_balance(&key1.pubkey()), 0); assert_eq!(bank.get_account(&key1.pubkey()), None); assert_eq!(bank.get_balance(&key2.pubkey()), 1); + trace!("start"); assert_eq!( bank.get_signature_status(&tx_move_mint_to_1.signatures[0]), Some(Ok(())) @@ -1469,6 +1489,7 @@ mod tests { ); // works iteration 0, no-ops on iteration 1 and 2 + trace!("SQUASH"); bank.squash(); assert_eq!(parent.transaction_count(), 1); diff --git a/runtime/src/blockhash_queue.rs b/runtime/src/blockhash_queue.rs index d366819a99..2143be4341 100644 --- a/runtime/src/blockhash_queue.rs +++ b/runtime/src/blockhash_queue.rs @@ -69,6 +69,10 @@ impl BlockhashQueue { self.last_hash = Some(*hash); } + fn check_age(hash_height: u64, max_age: usize, age: &HashAge) -> bool { + hash_height - age.hash_height <= max_age as u64 + } + pub fn register_hash(&mut self, hash: &Hash) { self.hash_height += 1; let hash_height = self.hash_height; @@ -78,7 +82,7 @@ impl BlockhashQueue { let max_age = self.max_age; if self.ages.len() >= max_age { self.ages - .retain(|_, age| hash_height - age.hash_height <= max_age as u64); + .retain(|_, age| Self::check_age(hash_height, max_age, age)); } self.ages.insert( *hash, @@ -118,10 +122,10 @@ mod tests { } #[test] fn test_reject_old_last_hash() { - let last_hash = Hash::default(); let mut hash_queue = BlockhashQueue::new(100); - for i in 0..100 { - let last_hash = hash(&serialize(&i).unwrap()); // Unique hash + let last_hash = hash(&serialize(&0).unwrap()); + for i in 0..102 { + let last_hash = hash(&serialize(&i).unwrap()); hash_queue.register_hash(&last_hash); } // Assert we're no longer able to use the oldest hash. diff --git a/runtime/src/status_cache.rs b/runtime/src/status_cache.rs index 45752f6b67..a1d377f778 100644 --- a/runtime/src/status_cache.rs +++ b/runtime/src/status_cache.rs @@ -1,411 +1,245 @@ -use crate::bloom::{Bloom, BloomHashIndex}; -use hashbrown::HashMap; +use hashbrown::{HashMap, HashSet}; use log::*; use solana_sdk::hash::Hash; use solana_sdk::signature::Signature; -use std::collections::VecDeque; -use std::ops::Deref; -#[cfg(test)] -use std::ops::DerefMut; -use std::sync::Arc; -/// Each cache entry is designed to span ~1 second of signatures const MAX_CACHE_ENTRIES: usize = solana_sdk::timing::MAX_HASH_AGE_IN_SECONDS; -type FailureMap = HashMap; +// Store forks in a single chunk of memory to avoid another lookup. +pub type ForkId = u64; +pub type ForkStatus = Vec<(ForkId, T)>; +type SignatureMap = HashMap>; +type StatusMap = HashMap)>; -#[derive(Clone)] -struct Status { +pub struct StatusCache { /// all signatures seen during a hash period - signatures: Bloom, - - /// failures - failures: FailureMap, -} - -impl Status { - // new needs to be called once per second to be useful for the Bank - // 1M TPS * 1s (length of block in sigs) == 1M items in filter - // 1.0E-8 false positive rate - // https://hur.st/bloomfilter/?n=1000000&p=1.0E-8&m=&k= - fn new(blockhash: &Hash) -> Self { - let keys = (0..27).map(|i| blockhash.hash_at_index(i)).collect(); - Status { - signatures: Bloom::new(38_340_234, keys), - failures: HashMap::default(), - } - } - fn has_signature(&self, sig: &Signature) -> bool { - self.signatures.contains(&sig) - } - - fn add(&mut self, sig: &Signature) { - self.signatures.add(&sig); - } - - fn clear(&mut self) { - self.failures.clear(); - self.signatures.clear(); - } - pub fn get_signature_status(&self, sig: &Signature) -> Option> { - if let Some(res) = self.failures.get(sig) { - return Some(Err(res.clone())); - } else if self.signatures.contains(sig) { - return Some(Ok(())); - } - None - } -} - -#[derive(Clone)] -pub struct StatusCache { - /// currently active status - active: Option>, - - /// merges cover previous periods, and are read-only - merges: VecDeque>>, + cache: StatusMap, + roots: HashSet, } impl Default for StatusCache { fn default() -> Self { - Self::new(&Hash::default()) + Self { + cache: HashMap::default(), + roots: HashSet::default(), + } } } impl StatusCache { - pub fn new(blockhash: &Hash) -> Self { - Self { - active: Some(Status::new(blockhash)), - merges: VecDeque::default(), - } - } - fn has_signature_merged(&self, sig: &Signature) -> bool { - for c in &self.merges { - if c.has_signature(sig) { - return true; - } - } - false - } - /// test if a signature is known - pub fn has_signature(&self, sig: &Signature) -> bool { - self.active - .as_ref() - .map_or(false, |active| active.has_signature(&sig)) - || self.has_signature_merged(sig) + /// Check if the signature from a transaction is in any of the forks in the ancestors set. + pub fn get_signature_status( + &self, + sig: &Signature, + transaction_blockhash: &Hash, + ancestors: &HashMap, + ) -> Option<(ForkId, T)> { + let (_, sigmap) = self.cache.get(transaction_blockhash)?; + let stored_forks = sigmap.get(sig)?; + stored_forks + .iter() + .filter(|(f, _)| ancestors.get(f).is_some() || self.roots.get(f).is_some()) + .nth(0) + .cloned() } - /// add a signature - pub fn add(&mut self, sig: &Signature) { - if let Some(active) = self.active.as_mut() { - active.add(&sig); - } - } - - /// Save an error status for a signature - pub fn save_failure_status(&mut self, sig: &Signature, err: T) { - assert!( - self.active - .as_ref() - .map_or(false, |active| active.has_signature(sig)), - "sig not found" - ); - - self.active - .as_mut() - .map(|active| active.failures.insert(*sig, err)); - } - /// Forget all signatures. Useful for benchmarking. - pub fn clear(&mut self) { - if let Some(active) = self.active.as_mut() { - active.clear(); - } - self.merges = VecDeque::new(); - } - - fn get_signature_status_merged(&self, sig: &Signature) -> Option> { - for c in &self.merges { - if c.has_signature(sig) { - return c.get_signature_status(sig); + /// TODO: wallets should send the Transactions recent blockhash as well + pub fn get_signature_status_slow( + &self, + sig: &Signature, + ancestors: &HashMap, + ) -> Option<(usize, T)> { + trace!("get_signature_status_slow"); + for blockhash in self.cache.keys() { + trace!("get_signature_status_slow: trying {}", blockhash); + if let Some((forkid, res)) = self.get_signature_status(sig, blockhash, ancestors) { + trace!("get_signature_status_slow: got {}", forkid); + return ancestors + .get(&forkid) + .map(|id| (*id, res.clone())) + .or_else(|| Some((ancestors.len(), res))); } } None } - pub fn get_signature_status(&self, sig: &Signature) -> Option> { - self.active - .as_ref() - .and_then(|active| active.get_signature_status(sig)) - .or_else(|| self.get_signature_status_merged(sig)) - } - fn squash_parent_is_full(&mut self, parent: &Self) -> bool { - // flatten and squash the parent and its merges into self.merges, - // returns true if self is full - if parent.active.is_some() { - warn!("=========== FIXME: squash() on an active parent! ================"); - } - // TODO: put this assert back in - //assert!(parent.active.is_none()); - - if self.merges.len() < MAX_CACHE_ENTRIES { - for merge in parent - .merges - .iter() - .take(MAX_CACHE_ENTRIES - self.merges.len()) - { - self.merges.push_back(merge.clone()); - } - } - self.merges.len() == MAX_CACHE_ENTRIES - } - - /// copy the parents and parents' merges up to this instance, up to - /// MAX_CACHE_ENTRIES deep - pub fn squash(&mut self, parents: &[U]) - where - U: Deref, - { - for parent in parents { - if self.squash_parent_is_full(parent) { - break; + /// Add a known root fork. Roots are always valid ancestors. + /// After MAX_CACHE_ENTRIES, roots are removed, and any old signatures are cleared. + pub fn add_root(&mut self, fork: ForkId) { + self.roots.insert(fork); + if self.roots.len() > MAX_CACHE_ENTRIES { + if let Some(min) = self.roots.iter().min().cloned() { + self.roots.remove(&min); + self.cache.retain(|_, (fork, _)| *fork > min); } } } - /// Crate a new cache, pushing the old cache into the merged queue - pub fn new_cache(&mut self, blockhash: &Hash) { - assert!(self.active.is_some()); - let merge = self.active.replace(Status::new(blockhash)); - - self.merges.push_front(Arc::new(merge.unwrap())); - if self.merges.len() > MAX_CACHE_ENTRIES { - self.merges.pop_back(); - } + /// Insert a new signature for a specific fork. + pub fn insert(&mut self, transaction_blockhash: &Hash, sig: &Signature, fork: ForkId, res: T) { + let sig_map = self + .cache + .entry(*transaction_blockhash) + .or_insert((fork, HashMap::new())); + sig_map.0 = std::cmp::max(fork, sig_map.0); + let sig_forks = sig_map.1.entry(*sig).or_insert(vec![]); + sig_forks.push((fork, res)); } - pub fn freeze(&mut self) { - if let Some(active) = self.active.take() { - self.merges.push_front(Arc::new(active)); + /// Clear for testing + pub fn clear_signatures(&mut self) { + for v in self.cache.values_mut() { + v.1 = HashMap::new(); } } - - pub fn get_signature_status_all( - checkpoints: &[U], - signature: &Signature, - ) -> Option<(usize, Result<(), T>)> - where - U: Deref, - { - for (i, c) in checkpoints.iter().enumerate() { - if let Some(status) = c.get_signature_status(signature) { - return Some((i, status)); - } - } - None - } - pub fn has_signature_all(checkpoints: &[U], signature: &Signature) -> bool - where - U: Deref, - { - for c in checkpoints { - if c.has_signature(signature) { - return true; - } - } - false - } - #[cfg(test)] - pub fn clear_all(checkpoints: &mut [U]) -> bool - where - U: DerefMut, - { - for c in checkpoints.iter_mut() { - c.clear(); - } - false - } } + #[cfg(test)] mod tests { use super::*; use solana_sdk::hash::hash; - use solana_sdk::transaction::TransactionError; - type BankStatusCache = StatusCache; + type BankStatusCache = StatusCache<()>; #[test] - fn test_has_signature() { + fn test_empty_has_no_sigs() { let sig = Signature::default(); let blockhash = hash(Hash::default().as_ref()); - let mut status_cache = BankStatusCache::new(&blockhash); - assert_eq!(status_cache.has_signature(&sig), false); - assert_eq!(status_cache.get_signature_status(&sig), None); - status_cache.add(&sig); - assert_eq!(status_cache.has_signature(&sig), true); - assert_eq!(status_cache.get_signature_status(&sig), Some(Ok(()))); - } - - #[test] - fn test_has_signature_checkpoint() { - let sig = Signature::default(); - let blockhash = hash(Hash::default().as_ref()); - let mut first = BankStatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.get_signature_status(&sig), Some(Ok(()))); - let blockhash = hash(blockhash.as_ref()); - let second = StatusCache::new(&blockhash); - let checkpoints = [&second, &first]; + let status_cache = BankStatusCache::default(); assert_eq!( - BankStatusCache::get_signature_status_all(&checkpoints, &sig), - Some((1, Ok(()))), + status_cache.get_signature_status(&sig, &blockhash, &HashMap::new()), + None + ); + assert_eq!( + status_cache.get_signature_status_slow(&sig, &HashMap::new()), + None ); - assert!(StatusCache::has_signature_all(&checkpoints, &sig)); } #[test] - fn test_new_cache() { + fn test_find_sig_with_ancestor_fork() { let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); - let mut first = BankStatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.get_signature_status(&sig), Some(Ok(()))); - let blockhash = hash(blockhash.as_ref()); - first.new_cache(&blockhash); - assert_eq!(first.get_signature_status(&sig), Some(Ok(()))); - assert!(first.has_signature(&sig)); - first.clear(); - assert_eq!(first.get_signature_status(&sig), None); - assert!(!first.has_signature(&sig)); + let ancestors = vec![(0, 1)].into_iter().collect(); + status_cache.insert(&blockhash, &sig, 0, ()); + assert_eq!( + status_cache.get_signature_status(&sig, &blockhash, &ancestors), + Some((0, ())) + ); + assert_eq!( + status_cache.get_signature_status_slow(&sig, &ancestors), + Some((1, ())) + ); } #[test] - fn test_new_cache_full() { + fn test_find_sig_without_ancestor_fork() { let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); - let mut first = BankStatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.get_signature_status(&sig), Some(Ok(()))); - for _ in 0..(MAX_CACHE_ENTRIES + 1) { - let blockhash = hash(blockhash.as_ref()); - first.new_cache(&blockhash); + let ancestors = HashMap::new(); + status_cache.insert(&blockhash, &sig, 0, ()); + assert_eq!( + status_cache.get_signature_status(&sig, &blockhash, &ancestors), + None + ); + assert_eq!( + status_cache.get_signature_status_slow(&sig, &ancestors), + None + ); + } + + #[test] + fn test_find_sig_with_root_ancestor_fork() { + let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); + let blockhash = hash(Hash::default().as_ref()); + let ancestors = HashMap::new(); + status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.add_root(0); + assert_eq!( + status_cache.get_signature_status(&sig, &blockhash, &ancestors), + Some((0, ())) + ); + } + + #[test] + fn test_find_sig_with_root_ancestor_fork_max_len() { + let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); + let blockhash = hash(Hash::default().as_ref()); + let ancestors = vec![(2, 2)].into_iter().collect(); + status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.add_root(0); + assert_eq!( + status_cache.get_signature_status_slow(&sig, &ancestors), + Some((ancestors.len(), ())) + ); + } + + #[test] + fn test_insert_picks_latest_blockhash_fork() { + let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); + let blockhash = hash(Hash::default().as_ref()); + let ancestors = vec![(0, 0)].into_iter().collect(); + status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, &sig, 1, ()); + for i in 0..(MAX_CACHE_ENTRIES + 1) { + status_cache.add_root(i as u64); } - assert_eq!(first.get_signature_status(&sig), None); - assert!(!first.has_signature(&sig)); + assert!(status_cache + .get_signature_status(&sig, &blockhash, &ancestors) + .is_some()); } #[test] - fn test_status_cache_squash_has_signature() { + fn test_root_expires() { let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); - let mut first = BankStatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.get_signature_status(&sig), Some(Ok(()))); - - // give first a merge - let blockhash = hash(blockhash.as_ref()); - first.new_cache(&blockhash); - - let blockhash = hash(blockhash.as_ref()); - let mut second = BankStatusCache::new(&blockhash); - first.freeze(); - second.squash(&[&first]); - - assert_eq!(second.get_signature_status(&sig), Some(Ok(()))); - assert!(second.has_signature(&sig)); - } - - #[test] - fn test_status_cache_squash_overflow() { - let mut blockhash = hash(Hash::default().as_ref()); - let mut cache = BankStatusCache::new(&blockhash); - - let parents: Vec<_> = (0..MAX_CACHE_ENTRIES) - .map(|_| { - blockhash = hash(blockhash.as_ref()); - - let mut p = BankStatusCache::new(&blockhash); - p.freeze(); - p - }) - .collect(); - - let mut parents_refs: Vec<_> = parents.iter().collect(); - - blockhash = hash(Hash::default().as_ref()); - let mut root = BankStatusCache::new(&blockhash); - - let sig = Signature::default(); - root.add(&sig); - - parents_refs.push(&root); - - assert_eq!(root.get_signature_status(&sig), Some(Ok(()))); - assert!(root.has_signature(&sig)); - - // will overflow - cache.squash(&parents_refs); - - assert_eq!(cache.get_signature_status(&sig), None); - assert!(!cache.has_signature(&sig)); - } - - #[test] - fn test_failure_status() { - let sig = Signature::default(); - let blockhash = hash(Hash::default().as_ref()); - let mut first = StatusCache::new(&blockhash); - first.add(&sig); - first.save_failure_status(&sig, TransactionError::DuplicateSignature); - assert_eq!(first.has_signature(&sig), true); + let ancestors = HashMap::new(); + status_cache.insert(&blockhash, &sig, 0, ()); + for i in 0..(MAX_CACHE_ENTRIES + 1) { + status_cache.add_root(i as u64); + } assert_eq!( - first.get_signature_status(&sig), - Some(Err(TransactionError::DuplicateSignature)), + status_cache.get_signature_status(&sig, &blockhash, &ancestors), + None + ); + assert_eq!( + status_cache.get_signature_status_slow(&sig, &ancestors), + None ); } #[test] - fn test_clear_signatures() { + fn test_clear_signatures_sigs_are_gone() { let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); - let mut first = StatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.has_signature(&sig), true); - first.save_failure_status(&sig, TransactionError::DuplicateSignature); + let ancestors = HashMap::new(); + status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.add_root(0); + status_cache.clear_signatures(); assert_eq!( - first.get_signature_status(&sig), - Some(Err(TransactionError::DuplicateSignature)), - ); - first.clear(); - assert_eq!(first.has_signature(&sig), false); - assert_eq!(first.get_signature_status(&sig), None); - } - #[test] - fn test_clear_signatures_all() { - let sig = Signature::default(); - let blockhash = hash(Hash::default().as_ref()); - let mut first = StatusCache::new(&blockhash); - first.add(&sig); - assert_eq!(first.has_signature(&sig), true); - let mut second = StatusCache::new(&blockhash); - let mut checkpoints = [&mut second, &mut first]; - BankStatusCache::clear_all(&mut checkpoints); - assert_eq!( - BankStatusCache::has_signature_all(&checkpoints, &sig), - false + status_cache.get_signature_status(&sig, &blockhash, &ancestors), + None ); } #[test] - fn test_status_cache_freeze() { + fn test_clear_signatures_insert_works() { let sig = Signature::default(); + let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); - let mut cache: StatusCache<()> = StatusCache::new(&blockhash); - - cache.freeze(); - cache.freeze(); - - cache.add(&sig); - assert_eq!(cache.has_signature(&sig), false); + let ancestors = HashMap::new(); + status_cache.add_root(0); + status_cache.clear_signatures(); + status_cache.insert(&blockhash, &sig, 0, ()); + assert!(status_cache + .get_signature_status(&sig, &blockhash, &ancestors) + .is_some()); } - }