-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implement
ProposedBlock::new
(partially)
- Loading branch information
1 parent
e82dee0
commit 65b6da8
Showing
7 changed files
with
691 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
use std::collections::BTreeMap; | ||
|
||
use crate::{ | ||
account::AccountId, | ||
block::BlockHeader, | ||
crypto::merkle::{MerklePath, SmtProof}, | ||
note::{NoteId, NoteInclusionProof, Nullifier}, | ||
transaction::ChainMmr, | ||
Digest, | ||
}; | ||
|
||
// BLOCK INPUTS | ||
// ================================================================================================ | ||
|
||
/// Information needed from the store to build a block | ||
#[derive(Clone, Debug)] | ||
pub struct BlockInputs { | ||
/// Previous block header | ||
prev_block_header: BlockHeader, | ||
|
||
/// MMR peaks for the current chain state | ||
chain_mmr: ChainMmr, | ||
|
||
/// The hashes of the requested accounts and their authentication paths | ||
accounts: BTreeMap<AccountId, AccountWitness>, | ||
|
||
/// The requested nullifiers and their authentication paths | ||
nullifiers: BTreeMap<Nullifier, SmtProof>, | ||
|
||
/// List of unauthenticated notes found in the store | ||
unauthenticated_note_proofs: BTreeMap<NoteId, NoteInclusionProof>, | ||
} | ||
|
||
impl BlockInputs { | ||
pub fn new( | ||
prev_block_header: BlockHeader, | ||
chain_mmr: ChainMmr, | ||
accounts: BTreeMap<AccountId, AccountWitness>, | ||
nullifiers: BTreeMap<Nullifier, SmtProof>, | ||
unauthenticated_note_proofs: BTreeMap<NoteId, NoteInclusionProof>, | ||
) -> Self { | ||
Self { | ||
prev_block_header, | ||
chain_mmr, | ||
accounts, | ||
nullifiers, | ||
unauthenticated_note_proofs, | ||
} | ||
} | ||
|
||
pub fn prev_block_header(&self) -> &BlockHeader { | ||
&self.prev_block_header | ||
} | ||
|
||
pub fn chain_mmr(&self) -> &ChainMmr { | ||
&self.chain_mmr | ||
} | ||
|
||
pub fn accounts(&self) -> &BTreeMap<AccountId, AccountWitness> { | ||
&self.accounts | ||
} | ||
|
||
pub fn accounts_mut(&mut self) -> &mut BTreeMap<AccountId, AccountWitness> { | ||
&mut self.accounts | ||
} | ||
|
||
pub fn nullifiers(&self) -> &BTreeMap<Nullifier, SmtProof> { | ||
&self.nullifiers | ||
} | ||
|
||
/// Takes the map of nullifiers to their proofs from the block inputs. | ||
/// | ||
/// This has the semantics of [`core::mem::take`], i.e. the nullifiers are set to an empty | ||
/// `BTreeMap` after this operation. | ||
pub fn take_nullifiers(&mut self) -> BTreeMap<Nullifier, SmtProof> { | ||
core::mem::take(&mut self.nullifiers) | ||
} | ||
|
||
pub fn unauthenticated_note_proofs(&self) -> &BTreeMap<NoteId, NoteInclusionProof> { | ||
&self.unauthenticated_note_proofs | ||
} | ||
|
||
pub fn into_parts( | ||
self, | ||
) -> ( | ||
BlockHeader, | ||
ChainMmr, | ||
BTreeMap<AccountId, AccountWitness>, | ||
BTreeMap<Nullifier, SmtProof>, | ||
BTreeMap<NoteId, NoteInclusionProof>, | ||
) { | ||
( | ||
self.prev_block_header, | ||
self.chain_mmr, | ||
self.accounts, | ||
self.nullifiers, | ||
self.unauthenticated_note_proofs, | ||
) | ||
} | ||
} | ||
|
||
// ACCOUNT WITNESS | ||
// ================================================================================================ | ||
|
||
#[derive(Clone, Debug, Default)] | ||
pub struct AccountWitness { | ||
initial_state_commitment: Digest, | ||
proof: MerklePath, | ||
} | ||
|
||
impl AccountWitness { | ||
pub fn new(initial_state_commitment: Digest, proof: MerklePath) -> Self { | ||
Self { initial_state_commitment, proof } | ||
} | ||
|
||
pub fn initial_state_commitment(&self) -> Digest { | ||
self.initial_state_commitment | ||
} | ||
|
||
pub fn proof(&self) -> &MerklePath { | ||
&self.proof | ||
} | ||
|
||
pub fn into_parts(self) -> (Digest, MerklePath) { | ||
(self.initial_state_commitment, self.proof) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use crate::{ | ||
block::BlockNumber, | ||
crypto::{ | ||
hash::rpo::RpoDigest, | ||
merkle::{MutationSet, Smt, SmtProof, SMT_DEPTH}, | ||
}, | ||
errors::NullifierTreeError, | ||
note::Nullifier, | ||
Felt, FieldElement, Word, | ||
}; | ||
|
||
/// Nullifier SMT. | ||
#[derive(Debug, Clone)] | ||
pub struct NullifierTree(Smt); | ||
|
||
impl NullifierTree { | ||
/// Construct new nullifier tree from list of items. | ||
pub fn with_entries( | ||
entries: impl IntoIterator<Item = (Nullifier, BlockNumber)>, | ||
) -> Result<Self, NullifierTreeError> { | ||
let leaves = entries | ||
.into_iter() | ||
.map(|(nullifier, block_num)| (nullifier.inner(), block_num_to_leaf_value(block_num))); | ||
|
||
let inner = Smt::with_entries(leaves).map_err(NullifierTreeError::CreationFailed)?; | ||
|
||
Ok(Self(inner)) | ||
} | ||
|
||
/// Returns the root of the nullifier SMT. | ||
pub fn root(&self) -> RpoDigest { | ||
self.0.root() | ||
} | ||
|
||
/// Returns an opening of the leaf associated with the given nullifier. | ||
pub fn open(&self, nullifier: &Nullifier) -> SmtProof { | ||
self.0.open(&nullifier.inner()) | ||
} | ||
|
||
/// Returns block number stored for the given nullifier or `None` if the nullifier wasn't | ||
/// consumed. | ||
pub fn get_block_num(&self, nullifier: &Nullifier) -> Option<BlockNumber> { | ||
let value = self.0.get_value(&nullifier.inner()); | ||
if value == Smt::EMPTY_VALUE { | ||
return None; | ||
} | ||
|
||
Some(Self::leaf_value_to_block_num(value)) | ||
} | ||
|
||
/// Computes mutations for the nullifier SMT. | ||
pub fn compute_mutations( | ||
&self, | ||
kv_pairs: impl IntoIterator<Item = (Nullifier, BlockNumber)>, | ||
) -> MutationSet<SMT_DEPTH, RpoDigest, Word> { | ||
self.0.compute_mutations( | ||
kv_pairs.into_iter().map(|(nullifier, block_num)| { | ||
(nullifier.inner(), block_num_to_leaf_value(block_num)) | ||
}), | ||
) | ||
} | ||
|
||
/// Applies mutations to the nullifier SMT. | ||
pub fn apply_mutations( | ||
&mut self, | ||
mutations: MutationSet<SMT_DEPTH, RpoDigest, Word>, | ||
) -> Result<(), NullifierTreeError> { | ||
self.0.apply_mutations(mutations).map_err(NullifierTreeError::MutationFailed) | ||
} | ||
|
||
// HELPER FUNCTIONS | ||
// -------------------------------------------------------------------------------------------- | ||
|
||
/// Given the leaf value of the nullifier SMT, returns the nullifier's block number. | ||
/// | ||
/// There are no nullifiers in the genesis block. The value zero is instead used to signal | ||
/// absence of a value. | ||
fn leaf_value_to_block_num(value: Word) -> BlockNumber { | ||
let block_num: u32 = | ||
value[0].as_int().try_into().expect("invalid block number found in store"); | ||
|
||
block_num.into() | ||
} | ||
} | ||
|
||
/// Returns the nullifier's leaf value in the SMT by its block number. | ||
pub(super) fn block_num_to_leaf_value(block: BlockNumber) -> Word { | ||
[Felt::from(block), Felt::ZERO, Felt::ZERO, Felt::ZERO] | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use miden_objects::{Felt, ZERO}; | ||
|
||
use super::NullifierTree; | ||
use crate::block::nullifier_tree::block_num_to_leaf_value; | ||
|
||
#[test] | ||
fn leaf_value_encoding() { | ||
let block_num = 123; | ||
let nullifier_value = block_num_to_leaf_value(block_num.into()); | ||
|
||
assert_eq!(nullifier_value, [Felt::from(block_num), ZERO, ZERO, ZERO]); | ||
} | ||
|
||
#[test] | ||
fn leaf_value_decoding() { | ||
let block_num = 123; | ||
let nullifier_value = [Felt::from(block_num), ZERO, ZERO, ZERO]; | ||
let decoded_block_num = NullifierTree::leaf_value_to_block_num(nullifier_value); | ||
|
||
assert_eq!(decoded_block_num, block_num.into()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
use miden_crypto::merkle::{PartialSmt, SmtProof}; | ||
use vm_core::{Word, EMPTY_WORD}; | ||
use vm_processor::Digest; | ||
|
||
use crate::{ | ||
block::{nullifier_tree::block_num_to_leaf_value, BlockNumber}, | ||
note::Nullifier, | ||
}; | ||
|
||
pub struct PartialNullifierTree(PartialSmt); | ||
|
||
impl PartialNullifierTree { | ||
pub const UNSPENT_NULLIFIER_VALUE: Word = EMPTY_WORD; | ||
|
||
pub fn new() -> Self { | ||
PartialNullifierTree(PartialSmt::new()) | ||
} | ||
|
||
pub fn add_nullifier_path(&mut self, nullifier: Nullifier, proof: SmtProof) { | ||
let (path, leaf) = proof.into_parts(); | ||
|
||
for (key, value) in leaf.into_entries() { | ||
// We only need to check that the nullifier is unspent, the other key-value pairs of the | ||
// leaf entries are unimportant here but still need to be added to the SMT to produce | ||
// the correct nullifier tree root. | ||
if key == nullifier.inner() && value != Self::UNSPENT_NULLIFIER_VALUE { | ||
todo!("error: nullifier is already spent") | ||
} | ||
|
||
self.0.add_path(key, value, path.clone()).expect("TODO: Error"); | ||
} | ||
} | ||
|
||
pub fn mark_spent(&mut self, nullifier: Nullifier, block_num: BlockNumber) { | ||
self.0.insert(nullifier.inner(), block_num_to_leaf_value(block_num)); | ||
} | ||
|
||
pub fn root(&self) -> Digest { | ||
self.0.root() | ||
} | ||
} |
Oops, something went wrong.