Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1cbd5f9
feat: add skeleton batch kernel + ProvenBatch proof field
claude May 12, 2026
0411259
Apply suggestions from code review
mmagician May 27, 2026
ee2df53
chore(protocol): drop premature BATCH error category
claude May 27, 2026
c508740
refactor(protocol): order build_input_stack params to match stack layout
claude May 27, 2026
9ed96f7
refactor(batch-prover): remove unused proof_security_level accessor
claude May 27, 2026
52adb85
refactor(protocol): simplify batch output padding check
claude May 27, 2026
ee3f559
test(batch): reuse shared chain setup helpers
claude May 27, 2026
e3d15c5
docs: trim verbose batch kernel CHANGELOG entry
claude May 27, 2026
ba9af23
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude May 27, 2026
0d20126
Merge remote-tracking branch 'origin/next' into mmagician-claude/batc…
claude May 29, 2026
11f0760
refactor(protocol): name batch kernel inputs BATCH_ID and BLOCK_COMMI…
claude May 29, 2026
7a4afcb
refactor(protocol): use Felt::ZERO associated constant
claude May 29, 2026
26c044b
refactor(protocol): type batch kernel errors instead of stringifying
claude May 29, 2026
7c72675
test(batch): rename batch_kernel module to test_batch_kernel
claude May 29, 2026
b84ea28
chore: shorten comment
mmagician May 29, 2026
5e25d94
chore: fmt
mmagician May 29, 2026
d812d4d
refactor(batch-prover): split batch execution and proving
claude May 29, 2026
7f3a5df
Merge branch 'next' into mmagician-claude/batch-kernel-skeleton
mmagician May 29, 2026
fc7d855
Update crates/miden-tx-batch-prover/src/batch_executor.rs
mmagician May 29, 2026
79165e9
refactor(protocol): return a BatchOutput from parse_output_stack
claude May 29, 2026
e9c34d3
refactor(protocol): put BLOCK_COMMITMENT on top of the batch input stack
claude Jun 1, 2026
763a6a7
refactor(protocol): name the batch output BATCH_NOTE_TREE_ROOT
claude Jun 1, 2026
9c91b70
refactor(batch): strong-type build_input_stack batch_id param
claude Jun 1, 2026
727ee9b
refactor(batch): rename BatchOutput to BatchOutputs
claude Jun 1, 2026
eaa71d4
refactor(batch): move output parsing onto BatchOutputs and return errors
claude Jun 1, 2026
473d20a
refactor(batch): expose typed BatchOutputs from ExecutedBatch
claude Jun 1, 2026
5d1b7d7
test(batch): cover BatchOutputs::parse error branches
claude Jun 1, 2026
30fb74a
Apply suggestion from @mmagician
mmagician Jun 1, 2026
59c2efb
feat: verify batch tx list, tx headers, and input-notes commitment
claude Jun 1, 2026
be707bc
test: drive batch-kernel rejection tests through BatchExecutor advice…
claude Jun 1, 2026
093ac8e
style: use the '# =>' stack-comment convention in the batch kernel masm
claude Jun 1, 2026
b46a277
feat: emit batch_expiration_block_num as the running-min over transac…
claude Jun 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 @@ -3,6 +3,9 @@
## v0.16.0 (TBD)

### 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 now verifies the batch's transaction list against its `BATCH_ID`, verifies each transaction header, and computes the batch `INPUT_NOTES_COMMITMENT` ([#2905](https://github.com/0xMiden/protocol/pull/2905)).
- The batch kernel now emits `batch_expiration_block_num`, the running minimum of its transactions' expiration block numbers, cross-checked against the proposed batch ([#3019](https://github.com/0xMiden/protocol/pull/3019)).

- [BREAKING] Renamed `AccountStorageDelta` to `AccountStoragePatch` ([#3002](https://github.com/0xMiden/protocol/pull/3002)).
- [BREAKING] Extracted `NullifierTreeBackendReader` and `AccountTreeBackendReader` traits from existing `NullifierTreeBackend` and `AccountTreeBackend` traits ([#2755](https://github.com/0xMiden/protocol/pull/2755)).
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

234 changes: 234 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,234 @@
# MEMORY LAYOUT
# =================================================================================================
#
# Below is the memory layout used by the batch kernel:
#
# +-------------------+-------------------------+----------------------------------+
# | Address range | Constant | Contents |
# +-------------------+-------------------------+----------------------------------+
# | 0 | NUM_TRANSACTIONS_PTR | num_transactions (1 felt). |
# | 1 | BATCH_EXPIRATION_PTR | batch_expiration_block_num, |
# | | | the running min over all txs. |
# | 4..8 | BATCH_HASHER_RATE0_PTR | RATE0 of the batch-level |
# | | | 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. |
# | 20..8212 | TX_TUPLES_PTR | Layer 1 piped data: per |
# | | | transaction `[tx_id[4], |
# | | | 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). |
# | 32788..40980 | TX_NOTES_SCRATCH_PTR | Per-transaction scratch space |
# | | | for Layer 3 / Layer 3' note |
# | | | data (overwritten each tx). |
# +-------------------+-------------------------+----------------------------------+

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

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

#! Single-felt slot holding `batch_expiration_block_num`: the running minimum of every
#! transaction's `expiration_block_num`, initialised to `u32::MAX`.
const BATCH_EXPIRATION_PTR=1

# 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

# 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

# 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 EXPIRATION BLOCK NUM
# =================================================================================================

#! Stores `batch_expiration_block_num`.
#!
#! Inputs: [batch_expiration_block_num]
#! Outputs: []
pub proc set_batch_expiration_block_num
mem_store.BATCH_EXPIRATION_PTR
end

#! Returns `batch_expiration_block_num`.
#!
#! Inputs: []
#! Outputs: [batch_expiration_block_num]
pub proc get_batch_expiration_block_num
mem_load.BATCH_EXPIRATION_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
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

#! 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
137 changes: 137 additions & 0 deletions crates/miden-protocol/asm/kernels/batch/lib/note_tracker.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use miden::core::mem
use miden::core::crypto::hashes::poseidon2
use miden::core::word

use miden::batch_kernel::memory
use miden::batch_kernel::memory::TX_NOTES_SCRATCH_PTR

# ABSORPTION HELPER
# =================================================================================================

#! Absorbs the contents of [`memory::TX_NOTES_SCRATCH_PTR`] into the batch hasher state held in
#! [`memory::BATCH_HASHER_*_PTR`] memory slots.
#!
#! The scratch region is read as `(NULLIFIER, EMPTY_OR_NOTE_ID)` 8-felt tuples, in the order they
#! were piped (which matches `build_input_note_commitment` in Rust).
#!
#! The number of words to absorb is read from [`memory::SCRATCH_WORDS_COUNT_PTR`].
#!
#! Inputs: []
#! Outputs: []
proc absorb_scratch_into_batch_hasher
push.0 exec.memory::set_scratch_word_index

exec.memory::load_batch_hasher_state
# => [RATE0, RATE1, CAPACITY]

exec.memory::get_scratch_word_index
exec.memory::get_scratch_words_count
u32lt
# => [should_loop, RATE0, RATE1, CAPACITY]

while.true
exec.memory::get_scratch_word_index
mul.4 add.TX_NOTES_SCRATCH_PTR
# => [scratch_ptr, RATE0, RATE1, CAPACITY]

padw dup.4 mem_loadw_le
# => [DATA1, scratch_ptr, RATE0, RATE1, CAPACITY]

padw dup.8 add.4 mem_loadw_le
# => [DATA2, DATA1, scratch_ptr, RATE0, RATE1, CAPACITY]

movup.8 drop
# => [DATA2, DATA1, RATE0, RATE1, CAPACITY]

# Replace RATE0 + RATE1 with DATA1 + DATA2 (DATA1 first, DATA2 second), then permute.
swapdw
dropw dropw
swapw
exec.poseidon2::permute
# => [RATE0', RATE1', CAPACITY']

exec.memory::get_scratch_word_index add.2 exec.memory::set_scratch_word_index
exec.memory::get_scratch_word_index
exec.memory::get_scratch_words_count
u32lt
# => [should_loop, RATE0, RATE1, CAPACITY]
end

exec.memory::save_batch_hasher_state
end

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

#! Computes the batch's INPUT_NOTES_COMMITMENT.
#!
#! For each transaction in transaction order, verifies the per-transaction
#! `INPUT_NOTES_COMMITMENT_i` against its advice-map value (the `(NULLIFIER, EMPTY_OR_COMMITMENT)`
#! tuple list), then absorbs that verified data into the batch-level sequential hasher.
#!
#! Inputs: []
#! Outputs: [INPUT_NOTES_COMMITMENT]
#!
#! Panics if a transaction's `(NULLIFIER, EMPTY_OR_COMMITMENT)` tuple list piped from the advice map
#! does not hash to its verified per-tx `INPUT_NOTES_COMMITMENT_i`.
#!
#! TODO: erase intra-batch unauthenticated notes from the absorbed sequence (see
#! `InputOutputNoteTracker::from_transactions`).
#! TODO: re-sort + dedupe by nullifier so the result equals
#! `proposed_batch.input_notes().commitment()`.
#! TODO: authenticate unauthenticated input notes against `BLOCK_HASH`'s chain MMR.
#! TODO: enforce `MAX_INPUT_NOTES_PER_BATCH`.
pub proc compute_input_notes_commitment
exec.poseidon2::init_no_padding
exec.memory::save_batch_hasher_state

exec.memory::get_num_transactions
push.0
# => [tx_index, num_transactions]

dup.1 dup.1 neq
# => [should_loop, tx_index, num_transactions]

while.true
dup exec.memory::get_tx_input_notes_commitment
# => [INPUT_NOTES_COMMITMENT_i, tx_index, num_transactions]

dupw exec.word::eqz
# => [is_empty, INPUT_NOTES_COMMITMENT_i, tx_index, num_transactions]

if.true
dropw
else
push.TX_NOTES_SCRATCH_PTR movdn.4
# => [INPUT_NOTES_COMMITMENT_i, tx_notes_scratch_ptr, tx_index, num_transactions]

adv.push_mapvaln
adv_push div.4
# => [num_words, INPUT_NOTES_COMMITMENT_i, tx_notes_scratch_ptr, tx_index, num_transactions]

dup exec.memory::set_scratch_words_count

movup.5 swap
# => [num_words, tx_notes_scratch_ptr, INPUT_NOTES_COMMITMENT_i, tx_index, num_transactions]

# Pipe the note data into scratch, asserting its poseidon2 hash equals
# INPUT_NOTES_COMMITMENT_i.
exec.mem::pipe_preimage_to_memory
# => [end_ptr, tx_index, num_transactions]
drop

exec.absorb_scratch_into_batch_hasher
end
# => [tx_index, num_transactions]

add.1
dup.1 dup.1 neq
# => [should_loop, tx_index, num_transactions]
end

drop drop

exec.memory::load_batch_hasher_state
exec.poseidon2::squeeze_digest
# => [INPUT_NOTES_COMMITMENT]
end
Loading
Loading