Repair alternate versions of dead slots (#9805)

Co-authored-by: Carl <carl@solana.com>
This commit is contained in:
carllin
2020-05-05 14:07:21 -07:00
committed by GitHub
parent b2672fd623
commit 3442f36f8a
17 changed files with 1246 additions and 122 deletions

View File

@@ -9,7 +9,7 @@ use solana_sdk::{
signature::Signature,
};
use std::{
collections::{HashMap, HashSet},
collections::{hash_map::Entry, HashMap, HashSet},
sync::{Arc, Mutex},
};
@@ -82,6 +82,46 @@ impl<T: Serialize + Clone + PartialEq> PartialEq for StatusCache<T> {
}
impl<T: Serialize + Clone> StatusCache<T> {
pub fn clear_slot_signatures(&mut self, slot: Slot) {
let slot_deltas = self.slot_deltas.remove(&slot);
if let Some(slot_deltas) = slot_deltas {
let slot_deltas = slot_deltas.lock().unwrap();
for (blockhash, (_, signature_list)) in slot_deltas.iter() {
// Any blockhash that exists in self.slot_deltas must also exist
// in self.cache, because in self.purge_roots(), when an entry
// (b, (max_slot, _, _)) is removed from self.cache, this implies
// all entries in self.slot_deltas < max_slot are also removed
if let Entry::Occupied(mut o_blockhash_entries) = self.cache.entry(*blockhash) {
let (_, _, all_sig_maps) = o_blockhash_entries.get_mut();
for (sig_slice, _) in signature_list {
if let Entry::Occupied(mut o_sig_list) = all_sig_maps.entry(*sig_slice) {
let sig_list = o_sig_list.get_mut();
sig_list.retain(|(updated_slot, _)| *updated_slot != slot);
if sig_list.is_empty() {
o_sig_list.remove_entry();
}
} else {
panic!(
"Map for signature must exist if siganture exists in self.slot_deltas, slot: {}",
slot
)
}
}
if all_sig_maps.is_empty() {
o_blockhash_entries.remove_entry();
}
} else {
panic!(
"Blockhash must exist if it exists in self.slot_deltas, slot: {}",
slot
)
}
}
}
}
/// Check if the signature from a transaction is in any of the forks in the ancestors set.
pub fn get_signature_status(
&self,
@@ -408,6 +448,8 @@ mod tests {
status_cache.add_root(i as u64);
}
let slots: Vec<_> = (0_u64..MAX_CACHE_ENTRIES as u64 + 1).collect();
assert_eq!(status_cache.slot_deltas.len(), 1);
assert!(status_cache.slot_deltas.get(&1).is_some());
let slot_deltas = status_cache.slot_deltas(&slots);
let cache = StatusCache::from_slot_deltas(&slot_deltas);
assert_eq!(cache, status_cache);
@@ -417,4 +459,51 @@ mod tests {
fn test_age_sanity() {
assert!(MAX_CACHE_ENTRIES <= MAX_RECENT_BLOCKHASHES);
}
#[test]
fn test_clear_slot_signatures() {
let sig = Signature::default();
let mut status_cache = BankStatusCache::default();
let blockhash = hash(Hash::default().as_ref());
let blockhash2 = hash(blockhash.as_ref());
status_cache.insert(&blockhash, &sig, 0, ());
status_cache.insert(&blockhash, &sig, 1, ());
status_cache.insert(&blockhash2, &sig, 1, ());
let mut ancestors0 = HashMap::new();
ancestors0.insert(0, 0);
let mut ancestors1 = HashMap::new();
ancestors1.insert(1, 0);
// Clear slot 0 related data
assert!(status_cache
.get_signature_status(&sig, &blockhash, &ancestors0)
.is_some());
status_cache.clear_slot_signatures(0);
assert!(status_cache
.get_signature_status(&sig, &blockhash, &ancestors0)
.is_none());
assert!(status_cache
.get_signature_status(&sig, &blockhash, &ancestors1)
.is_some());
assert!(status_cache
.get_signature_status(&sig, &blockhash2, &ancestors1)
.is_some());
// Check that the slot delta for slot 0 is gone, but slot 1 still
// exists
assert!(status_cache.slot_deltas.get(&0).is_none());
assert!(status_cache.slot_deltas.get(&1).is_some());
// Clear slot 1 related data
status_cache.clear_slot_signatures(1);
assert!(status_cache.slot_deltas.is_empty());
assert!(status_cache
.get_signature_status(&sig, &blockhash, &ancestors1)
.is_none());
assert!(status_cache
.get_signature_status(&sig, &blockhash2, &ancestors1)
.is_none());
assert!(status_cache.cache.is_empty());
}
}