use alloc::string::ToString;
use miden_crypto::merkle::{
InnerNodeInfo,
LeafIndex,
SMT_DEPTH,
SmtLeaf,
SmtProof,
SmtProofError,
SparseMerklePath,
};
use crate::account::AccountId;
use crate::block::account_tree::{account_id_to_smt_key, smt_key_to_account_id};
use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
use crate::{AccountTreeError, Word};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountWitness {
id: AccountId,
commitment: Word,
path: SparseMerklePath,
}
impl AccountWitness {
pub fn new(
account_id: AccountId,
commitment: Word,
path: SparseMerklePath,
) -> Result<Self, AccountTreeError> {
if path.depth() != SMT_DEPTH {
return Err(AccountTreeError::WitnessMerklePathDepthDoesNotMatchAccountTreeDepth(
path.depth() as usize,
));
}
Ok(Self::new_unchecked(account_id, commitment, path))
}
pub(super) fn from_smt_proof(requested_account_id: AccountId, proof: SmtProof) -> Self {
let witness_id = match proof.leaf() {
SmtLeaf::Empty(_) => requested_account_id,
SmtLeaf::Single((key_in_leaf, _)) => {
smt_key_to_account_id(*key_in_leaf)
},
SmtLeaf::Multiple(_) => {
unreachable!("account tree should only contain zero or one entry per ID prefix")
},
};
let commitment = proof
.get(&account_id_to_smt_key(witness_id))
.expect("we should have received a proof for the witness key");
debug_assert_eq!(proof.path().depth(), SMT_DEPTH);
AccountWitness::new_unchecked(witness_id, commitment, proof.into_parts().0)
}
pub(super) fn new_unchecked(
account_id: AccountId,
commitment: Word,
path: SparseMerklePath,
) -> Self {
Self { id: account_id, commitment, path }
}
pub fn id(&self) -> AccountId {
self.id
}
pub fn state_commitment(&self) -> Word {
self.commitment
}
pub fn path(&self) -> &SparseMerklePath {
&self.path
}
pub fn leaf(&self) -> SmtLeaf {
if self.commitment == Word::empty() {
let leaf_idx = LeafIndex::from(account_id_to_smt_key(self.id));
SmtLeaf::new_empty(leaf_idx)
} else {
let key = account_id_to_smt_key(self.id);
SmtLeaf::new_single(key, self.commitment)
}
}
pub fn into_proof(self) -> SmtProof {
let leaf = self.leaf();
debug_assert_eq!(self.path.depth(), SMT_DEPTH);
SmtProof::new(self.path, leaf)
.expect("merkle path depth should be the SMT depth by construction")
}
pub fn authenticated_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> + '_ {
let leaf = self.leaf();
self.path()
.authenticated_nodes(leaf.index().value(), leaf.hash())
.expect("leaf index is u64 and should be less than 2^SMT_DEPTH")
}
}
impl From<AccountWitness> for SmtProof {
fn from(witness: AccountWitness) -> Self {
witness.into_proof()
}
}
impl Serializable for AccountWitness {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.id.write_into(target);
self.commitment.write_into(target);
self.path.write_into(target);
}
}
impl Deserializable for AccountWitness {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let id = AccountId::read_from(source)?;
let commitment = Word::read_from(source)?;
let path = SparseMerklePath::read_from(source)?;
if path.depth() != SMT_DEPTH {
return Err(DeserializationError::InvalidValue(
SmtProofError::InvalidMerklePathLength(path.depth() as usize).to_string(),
));
}
Ok(Self { id, commitment, path })
}
}