use crate::prelude::*;
use snarkvm_algorithms::{
merkle_tree::{MerklePath, MerkleTree},
prelude::*,
};
use snarkvm_utilities::has_duplicates;
use anyhow::{anyhow, Result};
use std::{collections::HashMap, sync::Arc};
#[derive(Clone, Derivative)]
#[derivative(Debug(bound = "N: Network"))]
pub struct LedgerTree<N: Network> {
#[derivative(Debug = "ignore")]
tree: Arc<MerkleTree<N::LedgerRootParameters>>,
block_hashes: HashMap<N::BlockHash, u32>,
current_index: u32,
}
impl<N: Network> LedgerTreeScheme<N> for LedgerTree<N> {
fn new() -> Result<Self> {
Ok(Self {
tree: Arc::new(MerkleTree::<N::LedgerRootParameters>::new::<N::BlockHash>(
Arc::new(N::ledger_root_parameters().clone()),
&[],
)?),
block_hashes: Default::default(),
current_index: 0,
})
}
fn add(&mut self, block_hash: &N::BlockHash) -> Result<u32> {
if self.contains_block_hash(block_hash) {
return Err(MerkleError::Message(format!("{} already exists in the ledger tree", block_hash)).into());
}
self.tree = Arc::new(self.tree.rebuild(self.current_index as usize, &[block_hash])?);
self.block_hashes.insert(*block_hash, self.current_index);
let current_index = self.current_index;
self.current_index = current_index
.checked_add(1)
.ok_or_else(|| anyhow!("The index exceeds the maximum number of allowed block hashes."))?;
Ok(current_index)
}
fn add_all(&mut self, block_hashes: &[N::BlockHash]) -> Result<(u32, u32)> {
if block_hashes.is_empty() {
return Err(anyhow!("The list of given block hashes must be non-empty"));
}
if has_duplicates(block_hashes.iter()) {
return Err(anyhow!("The list of given block hashes contains duplicates"));
}
if block_hashes.iter().any(|c| self.contains_block_hash(c)) {
return Err(anyhow!("The list of given block hashes contains duplicates"));
}
let start_index = self.current_index;
let num_block_hashes = block_hashes.len();
if (self.current_index as usize).saturating_add(num_block_hashes) > u32::MAX as usize {
return Err(anyhow!("The ledger tree will reach its maximum size."));
}
self.tree = match self.current_index {
0 => Arc::new(MerkleTree::<N::LedgerRootParameters>::new::<N::BlockHash>(
Arc::new(N::ledger_root_parameters().clone()),
block_hashes,
)?),
_ => Arc::new(self.tree.rebuild(self.current_index as usize, block_hashes)?),
};
self.block_hashes.extend(
block_hashes.iter().enumerate().map(|(index, block_hash)| (*block_hash, start_index + index as u32)),
);
self.current_index = self
.current_index
.checked_add(num_block_hashes as u32)
.ok_or_else(|| anyhow!("The index exceeds the maximum number of allowed block hashes."))?;
let end_index = self.current_index.checked_sub(1).ok_or_else(|| anyhow!("Integer underflow."))?;
Ok((start_index, end_index))
}
fn contains_block_hash(&self, block_hash: &N::BlockHash) -> bool {
self.block_hashes.contains_key(block_hash)
}
fn get_block_hash_index(&self, block_hash: &N::BlockHash) -> Option<&u32> {
self.block_hashes.get(block_hash)
}
fn root(&self) -> N::LedgerRoot {
(*self.tree.root()).into()
}
fn to_ledger_inclusion_proof(&self, block_hash: &N::BlockHash) -> Result<MerklePath<N::LedgerRootParameters>> {
match self.get_block_hash_index(block_hash) {
Some(index) => Ok(self.tree.generate_proof(*index as usize, block_hash)?),
_ => Err(MerkleError::MissingLeaf(format!("{}", block_hash)).into()),
}
}
}
impl<N: Network> Default for LedgerTree<N> {
fn default() -> Self {
Self::new().unwrap()
}
}