Skip to content

Commit 9b2a31a

Browse files
committed
feat: fill in batch kernel verification chain
Replaces the skeleton kernel from the parent PR with the recursive unhashing chain that verifies every batch output back to the public TRANSACTIONS_COMMITMENT. MASM: - lib/prologue.masm: pipes (tx_id, account_id) tuples keyed by TRANSACTIONS_COMMITMENT and per-tx headers keyed by each verified tx_id; tracks the running min of expiration_block_num. - lib/note_tracker.masm: pipes per-tx (NULLIFIER, EMPTY_OR_COMMITMENT) and (NOTE_ID, METADATA_COMMITMENT) tuples keyed by the verified per-tx INPUT_NOTES_COMMITMENT / OUTPUT_NOTES_COMMITMENT, absorbs into a batch-level sequential hasher, squeezes the final digest. - lib/memory.masm: pointer constants + accessor procs. - main.masm: orchestrates prologue + note_tracker + expiration read and produces the final output stack. Rust: - BatchId::hash_input_elements and TransactionId::input_elements exposed as pub(crate) helpers so the Rust hashers and the kernel advice builder share a single source of truth for the felt layouts. - BatchKernel::build_advice_inputs now populates the advice map with the layered map structure the kernel verifies. - LocalBatchProver::prove cross-checks the kernel's batch_expiration_block_num against proposed_batch.batch_expiration_block_num. Tests: happy path now asserts the real commitments + expiration value, plus four tampering tests that corrupt different layers of the advice chain and verify the kernel aborts with the matching error message.
1 parent 1cbd5f9 commit 9b2a31a

11 files changed

Lines changed: 984 additions & 73 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
- [BREAKING] Changed note metadata version 1 to encode as `1`, leaving encoded version `0` invalid.
3939
- Documented the `miden::protocol::account_id` module in the protocol library docs ([#2607](https://github.com/0xMiden/protocol/issues/2607)).
4040
- Added a skeleton batch kernel program with the public input/output contract from issue [#1122](https://github.com/0xMiden/protocol/issues/1122), wired through `LocalBatchProver::prove` and attached to `ProvenBatch` as an `ExecutionProof`. The kernel does not yet perform any verification; the verification chain that fills in the real outputs will land in a follow-up PR.
41+
- Added the batch kernel's verification chain: each transaction is reconstructed from the advice provider, anchored in `TRANSACTIONS_COMMITMENT`, and the per-tx note commitments are absorbed into the batch's `INPUT_NOTES_COMMITMENT` / `OUTPUT_NOTES_COMMITMENT` outputs. `LocalBatchProver::prove` now also cross-checks the kernel's `batch_expiration_block_num` against the proposed batch.
4142

4243
### Fixes
4344

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# MEMORY LAYOUT
2+
# =================================================================================================
3+
#
4+
# Below is the memory layout used by the batch kernel:
5+
#
6+
# +-------------------+-------------------------+----------------------------------+
7+
# | Address range | Constant | Contents |
8+
# +-------------------+-------------------------+----------------------------------+
9+
# | 0 | NUM_TRANSACTIONS_PTR | num_transactions (1 felt). |
10+
# | 1 | BATCH_EXPIRATION_PTR | batch_expiration_block_num. |
11+
# | 4..8 | BATCH_HASHER_RATE0_PTR | RATE0 of the batch-level |
12+
# | | | poseidon2 hasher state. |
13+
# | 8..12 | BATCH_HASHER_RATE1_PTR | RATE1 of the batch-level hasher. |
14+
# | 12..16 | BATCH_HASHER_CAP_PTR | CAPACITY of the batch-level |
15+
# | | | hasher. |
16+
# | 16 | SCRATCH_WORDS_COUNT_PTR | num_words piped into |
17+
# | | | TX_NOTES_SCRATCH_PTR for the |
18+
# | | | current transaction. |
19+
# | 17 | SCRATCH_WORD_INDEX_PTR | current iteration cursor for the |
20+
# | | | scratch absorption loop. |
21+
# | 20..8212 | TX_TUPLES_PTR | Layer 1 piped data: per |
22+
# | | | transaction `[tx_id[4], |
23+
# | | | account_id_prefix, |
24+
# | | | account_id_suffix, 0, 0]` |
25+
# | | | (8 felts each, sized for up to |
26+
# | | | 1024 transactions). |
27+
# | 8212..32788 | TX_HEADERS_PTR | Layer 2 piped data: per |
28+
# | | | transaction the felt sequence |
29+
# | | | TransactionId::new hashes |
30+
# | | | (24 felts each, sized for up to |
31+
# | | | 1024 transactions). |
32+
# | 32788..40980 | TX_NOTES_SCRATCH_PTR | Per-transaction scratch space |
33+
# | | | for Layer 3 / Layer 3' note |
34+
# | | | data (overwritten each tx). |
35+
# +-------------------+-------------------------+----------------------------------+
36+
37+
# BOOK KEEPING
38+
# =================================================================================================
39+
40+
#! Single-felt slot holding `num_transactions` after Layer 1 verification.
41+
const NUM_TRANSACTIONS_PTR=0
42+
43+
#! Single-felt slot holding the running min of all transactions'
44+
#! `expiration_block_num`, initialised to `u32::MAX`.
45+
const BATCH_EXPIRATION_PTR=1
46+
47+
# BATCH HASHER STATE
48+
# =================================================================================================
49+
50+
#! Word holding the RATE0 portion of the batch-level poseidon2 hasher state.
51+
const BATCH_HASHER_RATE0_PTR=4
52+
53+
#! Word holding the RATE1 portion of the batch-level poseidon2 hasher state.
54+
const BATCH_HASHER_RATE1_PTR=8
55+
56+
#! Word holding the CAPACITY portion of the batch-level poseidon2 hasher state.
57+
const BATCH_HASHER_CAP_PTR=12
58+
59+
# SCRATCH BOOKKEEPING
60+
# =================================================================================================
61+
62+
#! Number of words piped into TX_NOTES_SCRATCH_PTR for the transaction whose Layer 3 / 3' is
63+
#! being absorbed.
64+
const SCRATCH_WORDS_COUNT_PTR=16
65+
66+
#! Iteration cursor (index in words) into TX_NOTES_SCRATCH_PTR for the absorption loop.
67+
const SCRATCH_WORD_INDEX_PTR=17
68+
69+
# PIPED DATA REGIONS
70+
# =================================================================================================
71+
72+
#! Base of the Layer 1 piped data region. Per transaction, 8 felts:
73+
#! `[tx_id[4], account_id_prefix, account_id_suffix, 0, 0]`.
74+
const TX_TUPLES_PTR=20
75+
76+
#! Number of felts each transaction occupies in TX_TUPLES_PTR.
77+
const TX_TUPLE_FELT_LEN=8
78+
79+
#! Base of the Layer 2 piped data region. Per transaction, 24 felts:
80+
#! `[INIT[4], FINAL[4], INPUT_NOTES_COMMITMENT[4], OUTPUT_NOTES_COMMITMENT[4], FEE_ASSET[8]]`.
81+
#! This must match the felt-sequence layout of `TransactionId::new`.
82+
const TX_HEADERS_PTR=8212
83+
84+
#! Number of felts each transaction occupies in TX_HEADERS_PTR.
85+
const TX_HEADER_FELT_LEN=24
86+
87+
#! Felt offset within a transaction header where INPUT_NOTES_COMMITMENT starts.
88+
const TX_HEADER_INPUT_NOTES_OFFSET=8
89+
90+
#! Felt offset within a transaction header where OUTPUT_NOTES_COMMITMENT starts.
91+
const TX_HEADER_OUTPUT_NOTES_OFFSET=12
92+
93+
#! Per-transaction scratch space for Layer 3 / 3' note data, overwritten between iterations.
94+
const TX_NOTES_SCRATCH_PTR=32788
95+
96+
# NUM TRANSACTIONS
97+
# =================================================================================================
98+
99+
#! Stores `num_transactions`.
100+
#!
101+
#! Inputs: [num_transactions]
102+
#! Outputs: []
103+
pub proc set_num_transactions
104+
mem_store.NUM_TRANSACTIONS_PTR
105+
end
106+
107+
#! Returns `num_transactions`.
108+
#!
109+
#! Inputs: []
110+
#! Outputs: [num_transactions]
111+
pub proc get_num_transactions
112+
mem_load.NUM_TRANSACTIONS_PTR
113+
end
114+
115+
# BATCH EXPIRATION BLOCK NUM
116+
# =================================================================================================
117+
118+
#! Stores `batch_expiration_block_num`.
119+
#!
120+
#! Inputs: [batch_expiration_block_num]
121+
#! Outputs: []
122+
pub proc set_batch_expiration_block_num
123+
mem_store.BATCH_EXPIRATION_PTR
124+
end
125+
126+
#! Returns `batch_expiration_block_num`.
127+
#!
128+
#! Inputs: []
129+
#! Outputs: [batch_expiration_block_num]
130+
pub proc get_batch_expiration_block_num
131+
mem_load.BATCH_EXPIRATION_PTR
132+
end
133+
134+
# BATCH HASHER STATE
135+
# =================================================================================================
136+
137+
#! Persists the batch hasher state from the operand stack into memory.
138+
#!
139+
#! Inputs: [RATE0, RATE1, CAPACITY]
140+
#! Outputs: []
141+
pub proc save_batch_hasher_state
142+
mem_storew_le.BATCH_HASHER_RATE0_PTR dropw
143+
mem_storew_le.BATCH_HASHER_RATE1_PTR dropw
144+
mem_storew_le.BATCH_HASHER_CAP_PTR dropw
145+
end
146+
147+
#! Loads the batch hasher state from memory onto the operand stack.
148+
#!
149+
#! Inputs: []
150+
#! Outputs: [RATE0, RATE1, CAPACITY]
151+
pub proc load_batch_hasher_state
152+
padw mem_loadw_le.BATCH_HASHER_CAP_PTR
153+
padw mem_loadw_le.BATCH_HASHER_RATE1_PTR
154+
padw mem_loadw_le.BATCH_HASHER_RATE0_PTR
155+
end
156+
157+
# SCRATCH BOOKKEEPING
158+
# =================================================================================================
159+
160+
#! Stores the count (in words) of data piped into the per-transaction scratch.
161+
#!
162+
#! Inputs: [num_words]
163+
#! Outputs: []
164+
pub proc set_scratch_words_count
165+
mem_store.SCRATCH_WORDS_COUNT_PTR
166+
end
167+
168+
#! Returns the count (in words) of data piped into the per-transaction scratch.
169+
#!
170+
#! Inputs: []
171+
#! Outputs: [num_words]
172+
pub proc get_scratch_words_count
173+
mem_load.SCRATCH_WORDS_COUNT_PTR
174+
end
175+
176+
#! Stores the absorption iteration cursor (index in words).
177+
#!
178+
#! Inputs: [word_index]
179+
#! Outputs: []
180+
pub proc set_scratch_word_index
181+
mem_store.SCRATCH_WORD_INDEX_PTR
182+
end
183+
184+
#! Returns the absorption iteration cursor (index in words).
185+
#!
186+
#! Inputs: []
187+
#! Outputs: [word_index]
188+
pub proc get_scratch_word_index
189+
mem_load.SCRATCH_WORD_INDEX_PTR
190+
end
191+
192+
# TRANSACTION TUPLE / HEADER ACCESSORS
193+
# =================================================================================================
194+
195+
#! Returns a pointer to transaction `tx_index`'s entry in TX_TUPLES_PTR.
196+
#!
197+
#! Inputs: [tx_index]
198+
#! Outputs: [tx_tuple_ptr]
199+
pub proc tx_tuple_ptr
200+
mul.TX_TUPLE_FELT_LEN add.TX_TUPLES_PTR
201+
end
202+
203+
#! Returns the verified `tx_id` for transaction `tx_index` (loaded from TX_TUPLES_PTR).
204+
#!
205+
#! Inputs: [tx_index]
206+
#! Outputs: [TX_ID]
207+
pub proc get_tx_id
208+
exec.tx_tuple_ptr padw movup.4 mem_loadw_le
209+
end
210+
211+
#! Returns a pointer to transaction `tx_index`'s entry in TX_HEADERS_PTR.
212+
#!
213+
#! Inputs: [tx_index]
214+
#! Outputs: [tx_header_ptr]
215+
pub proc tx_header_ptr
216+
mul.TX_HEADER_FELT_LEN add.TX_HEADERS_PTR
217+
end
218+
219+
#! Returns the verified per-transaction INPUT_NOTES_COMMITMENT for transaction `tx_index`.
220+
#!
221+
#! Inputs: [tx_index]
222+
#! Outputs: [INPUT_NOTES_COMMITMENT_i]
223+
pub proc get_tx_input_notes_commitment
224+
exec.tx_header_ptr add.TX_HEADER_INPUT_NOTES_OFFSET padw movup.4 mem_loadw_le
225+
end
226+
227+
#! Returns the verified per-transaction OUTPUT_NOTES_COMMITMENT for transaction `tx_index`.
228+
#!
229+
#! Inputs: [tx_index]
230+
#! Outputs: [OUTPUT_NOTES_COMMITMENT_i]
231+
pub proc get_tx_output_notes_commitment
232+
exec.tx_header_ptr add.TX_HEADER_OUTPUT_NOTES_OFFSET padw movup.4 mem_loadw_le
233+
end

0 commit comments

Comments
 (0)