use std::collections::{hash_map::Entry, HashMap};
use casper_types::{EraId, PublicKey};
use datasize::DataSize;
use crate::types::{BlockHash, BlockSignatures};
#[derive(DataSize, Debug)]
pub(super) struct SignatureCache {
curr_era: EraId,
signatures: HashMap<BlockHash, BlockSignatures>,
}
impl SignatureCache {
pub(super) fn new() -> Self {
SignatureCache {
curr_era: EraId::from(0),
signatures: Default::default(),
}
}
pub(super) fn get(&self, hash: &BlockHash) -> Option<BlockSignatures> {
self.signatures.get(hash).cloned()
}
pub(super) fn insert(&mut self, block_signature: BlockSignatures) {
if self.curr_era < block_signature.era_id {
self.signatures.clear();
self.curr_era = block_signature.era_id;
}
match self.signatures.entry(block_signature.block_hash) {
Entry::Occupied(mut entry) => {
entry.get_mut().proofs.extend(block_signature.proofs);
}
Entry::Vacant(entry) => {
entry.insert(block_signature);
}
}
}
pub(super) fn known_signature(&self, block_hash: &BlockHash, public_key: &PublicKey) -> bool {
self.signatures
.get(block_hash)
.map_or(false, |bs| bs.has_proof(public_key))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{testing::TestRng, types::FinalitySignature};
use casper_types::{EraId, Signature};
use std::collections::BTreeMap;
#[test]
fn adding_signatures() {
let mut rng = TestRng::new();
let block_hash = BlockHash::random(&mut rng);
let mut cache = SignatureCache::new();
let mut block_signatures_a = BlockSignatures::new(block_hash, EraId::new(0));
let sig_a = FinalitySignature::random_for_block(block_hash, 0);
block_signatures_a.insert_proof(sig_a.public_key.clone(), sig_a.signature);
cache.insert(block_signatures_a.clone());
assert!(cache.known_signature(&block_hash, &sig_a.public_key));
let returned_signatures_a = cache.get(&block_hash).unwrap();
assert_eq!(block_signatures_a, returned_signatures_a);
let mut block_signatures_b = BlockSignatures::new(block_hash, EraId::new(0));
let sig_b = FinalitySignature::random_for_block(block_hash, 0);
block_signatures_b.insert_proof(sig_b.public_key.clone(), sig_b.signature);
cache.insert(block_signatures_b.clone());
assert!(cache.known_signature(&block_hash, &sig_b.public_key));
let returned_signatures_b = cache.get(&block_hash).unwrap();
let expected: BTreeMap<PublicKey, Signature> = block_signatures_a
.proofs
.into_iter()
.chain(block_signatures_b.proofs.into_iter())
.collect();
assert_eq!(expected, returned_signatures_b.proofs);
}
#[test]
fn purge_cache() {
let mut rng = TestRng::new();
let block_hash = BlockHash::random(&mut rng);
let mut cache = SignatureCache::new();
let mut block_signatures_a = BlockSignatures::new(block_hash, EraId::new(0));
let sig_a = FinalitySignature::random_for_block(block_hash, 0);
block_signatures_a.insert_proof(sig_a.public_key.clone(), sig_a.signature);
cache.insert(block_signatures_a);
let mut block_signatures_b = BlockSignatures::new(block_hash, EraId::new(1));
let sig_b = FinalitySignature::random_for_block(block_hash, 1);
block_signatures_b.insert_proof(sig_b.public_key.clone(), sig_b.signature);
cache.insert(block_signatures_b);
assert!(!cache.known_signature(&block_hash, &sig_a.public_key));
assert!(cache.known_signature(&block_hash, &sig_b.public_key));
}
}