Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Changes
- 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)).
Comment thread
mmagician marked this conversation as resolved.
Outdated

- [BREAKING] Renamed `AccountStorageDelta` to `AccountStoragePatch` ([#3002](https://github.com/0xMiden/protocol/pull/3002)).
Comment thread
mmagician marked this conversation as resolved.
Outdated
- [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)).
Expand Down
391 changes: 391 additions & 0 deletions crates/miden-protocol/asm/kernels/batch/lib/memory.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,391 @@
# MEMORY LAYOUT
# =================================================================================================
#
# Below is the memory layout used by the batch kernel:
#
# +-------------------+-------------------------+----------------------------------+
Comment thread
mmagician marked this conversation as resolved.
Outdated
# | Address range | Constant | Contents |
# +-------------------+-------------------------+----------------------------------+
# | 0 | NUM_TRANSACTIONS_PTR | num_transactions (1 felt). |
# | 4..8 | BATCH_HASHER_RATE0_PTR | RATE0 of the batch-level |
Comment thread
mmagician marked this conversation as resolved.
Outdated
# | | | poseidon2 hasher state. |
# | 8..12 | BATCH_HASHER_RATE1_PTR | RATE1 of the batch-level hasher. |
# | 12..16 | BATCH_HASHER_CAP_PTR | CAPACITY of the batch-level |
# | | | hasher. |
# | 16 | SCRATCH_WORDS_COUNT_PTR | num_words piped into |
# | | | TX_NOTES_SCRATCH_PTR for the |
# | | | current transaction. |
# | 17 | SCRATCH_WORD_INDEX_PTR | current iteration cursor for the |
# | | | scratch absorption loop. |
# | 18 | NUM_INPUT_NOTES_PTR | number of entries in the global |
# | | | input-note list. |
# | 19 | NUM_OUTPUT_NOTES_PTR | number of entries in the global |
# | | | output-note list. |
# | 20..8212 | TX_TUPLES_PTR | Layer 1 piped data: per |
# | | | transaction `[tx_id[4], |
Comment thread
mmagician marked this conversation as resolved.
Outdated
# | | | account_id_prefix, |
# | | | account_id_suffix, 0, 0]` |
# | | | (8 felts each, sized for up to |
# | | | 1024 transactions). |
# | 8212..32788 | TX_HEADERS_PTR | Layer 2 piped data: per |
# | | | transaction the felt sequence |
# | | | TransactionId::new hashes |
# | | | (24 felts each, sized for up to |
# | | | 1024 transactions). |
Comment thread
mmagician marked this conversation as resolved.
Outdated
# | 32788..40980 | TX_NOTES_SCRATCH_PTR | Per-transaction scratch space |
# | | | for Layer 3 / Layer 3' note |
# | | | data (overwritten each tx). |
# | 40980..49172 | INPUT_NOTES_PTR | Global input-note list, sorted |
# | | | by nullifier: `[NULLIFIER[4], |
# | | | NOTE_ID_OR_EMPTY[4]]` per note |
Comment thread
mmagician marked this conversation as resolved.
Outdated
# | | | (8 felts each, up to 1024). |
# | 49172..53268 | INPUT_NOTE_FLAGS_PTR | Parallel input-note flags: |
# | | | `[erasure, consumption, 0, 0]` |
# | | | per note (4 felts each). |
# | 53268..61460 | OUTPUT_NOTES_PTR | Global output-note list, sorted |
# | | | by note_id: `[NOTE_ID[4], 0,0, |
# | | | 0,0]` per note (8 felts each). |
# | 61460..65556 | OUTPUT_NOTE_FLAGS_PTR | Parallel output-note flags: |
# | | | `[will_be_erased, is_created, |
# | | | linked_input_index, 0]` per note |
# | | | (4 felts each). |
# +-------------------+-------------------------+----------------------------------+

# BOOK KEEPING
# =================================================================================================

#! Single-felt slot holding `num_transactions` after Layer 1 verification.
const NUM_TRANSACTIONS_PTR=0

# BATCH HASHER STATE
# =================================================================================================

#! Word holding the RATE0 portion of the batch-level poseidon2 hasher state.
const BATCH_HASHER_RATE0_PTR=4

#! Word holding the RATE1 portion of the batch-level poseidon2 hasher state.
const BATCH_HASHER_RATE1_PTR=8

#! Word holding the CAPACITY portion of the batch-level poseidon2 hasher state.
const BATCH_HASHER_CAP_PTR=12

# SCRATCH BOOKKEEPING
# =================================================================================================

#! Number of words piped into TX_NOTES_SCRATCH_PTR for the transaction whose Layer 3 / 3' is
#! being absorbed.
const SCRATCH_WORDS_COUNT_PTR=16

#! Iteration cursor (index in words) into TX_NOTES_SCRATCH_PTR for the absorption loop.
const SCRATCH_WORD_INDEX_PTR=17

#! Single-felt slot holding the number of entries in the global input-note list.
const NUM_INPUT_NOTES_PTR=18

#! Single-felt slot holding the number of entries in the global output-note list.
const NUM_OUTPUT_NOTES_PTR=19

# PIPED DATA REGIONS
# =================================================================================================

#! Base of the Layer 1 piped data region. Per transaction, 8 felts:
#! `[tx_id[4], account_id_prefix, account_id_suffix, 0, 0]`.
pub const TX_TUPLES_PTR=20

#! Number of felts each transaction occupies in TX_TUPLES_PTR.
const TX_TUPLE_FELT_LEN=8

#! Base of the Layer 2 piped data region. Per transaction, 24 felts:
#! `[INIT[4], FINAL[4], INPUT_NOTES_COMMITMENT[4], OUTPUT_NOTES_COMMITMENT[4], FEE_ASSET[8]]`.
#! This must match the felt-sequence layout of `TransactionId::new`.
const TX_HEADERS_PTR=8212

#! Number of felts each transaction occupies in TX_HEADERS_PTR.
const TX_HEADER_FELT_LEN=24

#! Felt offset within a transaction header where INPUT_NOTES_COMMITMENT starts.
const TX_HEADER_INPUT_NOTES_OFFSET=8

#! Felt offset within a transaction header where OUTPUT_NOTES_COMMITMENT starts.
const TX_HEADER_OUTPUT_NOTES_OFFSET=12

#! Per-transaction scratch space for Layer 3 note data, overwritten between iterations.
pub const TX_NOTES_SCRATCH_PTR=32788

# GLOBAL NOTE LISTS
# =================================================================================================

#! Base of the global input-note list, sorted by nullifier. Per note, an 8-felt sorted-array entry
#! `[NULLIFIER[4], NOTE_ID_OR_EMPTY[4]]`
#! where the KEY word is the nullifier (the `sorted_array` lookup key) and the VALUE word is the
#! note id (for unauthenticated notes) or the empty word. The VALUE word doubles as the second half
#! of the `(nullifier, note_id_or_empty)` tuple hashed into INPUT_NOTES_COMMITMENT.
pub const INPUT_NOTES_PTR=40980

#! Number of felts each note occupies in INPUT_NOTES_PTR / OUTPUT_NOTES_PTR (KEY word + VALUE word).
pub const NOTE_ENTRY_FELT_LEN=8

#! Base of the parallel input-note flags array. Per note, one word `[erasure, consumption, 0, 0]`.
#! `erasure` is 0 (not erased / external), 1 (expected to be erased, creator not yet seen) or 2
#! (erased, creator seen). `consumption` is 0 or 1.
Comment thread
mmagician marked this conversation as resolved.
Outdated
pub const INPUT_NOTE_FLAGS_PTR=49172

#! Number of felts each note occupies in the flag arrays (one word).
pub const NOTE_FLAGS_FELT_LEN=4

#! Felt offset of the `erasure` flag within an input-note flag word.
pub const INPUT_NOTE_ERASURE_OFFSET=0

#! Felt offset of the `consumption` flag within an input-note flag word.
pub const INPUT_NOTE_CONSUMPTION_OFFSET=1

#! Base of the global output-note list, sorted by note id. Per note, an 8-felt sorted-array entry
#! `[NOTE_ID[4], 0, 0, 0, 0]`
#! where the KEY word is the note id (the `sorted_array` lookup key); the VALUE word is unused.
pub const OUTPUT_NOTES_PTR=53268

#! Base of the parallel output-note flags array. Per note, one word
#! `[will_be_erased, is_created, linked_input_index, 0]`.
pub const OUTPUT_NOTE_FLAGS_PTR=61460

#! Felt offset of the `will_be_erased` flag within an output-note flag word.
pub const OUTPUT_NOTE_WILL_BE_ERASED_OFFSET=0

#! Felt offset of the `is_created` flag within an output-note flag word.
pub const OUTPUT_NOTE_IS_CREATED_OFFSET=1

#! Felt offset of the `linked_input_index` field within an output-note flag word.
pub const OUTPUT_NOTE_LINKED_INPUT_OFFSET=2

# CAPACITY LIMITS
# =================================================================================================

#! Maximum number of transactions in a batch. The Layer 1/2 piped-data regions are sized to hold
#! exactly this many transactions.
pub const MAX_TRANSACTIONS_PER_BATCH=1024

#! Maximum number of entries in a global note list and in a single transaction's note set. The
#! input/output note regions and the per-transaction scratch region each hold exactly this many
#! entries. Equals `MAX_INPUT_NOTES_PER_BATCH` = `MAX_OUTPUT_NOTES_PER_BATCH` in `src/constants.rs`.
pub const MAX_NOTES_PER_BATCH=1024

# NUM TRANSACTIONS
# =================================================================================================

#! Stores `num_transactions`.
#!
#! Inputs: [num_transactions]
#! Outputs: []
pub proc set_num_transactions
mem_store.NUM_TRANSACTIONS_PTR
end

#! Returns `num_transactions`.
#!
#! Inputs: []
#! Outputs: [num_transactions]
pub proc get_num_transactions
mem_load.NUM_TRANSACTIONS_PTR
end

# BATCH HASHER STATE
# =================================================================================================

#! Persists the batch hasher state from the operand stack into memory.
#!
#! Inputs: [RATE0, RATE1, CAPACITY]
#! Outputs: []
pub proc save_batch_hasher_state
mem_storew_le.BATCH_HASHER_RATE0_PTR dropw
mem_storew_le.BATCH_HASHER_RATE1_PTR dropw
mem_storew_le.BATCH_HASHER_CAP_PTR dropw
end

#! Loads the batch hasher state from memory onto the operand stack.
#!
#! Inputs: []
#! Outputs: [RATE0, RATE1, CAPACITY]
pub proc load_batch_hasher_state
padw mem_loadw_le.BATCH_HASHER_CAP_PTR
padw mem_loadw_le.BATCH_HASHER_RATE1_PTR
padw mem_loadw_le.BATCH_HASHER_RATE0_PTR
end

# SCRATCH BOOKKEEPING
# =================================================================================================

#! Stores the count (in words) of data piped into the per-transaction scratch.
#!
#! Inputs: [num_words]
#! Outputs: []
pub proc set_scratch_words_count
mem_store.SCRATCH_WORDS_COUNT_PTR
end

#! Returns the count (in words) of data piped into the per-transaction scratch.
#!
#! Inputs: []
#! Outputs: [num_words]
pub proc get_scratch_words_count
mem_load.SCRATCH_WORDS_COUNT_PTR
end

#! Stores the absorption iteration cursor (index in words).
#!
#! Inputs: [word_index]
#! Outputs: []
pub proc set_scratch_word_index
mem_store.SCRATCH_WORD_INDEX_PTR
end

#! Returns the absorption iteration cursor (index in words).
#!
#! Inputs: []
#! Outputs: [word_index]
pub proc get_scratch_word_index
mem_load.SCRATCH_WORD_INDEX_PTR
end

# TRANSACTION TUPLE / HEADER ACCESSORS
# =================================================================================================

#! Returns a pointer to transaction `tx_index`'s entry in TX_TUPLES_PTR.
#!
#! Inputs: [tx_index]
#! Outputs: [tx_tuple_ptr]
pub proc tx_tuple_ptr

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 would use get and set prefixes for all procedures here that fit this description. For consistency with the tx kernel.

mul.TX_TUPLE_FELT_LEN add.TX_TUPLES_PTR
end

#! Returns the verified `tx_id` for transaction `tx_index` (loaded from TX_TUPLES_PTR).
#!
#! Inputs: [tx_index]
#! Outputs: [TX_ID]
pub proc get_tx_id
exec.tx_tuple_ptr padw movup.4 mem_loadw_le
end
Comment thread
mmagician marked this conversation as resolved.

#! Returns a pointer to transaction `tx_index`'s entry in TX_HEADERS_PTR.
#!
#! Inputs: [tx_index]
#! Outputs: [tx_header_ptr]
pub proc tx_header_ptr
mul.TX_HEADER_FELT_LEN add.TX_HEADERS_PTR
end

#! Returns the verified per-transaction INPUT_NOTES_COMMITMENT for transaction `tx_index`.
#!
#! Inputs: [tx_index]
#! Outputs: [INPUT_NOTES_COMMITMENT_i]
pub proc get_tx_input_notes_commitment
exec.tx_header_ptr add.TX_HEADER_INPUT_NOTES_OFFSET padw movup.4 mem_loadw_le
end

#! Returns the verified per-transaction OUTPUT_NOTES_COMMITMENT for transaction `tx_index`.
#!
#! Inputs: [tx_index]
#! Outputs: [OUTPUT_NOTES_COMMITMENT_i]
pub proc get_tx_output_notes_commitment
exec.tx_header_ptr add.TX_HEADER_OUTPUT_NOTES_OFFSET padw movup.4 mem_loadw_le
end

# GLOBAL NOTE LIST ACCESSORS
# =================================================================================================

#! Stores the number of entries in the global input-note list.
#!
#! Inputs: [num_input_notes]
#! Outputs: []
pub proc set_num_input_notes
mem_store.NUM_INPUT_NOTES_PTR
end

#! Returns the number of entries in the global input-note list.
#!
#! Inputs: []
#! Outputs: [num_input_notes]
pub proc get_num_input_notes
mem_load.NUM_INPUT_NOTES_PTR
end

#! Stores the number of entries in the global output-note list.
#!
#! Inputs: [num_output_notes]
#! Outputs: []
pub proc set_num_output_notes
mem_store.NUM_OUTPUT_NOTES_PTR
end

#! Returns the number of entries in the global output-note list.
#!
#! Inputs: []
#! Outputs: [num_output_notes]
pub proc get_num_output_notes
mem_load.NUM_OUTPUT_NOTES_PTR
end

#! Returns the end pointer of the global input-note list (base + num_input_notes * 8), as required
#! by `sorted_array::find_key_value`.
#!
#! Inputs: []
#! Outputs: [input_notes_end_ptr]
pub proc get_input_notes_end_ptr
exec.get_num_input_notes mul.NOTE_ENTRY_FELT_LEN add.INPUT_NOTES_PTR
end

#! Returns the end pointer of the global output-note list (base + num_output_notes * 8).
#!
#! Inputs: []
#! Outputs: [output_notes_end_ptr]
pub proc get_output_notes_end_ptr
exec.get_num_output_notes mul.NOTE_ENTRY_FELT_LEN add.OUTPUT_NOTES_PTR
end

#! Returns a pointer to input-note entry `i` in INPUT_NOTES_PTR.
#!
#! Inputs: [i]
#! Outputs: [input_note_entry_ptr]
pub proc input_note_entry_ptr
mul.NOTE_ENTRY_FELT_LEN add.INPUT_NOTES_PTR
end

#! Returns a pointer to output-note entry `i` in OUTPUT_NOTES_PTR.
#!
#! Inputs: [i]
Comment thread
mmagician marked this conversation as resolved.
Outdated
#! Outputs: [output_note_entry_ptr]
pub proc output_note_entry_ptr
mul.NOTE_ENTRY_FELT_LEN add.OUTPUT_NOTES_PTR
end

#! Returns a pointer to input-note flag word `i` in INPUT_NOTE_FLAGS_PTR.
#!
#! Inputs: [i]
#! Outputs: [input_note_flags_ptr]
pub proc input_note_flags_ptr
mul.NOTE_FLAGS_FELT_LEN add.INPUT_NOTE_FLAGS_PTR
end

#! Returns a pointer to output-note flag word `i` in OUTPUT_NOTE_FLAGS_PTR.
#!
#! Inputs: [i]
#! Outputs: [output_note_flags_ptr]
pub proc output_note_flags_ptr
mul.NOTE_FLAGS_FELT_LEN add.OUTPUT_NOTE_FLAGS_PTR
end

#! Converts an input-note `key_ptr` (as returned by `sorted_array::find_key_value`) into its entry
#! index `(key_ptr - INPUT_NOTES_PTR) / 8`.
#!
#! Inputs: [key_ptr]
#! Outputs: [i]
pub proc input_entry_index_from_key_ptr
sub.INPUT_NOTES_PTR div.NOTE_ENTRY_FELT_LEN
end

#! Converts an output-note `key_ptr` into its entry index `(key_ptr - OUTPUT_NOTES_PTR) / 8`.
#!
#! Inputs: [key_ptr]
#! Outputs: [i]
pub proc output_entry_index_from_key_ptr
sub.OUTPUT_NOTES_PTR div.NOTE_ENTRY_FELT_LEN
end
Loading
Loading