use crate::{Error, Reference, Selection};
use chain_impl_mockchain::{block::Block, header::HeaderId, ledger::RewardsInfoParameters};
use std::sync::Arc;
pub struct Blockchain {
tip: Arc<Reference>,
heads: lru::LruCache<HeaderId, Arc<Reference>>,
cache: lru::LruCache<HeaderId, Arc<Reference>>,
}
#[derive(Debug, Copy, Clone)]
pub struct Configuration {
pub heads_capacity: usize,
pub cache_capacity: usize,
pub rewards_info_params: RewardsInfoParameters,
}
pub enum Event {
Added {
new_branch: bool,
new_tip: bool,
epoch_transition: bool,
new_reference: Arc<Reference>,
},
MissingParent {
parent: HeaderId,
},
}
impl Blockchain {
pub fn new(configuration: &Configuration, block0: Arc<Reference>) -> Self {
let mut blockchain = Self {
tip: Arc::clone(&block0),
heads: lru::LruCache::new(configuration.heads_capacity),
cache: lru::LruCache::new(configuration.cache_capacity),
};
blockchain.heads.put(block0.hash(), Arc::clone(&block0));
blockchain.cache.put(block0.hash(), block0);
blockchain
}
pub fn tip(&self) -> Arc<Reference> {
Arc::clone(&self.tip)
}
pub fn branches(&self) -> lru::Iter<'_, HeaderId, Arc<Reference>> {
self.heads.iter()
}
pub fn put(&mut self, block: &Block) -> Result<Event, Error> {
let parent_hash = block.header().block_parent_hash();
if let Some(parent) = self.heads.get(&parent_hash).cloned() {
self.cache.put(parent_hash, Arc::clone(&parent));
let new_reference = Reference::chain(Arc::clone(&parent), block)?;
let new_reference = Arc::new(new_reference);
let epoch_transition = parent.block_date().epoch < new_reference.block_date().epoch;
Ok(self.put_head(new_reference, false, epoch_transition))
} else if let Some(parent) = self.cache.get(&parent_hash).cloned() {
let new_reference = Reference::chain(Arc::clone(&parent), block)?;
let new_reference = Arc::new(new_reference);
let epoch_transition = parent.block_date().epoch < new_reference.block_date().epoch;
Ok(self.put_head(new_reference, true, epoch_transition))
} else {
Ok(Event::MissingParent {
parent: block.header().hash(),
})
}
}
fn put_head(
&mut self,
reference: Arc<Reference>,
new_branch: bool,
epoch_transition: bool,
) -> Event {
self.heads.put(reference.hash(), Arc::clone(&reference));
self.cache.put(reference.hash(), Arc::clone(&reference));
let new_tip = match self.tip.select(&reference) {
Selection::PreferCurrent => {
self.heads.put(self.tip.hash(), Arc::clone(&self.tip));
self.cache.put(self.tip.hash(), Arc::clone(&self.tip));
false
}
Selection::PreferCandidate => {
self.tip = Arc::clone(&reference);
true
}
};
Event::Added {
new_tip,
new_branch,
epoch_transition,
new_reference: reference,
}
}
}
impl Default for Configuration {
fn default() -> Self {
Self {
heads_capacity: 1024,
cache_capacity: 1024 * 1024 * 1024,
rewards_info_params: RewardsInfoParameters::default(),
}
}
}