Skip to content
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
48 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
a259c82
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude Jul 2, 2026
dc7558f
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude Aug 10, 2026
d7973e8
chore: address review comments (#3615)
zeapoz Aug 17, 2026
43ffbc1
Update crates/miden-protocol/asm/kernels/batch/lib/epilogue.masm
mmagician Aug 18, 2026
0914d80
pull out shared errors into errors.masm
claude Aug 18, 2026
f94484c
style(batch-kernel): format constants to match repo conventions
claude Aug 18, 2026
150bc93
style(batch-kernel): wrap comment lines exceeding 100 columns
claude Aug 18, 2026
b9194f6
Merge branch 'next' into mmagician-claude/batch-kernel-logic
mmagician Aug 18, 2026
ff1f7ec
docs: fix batch kernel changelog entry
claude Aug 18, 2026
23eba90
feat(batch): reject kernel outputs that do not match the proposed batch
claude Aug 18, 2026
0355fc3
style(batch-kernel): address MASM formatting and style review nits
claude Aug 20, 2026
97138b2
style(batch-kernel): apply review formatting conventions across all m…
claude Aug 21, 2026
e571bb4
style(batch-kernel): address review comments on the formatting sweep
claude Aug 21, 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features

- [BREAKING] Implemented partial batch kernel verification to check the transaction list against `BATCH_ID` and compute `INPUT_NOTES_COMMITMENT`; `BatchExecutor::execute` now additionally takes caller `AdviceInputs` ([#2905](https://github.com/0xMiden/protocol/pull/2905)).

### Changes

- [BREAKING] Moved the internal shared helpers of `miden::protocol::input_note`, `miden::protocol::active_note`, and the note memory-write helpers into private `input_note_internal` and `note_internal` modules ([#3501](https://github.com/0xMiden/protocol/pull/3501)).
Expand Down
184 changes: 184 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,184 @@
use miden::core::crypto::hashes::poseidon2

use miden::batch_kernel::memory
use {
INPUT_NOTE_CONSUMPTION_OFFSET,
NOTE_ENTRY_FELT_LEN,
OUTPUT_NOTE_IS_CREATED_OFFSET,
} from miden::batch_kernel::memory
use {ERR_BATCH_NOTE_CONSUMED_BEFORE_CREATED} from miden::batch_kernel::errors

# 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"

# 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
Comment thread
zeapoz marked this conversation as resolved.
exec.memory::get_num_output_notes
# => [num_output_notes]
push.0
# => [idx, num_output_notes]
dup.1 dup.1 neq
# => [should_loop, idx, num_output_notes]
Comment thread
mmagician marked this conversation as resolved.
Outdated
while.true
dup exec.memory::output_note_flags_ptr add.OUTPUT_NOTE_IS_CREATED_OFFSET mem_load
# => [is_created, idx, num_output_notes]
assert.err=ERR_BATCH_OUTPUT_NOTE_NOT_CREATED
# => [idx, num_output_notes]
add.1
# => [idx+1, num_output_notes]
dup.1 dup.1 neq
# => [should_loop, idx+1, num_output_notes]
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
Comment on lines +57 to +64

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.

In this procedure doc comment, I'd make it more obvious that NULLIFIER, NOTE_ID_OR_EMPTY is part of the type signature of this procedure, but passed in via memory.

Also applies to the the output, which is the hash in memory.

# => [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
Comment on lines +67 to +72

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.

nit: I'd maybe keep the hasher state on the stack even if it pollutes the inputs/outputs more. If we want to keep memory, I would use local memory of compute_input_notes_commitment here because this doesn't need to be in global memory, same as the scratch space.

# => [end_ptr, end_ptr]
drop drop
end

#! Processes one input-note list entry: asserts the entry was consumed exactly once and is not
#! left expected-to-be-erased, then absorbs it into the batch hasher unless it was erased.
#!
#! The absorbed_count is only incremented by 1 if the entry was not erased (erasure != 2).
#!
#! Inputs: [idx, absorbed_count, num]
#! Outputs: [idx + 1, absorbed_count, num]
proc process_input_entry
# 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

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.

Can we ever actually run into this assertion? Wouldn't all of these be caught by assert_input_not_consumed_before_created already?

# => [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

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.

nit: I think eq.0 (or eq.UNERASED) would avoid the negation and be clearer.

dup exec.absorb_input_entry
swap add.1 swap
# => [idx, absorbed_count+1, num]
end
add.1
end
Comment thread
mmagician marked this conversation as resolved.
Comment on lines +105 to +106

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.

Suggested change
add.1
end
add.1
# => [idx + 1, absorbed_count, num]
end


#! 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
Comment thread
zeapoz marked this conversation as resolved.
exec.process_input_entry
# => [idx+1, absorbed_count, num] (absorbed_count + 1 if entry was not erased)

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.

Suggested change
# => [idx+1, absorbed_count, num] (absorbed_count + 1 if entry was not erased)
# => [idx+1, absorbed_count, num]

nit: that's the concern of process_input_entry

dup dup.3 neq
# => [should_loop, idx+1, 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.
Comment on lines +139 to +150

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.

We don't need this, it's sufficient to

exec.memory::load_batch_hasher_state
exec.poseidon2::squeeze_digest

I think this means also that we can remove absorbed_count completely.

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
7 changes: 7 additions & 0 deletions crates/miden-protocol/asm/kernels/batch/lib/errors.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Error constants shared by batch kernel modules.

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

pub const ERR_BATCH_NOTE_CONSUMED_BEFORE_CREATED =
"an erased input note was consumed before the transaction that created it"
Loading
Loading