Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
01f5f62
feat: verify batch tx list, tx headers, and input-notes commitment
claude Jun 1, 2026
79967d6
test: drive batch-kernel rejection tests through BatchExecutor advice…
claude Jun 1, 2026
feeb1cc
style: use the '# =>' stack-comment convention in the batch kernel masm
claude Jun 1, 2026
622bee3
feat: compute the nullifier-sorted batch INPUT_NOTES_COMMITMENT
claude Jun 3, 2026
fc74536
feat: erase intra-batch created-and-consumed notes from the batch inp…
claude Jun 3, 2026
8746a94
fix: reject same-transaction create+consume via the erasure ordering …
claude Jun 3, 2026
0e9e806
test: assert concrete batch-kernel errors in the binding/erasure nega…
claude Jun 3, 2026
a4f8bf5
docs: describe final-state behavior in batch-kernel comments
claude Jun 3, 2026
6304eef
feat: bound batch note/tx counts and codegen sentinel keys
claude Jun 3, 2026
92502cf
test: harden batch verifier/output tests from review
claude Jun 4, 2026
f85ebac
docs: clarify batch verifier warning on self-reported BATCH_ID
claude Jun 4, 2026
e295049
refactor: address PR review comments
claude Jun 4, 2026
d62e9e7
refactor: simplify tampered-advice test parametrization
claude Jun 4, 2026
623d1cf
docs: address MASM review comments on memory/prologue
claude Jun 4, 2026
174ed7e
refactor: split batch kernel into prologue / note-tracking / epilogue
claude Jun 4, 2026
c1496dd
perf: fold per-input epilogue assertions into the commitment pass
claude Jun 4, 2026
4ee8a17
refactor: derive batch note-list keys from a domain message
claude Jun 4, 2026
0a80c7c
Merge branch 'next' into mmagician-claude/batch-kernel-logic
claude Jun 12, 2026
08cd5ac
refactor: address PR review on batch kernel docs and note-list keys
claude Jun 12, 2026
91a2e3b
refactor: derive note-list keys via MASM word(...) const
claude Jun 12, 2026
4abe140
docs: clarify note-tracking binding in note_tracker.masm
claude Jun 12, 2026
af662e6
refactor: name note lists by sort key
claude Jun 12, 2026
65158db
refactor: simplify epilogue hashing and trim key comments
claude Jun 12, 2026
74a4b8b
refactor(batch-kernel): drop dead scratch bookkeeping, clarify docs
claude Jun 12, 2026
9f4f2f8
perf(batch-kernel): absorb input entries in runs, drop unreachable as…
claude Jun 12, 2026
c6178b7
test(batch): cover capacity limits and duplicate note marking
claude Jun 12, 2026
d502cc6
Revert "perf(batch-kernel): absorb input entries in runs, drop unreac…
claude Jun 12, 2026
5bde6bf
test(batch): drive forged-batch-id tests through BatchExecutor
claude Jun 12, 2026
bf4eedd
refactor(batch): keep testing overrides out of BatchExecutor::execute
claude Jun 12, 2026
8d18cb2
refactor(batch): take caller advice as a parameter, mirroring the tx …
claude Jun 12, 2026
ef594bb
docs: strip self-evident and overly verbose comments
claude Jun 12, 2026
df9ee18
fix: address review comments
claude Jun 12, 2026
5df0369
Merge branch 'next' into mmagician-claude/batch-kernel-logic
mmagician Jun 12, 2026
273fdf0
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude Jun 30, 2026
941d204
feat(batch): surface unsupported batches early in BatchExecutor
claude Jul 1, 2026
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
- [BREAKING] Replaced the `BurnNote` marker type and its `BurnNote::create` factory with a struct built via a `bon` typestate builder (`BurnNote::builder()`); a `BurnNote` converts into a `Note` via `Note::from` ([#2283](https://github.com/0xMiden/protocol/issues/2283)).
- [BREAKING] Replaced the `P2ideNote` marker type and its `P2ideNote::create` factory with a `P2ideNote` struct built via a `bon` typestate builder (`P2ideNote::builder()`). P2IDE notes must now carry at least one asset; the optional `reclaim_height`/`timelock_height` are set via the builder, `P2ideNoteStorage::into_recipient` is now infallible, and a `P2ideNote` converts into a `Note` via `Note::from` ([#2283](https://github.com/0xMiden/protocol/issues/2283)).
- Added a skeleton batch kernel ([#1122](https://github.com/0xMiden/protocol/issues/1122)) wired through `LocalBatchProver::prove` and attached to `ProvenBatch` as an `ExecutionProof`. It does not yet perform any verification.
- The batch kernel verifies the transaction list against `BATCH_ID` and computes the nullifier-sorted `INPUT_NOTES_COMMITMENT` matching `ProposedBatch::input_notes().commitment()` ([#2905](https://github.com/0xMiden/protocol/pull/2905)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entry may need to call out the BatchExecutor::execute signature change too. It now requires caller AdviceInputs, so downstream code using execute(batch) will stop compiling.


- [BREAKING] Renamed `AccountStorageDelta` to `AccountStoragePatch` ([#3002](https://github.com/0xMiden/protocol/pull/3002)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this line duplicates the next changelog entry. The next one also includes #3154, so this shorter copy can probably be dropped.

- [BREAKING] Renamed `AccountStorageDelta` to `AccountStoragePatch` ([#3002](https://github.com/0xMiden/protocol/pull/3002), [#3154](https://github.com/0xMiden/protocol/pull/3154)).
- [BREAKING] Replaced the per-tree account and nullifier backend traits with shared `SmtBackend` and `SmtBackendReader` traits, split into read-only and read-write capabilities, enabling read-only `LargeSmt`-backed tree views via `reader()` ([#2755](https://github.com/0xMiden/protocol/pull/2755), [#3009](https://github.com/0xMiden/protocol/pull/3009)).
- [BREAKING] Block validator signatures are now verified against the validator key committed to by the parent block, enabling safe validator key rotation. `BlockHeader::validator_key` now denotes the signer of the *next* block, `ProvenBlock`/`SignedBlock` `new` no longer verify the signature (pass the parent header to `validate` to authenticate a block against its parent's validator key), and `ProposedBlock` serialization gained a trailing `next_validator_key` field ([#3030](https://github.com/0xMiden/protocol/pull/3030)).
Expand Down
162 changes: 162 additions & 0 deletions crates/miden-protocol/asm/kernels/batch/lib/epilogue.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use miden::core::crypto::hashes::poseidon2

use miden::batch_kernel::memory
use miden::batch_kernel::memory::NOTE_ENTRY_FELT_LEN
use miden::batch_kernel::memory::INPUT_NOTE_CONSUMPTION_OFFSET
use miden::batch_kernel::memory::OUTPUT_NOTE_IS_CREATED_OFFSET

# ERRORS
# =================================================================================================

const ERR_BATCH_INPUT_NOTE_NOT_CONSUMED="an input-note list entry was not consumed by any transaction"

const ERR_BATCH_OUTPUT_NOTE_NOT_CREATED="an output-note list entry was not created by any transaction"

const ERR_BATCH_NOTE_CONSUMED_BEFORE_CREATED="an erased input note was consumed before the transaction that creates it"

# ASSERTIONS
# =================================================================================================

#! Asserts every output-note list entry was created by exactly one transaction (with the per-tx
#! binding, this proves the list is exactly the union of the per-transaction output notes, so a host
#! cannot fabricate an erasure with a note no transaction creates).
#!
#! The matching input-note checks (every entry consumed exactly once, none left pending-erasure) are
#! folded into the single pass in [`compute_input_notes_commitment`].
#!
#! Inputs: []
#! Outputs: []
proc assert_all_output_notes_created
exec.memory::get_num_output_notes
push.0
dup.1 dup.1 neq
while.true
dup exec.memory::output_note_flags_ptr add.OUTPUT_NOTE_IS_CREATED_OFFSET mem_load
assert.err=ERR_BATCH_OUTPUT_NOTE_NOT_CREATED
add.1
dup.1 dup.1 neq
end
drop drop
end

# INPUT NOTES COMMITMENT
# =================================================================================================

#! Absorbs input-note list entry `idx`'s 8-felt `(NULLIFIER, NOTE_ID_OR_EMPTY)` tuple into the batch
#! hasher state held in memory (overwrite-mode poseidon2, matching `Hasher::hash_elements`).
#!
#! Inputs: [idx]
#! Outputs: []
proc absorb_input_entry
# The entry occupies one double word [entry_ptr, entry_ptr + NOTE_ENTRY_FELT_LEN).
exec.memory::input_note_entry_ptr
# => [entry_ptr]
dup add.NOTE_ENTRY_FELT_LEN swap
# => [entry_ptr, end_ptr]
exec.memory::load_batch_hasher_state
# => [RATE0, RATE1, CAPACITY, entry_ptr, end_ptr]
exec.poseidon2::absorb_double_words_from_memory
# => [RATE0', RATE1', CAPACITY', end_ptr, end_ptr]
exec.memory::save_batch_hasher_state
# => [end_ptr, end_ptr]
drop drop
end
Comment thread
mmagician marked this conversation as resolved.

#! Computes INPUT_NOTES_COMMITMENT as the sequential poseidon2 hash of the non-erased
#! `(NULLIFIER, NOTE_ID_OR_EMPTY)` entries of the nullifier-sorted input-note list (entries with
#! erasure flag == 2 are skipped).
#!
#! The single pass over the input entries also enforces the epilogue invariants on each entry: it
#! was consumed exactly once (consumption == 1) and is not left expected-to-be-erased (erasure != 1,
#! i.e. any created-and-consumed note had its creator processed).
#!
#! Inputs: []
#! Outputs: [INPUT_NOTES_COMMITMENT]
proc compute_input_notes_commitment
# Initialize the batch hasher state in memory.
exec.poseidon2::init_no_padding
exec.memory::save_batch_hasher_state

exec.memory::get_num_input_notes
push.0 push.0
# => [idx, absorbed_count, num]
dup dup.3 neq
# => [should_loop, idx, absorbed_count, num]
while.true
# Assert this entry was consumed exactly once and is not left expected-to-be-erased.
dup exec.memory::input_note_flags_ptr
# => [flags_ptr, idx, absorbed_count, num]
dup add.INPUT_NOTE_CONSUMPTION_OFFSET mem_load
# => [consumption, flags_ptr, idx, absorbed_count, num]
assert.err=ERR_BATCH_INPUT_NOTE_NOT_CONSUMED
# => [flags_ptr, idx, absorbed_count, num]
mem_load
# => [erasure, idx, absorbed_count, num]
dup neq.1 assert.err=ERR_BATCH_NOTE_CONSUMED_BEFORE_CREATED
# => [erasure, idx, absorbed_count, num]

# Absorb the entry unless it was erased (created-and-consumed in this batch).
neq.2
# => [not_erased, idx, absorbed_count, num]
if.true
dup exec.absorb_input_entry
swap add.1 swap
# => [idx, absorbed_count+1, num]
end
add.1
dup dup.3 neq
# => [should_loop, idx, absorbed_count, num]
end
# => [idx, absorbed_count, num]
drop swap drop
# => [absorbed_count]

# With no non-erased entries the commitment is the empty word, matching the early return in
# `build_input_note_commitment` (this is not the hash of zero elements). Otherwise squeeze the
# accumulated state.
eq.0
if.true
padw
# => [EMPTY_WORD]
else
exec.memory::load_batch_hasher_state
exec.poseidon2::squeeze_digest
# => [INPUT_NOTES_COMMITMENT]
end
Comment thread
mmagician marked this conversation as resolved.
end

# OUTPUT NOTES COMMITMENT
# =================================================================================================

#! Computes the batch's output-notes commitment (the batch note tree root).
#!
#! Placeholder: returns the empty word until the batch note tree is wired up.
#!
#! Inputs: []
#! Outputs: [OUTPUT_NOTES_COMMITMENT]
#!
#! TODO: hash the batch's output notes into the batch note tree (SMT) root.
proc compute_output_notes_commitment
padw
end

# EPILOGUE
# =================================================================================================

#! Verifies the note-tracking results and computes the batch's note commitments.
#!
#! Asserts every output-note list entry was created, then computes the input- and output-notes
#! commitments. The per-input-entry invariants (consumed exactly once, no pending erasure) are
#! enforced inside the input-commitment pass.
#!
#! Inputs: []
#! Outputs: [INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT]
#!
#! TODO: authenticate unauthenticated, non-erased input notes against BLOCK_COMMITMENT's chain MMR.
pub proc finalize
exec.assert_all_output_notes_created
exec.compute_output_notes_commitment
# => [OUTPUT_NOTES_COMMITMENT]
exec.compute_input_notes_commitment
# => [INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT]
end
Loading
Loading