2019-01-31 06:53:52 -08:00
|
|
|
use crate::bloom::{Bloom, BloomHashIndex};
|
|
|
|
use hashbrown::HashMap;
|
2019-03-20 11:06:39 -07:00
|
|
|
use log::*;
|
2019-01-31 06:53:52 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
|
|
|
use solana_sdk::signature::Signature;
|
|
|
|
use std::collections::VecDeque;
|
2019-02-18 23:26:22 -07:00
|
|
|
use std::ops::Deref;
|
|
|
|
#[cfg(test)]
|
|
|
|
use std::ops::DerefMut;
|
2019-03-20 11:06:39 -07:00
|
|
|
use std::sync::Arc;
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-03-01 12:16:20 -08:00
|
|
|
/// Each cache entry is designed to span ~1 second of signatures
|
|
|
|
const MAX_CACHE_ENTRIES: usize = solana_sdk::timing::MAX_HASH_AGE_IN_SECONDS;
|
2019-02-16 16:41:03 -08:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
type FailureMap<T> = HashMap<Signature, T>;
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2019-03-20 11:06:39 -07:00
|
|
|
struct Status<T> {
|
|
|
|
/// all signatures seen during a hash period
|
2019-01-31 06:53:52 -08:00
|
|
|
signatures: Bloom<Signature>,
|
|
|
|
|
|
|
|
/// failures
|
|
|
|
failures: FailureMap<T>,
|
2019-03-20 11:06:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Clone> Status<T> {
|
2019-03-20 11:10:42 -07:00
|
|
|
// 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=
|
2019-03-20 11:06:39 -07:00
|
|
|
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<Result<(), T>> {
|
|
|
|
if let Some(res) = self.failures.get(sig) {
|
|
|
|
return Some(Err(res.clone()));
|
|
|
|
} else if self.signatures.contains(sig) {
|
|
|
|
return Some(Ok(()));
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-03-20 11:06:39 -07:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct StatusCache<T> {
|
|
|
|
/// currently active status
|
|
|
|
active: Option<Status<T>>,
|
|
|
|
|
|
|
|
/// merges cover previous periods, and are read-only
|
|
|
|
merges: VecDeque<Arc<Status<T>>>,
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Clone> Default for StatusCache<T> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new(&Hash::default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Clone> StatusCache<T> {
|
2019-03-02 10:25:16 -08:00
|
|
|
pub fn new(blockhash: &Hash) -> Self {
|
2019-01-31 06:53:52 -08:00
|
|
|
Self {
|
2019-03-20 11:06:39 -07:00
|
|
|
active: Some(Status::new(blockhash)),
|
|
|
|
merges: VecDeque::default(),
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
2019-03-20 11:06:39 -07:00
|
|
|
self.active
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |active| active.has_signature(&sig))
|
|
|
|
|| self.has_signature_merged(sig)
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
2019-03-20 11:06:39 -07:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
/// add a signature
|
|
|
|
pub fn add(&mut self, sig: &Signature) {
|
2019-03-20 11:06:39 -07:00
|
|
|
if let Some(active) = self.active.as_mut() {
|
|
|
|
active.add(&sig);
|
|
|
|
}
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
2019-03-20 11:06:39 -07:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
/// Save an error status for a signature
|
|
|
|
pub fn save_failure_status(&mut self, sig: &Signature, err: T) {
|
2019-03-20 11:06:39 -07:00
|
|
|
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));
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
/// Forget all signatures. Useful for benchmarking.
|
|
|
|
pub fn clear(&mut self) {
|
2019-03-20 11:06:39 -07:00
|
|
|
if let Some(active) = self.active.as_mut() {
|
|
|
|
active.clear();
|
|
|
|
}
|
2019-02-16 16:41:03 -08:00
|
|
|
self.merges = VecDeque::new();
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
2019-03-20 11:06:39 -07:00
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
fn get_signature_status_merged(&self, sig: &Signature) -> Option<Result<(), T>> {
|
|
|
|
for c in &self.merges {
|
|
|
|
if c.has_signature(sig) {
|
|
|
|
return c.get_signature_status(sig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
pub fn get_signature_status(&self, sig: &Signature) -> Option<Result<(), T>> {
|
2019-03-20 11:06:39 -07:00
|
|
|
self.active
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|active| active.get_signature_status(sig))
|
|
|
|
.or_else(|| self.get_signature_status_merged(sig))
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
2019-02-21 12:08:50 -08:00
|
|
|
|
2019-02-25 20:34:05 -08:00
|
|
|
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
|
2019-03-20 11:06:39 -07:00
|
|
|
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());
|
|
|
|
}
|
2019-02-21 12:08:50 -08:00
|
|
|
}
|
|
|
|
self.merges.len() == MAX_CACHE_ENTRIES
|
|
|
|
}
|
|
|
|
|
|
|
|
/// copy the parents and parents' merges up to this instance, up to
|
|
|
|
/// MAX_CACHE_ENTRIES deep
|
2019-02-25 20:34:05 -08:00
|
|
|
pub fn squash<U>(&mut self, parents: &[U])
|
2019-02-21 12:08:50 -08:00
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
|
|
|
for parent in parents {
|
2019-02-25 20:34:05 -08:00
|
|
|
if self.squash_parent_is_full(parent) {
|
2019-02-21 12:08:50 -08:00
|
|
|
break;
|
|
|
|
}
|
2019-02-16 16:41:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Crate a new cache, pushing the old cache into the merged queue
|
2019-03-02 10:25:16 -08:00
|
|
|
pub fn new_cache(&mut self, blockhash: &Hash) {
|
2019-03-20 11:06:39 -07:00
|
|
|
assert!(self.active.is_some());
|
|
|
|
let merge = self.active.replace(Status::new(blockhash));
|
|
|
|
|
|
|
|
self.merges.push_front(Arc::new(merge.unwrap()));
|
2019-02-16 16:41:03 -08:00
|
|
|
if self.merges.len() > MAX_CACHE_ENTRIES {
|
2019-01-31 06:53:52 -08:00
|
|
|
self.merges.pop_back();
|
|
|
|
}
|
|
|
|
}
|
2019-03-20 11:06:39 -07:00
|
|
|
|
|
|
|
pub fn freeze(&mut self) {
|
|
|
|
if let Some(active) = self.active.take() {
|
|
|
|
self.merges.push_front(Arc::new(active));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
pub fn get_signature_status_all<U>(
|
|
|
|
checkpoints: &[U],
|
|
|
|
signature: &Signature,
|
2019-03-21 07:43:21 -07:00
|
|
|
) -> Option<(usize, Result<(), T>)>
|
2019-01-31 06:53:52 -08:00
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
2019-03-21 07:43:21 -07:00
|
|
|
for (i, c) in checkpoints.iter().enumerate() {
|
2019-01-31 06:53:52 -08:00
|
|
|
if let Some(status) = c.get_signature_status(signature) {
|
2019-03-21 07:43:21 -07:00
|
|
|
return Some((i, status));
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
pub fn has_signature_all<U>(checkpoints: &[U], signature: &Signature) -> bool
|
|
|
|
where
|
|
|
|
U: Deref<Target = Self>,
|
|
|
|
{
|
|
|
|
for c in checkpoints {
|
|
|
|
if c.has_signature(signature) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2019-02-18 23:26:22 -07:00
|
|
|
#[cfg(test)]
|
2019-01-31 06:53:52 -08:00
|
|
|
pub fn clear_all<U>(checkpoints: &mut [U]) -> bool
|
|
|
|
where
|
|
|
|
U: DerefMut<Target = Self>,
|
|
|
|
{
|
|
|
|
for c in checkpoints.iter_mut() {
|
|
|
|
c.clear();
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use solana_sdk::hash::hash;
|
2019-03-13 14:37:24 -06:00
|
|
|
use solana_sdk::transaction::TransactionError;
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-03-13 13:58:44 -06:00
|
|
|
type BankStatusCache = StatusCache<TransactionError>;
|
2019-01-31 06:53:52 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_has_signature() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut status_cache = BankStatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(status_cache.has_signature(&sig), false);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(status_cache.get_signature_status(&sig), None);
|
2019-01-31 06:53:52 -08:00
|
|
|
status_cache.add(&sig);
|
|
|
|
assert_eq!(status_cache.has_signature(&sig), true);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(status_cache.get_signature_status(&sig), Some(Ok(())));
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_has_signature_checkpoint() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = BankStatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.get_signature_status(&sig), Some(Ok(())));
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(blockhash.as_ref());
|
|
|
|
let second = StatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
let checkpoints = [&second, &first];
|
|
|
|
assert_eq!(
|
|
|
|
BankStatusCache::get_signature_status_all(&checkpoints, &sig),
|
2019-03-21 07:43:21 -07:00
|
|
|
Some((1, Ok(()))),
|
2019-01-31 06:53:52 -08:00
|
|
|
);
|
|
|
|
assert!(StatusCache::has_signature_all(&checkpoints, &sig));
|
|
|
|
}
|
|
|
|
|
2019-02-16 16:41:03 -08:00
|
|
|
#[test]
|
|
|
|
fn test_new_cache() {
|
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = BankStatusCache::new(&blockhash);
|
2019-02-16 16:41:03 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.get_signature_status(&sig), Some(Ok(())));
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(blockhash.as_ref());
|
|
|
|
first.new_cache(&blockhash);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(first.get_signature_status(&sig), Some(Ok(())));
|
2019-02-16 16:41:03 -08:00
|
|
|
assert!(first.has_signature(&sig));
|
|
|
|
first.clear();
|
|
|
|
assert_eq!(first.get_signature_status(&sig), None);
|
|
|
|
assert!(!first.has_signature(&sig));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_new_cache_full() {
|
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = BankStatusCache::new(&blockhash);
|
2019-02-16 16:41:03 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.get_signature_status(&sig), Some(Ok(())));
|
|
|
|
for _ in 0..(MAX_CACHE_ENTRIES + 1) {
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(blockhash.as_ref());
|
|
|
|
first.new_cache(&blockhash);
|
2019-02-16 16:41:03 -08:00
|
|
|
}
|
|
|
|
assert_eq!(first.get_signature_status(&sig), None);
|
|
|
|
assert!(!first.has_signature(&sig));
|
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
#[test]
|
2019-02-25 20:34:05 -08:00
|
|
|
fn test_status_cache_squash_has_signature() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = BankStatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.get_signature_status(&sig), Some(Ok(())));
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
// give first a merge
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(blockhash.as_ref());
|
|
|
|
first.new_cache(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(blockhash.as_ref());
|
|
|
|
let mut second = BankStatusCache::new(&blockhash);
|
2019-03-20 11:06:39 -07:00
|
|
|
first.freeze();
|
2019-02-25 20:34:05 -08:00
|
|
|
second.squash(&[&first]);
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
assert_eq!(second.get_signature_status(&sig), Some(Ok(())));
|
2019-01-31 06:53:52 -08:00
|
|
|
assert!(second.has_signature(&sig));
|
|
|
|
}
|
|
|
|
|
2019-02-21 12:08:50 -08:00
|
|
|
#[test]
|
2019-02-25 20:34:05 -08:00
|
|
|
fn test_status_cache_squash_overflow() {
|
2019-03-02 10:25:16 -08:00
|
|
|
let mut blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut cache = BankStatusCache::new(&blockhash);
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
let parents: Vec<_> = (0..MAX_CACHE_ENTRIES)
|
|
|
|
.map(|_| {
|
2019-03-02 10:25:16 -08:00
|
|
|
blockhash = hash(blockhash.as_ref());
|
2019-02-21 12:08:50 -08:00
|
|
|
|
2019-03-20 11:06:39 -07:00
|
|
|
let mut p = BankStatusCache::new(&blockhash);
|
|
|
|
p.freeze();
|
|
|
|
p
|
2019-02-21 12:08:50 -08:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut parents_refs: Vec<_> = parents.iter().collect();
|
|
|
|
|
2019-03-02 10:25:16 -08:00
|
|
|
blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut root = BankStatusCache::new(&blockhash);
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
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
|
2019-02-25 20:34:05 -08:00
|
|
|
cache.squash(&parents_refs);
|
2019-02-21 12:08:50 -08:00
|
|
|
|
|
|
|
assert_eq!(cache.get_signature_status(&sig), None);
|
|
|
|
assert!(!cache.has_signature(&sig));
|
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
#[test]
|
|
|
|
fn test_failure_status() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = StatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
first.add(&sig);
|
2019-03-13 13:58:44 -06:00
|
|
|
first.save_failure_status(&sig, TransactionError::DuplicateSignature);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(first.has_signature(&sig), true);
|
|
|
|
assert_eq!(
|
|
|
|
first.get_signature_status(&sig),
|
2019-03-13 13:58:44 -06:00
|
|
|
Some(Err(TransactionError::DuplicateSignature)),
|
2019-01-31 06:53:52 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_clear_signatures() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = StatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.has_signature(&sig), true);
|
2019-03-13 13:58:44 -06:00
|
|
|
first.save_failure_status(&sig, TransactionError::DuplicateSignature);
|
2019-01-31 06:53:52 -08:00
|
|
|
assert_eq!(
|
|
|
|
first.get_signature_status(&sig),
|
2019-03-13 13:58:44 -06:00
|
|
|
Some(Err(TransactionError::DuplicateSignature)),
|
2019-01-31 06:53:52 -08:00
|
|
|
);
|
|
|
|
first.clear();
|
|
|
|
assert_eq!(first.has_signature(&sig), false);
|
2019-02-21 12:08:50 -08:00
|
|
|
assert_eq!(first.get_signature_status(&sig), None);
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_clear_signatures_all() {
|
2019-02-09 09:20:43 -08:00
|
|
|
let sig = Signature::default();
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = hash(Hash::default().as_ref());
|
|
|
|
let mut first = StatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
first.add(&sig);
|
|
|
|
assert_eq!(first.has_signature(&sig), true);
|
2019-03-02 10:25:16 -08:00
|
|
|
let mut second = StatusCache::new(&blockhash);
|
2019-01-31 06:53:52 -08:00
|
|
|
let mut checkpoints = [&mut second, &mut first];
|
|
|
|
BankStatusCache::clear_all(&mut checkpoints);
|
|
|
|
assert_eq!(
|
|
|
|
BankStatusCache::has_signature_all(&checkpoints, &sig),
|
|
|
|
false
|
|
|
|
);
|
|
|
|
}
|
2019-03-20 11:06:39 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_status_cache_freeze() {
|
|
|
|
let sig = Signature::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);
|
|
|
|
}
|
|
|
|
|
2019-01-31 06:53:52 -08:00
|
|
|
}
|