use serde::{
de::{SeqAccess, Visitor},
ser::SerializeTuple,
Deserializer, Serializer,
};
use serde::{Deserialize, Serialize};
use solana_sdk::{
fee_calculator::FeeCalculator, hash::Hash, sysvar::recent_blockhashes, timing::timestamp,
};
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiExample)]
struct HashAge {
fee_calculator: FeeCalculator,
hash_height: u64,
timestamp: u64,
}
#[frozen_abi(digest = "J1fGiMHyiKEBcWE6mfm7grAEGJgYEaVLzcrNZvd37iA2")]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, AbiExample)]
pub struct BlockhashQueue {
hash_height: u64,
last_hash: Option<Hash>,
ages: HashMap<Hash, HashAge>,
max_age: usize,
}
impl BlockhashQueue {
pub fn new(max_age: usize) -> Self {
Self {
ages: HashMap::new(),
hash_height: 0,
last_hash: None,
max_age,
}
}
#[allow(dead_code)]
pub fn hash_height(&self) -> u64 {
self.hash_height
}
pub fn last_hash(&self) -> Hash {
self.last_hash.expect("no hash has been set")
}
pub fn get_fee_calculator(&self, hash: &Hash) -> Option<&FeeCalculator> {
self.ages.get(hash).map(|hash_age| &hash_age.fee_calculator)
}
pub fn check_hash_age(&self, hash: &Hash, max_age: usize) -> Option<bool> {
self.ages
.get(hash)
.map(|age| self.hash_height - age.hash_height <= max_age as u64)
}
pub fn get_hash_age(&self, hash: &Hash) -> Option<u64> {
self.ages
.get(hash)
.map(|age| self.hash_height - age.hash_height)
}
#[cfg(test)]
pub fn check_hash(&self, hash: Hash) -> bool {
self.ages.get(&hash).is_some()
}
pub fn genesis_hash(&mut self, hash: &Hash, fee_calculator: &FeeCalculator) {
self.ages.insert(
*hash,
HashAge {
fee_calculator: fee_calculator.clone(),
hash_height: 0,
timestamp: timestamp(),
},
);
self.last_hash = Some(*hash);
}
fn check_age(hash_height: u64, max_age: usize, age: &HashAge) -> bool {
hash_height - age.hash_height <= max_age as u64
}
pub fn register_hash(&mut self, hash: &Hash, fee_calculator: &FeeCalculator) {
self.hash_height += 1;
let hash_height = self.hash_height;
let max_age = self.max_age;
if self.ages.len() >= max_age {
self.ages
.retain(|_, age| Self::check_age(hash_height, max_age, age));
}
self.ages.insert(
*hash,
HashAge {
fee_calculator: fee_calculator.clone(),
hash_height,
timestamp: timestamp(),
},
);
self.last_hash = Some(*hash);
}
pub fn hash_height_to_timestamp(&self, hash_height: u64) -> Option<u64> {
for age in self.ages.values() {
if age.hash_height == hash_height {
return Some(age.timestamp);
}
}
None
}
pub fn get_recent_blockhashes(&self) -> impl Iterator<Item = recent_blockhashes::IterItem> {
(&self.ages)
.iter()
.map(|(k, v)| recent_blockhashes::IterItem(v.hash_height, k, &v.fee_calculator))
}
pub fn len(&self) -> usize {
self.max_age
}
}
pub const MAX_EVM_BLOCKHASHES: usize = 256;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockHashEvm {
#[serde(with = "BlockHashEvm")]
hashes: [evm_state::H256; MAX_EVM_BLOCKHASHES],
}
impl BlockHashEvm {
pub fn new() -> BlockHashEvm {
BlockHashEvm {
hashes: [evm_state::H256::zero(); MAX_EVM_BLOCKHASHES],
}
}
pub fn get_hashes(&self) -> &[evm_state::H256; MAX_EVM_BLOCKHASHES] {
&self.hashes
}
pub fn insert_hash(&mut self, hash: evm_state::H256) {
let new_hashes = self.hashes;
self.hashes[0..MAX_EVM_BLOCKHASHES - 1]
.copy_from_slice(&new_hashes[1..MAX_EVM_BLOCKHASHES]);
self.hashes[MAX_EVM_BLOCKHASHES - 1] = hash
}
fn deserialize<'de, D>(
deserializer: D,
) -> Result<[evm_state::H256; MAX_EVM_BLOCKHASHES], D::Error>
where
D: Deserializer<'de>,
{
struct ArrayVisitor<T> {
element: PhantomData<T>,
}
impl<'de, T> Visitor<'de> for ArrayVisitor<T>
where
T: Default + Copy + Deserialize<'de>,
{
type Value = [T; MAX_EVM_BLOCKHASHES];
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("an array of length ", 256))
}
fn visit_seq<A>(self, mut seq: A) -> Result<[T; MAX_EVM_BLOCKHASHES], A::Error>
where
A: SeqAccess<'de>,
{
let mut arr = [T::default(); MAX_EVM_BLOCKHASHES];
for (i, item) in arr.iter_mut().enumerate().take(MAX_EVM_BLOCKHASHES) {
*item = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
}
Ok(arr)
}
}
let visitor = ArrayVisitor {
element: PhantomData,
};
deserializer.deserialize_tuple(MAX_EVM_BLOCKHASHES, visitor)
}
fn serialize<S>(
data: &[evm_state::H256; MAX_EVM_BLOCKHASHES],
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_tuple(data.len())?;
for elem in &data[..] {
seq.serialize_element(elem)?;
}
seq.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
use bincode::serialize;
use evm_state::H256;
use solana_sdk::{
clock::MAX_RECENT_BLOCKHASHES, hash::hash, sysvar::recent_blockhashes::IterItem,
};
#[test]
fn test_register_hash() {
let last_hash = Hash::default();
let mut hash_queue = BlockhashQueue::new(100);
assert!(!hash_queue.check_hash(last_hash));
hash_queue.register_hash(&last_hash, &FeeCalculator::default());
assert!(hash_queue.check_hash(last_hash));
assert_eq!(hash_queue.hash_height(), 1);
}
#[test]
fn test_reject_old_last_hash() {
let mut hash_queue = BlockhashQueue::new(100);
let last_hash = hash(&serialize(&0).unwrap());
for i in 0..102 {
let last_hash = hash(&serialize(&i).unwrap());
hash_queue.register_hash(&last_hash, &FeeCalculator::default());
}
assert!(!hash_queue.check_hash(last_hash));
assert_eq!(None, hash_queue.check_hash_age(&last_hash, 0));
let last_valid_hash = hash(&serialize(&1).unwrap());
assert!(hash_queue.check_hash(last_valid_hash));
assert_eq!(Some(false), hash_queue.check_hash_age(&last_valid_hash, 0));
}
#[test]
fn test_queue_init_blockhash() {
let last_hash = Hash::default();
let mut hash_queue = BlockhashQueue::new(100);
hash_queue.register_hash(&last_hash, &FeeCalculator::default());
assert_eq!(last_hash, hash_queue.last_hash());
assert_eq!(Some(true), hash_queue.check_hash_age(&last_hash, 0));
}
#[test]
fn test_get_recent_blockhashes() {
let mut blockhash_queue = BlockhashQueue::new(MAX_RECENT_BLOCKHASHES);
let recent_blockhashes = blockhash_queue.get_recent_blockhashes();
assert_eq!(recent_blockhashes.count(), 0);
for i in 0..MAX_RECENT_BLOCKHASHES {
let hash = hash(&serialize(&i).unwrap());
blockhash_queue.register_hash(&hash, &FeeCalculator::default());
}
let recent_blockhashes = blockhash_queue.get_recent_blockhashes();
for IterItem(_slot, hash, _fee_calc) in recent_blockhashes {
assert_eq!(
Some(true),
blockhash_queue.check_hash_age(hash, MAX_RECENT_BLOCKHASHES)
);
}
}
#[test]
fn test_evm_blockhaheshes() {
let mut blockhash_queue = BlockHashEvm::new();
assert_eq!(
blockhash_queue.get_hashes(),
&[H256::zero(); MAX_EVM_BLOCKHASHES]
);
let hash1 = H256::repeat_byte(1);
blockhash_queue.insert_hash(hash1);
for hash in &blockhash_queue.get_hashes()[..MAX_EVM_BLOCKHASHES - 1] {
assert_eq!(*hash, H256::zero())
}
assert_eq!(blockhash_queue.get_hashes()[MAX_EVM_BLOCKHASHES - 1], hash1);
for i in 0..MAX_EVM_BLOCKHASHES {
let hash1 = H256::repeat_byte(i as u8);
blockhash_queue.insert_hash(hash1)
}
for (i, hash) in blockhash_queue.get_hashes()[..MAX_EVM_BLOCKHASHES]
.iter()
.enumerate()
{
let hash1 = H256::repeat_byte(i as u8);
assert_eq!(*hash, hash1)
}
}
}