Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0817f7d
feat: Implement account ID tree
PhilippGackstatter Mar 28, 2025
d86fad9
feat: Add `AccountTree::with_entries`
PhilippGackstatter Mar 28, 2025
0376218
feat: Add `PartialAccountTree`
PhilippGackstatter Apr 3, 2025
95d34e9
feat: Add `AccountMutationSet`
PhilippGackstatter Apr 3, 2025
a79544f
feat: Use uniqueness guarantee in account witness
PhilippGackstatter Apr 3, 2025
49e6134
Merge remote-tracking branch 'origin/next' into pgackst-account-tree
PhilippGackstatter Apr 3, 2025
83335ef
chore: Add `as_mutation_set`
PhilippGackstatter Apr 3, 2025
d89a74d
chore: Remove `LeafIndex` conversion on account ID
PhilippGackstatter Apr 3, 2025
c181bed
chore: Add changelog entry
PhilippGackstatter Apr 3, 2025
f65413f
feat: Document (partial) account tree methods
PhilippGackstatter Apr 3, 2025
569bc03
fix: partial account tree doc link
PhilippGackstatter Apr 3, 2025
b18b559
feat: Enforce account ID prefix validity in account witness
PhilippGackstatter Apr 4, 2025
867ebb8
chore: Fix docs, add todos
PhilippGackstatter Apr 4, 2025
cddbb29
feat: Add num created notes and commitment on txs
PhilippGackstatter Apr 4, 2025
59e1586
chore: Make error messages consistent
PhilippGackstatter Apr 4, 2025
d4cf130
feat: Add id suffix to account witness and add prefix test
PhilippGackstatter Apr 4, 2025
a0103bf
feat: Refactor account witness to contain merkle path & commitment
PhilippGackstatter Apr 14, 2025
fab77a8
feat: Add tests for partial account tree
PhilippGackstatter Apr 14, 2025
44eaa46
feat: Simplify duplicate id check, impl same block dup test
PhilippGackstatter Apr 14, 2025
c7e8e04
Merge remote-tracking branch 'origin/next' into pgackst-account-tree
PhilippGackstatter Apr 14, 2025
f62a18c
feat: Add `AccountWitness::new`
PhilippGackstatter Apr 15, 2025
8b3d858
chore: Use AccountTree over SimpleSmt in tests
PhilippGackstatter Apr 15, 2025
83a5673
feat: Simplify account witness constructors
PhilippGackstatter Apr 15, 2025
557c7df
chore: No-std imports
PhilippGackstatter Apr 15, 2025
04f6931
fix: use commitment from witness account ID
PhilippGackstatter Apr 15, 2025
6e5d6e7
chore: Better doc comment for `AccountMutationSet`
PhilippGackstatter Apr 16, 2025
deb30cc
chore: Update docs on account witness
PhilippGackstatter Apr 16, 2025
696129b
feat: Deduplicate witness construction from proof
PhilippGackstatter Apr 17, 2025
be9222c
feat: Move compute_tx_commitment to `OrderedTransactionHeaders`
PhilippGackstatter Apr 17, 2025
aa350cf
fix: witness doc link
PhilippGackstatter Apr 17, 2025
9c71d44
fix: state img in book
PhilippGackstatter Apr 17, 2025
e19eb3b
chore: update method names
bobbinth Apr 18, 2025
cda1a42
Merge branch 'next' into pgackst-account-tree
bobbinth Apr 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Added getter for proof security level in `ProvenBatch` and `ProvenBlock` (#1259).
- [BREAKING] Replaced the `ProvenBatch::new_unchecked` with the `ProvenBatch::new` method to initialize the struct with validations (#1260).
- Added a retry strategy for worker's health check (#1255).
- Add `AccountTree` and `PartialAccountTree` wrappers and enforce ID prefix uniqueness (#1254).

## 0.8.1 (2025-03-26) - `miden-objects` and `miden-tx` crates only.

Expand Down
15 changes: 6 additions & 9 deletions crates/miden-block-prover/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use miden_crypto::merkle::MerkleError;
use miden_objects::{Digest, NullifierTreeError, account::AccountId};
use miden_objects::{AccountTreeError, Digest, NullifierTreeError};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum ProvenBlockError {
#[error("nullifier witness has a different root than the current nullifier tree root")]
NullifierWitnessRootMismatch(#[source] NullifierTreeError),

#[error(
"account witness for account {account_id} has a different root than the current account tree root"
)]
AccountWitnessRootMismatch {
account_id: AccountId,
source: MerkleError,
},
#[error("failed to track account witness")]
AccountWitnessTracking { source: AccountTreeError },

#[error("account ID prefix already exists in the tree")]
AccountIdPrefixDuplicate { source: AccountTreeError },

#[error(
"account tree root of the previous block header is {prev_block_account_root} but the root of the partial tree computed from account witnesses is {stale_account_root}, indicating that the witnesses are stale"
Expand Down
61 changes: 22 additions & 39 deletions crates/miden-block-prover/src/local_block_prover.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::{collections::BTreeMap, vec::Vec};

use miden_crypto::merkle::{LeafIndex, PartialMerkleTree};
use miden_lib::transaction::TransactionKernel;
use miden_objects::{
Digest, Word,
Digest,
account::AccountId,
block::{
AccountUpdateWitness, BlockAccountUpdate, BlockHeader, BlockNoteIndex, BlockNoteTree,
BlockNumber, NullifierWitness, OutputNoteBatch, PartialNullifierTree, ProposedBlock,
ProvenBlock,
BlockNumber, NullifierWitness, OutputNoteBatch, PartialAccountTree, PartialNullifierTree,
ProposedBlock, ProvenBlock,
},
note::Nullifier,
transaction::ChainMmr,
Expand Down Expand Up @@ -78,18 +77,13 @@ impl LocalBlockProver {

let block_num = proposed_block.block_num();
let timestamp = proposed_block.timestamp();
let tx_commitment = BlockHeader::compute_tx_commitment(
proposed_block
.transactions()
.map(|tx_header| (tx_header.id(), tx_header.account_id())),
);

// Split the proposed block into its parts.
// --------------------------------------------------------------------------------------------

let (
batches,
mut account_updated_witnesses,
account_updated_witnesses,
output_note_batches,
created_nullifiers,
chain_mmr,
Expand All @@ -115,7 +109,7 @@ impl LocalBlockProver {
// --------------------------------------------------------------------------------------------

let new_account_root =
compute_account_root(&mut account_updated_witnesses, &prev_block_header)?;
compute_account_root(&account_updated_witnesses, &prev_block_header)?;

// Insert the previous block header into the block chain MMR to get the new chain
// commitment.
Expand Down Expand Up @@ -144,6 +138,7 @@ impl LocalBlockProver {
// --------------------------------------------------------------------------------------------

let txs = batches.into_transactions();
let tx_commitment = txs.commitment();

// Construct the new block header.
// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -208,7 +203,7 @@ fn compute_nullifiers(
// its corresponding nullifier witness, so we don't have to check again whether they match.
for witness in created_nullifiers.into_values() {
partial_nullifier_tree
.add_nullifier_witness(witness)
.track_nullifier(witness)
.map_err(ProvenBlockError::NullifierWitnessRootMismatch)?;
}

Expand Down Expand Up @@ -248,34 +243,21 @@ fn compute_chain_commitment(mut chain_mmr: ChainMmr, prev_block_header: BlockHea
/// It uses a PartialMerkleTree for now while we use a SimpleSmt for the account tree. Once that is
/// updated to an Smt, we can use a PartialSmt instead.
fn compute_account_root(
updated_accounts: &mut Vec<(AccountId, AccountUpdateWitness)>,
updated_accounts: &[(AccountId, AccountUpdateWitness)],
prev_block_header: &BlockHeader,
) -> Result<Digest, ProvenBlockError> {
// If no accounts were updated, the account tree root is unchanged.
if updated_accounts.is_empty() {
return Ok(prev_block_header.account_root());
}

let mut partial_account_tree = PartialMerkleTree::new();

// First reconstruct the current account tree from the provided merkle paths.
for (account_id, witness) in updated_accounts.iter_mut() {
let account_leaf_index = LeafIndex::from(*account_id);
// Shouldn't the value in PartialMerkleTree::add_path be a Word instead of a Digest?
// PartialMerkleTree::update_leaf (below) takes a Word as a value, so this seems
// inconsistent.
partial_account_tree
.add_path(
account_leaf_index.value(),
witness.initial_state_commitment(),
// We don't need the merkle path later, so we can take it out.
core::mem::take(witness.initial_state_proof_mut()),
)
.map_err(|source| ProvenBlockError::AccountWitnessRootMismatch {
account_id: *account_id,
source,
})?;
}
// If a witness points to a leaf where multiple account IDs share the same prefix, this will
// return an error.
let mut partial_account_tree = PartialAccountTree::with_witnesses(
updated_accounts.iter().map(|(_, update_witness)| update_witness.to_witness()),
)
.map_err(|source| ProvenBlockError::AccountWitnessTracking { source })?;

// Check the account tree root in the previous block header matches the reconstructed tree's
// root.
Expand All @@ -288,13 +270,14 @@ fn compute_account_root(

// Second, update the account tree by inserting the new final account state commitments to
// compute the new root of the account tree.
// TODO: Move this loop into a method on `PartialAccountTree` once it is added.
for (account_id, witness) in updated_accounts {
let account_leaf_index = LeafIndex::from(*account_id);
partial_account_tree
.update_leaf(account_leaf_index.value(), Word::from(witness.final_state_commitment()))
.expect("every account leaf should have been inserted in the first loop");
}
// If an account ID's prefix already exists in the tree, this will return an error.
// Note that we have inserted all witnesses that we want to update into the partial account
// tree, so we should not run into the untracked key error.
partial_account_tree
.upsert_state_commitments(updated_accounts.iter().map(|(account_id, update_witness)| {
(*account_id, update_witness.final_state_commitment())
}))
.map_err(|source| ProvenBlockError::AccountIdPrefixDuplicate { source })?;

Ok(partial_account_tree.root())
}
Expand Down
Loading