Repository navigation
feat(constraints): prove public RAM output constraint (26) - #15
Merged
Merged
Conversation
Execution preserves the device layout and both advice buffers (JoltIOSetupFrame.execInstr_rule, finalTraceState_ioSame). With the Rust-enforced JoltIOLayout.Valid and JoltIOState.AdviceFits premises on the initial device, the honest final RAM word equals the public I/O word on the public interval.
…layout Replace JoltIOLayout.Valid and JoltIOState.AdviceFits with the single premise the proof uses, JoltIOState.AdviceBelowInput: each advice buffer ends at or below input_start, and input_start is word-aligned relative to the lowest RAM address. Region ends and absolute alignment are no longer assumed. Add a transcription of MemoryLayout::new up to input_start and prove AdviceBelowInput.of_memoryLayout: every device laid out by Rust whose advice passes create_emulator's length checks satisfies the premise.
quangvdao
force-pushed
the
qvd/registers-34-36
branch
from
September 29, 2026 00:20
76679ef to
0d9fa22
Compare
quangvdao
force-pushed
the
qvd/ram-output-26
branch
from
September 29, 2026 00:22
889e7c7 to
1a2e4bd
Compare
abiswas3
added a commit
that referenced
this pull request
Sep 29, 2026
* build: migrate Lake configuration to TOML * docs: update jolt-qed.pdf * refactor(isa): share branch and operand helpers * feat(isa): model device memory and advice tape * feat(trace): add program and execution trace types * feat(witness): define NP witness type dependent on witness parameters * feat(metadata): add instruction metadata mappings * feat(witness): define honest NP witness length-T columns * feat(witness): define honest NP witness chunked one-hot selectors * feat(witness): define honest NP witness unchunked selectors * feat(witness): define honest NP witness value arrays * docs: justify the store assumption in trace row * feat(witness): combine all helpers to make np witness * feat(lookup_tables): Started on some lookup tables Most of them are empty. * feat(constraints): Starting to model constraints on witnesses * feat(isa): add VirtualXORROTL1 instruction * docs(bug-report): document CSRRS completeness issue (tentative) * docs(bug-report): explain CSRRS x0 rewrite bypass (tentative) * fix(constraints): restore destination regression imports * feat(model): align traces and final operands with Rust Add the pending trace, witness, and constraint definitions alongside the normalized final-row ISA representation and its proof updates. Add focused regression modules, including FinalOperands. Completeness proofs remain deferred. * fix(witness): reject traces beyond cycle domain * fix(witness): require prover-padded trace length * fix(witness): bind bytecode domain to expanded program * fix(witness): bound prover chunk dimensions * proof(constraints): close shift and one-hot Booleanity targets * proof(constraints): close ten Spartan witness targets * proof(constraints): close assertion lookup under passing operands * docs(bug-report): document terminating self-branch constraint failure * proof(constraints): close ten more honest-witness targets * proof(constraints): close bytecode read and addition targets * proof(constraints): close RAM and expansion targets * feat(bundle): A new bundle to handle the JAL translation issue * fix(jal): new native proof with correct translation * Clean general projection lemmas. * refactor(constraints): reduce lookup operand proofs to active rows * docs(constraints): clarify translation gaps in pending proofs * test(constraints): cover load capture consistency checks * chore(gitignore) * refactor(isa): share operand and branch calculations Port 2a46c17 with its branch proof updates. Issue draft: bug-report/isa-shared-helpers.md. * fix(isa): model device memory and runtime advice state Port 907d734 with its operand preservation, shared arithmetic helpers, and proof dependencies. Document device memory, advice tape, and operand representation issues under bug-report/. * feat(isa): add VirtualXORROTL1 execution Port f42ae4f and its proof-support cases. Issue draft: bug-report/isa-xorrotl1.md. * fix(isa): represent decoded operands at Rust widths Port the final-operand representation and proof changes from dfd9e93. Keep LD destination handling for a separate port. Issue draft: bug-report/isa-decoded-operands.md. * fix(isa): preserve final LD destinations during execution Port the LD execution and native proof repair from dfd9e93. Issue draft: bug-report/isa-ld-destination.md. * fix(isa): model JAL using the Rust PC correspondence Port the JoltBytecode changes from feae045, 3bf46b5, and 78bbca7 together so the bundle, semantics, proof, and projection helpers stay consistent. Include the reviewed issue draft in bug-report/pc_nextPc.md. * docs(isa): index migration issues and record build verification * docs(isa): move migration issue drafts into issues directory * fix(isa): complete native dispatch and operand correspondence * chore(docs): keep issue drafts local * test(witness): check native dispatch against witness extraction Check the normalized ADDI immediate and JALR x0 temporary destination, captured value, target lookup, and PC preservation. Exercise target 6 to guard against restoring the Sail alignment check in the Rust instruction body. * fix(csrrs): Fixing the issue for csrrs x0 * fix(srliw): Other issue posted on Jolt * broken(BytecodeRafAtEntryEqOne) This is not a real pass. FIXME: This is a real issue * backup: This is a backup * fix(isa): model VirtualHostIO live and replay behavior * feat(trace): require initialized program state Prove initial register values through the initializer and expose zero source registers from each initialized trace. * docs: explain Rust and Sail NextPC correspondence Document the JAL execution-stage mapping, witness PC columns, and the assumptions used by the updated equivalence proof. * feat(constraints): implement comparison, slice, and assertion lookup tables Add `uninterleave`, which follows Rust's `uninterleave_bits` step by step, and transcribe 24 more tables from their Rust `materialize_entry`: Andn, Equal, NotEqual, the unsigned and signed comparisons, RangeCheck, RangeCheckAligned, AlignAddr, UpperWord, LowerHalfWord, SignMask, SignExtendWord, Pow2, Pow2W, ShiftRightBitmask, ShiftRightBitmaskW, HalfwordAlignment, WordAlignment, MulUNoOverflow, ValidDiv0 and ValidUnsignedRemainder. The remaining tables still use the placeholder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement negate-if, XOR-rotate, and shift-data lookup tables Transcribe 15 more tables from their Rust `materialize_entry`: VirtualNegateIf, the four VirtualXORROT tables (rotations 32, 24, 16, 63), the seven VirtualXORROTW tables (rotations 16, 12, 8, 7, 22, 19, 6) and ShiftDataB/H/W. As in Rust, each XOR-rotate family is one definition parameterised by the rotation amount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement window-mask, rev8w, shift/rotate loop, and pext lookup tables Transcribe the last 12 tables from their Rust `materialize_entry`: WindowMaskB/H/W, VirtualRev8W, VirtualSRL, VirtualSRA, VirtualSRLW, VirtualSRAW, VirtualROTR, VirtualROTRW, Pext and PextSigned. The Rust helpers `rev8w`, `u32::swap_bytes`, `pext`, `window_sign_bit` and `pext_signed` get their own Lean definitions. Rust's `while` loop in `pext` becomes a 64-pass `for` loop with the same exit condition. Every LookupTableKind now has a table, so the `sorry` fallback in `lookupTableEntry` is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): close constraints 13 and 14 Prove honest-witness lookup-result and jump-return destination writeback. Connect successful ISA execution to captured destinations and derive the trace PC invariant using the existing bytecode memory helpers. Add the all_assumptions conjunction and expose scoped operand assumptions through JoltTrace. Split static program definitions from traces and carry expanded instructions and destination normalization through witness helpers. Keep the existing source-to-row translation obligations explicit. Validation: lake build; both constraint proofs have no sorryAx dependency. * docs(bug-report): document RAM address wrap completeness bug * Updated review * docs(model): record fork replay audit and deferred work * docs(spec): ai helper md file * docs(bug-report): mark CSRRS writeback issue fixed * refactor(model): centralize jump row validity * feat(constraints): prove next source PC for jumps * feat(constraints): prove next source PC for other rows * docs(constraints): record proof status and reusable method * docs: refresh constraint review after fork fast-forward * feat(constraints): prove register value history * test(ram): reproduce termination-word history failure * fix(trace)!: require only satisfiable row assumptions on JoltTrace `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * Prove register read/write constraints (34)-(36) Add source-operand helpers (register35_rs1?, register36_rs2?) with real-row characterisations of Rs1Ra/Rs1Value/Rs2Ra/Rs2Value and canonical-source address round-tripping, expose register42_registersVal_preState, and use them to prove honestWitness_rs1ValueEqRegistersRead, honestWitness_rs2ValueEqRegistersRead and honestWitness_rdWriteValueEqRegistersReadWrite. * refactor(constraints): name register-read helpers by content The helpers added for (34)-(36) were prefixed with the constraint number they were first written for, but (36) and (34) also use them. Name them after what they state instead. * fix(trace)!: require only satisfiable row assumptions on JoltTrace (#18) `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * feat(constraints): prove public RAM output constraint (26) (#15) * Prove public RAM output constraint (26) Execution preserves the device layout and both advice buffers (JoltIOSetupFrame.execInstr_rule, finalTraceState_ioSame). With the Rust-enforced JoltIOLayout.Valid and JoltIOState.AdviceFits premises on the initial device, the honest final RAM word equals the public I/O word on the public interval. * refactor(constraints): weaken (26) premise and derive it from Rust's layout Replace JoltIOLayout.Valid and JoltIOState.AdviceFits with the single premise the proof uses, JoltIOState.AdviceBelowInput: each advice buffer ends at or below input_start, and input_start is word-aligned relative to the lowest RAM address. Region ends and absolute alignment are no longer assumed. Add a transcription of MemoryLayout::new up to input_start and prove AdviceBelowInput.of_memoryLayout: every device laid out by Rust whose advice passes create_emulator's length checks satisfies the premise. * proof(constraints): prove (39) up to six shift/rotate table lemmas (#17) * proof(constraints): reduce (39) to per-table lookup entry lemmas Collapse the lookup-address and table sums in (39) to the flagged table's entry at the honest lookup index, then prove 47 of the 55 per-table entry lemmas. Six shift/rotate lemmas are false for arbitrary expanded bytecode (non-bitmask shift masks); Pext and PextSigned are open. Counterexamples in JoltConstraints/Tests/LookupShiftMask.lean. * proof(constraints): close the Pext and PextSigned lookup entry lemmas Prove that the PEXT table's pext (contiguous fast path plus the 64-pass clear-lowest-set-bit loop) equals jolt_virtual_pext_value, and that pextSigned equals jolt_virtual_pext_signed_value, in the new LookupPextProofHelpers.lean. Only propext, Classical.choice and Quot.sound. Constraint (39) now depends on six per-table sorries, the shift/rotate lemmas that are false for arbitrary bytecode. Drop the randomized Pext check from Tests/LookupShiftMask.lean, which the theorems supersede. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Quang Dao <qvd@andrew.cmu.edu>
abiswas3
added a commit
that referenced
this pull request
Oct 9, 2026
* build: migrate Lake configuration to TOML * docs: update jolt-qed.pdf * refactor(isa): share branch and operand helpers * feat(isa): model device memory and advice tape * feat(trace): add program and execution trace types * feat(witness): define NP witness type dependent on witness parameters * feat(metadata): add instruction metadata mappings * feat(witness): define honest NP witness length-T columns * feat(witness): define honest NP witness chunked one-hot selectors * feat(witness): define honest NP witness unchunked selectors * feat(witness): define honest NP witness value arrays * docs: justify the store assumption in trace row * feat(witness): combine all helpers to make np witness * feat(lookup_tables): Started on some lookup tables Most of them are empty. * feat(constraints): Starting to model constraints on witnesses * feat(isa): add VirtualXORROTL1 instruction * docs(bug-report): document CSRRS completeness issue (tentative) * docs(bug-report): explain CSRRS x0 rewrite bypass (tentative) * fix(constraints): restore destination regression imports * feat(model): align traces and final operands with Rust Add the pending trace, witness, and constraint definitions alongside the normalized final-row ISA representation and its proof updates. Add focused regression modules, including FinalOperands. Completeness proofs remain deferred. * fix(witness): reject traces beyond cycle domain * fix(witness): require prover-padded trace length * fix(witness): bind bytecode domain to expanded program * fix(witness): bound prover chunk dimensions * proof(constraints): close shift and one-hot Booleanity targets * proof(constraints): close ten Spartan witness targets * proof(constraints): close assertion lookup under passing operands * docs(bug-report): document terminating self-branch constraint failure * proof(constraints): close ten more honest-witness targets * proof(constraints): close bytecode read and addition targets * proof(constraints): close RAM and expansion targets * feat(bundle): A new bundle to handle the JAL translation issue * fix(jal): new native proof with correct translation * Clean general projection lemmas. * refactor(constraints): reduce lookup operand proofs to active rows * docs(constraints): clarify translation gaps in pending proofs * test(constraints): cover load capture consistency checks * chore(gitignore) * refactor(isa): share operand and branch calculations Port 2a46c17 with its branch proof updates. Issue draft: bug-report/isa-shared-helpers.md. * fix(isa): model device memory and runtime advice state Port 907d734 with its operand preservation, shared arithmetic helpers, and proof dependencies. Document device memory, advice tape, and operand representation issues under bug-report/. * feat(isa): add VirtualXORROTL1 execution Port f42ae4f and its proof-support cases. Issue draft: bug-report/isa-xorrotl1.md. * fix(isa): represent decoded operands at Rust widths Port the final-operand representation and proof changes from dfd9e93. Keep LD destination handling for a separate port. Issue draft: bug-report/isa-decoded-operands.md. * fix(isa): preserve final LD destinations during execution Port the LD execution and native proof repair from dfd9e93. Issue draft: bug-report/isa-ld-destination.md. * fix(isa): model JAL using the Rust PC correspondence Port the JoltBytecode changes from feae045, 3bf46b5, and 78bbca7 together so the bundle, semantics, proof, and projection helpers stay consistent. Include the reviewed issue draft in bug-report/pc_nextPc.md. * docs(isa): index migration issues and record build verification * docs(isa): move migration issue drafts into issues directory * fix(isa): complete native dispatch and operand correspondence * chore(docs): keep issue drafts local * test(witness): check native dispatch against witness extraction Check the normalized ADDI immediate and JALR x0 temporary destination, captured value, target lookup, and PC preservation. Exercise target 6 to guard against restoring the Sail alignment check in the Rust instruction body. * fix(csrrs): Fixing the issue for csrrs x0 * fix(srliw): Other issue posted on Jolt * broken(BytecodeRafAtEntryEqOne) This is not a real pass. FIXME: This is a real issue * backup: This is a backup * fix(isa): model VirtualHostIO live and replay behavior * feat(trace): require initialized program state Prove initial register values through the initializer and expose zero source registers from each initialized trace. * docs: explain Rust and Sail NextPC correspondence Document the JAL execution-stage mapping, witness PC columns, and the assumptions used by the updated equivalence proof. * feat(constraints): implement comparison, slice, and assertion lookup tables Add `uninterleave`, which follows Rust's `uninterleave_bits` step by step, and transcribe 24 more tables from their Rust `materialize_entry`: Andn, Equal, NotEqual, the unsigned and signed comparisons, RangeCheck, RangeCheckAligned, AlignAddr, UpperWord, LowerHalfWord, SignMask, SignExtendWord, Pow2, Pow2W, ShiftRightBitmask, ShiftRightBitmaskW, HalfwordAlignment, WordAlignment, MulUNoOverflow, ValidDiv0 and ValidUnsignedRemainder. The remaining tables still use the placeholder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement negate-if, XOR-rotate, and shift-data lookup tables Transcribe 15 more tables from their Rust `materialize_entry`: VirtualNegateIf, the four VirtualXORROT tables (rotations 32, 24, 16, 63), the seven VirtualXORROTW tables (rotations 16, 12, 8, 7, 22, 19, 6) and ShiftDataB/H/W. As in Rust, each XOR-rotate family is one definition parameterised by the rotation amount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement window-mask, rev8w, shift/rotate loop, and pext lookup tables Transcribe the last 12 tables from their Rust `materialize_entry`: WindowMaskB/H/W, VirtualRev8W, VirtualSRL, VirtualSRA, VirtualSRLW, VirtualSRAW, VirtualROTR, VirtualROTRW, Pext and PextSigned. The Rust helpers `rev8w`, `u32::swap_bytes`, `pext`, `window_sign_bit` and `pext_signed` get their own Lean definitions. Rust's `while` loop in `pext` becomes a 64-pass `for` loop with the same exit condition. Every LookupTableKind now has a table, so the `sorry` fallback in `lookupTableEntry` is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): close constraints 13 and 14 Prove honest-witness lookup-result and jump-return destination writeback. Connect successful ISA execution to captured destinations and derive the trace PC invariant using the existing bytecode memory helpers. Add the all_assumptions conjunction and expose scoped operand assumptions through JoltTrace. Split static program definitions from traces and carry expanded instructions and destination normalization through witness helpers. Keep the existing source-to-row translation obligations explicit. Validation: lake build; both constraint proofs have no sorryAx dependency. * docs(bug-report): document RAM address wrap completeness bug * Updated review * docs(model): record fork replay audit and deferred work * docs(spec): ai helper md file * docs(bug-report): mark CSRRS writeback issue fixed * refactor(model): centralize jump row validity * feat(constraints): prove next source PC for jumps * feat(constraints): prove next source PC for other rows * docs(constraints): record proof status and reusable method * docs: refresh constraint review after fork fast-forward * feat(constraints): prove register value history * test(ram): reproduce termination-word history failure * fix(trace)!: require only satisfiable row assumptions on JoltTrace `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * Prove register read/write constraints (34)-(36) Add source-operand helpers (register35_rs1?, register36_rs2?) with real-row characterisations of Rs1Ra/Rs1Value/Rs2Ra/Rs2Value and canonical-source address round-tripping, expose register42_registersVal_preState, and use them to prove honestWitness_rs1ValueEqRegistersRead, honestWitness_rs2ValueEqRegistersRead and honestWitness_rdWriteValueEqRegistersReadWrite. * refactor(constraints): name register-read helpers by content The helpers added for (34)-(36) were prefixed with the constraint number they were first written for, but (36) and (34) also use them. Name them after what they state instead. * fix(trace)!: require only satisfiable row assumptions on JoltTrace (#18) `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * feat(constraints): prove public RAM output constraint (26) (#15) * Prove public RAM output constraint (26) Execution preserves the device layout and both advice buffers (JoltIOSetupFrame.execInstr_rule, finalTraceState_ioSame). With the Rust-enforced JoltIOLayout.Valid and JoltIOState.AdviceFits premises on the initial device, the honest final RAM word equals the public I/O word on the public interval. * refactor(constraints): weaken (26) premise and derive it from Rust's layout Replace JoltIOLayout.Valid and JoltIOState.AdviceFits with the single premise the proof uses, JoltIOState.AdviceBelowInput: each advice buffer ends at or below input_start, and input_start is word-aligned relative to the lowest RAM address. Region ends and absolute alignment are no longer assumed. Add a transcription of MemoryLayout::new up to input_start and prove AdviceBelowInput.of_memoryLayout: every device laid out by Rust whose advice passes create_emulator's length checks satisfies the premise. * proof(constraints): prove (39) up to six shift/rotate table lemmas (#17) * proof(constraints): reduce (39) to per-table lookup entry lemmas Collapse the lookup-address and table sums in (39) to the flagged table's entry at the honest lookup index, then prove 47 of the 55 per-table entry lemmas. Six shift/rotate lemmas are false for arbitrary expanded bytecode (non-bitmask shift masks); Pext and PextSigned are open. Counterexamples in JoltConstraints/Tests/LookupShiftMask.lean. * proof(constraints): close the Pext and PextSigned lookup entry lemmas Prove that the PEXT table's pext (contiguous fast path plus the 64-pass clear-lowest-set-bit loop) equals jolt_virtual_pext_value, and that pextSigned equals jolt_virtual_pext_signed_value, in the new LookupPextProofHelpers.lean. Only propext, Classical.choice and Quot.sound. Constraint (39) now depends on six per-table sorries, the shift/rotate lemmas that are false for arbitrary bytecode. Drop the randomized Pext check from Tests/LookupShiftMask.lean, which the theorems supersede. * docs(human-comments): Started documenting program.lean * chore: clean up capture API and audit docs * refactor: separate constraints from completeness proofs Give each constraint a dedicated definition module and a matching completeness module. Add the AllConstraints bundle and an aggregate honest-witness theorem, and clean up proof linter warnings. * fix(completeness): assemble branch constraint from its theorem Admit the individual branch completeness proof pending the Rust and Lean model reconciliation, then use it in the aggregate theorem without assuming the constraint as a premise. * refactor(isa): mirror Rust JoltDevice and MemoryLayout Replace JoltIOLayout and JoltIOState with Rust's MemoryLayout (all 20 fields) and JoltDevice, using Rust's names. Rename SailJoltState.io to jolt_device, ramStartAddress to RAM_START_ADDRESS, and the device byte functions to JoltDevice.load? and JoltDevice.store?, built on Rust's is_* region checks. Add STACK_CANARY_SIZE. * fix(isa): model LD and SD with Jolt's MMU LD and SD now read and write memory directly, following Rust's MMU, with Rust's checks as panics. The Sail equivalence proofs keep their Sail assumptions and add JoltRamLoadOk / JoltRamStoreOk. MemoryLayout and JoltDevice move to JoltISA/JoltDevice.lean. * refactor: remove dead LD/SD code and unused memory-window arguments * refactor: read HostIO bytes through Jolt's MMU and drop unused trace assumptions HostIO byte reads now follow Rust's Mmu::load (trace_load, then load_raw), so no Jolt instruction uses Sail's memory read. TraceAssumptions keeps only xRegReadable; the RAM window, privilege and MPRV facts and the per-row assumption operands are no longer needed. * docs: start a new model review * feat: add Rust-faithful program and honest trace models * feat: prove facts about the Jolt program's rows * feat: prove uniqueness and the preprocessed bytecode layout * feat: prove HostIO keeps Sail state and registers stay readable * feat: prove asserts hold, registers start at 0, and size RAM like Rust * feat: prove advice regions sit below the inputs * feat: prove the trace starts at the entry slot and LD/SD addresses are aligned * feat: add the memory address of a trace row; record completeness conditions * feat: model Rust's prover config (trace length and RAM size) * feat: prove the prover's trace length fits and pads the run * feat: prove the prover's RAM size holds every access; FIXME for upstream ram_K bug * feat: state the spoil assert condition * docs: explain when a guest reaches a spoil assert * docs: link every completeness assumption to its upstream issue * feat: model Rust's last-row-is-a-jump check (a16z/jolt#1968) * feat: assume executed code is unchanged (FIXME a16z/jolt#1952) * feat: make expand opaque; assume next PC does not wrap (asked a16z) * feat: build witness sizes from the prover config (rewiring phase 0) * refactor: witness columns read the honest trace (rewiring phase 1) * docs: list the store fact among the proofs owed * chore: delete the old program and trace models (kept in history at d044e63) * docs: bug report and repro for final RAM over 2 GiB * docs: drop the untested fix from the final RAM report * docs: FIXME exception for final RAM above 2 GiB (asked a16z) * refactor: prove the bytecode layout the completeness proofs use (rewiring phase 2) * refactor: Terminated and RamFits over the honest trace, proved for Rust's sizes * refactor: honest witness over the trace; Rust's sizes meet the old size assumptions (rewiring phase 2) * refactor: prove the old trace's facts for the honest trace (rewiring phase 3) * refactor: constraint proof helpers over the honest trace (rewiring phase 3) * refactor: completeness theorems over the honest trace (rewiring phase 4); a16z: PC must not wrap * remove Tests, written against the old trace (rewiring phase 5) * prove prover_config_log_ram_K_lt: ram_K comes from 64-bit addresses * prove address-0 accesses: an LD at 0 reads 0, no SD stores at 0; ramAccessesValid is alignment only, proved * final theorem at Rust's sizes; no completeness theorem takes Terminated; a16z: 2 GiB RAM limit is an assumption * a16z: a program must not change its own code is an assumption * final theorem against the verifier's public I/O: the instance's inputs, claimed outputs and panic flag * prove (16): a taken branch is never the last row (#1968), so the next row starts at PC + imm * model_review lists only open issues; drop the archives; rename program_fresh to program * refactor(lean): separate general traces from honest execution Define Trace independently of HonestTrace and express AllConstraints over a witness with its instance-bound ConstraintContext. Keep honest witness construction and completeness in the honest layer. Prove store-word presence for indexed rows of HonestTrace by induction, replace the unrestricted admitted lemma, and check its proof and witness dependencies in the default build. Share state-preservation rules across memory, Sail, and device proofs. Move completeness helpers out of the constraint relation layer and keep model_review focused on open issues. Validation: lake build (8672 jobs), including StoreWordPresence checks. * docs: add soundness review notes and Lean tutorials Add the soundness method and completeness/soundness review document, alongside soundness examples and Lean tutorial materials. * docs: close A5 in the completeness review; HonestTrace admits every run Rust's tracer produces * fix(lean): enforce verifier input and witness-size checks Require valid public inputs, the padded trace limit, the instance's bytecode domain, and the verifier's RAM minimum and maximum in ConstraintContext. Derive input, trace, and bytecode checks from the existing completeness inputs. Add the approved ramSizeAccepted premise to allConstraints_rust_sizes and explain it in English beside the theorem. Prover acceptance alone does not imply this check: the included Rust reproducer traces an empty ELF, obtains ram_K = 4 from the prover, and receives InvalidRamK with minimum 16 from the verifier. Its self-jump control passes the verifier input checks. Build regression checks for oversized outputs, a zero-based layout, invalid trace and bytecode domains, and RAM sizes outside the verifier bounds. Validation: lake build (8674 jobs), including both default-build checks; the saved Rust reproducer and self-jump control pass their observed checks. * fix(lean): keep RAM completeness explicit and kernel checked Remove the ramSizeAccepted premise and retain the general RAM-size obligation as a documented sorry, consistent with the existing Rust-bug convention. Prove the nonempty-image lower inequality when the verifier minimum computes. Replace native evaluation in the boundary checks with kernel evaluation and audit the relevant axiom dependencies. Validation: lake build passes (8675 jobs). * fix(lean): bind trusted advice to the instance Model trusted-advice contents directly in the fixed instance and retain untrusted advice and the execution tape as prover inputs. Prove that initialized trusted advice cannot vary with prover inputs and that AllConstraints is tape-independent. Include both new files and axiom checks in the default build. Document the separate tape-consistency obligation and the unmodeled bytes-without-commitment case. Validation: lake build JoltConstraints passed (8675 jobs). * refactor(lean): general runs, instance-level code assumption, Rust runtime advice - ValidRun: a run of the instance's program (expansion, initial state, start, step, link) with no stop rule or advice choice; HonestTrace now extends it. - JoltInstance.CodeUnchanged replaces the HonestTrace.code_unchanged field (a16z, 2026-10-07, #1952); honest traces get it from the premise. - HonestTrace.advice_from_rust: VirtualAdvice rows carry the values Rust's tracer patches in (SourceInstruction.rustAdvice for the division family, reusing the bytecode project's advice values); SC.W/SC.D left as a TODO. - Rv64ProgramImage.ImageNonempty: a16z (phone, 2026-10-08) says a program is never empty; premise of the final theorem and of witness_params_ram_bounds. - witness_params_ram_bounds: FIXME, upper half blocked by #1951 item 4 (compute_max_ram_k rounds down); docs updated to match. - Review notes: B1/B2 closed, B4 decision recorded. Validation: lake build JoltConstraints passes (8675 jobs). * fix(lean): chunk widths are the prover's choice; drop check scripts - WitnessParams keeps only Rust's structural chunk-width checks; the prover's 4/16-or-8/32 choice is WitnessParams.ProverChunkConfig, used only by completeness (HonestTrace.witness_params_chunk_config). Remove the unused chunkBits_lt_128. - Move ramAccessesValid out of the relation into Completeness/Helpers/RamAccesses.lean. - Remove the Checks/ build-time check scripts; import advice_inputs directly. - Docs: constraint_proving.md names the current trace facts; model_review.md header states the Rust checkout; review notes close B3 and record the soundness premises (2^127 < ringChar F, NextPCNoWrap, CodeUnchanged). Validation: lake build JoltConstraints passes (8673 jobs). * feat(lean): define expansions and prove expanded-row facts Bring SourceInstruction.expand over from the bytecode project (expansions.lean, expansion_helpers.lean), with LR/SC programs in place of the generator output. Check every expansion's rows in expansion_facts.lean and use them to prove expand_program_rows_valid and the six shift lookupEntryCorrect lemmas. Shift tables: LookupShiftProofHelpers.lean proves the SRL, SRA, SRLW and SRAW table loops on right-shift bitmasks. shiftMaskOk_of_honest shows every honest row reads such a mask: immediate masks are checked in the bytecode, and a register mask is written by the VirtualShiftRightBitmask row just before. Move the nextPC frame lemmas to nextpc_frame.lean. * chore(lean): drop unused pc_map_ok_iff Neither the completeness nor the soundness statement uses it; the proofs work with pc_map_ok directly. * feat(lean): state soundness, proof sorried JoltInstance.InLanguage is Jolt's claim: for some private inputs, Rust's run stops at the PC stall with the instance's outputs and panic flag. Soundness assumes a field of characteristic above 2^127, NextPCNoWrap and CodeUnchanged. Expected to fail today for programs that stop or panic in ways SDK guests never do (B6). * ci: bump lean-action to v1.6.1 (pins its cache actions by SHA) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Quang Dao <qvd@andrew.cmu.edu>
abiswas3
added a commit
that referenced
this pull request
Oct 22, 2026
* build: migrate Lake configuration to TOML * docs: update jolt-qed.pdf * refactor(isa): share branch and operand helpers * feat(isa): model device memory and advice tape * feat(trace): add program and execution trace types * feat(witness): define NP witness type dependent on witness parameters * feat(metadata): add instruction metadata mappings * feat(witness): define honest NP witness length-T columns * feat(witness): define honest NP witness chunked one-hot selectors * feat(witness): define honest NP witness unchunked selectors * feat(witness): define honest NP witness value arrays * docs: justify the store assumption in trace row * feat(witness): combine all helpers to make np witness * feat(lookup_tables): Started on some lookup tables Most of them are empty. * feat(constraints): Starting to model constraints on witnesses * feat(isa): add VirtualXORROTL1 instruction * docs(bug-report): document CSRRS completeness issue (tentative) * docs(bug-report): explain CSRRS x0 rewrite bypass (tentative) * fix(constraints): restore destination regression imports * feat(model): align traces and final operands with Rust Add the pending trace, witness, and constraint definitions alongside the normalized final-row ISA representation and its proof updates. Add focused regression modules, including FinalOperands. Completeness proofs remain deferred. * fix(witness): reject traces beyond cycle domain * fix(witness): require prover-padded trace length * fix(witness): bind bytecode domain to expanded program * fix(witness): bound prover chunk dimensions * proof(constraints): close shift and one-hot Booleanity targets * proof(constraints): close ten Spartan witness targets * proof(constraints): close assertion lookup under passing operands * docs(bug-report): document terminating self-branch constraint failure * proof(constraints): close ten more honest-witness targets * proof(constraints): close bytecode read and addition targets * proof(constraints): close RAM and expansion targets * feat(bundle): A new bundle to handle the JAL translation issue * fix(jal): new native proof with correct translation * Clean general projection lemmas. * refactor(constraints): reduce lookup operand proofs to active rows * docs(constraints): clarify translation gaps in pending proofs * test(constraints): cover load capture consistency checks * chore(gitignore) * refactor(isa): share operand and branch calculations Port 2a46c17 with its branch proof updates. Issue draft: bug-report/isa-shared-helpers.md. * fix(isa): model device memory and runtime advice state Port 907d734 with its operand preservation, shared arithmetic helpers, and proof dependencies. Document device memory, advice tape, and operand representation issues under bug-report/. * feat(isa): add VirtualXORROTL1 execution Port f42ae4f and its proof-support cases. Issue draft: bug-report/isa-xorrotl1.md. * fix(isa): represent decoded operands at Rust widths Port the final-operand representation and proof changes from dfd9e93. Keep LD destination handling for a separate port. Issue draft: bug-report/isa-decoded-operands.md. * fix(isa): preserve final LD destinations during execution Port the LD execution and native proof repair from dfd9e93. Issue draft: bug-report/isa-ld-destination.md. * fix(isa): model JAL using the Rust PC correspondence Port the JoltBytecode changes from feae045, 3bf46b5, and 78bbca7 together so the bundle, semantics, proof, and projection helpers stay consistent. Include the reviewed issue draft in bug-report/pc_nextPc.md. * docs(isa): index migration issues and record build verification * docs(isa): move migration issue drafts into issues directory * fix(isa): complete native dispatch and operand correspondence * chore(docs): keep issue drafts local * test(witness): check native dispatch against witness extraction Check the normalized ADDI immediate and JALR x0 temporary destination, captured value, target lookup, and PC preservation. Exercise target 6 to guard against restoring the Sail alignment check in the Rust instruction body. * fix(csrrs): Fixing the issue for csrrs x0 * fix(srliw): Other issue posted on Jolt * broken(BytecodeRafAtEntryEqOne) This is not a real pass. FIXME: This is a real issue * backup: This is a backup * fix(isa): model VirtualHostIO live and replay behavior * feat(trace): require initialized program state Prove initial register values through the initializer and expose zero source registers from each initialized trace. * docs: explain Rust and Sail NextPC correspondence Document the JAL execution-stage mapping, witness PC columns, and the assumptions used by the updated equivalence proof. * feat(constraints): implement comparison, slice, and assertion lookup tables Add `uninterleave`, which follows Rust's `uninterleave_bits` step by step, and transcribe 24 more tables from their Rust `materialize_entry`: Andn, Equal, NotEqual, the unsigned and signed comparisons, RangeCheck, RangeCheckAligned, AlignAddr, UpperWord, LowerHalfWord, SignMask, SignExtendWord, Pow2, Pow2W, ShiftRightBitmask, ShiftRightBitmaskW, HalfwordAlignment, WordAlignment, MulUNoOverflow, ValidDiv0 and ValidUnsignedRemainder. The remaining tables still use the placeholder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement negate-if, XOR-rotate, and shift-data lookup tables Transcribe 15 more tables from their Rust `materialize_entry`: VirtualNegateIf, the four VirtualXORROT tables (rotations 32, 24, 16, 63), the seven VirtualXORROTW tables (rotations 16, 12, 8, 7, 22, 19, 6) and ShiftDataB/H/W. As in Rust, each XOR-rotate family is one definition parameterised by the rotation amount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): implement window-mask, rev8w, shift/rotate loop, and pext lookup tables Transcribe the last 12 tables from their Rust `materialize_entry`: WindowMaskB/H/W, VirtualRev8W, VirtualSRL, VirtualSRA, VirtualSRLW, VirtualSRAW, VirtualROTR, VirtualROTRW, Pext and PextSigned. The Rust helpers `rev8w`, `u32::swap_bytes`, `pext`, `window_sign_bit` and `pext_signed` get their own Lean definitions. Rust's `while` loop in `pext` becomes a 64-pass `for` loop with the same exit condition. Every LookupTableKind now has a table, so the `sorry` fallback in `lookupTableEntry` is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(constraints): close constraints 13 and 14 Prove honest-witness lookup-result and jump-return destination writeback. Connect successful ISA execution to captured destinations and derive the trace PC invariant using the existing bytecode memory helpers. Add the all_assumptions conjunction and expose scoped operand assumptions through JoltTrace. Split static program definitions from traces and carry expanded instructions and destination normalization through witness helpers. Keep the existing source-to-row translation obligations explicit. Validation: lake build; both constraint proofs have no sorryAx dependency. * docs(bug-report): document RAM address wrap completeness bug * Updated review * docs(model): record fork replay audit and deferred work * docs(spec): ai helper md file * docs(bug-report): mark CSRRS writeback issue fixed * refactor(model): centralize jump row validity * feat(constraints): prove next source PC for jumps * feat(constraints): prove next source PC for other rows * docs(constraints): record proof status and reusable method * docs: refresh constraint review after fork fast-forward * feat(constraints): prove register value history * test(ram): reproduce termination-word history failure * fix(trace)!: require only satisfiable row assumptions on JoltTrace `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * Prove register read/write constraints (34)-(36) Add source-operand helpers (register35_rs1?, register36_rs2?) with real-row characterisations of Rs1Ra/Rs1Value/Rs2Ra/Rs2Value and canonical-source address round-tripping, expose register42_registersVal_preState, and use them to prove honestWitness_rs1ValueEqRegistersRead, honestWitness_rs2ValueEqRegistersRead and honestWitness_rdWriteValueEqRegistersReadWrite. * refactor(constraints): name register-read helpers by content The helpers added for (34)-(36) were prefixed with the constraint number they were first written for, but (36) and (34) also use them. Name them after what they state instead. * fix(trace)!: require only satisfiable row assumptions on JoltTrace (#18) `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * feat(constraints): prove public RAM output constraint (26) (#15) * Prove public RAM output constraint (26) Execution preserves the device layout and both advice buffers (JoltIOSetupFrame.execInstr_rule, finalTraceState_ioSame). With the Rust-enforced JoltIOLayout.Valid and JoltIOState.AdviceFits premises on the initial device, the honest final RAM word equals the public I/O word on the public interval. * refactor(constraints): weaken (26) premise and derive it from Rust's layout Replace JoltIOLayout.Valid and JoltIOState.AdviceFits with the single premise the proof uses, JoltIOState.AdviceBelowInput: each advice buffer ends at or below input_start, and input_start is word-aligned relative to the lowest RAM address. Region ends and absolute alignment are no longer assumed. Add a transcription of MemoryLayout::new up to input_start and prove AdviceBelowInput.of_memoryLayout: every device laid out by Rust whose advice passes create_emulator's length checks satisfies the premise. * proof(constraints): prove (39) up to six shift/rotate table lemmas (#17) * proof(constraints): reduce (39) to per-table lookup entry lemmas Collapse the lookup-address and table sums in (39) to the flagged table's entry at the honest lookup index, then prove 47 of the 55 per-table entry lemmas. Six shift/rotate lemmas are false for arbitrary expanded bytecode (non-bitmask shift masks); Pext and PextSigned are open. Counterexamples in JoltConstraints/Tests/LookupShiftMask.lean. * proof(constraints): close the Pext and PextSigned lookup entry lemmas Prove that the PEXT table's pext (contiguous fast path plus the 64-pass clear-lowest-set-bit loop) equals jolt_virtual_pext_value, and that pextSigned equals jolt_virtual_pext_signed_value, in the new LookupPextProofHelpers.lean. Only propext, Classical.choice and Quot.sound. Constraint (39) now depends on six per-table sorries, the shift/rotate lemmas that are false for arbitrary bytecode. Drop the randomized Pext check from Tests/LookupShiftMask.lean, which the theorems supersede. * docs(human-comments): Started documenting program.lean * docs: scaffold Zola blog in isolated directory * chore(ci): remove blog publishing workflow * docs(site): publish initial Hello World site * fix(site): retain Pages marker across builds * chore: clean up capture API and audit docs * refactor: separate constraints from completeness proofs Give each constraint a dedicated definition module and a matching completeness module. Add the AllConstraints bundle and an aggregate honest-witness theorem, and clean up proof linter warnings. * fix(completeness): assemble branch constraint from its theorem Admit the individual branch completeness proof pending the Rust and Lean model reconciliation, then use it in the aggregate theorem without assuming the constraint as a premise. * docs(template): blog up No content of worth has been transferred yet. * refactor(isa): mirror Rust JoltDevice and MemoryLayout Replace JoltIOLayout and JoltIOState with Rust's MemoryLayout (all 20 fields) and JoltDevice, using Rust's names. Rename SailJoltState.io to jolt_device, ramStartAddress to RAM_START_ADDRESS, and the device byte functions to JoltDevice.load? and JoltDevice.store?, built on Rust's is_* region checks. Add STACK_CANARY_SIZE. * fix(isa): model LD and SD with Jolt's MMU LD and SD now read and write memory directly, following Rust's MMU, with Rust's checks as panics. The Sail equivalence proofs keep their Sail assumptions and add JoltRamLoadOk / JoltRamStoreOk. MemoryLayout and JoltDevice move to JoltISA/JoltDevice.lean. * refactor: remove dead LD/SD code and unused memory-window arguments * refactor: read HostIO bytes through Jolt's MMU and drop unused trace assumptions HostIO byte reads now follow Rust's Mmu::load (trace_load, then load_raw), so no Jolt instruction uses Sail's memory read. TraceAssumptions keeps only xRegReadable; the RAM window, privilege and MPRV facts and the per-row assumption operands are no longer needed. * docs: start a new model review * feat: add Rust-faithful program and honest trace models * feat: prove facts about the Jolt program's rows * feat: prove uniqueness and the preprocessed bytecode layout * feat: prove HostIO keeps Sail state and registers stay readable * feat: prove asserts hold, registers start at 0, and size RAM like Rust * feat: prove advice regions sit below the inputs * feat: prove the trace starts at the entry slot and LD/SD addresses are aligned * feat: add the memory address of a trace row; record completeness conditions * feat: model Rust's prover config (trace length and RAM size) * feat: prove the prover's trace length fits and pads the run * feat: prove the prover's RAM size holds every access; FIXME for upstream ram_K bug * feat: state the spoil assert condition * docs: explain when a guest reaches a spoil assert * docs: link every completeness assumption to its upstream issue * feat: model Rust's last-row-is-a-jump check (a16z/jolt#1968) * feat: assume executed code is unchanged (FIXME a16z/jolt#1952) * feat: make expand opaque; assume next PC does not wrap (asked a16z) * feat: build witness sizes from the prover config (rewiring phase 0) * refactor: witness columns read the honest trace (rewiring phase 1) * docs: list the store fact among the proofs owed * chore: delete the old program and trace models (kept in history at d044e63) * docs: bug report and repro for final RAM over 2 GiB * docs: drop the untested fix from the final RAM report * docs: FIXME exception for final RAM above 2 GiB (asked a16z) * refactor: prove the bytecode layout the completeness proofs use (rewiring phase 2) * refactor: Terminated and RamFits over the honest trace, proved for Rust's sizes * refactor: honest witness over the trace; Rust's sizes meet the old size assumptions (rewiring phase 2) * refactor: prove the old trace's facts for the honest trace (rewiring phase 3) * refactor: constraint proof helpers over the honest trace (rewiring phase 3) * refactor: completeness theorems over the honest trace (rewiring phase 4); a16z: PC must not wrap * remove Tests, written against the old trace (rewiring phase 5) * prove prover_config_log_ram_K_lt: ram_K comes from 64-bit addresses * prove address-0 accesses: an LD at 0 reads 0, no SD stores at 0; ramAccessesValid is alignment only, proved * final theorem at Rust's sizes; no completeness theorem takes Terminated; a16z: 2 GiB RAM limit is an assumption * a16z: a program must not change its own code is an assumption * final theorem against the verifier's public I/O: the instance's inputs, claimed outputs and panic flag * prove (16): a taken branch is never the last row (#1968), so the next row starts at PC + imm * model_review lists only open issues; drop the archives; rename program_fresh to program * refactor(lean): separate general traces from honest execution Define Trace independently of HonestTrace and express AllConstraints over a witness with its instance-bound ConstraintContext. Keep honest witness construction and completeness in the honest layer. Prove store-word presence for indexed rows of HonestTrace by induction, replace the unrestricted admitted lemma, and check its proof and witness dependencies in the default build. Share state-preservation rules across memory, Sail, and device proofs. Move completeness helpers out of the constraint relation layer and keep model_review focused on open issues. Validation: lake build (8672 jobs), including StoreWordPresence checks. * docs: add soundness review notes and Lean tutorials Add the soundness method and completeness/soundness review document, alongside soundness examples and Lean tutorial materials. * docs: close A5 in the completeness review; HonestTrace admits every run Rust's tracer produces * fix(lean): enforce verifier input and witness-size checks Require valid public inputs, the padded trace limit, the instance's bytecode domain, and the verifier's RAM minimum and maximum in ConstraintContext. Derive input, trace, and bytecode checks from the existing completeness inputs. Add the approved ramSizeAccepted premise to allConstraints_rust_sizes and explain it in English beside the theorem. Prover acceptance alone does not imply this check: the included Rust reproducer traces an empty ELF, obtains ram_K = 4 from the prover, and receives InvalidRamK with minimum 16 from the verifier. Its self-jump control passes the verifier input checks. Build regression checks for oversized outputs, a zero-based layout, invalid trace and bytecode domains, and RAM sizes outside the verifier bounds. Validation: lake build (8674 jobs), including both default-build checks; the saved Rust reproducer and self-jump control pass their observed checks. * fix(lean): keep RAM completeness explicit and kernel checked Remove the ramSizeAccepted premise and retain the general RAM-size obligation as a documented sorry, consistent with the existing Rust-bug convention. Prove the nonempty-image lower inequality when the verifier minimum computes. Replace native evaluation in the boundary checks with kernel evaluation and audit the relevant axiom dependencies. Validation: lake build passes (8675 jobs). * fix(lean): bind trusted advice to the instance Model trusted-advice contents directly in the fixed instance and retain untrusted advice and the execution tape as prover inputs. Prove that initialized trusted advice cannot vary with prover inputs and that AllConstraints is tape-independent. Include both new files and axiom checks in the default build. Document the separate tape-consistency obligation and the unmodeled bytes-without-commitment case. Validation: lake build JoltConstraints passed (8675 jobs). * refactor(lean): general runs, instance-level code assumption, Rust runtime advice - ValidRun: a run of the instance's program (expansion, initial state, start, step, link) with no stop rule or advice choice; HonestTrace now extends it. - JoltInstance.CodeUnchanged replaces the HonestTrace.code_unchanged field (a16z, 2026-10-07, #1952); honest traces get it from the premise. - HonestTrace.advice_from_rust: VirtualAdvice rows carry the values Rust's tracer patches in (SourceInstruction.rustAdvice for the division family, reusing the bytecode project's advice values); SC.W/SC.D left as a TODO. - Rv64ProgramImage.ImageNonempty: a16z (phone, 2026-10-08) says a program is never empty; premise of the final theorem and of witness_params_ram_bounds. - witness_params_ram_bounds: FIXME, upper half blocked by #1951 item 4 (compute_max_ram_k rounds down); docs updated to match. - Review notes: B1/B2 closed, B4 decision recorded. Validation: lake build JoltConstraints passes (8675 jobs). * fix(lean): chunk widths are the prover's choice; drop check scripts - WitnessParams keeps only Rust's structural chunk-width checks; the prover's 4/16-or-8/32 choice is WitnessParams.ProverChunkConfig, used only by completeness (HonestTrace.witness_params_chunk_config). Remove the unused chunkBits_lt_128. - Move ramAccessesValid out of the relation into Completeness/Helpers/RamAccesses.lean. - Remove the Checks/ build-time check scripts; import advice_inputs directly. - Docs: constraint_proving.md names the current trace facts; model_review.md header states the Rust checkout; review notes close B3 and record the soundness premises (2^127 < ringChar F, NextPCNoWrap, CodeUnchanged). Validation: lake build JoltConstraints passes (8673 jobs). * feat(lean): define expansions and prove expanded-row facts Bring SourceInstruction.expand over from the bytecode project (expansions.lean, expansion_helpers.lean), with LR/SC programs in place of the generator output. Check every expansion's rows in expansion_facts.lean and use them to prove expand_program_rows_valid and the six shift lookupEntryCorrect lemmas. Shift tables: LookupShiftProofHelpers.lean proves the SRL, SRA, SRLW and SRAW table loops on right-shift bitmasks. shiftMaskOk_of_honest shows every honest row reads such a mask: immediate masks are checked in the bytecode, and a register mask is written by the VirtualShiftRightBitmask row just before. Move the nextPC frame lemmas to nextpc_frame.lean. * chore(lean): drop unused pc_map_ok_iff Neither the completeness nor the soundness statement uses it; the proofs work with pc_map_ok directly. * feat(lean): state soundness, proof sorried JoltInstance.InLanguage is Jolt's claim: for some private inputs, Rust's run stops at the PC stall with the instance's outputs and panic flag. Soundness assumes a field of characteristic above 2^127, NextPCNoWrap and CodeUnchanged. Expected to fail today for programs that stop or panic in ways SDK guests never do (B6). * ci: bump lean-action to v1.6.1 (pins its cache actions by SHA) * refactor(lean): restate the advice field of HonestTrace, model the SC flag advice_from_rust now says, for every step n of the trace, that the value its VirtualAdvice writes is the value Jolt's tracer gives it (tracerAdviceAtStepN). The lookups are named helpers: the expansion a step belongs to, the advice steps before it, and the guest program's instruction at an address. They replace advice_ordinal and isVirtualAdvice. rustAdvice now gives SC.W and SC.D the tracer's reservation-success flag, computed from virtual registers 32 and 33, which Jolt's LR and SC expansions keep equal to the tracer's reservation. Before, an honest SC always failed. Comments on RuntimeAdvice, TraceRow and SourceInstruction reworded; model_review.md and the review method updated for the SC flag. * refactor(lean): name the honest tracer and some tracer in the advice block TraceRow.adviceValue? -> someTracerAdvice?, SourceInstruction.rustAdvice -> honestTracerAdvice, tracerAdviceAtStepN -> honestTracerAdviceAtStepN, advice_from_rust -> advice_from_honest_tracer. Comments say "the honest tracer (Jolt's tracer)" instead of "Rust". Docs updated for the new names. * feat(lean): bound the one-hot chunk width by the verifier's Dory setup The prover picks the one-hot chunk width; Jolt's verifier only checks it is nonzero, ordered and divisible. What bounds it is the commitment: Dory's verifier rejects an opening point with more coordinates than its setup has variables, and the one-hot chunks are opened at a point with logT + chunkBits coordinates. The setup size comes from the verifier's preprocessing. dorySetupLogN translates that setup size (setup_total_vars, rounded up to even); ConstraintContext gains oneHotFitsSetup. Soundness needs it to show each cycle selects exactly one bytecode row (2^chunkBits below the field's characteristic). Completeness proves it for the honest prover's sizes (witness_params_one_hot_fits_setup, no sorry); its statement is unchanged. * bug-report: SC.W forced failure is accepted by Jolt's verifier Honest prover outputs 7; a tracer that writes failure into the SC.W advice outputs 0x100, and Jolt's unmodified verifier accepts both proofs. Whether this is a bug depends on which claim a Jolt proof proves (asked in issue.md). Includes the reproducer (run.sh, make_elf.py, sc_flag.rs, tracer.patch) and the zip for the issue. * docs: comments on matches_outputs and withRuntimeAdvice; soundness plan matches_outputs now says which side is the instance's claimed bytes and which is the device the honest tracer writes. withRuntimeAdvice gets a comment. methods/soundness.md gains the layered plan for the soundness proof, what is done, and where we expect to get stuck. * feat(lean): soundness layers 0-4 and the start of layer 5 Layer 0: field-cast injectivity, the chunk-width bound from the verifier's Dory setup, and the 64-bit word bridge. Layer 1: chunk, bytecode, lookup and RAM selection, and the column reads at the selected entry. Layer 2: register-table histories, the canonical register map, initial-state agreement, and x0 staying zero. Layer 3: RAM histories and updates, access flags, address remapping, the layout bound and initial-state agreement. Layer 4: every lookup table interpreted at its recovered address; the pure table identities move here from the completeness helpers. Layer 5 (in progress): independent advice-tape answers with the fixed-tape inclusion, narrow advice loads, ADDI, register writes and memory addresses. Soundness premises: characteristic above 2^128 (Akita to be modeled separately), NoStoreConditional (temporary, a16z/jolt#2037), and RamRegionFits (to confirm with a16z). The plan in methods/soundness.md records each layer, the open obligations and the questions for a16z. bug-report/ram-address-wraparound shows the layout checks alone allow a RAM domain past 2^64. * refactor: generate LR and SC expansions in the automated module * feat(soundness): state temporary tracer address restrictions explicitly * feat(model): adopt PR 2039 deterministic store-conditional expansion * feat(soundness): prove XOR and SLTIU steps and SC advice absence * feat(model): migrate Jolt reference model to 8bbb2357 Model checked memory layout and prover sizing at the public Rust pin. Keep corrected sizing explicit and transfer completeness to Rust sizing when the highest touched slot is strictly inside the accepted RAM domain. Retain the merged advice-length removal, record reproducible LR/SC generator patches, and refresh trace, constraint and source correspondence across the six reviewed migration chunks. Validation: both Lean libraries build; 33 declarations axiom-audited. Existing sorry obligations and native bit-vector dependencies are retained and documented for separate review. No heartbeat limits are increased. * feat(theorems): bundle soundness and completeness assumptions Soundness takes a JoltField, JoltInstance.SoundnessAssumptions and JoltInstance.TemporaryRestrictions (Soundness/Assumptions.lean). Completeness is stated as JoltInstance.completeness over JoltInstance.CompletenessAssumptions and HonestTrace.ProverAccepts (Completeness/Assumptions.lean), at Jolt's own prover sizes. The preprocessed bytecode, entry slot and public I/O are now derived from the honest trace instead of taken as hypotheses. Declare HonestTrace and JoltDevice theorems outside the JoltConstraints namespace instead of with _root_. Note in JoltInstance that the PCS is ignored and how that differs from Jolt's verifier inputs. * feat(language): make L exactly what Jolt says it proves JoltInstance.InLanguage now also requires that Jolt's prover accepts the honest trace (prover_config.isSome) and that no spoil assert fails. Completeness becomes the clean statement: CompletenessAssumptions and InLanguage give private inputs and a witness satisfying AllConstraints. Helpers derive the corrected sizes from Jolt's prover accepting (corrected_prover_config_isSome), the preprocessed bytecode, the entry slot and the public I/O. HonestTrace.ProverAccepts is removed. * refactor: share the language file and drop unused sizing results Move the definition of Jolt's language to JoltConstraints/language.lean, imported directly by both soundness.lean and completeness.lean. Delete results nothing uses since completeness takes InLanguage: HonestTrace.allConstraints_rust_sizes, prover_config_eq_corrected_iff, prover_config_highest_le, prover_config_log_bounds, HonestTrace.witness_params, HonestTrace.highest_touched_slot, and the checkedTraceLength_bounds, checkedTraceLength_power and checkedRamSize_power lemmas. The private RAM helper keeps only the address fact the corrected-sizing lemma uses. * docs: record the language and completeness decisions L stays the fixed-tape language, and now includes Jolt's prover accepting the trace and its spoil asserts passing; the relaxed tape semantics is a proof device, and Layer 7 rebuilds one tape from the witness's answers. Update the soundness statement, the advice-tape sections, Layers 5a, 7 and 8, the status log, and the assumption table for the bundles and JoltInstance.completeness. * refactor(soundness): share Layer 5b register steps and advice bridge Factor canonical source recovery, lookup-write reasoning and successful register writes into shared lemmas used by XOR, SLTIU and ADDI. Apply runtime advice in their execution conclusions to match the trace API, retaining full post-states, alias coverage and last-cycle execution. Validation: both Lean libraries build (8751 jobs); the 11 affected public lemmas use only the standard Lean axioms and the allowed Sail constant. No new soundness premise, sorry, native proof or heartbeat override. * feat(soundness): prove six more Layer 5b instruction cases * feat(soundness): prove alignment and ordinary RAM store steps * feat(soundness): extend Layer 5b row coverage and temporary liveness Prove routine register, assertion, and ordinary RAM row steps; add the bytecode PC bridge and temporary HostIO/termination restrictions. Check each short-lived expansion temporary is written before its first read, excluding reserved LR/SC registers. Document the remaining dispatcher and device obligations. Validation: lake build JoltConstraints (8783 jobs); axiom audit of expand_temps_ok and host_call_exec_sail_frame. * docs(soundness): prepare Layer 5b fresh-session handoff Record the 74 proved row kinds, two unreachable kinds, twelve open steps, HostIO liveness boundary, remaining device and dispatcher work, and proof constraints. Reconcile migration summaries with the deleted unused Rust-sizes corollary and mark the older review as historical. * Add AUIPC and HostIO soundness steps; update Layer 5b handoff * Start top-down soundness; add panic restrictions and bug write-ups Soundness proof: - JoltInstance.soundness is now a short proof from sorried top-level helpers in Soundness/TopLevel.lean (tape, Represents, trace reconstruction, outputs, prover acceptance, spoil asserts), listed under "Proofs owed" in model_review.md. - New FIXME restrictions PanicStoresOne and ClaimsNoPanic (Layer5/PanicRestriction.lean) exclude B6's two panic counterexamples; both are Jolt bugs to be fixed upstream. - HostIO: expanded rows target temporary 40 (HostCallExpansion.lean), agreement except that temporary, and liveness lemmas; JAL step. Docs: - methods/soundness.md: change of course to top-down, the argument in brief, where to start, no known-false statements without approval. - model_review.md and the review doc point to the new plan. Blog: - One hidden-capable post per finding: panic definition, early stop at a jump, host-call destination, host-call failure, tracer address checks, termination word. - Hidden-post flag ([extra] hidden), tag taxonomy removed, feed without the site-wide last-updated date. * Checkpoint soundness proof and trace model * Checkpoint Layer 7 stopping proof integration Prove last-jump, termination-store, padding, JAL and branch replay lemmas; stage remaining JALR and no-store obligations as named declarations. * Checkpoint Layer 7 stopping work; add agent guidance and step (c) plan Prove both architectural JALR replay steps, split the last-row lemma into named cases, and extract the shared replay-step helpers. Five gaps remain in StallSuffix.lean; methods/step_c_closing_plan.md is the plan for closing them. AGENTS.md records the working rules for agents. StallSuffix.lean and TerminationStore.lean were last edited after the agent's final build report and have not been rebuilt since. * Step (c) plan: start by building the last unbuilt edits * Step (c) plan: add the tape rebuild's advice-pair check to Part 1 * Step (c) plan: state advicePairsOk in the exact form narrow advice programs emit * Step (c) plan: work on the branch head * Add the tape rebuild closing plan * Allow slower proofs only when significantly simpler * Accept bv_decide for now, tracked for later removal * Close step (c) stopping proof through shared expansion checks * Stage Layer 7 tape rebuild obligations * Prove advice masking and replay stop transfers * Prove spoil transfer and immediate masking * Prove RAM address and dimension transfer for tape replay * Prove rebuilt tape reads stay in low-byte prefix * Stage rowwise fixed-tape replay certificate * Replace native bitvector decision with bit-level proofs * Specialize replay helper to rebuilt tape initial state * Stage tape rebuild proof and cursor lemma * Prove tape cursor and non-read replay frames * Prove tape prefix, rebasing and honest-advice transfer lemmas The file elaborates; its only remaining sorry is rebuild_rows. * Construct fixed-tape replay rows from relaxed advice runs * Lift local instruction pairs to materialized bytecode rows * Share instruction preservation across tape and device frames * Close tape rebuild through shared adjacent-row properties --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Quang Dao <qvd@andrew.cmu.edu>
abiswas3
added a commit
that referenced
this pull request
Oct 27, 2026
…ons (#31) * docs(bug-report): document RAM address wrap completeness bug * Updated review * docs(model): record fork replay audit and deferred work * docs(spec): ai helper md file * docs(bug-report): mark CSRRS writeback issue fixed * refactor(model): centralize jump row validity * feat(constraints): prove next source PC for jumps * feat(constraints): prove next source PC for other rows * docs(constraints): record proof status and reusable method * docs: refresh constraint review after fork fast-forward * feat(constraints): prove register value history * test(ram): reproduce termination-word history failure * fix(trace)!: require only satisfiable row assumptions on JoltTrace `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * Prove register read/write constraints (34)-(36) Add source-operand helpers (register35_rs1?, register36_rs2?) with real-row characterisations of Rs1Ra/Rs1Value/Rs2Ra/Rs2Value and canonical-source address round-tripping, expose register42_registersVal_preState, and use them to prove honestWitness_rs1ValueEqRegistersRead, honestWitness_rs2ValueEqRegistersRead and honestWitness_rdWriteValueEqRegistersReadWrite. * refactor(constraints): name register-read helpers by content The helpers added for (34)-(36) were prefixed with the constraint number they were first written for, but (36) and (34) also use them. Name them after what they state instead. * fix(trace)!: require only satisfiable row assumptions on JoltTrace (#18) `JoltTrace.allAssumptions` required `all_assumptions` at every row's pre-state. Its `MstatusMppMachine` conjunct is false at `init_state`, whose virtual registers are zero as in Rust's `Cpu::new`, so every trace was empty and every constraint theorem over traces held vacuously. Replace the field with `rowAssumptions : TraceAssumptions ...`, which keeps only what the constraint proofs use: x-register readability, the LD/SD memory-window facts (`RamWindowAssumptions`), `CurPrivilegeMachine` and `MstatusMprvZero`. `TraceAssumptions.of_all_assumptions` shows it is weaker. Tests/TraceNonempty.lean builds a terminated one-row `jal x0, 0` trace from `init_state`, pins its axioms (no `sorryAx`), and shows `all_assumptions` fails at its pre-state. * feat(constraints): prove public RAM output constraint (26) (#15) * Prove public RAM output constraint (26) Execution preserves the device layout and both advice buffers (JoltIOSetupFrame.execInstr_rule, finalTraceState_ioSame). With the Rust-enforced JoltIOLayout.Valid and JoltIOState.AdviceFits premises on the initial device, the honest final RAM word equals the public I/O word on the public interval. * refactor(constraints): weaken (26) premise and derive it from Rust's layout Replace JoltIOLayout.Valid and JoltIOState.AdviceFits with the single premise the proof uses, JoltIOState.AdviceBelowInput: each advice buffer ends at or below input_start, and input_start is word-aligned relative to the lowest RAM address. Region ends and absolute alignment are no longer assumed. Add a transcription of MemoryLayout::new up to input_start and prove AdviceBelowInput.of_memoryLayout: every device laid out by Rust whose advice passes create_emulator's length checks satisfies the premise. * proof(constraints): prove (39) up to six shift/rotate table lemmas (#17) * proof(constraints): reduce (39) to per-table lookup entry lemmas Collapse the lookup-address and table sums in (39) to the flagged table's entry at the honest lookup index, then prove 47 of the 55 per-table entry lemmas. Six shift/rotate lemmas are false for arbitrary expanded bytecode (non-bitmask shift masks); Pext and PextSigned are open. Counterexamples in JoltConstraints/Tests/LookupShiftMask.lean. * proof(constraints): close the Pext and PextSigned lookup entry lemmas Prove that the PEXT table's pext (contiguous fast path plus the 64-pass clear-lowest-set-bit loop) equals jolt_virtual_pext_value, and that pextSigned equals jolt_virtual_pext_signed_value, in the new LookupPextProofHelpers.lean. Only propext, Classical.choice and Quot.sound. Constraint (39) now depends on six per-table sorries, the shift/rotate lemmas that are false for arbitrary bytecode. Drop the randomized Pext check from Tests/LookupShiftMask.lean, which the theorems supersede. * docs(human-comments): Started documenting program.lean * docs: scaffold Zola blog in isolated directory * chore(ci): remove blog publishing workflow * docs(site): publish initial Hello World site * fix(site): retain Pages marker across builds * chore: clean up capture API and audit docs * refactor: separate constraints from completeness proofs Give each constraint a dedicated definition module and a matching completeness module. Add the AllConstraints bundle and an aggregate honest-witness theorem, and clean up proof linter warnings. * fix(completeness): assemble branch constraint from its theorem Admit the individual branch completeness proof pending the Rust and Lean model reconciliation, then use it in the aggregate theorem without assuming the constraint as a premise. * docs(template): blog up No content of worth has been transferred yet. * refactor(isa): mirror Rust JoltDevice and MemoryLayout Replace JoltIOLayout and JoltIOState with Rust's MemoryLayout (all 20 fields) and JoltDevice, using Rust's names. Rename SailJoltState.io to jolt_device, ramStartAddress to RAM_START_ADDRESS, and the device byte functions to JoltDevice.load? and JoltDevice.store?, built on Rust's is_* region checks. Add STACK_CANARY_SIZE. * fix(isa): model LD and SD with Jolt's MMU LD and SD now read and write memory directly, following Rust's MMU, with Rust's checks as panics. The Sail equivalence proofs keep their Sail assumptions and add JoltRamLoadOk / JoltRamStoreOk. MemoryLayout and JoltDevice move to JoltISA/JoltDevice.lean. * refactor: remove dead LD/SD code and unused memory-window arguments * refactor: read HostIO bytes through Jolt's MMU and drop unused trace assumptions HostIO byte reads now follow Rust's Mmu::load (trace_load, then load_raw), so no Jolt instruction uses Sail's memory read. TraceAssumptions keeps only xRegReadable; the RAM window, privilege and MPRV facts and the per-row assumption operands are no longer needed. * docs: start a new model review * feat: add Rust-faithful program and honest trace models * feat: prove facts about the Jolt program's rows * feat: prove uniqueness and the preprocessed bytecode layout * feat: prove HostIO keeps Sail state and registers stay readable * feat: prove asserts hold, registers start at 0, and size RAM like Rust * feat: prove advice regions sit below the inputs * feat: prove the trace starts at the entry slot and LD/SD addresses are aligned * feat: add the memory address of a trace row; record completeness conditions * feat: model Rust's prover config (trace length and RAM size) * feat: prove the prover's trace length fits and pads the run * feat: prove the prover's RAM size holds every access; FIXME for upstream ram_K bug * feat: state the spoil assert condition * docs: explain when a guest reaches a spoil assert * docs: link every completeness assumption to its upstream issue * feat: model Rust's last-row-is-a-jump check (a16z/jolt#1968) * feat: assume executed code is unchanged (FIXME a16z/jolt#1952) * feat: make expand opaque; assume next PC does not wrap (asked a16z) * feat: build witness sizes from the prover config (rewiring phase 0) * refactor: witness columns read the honest trace (rewiring phase 1) * docs: list the store fact among the proofs owed * chore: delete the old program and trace models (kept in history at d044e63) * docs: bug report and repro for final RAM over 2 GiB * docs: drop the untested fix from the final RAM report * docs: FIXME exception for final RAM above 2 GiB (asked a16z) * refactor: prove the bytecode layout the completeness proofs use (rewiring phase 2) * refactor: Terminated and RamFits over the honest trace, proved for Rust's sizes * refactor: honest witness over the trace; Rust's sizes meet the old size assumptions (rewiring phase 2) * refactor: prove the old trace's facts for the honest trace (rewiring phase 3) * refactor: constraint proof helpers over the honest trace (rewiring phase 3) * refactor: completeness theorems over the honest trace (rewiring phase 4); a16z: PC must not wrap * remove Tests, written against the old trace (rewiring phase 5) * prove prover_config_log_ram_K_lt: ram_K comes from 64-bit addresses * prove address-0 accesses: an LD at 0 reads 0, no SD stores at 0; ramAccessesValid is alignment only, proved * final theorem at Rust's sizes; no completeness theorem takes Terminated; a16z: 2 GiB RAM limit is an assumption * a16z: a program must not change its own code is an assumption * final theorem against the verifier's public I/O: the instance's inputs, claimed outputs and panic flag * prove (16): a taken branch is never the last row (#1968), so the next row starts at PC + imm * model_review lists only open issues; drop the archives; rename program_fresh to program * refactor(lean): separate general traces from honest execution Define Trace independently of HonestTrace and express AllConstraints over a witness with its instance-bound ConstraintContext. Keep honest witness construction and completeness in the honest layer. Prove store-word presence for indexed rows of HonestTrace by induction, replace the unrestricted admitted lemma, and check its proof and witness dependencies in the default build. Share state-preservation rules across memory, Sail, and device proofs. Move completeness helpers out of the constraint relation layer and keep model_review focused on open issues. Validation: lake build (8672 jobs), including StoreWordPresence checks. * docs: add soundness review notes and Lean tutorials Add the soundness method and completeness/soundness review document, alongside soundness examples and Lean tutorial materials. * docs: close A5 in the completeness review; HonestTrace admits every run Rust's tracer produces * fix(lean): enforce verifier input and witness-size checks Require valid public inputs, the padded trace limit, the instance's bytecode domain, and the verifier's RAM minimum and maximum in ConstraintContext. Derive input, trace, and bytecode checks from the existing completeness inputs. Add the approved ramSizeAccepted premise to allConstraints_rust_sizes and explain it in English beside the theorem. Prover acceptance alone does not imply this check: the included Rust reproducer traces an empty ELF, obtains ram_K = 4 from the prover, and receives InvalidRamK with minimum 16 from the verifier. Its self-jump control passes the verifier input checks. Build regression checks for oversized outputs, a zero-based layout, invalid trace and bytecode domains, and RAM sizes outside the verifier bounds. Validation: lake build (8674 jobs), including both default-build checks; the saved Rust reproducer and self-jump control pass their observed checks. * fix(lean): keep RAM completeness explicit and kernel checked Remove the ramSizeAccepted premise and retain the general RAM-size obligation as a documented sorry, consistent with the existing Rust-bug convention. Prove the nonempty-image lower inequality when the verifier minimum computes. Replace native evaluation in the boundary checks with kernel evaluation and audit the relevant axiom dependencies. Validation: lake build passes (8675 jobs). * fix(lean): bind trusted advice to the instance Model trusted-advice contents directly in the fixed instance and retain untrusted advice and the execution tape as prover inputs. Prove that initialized trusted advice cannot vary with prover inputs and that AllConstraints is tape-independent. Include both new files and axiom checks in the default build. Document the separate tape-consistency obligation and the unmodeled bytes-without-commitment case. Validation: lake build JoltConstraints passed (8675 jobs). * refactor(lean): general runs, instance-level code assumption, Rust runtime advice - ValidRun: a run of the instance's program (expansion, initial state, start, step, link) with no stop rule or advice choice; HonestTrace now extends it. - JoltInstance.CodeUnchanged replaces the HonestTrace.code_unchanged field (a16z, 2026-10-07, #1952); honest traces get it from the premise. - HonestTrace.advice_from_rust: VirtualAdvice rows carry the values Rust's tracer patches in (SourceInstruction.rustAdvice for the division family, reusing the bytecode project's advice values); SC.W/SC.D left as a TODO. - Rv64ProgramImage.ImageNonempty: a16z (phone, 2026-10-08) says a program is never empty; premise of the final theorem and of witness_params_ram_bounds. - witness_params_ram_bounds: FIXME, upper half blocked by #1951 item 4 (compute_max_ram_k rounds down); docs updated to match. - Review notes: B1/B2 closed, B4 decision recorded. Validation: lake build JoltConstraints passes (8675 jobs). * fix(lean): chunk widths are the prover's choice; drop check scripts - WitnessParams keeps only Rust's structural chunk-width checks; the prover's 4/16-or-8/32 choice is WitnessParams.ProverChunkConfig, used only by completeness (HonestTrace.witness_params_chunk_config). Remove the unused chunkBits_lt_128. - Move ramAccessesValid out of the relation into Completeness/Helpers/RamAccesses.lean. - Remove the Checks/ build-time check scripts; import advice_inputs directly. - Docs: constraint_proving.md names the current trace facts; model_review.md header states the Rust checkout; review notes close B3 and record the soundness premises (2^127 < ringChar F, NextPCNoWrap, CodeUnchanged). Validation: lake build JoltConstraints passes (8673 jobs). * feat(lean): define expansions and prove expanded-row facts Bring SourceInstruction.expand over from the bytecode project (expansions.lean, expansion_helpers.lean), with LR/SC programs in place of the generator output. Check every expansion's rows in expansion_facts.lean and use them to prove expand_program_rows_valid and the six shift lookupEntryCorrect lemmas. Shift tables: LookupShiftProofHelpers.lean proves the SRL, SRA, SRLW and SRAW table loops on right-shift bitmasks. shiftMaskOk_of_honest shows every honest row reads such a mask: immediate masks are checked in the bytecode, and a register mask is written by the VirtualShiftRightBitmask row just before. Move the nextPC frame lemmas to nextpc_frame.lean. * chore(lean): drop unused pc_map_ok_iff Neither the completeness nor the soundness statement uses it; the proofs work with pc_map_ok directly. * feat(lean): state soundness, proof sorried JoltInstance.InLanguage is Jolt's claim: for some private inputs, Rust's run stops at the PC stall with the instance's outputs and panic flag. Soundness assumes a field of characteristic above 2^127, NextPCNoWrap and CodeUnchanged. Expected to fail today for programs that stop or panic in ways SDK guests never do (B6). * ci: bump lean-action to v1.6.1 (pins its cache actions by SHA) * refactor(lean): restate the advice field of HonestTrace, model the SC flag advice_from_rust now says, for every step n of the trace, that the value its VirtualAdvice writes is the value Jolt's tracer gives it (tracerAdviceAtStepN). The lookups are named helpers: the expansion a step belongs to, the advice steps before it, and the guest program's instruction at an address. They replace advice_ordinal and isVirtualAdvice. rustAdvice now gives SC.W and SC.D the tracer's reservation-success flag, computed from virtual registers 32 and 33, which Jolt's LR and SC expansions keep equal to the tracer's reservation. Before, an honest SC always failed. Comments on RuntimeAdvice, TraceRow and SourceInstruction reworded; model_review.md and the review method updated for the SC flag. * refactor(lean): name the honest tracer and some tracer in the advice block TraceRow.adviceValue? -> someTracerAdvice?, SourceInstruction.rustAdvice -> honestTracerAdvice, tracerAdviceAtStepN -> honestTracerAdviceAtStepN, advice_from_rust -> advice_from_honest_tracer. Comments say "the honest tracer (Jolt's tracer)" instead of "Rust". Docs updated for the new names. * feat(lean): bound the one-hot chunk width by the verifier's Dory setup The prover picks the one-hot chunk width; Jolt's verifier only checks it is nonzero, ordered and divisible. What bounds it is the commitment: Dory's verifier rejects an opening point with more coordinates than its setup has variables, and the one-hot chunks are opened at a point with logT + chunkBits coordinates. The setup size comes from the verifier's preprocessing. dorySetupLogN translates that setup size (setup_total_vars, rounded up to even); ConstraintContext gains oneHotFitsSetup. Soundness needs it to show each cycle selects exactly one bytecode row (2^chunkBits below the field's characteristic). Completeness proves it for the honest prover's sizes (witness_params_one_hot_fits_setup, no sorry); its statement is unchanged. * bug-report: SC.W forced failure is accepted by Jolt's verifier Honest prover outputs 7; a tracer that writes failure into the SC.W advice outputs 0x100, and Jolt's unmodified verifier accepts both proofs. Whether this is a bug depends on which claim a Jolt proof proves (asked in issue.md). Includes the reproducer (run.sh, make_elf.py, sc_flag.rs, tracer.patch) and the zip for the issue. * docs: comments on matches_outputs and withRuntimeAdvice; soundness plan matches_outputs now says which side is the instance's claimed bytes and which is the device the honest tracer writes. withRuntimeAdvice gets a comment. methods/soundness.md gains the layered plan for the soundness proof, what is done, and where we expect to get stuck. * feat(lean): soundness layers 0-4 and the start of layer 5 Layer 0: field-cast injectivity, the chunk-width bound from the verifier's Dory setup, and the 64-bit word bridge. Layer 1: chunk, bytecode, lookup and RAM selection, and the column reads at the selected entry. Layer 2: register-table histories, the canonical register map, initial-state agreement, and x0 staying zero. Layer 3: RAM histories and updates, access flags, address remapping, the layout bound and initial-state agreement. Layer 4: every lookup table interpreted at its recovered address; the pure table identities move here from the completeness helpers. Layer 5 (in progress): independent advice-tape answers with the fixed-tape inclusion, narrow advice loads, ADDI, register writes and memory addresses. Soundness premises: characteristic above 2^128 (Akita to be modeled separately), NoStoreConditional (temporary, a16z/jolt#2037), and RamRegionFits (to confirm with a16z). The plan in methods/soundness.md records each layer, the open obligations and the questions for a16z. bug-report/ram-address-wraparound shows the layout checks alone allow a RAM domain past 2^64. * refactor: generate LR and SC expansions in the automated module * feat(soundness): state temporary tracer address restrictions explicitly * feat(model): adopt PR 2039 deterministic store-conditional expansion * feat(soundness): prove XOR and SLTIU steps and SC advice absence * feat(model): migrate Jolt reference model to 8bbb2357 Model checked memory layout and prover sizing at the public Rust pin. Keep corrected sizing explicit and transfer completeness to Rust sizing when the highest touched slot is strictly inside the accepted RAM domain. Retain the merged advice-length removal, record reproducible LR/SC generator patches, and refresh trace, constraint and source correspondence across the six reviewed migration chunks. Validation: both Lean libraries build; 33 declarations axiom-audited. Existing sorry obligations and native bit-vector dependencies are retained and documented for separate review. No heartbeat limits are increased. * feat(theorems): bundle soundness and completeness assumptions Soundness takes a JoltField, JoltInstance.SoundnessAssumptions and JoltInstance.TemporaryRestrictions (Soundness/Assumptions.lean). Completeness is stated as JoltInstance.completeness over JoltInstance.CompletenessAssumptions and HonestTrace.ProverAccepts (Completeness/Assumptions.lean), at Jolt's own prover sizes. The preprocessed bytecode, entry slot and public I/O are now derived from the honest trace instead of taken as hypotheses. Declare HonestTrace and JoltDevice theorems outside the JoltConstraints namespace instead of with _root_. Note in JoltInstance that the PCS is ignored and how that differs from Jolt's verifier inputs. * feat(language): make L exactly what Jolt says it proves JoltInstance.InLanguage now also requires that Jolt's prover accepts the honest trace (prover_config.isSome) and that no spoil assert fails. Completeness becomes the clean statement: CompletenessAssumptions and InLanguage give private inputs and a witness satisfying AllConstraints. Helpers derive the corrected sizes from Jolt's prover accepting (corrected_prover_config_isSome), the preprocessed bytecode, the entry slot and the public I/O. HonestTrace.ProverAccepts is removed. * refactor: share the language file and drop unused sizing results Move the definition of Jolt's language to JoltConstraints/language.lean, imported directly by both soundness.lean and completeness.lean. Delete results nothing uses since completeness takes InLanguage: HonestTrace.allConstraints_rust_sizes, prover_config_eq_corrected_iff, prover_config_highest_le, prover_config_log_bounds, HonestTrace.witness_params, HonestTrace.highest_touched_slot, and the checkedTraceLength_bounds, checkedTraceLength_power and checkedRamSize_power lemmas. The private RAM helper keeps only the address fact the corrected-sizing lemma uses. * docs: record the language and completeness decisions L stays the fixed-tape language, and now includes Jolt's prover accepting the trace and its spoil asserts passing; the relaxed tape semantics is a proof device, and Layer 7 rebuilds one tape from the witness's answers. Update the soundness statement, the advice-tape sections, Layers 5a, 7 and 8, the status log, and the assumption table for the bundles and JoltInstance.completeness. * refactor(soundness): share Layer 5b register steps and advice bridge Factor canonical source recovery, lookup-write reasoning and successful register writes into shared lemmas used by XOR, SLTIU and ADDI. Apply runtime advice in their execution conclusions to match the trace API, retaining full post-states, alias coverage and last-cycle execution. Validation: both Lean libraries build (8751 jobs); the 11 affected public lemmas use only the standard Lean axioms and the allowed Sail constant. No new soundness premise, sorry, native proof or heartbeat override. * feat(soundness): prove six more Layer 5b instruction cases * feat(soundness): prove alignment and ordinary RAM store steps * feat(soundness): extend Layer 5b row coverage and temporary liveness Prove routine register, assertion, and ordinary RAM row steps; add the bytecode PC bridge and temporary HostIO/termination restrictions. Check each short-lived expansion temporary is written before its first read, excluding reserved LR/SC registers. Document the remaining dispatcher and device obligations. Validation: lake build JoltConstraints (8783 jobs); axiom audit of expand_temps_ok and host_call_exec_sail_frame. * docs(soundness): prepare Layer 5b fresh-session handoff Record the 74 proved row kinds, two unreachable kinds, twelve open steps, HostIO liveness boundary, remaining device and dispatcher work, and proof constraints. Reconcile migration summaries with the deleted unused Rust-sizes corollary and mark the older review as historical. * Add AUIPC and HostIO soundness steps; update Layer 5b handoff * Start top-down soundness; add panic restrictions and bug write-ups Soundness proof: - JoltInstance.soundness is now a short proof from sorried top-level helpers in Soundness/TopLevel.lean (tape, Represents, trace reconstruction, outputs, prover acceptance, spoil asserts), listed under "Proofs owed" in model_review.md. - New FIXME restrictions PanicStoresOne and ClaimsNoPanic (Layer5/PanicRestriction.lean) exclude B6's two panic counterexamples; both are Jolt bugs to be fixed upstream. - HostIO: expanded rows target temporary 40 (HostCallExpansion.lean), agreement except that temporary, and liveness lemmas; JAL step. Docs: - methods/soundness.md: change of course to top-down, the argument in brief, where to start, no known-false statements without approval. - model_review.md and the review doc point to the new plan. Blog: - One hidden-capable post per finding: panic definition, early stop at a jump, host-call destination, host-call failure, tracer address checks, termination word. - Hidden-post flag ([extra] hidden), tag taxonomy removed, feed without the site-wide last-updated date. * Checkpoint soundness proof and trace model * Checkpoint Layer 7 stopping proof integration Prove last-jump, termination-store, padding, JAL and branch replay lemmas; stage remaining JALR and no-store obligations as named declarations. * Checkpoint Layer 7 stopping work; add agent guidance and step (c) plan Prove both architectural JALR replay steps, split the last-row lemma into named cases, and extract the shared replay-step helpers. Five gaps remain in StallSuffix.lean; methods/step_c_closing_plan.md is the plan for closing them. AGENTS.md records the working rules for agents. StallSuffix.lean and TerminationStore.lean were last edited after the agent's final build report and have not been rebuilt since. * Step (c) plan: start by building the last unbuilt edits * Step (c) plan: add the tape rebuild's advice-pair check to Part 1 * Step (c) plan: state advicePairsOk in the exact form narrow advice programs emit * Step (c) plan: work on the branch head * Add the tape rebuild closing plan * Allow slower proofs only when significantly simpler * Accept bv_decide for now, tracked for later removal * Close step (c) stopping proof through shared expansion checks * Stage Layer 7 tape rebuild obligations * Prove advice masking and replay stop transfers * Prove spoil transfer and immediate masking * Prove RAM address and dimension transfer for tape replay * Prove rebuilt tape reads stay in low-byte prefix * Stage rowwise fixed-tape replay certificate * Replace native bitvector decision with bit-level proofs * Specialize replay helper to rebuilt tape initial state * Stage tape rebuild proof and cursor lemma * Prove tape cursor and non-read replay frames * Prove tape prefix, rebasing and honest-advice transfer lemmas The file elaborates; its only remaining sorry is rebuild_rows. * Construct fixed-tape replay rows from relaxed advice runs * Lift local instruction pairs to materialized bytecode rows * Share instruction preservation across tape and device frames * Close tape rebuild through shared adjacent-row properties * Update plan docs after closing Layer 7; add the Layer 6 plan Bring methods/soundness.md and model_review.md in line with the proof: Layers 7 and 8 are proved, and the only remaining sorries are the execution-prefix agreement and helpers C and D. Add methods/layer6/ with a plan for Layers 5b-6 split across five agents, and a runbook for starting them in methods/soundness.md. Mark the step (c) and tape rebuild plans as completed. * AGENTS.md: Claude is the auditor; reports must be checkable * Fix the witness-to-run relation for division advice; move the Jolt pin to 5def437f RepresentsRows no longer compares Rs1Value and Rs2Value at instruction temporaries (registers 40-47). For REM, REMU, REMW, REMUW and DIVUW with a zero divisor, every quotient advice passes the expansion's checks, so a witness may record advice other than the honest tracer's while the result is the same. The honest run and the witness then differ only in temporaries. represented_last_control_read takes a non-temporary hypothesis, discharged at its three callers by expanded_control_read_not_temporary. Record a16z/jolt main 5def437f as the current pin, cite the stopping rule at PR #2109's merge commit 0249591a, and refresh citations into changed Jolt files. Replace the old Layer 6 agent plans with the advice-fix and pin-update plans and reports. Relax proof-speed targets in AGENTS.md for now. * Model the verifier's 256-cycle floor; restrict full-length traces for helper C Jolt's base verifier now rejects trace lengths below 256 (a16z/jolt 521e53f6). ConstraintContext gains traceLengthFloor, and completeness supplies it from the prover's padding. This removes helper C's first counterexample. The remaining mismatch (a witness whose real rows fill the largest power-of-two length within the maximum is accepted by Jolt's verifier but refused by its prover) becomes the FIXME restriction NoMaxLengthTrace in TemporaryRestrictions. Helper C takes that field and is no longer known false; its DANGER labels are removed. C remains unproved. * Model operandless HostIO; drop the HostCallRdZero restriction Jolt now decodes VirtualHostIO with no operands and rejects a nonzero rd (a16z/jolt bc4bfb1f). Both instruction types lose HostIO's operands; its row names no destination or source, so the witness register table is unchanged and host_call_selected_step returns full RegisterAgreement via RegisterAgreement.noWrite. HostIO's executor still reads x10-x13 implicitly. The HostCallRdZero restriction, RegisterAgreementExceptHostTemp and its lemmas, and HostCallExpansion.lean existed only for the old rd=x0 rewrite to temporary 40 and are deleted. HostCallsSucceed remains. * Layer 6 stage 1: stage the execution-prefix induction over source instructions Move soundness_execution_prefix_agreement, statement unchanged, to Soundness/Layer6/PrefixAgreement.lean and prove it by strong induction from two staged lemmas: boundaryState_empty and boundary_step. The invariant BoundaryState holds between source instructions, where every expansion is complete: represented rows and RAM, real rows, no earlier stop, honest advice, the last row ending its source, register agreement outside instruction temporaries (new RegisterAgreementOn), a zero termination word before any termination store, and NextStep readiness for the next source instruction. * Remove finished plans; add the Layer 6 stage 2 plan The step (c), tape-rebuild, advice-fix, pin-update and Layer 6 stage 1 plans are done; their reports remain and git keeps the plans. Stage 2 splits boundary_step into expansion_start, row_step, division_block and expansion_finish. * AGENTS.md: cite Jolt by file and function; refresh only changed code * Layer 6 stage 2: split one source instruction into rows Prove boundary_step, statement unchanged, from four staged lemmas: expansion_start, row_step (one row of an expansion with no advice row), division_block (a whole advice-bearing division expansion, with honest advice in the run), and expansion_finish (the source-boundary transition). The new ExpansionState carries the boundary invariant inside an expansion, with scratch registers live only while the expansion is unfinished. * Add the Layer 6 reuse map and the stage 3a and 3b plans * Layer 6: one file per staged lemma; RegisterAgreementOn helpers Move the Layer 6 definitions to PrefixState.lean and each staged lemma to its own file (BoundaryStart, DivisionBlock, RowStep, ExpansionFinish), with no statement or proof changed, so agents can work in parallel. Add RegisterAgreementOn.mono and RegisterAgreement.toOn. * Layer 6 stage 3a: stage the division block Prove division_block, statement unchanged, from five staged steps: the source layout (one of eight divisions, rows = its generated program with the advice patched), the witness-advice run of a straight-line arithmetic sequence (generic over the twelve row kinds the divisions use), the final Sail result from *Program_sound, the honest run from *Program_concrete agreeing outside instruction temporaries, and appending the honest rows. * Layer 6 stage 3b: prove expansion_start; build the empty run expansion_start is proved. boundaryState_empty constructs the empty run and proves every BoundaryState field, staging two facts: first_row_of_entry_slot (the entry slot's row starts its source instruction) and empty_run_ram_word (the empty run's final RAM image equals the initial image outside the termination word). context_initial_termination_zero is now public. * Add the Layer 6 stage 4a and 4b plans * Layer 6 stage 4b: prove the entry row and the empty run's RAM word first_row_of_entry_slot and empty_run_ram_word are proved with their statements unchanged. The initial panic word is staged as initialized_device_words. * Layer 6 stage 4a part A: live-register step lemmas and the VirtualAdvice step The register helpers and the eight step lemmas used by the division expansions now work over RegisterAgreementOn; the full-agreement versions are derived from them. advice_step executes a VirtualAdvice row with the witness's range-checked word. * Layer 6 stage 4c: prove the initial panic word; share the I/O zero-word lemma initialized_device_words is proved, so boundaryState_empty no longer reaches sorryAx. initialRamWord_after_output_zero covers both the panic and termination words; first_address_row_starts is split out of first_row_of_entry_slot. * Add the Layer 6 stage 5b plan: close a source expansion * Stage 5b plan: the terminal next-PC lemma needs a real successor cycle * Layer 6 stage 5b: prove expansion_finish from the staged terminal next-PC lemma expansion_finish is proved with its statement unchanged. The witness's next unexpanded PC after a source's last row, given a real next cycle, is staged as terminal_next_unexpanded_pc. selected_interior_current_remaining is extracted from selected_interior_predecessor, and stall_next_cycle_of_target is public as next_cycle_of_target. * Add the Layer 6 stage 5c plan: the next PC after a source's last row * Remove finished Layer 6 stage plans * Add the Layer 6 stage 6a plan: append the honest division rows * Stage 6a plan: avoid an import cycle with a definitions file * Stage 6a plan: describe the uncommitted Part B work for a fresh agent * Layer 6 stage 4a part B and stage 5c: witness-advice run, terminal next PC Part B proves arithmetic_advice_sequence_run with its statement unchanged, through the plain row step for the twelve division row kinds; the other plain kinds are staged as plain_row_step_other. Stage 5c proves terminal_next_unexpanded_pc for ordinary, JAL and JALR rows; the branch case is staged as terminal_branch. * Stage 5c plan: the taken-branch target bound; stage 6a plan: Part B is committed * Add the Layer 6 stage 6b plan: live-register steps for the remaining plain rows * Stage 6a plan: moved private helpers may become public * Stage 6b plan: leave the two new step lemmas for a later stage * Layer 6 stage 6a: prove division_append_honest from three staged replay lemmas The shared division declarations move unchanged to DivisionDefs.lean; arithmetic_exec_eq becomes public. division_append_honest is proved with its statement unchanged from division_replay_rows, division_replay_complete and division_replay_honest_new, which are staged in DivisionAppend.lean. * Add the Layer 6 stage 6c and 6d plans; remove the finished 6a plan * Layer 6 stage 5c: prove the branch case; expansion_finish is sorry-free terminal_branch is proved with its statement unchanged: the real next row bounds the taken target, so it does not wrap. next_cycle_address is extracted from next_cycle_of_target, and represented_last_branch_registers from stall_suffix_initial_branch. * Docs: bring Proofs owed, the Layer 6 status and the Layer 5b handoff up to date List the ten remaining soundness sorrys with their current files, describe the Layer 6 induction as built, replace the stale Layer 5b handoff (deleted HostIO files, missing layer6/README.md) with the current step-lemma state, record the 5def437f pin, and add the Layer 6 cleanup items. * Docs: Layer 7 is proved except helpers C and D * Add the Layer 7 helper D plan: spoil asserts pass * Helper D plan: cite where SpoilAssertsPass is defined * Plan: do the unknown areas first (device-memory LD/SD, helper C); drop the deleted HostCallRdZero * Runbook: three agents, two strong and one medium * Layer 6 stage 6c (partial): prove division_replay_honest_new The appended division rows record the honest tracer's advice. Three private helpers become public with their statements unchanged. * Layer 6 stage 6b: port the remaining plain-row step lemmas to RegisterAgreementOn Fifteen Layer 5 families now take agreement on live registers, need their sources live and add their destination. ImmediatePowerMask and XorRotation are left unchanged because no source expansion emits their instructions. * Remove the finished stage 6b plan * Proofs owed: the two unreachable Layer 5 families * Layer 7 helper D: prove spoil asserts pass from one staged expansion fact binaryAssertion_passes_of_inputs extracts the shared constraint argument. D follows from it, RepresentsRows and the staged source_spoil_assert_read_not_temporary: no expansion emits a spoil assert that reads an instruction temporary. * Add the Layer 7 helper C staging plan * Remove the finished helper D plan * Add the helper D spoil-assert fact plan (checker extension, approved by Ari) * Helper D fact plan: the checker is a proof tool * Layer 7 helper C: stage its three parts and prove C from them represented_trace_ends_in_jump, checked_trace_length_fits and represented_ram_size_computes are staged in ProverAccepts.lean; C's statement is unchanged. * Remove the finished helper C staging plan * Runbook: agent 3 is the strong agent; agents 1 and 2 are medium * Add the device-memory LD/SD staging plan (largest remaining unknown) * Layer 6 stage 6d: prove the division program result and honest rerun division_program_result and division_program_honest move unchanged to DivisionPrograms.lean and are proved from the JoltBytecode *Program_sound and *Program_concrete theorems and one frame over the program rows. * Remove the finished stage 6d plan * Device memory: stage the LD and SD steps by region; no counterexample found ld_device_step and sd_device_step are staged over seven device regions with DeviceRamAgreement, the below-RAM part of the cut RAM agreement. The audit finds every region covered by the constraints and the current restrictions. * Remove the finished device-memory staging plan * Layer 6 stage 6c: prove division_replay_rows The honest program run is split into rows and appended to the prefix. zeroRuntimeAdvice is generalized to runtimeAdviceFor, which takes the word; four private PlainRow helpers become public with statements unchanged. * Add the device-memory step proof plan * Layer 7 helper D closed: the checker rules out spoil asserts in sequences noSpoilAsserts joins sequenceOk, and source_spoil_assert_read_not_temporary is proved from it and the native rows' temporary check. expansion_facts.lean checks in 17.5 s, up from 17.1 s. * Remove the finished helper D fact plan * Add the helper C padded-length plan * Layer 7 helper C: prove checked_trace_length_fits The row count plus one fits the largest power of two at most the public maximum, so checked padding succeeds within it. * Remove the finished helper C padded-length plan * Device memory: prove the LD and SD steps from five staged region lemmas sd_device_step and ld_device_step keep their statements. The table-column argument, address classification and byte packing are proved once; the store cases, the output, panic and termination store images, and the load image bytes are staged. * Device-memory proof plan: the five region lemmas * Proofs owed: the eleven remaining soundness sorrys at the end of 2026-10-23 * Device-memory report: the region lemmas need run-level device frames * Layer 6 stage 6c: the division prefix frame One parameterized frame proof now gives both DivisionFrame and, for rows that write only temporaries, Sail equality. division_program_prefix_frame: every proper prefix of a generated division program keeps the Sail state. division_replay_complete is still staged. * Runbook: agents 2 and 3 are strong; agent 1 is medium * Add the helper C ends-in-a-jump plan * Add the helper C RAM-size plan * Layer 7 helper C: prove the trace ends in a jump A branch stall cannot end a represented trace: its replay keeps a nonzero next unexpanded PC through every remaining witness cycle, contradicting the final zero shift and padding. The selection, branch-replay and branch-start proofs are extracted in StallSuffix.lean and the old lemmas derived from them. * Remove the finished helper C ends-in-a-jump plan * Layer 6 stage 6c: block-row frames for the division replay rs1Value_real and rs2Value_real now take any Trace. Candidate and replay pre-states keep the prefix frame at every block row, and equal final states give equal final RAM words. division_replay_complete is proved from these and the staged division_replay_complete_transfer. * Layer 7 helper C closed: prove represented_ram_size_computes Every recorded RAM address is zero or at least the layout's lowest address, and verifier sizing bounds the image end, so checked prover RAM sizing succeeds. Two private lemmas become public with statements unchanged. * Remove the finished helper C RAM-size plan * Device memory: prove the output-store image; run-level device frames UntrustedAnswerRun.device_relation, final_device_same and final_outputs_fit_layout are proved for any run, and the completed-trace versions are derived from them. device_output_store_image is proved; four region lemmas remain staged. * Add the row_step staging plan * row_step plan: prepared-state PC lemma and live-register steps (agent 2's audit) * row_step staging audit report * row_step plan: avoid the PlainRow/StallSuffix import cycle * Layer 6: stage row_step's per-kind steps and prove row_step from them The prepared-state PC fact is extracted from untrusted_row_pc and untrusted_row_next_pc, and the append constructor is generalized to rows that touch memory. row_step dispatches once on a small row classification to plain_row_step and seven staged steps: LD and SD for ordinary RAM and device memory, terminal HostIO, JAL and JALR. * row_step plan: next, prove the staged steps * Layer 6 stage 6c closed: prove division_replay_complete_transfer The bytecode source selectors equal the witness operands (bytecodeRs1Register_eq_rs1Operand, bytecodeRs2Register_eq_rs2Operand), and the honest replay inherits every ExpansionState field of the witness block. division_append_honest no longer depends on sorryAx. * Remove the finished stage 6c plan * Layer 5: the JALR step over live registers jalr_step reads the live base before writing the link, clears target bit zero, and adds the destination to the live set. * Device memory closed: prove the four remaining region lemmas The shared instruction frame gains a variant whose byte-store obligation applies only to SD; every existing public frame statement is derived from it. With it, a non-SD row keeps the whole device, the run keeps panic false until a recorded panic store, and the store-cases, panic, termination and load region lemmas are proved. ld_device_step and sd_device_step no longer depend on sorryAx. * Remove the finished device-memory proof plan * Add the branch/tape-load step and division layout plan * Branch step: derive target alignment from the witness; record the odd-offset model gap Agent 3 found that an image may contain an odd branch offset, which Jolt's decoder never produces, and that a taken one fails Sail's alignment assertion. A satisfying witness can never take it, so the branch step gets alignment from the next cycle, as terminal_branch does. * Layer 6: prove the JALR and LD/SD row steps ramAgreement_iff splits the cut RAM agreement into ordinary-RAM and device agreement once, for both the next and final columns. One append per instruction family covers both address regions. ld_ram_step, sd_ram_step and sd_ram_step_agreement now take agreement on live registers; five shared append helpers become public with statements unchanged. * Add the plain_row_step_other plan * Layer 5: port jal_step and host_call_selected_step to live registers * plain_row_step_other plan: corrections from the audit (reuse as is; move helpers down) * Layer 6: prove the JAL, HostIO and scalar plain-row steps; branch step and misa invariant jal_row_step and host_row_step are proved from a shared append for non-memory rows. plain_row_step_other is proved for every scalar kind; the branch and tape-load cases remain staged. The branch step derives target alignment from the witness and uses the preserved misa value. * Plans: reassign the tape-load step; the checker does not check advice-load widths * Plain steps report: branch step, misa invariant, advice-load width counterexample * plain-row-other plan: tape-load step over any destination, not adviceLoad_step * Division layout: move the staged theorem below DivisionPrograms (plan correction and report) * Layer 6: prove the branch and tape-load plain rows; plain_row_step_other closed The checker now requires advice-load byte counts of 1, 2, 4 or 8. The tape-load step covers any destination and shares the range-check written-value lemma with advice_step. Delete the finished plan. * Layer 6: prove division_expansion_layout (moved unchanged into DivisionLayout.lean) Every soundness lemma now has a proof. Delete the finished plan. * Plan: remove every bv_decide, the soundness use first * bv_decide plan: correct the use count * Docs: soundness theorem closed; record axioms, bv_decide list and cleanup list Full build passes at b9efd78; #print axioms JoltInstance.soundness lists propext, sys_enable_experimental_extensions, Classical.choice, Quot.sound and one bv_decide axiom (branch_target_toNat), with no sorryAx. Replace the stale Layer 6 runbook, order and handoff sections with the remaining work. * bv_decide removal: limit scope to the soundness path (Ari) * bv_decide removal: restore Part 2 (all 19 older uses go too, after Part 1 is pushed) * Remove the soundness-path bv_decide: prove the branch-offset sign extension bit by bit encoded_branch_offset now has a bit-level proof, and branch_target_toNat uses it. After a full build, #print axioms JoltInstance.soundness lists only propext, sys_enable_experimental_extensions, Classical.choice and Quot.sound. * Docs: soundness axioms after removing its bv_decide * Remove the remaining 19 bv_decide uses Shared bit-mask lemmas (BitVecMasks.lean) replace the mask, alignment and byte-extraction uses in Splice, Sd and the lookup-entry helpers; the byte-swap facts reuse the soundness proof's kernel-checked lemmas, moved unchanged into Layer4/ByteSwap.lean. No statement changed. The full build passes (8,832 jobs); JoltInstance.soundness and JoltInstance.completeness depend on no bv_decide axiom. * Delete the rev8_32 and rev8w_eq wrappers; use the shared byte-swap lemmas directly Full build passes (8,832 jobs); soundness and completeness axioms unchanged. * Docs: no bv_decide remains; record the axioms; delete the finished plan * bv_decide report: agent's recheck after the cleanup * chore: remove obsolete blog site and update writeup references --------- Co-authored-by: Quang Dao <qvd@andrew.cmu.edu>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR proves constraint (26):
honestWitness_ramOutputEqPublicIo, the public RAM output check in stage 2. On the public I/O interval, the honest final-RAM column equals the public I/O word. The interval runs frominput_startup to, but not including,RAM_START_ADDRESS.The proof has one premise, on the initial device:
JoltIOState.AdviceBelowInput. It says:input_start;input_startis a whole number of words above the lowest RAM address.This PR also proves that Rust always satisfies the premise, instead of arguing it in prose.
JoltIOState.AdviceBelowInput.of_memoryLayoutderives it from a Lean transcription ofMemoryLayout::newtogether withcreate_emulator's advice-length checks.The
sorryis gone, the stale FIXME row inmodel_review.mdis removed, andconstraints.mdnow reads 57 proved, 2 TODO ((38), (39)), 3 FIXME.Diff metadata
qvd/registers-34-36. The merge base is0d9fa22a0a28cf7711c2460eabe1c7770481ad80, which now sits ona3e7aad0fec915b7a887e4a73aef7a70245f7ea1(qvd/trace-assumptions, fix(trace)!: require only satisfiable row assumptions on JoltTrace #18).1a2e4bd0dc2aa2450a2cc8aaceca0c8d6b57f370Motivation
The (26) target carried a FIXME. It said the Lean model did not enforce Rust's layout and buffer bounds, and that this boundary had to be audited before attempting a proof, without restricting Rust-valid executions.
Without some such bound, the statement is false. Take an advice buffer that runs past
input_startwithinputsempty. Its bytes land infinalRamWordinside the window, whileramPublicIoWordis 0 there.How trivial is (26)?
It is mostly definitional, and that is faithful to Rust.
final_ram_state(crates/jolt-witness/src/backend/trace/ram.rs#L134) builds the honest final RAM as layers taken from the device, in this order:memory image → trusted advice → untrusted advice → inputs → outputs → panic → termination
The verifier's public word stacks the last four layers from the same device, and the Lean definitions copy this structure. So (26) says only that the first three layers stay out of the I/O window.
Whether the device's
outputs/panicagree with what the program actually stored is the job of (38), not (26). A blind read-back of the statement independently reached the same conclusion.The premise, and why Rust always satisfies it
JoltIOState.AdviceBelowInput, inConstraints/RamReadData.lean:This is exactly what
overlayRamBytes_below_inputconsumes. Nothing about region ends, absolute alignment, advice-start alignment, or the output/panic/termination placement is assumed.inputWordAlignedis necessary. With 4 bytes of advice at the lowest address andinput_start4 bytes above it, the first masked word still contains advice.Constraints/MemoryLayout.lean(new) transcribesMemoryLayout::newup toinput_start(common/src/jolt_device.rs#L346-L438) asadviceInputLayout : MemoryConfig → Option AdviceInputLayout.align_up, the checked sums,next_power_of_twoandRAM_START_ADDRESS - io_bytes. Each Rust panic becomesnone.input_start. These only add panics, so the Lean function succeeds on at least every configuration Rust accepts.JoltIOState.AdviceBelowInput.of_memoryLayoutthen showsio.AdviceBelowInputfrom:adviceInputLayout config = some layout;create_emulator,tracer/src/lib.rs#L382-L393.Sanity check.
#eval adviceInputLayout ⟨0, 0, 0, 8⟩givesinput_start = 0x7fffffe0. That matches the layout printed by the Rust tracer harness for the sameMemoryConfigat a16z/jolt754fc88.The theorem keeps the weaker premise rather than taking "the layout is
MemoryLayout::new config" as a hypothesis, so it applies to any device with the property. The bridge theorem connects it to Rust.Proof structure
IOSetupFrame.lean(new). A frame calculus overJoltMonad:Same,Preserves, thepure/bind/lift/throw/get/modifyrules, register, memory and device rules,execHostIO_ruleandexecInstr_rule. No instruction changes the layout or either advice buffer.finalTraceState_ioSamelifts this to the whole trace, which matters becausefinalRamWordremaps with the initial layout.overlayRamBytes_below_input. An overlay whose bytes end at or belowinput_startis the identity from the input's first word onward. It needs no alignment of the overlay start: remapping rounds down, which only moves bytes lower.finalRamWord_eq_ramPublicIoWord. Inside the mask,L + 8a < RAM_START_ADDRESS, so the memory-image layer is 0. Both advice layers drop out by step 2. The remaining overlays are syntactically equal.honestWitness_ramOutputEqPublicIo. Closes withsub_selfinside the mask.Trust surface
adviceBelowInput, on the initial state, with the machine-checked bridge above.adviceInputLayoutis hand-transcribed from Rust, the same trust model asJoltISA.sorry,admit,native_decideor option changes (other thanautoImplicit false).#print axioms honestWitness_ramOutputEqPublicIo:load_reservation,match_reservation,plat_term_writeandsys_enable_experimental_extensions, which are existing LeanRV64D Sail platform axioms (I did not trace which lemma pulls them in), pluspropext,Classical.choiceandQuot.sound.#print axioms JoltIOState.AdviceBelowInput.of_memoryLayout:propext,Classical.choice,Quot.sound.final_ram_stateremaps withpreprocessing.memory_layoutbut reads start addresses fromdevice.memory_layout. Lean uses one initial layout for both. If the two can differ, the model diverges.dram_startis always ≥RAM_START_ADDRESS. Lean gates the memory image atRAM_START_ADDRESS.Commit map
280da7e: I/O frame, the (26) proof, and a first pair of premises,JoltIOLayout.ValidandJoltIOState.AdviceFits.1a2e4bd: replaces those with the single weakerAdviceBelowInput, and adds theMemoryLayout::newtranscription plusof_memoryLayout.Validation at
1a2e4bdlake buildsucceeded locally (8589 jobs).#evalof the transcription matches the executed Rust layout.0d9fa22adds nosorry,admit,native_decideoraxiom.Remaining work
input_start. Later theorems, such as (38) and constraint soundness, will want the rest ofMemoryLayout::new.Reviewer map
Constraints/RamReadData.lean:AdviceBelowInput.Constraints/MemoryLayout.lean: compareadviceInputLayoutline by line withjolt_device.rs#L346-L438, thenof_memoryLayout.Constraints/RamOutputEqPublicIo.lean: the theorem statement, then the two lemmas.Constraints/IOSetupFrame.lean: skim the rules; readexecInstr_ruleandfinalTraceState_ioSame.constraints.md,model_review.md.AI usage
Claude Code (Opus) found the premise, checked it against the Rust source, transcribed
MemoryLayout::new, and wrote the proofs under my direction. A separate Claude Code agent did a blind read-back of the statement. I reviewed the statement, the premise, the transcription and the Rust citations, and confirmed the build and axiom footprints locally.🤖 Generated with Claude Code