use alloc::collections::BTreeSet;
use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
use miden_crypto::merkle::{Forest, Mmr, MmrError, MmrPeaks, MmrProof, PartialMmr};
use miden_processor::DeserializationError;
use crate::Word;
use crate::block::BlockNumber;
#[derive(Debug, Clone)]
pub struct Blockchain {
mmr: Mmr,
}
impl Blockchain {
pub fn new() -> Self {
Self { mmr: Mmr::new() }
}
pub fn from_mmr_unchecked(mmr: Mmr) -> Self {
Self { mmr }
}
pub fn num_blocks(&self) -> u32 {
self.mmr.forest().num_leaves() as u32
}
pub fn chain_tip(&self) -> Option<BlockNumber> {
if self.num_blocks() == 0 {
return None;
}
Some(BlockNumber::from(self.num_blocks() - 1))
}
pub fn commitment(&self) -> Word {
self.peaks().hash_peaks()
}
pub fn peaks(&self) -> MmrPeaks {
self.mmr.peaks()
}
pub fn peaks_at(&self, checkpoint: BlockNumber) -> Result<MmrPeaks, MmrError> {
self.mmr.peaks_at(Forest::new(checkpoint.as_usize()))
}
pub fn open(&self, block: BlockNumber) -> Result<MmrProof, MmrError> {
self.mmr.open(block.as_usize())
}
pub fn open_at(
&self,
block: BlockNumber,
checkpoint: BlockNumber,
) -> Result<MmrProof, MmrError> {
self.mmr.open_at(block.as_usize(), Forest::new(checkpoint.as_usize()))
}
pub fn as_mmr(&self) -> &Mmr {
&self.mmr
}
pub fn partial_mmr_from_blocks(
&self,
blocks: &BTreeSet<BlockNumber>,
checkpoint: BlockNumber,
) -> Result<PartialMmr, MmrError> {
let peaks = self.peaks_at(checkpoint)?;
let mut partial_mmr = PartialMmr::from_peaks(peaks);
for block_num in blocks.iter() {
let leaf = self.mmr.get(block_num.as_usize())?;
let path = self.open_at(*block_num, checkpoint)?.merkle_path;
partial_mmr
.track(block_num.as_usize(), leaf, &path)
.expect("filling partial mmr with data from mmr should succeed");
}
Ok(partial_mmr)
}
pub fn push(&mut self, block_commitment: Word) {
self.mmr.add(block_commitment);
}
}
impl Default for Blockchain {
fn default() -> Self {
Self::new()
}
}
impl Serializable for Blockchain {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.mmr.write_into(target);
}
}
impl Deserializable for Blockchain {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let chain = Mmr::read_from(source)?;
Ok(Self::from_mmr_unchecked(chain))
}
}