Skip to content

Commit 5a1ebc4

Browse files
tob-joeclaude
andcommitted
docs: update COMPARISON.md for word-addressed memory, emit events, stack depth
S-1 rewritten: stack depth now enforced via overflow guards (11 handlers) S-2 updated: memory model is now word-addressed (Nat -> Word), matching Rust S-4 updated: emit/emitImm now record event IDs in state.events field Test table: added stack depth edge case row (8 tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 50c4dc0 commit 5a1ebc4

1 file changed

Lines changed: 52 additions & 77 deletions

File tree

COMPARISON.md

Lines changed: 52 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -80,62 +80,45 @@ actual behavior.
8080

8181
## Intentional Modeling Simplifications
8282

83-
### S-1: Unbounded stack depth
84-
85-
**Lean:** `List Felt` (unbounded, no minimum depth)
86-
**Rust:** Minimum 16 elements (zero-padded), maximum 2^16
87-
88-
The Lean model allows operations on stacks smaller than 16 elements.
89-
This is standard in formal machine models (see LNSym, eth-isabelle,
90-
Cairo formal proofs). It avoids modeling the zero-padding logic, which
91-
is an implementation detail not relevant to procedure correctness.
92-
93-
**Impact:** Proofs do not verify stack overflow behavior. Programs that
94-
depend on the zero-initialized padding below the 16th element would need
95-
explicit hypotheses.
96-
97-
**Soundness for proven theorems:** All proven procedure theorems take
98-
the initial stack as an explicit hypothesis (e.g.,
99-
`hs : s.stack = a :: b :: c :: rest`). This fixes the stack depth to
100-
at least the number of named elements. No proven theorem relies on
101-
accessing zero-padded positions below its explicitly stated inputs,
102-
so the unbounded model produces identical results to the Rust model
103-
for all proven theorems.
104-
105-
### S-2: Element-addressed memory vs word-addressed memory
106-
107-
**Lean:** `Nat -> Felt` (one felt per address, total function)
83+
### S-1: Stack depth enforcement
84+
85+
**Lean:** `List Felt` with overflow guards; `MIN_STACK_DEPTH = 16`,
86+
`MAX_STACK_DEPTH = 2^16`, `wellFormed` predicate in State.lean.
87+
11 instruction handlers that increase stack depth check
88+
`s.stack.length + N > MAX_STACK_DEPTH` and return `none` on overflow.
89+
**Rust:** Minimum 16 elements (zero-padded), maximum 2^16.
90+
91+
Remaining differences:
92+
- The Lean model does not auto-pad stacks below 16 elements with
93+
zeros. Operations on short stacks fail via pattern matching rather
94+
than reading zero from padding. The `padStack` function exists but
95+
is not called automatically.
96+
- The Lean model does not prevent the stack from shrinking below 16
97+
via pop operations. In the Rust VM, the visible stack is always
98+
exactly 16 elements with an overflow table backing it.
99+
100+
All proven procedure theorems carry
101+
`hlen : rest.length + 30 <= MAX_STACK_DEPTH`, which ensures the
102+
stack stays within bounds throughout execution. The constant 30
103+
accounts for the maximum intermediate stack growth of any proven
104+
procedure.
105+
106+
### S-2: Word-addressed memory (matched)
107+
108+
**Lean:** `Nat -> Word` (word-addressed, total function, 0-initialized)
108109
**Rust:** `BTreeMap<(ContextId, u32), Word>` (word-addressed, sparse)
109110

110-
The Lean model stores one felt per natural number address. Word
111-
operations (memLoadw, memStorew) read/write 4 consecutive addresses. The
112-
Lean model provides separate Be (big-endian) and Le (little-endian)
113-
variants for word operations.
111+
The Lean model now matches the Rust VM's word-addressed memory layout.
112+
Each address maps to a `Word` (4 field elements). `memLoad` reads
113+
element 0 of the word; `memStore` writes element 0 (preserving elements
114+
1-3). `memLoadw`/`memStorew` read/write full words. Be and Le variants
115+
control element ordering on the stack.
114116

115-
The Rust model stores 4-element Words at word-aligned addresses.
116-
Individual element access uses `split_addr(addr)` to decompose into
117-
(word_addr, index_within_word). The Rust `op_mloadw` reads a Word and
118-
places `word[0]` at stack top; `op_mstorew` writes the top 4 stack
119-
elements as a Word starting from position 1 (after popping the address).
120-
121-
The Rust VM's element ordering within a word is:
122-
- `word[0]` <-> lowest address within the 4-element group
123-
- `word[0]` <-> stack top after mloadw
124-
125-
This matches the Lean Le (little-endian) variant where the lowest
126-
address goes to the stack top.
127-
128-
**Impact:** Proofs using word memory operations must specify which
129-
variant (Be or Le) is being used. For fidelity with the Rust VM, the Le
130-
variants should be preferred.
131-
132-
**Soundness for proven theorems:** The only memory-writing procedure
133-
proved is `word.store_word_u32s_le`, which uses `memStorewLe` (the Le
134-
variant matching Rust's element ordering). Its theorem states the
135-
exact memory addresses written and their values. The per-element
136-
memory model gives identical results to the per-word model when
137-
accessed through word-aligned Le operations, which is the only
138-
pattern used in proven theorems.
117+
Remaining differences:
118+
- Lean uses a total function (`Nat -> Word`) returning `Word.zero` for
119+
unwritten addresses. Rust uses a sparse `BTreeMap` with implicit zero
120+
default. Functionally equivalent.
121+
- Lean does not model `ContextId`. See S-3.
139122

140123
### S-3: No execution contexts
141124

@@ -146,36 +129,27 @@ The Lean model does not support multiple execution contexts. This is
146129
appropriate for the current scope (single-procedure correctness proofs)
147130
since all proven procedures execute in a single context.
148131

149-
### S-4: Emit as pure no-op
132+
### S-4: Emit records event IDs (matched)
150133

151-
**Lean:** `execEmit` checks stack >= 1, returns state unchanged
134+
**Lean:** `execEmit` reads top stack element as event ID, appends it
135+
to `s.events`. `emitImm v` appends `v` directly. Stack unchanged.
152136
**Rust:** `op_emit` reads top element as event ID, dispatches to host.
153-
Stack is unchanged in both.
137+
Stack unchanged.
154138

155-
The Lean model does not extract the event ID or model host interaction.
156-
This is correct for functional semantics since emit does not modify the
157-
VM state (stack, memory, advice).
139+
The Lean model now records emitted event IDs in the state's `events`
140+
field (most recent first). This matches the Rust VM's event dispatch
141+
behavior at the state level.
158142

159143
**`emitImm` in `u64::divmod`:** The divmod procedure begins with
160144
`.inst (.emitImm 14153021663962350784)`. In the real Miden VM, this
161-
emit triggers the host to push the quotient and remainder (as u32
162-
limbs) onto the advice stack. The subsequent `advPush 2` then
163-
consumes those values. The Lean model abstracts this host interaction
164-
by treating emitImm as a no-op and requiring the divmod correctness
165-
theorem (`u64_divmod_correct`) to take explicit advice-tape
166-
hypotheses: the advice stack must contain `[q_hi, q_lo, r_hi, r_lo]`
167-
satisfying the division relation `divisor * q + r = n` with
168-
`r < divisor`. This makes the host's role explicit in the theorem
169-
statement rather than modeling it in the semantics.
170-
171-
**Soundness for proven theorems:** `emitImm` is a pure no-op in both
172-
the Lean model and the Rust VM (neither modifies stack, memory, or
173-
locals). The only functional effect is the host pushing advice values,
174-
which the Lean model captures via explicit theorem hypotheses
175-
(the advice stack must contain specific values). This is strictly
176-
stronger than the Rust model: the theorem specifies exactly what
177-
the host must provide, rather than relying on an implicit host
178-
protocol.
145+
emit triggers the host to push the quotient and remainder onto the
146+
advice stack. The Lean model records the event ID but does not model
147+
host-side effects. The divmod correctness theorem takes explicit
148+
advice-tape hypotheses instead.
149+
150+
Remaining difference: the Lean model does not model host-side effects
151+
triggered by emit events (e.g., advice stack population). This is
152+
captured by explicit hypotheses in affected theorems.
179153

180154
### S-5: Error codes as strings
181155

@@ -251,4 +225,5 @@ cases across all modeled instruction categories:
251225
| Advice stack | 4 | ordering, insufficient |
252226
| Memory | 4 | store/load, unwritten, OOB |
253227
| Control flow | 6 | ifElse, repeat, while |
228+
| Stack depth | 8 | overflow on push/dup/padw/etc |
254229

0 commit comments

Comments
 (0)