use std::collections::BTreeMap;
use std::error::Error;
use crate::db::types::{Decode, Encode};
use crate::global::MAX_REORG_HISTORY_SIZE;
#[derive(Clone)]
pub struct BlockHistoryCacheData<V>
where
V: Encode + Decode + Clone + Eq,
{
cache: BTreeMap<u64, Option<V>>,
}
pub trait BlockHistoryCache<V>
where
V: Encode + Decode + Clone + Eq,
{
fn new(initial_value: Option<V>) -> Self;
fn latest(&self) -> Option<V>;
fn set(&mut self, block_number: u64, value: V);
fn unset(&mut self, block_number: u64);
fn reorg(&mut self, latest_valid_block_number: u64);
fn is_old(&self, block_number: u64) -> bool;
}
impl<V> BlockHistoryCacheData<V>
where
V: Encode + Decode + Clone + Eq,
{
fn remove_old_values(&mut self, latest_block_number: u64) {
let keys_to_remove: Vec<u64> = self
.cache
.keys()
.filter(|&&key| key + MAX_REORG_HISTORY_SIZE <= latest_block_number)
.cloned()
.collect();
if keys_to_remove.len() != 0 {
for key in keys_to_remove.iter().take(keys_to_remove.len() - 1) {
self.cache.remove(key);
}
}
}
}
impl<V> BlockHistoryCache<V> for BlockHistoryCacheData<V>
where
V: Encode + Decode + Clone + Eq,
{
fn new(initial_value: Option<V>) -> Self {
let mut cache = BTreeMap::new();
cache.insert(0, initial_value);
Self { cache }
}
fn latest(&self) -> Option<V> {
self.cache
.values()
.last()
.cloned()
.expect("Cache is never empty")
}
fn set(&mut self, block_number: u64, value: V) {
if let Some((latest_stored_block_number, _)) = self.cache.iter().last() {
if block_number < *latest_stored_block_number {
panic!("Block number must be greater than or equal to the latest block number");
}
}
if let Some(latest) = self.latest() {
if latest == value {
return;
}
}
self.cache.insert(block_number, Some(value));
self.remove_old_values(block_number);
}
fn unset(&mut self, block_number: u64) {
if let Some((latest_stored_block_number, _)) = self.cache.iter().last() {
if block_number < *latest_stored_block_number {
panic!("Block number must be greater than or equal to the latest block number");
}
}
if self.latest().is_none() {
return;
}
self.cache.insert(block_number, None);
self.remove_old_values(block_number);
}
fn reorg(&mut self, latest_valid_block_number: u64) {
self.cache
.retain(|&key, _| key <= latest_valid_block_number);
if self.cache.is_empty() {
panic!("Reorg too deep. Please restart your indexer.");
}
}
fn is_old(&self, latest_block_number: u64) -> bool {
if let Some(&latest_stored_block_number) = self.cache.keys().last() {
return latest_stored_block_number + MAX_REORG_HISTORY_SIZE < latest_block_number;
} else {
return true;
}
}
}
impl<V> Encode for BlockHistoryCacheData<V>
where
V: Encode + Decode + Clone + Eq,
{
fn encode(&self, buffer: &mut Vec<u8>) {
(self.cache.len() as u32).encode(buffer);
for (block_number, value) in &self.cache {
block_number.encode(buffer);
value.encode(buffer);
}
}
}
impl<V> Decode for BlockHistoryCacheData<V>
where
V: Encode + Decode + Clone + Eq,
{
fn decode(bytes: &[u8], offset: usize) -> Result<(Self, usize), Box<dyn Error>> {
let mut cache: BTreeMap<u64, Option<V>> = BTreeMap::new();
let (len, mut offset) = u32::decode(bytes, offset)?;
for _ in 0..len {
let (block_number, offset_temp) = Decode::decode(bytes, offset)?;
offset = offset_temp;
let (value, offset_temp) = Decode::decode(bytes, offset)?;
offset = offset_temp;
cache.insert(block_number, value);
}
Ok((Self { cache }, offset))
}
}
#[cfg(test)]
mod tests {
use alloy::primitives::U256;
use super::*;
use crate::db::types::U256ED;
#[test]
fn test_block_cache() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
let block_number = 1;
let value_ed: U256ED = U256::from(100).into();
cache.set(block_number, value_ed.clone());
assert_eq!(cache.latest().unwrap(), value_ed);
let value_ed2: U256ED = U256::from(200).into();
cache.set(block_number, value_ed2.clone());
assert_eq!(cache.latest().unwrap(), value_ed2);
let block_number2 = 2;
let value_ed3: U256ED = U256::from(300).into();
cache.set(block_number2, value_ed3.clone());
assert_eq!(cache.latest().unwrap(), value_ed3);
let value_ed4: U256ED = U256::from(400).into();
cache.set(block_number2, value_ed4.clone());
assert_eq!(cache.latest().unwrap(), value_ed4);
}
#[test]
fn test_history_size() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
for i in 0..MAX_REORG_HISTORY_SIZE + 2 {
let value = U256::from(100 * i);
cache.set(i, value.into());
}
assert_eq!(cache.cache.len(), (MAX_REORG_HISTORY_SIZE + 1) as usize);
}
#[test]
fn test_none_values() {
let cache = BlockHistoryCacheData::<U256ED>::new(None);
assert!(cache.latest().is_none());
}
#[test]
fn test_encode_decode() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
let block_number = 1;
let value = U256::from(100);
cache.set(block_number, value.into());
let encoded = cache.encode_vec();
let decoded = BlockHistoryCacheData::<U256ED>::decode_vec(&encoded).unwrap();
assert_eq!(decoded.latest().unwrap().uint, value);
}
#[test]
fn test_reorg() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
let block_number = 1;
let value = U256::from(100);
cache.set(block_number, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
let block_number2 = 2;
let value2 = U256::from(200);
cache.set(block_number2, value2.into());
assert_eq!(cache.latest().unwrap(), value2.into());
cache.reorg(1);
assert_eq!(cache.latest().unwrap(), value.into());
}
#[test]
fn test_reorg_multiple_blocks() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
for i in 1..=11 {
let value = U256::from(100 * i);
cache.set(i, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
}
cache.reorg(5);
assert_eq!(cache.latest().unwrap(), U256::from(500).into());
}
#[test]
fn test_reorg_all_blocks() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
for i in 1..=10 {
let value = U256::from(100 * i);
cache.set(i, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
}
cache.reorg(0);
assert!(cache.latest().is_none());
}
#[test]
fn test_same_values() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
let value = U256::from(100);
cache.set(0, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
cache.set(1, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
cache.set(2, value.into());
assert_eq!(cache.cache.len(), 1);
}
#[test]
#[should_panic]
fn test_reorg_too_old() {
let mut cache = BlockHistoryCacheData::<U256ED>::new(None);
for i in 1..=11 {
let value = U256::from(100 * i);
cache.set(i, value.into());
assert_eq!(cache.latest().unwrap(), value.into());
}
cache.reorg(0);
}
}