diff --git a/.github/workflows/lean_action_ci.yml b/.github/workflows/lean_action_ci.yml index 5fef6799..0c08132b 100644 --- a/.github/workflows/lean_action_ci.yml +++ b/.github/workflows/lean_action_ci.yml @@ -16,6 +16,8 @@ jobs: - uses: actions/checkout@v4 - name: No skipKernelTC in source run: scripts/check_no_skipkerneltc.sh + - name: No native_decide in the main SP1Clean library + run: scripts/check_no_native_decide.sh build: runs-on: @@ -31,6 +33,11 @@ jobs: - uses: actions/checkout@v4 # Test that the project builds without failure # Note: defaults to ignore warnings and deprecations, maybe change eventually + # lean-action's `use-github-cache` caches the WHOLE `.lake` (project build + deps) under a + # commit-keyed key (`lake-----`) with a manifest-prefix + # `restore-keys` fallback. So this build is incremental from the most recent prior commit, and the + # exact-`` cache it saves is what the `lint`/`test` jobs restore (an exact hit → `lake build` + # there just replays, no recompilation). No separate per-commit project cache is needed. - uses: leanprover/lean-action@v1 with: build: "true" @@ -38,3 +45,62 @@ jobs: lint: "false" use-github-cache: "true" + lint: + # Environment linters (`lake lint` → the `sp1Lint` lintDriver; a curated subset of the Batteries + # `#lint` linters over the hand-written `SP1Clean.*` declarations; see AGENTS.md § Linters). Split + # into its own job so it is clearly independent in the checks list. `needs: build` so it runs after + # the build job has saved its exact-`` `.lake` cache; lean-action below restores that on an exact + # hit, so `lake build` just replays the already-built project (no recompilation) before `lake lint`. + # If that cache were ever absent, lean-action's manifest-prefix `restore-keys` fallback does a correct + # incremental build instead, so the job stays correct either way. + needs: build + runs-on: + [ + runs-on, + runner=64cpu-linux-x64, + spot=false, + disk=large, + "run-id=${{ github.run_id }}", + ] + + steps: + - uses: actions/checkout@v4 + - uses: leanprover/lean-action@v1 + with: + build: "true" + test: "false" + lint: "false" + use-github-cache: "true" + - name: Environment linters (lake lint) + run: lake lint + + test: + # Conformance tests (`lake test` → the `SP1CleanTest` testDriver: the witness-generation and + # whole-trace `native_decide` anchors checked against batteries dumped from SP1's real Rust prover). + # These live in their own library — the ONLY place `native_decide` is allowed — so they are gated + # here, separately from the `native_decide`-free main build. Split into its own job (in parallel with + # `lint`) so the trusted-compiler checks are clearly independent in the checks list. `needs: build` + # so it runs after the build job has saved its exact-`` `.lake` cache; lean-action below restores + # that on an exact hit, so `lake build` replays the already-built main library (no recompilation) and + # `lake test` only elaborates the test modules. If that cache were ever absent, lean-action's + # manifest-prefix `restore-keys` fallback does a correct incremental build instead. + needs: build + runs-on: + [ + runs-on, + runner=64cpu-linux-x64, + spot=false, + disk=large, + "run-id=${{ github.run_id }}", + ] + + steps: + - uses: actions/checkout@v4 + - uses: leanprover/lean-action@v1 + with: + build: "true" + test: "false" + lint: "false" + use-github-cache: "true" + - name: Conformance tests (lake test) + run: lake test diff --git a/AGENTS.md b/AGENTS.md index 2123de2d..07805dca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,7 +39,11 @@ The SP1 Rust source (the extraction/spec oracle, read-only reference) lives in a ## Build - Full build: `lake build SP1Clean` (the default target). Passing = **0 errors AND 0 warnings**, and - **no stray `info:` notes** — leave the build output clean (see the `ring` note below). + **no stray `info:` notes** — leave the build output clean (see the `ring` note below). This builds + **only the main library** — it carries no `native_decide` (gated by `scripts/check_no_native_decide.sh`). +- Tests: `lake test` (the `SP1CleanTest` `testDriver`). Builds/elaborates the witness- and trace-conformance + anchors — the project's **only** `native_decide` — checked against batteries dumped from SP1's real Rust + prover. Runs on top of the cached main-library oleans (the test lib imports `SP1Clean`, never vice-versa). - Single file: `lake env lean SP1Clean/Proofs/Chips/AddChip/Formal.lean` (builds deps from cache, then elaborates). ⚠️ `lake env lean ` **exits 0 even on a Lean stack overflow**, and a stale cached olean can make downstream checks pass falsely — **always finish a phase with `lake build SP1Clean`**. @@ -49,7 +53,8 @@ The SP1 Rust source (the extraction/spec oracle, read-only reference) lives in a A `run_in_background` build can outlive its shell — check with `ps -ef | grep -E "lake|lean" | grep -v lsp` before spawning another. The lean LSP server (`uvx lean-lsp-mcp`) also keeps several GB warm. - **Toolchain (pinned, do not bump):** `lean-toolchain` = `leanprover/lean4:v4.28.0`; mathlib `v4.28.0`; - Clean from `github.com/Verified-zkEVM/clean @ main`. Sail comes from two `github.com/succinctlabs/*` deps + Clean pinned to a specific commit on `github.com/Verified-zkEVM/clean` (the PR #398 head — re-pin to + `@ main` once it merges; see `lakefile.toml` + roadmap W9). Sail comes from two `github.com/succinctlabs/*` deps pinned to the `dtumad/clean-native` branch — `LeanRV64D` (`sail-riscv-lean`, the generated RV64 model) and `RISCV` (`riscv-lean`, the lightweight ISA fns) — which transitively pull the `rems-project/lean-sail @ v4` runtime; each carries a 4.28 `lean-toolchain`. All deps are fetched by `lake build`; nothing is a local @@ -90,22 +95,26 @@ Mirror-rust layout under `SP1Clean/`: - **`Proofs/`** — the "proven sound/complete" pillar: `Proofs/Chips/Chip/{Formal,Bridge,…}.lean` (soundness/completeness/`circuit` + the Sail bridge; the `Spec` is in `FormalModel/Contracts/Chips.lean`, the ALU chips' `Assumptions`/`ProverAssumptions` in `Contracts/ChipAssumptions.lean`), - `Proofs/Operations//Formal.lean` (the `FormalAssertion` soundness/completeness), and - `Proofs/WitnessTests/*.lean` (the `Witness.lean` `native_decide` conformance anchors + - `WitnessConformance.lean`, in namespace `SP1Clean.WitnessTests`; their auto-gen vectors are under - `Extracted/WitnessVectors/`). Flat receiver infra (`ByteChip`/`ProgramChip`/`MemoryProvider`) sits in + `Proofs/Operations//Formal.lean` (the `FormalAssertion` soundness/completeness). Flat receiver + infra (`ByteChip`/`ProgramChip`/`MemoryProvider`) sits in `Proofs/Chips/`. The 3 entangled complex chips (DivRem/ShiftLeft/ShiftRight) stay **whole** in `Proofs/Chips/` — their `Defs` import sibling proof files, so they are not split into `Native/`. - **`Faithful/`** — the "proven faithful" pillar: the per-operation/chip constraint anchors (`.lean`). -- **`Proofs/TraceGenTests/`** — the **full-trace** chip testing layer, kept **whole** in `Proofs/` (the - auto-gen `ChipTraceVectors.lean` batteries import the hand-written scaffold types, so they are - *not* split into `Extracted/` like `WitnessVectors/`): the **circuit-as-trace-generator** - (`TraceGenerator.lean` + `EventPopulate.lean` + `Conformance.lean` + `ChipTrace{Vectors,Witness}.lean`) - — whole chip traces derived from the chips' own `main` witness closures + output struct, - `native_decide`-checked against SP1's real `generate_trace`. 10 chips, **all unmasked**: - Add/Sub/Subw/Addw fixed-witness; Mul/DivRem/Bitwise/Lt/ShiftLeft/ShiftRight hint-driven flags (per-event - `ProverHint` from the dumped executor opcode). Namespace `SP1Clean.TraceGenTests`; vectors regenerated by - `update_extracted.py` (the `TRACEGEN_DIR` writer). +- **`SP1CleanTest/`** (top-level, **not** under `SP1Clean/`) — the **test library**, the sole home of + `native_decide` and the `lake test` target (`testDriver`). It imports the main `SP1Clean` library and + is never imported by it, so the default `lake build SP1Clean` stays `native_decide`-free (enforced by + `scripts/check_no_native_decide.sh`; `native_decide` trusts the whole compiler, adding + `Lean.ofReduceBool`/`Lean.trustCompiler`). Two layers, namespaces preserved (`SP1Clean.WitnessTests` / + `SP1Clean.TraceGenTests`, decoupled from the new module paths): + - `WitnessTests/` — the `Witness.lean` witness-generation conformance anchors + + `WitnessConformance.lean` scaffold; auto-gen vectors under `WitnessTests/Vectors/`. + - `TraceGenTests/` — the **circuit-as-trace-generator** full-trace layer (`TraceGenerator.lean` + + `EventPopulate.lean` + `Conformance.lean` + `ChipTrace{Vectors,Witness}.lean`): whole chip + traces derived from the chips' own `main` witness closures + output struct, `native_decide`-checked + against SP1's real `generate_trace`. 10 chips, **all unmasked**: Add/Sub/Subw/Addw fixed-witness; + Mul/DivRem/Bitwise/Lt/ShiftLeft/ShiftRight hint-driven flags (per-event `ProverHint` from the dumped + executor opcode). + Both vector batteries are regenerated by `update_extracted.py` (the `WITNESS_DIR`/`TRACEGEN_DIR` writers). - **`Soundness/`** — the whole-machine layer: per-bus `{State,Byte,Program,Memory}Consistency.lean`; `ChipRow.lean` (the `ChipKind` structure-of-functions — each chip registers one `kind`, carrying a `name` = its SP1 `MachineAir::name`) + `ChipRegistry.lean` (`allChipKinds`); the gated execution capstone @@ -128,18 +137,27 @@ Mirror-rust layout under `SP1Clean/`: any fully-qualified name (only `import` lines and tooling path-globs follow the move). Keep it that way. **Lake libraries** (`lakefile.toml`): the umbrella `SP1Clean` (the default target — its root index imports -everything, so `lake build` builds the whole project) plus per-pillar build-targets `SP1Math` / `SP1Model` / -`SP1Extracted` / `SP1FormalModel` / `SP1Native` / `SP1Proofs` (selected by submodule globs, e.g. +the whole **main** library, so `lake build` builds all of it) plus per-pillar build-targets `SP1Math` / +`SP1Model` / `SP1Extracted` / `SP1FormalModel` / `SP1Native` / `SP1Proofs` (selected by submodule globs, e.g. `"SP1Clean.Math.+"`; `SP1Proofs` groups `Proofs` + `Faithful` + `Soundness`). `lake build SP1Extracted` -builds just that layer. Isolation is **by convention** — Lake does not forbid cross-layer -imports within one package; the auto-gen guard is the `Extracted/` "do not hand-edit" headers + the sole -writer `update_extracted.py`. +builds just that layer. Separately, the top-level **test** library `SP1CleanTest` (glob `"SP1CleanTest.+"`, +the `testDriver` → `lake test`) holds the witness/trace conformance anchors; it imports `SP1Clean` but is +**not** part of the umbrella, so `lake build SP1Clean` never compiles it (keeping the main build +`native_decide`-free). Isolation is **by convention** — Lake does not forbid cross-layer +imports within one package; the auto-gen guard is the `Extracted/` + `SP1CleanTest/**/Vectors`/`*TraceVectors` +"do not hand-edit" headers + the sole writer `update_extracted.py`. -**Restructure status (2026-06-16).** Landed & green: the `Math`/`Model` split, `Extracted/` auto-gen +**Restructure status (updated 2026-06-23).** Landed & green: the `Math`/`Model` split, `Extracted/` auto-gen consolidation (`Circuit/` + `WitnessVectors/`), the `FormalModel/Contracts/` audit surface (all `Spec`s + the ALU chips' `Assumptions`/`ProverAssumptions`), the `Native/`+`Proofs/` five-pillar re-bucket of `Chips`/`Operations`/`Readers`/`WitnessTests`, and all six per-pillar layer libraries — build green -(3633 jobs), audit clean (314 probes, `sorryAx` confined to the known 4-sorry debt). Planned (approved +(3676 jobs), audit clean (366 probes, `sorryAx` confined to the known debt). **DivRem/ShiftLeft/ShiftRight/Mul +completeness are all closed** (2026-06-12 / 06-18), leaving **one** remaining `sorry` — `sp1_witness_decode` +(the capstone W1b/W1c decode seam). A large proof-cleanup campaign (2026-06-22 / 06-23) golfed ~109 +hand-written files (−591 lines) while preserving axiom-cleanliness, plus substrate-hoist refactors +(`Word.isU64_four`, Faithful `val_16`/`bool_iff` dedup → `ChipTactics`). Upstream `main` was merged in +2026-06-23 (#100 hard-gates `skipKernelTC` and removes overrides; #101 fleshes out the immediate-type Sail +bridges; #102 makes the jalr/jal/branch specs explicit about divisibility / LSB-clearing). Planned (approved plan, not yet done): lifting the remaining chips' `Assumptions` onto the audit surface (helper-dependent chips Jal/Jalr/Branch/DivRem/ShiftLeft/ShiftRight + split-`Spec` Lt/Bitwise keep theirs in their proof files). The guest-program execution model was surfaced in `FormalModel/Trace/GuestProgram.lean` (Phase 5); @@ -178,8 +196,39 @@ These are the keepers from sp1-lean's "faithful sub-circuit composition" discipl - `circuit_proof_start` (from `Clean.Utils.Tactics`) is the **first** tactic in soundness/completeness proofs; any `haveI`/`set_option` must come after it, or it errors "can only be used on Soundness/Completeness". -- Imports MUST precede the module doc-comment (the package `-D linter.flexible` flag is rejected on a - zero-imports header). +- Imports MUST precede the module doc-comment (a `-D linter.*` flag can't be validated against a header that + opens with a doc-comment before its imports — the linter's registration module isn't in scope yet; same + "Step 0" reason the package `[leanOptions]` carries no Mathlib linter flags, see `lakefile.toml`). +- **Linters — two kinds.** *Syntactic* linters run during `lake build` (option-gated); *environment* + linters run as a separate `lake lint` pass over the built environment. + - **Syntactic.** Every hand-written **core** pillar lake library enables the same eight `-D linter.*` flags + (via its `moreLeanArgs`): `SP1Math`, `SP1Model`, `SP1FormalModel`, `SP1Native`, and `SP1Proofs` + (`Proofs/`+`Faithful/`+`Soundness/`). The flags: `style.lambdaSyntax`/`style.dollarSyntax`, the four + deprecated-tactic guards `style.refine`/`style.cases`/`style.induction`/`style.admit`, and + `oldObtain`/`style.cdot` — all at **zero** violations. They apply during the normal `lake build SP1Clean`. + `linter.style.longLine` is the remaining candidate (real fallout, concentrated in Native/FormalModel; see + `docs/roadmap.md` § "Cleanup / polish backlog"). The flags are scoped **per-lib** rather than at package level so the + auto-gen `SP1Extracted` library stays out of the set (it carries per-file `set_option linter.all false`); + keep the five identical `moreLeanArgs` copies in `lakefile.toml` in sync. (Every hand-written pillar + transitively imports Mathlib — Native via Clean — so the `-D linter.*` options register fine; the older + "Clean-only Math/Model lack the registration" note was stale. Still true: a file's imports must precede + its module doc-comment, or the `-D` flag can't be validated — see the Step 0 bullet above.) + - **Environment (`lake lint`).** Run `lake lint` (after a build — it imports the oleans) for the Batteries + `#lint` checks. The driver is `scripts/sp1Lint.lean` (package `lintDriver = "sp1Lint"`), a thin wrapper over + `getChecks`/`lintCore`. We use a **custom driver, not the stock `runLinter` exe**, because `runLinter` + scopes by namespace *root* (`getDeclsInPackage module.getRoot`) — it would lint all `SP1Clean.*` incl. + `SP1Clean.Extracted.*`, and the per-file `set_option linter.all false` headers do **nothing** against + environment linters (those run post-import; only `nolints.json`/`@[nolint]` suppress them). `sp1Lint` + instead filters decls by full module path (drops `Extracted/`+`*Vectors`) and runs a **curated** set of 13 + low-noise linters (incl. the Mathlib `structureInType`/`deprecatedNoSince` hygiene checks, both at zero + violations). Residue lives in `scripts/nolints.json` (21 stable entries — 3 `defLemma` + obligation-bundle defs + 18 `simpComm`/`simpNF` Math/Model/Sail simp lemmas); `lake exe sp1Lint --update` + regenerates it; CI runs `lake lint` in the build job. Deliberately **dropped**: `docBlame`/`tacticDocs` + (doc-coverage noise) and `unusedArguments` (flags only the uniform field-generic / `ProverData` signature + args — all structural, and a fresh false-positive per new chip). + - The next-candidate linter (`longLine`) is tracked in `docs/roadmap.md` § "Cleanup / polish backlog"; the + non-negotiable suppressions — `unusedSectionVars`/`unusedSimpArgs` (structurally necessary in circuit + proofs) and the auto-gen `linter.all false` — must stay. - Heavy `toBitVec64` rw chains are whnf-expensive — `set_option maxHeartbeats 2000000 in` (carry lemmas need up to `16000000`). - **Never `set_option (debug.)skipKernelTC`.** It bypasses the kernel's type-check re-run — the trust anchor @@ -189,6 +238,12 @@ These are the keepers from sp1-lean's "faithful sub-circuit composition" discipl helper** proved once over variables (the `srl_toNat`/`sra_toNat` pattern), then apply it symbolically — never silence the kernel. See `docs/agents/proof-patterns.md` §"Bit-shift chip soundness" (the `2^64` bullet) for the worked fix. +- **Never `native_decide` in the main `SP1Clean/` library.** It discharges goals by running compiled code, + trusting the **whole compiler** (adds `Lean.ofReduceBool`/`Lean.trustCompiler`) — so headline soundness + theorems would no longer be `[propext, Classical.choice, Quot.sound]`-clean. It is **CI-gated** + (`scripts/check_no_native_decide.sh`, run by the audit + the `guards` job; any hit in `SP1Clean/**/*.lean` + fails the build). Conformance checks that genuinely need it live in the separate top-level `SP1CleanTest` + library (`lake test`); to disclose a new one, put the anchor there, not in `SP1Clean/`. - `mul_eq_zero` won't fire on `ZMod p` (a `Nat.rec` Mul-instance quirk) — derive booleanness via `inv_mul_cancel₀` / a `bool_of_mul_pred`-style lemma instead. - `Word` is an `abbrev` for `Vector` — `w.toBitVec64` dot-notation fails; write `Word.toBitVec64 w`. @@ -220,11 +275,19 @@ after installing or toggling. ## docs/ +- `docs/README.md` — index + "what to read first". - `docs/architecture.md` — the four-artifact chain, layout, design verdict, what's deferred. +- `docs/roadmap.md` — the W-graph dependency chart, open work, debt status. +- `docs/bus-model.md` — the cross-chip interaction-bus model (channels, consistency). +- `docs/release-audit.md` — the honest-claim / trust-boundary report (axiom census + sorry inventory; + regenerate with `scripts/run_audit.sh`). - `docs/agents/lean-sail-notes.md` — the 4.28 environment: toolchain pins, why public Clean `main`, the local Sail setup + `lake update` trap, the Clean-main ↔ Batteries import collision and its fix. -- `docs/agents/proof-patterns.md` — the witnessed-`FormalCircuit` soundness/completeness recipe + concrete landmines. +- `docs/agents/proof-patterns.md` — the witnessed-`FormalCircuit` soundness/completeness recipe + concrete + landmines + the **Golf & cleanup discipline** section (how to golf/clean proofs safely). - `docs/agents/porting-recipe.md` — step-by-step checklist to port a new chip from the Add/Bitwise template. +- `docs/agents/extraction.md` — the constraint-extraction pipeline (compiler → Python → Lean DSL). +- `docs/agents/mul-operation-learnings.md` — Mul-specific soundness/completeness pitfalls. - `docs/snapshots/compile-profile.md` — per-module wall-clock compile profile + worst offenders + common threads (point-in-time snapshot); re-run with `scripts/profile_compile.sh`. - `docs/snapshots/axiom-ledger.md` — machine-checked `#print axioms` inventory per theorem (point-in-time diff --git a/README.md b/README.md index 79e9ff9d..59cb98a8 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,14 @@ Trace-level consistency properties and the whole-machine capstone. - `GatedVm/` + `SP1GatedVm.lean` — the execution capstone (`sp1_machine_soundness`), the final Clean `FormalEnsemble`. -### `Proofs/WitnessTests/` (+ `Extracted/WitnessVectors/`) -Tests the witness generators for most operations against explicit vectors generated by SP1. The hand-written -`native_decide` anchors (`Witness.lean`) live in `Proofs/WitnessTests/`; the auto-generated vectors live in -`Extracted/WitnessVectors/`. Shows conformance on test cases; doesn't directly prove witness generation faithful. +### `SP1CleanTest/` — the test library (`lake test`) +A separate top-level library (it imports `SP1Clean`, but is **not** part of the main `lake build`) holding +the conformance tests — the project's only `native_decide` (which trusts the whole compiler, so it is kept +out of the main library by `scripts/check_no_native_decide.sh`). Two layers: `WitnessTests/` checks each +operation's witness generator against explicit vectors dumped from SP1 (`Witness.lean` anchors + +auto-generated `WitnessTests/Vectors/`), and `TraceGenTests/` checks whole chip traces re-derived from each +chip's own circuit against SP1's real `generate_trace`. Shows conformance on test cases; doesn't directly +prove witness generation faithful. Run with `lake test`. ## How a proof connects to SP1 diff --git a/SP1Clean.lean b/SP1Clean.lean index ac6c3f05..a77d5487 100644 --- a/SP1Clean.lean +++ b/SP1Clean.lean @@ -32,6 +32,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Remu import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Remuw import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Remw import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Reader +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail import SP1Clean.Proofs.Chips.DivRemChip.Populate import SP1Clean.Proofs.Chips.DivRemChip.Populate.Abs import SP1Clean.Proofs.Chips.DivRemChip.Populate.Bounds @@ -217,6 +218,8 @@ import SP1Clean.Faithful.U16toU8OperationSafe import SP1Clean.Faithful.U16toU8OperationUnsafe import SP1Clean.Faithful.UTypeChip import SP1Clean.Math.Bitwise +import SP1Clean.Math.EvalVec +import SP1Clean.Math.Gate import SP1Clean.Model.ByteTable import SP1Clean.Model.Channels import SP1Clean.Model.ChipAir @@ -334,53 +337,3 @@ import SP1Clean.FormalModel.Trace.Witness import SP1Clean.FormalModel.Contracts.Operations import SP1Clean.FormalModel.Contracts.Readers import SP1Clean.Soundness.RowView -import SP1Clean.Proofs.WitnessTests.AddOperationWitness -import SP1Clean.Extracted.WitnessVectors.AddOperation -import SP1Clean.Proofs.WitnessTests.AddrAddOperationWitness -import SP1Clean.Extracted.WitnessVectors.AddrAddOperation -import SP1Clean.Proofs.WitnessTests.AddwOperationWitness -import SP1Clean.Extracted.WitnessVectors.AddwOperation -import SP1Clean.Proofs.WitnessTests.BitwiseOperationWitness -import SP1Clean.Extracted.WitnessVectors.BitwiseOperation -import SP1Clean.Proofs.WitnessTests.IsEqualWordOperationWitness -import SP1Clean.Extracted.WitnessVectors.IsEqualWordOperation -import SP1Clean.Proofs.WitnessTests.IsZeroOperationWitness -import SP1Clean.Extracted.WitnessVectors.IsZeroOperation -import SP1Clean.Proofs.WitnessTests.IsZeroWordOperationWitness -import SP1Clean.Extracted.WitnessVectors.IsZeroWordOperation -import SP1Clean.Proofs.WitnessTests.LtOperationUnsignedWitness -import SP1Clean.Extracted.WitnessVectors.LtOperationUnsigned -import SP1Clean.Proofs.WitnessTests.MulOperationWitness -import SP1Clean.Extracted.WitnessVectors.MulOperation -import SP1Clean.Proofs.WitnessTests.SubOperationWitness -import SP1Clean.Extracted.WitnessVectors.SubOperation -import SP1Clean.Proofs.WitnessTests.SubwOperationWitness -import SP1Clean.Extracted.WitnessVectors.SubwOperation -import SP1Clean.Proofs.WitnessTests.U16CompareOperationWitness -import SP1Clean.Extracted.WitnessVectors.U16CompareOperation -import SP1Clean.Proofs.WitnessTests.U16MSBOperationWitness -import SP1Clean.Extracted.WitnessVectors.U16MSBOperation -import SP1Clean.Proofs.WitnessTests.WitnessConformance -import SP1Clean.Proofs.TraceGenTests.Conformance -import SP1Clean.Proofs.TraceGenTests.EventPopulate -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.AddChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.AddChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.AddwChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.AddwChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.BitwiseChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.BitwiseChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.DivRemChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.DivRemChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.LtChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.LtChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.MulChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.MulChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.ShiftLeftChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.ShiftLeftChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.ShiftRightChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.ShiftRightChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.SubChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.SubChipTraceWitness -import SP1Clean.Proofs.TraceGenTests.SubwChipTraceVectors -import SP1Clean.Proofs.TraceGenTests.SubwChipTraceWitness diff --git a/SP1Clean/Faithful/ALUTypeReader.lean b/SP1Clean/Faithful/ALUTypeReader.lean index b90e0a77..f4d729db 100644 --- a/SP1Clean/Faithful/ALUTypeReader.lean +++ b/SP1Clean/Faithful/ALUTypeReader.lean @@ -32,11 +32,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16' [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in /-- **Faithfulness anchor (ALUTypeReader fragment).** Under `is_real = is_trusted = 1`, SP1's generated `ALUTypeReader` constraint list holds iff the combined spec holds. -/ @@ -69,9 +64,9 @@ theorem alutypereader_constraints_faithful Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_memory, Interaction.toProp_send_program, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, - ByteOpcode.constrain_U8Range, val_16', ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, + ByteOpcode.constrain_U8Range, val_16_zmod_p, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, sub_self, mul_zero, sub_zero, zero_mul, Nat.ofNat_pos, true_and, and_true, - show (2 : ℕ) ^ 8 = 256 from by norm_num, show (2 : ℕ) ^ 16 = 65536 from by norm_num] + show (2 : ℕ) ^ 8 = 256 by norm_num, show (2 : ℕ) ^ 16 = 65536 by norm_num] tauto open SP1Clean.Channels (byteChannel memoryChannel MemoryMsg programChannel ProgramMsg) diff --git a/SP1Clean/Faithful/AddrAddOperation.lean b/SP1Clean/Faithful/AddrAddOperation.lean index 13b86af3..1b816298 100644 --- a/SP1Clean/Faithful/AddrAddOperation.lean +++ b/SP1Clean/Faithful/AddrAddOperation.lean @@ -7,6 +7,7 @@ import SP1Clean.Model.InteractionProjection import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.AddrAddOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (AddrAdd) @@ -23,18 +24,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - /-- **Faithfulness anchor.** SP1's `AddrAddOperation` constraint list holds iff the native gadget's `RawSpec` holds. -/ theorem addrAdd_constraints_faithful (a b : Word (ZMod p)) diff --git a/SP1Clean/Faithful/AddressOperation.lean b/SP1Clean/Faithful/AddressOperation.lean index 899f0618..a45e75f3 100644 --- a/SP1Clean/Faithful/AddressOperation.lean +++ b/SP1Clean/Faithful/AddressOperation.lean @@ -3,6 +3,7 @@ import Mathlib.Data.ZMod.Basic import SP1Clean.Native.Operations.AddressOperation import SP1Clean.Model.SP1Constraint import SP1Clean.Extracted.AddressOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (Address) @@ -19,23 +20,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -/-- `(13 : ZMod p).val = 13` under `Fact (2^17 < p)`. -/ -private lemma val_13 [NeZero p] : (13 : ZMod p).val = 13 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (13 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - set_option maxHeartbeats 4000000 in /-- **Faithfulness anchor.** SP1's `AddressOperation` constraint lists (`asserts` + `interactions`) hold iff the native gadget's `RawSpec` holds. -/ @@ -52,7 +36,7 @@ theorem address_constraints_faithful (b cc : Word (ZMod p)) List.Forall, List.cons_append, List.nil_append, Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, List.getElem_cons_succ, Interaction.toProp_send_byte, ByteOpcode.ofNat_six, - ByteOpcode.constrain_Range, val_16, val_13, one_ne_zero, ne_eq, not_false_eq_true, true_implies, + ByteOpcode.constrain_Range, val_16, val_13_zmod_p, one_ne_zero, ne_eq, not_false_eq_true, true_implies, SP1Clean.AddressOperation.RawSpec, SP1Clean.AddrAddOperation.RawSpec, one_mul, add_zero, sub_zero, sub_self, mul_zero, true_and, and_assoc, bool_iff, show (2 : ℕ) ^ 16 = 65536 from by norm_num] diff --git a/SP1Clean/Faithful/Addw.lean b/SP1Clean/Faithful/Addw.lean index bab9b5cb..adbc5a6c 100644 --- a/SP1Clean/Faithful/Addw.lean +++ b/SP1Clean/Faithful/Addw.lean @@ -8,6 +8,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.AddwOperation import SP1Clean.Faithful.U16MSBOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — `AddwOperation` constraints ↔ native raw spec @@ -25,18 +26,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - set_option linter.unusedSimpArgs false in /-- **Faithfulness anchor.** SP1's composed `AddwOperation` constraint list holds iff the native gadget's combined raw spec (`U16MSBOperation.RawSpec` on the sign bit ∧ `AddwOperation.RawSpec` on diff --git a/SP1Clean/Faithful/AddwChip.lean b/SP1Clean/Faithful/AddwChip.lean index 2bcfd17f..ecebda0e 100644 --- a/SP1Clean/Faithful/AddwChip.lean +++ b/SP1Clean/Faithful/AddwChip.lean @@ -236,7 +236,6 @@ theorem addwcols_memory_interactions_faithful_syntactic h_pl_b, h_pv_b0, h_pv_b1, h_pv_b2, h_pv_b3, h_pl_c, h_pv_c0, h_pv_c1, h_pv_c2, h_pv_c3, sub_eq_add_neg] - set_option maxHeartbeats 4000000 in set_option linter.unusedSimpArgs false in /-- **Chip-level faithfulness anchor — Byte-bus interactions, SYNTACTIC (multi-fragment `Perm` + WITNESSED).** @@ -307,7 +306,6 @@ theorem addwcols_byte_interactions_faithful_syntactic -- swap the first two blocks (the `ALUTypeReader 6` tail is shared). exact (List.perm_append_comm (l₁ := [_, _]) (l₂ := [_, _, _])).append_right [_, _, _, _, _, _] - set_option maxHeartbeats 4000000 in /-- **Chip-level faithfulness anchor — COMBINED, SYNTACTIC.** The full faithfulness statement for `AddwChip`: the interactions the row emits on its four buses — `State`, `Byte`, `Memory`, `Program` — taken together diff --git a/SP1Clean/Faithful/AluX0.lean b/SP1Clean/Faithful/AluX0.lean index b155cde6..48389ffa 100644 --- a/SP1Clean/Faithful/AluX0.lean +++ b/SP1Clean/Faithful/AluX0.lean @@ -22,10 +22,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16'' [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - private lemma val_29'' [NeZero p] : (29 : ZMod p).val = 29 := by have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega exact ZMod.val_natCast_of_lt (show (29 : ℕ) < p by omega) @@ -76,7 +72,7 @@ theorem alux0cols_constraints_faithful (cols : Extracted.AluX0Cols (ZMod p)) Interaction.toProp_send_memory, Interaction.toProp_send_program, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.ofNat_four, ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, ByteOpcode.constrain_LTU, - val_16'', val_29'', ZMod.val_zero, ZMod.val_one, one_ne_zero, ne_eq, not_false_eq_true, + val_16_zmod_p, val_29'', ZMod.val_zero, ZMod.val_one, one_ne_zero, ne_eq, not_false_eq_true, true_implies, sub_self, mul_zero, sub_zero, zero_mul, one_mul, Nat.ofNat_pos, true_and, and_true, false_or, true_iff, show (1 : ℕ) < 256 from by norm_num, show (29 : ℕ) < 256 from by norm_num, show (2 : ℕ) ^ 8 = 256 from by norm_num, show (2 : ℕ) ^ 16 = 65536 from by norm_num] diff --git a/SP1Clean/Faithful/BranchChip.lean b/SP1Clean/Faithful/BranchChip.lean index 29cf8d5e..344d1aeb 100644 --- a/SP1Clean/Faithful/BranchChip.lean +++ b/SP1Clean/Faithful/BranchChip.lean @@ -3,6 +3,7 @@ import Mathlib.Data.ZMod.Basic import SP1Clean.Proofs.Chips.BranchChip.Formal import SP1Clean.Model.SP1Constraint import SP1Clean.Extracted.BranchChip +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor for BRANCH @@ -25,16 +26,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(14 : ZMod p).val = 14` under `Fact (2^17 < p)` (for the `Range(next_pc[0]/4, 14)` send). -/ -private lemma val_14 [NeZero p] : (14 : ZMod p).val = 14 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (14 : ℕ) < p by omega) - -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)` (for the `next_pc[1]`/`next_pc[2]` u16 sends). -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - /-- The chip-local structural meaning of BRANCH's `asserts` inline tail (everything past the composed `CPUState`/`ITypeReaderImmutable`/`LtOperationSigned` sub-lists): the six opcode-flag booleans, the flag sum bound (`E16 = Σ is_b*` boolean), the `is_branching` boolean, the `is_real`-gated decision constraint @@ -97,7 +88,7 @@ theorem branch_interactions_faithful (cols : BranchColumns (ZMod p)) : -- `Range(next_pc[0]/4, 14)`, u16 `next_pc[1]`, u16 `next_pc[2]`, each gated by the flag sum `E16`. simp only [Extracted.BranchColumns.interactions, List.forall_append, List.Forall, Interaction.toProp_send_byte, ByteOpcode.ofNat_six, ByteOpcode.constrain_Range, - val_14, val_16] at h + val_14_zmod_p, val_16] at h obtain ⟨-, h0, h1, h2⟩ := h simp only [branchInteractSpec] refine ⟨?_, ?_, ?_⟩ diff --git a/SP1Clean/Faithful/CPUState.lean b/SP1Clean/Faithful/CPUState.lean index 30180dd2..418e3fee 100644 --- a/SP1Clean/Faithful/CPUState.lean +++ b/SP1Clean/Faithful/CPUState.lean @@ -24,11 +24,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(13 : ZMod p).val = 13` under `Fact (2^17 < p)`. -/ -private lemma val_13 [NeZero p] : (13 : ZMod p).val = 13 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (13 : ℕ) < p by omega) - /-- **Faithfulness anchor (CPUState fragment).** Under `is_real = 1`, SP1's generated `CPUState` constraint list holds iff the two clock-range bounds hold — i.e. iff `Readers.CPUState.Spec` holds. The `.state` `receive`/`send` interactions contribute `True` (their meaning is the trace-level bus); @@ -42,9 +37,9 @@ theorem cpustate_constraints_faithful simp only [Extracted.CPUState.asserts, Extracted.CPUState.interactions, List.Forall, Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_state, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, - ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_13, ZMod.val_zero, + ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_13_zmod_p, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, sub_self, mul_zero, - Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 from by norm_num] + Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 by norm_num] omit [Fact (2 ^ 17 < p)] in /-- **CPUState fragment — assertion half.** `CPUState` emits only the (vacuous at `is_real = 1`) @@ -65,9 +60,9 @@ theorem cpustate_interactions_faithful simp only [Extracted.CPUState.interactions, List.Forall, Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_state, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, - ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_13, ZMod.val_zero, + ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_13_zmod_p, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, - Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 from by norm_num] + Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 by norm_num] open SP1Clean.Channels (stateChannel byteChannel StateMsg) diff --git a/SP1Clean/Faithful/ChipTactics.lean b/SP1Clean/Faithful/ChipTactics.lean index 62a148b7..7e39ca25 100644 --- a/SP1Clean/Faithful/ChipTactics.lean +++ b/SP1Clean/Faithful/ChipTactics.lean @@ -22,10 +22,9 @@ namespace SP1Clean.Faithful variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) +/-- `(16 : ZMod p).val = 16` — the Faithful-local spelling of `SP1Clean.val_16_zmod_p` +(kept as the established name used across the per-chip anchors' `simp only` lists). -/ +lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := val_16_zmod_p omit [Fact (2 ^ 17 < p)] in /-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via the `mul_eq_zero`-free diff --git a/SP1Clean/Faithful/ITypeReader.lean b/SP1Clean/Faithful/ITypeReader.lean index 8ee8e2db..f0b11093 100644 --- a/SP1Clean/Faithful/ITypeReader.lean +++ b/SP1Clean/Faithful/ITypeReader.lean @@ -2,6 +2,7 @@ import Mathlib.Tactic import Mathlib.Data.ZMod.Basic import SP1Clean.Model.SP1Constraint import SP1Clean.Extracted.ITypeReader +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — SP1's `ITypeReader` constraint fragment ↔ the native reader spec @@ -30,11 +31,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 2000000 in /-- **Faithfulness anchor (ITypeReader fragment).** Under `is_real = is_trusted = 1`, SP1's generated `ITypeReader` constraint list holds iff the four `op_a_0` zeroing equations and the two register diff --git a/SP1Clean/Faithful/ITypeReaderImmutable.lean b/SP1Clean/Faithful/ITypeReaderImmutable.lean index 4b0558c0..5a433fa4 100644 --- a/SP1Clean/Faithful/ITypeReaderImmutable.lean +++ b/SP1Clean/Faithful/ITypeReaderImmutable.lean @@ -2,6 +2,7 @@ import Mathlib.Tactic import Mathlib.Data.ZMod.Basic import SP1Clean.Model.SP1Constraint import SP1Clean.Extracted.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — SP1's `ITypeReaderImmutable` constraint fragment ↔ the native reader spec @@ -26,11 +27,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 2000000 in /-- **Faithfulness anchor (ITypeReaderImmutable fragment).** Under `is_real = is_trusted = 1`, SP1's generated `ITypeReaderImmutable` constraint list holds iff the four `op_a_0` *read*-zeroing equations and diff --git a/SP1Clean/Faithful/IsEqualWordOperation.lean b/SP1Clean/Faithful/IsEqualWordOperation.lean index 222daa5b..8873e328 100644 --- a/SP1Clean/Faithful/IsEqualWordOperation.lean +++ b/SP1Clean/Faithful/IsEqualWordOperation.lean @@ -8,6 +8,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.IsEqualWordOperation import SP1Clean.Faithful.IsZeroWordOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (IsEqualWord) @@ -24,13 +25,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - omit [Fact (2 ^ 17 < p)] in /-- **Faithfulness anchor.** SP1's `IsEqualWordOperation` constraint list holds iff the native gadget's `RawSpec` holds. -/ diff --git a/SP1Clean/Faithful/IsZeroOperation.lean b/SP1Clean/Faithful/IsZeroOperation.lean index 02ab0286..f2ddd07b 100644 --- a/SP1Clean/Faithful/IsZeroOperation.lean +++ b/SP1Clean/Faithful/IsZeroOperation.lean @@ -7,6 +7,7 @@ import SP1Clean.Model.InteractionProjection import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.IsZeroOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (IsZero) @@ -24,13 +25,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - omit [Fact (2 ^ 17 < p)] in /-- **Faithfulness anchor — assertion half.** SP1's `IsZeroOperation` `asserts` list holds iff the native gadget's `AssertSpec` holds. (No range bounds here, so `NeZero p` follows from primality diff --git a/SP1Clean/Faithful/IsZeroWordOperation.lean b/SP1Clean/Faithful/IsZeroWordOperation.lean index e27dd6cc..0302993c 100644 --- a/SP1Clean/Faithful/IsZeroWordOperation.lean +++ b/SP1Clean/Faithful/IsZeroWordOperation.lean @@ -8,6 +8,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.IsZeroWordOperation import SP1Clean.Faithful.IsZeroOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (IsZeroWord) @@ -26,13 +27,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - omit [Fact (2 ^ 17 < p)] in /-- **Faithfulness anchor.** SP1's `IsZeroWordOperation` constraint list holds iff the native gadget's `RawSpec` holds. (No range bounds, so `NeZero p` follows from primality.) -/ diff --git a/SP1Clean/Faithful/JTypeReader.lean b/SP1Clean/Faithful/JTypeReader.lean index a6c076ec..8eb8b7af 100644 --- a/SP1Clean/Faithful/JTypeReader.lean +++ b/SP1Clean/Faithful/JTypeReader.lean @@ -31,11 +31,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16' [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - omit [Fact (2 ^ 17 < p)] in /-- **JTypeReader fragment — assertion half.** Under `is_real = 1`, SP1's generated `JTypeReader` `asserts` list holds iff the four `op_a_0` zeroing equations (`rd = x0 ⟹ write 0`); the two binary @@ -67,7 +62,7 @@ theorem jtypereader_interactions_faithful Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_memory, Interaction.toProp_send_program, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, - ByteOpcode.constrain_U8Range, val_16', ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, + ByteOpcode.constrain_U8Range, val_16_zmod_p, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 from by norm_num, show (2 : ℕ) ^ 16 = 65536 from by norm_num] diff --git a/SP1Clean/Faithful/JalChip.lean b/SP1Clean/Faithful/JalChip.lean index 86d20b4b..8137681a 100644 --- a/SP1Clean/Faithful/JalChip.lean +++ b/SP1Clean/Faithful/JalChip.lean @@ -33,15 +33,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(13 : ZMod p).val = 13` under `Fact (2^17 < p)`. -/ -private lemma val_13 [NeZero p] : (13 : ZMod p).val = 13 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (13 : ℕ) < p by omega) - -/-- `(14 : ZMod p).val = 14` under `Fact (2^17 < p)`. -/ -private lemma val_14 [NeZero p] : (14 : ZMod p).val = 14 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (14 : ℕ) < p by omega) set_option maxHeartbeats 2000000 in /-- **Chip-level faithfulness anchor — assertion half.** Under `is_real = 1 ∧ op_a_0 = 0`, SP1's generated @@ -101,7 +92,7 @@ theorem jalcols_interactions_faithful (cols : Extracted.JalColumns (ZMod p)) simp only [List.Forall, Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_memory, Interaction.toProp_send_program, Interaction.toProp_send_state, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, - ByteOpcode.constrain_U8Range, val_13, val_14, val_16, ZMod.val_zero, one_ne_zero, ne_eq, + ByteOpcode.constrain_U8Range, val_13_zmod_p, val_14_zmod_p, val_16, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 from by norm_num] tauto diff --git a/SP1Clean/Faithful/JalrChip.lean b/SP1Clean/Faithful/JalrChip.lean index 77c9d888..e00971d4 100644 --- a/SP1Clean/Faithful/JalrChip.lean +++ b/SP1Clean/Faithful/JalrChip.lean @@ -35,11 +35,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(14 : ZMod p).val = 14` under `Fact (2^17 < p)`. -/ -private lemma val_14 [NeZero p] : (14 : ZMod p).val = 14 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (14 : ℕ) < p by omega) - set_option maxHeartbeats 2000000 in /-- **Chip-level faithfulness anchor — assertion half.** Under `is_real = 1 ∧ op_a_0 = 0`, SP1's generated JALR `asserts` list holds iff: the two `AddOperation` assertion specs (carry-bools) for the jump target @@ -107,7 +102,7 @@ theorem jalrcols_interactions_faithful (cols : Extracted.JalrColumns (ZMod p)) simp only [Extracted.ITypeReader.interactions, List.Forall, Interaction.toProp_send_byte, Interaction.toProp_receive, Interaction.toProp_send_memory, Interaction.toProp_send_program, ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, - ByteOpcode.constrain_U8Range, val_14, val_16, ZMod.val_zero, one_ne_zero, ne_eq, + ByteOpcode.constrain_U8Range, val_14_zmod_p, val_16, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, Nat.ofNat_pos, true_and, and_true, show (2 : ℕ) ^ 8 = 256 from by norm_num] tauto diff --git a/SP1Clean/Faithful/LoadByte.lean b/SP1Clean/Faithful/LoadByte.lean index c4a5cd59..6ea40045 100644 --- a/SP1Clean/Faithful/LoadByte.lean +++ b/SP1Clean/Faithful/LoadByte.lean @@ -6,6 +6,7 @@ import SP1Clean.Extracted.LoadByteChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReader +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `LoadByte` chip constraint list ↔ combined spec -/ @@ -17,10 +18,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option linter.unusedSimpArgs false in set_option maxHeartbeats 4000000 in theorem loadbytecols_constraints_faithful (cols : Extracted.LoadByteColumns (ZMod p)) diff --git a/SP1Clean/Faithful/LoadDouble.lean b/SP1Clean/Faithful/LoadDouble.lean index 6538844d..7989ec19 100644 --- a/SP1Clean/Faithful/LoadDouble.lean +++ b/SP1Clean/Faithful/LoadDouble.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.LoadDoubleChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReader +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `LoadDouble` chip constraint list ↔ combined spec @@ -28,11 +29,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in /-- **Chip-level faithfulness anchor.** Under `is_real = 1`, SP1's generated `LoadDouble` chip constraint list holds iff: the `AddressOperation` raw spec on the `rs1` register read and the diff --git a/SP1Clean/Faithful/LoadHalf.lean b/SP1Clean/Faithful/LoadHalf.lean index 0faa98cf..961c3e21 100644 --- a/SP1Clean/Faithful/LoadHalf.lean +++ b/SP1Clean/Faithful/LoadHalf.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.LoadHalfChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReader +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `LoadHalf` chip constraint list ↔ combined spec -/ @@ -16,10 +17,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option linter.unusedSimpArgs false in set_option maxHeartbeats 4000000 in theorem loadhalfcols_constraints_faithful (cols : Extracted.LoadHalfColumns (ZMod p)) diff --git a/SP1Clean/Faithful/LoadWord.lean b/SP1Clean/Faithful/LoadWord.lean index 0d7b5bec..0fa95ea1 100644 --- a/SP1Clean/Faithful/LoadWord.lean +++ b/SP1Clean/Faithful/LoadWord.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.LoadWordChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReader +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `LoadWord` chip constraint list ↔ combined spec -/ @@ -16,10 +17,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in theorem loadwordcols_constraints_faithful (cols : Extracted.LoadWordColumns (ZMod p)) (h_real : cols.is_lw + cols.is_lwu = 1) : diff --git a/SP1Clean/Faithful/LoadX0.lean b/SP1Clean/Faithful/LoadX0.lean index 085bbc7b..569e2d76 100644 --- a/SP1Clean/Faithful/LoadX0.lean +++ b/SP1Clean/Faithful/LoadX0.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.LoadX0Chip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `LoadX0` chip constraint list ↔ combined spec @@ -26,10 +27,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 8000000 in /-- **Chip-level faithfulness anchor.** Under the one-hot selector hypothesis `is_lb + … + is_ld = 1`, SP1's generated `LoadX0` chip constraint list holds iff: the `AddressOperation` raw spec (with the three diff --git a/SP1Clean/Faithful/LtOperationSigned.lean b/SP1Clean/Faithful/LtOperationSigned.lean index 9dbad5a0..25231cc1 100644 --- a/SP1Clean/Faithful/LtOperationSigned.lean +++ b/SP1Clean/Faithful/LtOperationSigned.lean @@ -4,6 +4,7 @@ import SP1Clean.Native.Operations.LtOperationSigned.RawSpec import SP1Clean.Model.SP1Constraint import SP1Clean.Extracted.LtOperationSigned import SP1Clean.Faithful.LtOperationUnsigned +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (LtSigned) @@ -22,18 +23,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - set_option maxHeartbeats 8000000 in /-- **Faithfulness anchor.** SP1's `LtOperationSigned` constraint list (at `is_real = 1`, `is_signed` free) holds iff the native gadget's `RawSpec` holds. -/ diff --git a/SP1Clean/Faithful/LtOperationUnsigned.lean b/SP1Clean/Faithful/LtOperationUnsigned.lean index 4bcde0bd..0fab4e79 100644 --- a/SP1Clean/Faithful/LtOperationUnsigned.lean +++ b/SP1Clean/Faithful/LtOperationUnsigned.lean @@ -8,6 +8,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Faithful.U16CompareOperation import SP1Clean.Extracted.LtOperationUnsigned +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (LtUnsigned) @@ -25,18 +26,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - set_option maxHeartbeats 4000000 in /-- **Faithfulness anchor.** SP1's `LtOperationUnsigned` constraint list holds iff the native gadget's `RawSpec` holds. -/ diff --git a/SP1Clean/Faithful/MulChip.lean b/SP1Clean/Faithful/MulChip.lean index 49ad7ce5..da3f55b2 100644 --- a/SP1Clean/Faithful/MulChip.lean +++ b/SP1Clean/Faithful/MulChip.lean @@ -32,7 +32,6 @@ theorem mul_asserts_faithful (cols : Extracted.MulCols (ZMod p)) : intro h haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 24 < p); omega⟩ simp only [Extracted.MulCols.asserts, Extracted.forall_append_single, List.Forall] at h - dsimp only [SP1Clean.MulChip.AssertSpec] exact h.2 omit [Fact (2 ^ 24 < p)] in diff --git a/SP1Clean/Faithful/MulOperation.lean b/SP1Clean/Faithful/MulOperation.lean index 676dd266..9b1d0087 100644 --- a/SP1Clean/Faithful/MulOperation.lean +++ b/SP1Clean/Faithful/MulOperation.lean @@ -26,10 +26,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16' [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - /-- The high decomposition byte of `b` (`E7 = U16toU8OperationSafe.value b ...[7]`), the value the `MSB` send pins `b_msb` against. -/ private def hiByteB (b : Word (ZMod p)) (cols : Extracted.MulOperation (ZMod p)) (is_real : ZMod p) : @@ -92,7 +88,7 @@ theorem mulOp_interactions_faithful (a b c : Word (ZMod p)) haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ simp only [Extracted.MulOperation.interactions, List.forall_append, List.Forall, Interaction.toProp_send_byte, ByteOpcode.ofNat_three, ByteOpcode.ofNat_five, ByteOpcode.ofNat_six, - ByteOpcode.constrain_U8Range, ByteOpcode.constrain_MSB, ByteOpcode.constrain_Range, val_16', + ByteOpcode.constrain_U8Range, ByteOpcode.constrain_MSB, ByteOpcode.constrain_Range, val_16_zmod_p, one_ne_zero, ne_eq, not_false_eq_true, true_implies, MulOpInteractSpec, hiByteB, hiByteC, and_assoc] diff --git a/SP1Clean/Faithful/RTypeReader.lean b/SP1Clean/Faithful/RTypeReader.lean index 9d879136..f8c48b56 100644 --- a/SP1Clean/Faithful/RTypeReader.lean +++ b/SP1Clean/Faithful/RTypeReader.lean @@ -6,6 +6,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Native.Readers.RTypeReader import SP1Clean.Extracted.RTypeReader +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — SP1's `RTypeReader` constraint fragment ↔ the native reader spec @@ -33,11 +34,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 2000000 in /-- **Faithfulness anchor (RTypeReader fragment).** Under `is_real = is_trusted = 1`, SP1's generated `RTypeReader` constraint list holds iff the four `op_a_0` zeroing equations and the three operands' @@ -68,7 +64,7 @@ theorem rtypereader_constraints_faithful ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_16, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, sub_self, mul_zero, sub_zero, Nat.ofNat_pos, true_and, and_true, - show (2 : ℕ) ^ 8 = 256 from by norm_num, show (2 : ℕ) ^ 16 = 65536 from by norm_num] + show (2 : ℕ) ^ 8 = 256 by norm_num, show (2 : ℕ) ^ 16 = 65536 by norm_num] -- The simp leaves a right-associated flat conjunction; the RHS groups the byte facts per operand. -- Only associativity differs. tauto @@ -113,7 +109,7 @@ theorem rtypereader_interactions_faithful ByteOpcode.ofNat_six, ByteOpcode.ofNat_three, ByteOpcode.constrain_Range, ByteOpcode.constrain_U8Range, val_16, ZMod.val_zero, one_ne_zero, ne_eq, not_false_eq_true, true_implies, Nat.ofNat_pos, true_and, and_true, - show (2 : ℕ) ^ 8 = 256 from by norm_num, show (2 : ℕ) ^ 16 = 65536 from by norm_num] + show (2 : ℕ) ^ 8 = 256 by norm_num, show (2 : ℕ) ^ 16 = 65536 by norm_num] tauto open SP1Clean.Channels (byteChannel memoryChannel MemoryMsg programChannel ProgramMsg) diff --git a/SP1Clean/Faithful/ShiftLeftChip.lean b/SP1Clean/Faithful/ShiftLeftChip.lean index 86c5f826..c3eb21c7 100644 --- a/SP1Clean/Faithful/ShiftLeftChip.lean +++ b/SP1Clean/Faithful/ShiftLeftChip.lean @@ -28,7 +28,6 @@ theorem shiftLeft_asserts_faithful (cols : Extracted.ShiftLeftCols (ZMod p)) : intro h haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ simp only [Extracted.ShiftLeftCols.asserts, Extracted.forall_append_single, List.Forall] at h - dsimp only [SP1Clean.ShiftLeftChip.AssertSpec] exact h.2 omit [Fact (2 ^ 17 < p)] in @@ -43,7 +42,6 @@ theorem shiftLeft_interactions_faithful (cols : Extracted.ShiftLeftCols (ZMod p) List.Forall, Interaction.toProp_send_byte, show (ByteOpcode.ofNat 6 : ByteOpcode) = ByteOpcode.Range from rfl, ByteOpcode.constrain, zero_add] at h - dsimp only [SP1Clean.ShiftLeftChip.InteractSpec] exact h.2 end SP1Clean.Faithful diff --git a/SP1Clean/Faithful/ShiftRightChip.lean b/SP1Clean/Faithful/ShiftRightChip.lean index 52914989..33ff33c8 100644 --- a/SP1Clean/Faithful/ShiftRightChip.lean +++ b/SP1Clean/Faithful/ShiftRightChip.lean @@ -34,7 +34,6 @@ theorem shiftRight_asserts_faithful (cols : Extracted.ShiftRightCols (ZMod p)) : haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ simp only [Extracted.ShiftRightCols.asserts, Extracted.forall_append_single, List.Forall, Nat.cast_one] at h - dsimp only [SP1Clean.ShiftRightChip.AssertSpec] exact h.2 omit [Fact (2 ^ 17 < p)] in @@ -49,7 +48,6 @@ theorem shiftRight_interactions_faithful (cols : Extracted.ShiftRightCols (ZMod List.Forall, Interaction.toProp_send_byte, show (ByteOpcode.ofNat 6 : ByteOpcode) = ByteOpcode.Range from rfl, ByteOpcode.constrain, zero_add] at h - dsimp only [SP1Clean.ShiftRightChip.InteractSpec] exact h.2 end SP1Clean.Faithful diff --git a/SP1Clean/Faithful/StoreByte.lean b/SP1Clean/Faithful/StoreByte.lean index f2388fbb..a0d0e9a7 100644 --- a/SP1Clean/Faithful/StoreByte.lean +++ b/SP1Clean/Faithful/StoreByte.lean @@ -6,6 +6,7 @@ import SP1Clean.Extracted.StoreByteChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `StoreByte` chip constraint list ↔ combined spec -/ @@ -17,10 +18,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option linter.unusedSimpArgs false in set_option maxHeartbeats 4000000 in theorem storebytecols_constraints_faithful (cols : Extracted.StoreByteColumns (ZMod p)) diff --git a/SP1Clean/Faithful/StoreDouble.lean b/SP1Clean/Faithful/StoreDouble.lean index 3362fe3e..0ac18cda 100644 --- a/SP1Clean/Faithful/StoreDouble.lean +++ b/SP1Clean/Faithful/StoreDouble.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.StoreDoubleChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `StoreDouble` chip constraint list ↔ combined spec @@ -28,10 +29,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in /-- **Chip-level faithfulness anchor.** Under `is_real = 1`, SP1's generated `StoreDouble` chip constraint list holds iff: the `AddressOperation` raw spec on the `rs1` register read and the immediate diff --git a/SP1Clean/Faithful/StoreHalf.lean b/SP1Clean/Faithful/StoreHalf.lean index 923e3e22..29b8a391 100644 --- a/SP1Clean/Faithful/StoreHalf.lean +++ b/SP1Clean/Faithful/StoreHalf.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.StoreHalfChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `StoreHalf` chip constraint list ↔ combined spec -/ @@ -16,10 +17,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in theorem storehalfcols_constraints_faithful (cols : Extracted.StoreHalfColumns (ZMod p)) (h_real : cols.is_real = 1) : diff --git a/SP1Clean/Faithful/StoreWord.lean b/SP1Clean/Faithful/StoreWord.lean index 95d79fb0..0cc4af23 100644 --- a/SP1Clean/Faithful/StoreWord.lean +++ b/SP1Clean/Faithful/StoreWord.lean @@ -5,6 +5,7 @@ import SP1Clean.Extracted.StoreWordChip import SP1Clean.Faithful.AddressOperation import SP1Clean.Faithful.CPUState import SP1Clean.Faithful.ITypeReaderImmutable +import SP1Clean.Faithful.ChipTactics /-! # Chip-level faithfulness anchor — SP1's whole `StoreWord` chip constraint list ↔ combined spec -/ @@ -16,10 +17,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - set_option maxHeartbeats 4000000 in theorem storewordcols_constraints_faithful (cols : Extracted.StoreWordColumns (ZMod p)) (h_real : cols.is_real = 1) : diff --git a/SP1Clean/Faithful/SubChip.lean b/SP1Clean/Faithful/SubChip.lean index 37cd0cdf..6e297e4f 100644 --- a/SP1Clean/Faithful/SubChip.lean +++ b/SP1Clean/Faithful/SubChip.lean @@ -364,5 +364,4 @@ theorem subcols_interactions_faithful_syntactic rw [hS, hM, hP] exact ((hB.append_left _).append_right _).append_right _ - end SP1Clean.Faithful diff --git a/SP1Clean/Faithful/Subw.lean b/SP1Clean/Faithful/Subw.lean index ae2ed81c..c8eb58e6 100644 --- a/SP1Clean/Faithful/Subw.lean +++ b/SP1Clean/Faithful/Subw.lean @@ -8,6 +8,7 @@ import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.SubwOperation import SP1Clean.Faithful.U16MSBOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — `SubwOperation` constraints ↔ native combined raw spec @@ -26,18 +27,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - set_option linter.unusedSimpArgs false in /-- **Faithfulness anchor.** SP1's composed `SubwOperation` constraint list holds iff the native gadget's combined raw spec (`U16MSBOperation.RawSpec` on the sign bit ∧ `SubwOperation.RawSpec` on diff --git a/SP1Clean/Faithful/SubwChip.lean b/SP1Clean/Faithful/SubwChip.lean index 7d274b3f..93510835 100644 --- a/SP1Clean/Faithful/SubwChip.lean +++ b/SP1Clean/Faithful/SubwChip.lean @@ -370,5 +370,4 @@ theorem subwcols_interactions_faithful_syntactic rw [hS, hM, hP] exact ((hB.append_left _).append_right _).append_right _ - end SP1Clean.Faithful diff --git a/SP1Clean/Faithful/U16CompareOperation.lean b/SP1Clean/Faithful/U16CompareOperation.lean index b2803104..52729da2 100644 --- a/SP1Clean/Faithful/U16CompareOperation.lean +++ b/SP1Clean/Faithful/U16CompareOperation.lean @@ -7,6 +7,7 @@ import SP1Clean.Model.InteractionProjection import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.U16CompareOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor to the SP1 (Rust-extraction) constraints (U16Compare) @@ -24,18 +25,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - /-- **Faithfulness anchor.** SP1's `U16CompareOperation` constraint list holds iff the native gadget's `RawSpec` holds. -/ theorem u16compare_constraints_faithful (a b : ZMod p) diff --git a/SP1Clean/Faithful/U16MSBOperation.lean b/SP1Clean/Faithful/U16MSBOperation.lean index 76d6bc9f..ced152ee 100644 --- a/SP1Clean/Faithful/U16MSBOperation.lean +++ b/SP1Clean/Faithful/U16MSBOperation.lean @@ -7,6 +7,7 @@ import SP1Clean.Model.InteractionProjection import SP1Clean.Model.InteractionRecovery import SP1Clean.Faithful.ExtractedInteractionModel import SP1Clean.Extracted.U16MSBOperation +import SP1Clean.Faithful.ChipTactics /-! # Faithfulness anchor — `U16MSBOperation` constraints ↔ native `RawSpec` @@ -23,18 +24,6 @@ open scoped SP1Clean.ConstraintCoe variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] -/-- `(16 : ZMod p).val = 16` under `Fact (2^17 < p)`. -/ -private lemma val_16 [NeZero p] : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) - -omit [Fact (2 ^ 17 < p)] in -/-- Carry-bool bridge: `x*(x-1)=0 ↔ x∈{0,1}` (field; `→` via `bool_of_mul_pred`). -/ -private lemma bool_iff {x : ZMod p} : x * (x - 1) = 0 ↔ (x = 0 ∨ x = 1) := by - rw [sub_eq_add_neg] - exact ⟨SP1Clean.bool_of_mul_pred, - fun h => by rcases h with h | h <;> rw [h] <;> ring⟩ - -- All four `1 ≠ 0 →` discharge lemmas (`one_ne_zero`/`ne_eq`/`not_false_eq_true`/`true_implies`) -- are genuinely needed — dropping either pair leaves unsolved goals — but on this short -- (single-send) constraint list the `unusedSimpArgs` linter false-positives on two of them, so diff --git a/SP1Clean/FormalModel/Contracts/Chips.lean b/SP1Clean/FormalModel/Contracts/Chips.lean index 33a07aa1..61544dd2 100644 --- a/SP1Clean/FormalModel/Contracts/Chips.lean +++ b/SP1Clean/FormalModel/Contracts/Chips.lean @@ -374,9 +374,7 @@ private lemma high64_mul (b' c' : BitVec 129) (b'' c'' : BitVec 128) /-- `RV64.mul rs2 rs1 = rs1 * rs2` (commuted into the gadget's `b * c` form). -/ lemma rv64_mul_eq (x y : BitVec 64) : RV64.mul x y = y * x := by - simp only [RV64.mul] - apply BitVec.eq_of_toNat_eq - rw [BitVec.toNat_mul, BitVec.toNat_mul, Nat.mul_comm] + rw [RV64.mul, BitVec.mul_comm] /-- `RV64.mulh`'s high-64-bit signed×signed product equals the gadget's `>>>64 |>.setWidth 64` form. -/ lemma rv64_mulh_eq (x y : BitVec 64) : diff --git a/SP1Clean/Math/Bitwise.lean b/SP1Clean/Math/Bitwise.lean index 36f1bbb6..fef904d3 100644 --- a/SP1Clean/Math/Bitwise.lean +++ b/SP1Clean/Math/Bitwise.lean @@ -26,8 +26,7 @@ variable {p : ℕ} exact ZMod.val_natCast_of_lt (show (256 : ℕ) < p by omega) lemma val_256_ne_zero [NeZero p] [Fact (2 ^ 17 < p)] : (256 : ZMod p) ≠ 0 := by - have h : (256 : ZMod p).val = 256 := val_256_zmod_p - intro hz; rw [hz] at h; simp at h + simp [← ZMod.val_eq_zero, val_256_zmod_p] /-! ## The byte operation (AND=0, OR=1, XOR=2) -/ diff --git a/SP1Clean/Math/EvalVec.lean b/SP1Clean/Math/EvalVec.lean new file mode 100644 index 00000000..308711b8 --- /dev/null +++ b/SP1Clean/Math/EvalVec.lean @@ -0,0 +1,24 @@ +import Clean.Circuit.Basic +import Clean.Utils.Field +import Mathlib.Tactic.IntervalCases + +/-! # `vec4_eval` — folding a length-4 `#v` of pointwise evaluations back to `Vector.map` + +The constraint extractor writes a witness hint's `populate` operands as an explicit length-4 vector +literal `#v[eval e v[0], eval e v[1], eval e v[2], eval e v[3]]`, whereas `h_input` provides them in +`Vector.map (eval e) v` form. This one lemma bridges the two; it recurs across the Mul / Lt / Bitwise +/ ShiftLeft / ShiftRight chip `Formal` proofs and the DivRem completeness `Driver`, so it lives here +in `Math` rather than being re-declared per file. Generic over a prime field `ZMod p` +(`Expression.eval` needs the `Field` instance, which the primality `Fact` supplies). -/ + +namespace SP1Clean + +variable {p : ℕ} [Fact p.Prime] + +lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : + (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], + Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by + ext k hk + interval_cases k <;> simp [Vector.getElem_map] + +end SP1Clean diff --git a/SP1Clean/Math/Gate.lean b/SP1Clean/Math/Gate.lean new file mode 100644 index 00000000..9bcf6a1b --- /dev/null +++ b/SP1Clean/Math/Gate.lean @@ -0,0 +1,75 @@ +import Mathlib.Data.ZMod.Basic +import Mathlib.Tactic + +/-! # Binary-field gates and BitVec comparisons + +Field-generic helper lemmas for **binary (boolean) field elements** (`x = 0 ∨ x = 1`) and the two +`BitVec` order comparisons (`slt`/`ult`). These recur in the chip decision/selector proofs (Branch's +signed/unsigned branch decision, Lt's flag selectors); collected here so any chip can reuse them rather +than re-proving a private copy. Pure `ZMod p` / `BitVec` facts — no SP1-specific or circuit dependency. -/ + +namespace SP1Clean + +variable {p : ℕ} + +/-- A boolean field element has `val ≤ 1`. -/ +lemma bool_val_le [Fact p.Prime] {x : ZMod p} (h : x = 0 ∨ x = 1) : x.val ≤ 1 := by + haveI : Fact (1 < p) := ⟨(Fact.out : p.Prime).one_lt⟩ + rcases h with h | h <;> simp [h, ZMod.val_one] + +/-- Distinct field elements have distinct `val`s. -/ +lemma val_ne [NeZero p] {x y : ZMod p} (h : x ≠ y) : x.val ≠ y.val := fun hv => + h (by rw [← ZMod.natCast_zmod_val x, ← ZMod.natCast_zmod_val y, hv]) + +/-- A field element equal to `if P then 1 else 0` is `1` iff `P` holds. -/ +lemma eq_one_iff_of_ite [Fact p.Prime] {x : ZMod p} {P : Prop} [Decidable P] + (h : x = if P then 1 else 0) : x = 1 ↔ P := by + by_cases hP : P + · simp [h, hP] + · simp only [h, if_neg hP] + exact ⟨fun he => absurd he zero_ne_one, fun hp => absurd hp hP⟩ + +/-- A field element equal to `1 - (if P then 1 else 0)` is `1` iff `P` fails. -/ +lemma eq_one_iff_of_one_sub_ite [Fact p.Prime] {x : ZMod p} {P : Prop} [Decidable P] + (h : x = 1 - (if P then 1 else 0)) : x = 1 ↔ ¬ P := by + by_cases hP : P + · simp only [h, if_pos hP, sub_self] + exact ⟨fun he => absurd he zero_ne_one, fun hp => absurd hP hp⟩ + · simp [h, hP] + +/-- A binary `x` with `x = 1 ↔ P` is `if P then 1 else 0` (completeness direction of +`eq_one_iff_of_ite`). -/ +lemma bool_eq_ite_of_iff {x : ZMod p} (hx : x = 0 ∨ x = 1) {P : Prop} [Decidable P] + (h : x = 1 ↔ P) : x = if P then 1 else 0 := by + by_cases hP : P + · rw [if_pos hP]; exact h.mpr hP + · rw [if_neg hP]; rcases hx with h0 | h1 + · exact h0 + · exact absurd (h.mp h1) hP + +/-- A binary `x` with `x = 1 ↔ y = 0` (for binary `y`) is `1 - y`. -/ +lemma bool_eq_one_sub [Fact p.Prime] {x y : ZMod p} (hx : x = 0 ∨ x = 1) (hy : y = 0 ∨ y = 1) + (h : x = 1 ↔ y = 0) : x = 1 - y := by + rcases hy with hy | hy + · rw [hy, sub_zero]; exact h.mpr hy + · rw [hy]; rcases hx with hx | hx + · rw [hx]; simp + · have := h.mp hx; rw [hy] at this; exact absurd this one_ne_zero + +/-- `BitVec.slt` as a signed-`toInt` comparison. -/ +lemma slt_true_iff {w : ℕ} (x y : BitVec w) : x.slt y = true ↔ x.toInt < y.toInt := by + simp [BitVec.slt] + +/-- `BitVec.slt` false as the negated signed comparison. -/ +lemma slt_false_iff {w : ℕ} (x y : BitVec w) : x.slt y = false ↔ ¬ x.toInt < y.toInt := by + simp [BitVec.slt] + +/-- `BitVec.ult` as an unsigned-`toNat` comparison. -/ +lemma ult_true_iff {w : ℕ} (x y : BitVec w) : x.ult y = true ↔ x.toNat < y.toNat := by + simp [BitVec.ult] + +/-- `BitVec.ult` false as the negated unsigned comparison. -/ +lemma ult_false_iff {w : ℕ} (x y : BitVec w) : x.ult y = false ↔ ¬ x.toNat < y.toNat := by + simp [BitVec.ult] + +end SP1Clean diff --git a/SP1Clean/Math/Misc.lean b/SP1Clean/Math/Misc.lean index b3e45676..bb0bb6cc 100644 --- a/SP1Clean/Math/Misc.lean +++ b/SP1Clean/Math/Misc.lean @@ -60,8 +60,8 @@ end Int · split_ifs <;> simp [h] instance Fin.noZeroDivisors_of_prime (p : ℕ) - [hp : Fact (Nat.Prime (p + 1))] : NoZeroDivisors (Fin (p + 1)) := by - refine IsDomain.to_noZeroDivisors (ZMod (p + 1)) + [hp : Fact (Nat.Prime (p + 1))] : NoZeroDivisors (Fin (p + 1)) := + IsDomain.to_noZeroDivisors (ZMod (p + 1)) @[simp] theorem Std.ExtDHashMap.insert_insert [BEq α] [Hashable α] [LawfulBEq α] {m : Std.ExtDHashMap α β} (a : α) (b b' : β a) : diff --git a/SP1Clean/Math/MulCarryChain.lean b/SP1Clean/Math/MulCarryChain.lean index cf2c2daf..39abd46a 100644 --- a/SP1Clean/Math/MulCarryChain.lean +++ b/SP1Clean/Math/MulCarryChain.lean @@ -86,9 +86,9 @@ variable {p : ℕ} theorem gate_zero (cp : ℕ → ℕ) : ((product cp 0 : ℕ) : ZMod p) = ((cp 0 : ℕ) : ZMod p) - ((carry cp 0 : ℕ) : ZMod p) * 256 := by - have h := recurrence_zero cp have hc : (((product cp 0 : ℕ) : ZMod p) + 256 * ((carry cp 0 : ℕ) : ZMod p)) - = ((cp 0 : ℕ) : ZMod p) := by exact_mod_cast congrArg (Nat.cast : ℕ → ZMod p) h + = ((cp 0 : ℕ) : ZMod p) := by + exact_mod_cast congrArg (Nat.cast : ℕ → ZMod p) (recurrence_zero cp) linear_combination hc /-- Step carry-chain gate over `ZMod p`. -/ @@ -96,10 +96,9 @@ theorem gate_succ (cp : ℕ → ℕ) (i : ℕ) : ((product cp (i + 1) : ℕ) : ZMod p) = ((cp (i + 1) : ℕ) : ZMod p) + ((carry cp i : ℕ) : ZMod p) - ((carry cp (i + 1) : ℕ) : ZMod p) * 256 := by - have h := recurrence_succ cp i have hc : (((product cp (i + 1) : ℕ) : ZMod p) + 256 * ((carry cp (i + 1) : ℕ) : ZMod p)) = ((cp (i + 1) : ℕ) : ZMod p) + ((carry cp i : ℕ) : ZMod p) := by - exact_mod_cast congrArg (Nat.cast : ℕ → ZMod p) h + exact_mod_cast congrArg (Nat.cast : ℕ → ZMod p) (recurrence_succ cp i) linear_combination hc /-- The carry cast into `ZMod p` round-trips through `.val` (no wrap), giving the `< 65536` range diff --git a/SP1Clean/Math/Word.lean b/SP1Clean/Math/Word.lean index bdc4252e..c2add9d1 100644 --- a/SP1Clean/Math/Word.lean +++ b/SP1Clean/Math/Word.lean @@ -83,6 +83,15 @@ lemma toBitVec128_toNat [NeZero p] {w : Word (ZMod p)} (hw : w.isU64) : have := lt_cases_of_isU64 hw rw [Nat.mod_eq_of_lt (by omega)] +/-- The constant word `#v[4,0,0,0]` (the "+4" PC increment used by Jal/Jalr/Branch) is a valid u64. -/ +lemma isU64_four [NeZero p] [Fact (2 ^ 17 < p)] : + isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := by + have h4lt : (4 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + refine isU64_of_cases ?_ ?_ ?_ ?_ <;> + simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, + List.getElem_cons_succ, show (4 : ZMod p) = ((4 : ℕ) : ZMod p) from by norm_cast, + ZMod.val_natCast_of_lt h4lt, ZMod.val_zero] <;> norm_num + end Word /-- Clearing bit 0 (the Sail `BitVec.update _ 0 0#1`, which unfolds to `~~~(1#64) &&& ·`) of @@ -110,8 +119,7 @@ lemma ofNat64_clear_lsb_and {M b : ℕ} (hb : b ≤ 1) (hM : M % 2 = 0) : exact ZMod.val_natCast_of_lt (show (65536 : ℕ) < p by omega) lemma val_65536_ne_zero [NeZero p] [Fact (2 ^ 17 < p)] : (65536 : ZMod p) ≠ 0 := by - have h : (65536 : ZMod p).val = 65536 := val_65536_zmod_p - intro hz; rw [hz] at h; simp at h + simp [← ZMod.val_eq_zero, val_65536_zmod_p] @[simp] lemma val_2_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : (2 : ZMod p).val = 2 := by @@ -124,8 +132,42 @@ lemma val_65536_ne_zero [NeZero p] [Fact (2 ^ 17 < p)] : (65536 : ZMod p) ≠ 0 exact ZMod.val_natCast_of_lt (show (4 : ℕ) < p by omega) lemma val_4_ne_zero [NeZero p] [Fact (2 ^ 17 < p)] : (4 : ZMod p) ≠ 0 := by - have h : (4 : ZMod p).val = 4 := val_4_zmod_p - intro hz; rw [hz] at h; simp at h + simp [← ZMod.val_eq_zero, val_4_zmod_p] + +@[simp] lemma val_8_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (8 : ZMod p).val = 8 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (8 : ℕ) < p by omega) + +@[simp] lemma val_13_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (13 : ZMod p).val = 13 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (13 : ℕ) < p by omega) + +@[simp] lemma val_14_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (14 : ZMod p).val = 14 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (14 : ℕ) < p by omega) + +@[simp] lemma val_16_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (16 : ZMod p).val = 16 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) + +@[simp] lemma val_32_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (32 : ZMod p).val = 32 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (32 : ℕ) < p by omega) + +@[simp] lemma val_64_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (64 : ZMod p).val = 64 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (64 : ℕ) < p by omega) + +@[simp] lemma val_32768_zmod_p [NeZero p] [Fact (2 ^ 17 < p)] : + (32768 : ZMod p).val = 32768 := by + have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega + exact ZMod.val_natCast_of_lt (show (32768 : ℕ) < p by omega) /-- The `÷4` alignment check `(x · 4⁻¹).val < 2^14` pins `x = (x · 4⁻¹) · 4` exactly in `ℕ`: since `(x · 4⁻¹).val · 4 < 2^16 < p` there is no wrap, so `x.val = (x · 4⁻¹).val · 4`. The shared core of the @@ -159,6 +201,9 @@ lemma val_lt_65536_of_mul_inv_four_lt [Fact p.Prime] [Fact (2 ^ 17 < p)] {x : ZM have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega exact ZMod.val_natCast_of_lt (show (65535 : ℕ) < p by omega) +lemma val_65535_ne_zero [NeZero p] [Fact (2 ^ 17 < p)] : (65535 : ZMod p) ≠ 0 := by + simp [← ZMod.val_eq_zero, val_65535_zmod_p] + /-- The 16-bit complement `65535 - x` has a clean `.val` (= `65535 - x.val` in `ℕ`). Used by the Sub borrow-chain limb lift: feeding `bbar := 65535 - b[i]` (a genuine 16-bit value) into the unchanged `limb_lift` keeps every additive sum `< p`, exactly diff --git a/SP1Clean/Model/InteractionBus.lean b/SP1Clean/Model/InteractionBus.lean index e112f9cc..d65d47fe 100644 --- a/SP1Clean/Model/InteractionBus.lean +++ b/SP1Clean/Model/InteractionBus.lean @@ -64,9 +64,7 @@ theorem multiplicitySum_cons (head : LookupAccess) (tail : LookupAccessList) (k multiplicitySum (head :: tail) k = (if keyOf head = k then multOf head else 0) + multiplicitySum tail k := by simp only [multiplicitySum, filterKey, List.filter_cons] - by_cases h : keyOf head = k - · simp [h] - · simp [h] + by_cases h : keyOf head = k <;> simp [h] theorem multiplicitySum_append (l1 l2 : LookupAccessList) (k : LookupKey) : multiplicitySum (l1 ++ l2) k = multiplicitySum l1 k + multiplicitySum l2 k := by @@ -181,7 +179,7 @@ theorem provider_touches_pos_send -- so the provider's sum at this key is non-zero, hence its filtered list is non-empty have hprov_ne : multiplicitySum prov (keyOf a) ≠ 0 := by omega by_contra h - push_neg at h + push Not at h refine hprov_ne ?_ simp only [multiplicitySum, filterKey] rw [List.filter_eq_nil_iff.mpr ?_]; · rfl @@ -207,7 +205,7 @@ theorem balanced_pos_has_neg (a : LookupAccess) (ha : a ∈ accesses) (hak : keyOf a = k) (h_pos : 0 < multOf a) : ∃ b ∈ accesses, keyOf b = k ∧ multOf b < 0 := by by_contra h - push_neg at h + push Not at h -- if no contribution at key `k` is strictly negative, every summand of `multiplicitySum … k` is `≥ 0` have h_nonneg : ∀ x ∈ (filterKey accesses k).map multOf, 0 ≤ x := by intro x hx diff --git a/SP1Clean/Model/InteractionProjection.lean b/SP1Clean/Model/InteractionProjection.lean index 1bcee991..d467b2be 100644 --- a/SP1Clean/Model/InteractionProjection.lean +++ b/SP1Clean/Model/InteractionProjection.lean @@ -39,9 +39,8 @@ omit [NeZero p] in lemma val_of_binary (hp : 2 < p) {is_real : ZMod p} (h : is_real = 0 ∨ is_real = 1) : is_real.val = 0 ∨ is_real.val = 1 := by haveI : NeZero p := ⟨by omega⟩ - rcases h with h | h <;> subst h - · exact Or.inl ZMod.val_zero - · exact Or.inr (by rw [ZMod.val_one_eq_one_mod, Nat.mod_eq_of_lt (show 1 < p by omega)]) + rcases h with h | h <;> subst h <;> + simp [ZMod.val_one_eq_one_mod, Nat.mod_eq_of_lt (show 1 < p by omega)] omit [NeZero p] in /-- For a binary `is_real`, `signedVal is_real = is_real.val` (the positive/`send` multiplicity; both diff --git a/SP1Clean/Model/InteractionRecovery.lean b/SP1Clean/Model/InteractionRecovery.lean index 886882ea..db20d236 100644 --- a/SP1Clean/Model/InteractionRecovery.lean +++ b/SP1Clean/Model/InteractionRecovery.lean @@ -42,9 +42,7 @@ lemma interactionsWith_formalSubcircuit_eq_nil {Input Output : TypeMap} rw [FlatOperation.channels]; exact List.mem_map.mpr ⟨i, hi, rfl⟩ have hcontra := hsub hmem rw [hci'] at hcontra - rcases List.mem_append.mp hcontra with h | h - · exact h_g h - · exact h_r h + exact (List.mem_append.mp hcontra).elim h_g h_r /-- **`.main`-form companion** (matches what `circuit_norm` leaves after reducing a formal subcircuit): a circuit's `main` emits nothing on a channel outside its declared `channels` (= guarantees ++ @@ -88,9 +86,7 @@ lemma filter_interactions_formalAssertion_eq_nil {Input : TypeMap} [ProvableType rw [FlatOperation.channels]; exact List.mem_map.mpr ⟨i, hi, rfl⟩ have hcontra := hsub hmem rw [hci'] at hcontra - rcases List.mem_append.mp hcontra with h | h - · exact h_g h - · exact h_r h + exact (List.mem_append.mp hcontra).elim h_g h_r /-- `.2`-projection form of `interactionsWith_main_eq_nil` — `circuit_norm` unfolds `Circuit.operations` to `(circuit.main input offset).2`, so this is the shape that actually appears after reducing diff --git a/SP1Clean/Model/Register.lean b/SP1Clean/Model/Register.lean index f35f36a0..eaafdadf 100644 --- a/SP1Clean/Model/Register.lean +++ b/SP1Clean/Model/Register.lean @@ -64,7 +64,7 @@ def reg_idx_to_Register (idx : BitVec 5) : Register := /-- All of the registers corresponding to a `BitVec 5` contain 64-bit values. -/ lemma reg_idx_must_64 (idx : BitVec 5) : RegisterType (reg_idx_to_Register idx) = BitVec 64 := by - simp [reg_idx_to_Register] + simp only [reg_idx_to_Register] split <;> rfl /-- Interpret a bitvec into a register type for some vector index. -/ @@ -269,10 +269,6 @@ reg_idx_to_Register idx = .x31 ↔ idx = 31#5 ∨ idx = 0#5 := by fin_cases idx lemma regidxToRegister_inj (idx idx' : BitVec 5) : reg_idx_to_Register idx = reg_idx_to_Register idx' ↔ idx = idx' ∨ (idx = 0#5 ∧ idx' = 31#5) ∨ (idx = 31#5 ∧ idx' = 0#5) := by - fin_cases idx - all_goals { - simp [eq_comm (a := idx')] - try {aesop} - } + fin_cases idx <;> (simp [eq_comm (a := idx')]; try aesop) end regidx diff --git a/SP1Clean/Model/SP1Constraint.lean b/SP1Clean/Model/SP1Constraint.lean index 0b27d699..44d98035 100644 --- a/SP1Clean/Model/SP1Constraint.lean +++ b/SP1Clean/Model/SP1Constraint.lean @@ -86,8 +86,7 @@ SP1's RISC-V `Opcode` enum, emitted by the constraint compiler as `Opcode.ofNat `.program` bus interactions (the chip adapters' instruction reads). The *semantics* of program interactions are deferred (their `toProp` is `True`, see `Extracted/ExtractionDSL.lean`), so — unlike the fully-modelled `ByteOpcode` — `Opcode` only needs to carry the numeric tag and elaborate. -It is -a thin `ℕ` wrapper with an `ofNat` that, like `ByteOpcode.ofNat`, composes with the +It is a thin `ℕ` wrapper with an `ofNat` that, like `ByteOpcode.ofNat`, composes with the `[CoeHead F ℕ]` hypothesis so `Opcode.ofNat (opcode : F)` typechecks. -/ abbrev Opcode := ℕ diff --git a/SP1Clean/Model/SailWrap.lean b/SP1Clean/Model/SailWrap.lean index bb8be6a5..61c39e4b 100644 --- a/SP1Clean/Model/SailWrap.lean +++ b/SP1Clean/Model/SailWrap.lean @@ -57,7 +57,6 @@ section sailboats namespace Option - /-- Version of `Option.getM` using `throw` instead of `failure`. -/ def toSailM {α} (x : Option α) : SailM α := Option.elim x (throw Sail.Error.Unreachable) pure diff --git a/SP1Clean/Native/Operations/AddressOperation.lean b/SP1Clean/Native/Operations/AddressOperation.lean index b330c91d..58a08062 100644 --- a/SP1Clean/Native/Operations/AddressOperation.lean +++ b/SP1Clean/Native/Operations/AddressOperation.lean @@ -86,10 +86,9 @@ theorem addressSemantics_of_raw {input : Inputs (ZMod p)} Word.isU64_of_cases (hcc 0) (hcc 1) (hcc 2) (hcc 3) have h_sum := AddrAddOperation.addrAddSemantics_of_carries hbW hccW h_aa_raw refine ⟨?_, hob0, hob1, hob2⟩ - rw [show Word.toNat #v[input.b[0], input.b[1], input.b[2], input.b[3]] = Word.toNat input.b from rfl, + rwa [show Word.toNat #v[input.b[0], input.b[1], input.b[2], input.b[3]] = Word.toNat input.b from rfl, show Word.toNat #v[input.cc[0], input.cc[1], input.cc[2], input.cc[3]] = Word.toNat input.cc from rfl] at h_sum - exact h_sum /-! ## The witnessed `FormalCircuit` diff --git a/SP1Clean/Native/Operations/AddwOperation/RawSpec.lean b/SP1Clean/Native/Operations/AddwOperation/RawSpec.lean index 4783dff5..cdfe062e 100644 --- a/SP1Clean/Native/Operations/AddwOperation/RawSpec.lean +++ b/SP1Clean/Native/Operations/AddwOperation/RawSpec.lean @@ -63,11 +63,6 @@ theorem addwSemantics_of_carries {a b : Word (ZMod p)} {cols : Extracted.AddwOpe (BitVec.setWidth 32 (Word.toBitVec64 a + Word.toBitVec64 b)) cols.msb.msb hv0 hv1 h_msb rfl rfl hX -/-- `(65535 : ZMod p) ≠ 0` (its `val` is `65535`). -/ -private lemma c65535_ne_zero : (65535 : ZMod p) ≠ 0 := by - haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - intro h; have := congrArg ZMod.val h; rw [val_65535_zmod_p, ZMod.val_zero] at this; omega - set_option maxHeartbeats 16000000 in /-- Backward (completeness) core — the converse of `addwSemantics_of_carries`: a 64-bit value equal to the sign extension of the low-32 add witnesses the unique boolean carry chain + ranges **and** the sign @@ -80,7 +75,6 @@ theorem carries_of_addwSemantics {a b : Word (ZMod p)} {cols : Extracted.AddwOpe = (BitVec.setWidth 32 (Word.toBitVec64 a + Word.toBitVec64 b)).signExtend 64) : RawSpec a b cols ∧ cols.msb.msb = if cols.value[1].val ≥ 32768 then 1 else 0 := by haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - have hp : 2 ^ 17 < p := Fact.out obtain ⟨ha0, ha1, ha2, ha3⟩ := Word.lt_cases_of_isU64 ha obtain ⟨hbb0, hbb1, hbb2, hbb3⟩ := Word.lt_cases_of_isU64 hb obtain ⟨hv0, hv1, hm2, _⟩ := Word.lt_cases_of_isU64 hU @@ -164,7 +158,7 @@ theorem carries_of_addwSemantics {a b : Word (ZMod p)} {cols : Extracted.AddwOpe have hvaleq : (m * 65535 : ZMod p).val = (65535 : ZMod p).val := by rw [hMdef, hMval, val_65535_zmod_p] have hmul : m * 65535 = (1 : ZMod p) * 65535 := by rw [one_mul]; exact (ZMod.val_injective _) hvaleq - exact mul_right_cancel₀ c65535_ne_zero hmul + exact mul_right_cancel₀ val_65535_ne_zero hmul · rw [if_neg hcond] at hbvN have hmsblt : (a[0].val + b[0].val + (a[1].val + b[1].val) * 2 ^ 16) % 2 ^ 32 < 2 ^ 31 := by have h := hcond @@ -179,6 +173,6 @@ theorem carries_of_addwSemantics {a b : Word (ZMod p)} {cols : Extracted.AddwOpe have hvaleq : (m * 65535 : ZMod p).val = (0 : ZMod p).val := by rw [hMdef, hMval, ZMod.val_zero] have hmul : m * 65535 = (0 : ZMod p) * 65535 := by rw [zero_mul]; exact (ZMod.val_injective _) hvaleq - exact mul_right_cancel₀ c65535_ne_zero hmul + exact mul_right_cancel₀ val_65535_ne_zero hmul end SP1Clean.AddwOperation diff --git a/SP1Clean/Native/Operations/BitwiseU16Operation.lean b/SP1Clean/Native/Operations/BitwiseU16Operation.lean index 14b67641..5279a5c4 100644 --- a/SP1Clean/Native/Operations/BitwiseU16Operation.lean +++ b/SP1Clean/Native/Operations/BitwiseU16Operation.lean @@ -71,8 +71,7 @@ def Spec (input : Inputs (ZMod p)) : Prop := lemma reassemble (w low : ZMod p) : w = low + (w - low) * (256 : ZMod p)⁻¹ * 256 := by haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - have h256 : (256 : ZMod p)⁻¹ * 256 = 1 := inv_mul_cancel₀ val_256_ne_zero - rw [mul_assoc, h256, mul_one]; ring + rw [mul_assoc, inv_mul_cancel₀ val_256_ne_zero, mul_one]; ring /-- A limb's value splits as its low byte plus 256× its high byte, given both are bytes. -/ lemma limb_split {w low : ZMod p} (hlo : low.val < 256) @@ -261,8 +260,7 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨_h_gate, h_bw⟩ := h_holds have h_spec := h_bw ⟨hop, hbin⟩ refine ⟨?_, Or.inr ⟨hop, hbin⟩⟩ - simp only [decompBytes, eb, ec, elb, elc, ← sub_eq_add_neg] at h_spec ⊢ - exact h_spec + simpa only [decompBytes, eb, ec, elb, elc, ← sub_eq_add_neg] using h_spec set_option maxHeartbeats 4000000 in theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Spec := by @@ -283,8 +281,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp fun k _ => by rw [← hilc]; simp only [Vector.getElem_map] refine ⟨?_, ⟨hop, hbin⟩, ?_⟩ · rcases hbin with h | h <;> rw [h] <;> simp - · simp only [decompBytes, eb, ec, elb, elc, ← sub_eq_add_neg] at h_spec ⊢ - exact h_spec + · simpa only [decompBytes, eb, ec, elb, elc, ← sub_eq_add_neg] using h_spec /-- SP1's `BitwiseU16Operation::eval` as a Clean-native `FormalAssertion`: the `is_real` binary gate plus the composed `BitwiseOperation` on the two `U16toU8` byte decompositions, witnessing nothing. -/ diff --git a/SP1Clean/Native/Operations/IsEqualWordOperation/RawSpec.lean b/SP1Clean/Native/Operations/IsEqualWordOperation/RawSpec.lean index 3d787a57..e21c61f1 100644 --- a/SP1Clean/Native/Operations/IsEqualWordOperation/RawSpec.lean +++ b/SP1Clean/Native/Operations/IsEqualWordOperation/RawSpec.lean @@ -31,9 +31,7 @@ theorem isEqualWord_of_raw {a b : Word (ZMod p)} {cols : Extracted.IsEqualWordOp (h_raw : RawSpec a b cols) : cols.is_diff_zero.result = if (a[0] = b[0] ∧ a[1] = b[1] ∧ a[2] = b[2] ∧ a[3] = b[3]) then 1 else 0 := by - have h := IsZeroWordOperation.isZeroWord_of_raw h_raw - simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, - List.getElem_cons_succ, sub_eq_zero] at h - exact h + simpa only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, + List.getElem_cons_succ, sub_eq_zero] using IsZeroWordOperation.isZeroWord_of_raw h_raw end SP1Clean.IsEqualWordOperation diff --git a/SP1Clean/Native/Operations/LtOperationUnsigned/RawSpec.lean b/SP1Clean/Native/Operations/LtOperationUnsigned/RawSpec.lean index 293c8e1c..5d9d5f34 100644 --- a/SP1Clean/Native/Operations/LtOperationUnsigned/RawSpec.lean +++ b/SP1Clean/Native/Operations/LtOperationUnsigned/RawSpec.lean @@ -1,4 +1,5 @@ import SP1Clean.Math.Word +import SP1Clean.Math.Gate import SP1Clean.Extracted.LtOperationUnsigned import SP1Clean.Native.Operations.U16CompareOperation.RawSpec import SP1Clean.Proofs.Operations.U16CompareOperation.Formal @@ -23,12 +24,6 @@ variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] instance : NeZero p := ⟨(Fact.out : p.Prime).pos.ne'⟩ -omit [Fact (2 ^ 17 < p)] in -/-- A boolean field element has `val ≤ 1`. -/ -private lemma bool_val_le {x : ZMod p} (h : x = 0 ∨ x = 1) : x.val ≤ 1 := by - haveI : Fact (1 < p) := ⟨(Fact.out : p.Prime).one_lt⟩ - rcases h with h | h <;> simp [h, ZMod.val_one] - set_option maxHeartbeats 1000000 in /-- Four boolean flags whose field sum is `0` or `1` have **at most one** set: the assignment is one of the five all-but-one-zero patterns. The key step lifts the field sum-bound to `ℕ` @@ -93,11 +88,6 @@ def Selectors (b cc : Word (ZMod p)) (cols : Extracted.LtOperationUnsigned (ZMod ((cc[3] * f3 + cc[2] * f2 + cc[1] * f1 + cc[0] * f0) - cl1 = 0) ∧ (((1 - sumf) - 1) * (cols.not_eq_inv * (cl0 - cl1) - 1) = 0) -omit [Fact (2 ^ 17 < p)] in -/-- Distinct field elements have distinct `val`s. -/ -private lemma val_ne {x y : ZMod p} (h : x ≠ y) : x.val ≠ y.val := fun hv => - h (by rw [← ZMod.natCast_zmod_val x, ← ZMod.natCast_zmod_val y, hv]) - set_option maxHeartbeats 1000000 in /-- Soundness core: the boolean flags select the most-significant differing limb (all higher limbs forced equal by the prefix-sum selectors, the selected limb forced distinct by the `not_eq_inv` diff --git a/SP1Clean/Native/Operations/MulOperation/RawSpec.lean b/SP1Clean/Native/Operations/MulOperation/RawSpec.lean index fb7ed866..505ae263 100644 --- a/SP1Clean/Native/Operations/MulOperation/RawSpec.lean +++ b/SP1Clean/Native/Operations/MulOperation/RawSpec.lean @@ -535,18 +535,14 @@ lemma lower_toNat (w : Word (ZMod p)) (lower : Extracted.U16toU8Operation (ZMod have h1 : w[1].val = (lower.low_bytes[1]).val + ((w[1] - lower.low_bytes[1]) * 256⁻¹).val * 256 := hw 1 have h2 : w[2].val = (lower.low_bytes[2]).val + ((w[2] - lower.low_bytes[2]) * 256⁻¹).val * 256 := hw 2 have h3 : w[3].val = (lower.low_bytes[3]).val + ((w[3] - lower.low_bytes[3]) * 256⁻¹).val * 256 := hw 3 - have e0 : byteAt (extendedBytes w lower s) 0 = lower.low_bytes[0] := by rfl - have e1 : byteAt (extendedBytes w lower s) 1 = (w[0] - lower.low_bytes[0]) * 256⁻¹ := by - rfl - have e2 : byteAt (extendedBytes w lower s) 2 = lower.low_bytes[1] := by rfl - have e3 : byteAt (extendedBytes w lower s) 3 = (w[1] - lower.low_bytes[1]) * 256⁻¹ := by - rfl - have e4 : byteAt (extendedBytes w lower s) 4 = lower.low_bytes[2] := by rfl - have e5 : byteAt (extendedBytes w lower s) 5 = (w[2] - lower.low_bytes[2]) * 256⁻¹ := by - rfl - have e6 : byteAt (extendedBytes w lower s) 6 = lower.low_bytes[3] := by rfl - have e7 : byteAt (extendedBytes w lower s) 7 = (w[3] - lower.low_bytes[3]) * 256⁻¹ := by - rfl + have e0 : byteAt (extendedBytes w lower s) 0 = lower.low_bytes[0] := rfl + have e1 : byteAt (extendedBytes w lower s) 1 = (w[0] - lower.low_bytes[0]) * 256⁻¹ := rfl + have e2 : byteAt (extendedBytes w lower s) 2 = lower.low_bytes[1] := rfl + have e3 : byteAt (extendedBytes w lower s) 3 = (w[1] - lower.low_bytes[1]) * 256⁻¹ := rfl + have e4 : byteAt (extendedBytes w lower s) 4 = lower.low_bytes[2] := rfl + have e5 : byteAt (extendedBytes w lower s) 5 = (w[2] - lower.low_bytes[2]) * 256⁻¹ := rfl + have e6 : byteAt (extendedBytes w lower s) 6 = lower.low_bytes[3] := rfl + have e7 : byteAt (extendedBytes w lower s) 7 = (w[3] - lower.low_bytes[3]) * 256⁻¹ := rfl rw [e0, e1, e2, e3, e4, e5, e6, e7, Word.toNat_def, h0, h1, h2, h3] ring @@ -742,14 +738,14 @@ lemma extendedBytes_toNat (w : Word (ZMod p)) (lower : Extracted.U16toU8Operatio (byteAt (extendedBytes w lower s) 0).val + (byteAt (extendedBytes w lower s) 1).val*256^1 + (byteAt (extendedBytes w lower s) 2).val*256^2 + (byteAt (extendedBytes w lower s) 3).val*256^3 + (byteAt (extendedBytes w lower s) 4).val*256^4 + (byteAt (extendedBytes w lower s) 5).val*256^5 + (byteAt (extendedBytes w lower s) 6).val*256^6 + (byteAt (extendedBytes w lower s) 7).val*256^7 + (byteAt (extendedBytes w lower s) 8).val*256^8 + (byteAt (extendedBytes w lower s) 9).val*256^9 + (byteAt (extendedBytes w lower s) 10).val*256^10 + (byteAt (extendedBytes w lower s) 11).val*256^11 + (byteAt (extendedBytes w lower s) 12).val*256^12 + (byteAt (extendedBytes w lower s) 13).val*256^13 + (byteAt (extendedBytes w lower s) 14).val*256^14 + (byteAt (extendedBytes w lower s) 15).val*256^15 = w.toNat + (s * 255).val * (256^8 + 256^9 + 256^10 + 256^11 + 256^12 + 256^13 + 256^14 + 256^15) := by have hlow := lower_toNat w lower s hspec - have e8 : byteAt (extendedBytes w lower s) 8 = s * 255 := by rfl - have e9 : byteAt (extendedBytes w lower s) 9 = s * 255 := by rfl - have e10 : byteAt (extendedBytes w lower s) 10 = s * 255 := by rfl - have e11 : byteAt (extendedBytes w lower s) 11 = s * 255 := by rfl - have e12 : byteAt (extendedBytes w lower s) 12 = s * 255 := by rfl - have e13 : byteAt (extendedBytes w lower s) 13 = s * 255 := by rfl - have e14 : byteAt (extendedBytes w lower s) 14 = s * 255 := by rfl - have e15 : byteAt (extendedBytes w lower s) 15 = s * 255 := by rfl + have e8 : byteAt (extendedBytes w lower s) 8 = s * 255 := rfl + have e9 : byteAt (extendedBytes w lower s) 9 = s * 255 := rfl + have e10 : byteAt (extendedBytes w lower s) 10 = s * 255 := rfl + have e11 : byteAt (extendedBytes w lower s) 11 = s * 255 := rfl + have e12 : byteAt (extendedBytes w lower s) 12 = s * 255 := rfl + have e13 : byteAt (extendedBytes w lower s) 13 = s * 255 := rfl + have e14 : byteAt (extendedBytes w lower s) 14 = s * 255 := rfl + have e15 : byteAt (extendedBytes w lower s) 15 = s * 255 := rfl rw [e8, e9, e10, e11, e12, e13, e14, e15] omega diff --git a/SP1Clean/Native/Operations/SubwOperation/RawSpec.lean b/SP1Clean/Native/Operations/SubwOperation/RawSpec.lean index 7a8907dd..23d02b52 100644 --- a/SP1Clean/Native/Operations/SubwOperation/RawSpec.lean +++ b/SP1Clean/Native/Operations/SubwOperation/RawSpec.lean @@ -70,11 +70,6 @@ theorem subwSemantics_of_carries {a b : Word (ZMod p)} {cols : Extracted.SubwOpe (BitVec.setWidth 32 (Word.toBitVec64 a - Word.toBitVec64 b)) cols.msb.msb hv0 hv1 h_msb rfl rfl hX -/-- `(65535 : ZMod p) ≠ 0` (its `val` is `65535`). -/ -private lemma c65535_ne_zero : (65535 : ZMod p) ≠ 0 := by - haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - intro h; have := congrArg ZMod.val h; rw [val_65535_zmod_p, ZMod.val_zero] at this; omega - set_option maxHeartbeats 16000000 in /-- Backward (completeness) core — the converse of `subwSemantics_of_carries`. Borrow-form analog of `AddwOperation.carries_of_addwSemantics`. -/ @@ -85,7 +80,6 @@ theorem carries_of_subwSemantics {a b : Word (ZMod p)} {cols : Extracted.SubwOpe = (BitVec.setWidth 32 (Word.toBitVec64 a - Word.toBitVec64 b)).signExtend 64) : RawSpec a b cols ∧ cols.msb.msb = if cols.value[1].val ≥ 32768 then 1 else 0 := by haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - have hp : 2 ^ 17 < p := Fact.out obtain ⟨ha0, ha1, ha2, ha3⟩ := Word.lt_cases_of_isU64 ha obtain ⟨hbb0, hbb1, hbb2, hbb3⟩ := Word.lt_cases_of_isU64 hb obtain ⟨hv0, hv1, hm2, _⟩ := Word.lt_cases_of_isU64 hU @@ -167,7 +161,7 @@ theorem carries_of_subwSemantics {a b : Word (ZMod p)} {cols : Extracted.SubwOpe have hvaleq : (m * 65535 : ZMod p).val = (65535 : ZMod p).val := by rw [hMdef, hMval, val_65535_zmod_p] have hmul : m * 65535 = (1 : ZMod p) * 65535 := by rw [one_mul]; exact (ZMod.val_injective _) hvaleq - exact mul_right_cancel₀ c65535_ne_zero hmul + exact mul_right_cancel₀ val_65535_ne_zero hmul · rw [if_neg hcond] at hbvN have hmsblt : ((a[0].val + 65535 - b[0].val + 1) + (a[1].val + 65535 - b[1].val) * 2 ^ 16) % 2 ^ 32 < 2 ^ 31 := by have h := hcond @@ -182,6 +176,6 @@ theorem carries_of_subwSemantics {a b : Word (ZMod p)} {cols : Extracted.SubwOpe have hvaleq : (m * 65535 : ZMod p).val = (0 : ZMod p).val := by rw [hMdef, hMval, ZMod.val_zero] have hmul : m * 65535 = (0 : ZMod p) * 65535 := by rw [zero_mul]; exact (ZMod.val_injective _) hvaleq - exact mul_right_cancel₀ c65535_ne_zero hmul + exact mul_right_cancel₀ val_65535_ne_zero hmul end SP1Clean.SubwOperation diff --git a/SP1Clean/Native/Operations/U16toU8OperationUnsafe.lean b/SP1Clean/Native/Operations/U16toU8OperationUnsafe.lean index d96f5f7b..4118c8c2 100644 --- a/SP1Clean/Native/Operations/U16toU8OperationUnsafe.lean +++ b/SP1Clean/Native/Operations/U16toU8OperationUnsafe.lean @@ -58,8 +58,7 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := circuit_proof_start haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ have h256 : (256 : ZMod p)⁻¹ * 256 = 1 := inv_mul_cancel₀ val_256_ne_zero - refine ⟨?_, ?_, ?_, ?_⟩ <;> - · rw [mul_assoc, h256, mul_one]; ring + refine ⟨?_, ?_, ?_, ?_⟩ <;> rw [mul_assoc, h256, mul_one] <;> ring omit [Fact (2 ^ 17 < p)] in theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Spec := by diff --git a/SP1Clean/Native/Readers/ALUTypeReader.lean b/SP1Clean/Native/Readers/ALUTypeReader.lean index bf2e495c..1339632e 100644 --- a/SP1Clean/Native/Readers/ALUTypeReader.lean +++ b/SP1Clean/Native/Readers/ALUTypeReader.lean @@ -134,9 +134,8 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := Or.inr h_assumptions, Or.inr h_assumptions, Or.inr (bool_of_mul_pred h_immbin), fun _ _ => bool_of_mul_pred hbin⟩ -- the four immediate gates: bridge `input_cols_op_c[i]` / `…prev_value[i]` (value-level) to the -- `Expression.eval env …[i]` form `h_holds` carries, via the `h_input` Word equalities + `getElem_map`. - have hoc := h_input.1.2.2.2.2.2.1 - have hpv := h_input.1.2.2.2.2.2.2.1.1 - rw [← hoc, ← hpv]; simp only [Vector.getElem_map]; exact ⟨i0, i1, i2, i3⟩ + rw [← h_input.1.2.2.2.2.2.1, ← h_input.1.2.2.2.2.2.2.1.1] + simp only [Vector.getElem_map]; exact ⟨i0, i1, i2, i3⟩ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Spec := by circuit_proof_start diff --git a/SP1Clean/Native/Readers/ALUTypeReaderImmutable.lean b/SP1Clean/Native/Readers/ALUTypeReaderImmutable.lean index e9ba5930..0584fefd 100644 --- a/SP1Clean/Native/Readers/ALUTypeReaderImmutable.lean +++ b/SP1Clean/Native/Readers/ALUTypeReaderImmutable.lean @@ -122,15 +122,12 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := -- bridge op_a_memory.prev_value eval → value for the four read-zeroing gates (nested vector field). have hmap_a : Vector.map (Expression.eval env) input_var_cols_op_a_memory_prev_value = input_cols_op_a_memory_prev_value := h_input.1.2.1.1 - have ea0 : Expression.eval env input_var_cols_op_a_memory_prev_value[0] - = input_cols_op_a_memory_prev_value[0] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_cols_op_a_memory_prev_value[1] - = input_cols_op_a_memory_prev_value[1] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env input_var_cols_op_a_memory_prev_value[2] - = input_cols_op_a_memory_prev_value[2] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env input_var_cols_op_a_memory_prev_value[3] - = input_cols_op_a_memory_prev_value[3] := by rw [← hmap_a]; simp only [Vector.getElem_map] - rw [ea0] at z0; rw [ea1] at z1; rw [ea2] at z2; rw [ea3] at z3 + have ea : ∀ (i : ℕ) (hi : i < 4), + Expression.eval env (input_var_cols_op_a_memory_prev_value[i]'hi) + = input_cols_op_a_memory_prev_value[i]'hi := by + intro i hi; rw [← hmap_a, Vector.getElem_map] + rw [ea 0 (by norm_num)] at z0; rw [ea 1 (by norm_num)] at z1 + rw [ea 2 (by norm_num)] at z2; rw [ea 3 (by norm_num)] at z3 -- the immediate gates bridge (op_c + op_c_memory.prev_value), as in `ALUTypeReader.soundness`. have hoc := h_input.1.2.2.2.2.2.1 have hpv := h_input.1.2.2.2.2.2.2.1.1 @@ -146,15 +143,12 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp -- bridges: op_a_memory.prev_value (zeroing gates) and op_c / op_c_memory.prev_value (immediate gates). have hmap_a : Vector.map (Expression.eval env.toEnvironment) input_var_cols_op_a_memory_prev_value = input_cols_op_a_memory_prev_value := h_input.1.2.1.1 - have ea0 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[0] - = input_cols_op_a_memory_prev_value[0] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[1] - = input_cols_op_a_memory_prev_value[1] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[2] - = input_cols_op_a_memory_prev_value[2] := by rw [← hmap_a]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[3] - = input_cols_op_a_memory_prev_value[3] := by rw [← hmap_a]; simp only [Vector.getElem_map] - rw [← ea0] at z0; rw [← ea1] at z1; rw [← ea2] at z2; rw [← ea3] at z3 + have ea : ∀ (i : ℕ) (hi : i < 4), + Expression.eval env.toEnvironment (input_var_cols_op_a_memory_prev_value[i]'hi) + = input_cols_op_a_memory_prev_value[i]'hi := by + intro i hi; rw [← hmap_a, Vector.getElem_map] + rw [← ea 0 (by norm_num)] at z0; rw [← ea 1 (by norm_num)] at z1 + rw [← ea 2 (by norm_num)] at z2; rw [← ea 3 (by norm_num)] at z3 have hoc := h_input.1.2.2.2.2.2.1 have hpv := h_input.1.2.2.2.2.2.2.1.1 have eoc : ∀ (i : ℕ) (hi : i < 4), diff --git a/SP1Clean/Native/Readers/CPUState.lean b/SP1Clean/Native/Readers/CPUState.lean index 03a04048..77547816 100644 --- a/SP1Clean/Native/Readers/CPUState.lean +++ b/SP1Clean/Native/Readers/CPUState.lean @@ -94,11 +94,9 @@ theorem soundness [Fact (2 ^ 17 < p)] : FormalAssertion.Soundness (ZMod p) main -- (`Guarantees := True`), so the Spec implication is the only goal. intro hr1 have hneg : -input_is_real = -1 := by rw [hr1] - have hb1 := h_holds.1 hneg - have hb2 := h_holds.2 hneg - refine ⟨(byteRowSpec_range _ h13p).mp ?_, ((byteRowSpec_u8range_pair _ _).mp hb2).1⟩ + refine ⟨(byteRowSpec_range _ h13p).mp ?_, ((byteRowSpec_u8range_pair _ _).mp (h_holds.2 hneg)).1⟩ rw [sub_eq_add_neg, Nat.cast_ofNat] - exact hb1 + exact h_holds.1 hneg theorem completeness [Fact (2 ^ 17 < p)] : FormalAssertion.Completeness (ZMod p) main Assumptions Spec := by circuit_proof_start @@ -109,15 +107,13 @@ theorem completeness [Fact (2 ^ 17 < p)] : FormalAssertion.Completeness (ZMod p) simp only [circuit_norm, byteChannel] refine ⟨?_, ?_⟩ · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hb1, _⟩ := h_spec hr1 + obtain ⟨hb1, _⟩ := h_spec (neg_inj.mp hneg) rw [sub_eq_add_neg] at hb1 have key := (byteRowSpec_range _ h13p).mpr hb1 rw [Nat.cast_ofNat] at key exact key · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨_, hb2⟩ := h_spec hr1 + obtain ⟨_, hb2⟩ := h_spec (neg_inj.mp hneg) exact (byteRowSpec_u8range_pair _ _).mpr ⟨hb2, by rw [ZMod.val_zero]; norm_num⟩ /-- The native CPUState reader as a Clean `FormalAssertion`: takes the chip-owned `cols` block plus diff --git a/SP1Clean/Native/Readers/ITypeReaderImmutable.lean b/SP1Clean/Native/Readers/ITypeReaderImmutable.lean index 71683beb..01e740c8 100644 --- a/SP1Clean/Native/Readers/ITypeReaderImmutable.lean +++ b/SP1Clean/Native/Readers/ITypeReaderImmutable.lean @@ -96,13 +96,13 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := have hmap : Vector.map (Expression.eval env) input_var_cols_op_a_memory_prev_value = input_cols_op_a_memory_prev_value := h_input.1.2.1.1 have ev0 : Expression.eval env input_var_cols_op_a_memory_prev_value[0] - = input_cols_op_a_memory_prev_value[0] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[0] := by simp [← hmap] have ev1 : Expression.eval env input_var_cols_op_a_memory_prev_value[1] - = input_cols_op_a_memory_prev_value[1] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[1] := by simp [← hmap] have ev2 : Expression.eval env input_var_cols_op_a_memory_prev_value[2] - = input_cols_op_a_memory_prev_value[2] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[2] := by simp [← hmap] have ev3 : Expression.eval env input_var_cols_op_a_memory_prev_value[3] - = input_cols_op_a_memory_prev_value[3] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[3] := by simp [← hmap] rw [ev0] at z0; rw [ev1] at z1; rw [ev2] at z2; rw [ev3] at z3 exact ⟨⟨⟨z0, z1, z2, z3⟩, bool_of_mul_pred hbin, h_rac_a h_assumptions, h_rac_b h_assumptions⟩, @@ -115,13 +115,13 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp have hmap : Vector.map (Expression.eval env.toEnvironment) input_var_cols_op_a_memory_prev_value = input_cols_op_a_memory_prev_value := h_input.1.2.1.1 have ev0 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[0] - = input_cols_op_a_memory_prev_value[0] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[0] := by simp [← hmap] have ev1 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[1] - = input_cols_op_a_memory_prev_value[1] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[1] := by simp [← hmap] have ev2 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[2] - = input_cols_op_a_memory_prev_value[2] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[2] := by simp [← hmap] have ev3 : Expression.eval env.toEnvironment input_var_cols_op_a_memory_prev_value[3] - = input_cols_op_a_memory_prev_value[3] := by rw [← hmap]; simp only [Vector.getElem_map] + = input_cols_op_a_memory_prev_value[3] := by simp [← hmap] rw [← ev0] at z0; rw [← ev1] at z1; rw [← ev2] at z2; rw [← ev3] at z3 refine ⟨⟨h_assumptions, hrac_a⟩, ⟨h_assumptions, hrac_b⟩, ?_, z0, z1, z2, z3⟩ rcases hbin with h | h <;> rw [h] <;> simp diff --git a/SP1Clean/Native/Readers/RegisterAccessCols.lean b/SP1Clean/Native/Readers/RegisterAccessCols.lean index 3901d7ab..167f04d7 100644 --- a/SP1Clean/Native/Readers/RegisterAccessCols.lean +++ b/SP1Clean/Native/Readers/RegisterAccessCols.lean @@ -85,12 +85,10 @@ set_option linter.unusedSectionVars false in @[circuit_norm] lemma localLength_eq (x : Var Inputs (ZMod p)) : (elaborated (p := p)).localLength x = 0 := rfl - /-- `is_real` is binary — the precondition for the genuinely `is_real`-gated byte receives (and threaded down to the composed `RegisterAccessTimestamp`). Discharged by the chip's gate. -/ def Assumptions (input : Inputs (ZMod p)) : Prop := input.is_real = 0 ∨ input.is_real = 1 - theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := by -- `Spec` = the composed timestamp sub-assertion's `Spec` (the two byte bounds); the sub gives it from -- `h_holds`, and the requirement tail is the sub's `is_real`-binary `Assumptions`. diff --git a/SP1Clean/Proofs/Chips/AddChip/Formal.lean b/SP1Clean/Proofs/Chips/AddChip/Formal.lean index c333f75c..aa191991 100644 --- a/SP1Clean/Proofs/Chips/AddChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/AddChip/Formal.lean @@ -1,7 +1,11 @@ import SP1Clean.Native.Chips.AddChip.Defs import SP1Clean.FormalModel.Contracts.ChipAssumptions -/-! # `SP1Clean.AddChip` — contract: `Assumptions` / soundness / completeness / `circuit` -/ +/-! # `SP1Clean.AddChip` — contract: `Assumptions` / soundness / completeness / `circuit` + +This is the canonical "template" chip for the porting recipe (`docs/agents/porting-recipe.md`). When +golfing / cleaning proofs here or in any sibling chip, follow `docs/agents/proof-patterns.md` § +"Golf & cleanup discipline". -/ namespace SP1Clean.AddChip @@ -33,18 +37,13 @@ theorem completeness : obtain ⟨ha, hb, hbin, hop_a_0, h_cpu, hrac_a, hrac_b, hrac_c⟩ := h_assumptions obtain ⟨-, -, -, -, -, -, ⟨hob, -, -⟩, -, hoc, -, -⟩ := h_input have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] - have hbeq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_b_memory_prev_value := by - rw [← hob]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl - have hceq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_c_memory_prev_value := by - rw [← hoc]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have mapEq : ∀ (vv : Word (Expression (ZMod p))) (v : Word (ZMod p)), + Vector.map (Expression.eval env.toEnvironment) vv = v → + (#v[Expression.eval env.toEnvironment vv[0], Expression.eval env.toEnvironment vv[1], + Expression.eval env.toEnvironment vv[2], Expression.eval env.toEnvironment vv[3]] : Word (ZMod p)) = v := + fun vv v h => by rw [← h]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have hbeq := mapEq input_var_adapter_op_b_memory_prev_value _ hob + have hceq := mapEq input_var_adapter_op_c_memory_prev_value _ hoc -- The witnessed `value` is `populate op_b op_c` (`h_env` per-limb). have hval : (Vector.map (Expression.eval env.toEnvironment) (Vector.mapRange 4 fun i => var {index := i₀ + i}) : Word (ZMod p)) diff --git a/SP1Clean/Proofs/Chips/AddiChip/Formal.lean b/SP1Clean/Proofs/Chips/AddiChip/Formal.lean index 527d074b..8a61d973 100644 --- a/SP1Clean/Proofs/Chips/AddiChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/AddiChip/Formal.lean @@ -30,18 +30,13 @@ theorem completeness : obtain ⟨ha, hb, hbin, hop_a_0, h_cpu, hrac_a, hrac_b⟩ := h_assumptions obtain ⟨-, -, -, -, -, -, ⟨hob, -, -⟩, hoc⟩ := h_input have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] - have hbeq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_b_memory_prev_value := by - rw [← hob]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl - have hceq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_c_imm[0], - Expression.eval env.toEnvironment input_var_adapter_op_c_imm[1], - Expression.eval env.toEnvironment input_var_adapter_op_c_imm[2], - Expression.eval env.toEnvironment input_var_adapter_op_c_imm[3]] : Word (ZMod p)) - = input_adapter_op_c_imm := by - rw [← hoc]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have mapEq : ∀ (vv : Word (Expression (ZMod p))) (v : Word (ZMod p)), + Vector.map (Expression.eval env.toEnvironment) vv = v → + (#v[Expression.eval env.toEnvironment vv[0], Expression.eval env.toEnvironment vv[1], + Expression.eval env.toEnvironment vv[2], Expression.eval env.toEnvironment vv[3]] : Word (ZMod p)) = v := + fun vv v h => by rw [← h]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have hbeq := mapEq input_var_adapter_op_b_memory_prev_value _ hob + have hceq := mapEq input_var_adapter_op_c_imm _ hoc have hval : (Vector.map (Expression.eval env.toEnvironment) (Vector.mapRange 4 fun i => var {index := i₀ + i}) : Word (ZMod p)) = AddOperation.populate input_adapter_op_b_memory_prev_value input_adapter_op_c_imm := by diff --git a/SP1Clean/Proofs/Chips/AddwChip/Formal.lean b/SP1Clean/Proofs/Chips/AddwChip/Formal.lean index 8f897819..71cec214 100644 --- a/SP1Clean/Proofs/Chips/AddwChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/AddwChip/Formal.lean @@ -48,18 +48,13 @@ theorem completeness : obtain ⟨-, -, -, -, -, -, ⟨hob, -, -⟩, -, ⟨hoc, -, -⟩, -⟩ := h_input obtain ⟨h_env_val, h_env_msb⟩ := h_env have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] - have hbeq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_b_memory_prev_value := by - rw [← hob]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl - have hceq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_c_memory_prev_value := by - rw [← hoc]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have mapEq : ∀ (vv : Word (Expression (ZMod p))) (v : Word (ZMod p)), + Vector.map (Expression.eval env.toEnvironment) vv = v → + (#v[Expression.eval env.toEnvironment vv[0], Expression.eval env.toEnvironment vv[1], + Expression.eval env.toEnvironment vv[2], Expression.eval env.toEnvironment vv[3]] : Word (ZMod p)) = v := + fun vv v h => by rw [← h]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have hbeq := mapEq input_var_adapter_op_b_memory_prev_value _ hob + have hceq := mapEq input_var_adapter_op_c_memory_prev_value _ hoc have hval : (Vector.map (Expression.eval env.toEnvironment) (Vector.mapRange 2 fun i => var { index := i₀ + i }) : Vector (ZMod p) 2) = AddwOperation.addwValueWitness input_adapter_op_b_memory_prev_value diff --git a/SP1Clean/Proofs/Chips/AluX0Chip/Formal.lean b/SP1Clean/Proofs/Chips/AluX0Chip/Formal.lean index 02628b21..a2ec483a 100644 --- a/SP1Clean/Proofs/Chips/AluX0Chip/Formal.lean +++ b/SP1Clean/Proofs/Chips/AluX0Chip/Formal.lean @@ -24,12 +24,8 @@ lemma byteRowSpec_ltu_29 {op : ZMod p} (h : op.val < 29) : rw [show (29 : ZMod p) = ((29 : ℕ) : ZMod p) from by norm_cast, ZMod.val_natCast_of_lt h29p] haveI : Fact (1 < p) := ⟨hp2⟩ refine ⟨ByteOpcode.LTU, by norm_cast, ?_⟩ - simp only [ByteOpcode.constrain_LTU] - refine ⟨⟨?_, ?_, ?_⟩, Or.inr trivial, ?_⟩ - · rw [ZMod.val_one]; norm_num - · omega - · rw [h29]; norm_num - · rw [h29]; exact ⟨fun _ => h, fun _ => trivial⟩ + simp only [ByteOpcode.constrain_LTU, ZMod.val_one, h29] + exact ⟨⟨by norm_num, by omega, by norm_num⟩, Or.inr trivial, fun _ => h, fun _ => trivial⟩ /-- Verifier-side `Assumptions` — trivial. `AluX0` discards its result, so there are no operand well-formedness obligations; `is_real` binary and the reader contract are derived in soundness from the @@ -44,8 +40,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe simp only [← sub_eq_add_neg] at h_oa1 h_oa2 simp only [isReal, clkLow, opcodeVal] -- the per-emitter channel-requirement tail: CPUState / reader (the gated byte pull owes no requirement). - exact ⟨⟨h_reader h_bin, h_bin, h_oa1, h_oa2⟩, - Or.inr h_bin, Or.inr h_bin⟩ + exact ⟨⟨h_reader h_bin, h_bin, h_oa1, h_oa2⟩, Or.inr h_bin, Or.inr h_bin⟩ /-- Honest prover-side row well-formedness: `is_real` binary, the two `op_a_0` forcing gates, the CPUState clock bounds + the immutable-ALU-reader contract, and the dynamic opcode in ALU range @@ -69,16 +64,13 @@ theorem completeness : simp only [isReal, clkLow, opcodeVal] at h_assumptions obtain ⟨h_bin, h_oa1, h_oa2, h_cpu, h_reader, h_op_lt⟩ := h_assumptions simp only [sub_eq_add_neg] at h_oa1 h_oa2 - refine ⟨⟨h_bin, h_cpu⟩, ?_, ⟨h_bin, h_reader⟩, ?_, ?_, ?_⟩ + refine ⟨⟨h_bin, h_cpu⟩, ?_, ⟨h_bin, h_reader⟩, ?_, h_oa1, h_oa2⟩ · -- the LTU `opcode < 29` byte pull (fires on real rows). intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg simp only [byteChannel] - exact byteRowSpec_ltu_29 (h_op_lt hr1) + exact byteRowSpec_ltu_29 (h_op_lt (neg_inj.mp hneg)) · -- `is_real` binary gate. rcases h_bin with h | h <;> rw [h] <;> simp - · exact h_oa1 - · exact h_oa2 /-- The `AluX0` chip row as a `GeneralFormalCircuit`: validates the ALU-into-`x0` program/register accesses and advances state (the result discarded); output is the extracted `AluX0Cols`. -/ diff --git a/SP1Clean/Proofs/Chips/BitwiseChip/Formal.lean b/SP1Clean/Proofs/Chips/BitwiseChip/Formal.lean index 76de84d7..0d583d55 100644 --- a/SP1Clean/Proofs/Chips/BitwiseChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/BitwiseChip/Formal.lean @@ -1,4 +1,5 @@ import SP1Clean.Native.Chips.BitwiseChip.Defs +import SP1Clean.Math.EvalVec /-! # `SP1Clean.BitwiseChip` — contract: `Assumptions` / soundness / completeness / `circuit` @@ -69,16 +70,11 @@ private lemma one_hot3 {x o a : ZMod p} (x = 1 → o = 0 ∧ a = 0) ∧ (o = 1 → x = 0 ∧ a = 0) ∧ (a = 1 → x = 0 ∧ o = 0) := by have hp : 2 ^ 17 < p := Fact.out haveI : Fact (1 < p) := ⟨by omega⟩ - have h2 : (2 : ZMod p) ≠ 0 := by - have h : ((2 : ℕ) : ZMod p) ≠ 0 := by - rw [Ne, CharP.cast_eq_zero_iff (ZMod p) p]; intro hd - have := Nat.le_of_dvd (by norm_num) hd; omega - simpa using h - have h6 : (6 : ZMod p) ≠ 0 := by - have h : ((6 : ℕ) : ZMod p) ≠ 0 := by - rw [Ne, CharP.cast_eq_zero_iff (ZMod p) p]; intro hd - have := Nat.le_of_dvd (by norm_num) hd; omega - simpa using h + have hne : ∀ k : ℕ, 0 < k → k < p → ((k : ℕ) : ZMod p) ≠ 0 := fun k hk hkp => by + rw [Ne, CharP.cast_eq_zero_iff (ZMod p) p]; intro hd + exact absurd (Nat.le_of_dvd hk hd) (by omega) + have h2 : (2 : ZMod p) ≠ 0 := by simpa using hne 2 (by norm_num) (by omega) + have h6 : (6 : ZMod p) ≠ 0 := by simpa using hne 6 (by norm_num) (by omega) rcases hx with rfl | rfl <;> rcases ho with rfl | rfl <;> rcases ha with rfl | rfl <;> refine ⟨fun h => ?_, fun h => ?_, fun h => ?_⟩ <;> first @@ -87,6 +83,16 @@ private lemma one_hot3 {x o a : ZMod p} | (exfalso; apply h2; linear_combination hsum) | (exfalso; apply h6; linear_combination hsum) +/-- The byte opcode `is_xor·2 + is_or·1 + is_and·0` lands in `{0,1,2}` (one-hot), so its `val < 3` — +the operand-range part of the composed `BitwiseU16Operation.circuit`'s `Assumptions`. -/ +private lemma val_lt_three {x : ZMod p} (h : x = 0 ∨ x = 1 ∨ x = 2) : x.val < 3 := by + haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ + have hp : 2 < p := by have := Fact.out (p := 2 ^ 17 < p); omega + rcases h with rfl | rfl | rfl + · simp + · rw [ZMod.val_one]; omega + · rw [show (2 : ZMod p) = ((2 : ℕ) : ZMod p) by norm_cast, ZMod.val_natCast_of_lt hp]; omega + set_option maxHeartbeats 2000000 in theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by circuit_proof_start [Spec] @@ -99,23 +105,14 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have h_and_bool := bool_of_mul_pred h_and_bin have hoh := one_hot3 h_xor_bool h_or_bool h_and_bool h_sum haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - -- the byte opcode `is_xor·2 + is_or·1` lands in `{0,1,2}` (one-hot), so it is `< 3` — the operand - -- range part of the composed `BitwiseU16Operation.circuit`'s `Assumptions`. - have hop3 : (env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0).val < 3 := by - have key : env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 0 - ∨ env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 1 - ∨ env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 2 := by + have hop3 : (env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0).val < 3 := + val_lt_three <| by rcases h_xor_bool with hx | hx · rcases h_or_bool with ho | ho · exact Or.inl (by rw [hx, ho]; ring) · exact Or.inr (Or.inl (by rw [hx, ho]; ring)) · obtain ⟨ho, _⟩ := hoh.1 hx exact Or.inr (Or.inr (by rw [hx, ho]; ring)) - rcases key with h | h | h <;> rw [h] - · rw [ZMod.val_zero]; omega - · rw [ZMod.val_one]; omega - · rw [show (2 : ZMod p) = ((2 : ℕ) : ZMod p) by norm_cast, - ZMod.val_natCast_of_lt (by have := Fact.out (p := 2 ^ 17 < p); omega : 2 < p)]; omega -- once the active flag forces the others to 0, the byte opcode reduces to a literal refine ⟨⟨h_bin, fun hr => ⟨fun hand => ?_, fun hor => ?_, fun hxor => ?_⟩⟩, Or.inr h_bin, Or.inr ⟨ha, hb, hop3, h_bin⟩, Or.inr h_bin⟩ @@ -132,22 +129,8 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe · obtain ⟨ho0, _ha0⟩ := hoh.1 hxor have hopc : env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 2 := by rw [hxor, ho0]; ring - have h2 : (2 : ZMod p).val < 3 := by - rw [show (2 : ZMod p) = ((2 : ℕ) : ZMod p) by norm_cast, - ZMod.val_natCast_of_lt (by have := Fact.out (p := 2 ^ 17 < p); omega : 2 < p)] - omega exact (BitwiseU16Operation.result_semantic _ hr - (h_bw ⟨ha, hb, by rw [hopc]; exact h2, h_bin⟩)).2.2 hopc - -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator — lets the witness -hint's `populate` operands (written `#v[env op_b_val[k]]` by the generator) be rewritten to the -`Vector.map eval …` form that `h_input` provides. -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] + (h_bw ⟨ha, hb, by rw [hopc]; exact val_lt_three (Or.inr (Or.inr rfl)), h_bin⟩)).2.2 hopc set_option maxHeartbeats 8000000 in theorem completeness : @@ -178,10 +161,8 @@ theorem completeness : have hpvc : Vector.map (Expression.eval env.toEnvironment) input_var_adapter_op_c_memory_prev_value = input_adapter_op_c_memory_prev_value := h_input.2.2.2.2.2.2.2.2.1.1 simp only [Inputs.op_b_val, Inputs.op_c_val, vec4_eval, hpvb, hpvc] at h_env_cols - have hop3 : (env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0).val < 3 := by - have key : env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 0 - ∨ env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 1 - ∨ env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0 = 2 := by + have hop3 : (env.get i₀ * 2 + env.get (i₀ + 1) * 1 + env.get (i₀ + 2) * 0).val < 3 := + val_lt_three <| by rw [hflag0, hflag1, hflag2] rcases hf0 with hx | hx · rcases hf1 with ho | ho @@ -189,11 +170,6 @@ theorem completeness : · exact Or.inr (Or.inl (by rw [hx, ho]; ring)) · obtain ⟨ho, -⟩ := hone0 hx exact Or.inr (Or.inr (by rw [hx, ho]; ring)) - rcases key with h | h | h <;> rw [h] - · rw [ZMod.val_zero]; omega - · rw [ZMod.val_one]; omega - · rw [show (2 : ZMod p) = ((2 : ℕ) : ZMod p) by norm_cast, - ZMod.val_natCast_of_lt (by have := Fact.out (p := 2 ^ 17 < p); omega : 2 < p)]; omega refine ⟨⟨hbin, h_cpu⟩, ⟨⟨ha, hb, hop3, hbin⟩, ?_⟩, diff --git a/SP1Clean/Proofs/Chips/BranchChip/Bridge.lean b/SP1Clean/Proofs/Chips/BranchChip/Bridge.lean index a42be546..42c3a382 100644 --- a/SP1Clean/Proofs/Chips/BranchChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/BranchChip/Bridge.lean @@ -85,9 +85,9 @@ theorem correct_branch_native have hsp_pc : sp.regs.get Register.PC (hsp_init _) = pc := by rw [key Register.PC (hs _) (hsp_init _) (by decide)]; exact hpc_get have hsp_rs1 : SailState.get_reg? sp rs1 = some rs1_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hsp_rs2 : SailState.get_reg? sp rs2 = some rs2_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs2 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hexec : EStateM.run (execute_BTYPE imm (.Regidx rs2) (.Regidx rs1) op) sp = .ok RETIRE_SUCCESS { s with regs := s.regs.insert Register.nextPC (Word.toBitVec64 next_pc_word) } := by rcases em (branchCond op rs1_val rs2_val) with hc | hc @@ -173,12 +173,12 @@ theorem branch_chip_reaches_sail obtain ⟨d_beq, d_bne, d_blt, d_bge, d_bltu, d_bgeu⟩ := h_decision h_real refine ⟨fun hf => key bop.BEQ ?_, fun hf => key bop.BNE ?_, fun hf => key bop.BLT ?_, fun hf => key bop.BGE ?_, fun hf => key bop.BLTU ?_, fun hf => key bop.BGEU ?_⟩ - · have := d_beq hf; rw [h_rs1v, h_rs2v] at this; exact this - · have := d_bne hf; rw [h_rs1v, h_rs2v] at this; exact this - · have := d_blt hf; rw [h_rs1v, h_rs2v] at this; exact this - · have := d_bge hf; rw [h_rs1v, h_rs2v] at this; exact this - · have := d_bltu hf; rw [h_rs1v, h_rs2v] at this; exact this - · have := d_bgeu hf; rw [h_rs1v, h_rs2v] at this; exact this + · have := d_beq hf; rwa [h_rs1v, h_rs2v] at this + · have := d_bne hf; rwa [h_rs1v, h_rs2v] at this + · have := d_blt hf; rwa [h_rs1v, h_rs2v] at this + · have := d_bge hf; rwa [h_rs1v, h_rs2v] at this + · have := d_bltu hf; rwa [h_rs1v, h_rs2v] at this + · have := d_bgeu hf; rwa [h_rs1v, h_rs2v] at this end SP1Clean.BranchSail diff --git a/SP1Clean/Proofs/Chips/BranchChip/Decision.lean b/SP1Clean/Proofs/Chips/BranchChip/Decision.lean index bd49296c..ad218da0 100644 --- a/SP1Clean/Proofs/Chips/BranchChip/Decision.lean +++ b/SP1Clean/Proofs/Chips/BranchChip/Decision.lean @@ -1,4 +1,5 @@ import SP1Clean.Math.Word +import SP1Clean.Math.Gate /-! # `SP1Clean.BranchChip` — the six-way decision dispatch (shared field lemmas) @@ -32,10 +33,7 @@ local instance : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ /-- `(0 : ZMod p) ≠ 1` (since `2^17 < p`). -/ lemma zero_ne_one' : (0 : ZMod p) ≠ 1 := by haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - intro h - have := congrArg ZMod.val h - rw [ZMod.val_zero, ZMod.val_one] at this - exact absurd this (by norm_num) + exact zero_ne_one /-- The `ℕ`-value of a binary field element (`0` or `1`). -/ lemma val_of_bool {b : ZMod p} (h : b = 0 ∨ b = 1) : b.val = 0 ∨ b.val = 1 := by @@ -65,42 +63,6 @@ lemma one_hot6 {b0 b1 b2 b3 b4 b5 : ZMod p} · right; rw [h, ZMod.val_one] omega -/-- A binary field element that equals `if P then 1 else 0` is `1` iff `P` holds. -/ -private lemma eq_one_iff_of_ite {x : ZMod p} {P : Prop} [Decidable P] - (h : x = if P then 1 else 0) : x = 1 ↔ P := by - by_cases hP : P - · simp [h, hP] - · simp only [h, if_neg hP] - exact ⟨fun he => absurd he zero_ne_one', fun hp => absurd hp hP⟩ - -/-- A field element equal to `1 - (if P then 1 else 0)` is `1` iff `P` fails. -/ -private lemma eq_one_iff_of_one_sub_ite {x : ZMod p} {P : Prop} [Decidable P] - (h : x = 1 - (if P then 1 else 0)) : x = 1 ↔ ¬ P := by - by_cases hP : P - · simp only [h, if_pos hP, sub_self] - exact ⟨fun he => absurd he zero_ne_one', fun hp => absurd hP hp⟩ - · simp [h, hP] - -omit [Fact (2 ^ 17 < p)] in -/-- A binary `x` with `x = 1 ↔ P` is `if P then 1 else 0` (completeness direction of `eq_one_iff_of_ite`). -/ -private lemma bool_eq_ite_of_iff {x : ZMod p} (hx : x = 0 ∨ x = 1) {P : Prop} [Decidable P] - (h : x = 1 ↔ P) : x = if P then 1 else 0 := by - by_cases hP : P - · rw [if_pos hP]; exact h.mpr hP - · rw [if_neg hP]; rcases hx with h0 | h1 - · exact h0 - · exact absurd (h.mp h1) hP - -omit [Fact (2 ^ 17 < p)] in -/-- A binary `x` with `x = 1 ↔ y = 0` (for binary `y`) is `1 - y`. -/ -private lemma bool_eq_one_sub {x y : ZMod p} (hx : x = 0 ∨ x = 1) (hy : y = 0 ∨ y = 1) - (h : x = 1 ↔ y = 0) : x = 1 - y := by - rcases hy with hy | hy - · rw [hy, sub_zero]; exact h.mpr hy - · rw [hy]; rcases hx with hx | hx - · rw [hx]; simp - · have := h.mp hx; rw [hy] at this; exact absurd this one_ne_zero - omit [Fact (2 ^ 17 < p)] in /-- A binary `x` with `x = 1 ↔ ¬ P` (where `bit = if P then 1 else 0`) is `1 - bit`. -/ private lemma bool_eq_one_sub_ite {x bit : ZMod p} (hx : x = 0 ∨ x = 1) {P : Prop} [Decidable P] @@ -121,22 +83,6 @@ private lemma bool_eq_of_iff_ne {x y : ZMod p} (hx : x = 0 ∨ x = 1) (hy : y = · exact absurd (h.mp hx) (by rw [hy]; simp) · rw [hy]; exact h.mpr (by rw [hy]; exact one_ne_zero) -/-- `BitVec.slt` as a `toInt` comparison. -/ -private lemma slt_true_iff (x y : BitVec 64) : x.slt y = true ↔ x.toInt < y.toInt := by - simp [BitVec.slt] - -/-- `BitVec.slt` false as the negated `toInt` comparison. -/ -private lemma slt_false_iff (x y : BitVec 64) : x.slt y = false ↔ ¬ x.toInt < y.toInt := by - simp [BitVec.slt] - -/-- `BitVec.ult` as a `toNat` comparison. -/ -private lemma ult_true_iff (x y : BitVec 64) : x.ult y = true ↔ x.toNat < y.toNat := by - simp [BitVec.ult] - -/-- `BitVec.ult` false as the negated `toNat` comparison. -/ -private lemma ult_false_iff (x y : BitVec 64) : x.ult y = false ↔ ¬ x.toNat < y.toNat := by - simp [BitVec.ult] - /-! ## The six-way decision dispatch `branchDecision` is the in-circuit decision expression; the two lemmas below are the dual readings of @@ -184,8 +130,7 @@ lemma branch_conditions_of_decision_eq {rs1 rs2 : Word (ZMod p)} have e5 := zero_of _ hb5 (by omega) have hsig0 : b2 + b3 = 0 := by rw [e2, e3]; simp rw [hf, e1, e2, e3, e4, e5] at hbrdec - have heq := h_eqf hsig0 - refine Iff.trans ?_ heq + refine Iff.trans ?_ (h_eqf hsig0) constructor · intro hib; linear_combination hbrdec - hib · intro hF; linear_combination hbrdec - hF @@ -196,8 +141,7 @@ lemma branch_conditions_of_decision_eq {rs1 rs2 : Word (ZMod p)} have e5 := zero_of _ hb5 (by omega) have hsig0 : b2 + b3 = 0 := by rw [e2, e3]; simp rw [hf, e0, e2, e3, e4, e5] at hbrdec - have heq := h_eqf hsig0 - refine Iff.trans ?_ (not_congr heq) + refine Iff.trans ?_ (not_congr (h_eqf hsig0)) constructor · intro hib hF; exact zero_ne_one' (by linear_combination -hbrdec + hib - hF) · intro hF; rcases hbr with h0 | h1 @@ -308,9 +252,8 @@ lemma branch_decision_eq_of_conditions {rs1 rs2 : Word (ZMod p)} simp only [eq_false hsigne] at h_bit have hiff := (hd4 hf4).trans (ult_true_iff _ _) rw [Word.toBitVec64_toNat hrs1U, Word.toBitVec64_toNat hrs2U] at hiff - have hbreq := (bool_eq_ite_of_iff hbr hiff).trans h_bit.symm rw [e0, e1, e2, e3, hf4, e5] - linear_combination hbreq + linear_combination (bool_eq_ite_of_iff hbr hiff).trans h_bit.symm · -- BGE (`b3 = 1`): `br = 1 - bit`, `is_signed = 1`. have v1 : b3.val = 1 := by rw [hf3]; simp [ZMod.val_one] have e0 := zero_of _ (show b0.val = 0 by omega) diff --git a/SP1Clean/Proofs/Chips/BranchChip/Formal.lean b/SP1Clean/Proofs/Chips/BranchChip/Formal.lean index aa9b5db8..f1eedabe 100644 --- a/SP1Clean/Proofs/Chips/BranchChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/BranchChip/Formal.lean @@ -120,12 +120,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have hpcU : Word.isU64 (#v[Expression.eval env input_var_state_pc[0], Expression.eval env input_var_state_pc[1], Expression.eval env input_var_state_pc[2], 0] : Word (ZMod p)) := hpceq ▸ h_pcU - have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := by - have h4lt : (4 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - refine Word.isU64_of_cases ?_ ?_ ?_ ?_ <;> - simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, - List.getElem_cons_succ, show (4 : ZMod p) = ((4 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt h4lt, ZMod.val_zero] <;> norm_num + have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := Word.isU64_four have h_onehot := one_hot6 hbeq hbne hblt hbge hbltu hbgeu h_sumbin have h_sig_bin : env.get (i₀ + 2) + env.get (i₀ + 3) = 0 ∨ env.get (i₀ + 2) + env.get (i₀ + 3) = 1 := by rcases hblt with hl | hl <;> rcases hbge with hg | hg @@ -227,14 +222,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe · right; rw [h1, hb0]; simp · left; rw [h1, hb1]; simp -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations folds to the `Vector.map` of the evaluator. -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - set_option maxHeartbeats 4000000 in /-- Completeness via honest `ProverHint` flag witnesses (`hintFlags`/`hintBranching`). The six-way decision constraint is discharged by mirroring soundness's case analysis in reverse via `Decision`. -/ @@ -276,12 +263,7 @@ theorem completeness : have ha1U : Word.isU64 (#v[Expression.eval env.toEnvironment input_var_state_pc[0], Expression.eval env.toEnvironment input_var_state_pc[1], Expression.eval env.toEnvironment input_var_state_pc[2], 0] : Word (ZMod p)) := ha1eq ▸ h_pcU - have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := by - have h4lt : (4 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - refine Word.isU64_of_cases ?_ ?_ ?_ ?_ <;> - simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, - List.getElem_cons_succ, show (4 : ZMod p) = ((4 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt h4lt, ZMod.val_zero] <;> norm_num + have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := Word.isU64_four have hcimm : Vector.map (Expression.eval env.toEnvironment) input_var_adapter_op_c_imm = input_adapter_op_c_imm := h_input.2.2.2.2.2.2.2 have hcimmeq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_c_imm[0], diff --git a/SP1Clean/Proofs/Chips/ByteChip.lean b/SP1Clean/Proofs/Chips/ByteChip.lean index 4a6af8db..bed94374 100644 --- a/SP1Clean/Proofs/Chips/ByteChip.lean +++ b/SP1Clean/Proofs/Chips/ByteChip.lean @@ -53,8 +53,7 @@ theorem byteRowSpec_of_provider {row : ByteRow (ZMod p)} {prov : LookupAccessLis (hk : keyOf b = byteRowKey row) : ByteRowSpec row := by obtain ⟨row', h_spec, h_key⟩ := h_prov b hb rw [hk] at h_key - rw [byteRow_eq_of_key h_key] - exact h_spec + rwa [byteRow_eq_of_key h_key] /-! ## The `Range` vs `Byte` receiver split (two preprocessed chips, one Byte bus) diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Completeness/Driver.lean b/SP1Clean/Proofs/Chips/DivRemChip/Completeness/Driver.lean index 9ca4adad..e2ff9ace 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Completeness/Driver.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Completeness/Driver.lean @@ -6,6 +6,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Populate.Euclid import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly import SP1Clean.Proofs.Chips.DivRemChip.Completeness.OwnComplete +import SP1Clean.Math.EvalVec import SP1Clean.Proofs.Chips.DivRemChip.Completeness.BytePulls import SP1Clean.Proofs.Chips.DivRemChip.Completeness.SubSpecs @@ -49,16 +50,6 @@ local instance : Fact (2 ^ 17 < p) := ⟨by have := Fact.out (p := 2 ^ 24 < p); attribute [local circuit_norm ↓ 100000] ProvableType.eval_fromElements -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator (folds the witness -closures' operands back to `Vector.map`; mirrors `ShiftRightChip.vec4_eval`). -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - - set_option maxHeartbeats 64000000 in /-- Completeness: `main`'s honest `Populate` witness closures (flags from the `"div_rem_flags"` hint) satisfy every constraint under `ProverAssumptions`. -/ diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Div.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Div.lean index 14b64057..602744ee 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Div.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Div.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `div` conjunct soundness (split for parallel compilation) @@ -28,7 +29,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `div` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `op_b_val`/`op_c_val` are now the raw register reads (adapter projections); `hbU`/`hcU` are their `isU64`, -- used by the shared `?_tail` `msb` range-checks. The `?_spec` arithmetic aliases the witnessed **operand** -- columns `b`/`c` (i₀+16/i₀+20) *locally* under these names and shadows `hbU`/`hcU` with the operand `isU64`. @@ -52,7 +54,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr -- alias the witnessed operand columns `b`/`c` under the old names (local to `?_spec`, so the shared -- `?_tail` is unaffected); the body's arithmetic uses these and the operand `isU64` shadows below. @@ -757,270 +758,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe interval_cases i <;> simp only [circuit_norm, Nat.add_zero] exacts [ha0, ha1, ha2, ha3] rw [hbeq, hceq] at hdiv_id; rw [haqc]; exact hdiv_id - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundDiv diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divu.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divu.lean index 9aaf8892..829de352 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divu.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divu.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `divu` conjunct soundness (split for parallel compilation) @@ -27,7 +28,11 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `divu` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + -- The `Operations.Requirements` tail is `Spec`-independent; it is supplied once by + -- `requirements_holds` via `soundness_of_specObligation`. `spec_proof_start` reproduces the stock + -- `circuit_proof_start` setup so the spec proof below is unchanged. + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +56,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr set input_op_b_val : Word (ZMod p) := Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + i }) with hbdef @@ -488,270 +492,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe interval_cases i <;> simp only [circuit_norm, Nat.add_zero] exacts [ha0, ha1, ha2, ha3] rw [hbeq, hceq] at hpair; rw [haqc]; exact hpair.1 - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundDivu diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divuw.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divuw.lean index e859d8d1..3da6225f 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divuw.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divuw.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `divuw` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `divuw` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr set input_op_b_val : Word (ZMod p) := Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + i }) with hbdef @@ -422,270 +423,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [toBitVec64_extractLsb hcU h_assumptions.2 hc0op hc1op, toBitVec64_extractLsb hbU h_assumptions.1 hb0op hb1op]] at hpair exact hpair.1 - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundDivuw diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divw.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divw.lean index 0bad08d3..f70f536e 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divw.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Divw.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `divw` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `divw` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr -- alias the witnessed operand columns `b`/`c` (i₀+16..19, i₀+20..23) under the old names (local to -- `?_spec`). `input_op_b_val`/`input_op_c_val` now mean the *operands*; the read is the adapter value. @@ -742,270 +743,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [toBitVec64_extractLsb hcU h_assumptions.2 hc0op hc1op, toBitVec64_extractLsb hbU h_assumptions.1 hb0op hb1op]] at hdivw_id exact hdivw_id - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundDivw diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Reader.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Reader.lean index f995b526..6791ae69 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Reader.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Reader.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — reader sub-`Spec` + `is_real`-binary (split for parallel compilation) @@ -40,7 +41,8 @@ set_option linter.unusedVariables false in set_option linter.unusedSimpArgs false in /-- Soundness of the reader sub-`Spec` + `is_real`-binary conjuncts. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `op_b_val`/`op_c_val` are now the raw register reads `input_adapter_op_b/c_memory_prev_value`; the `msb` -- range checks bound the reads directly (`hbU`/`hcU` are their `isU64`). The arithmetic operand `c` is the -- separate witnessed column at `i₀+20..23` — used only for the `is_c_0` split. @@ -64,7 +66,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · -- reader sub-`Spec` (`h_rtype` guarantee, its `circuit.Spec` projection unfolded) ∧ `is_real`-binary -- (the gate `E355`, per `Defs.lean`). obtain ⟨h_oir, -⟩ := h_input @@ -73,271 +74,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have hr := h_rtype h_bin simp only [Readers.RTypeReader.circuit] at hr exact ⟨hr, h_bin⟩ - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundReader diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Rem.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Rem.lean index 462eb2da..5f753d52 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Rem.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Rem.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `rem` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `rem` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr set input_op_b_val : Word (ZMod p) := Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + i }) with hbdef @@ -757,270 +758,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe List.getElem_toArray, List.getElem_cons_zero, List.getElem_cons_succ] exacts [hb0, hb1, hb2, hb3] rw [hbeq, hceq] at hrem_id; rw [haqc]; exact hrem_id - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundRem diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remu.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remu.lean index 303b379e..7ebb6124 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remu.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remu.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `remu` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `remu` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr set input_op_b_val : Word (ZMod p) := Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + i }) with hbdef @@ -442,270 +443,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe List.getElem_toArray, List.getElem_cons_zero, List.getElem_cons_succ] exacts [hb0, hb1, hb2, hb3] rw [hbeq, hceq] at hpair; rw [haqc]; exact hpair.2 - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundRemu diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remuw.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remuw.lean index bf28a961..6adf871a 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remuw.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remuw.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `remuw` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `remuw` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr set input_op_b_val : Word (ZMod p) := Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + i }) with hbdef @@ -422,270 +423,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [toBitVec64_extractLsb hcU h_assumptions.2 hc0op hc1op, toBitVec64_extractLsb hbU h_assumptions.1 hb0op hb1op]] at hpair exact hpair.2 - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundRemuw diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remw.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remw.lean index f13a5fc3..a209825b 100644 --- a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remw.lean +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Remw.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs import SP1Clean.Proofs.Chips.DivRemChip.Soundness import SP1Clean.Proofs.Chips.DivRemChip.Assembly +import SP1Clean.Proofs.Chips.DivRemChip.Soundness.Tail /-! # `DivRemChip` — `remw` conjunct soundness (split for parallel compilation) @@ -27,7 +28,8 @@ set_option maxHeartbeats 128000000 in set_option linter.unusedSimpArgs false in /-- Soundness of the `remw` conjunct. -/ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by - circuit_proof_start + apply soundness_of_specObligation + spec_proof_start -- `Assumptions` lives in `Defs` (an enclosing namespace here, not a current-namespace member as in -- `Formal`), so `circuit_proof_start`'s `dsimp only [Assumptions]` doesn't fire; project via -- `.1`/`.2` (whnf-unfolds the `def`) rather than `rcases`/`obtain`. @@ -51,7 +53,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, e357, e359, e367, eopa0⟩ := h_own - refine ⟨?_spec, ?_tail⟩ · intro hr -- alias the witnessed operand columns `b`/`c` (i₀+16..19, i₀+20..23) under the old names (local to -- `?_spec`). `input_op_b_val`/`input_op_c_val` now mean the *operands*; the read is the adapter value. @@ -743,270 +744,5 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [toBitVec64_extractLsb hcU h_assumptions.2 hc0op hc1op, toBitVec64_extractLsb hbU h_assumptions.1 hb0op hb1op]] at hremw_id exact hremw_id - · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input - set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef - -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord - -- gadget tests *this* for the divide-by-zero split. - set cop : Word (ZMod p) := - Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) - with hcop - have hbin : input_is_real = 0 ∨ input_is_real = 1 := by - have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h - simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 - have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 - have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 - have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 - have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 - have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) - have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := - group_binary4 bdw brw bduw bruw (by omega) - have hsel5 := group_binary4 bdu bru bd br (by omega) - have hsel6 := group_binary2 bdw brw (by omega) - have hsel7 := group_binary2 bduw bruw (by omega) - have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) - + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by - linear_combination -e367 - have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ - have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - have hquotmsb := hms6.1 - have hremmsb := hms5.1 - have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) - from by linear_combination e48] - exact isU16_of_byteRowSpec (hb_q0 hrneg') - · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) - from by linear_combination e49] - exact isU16_of_byteRowSpec (hb_q1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 - hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 - hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) - have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] - exact isU16_of_byteRowSpec (hb_r0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) - = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] - exact isU16_of_byteRowSpec (hb_r1 hrneg') - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 - hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) - · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 - hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) - have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), - isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] - have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), - isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] - have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] - = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] - have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] - have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] - = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] - -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. - have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t - have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by - rcases hbin with h | h - · left; rw [h] at h13t; linear_combination h13t - · rcases he2 with h2 | h2 - · right; rw [h, h2] at h13t; linear_combination h13t - · left; rw [h, h2] at h13t; linear_combination h13t - have hirnw_imp : env.get (B + 4) = 1 → - input_is_real = 1 ∧ - env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by - intro hir - rcases hbin with h | h - · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - · rcases he2 with h2 | h2 - · exact ⟨h, h2⟩ - · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) - -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). - have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) - (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by - intro hr - have hrneg' : -input_is_real = -1 := by rw [hr] - have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr - have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 - simp only [circuit_norm] at h299 h300 h301 h302 - rw [iszeroword_result_proj] at h299 h300 h301 h302 - simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, - ZMod.val_one]; norm_num - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp - · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [hsem] at h299 h300 h301 h302 - apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) - from by linear_combination h299] - exact isU16_of_byteRowSpec (hb_absc0 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) - from by linear_combination h300] - exact isU16_of_byteRowSpec (hb_absc1 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) - from by linear_combination h301] - exact isU16_of_byteRowSpec (hb_absc2 hrneg') - · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) - from by linear_combination h302] - exact isU16_of_byteRowSpec (hb_absc3 hrneg') - -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the - -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, - -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified - -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the - -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. - have hcU_op : Word.isU64 cop := by - obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU - have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] - = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] - have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] - = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] - simp only [circuit_norm] at e21 e23 e35 e47 - refine operand_isU64 - (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) - (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc0]; linear_combination -e21 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc1]; linear_combination -e23 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc2]; linear_combination e35 - · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] - rw [← hcc3]; linear_combination e47 - · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, - -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). - have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, - hirnw⟩).1 - simp only [circuit_norm] at hcmsb - have hE10 := group_binary4 bd br bdw brw (by omega) - have h19 := e19; simp only [circuit_norm] at h19 - rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> - simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> - first - | exact Or.inl (by linear_combination -h19) - | exact Or.inr (by linear_combination -h19) - refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, - ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ - case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] - case eqb => exact Or.inl rfl - case eqc => exact Or.inl rfl - case eqb2 => exact Or.inl rfl - case eqc2 => exact Or.inl rfl - case isc0 => exact Or.inl rfl - case cpu => exact Or.inr hbin - case rtype => exact Or.inr hbin - case mulLo => - exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, - Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ - case mulHi => - refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, - fun h => (zero_ne_one h).elim, Or.inl rfl, - group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ - rcases group_binary4 bd br bdu bru (by omega) with h | h - · exact Or.inl (by linear_combination h) - · exact Or.inr (by linear_combination h) - case addc => - refine Or.inr ⟨fun hace => ?_, ?_⟩ - · have h286 := e286; simp only [circuit_norm] at h286 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 - exact zero_ne_one (h286.symm.trans hace) - · exact h - exact ⟨hcU_op, habscU hr⟩ - · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case addr => - refine Or.inr ⟨fun hrae => ?_, ?_⟩ - · have h288 := e288; simp only [circuit_norm] at h288 - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 - exact zero_ne_one (h288.symm.trans hrae) - · exact h - exact ⟨hrcU hr, habsrU hr⟩ - · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h - case lt => - -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. - have h305 := e305; simp only [circuit_norm] at h305 - rw [iszeroword_result_proj] at h305 - simp only [Vector.getElem_mapRange, circuit_norm] at h305 - rw [h_oir] at h305 - refine Or.inr ⟨fun hrcm => ?_, ?_⟩ - · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. - have hr : input_is_real = 1 := by - rcases hbin with h | h - · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) - · exact h - exact ⟨habsrU hr, hmaxU hr⟩ - · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. - rcases hbin with h | h - · left; rw [h] at h305; linear_combination -h305 - · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h - by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 - ∧ cop[3] = 0 - · left - rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - · right - rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem - simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, - Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem - rw [h, hsem] at h305; linear_combination -h305 - case msb0 => - -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 - case msb1 => - refine Or.inr ⟨fun _ => ?_, hirnw⟩ - rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 - case msb2 => - refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ - obtain ⟨hr, -⟩ := hirnw_imp hirnwg - have hrneg' : -input_is_real = -1 := by rw [hr] - exact isU16_of_byteRowSpec (hb_r3 hrneg') - case msb3 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 - case msb4 => - refine Or.inr ⟨fun _ => ?_, he2⟩ - rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 - case msb5 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ - case msb6 => - exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ end SP1Clean.DivRemChip.SoundRemw diff --git a/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Tail.lean b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Tail.lean new file mode 100644 index 00000000..aa66b044 --- /dev/null +++ b/SP1Clean/Proofs/Chips/DivRemChip/Soundness/Tail.lean @@ -0,0 +1,367 @@ +import Lean +import SP1Clean.Proofs.Chips.DivRemChip.Defs +import SP1Clean.Proofs.Chips.DivRemChip.Soundness +import SP1Clean.Proofs.Chips.DivRemChip.Assembly + +/-! # `DivRemChip` — the shared `Operations.Requirements` tail (extracted once) + +The `?_tail` half of every `DivRemChip` soundness conjunct (the `Operations.Requirements` +obligation) is **byte-identical** across all nine `Soundness/.lean` files: it is +`Spec`-independent, depending only on `main`/`Assumptions`/`h_holds`. It was being re-elaborated +nine times at 128M heartbeats. Here it is proved **once**, as the `Requirements` half of a +trivial-spec (`fun _ _ _ => True`) `GeneralFormalCircuit.Soundness`, so `circuit_proof_start` sets +up the identical context. Each conjunct now discharges its `?_tail` by projecting +`(requirements_holds …).2`. -/ + +namespace SP1Clean.DivRemChip + +open Circuit +open Extracted (DivRemCols) +open SP1Clean.Channels (stateChannel byteChannel memoryChannel programChannel) + +variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 24 < p)] + +local instance : Fact (2 ^ 17 < p) := ⟨by have := Fact.out (p := 2 ^ 24 < p); omega⟩ + +set_option maxHeartbeats 128000000 in +set_option linter.unusedSimpArgs false in +set_option linter.unusedVariables false in +/-- The shared `Operations.Requirements` tail of the `DivRemChip` soundness conjuncts, proved once +(the `Spec`-independent second component). `circuit_proof_start` collapses the trivial `True` spec +via `true_and ∈ circuit_norm`, leaving exactly the `Requirements` goal the per-conjunct tail proves. -/ +lemma requirements_holds : + GeneralFormalCircuit.Soundness (ZMod p) main Assumptions + (fun _ _ _ => True : Inputs (ZMod p) → DivRemCols (ZMod p) → ProverData (ZMod p) → Prop) := by + circuit_proof_start + have hbU := h_assumptions.1 + have hcU := h_assumptions.2 + obtain ⟨h_mul_lo, h_mul_hi, + h_ctq0, h_ctq1, h_ctq2, h_ctq3, h_ctq4, h_ctq5, h_ctq6, h_ctq7, + h_eqb, h_eqc, h_eqb2, h_eqc2, h_isc0, h_addc, h_addr, h_lt, + h_msb0, h_msb1, h_msb2, h_msb3, h_msb4, h_msb5, h_msb6, h_cpu, h_rtype, h_own, + hb_e123, hb_e127, hb_e131, hb_e135, hb_e139, hb_e143, hb_e147, hb_e151, + hb_absc0, hb_absc1, hb_absc2, hb_absc3, hb_absr0, hb_absr1, hb_absr2, hb_absr3, + hb_q0, hb_q1, hb_q2, hb_q3, hb_r0, hb_r1, hb_r2, hb_r3, + hb_ctq0, hb_ctq1, hb_ctq2, hb_ctq3, hb_ctq4, hb_ctq5, hb_ctq6, hb_ctq7, + hb_e2r1, hb_e2q1⟩ := h_holds + simp only [ownAsserts] at h_own + obtain ⟨e13, e15, e17, e19, e20, e21, e22, e23, e29, e35, e41, e47, e48, e49, e51, e54, e57, e59, + e61, e64, e67, e69, e70, e71, e73, e76, e79, e81, e83, e86, e89, e91, e96, e99, e103, e105, e107, + e109, e111, e113, e115, e117, e119, e154, e157, e160, e163, e167, e171, e175, e179, e184, e189, + e194, e199, e204, e209, e214, e219, e225, e228, e230, e232, e234, e236, e238, e240, e242, e244, + e247, e250, e253, e256, e259, e262, e265, e268, e270, e272, e274, e276, e278, e280, e282, e284, + e286, e288, e299, e300, e301, e302, e305, e307, e309, e311, e313, e315, e317, e319, e321, e323, + e325, e327, e329, e331, e333, e335, e337, e339, e341, e343, e345, e347, e349, e351, e353, e355, + e357, e359, e367, eopa0⟩ := h_own + · obtain ⟨h_oir, -, -, -, -, -, ⟨h_ob, -, -⟩, -, ⟨h_oc, -, -⟩⟩ := h_input + set B := i₀ + 8 + 4 + 4 + 4 + 4 + 45 + 45 with hBdef + -- the arithmetic operand `c` (witnessed column at `i₀+20..23`), distinct from the read; the IsZeroWord + -- gadget tests *this* for the divide-by-zero split. + set cop : Word (ZMod p) := + Vector.map (Expression.eval env) (Vector.mapRange 4 fun i => var { index := i₀ + 8 + 4 + 4 + 4 + i }) + with hcop + have hbin : input_is_real = 0 ∨ input_is_real = 1 := by + have h := e355; simp only [circuit_norm] at h; rw [h_oir] at h; exact bool_of_mul_pred h + simp only [circuit_norm] at e325 e327 e329 e331 e333 e335 e337 e339 e367 + have bd := bool_of_mul_pred e325; have bdu := bool_of_mul_pred e327 + have br := bool_of_mul_pred e329; have bru := bool_of_mul_pred e331 + have bdw := bool_of_mul_pred e333; have brw := bool_of_mul_pred e335 + have bduw := bool_of_mul_pred e337; have bruw := bool_of_mul_pred e339 + have hvs := flags_val_sum bd bdu br bru bdw brw bduw bruw (by linear_combination -e367) + have he2 : env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 ∨ + env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 1 := + group_binary4 bdw brw bduw bruw (by omega) + have hsel5 := group_binary4 bdu bru bd br (by omega) + have hsel6 := group_binary2 bdw brw (by omega) + have hsel7 := group_binary2 bduw bruw (by omega) + have hsum567 : env.get (i₀ + 1) + env.get (i₀ + 3) + env.get i₀ + env.get (i₀ + 2) + + (env.get (i₀ + 4) + env.get (i₀ + 5)) + (env.get (i₀ + 6) + env.get (i₀ + 7)) = 1 := by + linear_combination -e367 + have hms6 := h_msb6 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ + have hms5 := h_msb5 ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ + have hquotmsb := hms6.1 + have hremmsb := hms5.1 + have hqcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) + (Vector.mapRange 4 fun i => var { index := i₀ + 8 + i }) : Word (ZMod p)) := by + intro hr + have hrneg' : -input_is_real = -1 := by rw [hr] + simp only [circuit_norm] at e48 e49 e51 e54 e59 e61 e64 e69 + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + · rw [show env.get (i₀ + 8) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4) + from by linear_combination e48] + exact isU16_of_byteRowSpec (hb_q0 hrneg') + · rw [show env.get (i₀ + 8 + 1) = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+4+1) + from by linear_combination e49] + exact isU16_of_byteRowSpec (hb_q1 hrneg') + · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e59 e54 e51 + hquotmsb (isU16_of_byteRowSpec (hb_q2 hrneg')) + · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e69 e64 e61 + hquotmsb (isU16_of_byteRowSpec (hb_q3 hrneg')) + have hrcU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) + (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4 + i }) : Word (ZMod p)) := by + intro hr + have hrneg' : -input_is_real = -1 := by rw [hr] + simp only [circuit_norm] at e70 e71 e73 e76 e81 e83 e86 e91 + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + · rw [show env.get (B + 7+8+8+11+11+11+4+4) + = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1) from by linear_combination e70] + exact isU16_of_byteRowSpec (hb_r0 hrneg') + · rw [show env.get (B + 7+8+8+11+11+11+4+4+1) + = env.get (B + 7+8+8+11+11+11+4+4+4+4+4+4+3+2+4+1+1+1) from by linear_combination e71] + exact isU16_of_byteRowSpec (hb_r1 hrneg') + · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e81 e76 e73 + hremmsb (isU16_of_byteRowSpec (hb_r2 hrneg')) + · exact comp_limb_isU16 hsel5 hsel6 hsel7 hsum567 e91 e86 e83 + hremmsb (isU16_of_byteRowSpec (hb_r3 hrneg')) + have habscU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) + (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11 + i }) : Word (ZMod p)) := by + intro hr + have hrneg' : -input_is_real = -1 := by rw [hr] + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + exacts [isU16_of_byteRowSpec (hb_absc0 hrneg'), isU16_of_byteRowSpec (hb_absc1 hrneg'), + isU16_of_byteRowSpec (hb_absc2 hrneg'), isU16_of_byteRowSpec (hb_absc3 hrneg')] + have habsrU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) + (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4 + i }) : Word (ZMod p)) := by + intro hr + have hrneg' : -input_is_real = -1 := by rw [hr] + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + exacts [isU16_of_byteRowSpec (hb_absr0 hrneg'), isU16_of_byteRowSpec (hb_absr1 hrneg'), + isU16_of_byteRowSpec (hb_absr2 hrneg'), isU16_of_byteRowSpec (hb_absr3 hrneg')] + have hbb1 : Expression.eval env input_var_adapter_op_b_memory_prev_value[1] + = input_adapter_op_b_memory_prev_value[1] := by rw [← h_ob]; simp [Vector.getElem_map] + have hcc1 : Expression.eval env input_var_adapter_op_c_memory_prev_value[1] + = input_adapter_op_c_memory_prev_value[1] := by rw [← h_oc]; simp [Vector.getElem_map] + have hbb3 : Expression.eval env input_var_adapter_op_b_memory_prev_value[3] + = input_adapter_op_b_memory_prev_value[3] := by rw [← h_ob]; simp [Vector.getElem_map] + have hcc3 : Expression.eval env input_var_adapter_op_c_memory_prev_value[3] + = input_adapter_op_c_memory_prev_value[3] := by rw [← h_oc]; simp [Vector.getElem_map] + -- `is_real_not_word` (E13 = `is_real * (1 - e2)`): binary, and `= 1 → is_real = 1 ∧ e2 = 0`. + have h13t := e13; simp only [circuit_norm] at h13t; rw [h_oir] at h13t + have hirnw : env.get (B + 4) = 0 ∨ env.get (B + 4) = 1 := by + rcases hbin with h | h + · left; rw [h] at h13t; linear_combination h13t + · rcases he2 with h2 | h2 + · right; rw [h, h2] at h13t; linear_combination h13t + · left; rw [h, h2] at h13t; linear_combination h13t + have hirnw_imp : env.get (B + 4) = 1 → + input_is_real = 1 ∧ + env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7) = 0 := by + intro hir + rcases hbin with h | h + · exfalso; rw [h, hir] at h13t; exact one_ne_zero (by linear_combination h13t) + · rcases he2 with h2 | h2 + · exact ⟨h, h2⟩ + · exfalso; rw [h, h2, hir] at h13t; exact one_ne_zero (by linear_combination h13t) + -- `max_abs_c_or_1` is `isU64`: `is_c_0 = 1 → #v[1,0,0,0]` (c = 0 branch), else `= abs_c` (E299-302). + have hmaxU : input_is_real = 1 → Word.isU64 (Vector.map (Expression.eval env) + (Vector.mapRange 4 fun i => var { index := B + 7+8+8+11+11+11+4+4+4 + i }) : Word (ZMod p)) := by + intro hr + have hrneg' : -input_is_real = -1 := by rw [hr] + have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr hr)) hr + have h299 := e299; have h300 := e300; have h301 := e301; have h302 := e302 + simp only [circuit_norm] at h299 h300 h301 h302 + rw [iszeroword_result_proj] at h299 h300 h301 h302 + simp only [Vector.getElem_mapRange, circuit_norm] at h299 h300 h301 h302 + by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 + ∧ cop[3] = 0 + · rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem + simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, + Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem + rw [hsem] at h299 h300 h301 h302 + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + · haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ + rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = 1 from by linear_combination h299, + ZMod.val_one]; norm_num + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = 0 from by linear_combination h300]; simp + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = 0 from by linear_combination h301]; simp + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = 0 from by linear_combination h302]; simp + · rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem + simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, + Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem + rw [hsem] at h299 h300 h301 h302 + apply Word.isU64_of_cases <;> simp only [circuit_norm, Nat.add_zero] + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4) = env.get (B + 7+8+8+11+11+11) + from by linear_combination h299] + exact isU16_of_byteRowSpec (hb_absc0 hrneg') + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 1) = env.get (B + 7+8+8+11+11+11 + 1) + from by linear_combination h300] + exact isU16_of_byteRowSpec (hb_absc1 hrneg') + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 2) = env.get (B + 7+8+8+11+11+11 + 2) + from by linear_combination h301] + exact isU16_of_byteRowSpec (hb_absc2 hrneg') + · rw [show env.get (B + 7+8+8+11+11+11+4+4+4 + 3) = env.get (B + 7+8+8+11+11+11 + 3) + from by linear_combination h302] + exact isU16_of_byteRowSpec (hb_absc3 hrneg') + -- `isU64` of the operand `c` (= `cop`), distinct from `isU64` of the read (`hcU`), feeding the + -- `MulOperation`/`AddOperation` Assumptions (`mulLo`/`mulHi`/`addc`). Low limbs equal the read (E21/E23, + -- bounded by `hcU`); high limbs are `read·(1-E2) + c_neg·E2·65535` (E35/E47) — closed by the verified + -- `operand_isU64` helper (`Extract.lean`) given `he2` + `c_neg` binary (E19: `c_neg = c_msb·E10`, the + -- `U16MSB` gadget binary, `E10` from the mutually-exclusive flags). TODO: discharge via `operand_isU64`. + have hcU_op : Word.isU64 cop := by + obtain ⟨hcr0, hcr1, hcr2, hcr3⟩ := Word.lt_cases_of_isU64 hcU + have hcc0 : Expression.eval env input_var_adapter_op_c_memory_prev_value[0] + = input_adapter_op_c_memory_prev_value[0] := by rw [← h_oc]; simp [Vector.getElem_map] + have hcc2 : Expression.eval env input_var_adapter_op_c_memory_prev_value[2] + = input_adapter_op_c_memory_prev_value[2] := by rw [← h_oc]; simp [Vector.getElem_map] + simp only [circuit_norm] at e21 e23 e35 e47 + refine operand_isU64 + (e2 := env.get (i₀ + 4) + env.get (i₀ + 5) + env.get (i₀ + 6) + env.get (i₀ + 7)) + (s := env.get (B + 6)) ?_ ?_ ?_ ?_ hcr0 hcr1 hcr2 hcr3 he2 ?_ + · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] + rw [← hcc0]; linear_combination -e21 + · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] + rw [← hcc1]; linear_combination -e23 + · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] + rw [← hcc2]; linear_combination e35 + · simp only [hcop, Vector.getElem_map, Vector.getElem_mapRange, circuit_norm] + rw [← hcc3]; linear_combination e47 + · -- `c_neg = c_msb.msb * E10` (E19) binary: `c_msb` binary from the `U16MSB` gadget (@ `irnw`, + -- via `hirnw` — unconditional), `E10 = is_div+is_rem+is_divw+is_remw ∈ {0,1}` (disjoint flags). + have hcmsb := (h_msb1 ⟨fun _ => by rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2, + hirnw⟩).1 + simp only [circuit_norm] at hcmsb + have hE10 := group_binary4 bd br bdw brw (by omega) + have h19 := e19; simp only [circuit_norm] at h19 + rcases hcmsb with hm | hm <;> rcases hE10 with hE | hE <;> + simp only [hm, hE, mul_zero, zero_mul, mul_one, one_mul, sub_zero] at h19 <;> + first + | exact Or.inl (by linear_combination -h19) + | exact Or.inr (by linear_combination -h19) + refine ⟨?mulLo, ?mulHi, ?eqb, ?eqc, ?eqb2, ?eqc2, ?isc0, ?addc, ?addr, ?lt, + ?msb0, ?msb1, ?msb2, ?msb3, ?msb4, ?msb5, ?msb6, ?cpu, ?rtype, ?own⟩ + case own => simp only [circuit_norm, assertZeros, forAllNoOffset_map_assert] + case eqb => exact Or.inl rfl + case eqc => exact Or.inl rfl + case eqb2 => exact Or.inl rfl + case eqc2 => exact Or.inl rfl + case isc0 => exact Or.inl rfl + case cpu => exact Or.inr hbin + case rtype => exact Or.inr hbin + case mulLo => + exact Or.inr ⟨fun hr => ⟨hqcU hr, hcU_op⟩, hbin, fun h => (zero_ne_one h).elim, hbin, + Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl, by rcases hbin with h | h <;> simp [h]⟩ + case mulHi => + refine Or.inr ⟨fun hr => ⟨hqcU (hirnw_imp hr).1, hcU_op⟩, hirnw, + fun h => (zero_ne_one h).elim, Or.inl rfl, + group_binary2 bd br (by omega), group_binary2 bdu bru (by omega), Or.inl rfl, Or.inl rfl, ?_⟩ + rcases group_binary4 bd br bdu bru (by omega) with h | h + · exact Or.inl (by linear_combination h) + · exact Or.inr (by linear_combination h) + case addc => + refine Or.inr ⟨fun hace => ?_, ?_⟩ + · have h286 := e286; simp only [circuit_norm] at h286 + have hr : input_is_real = 1 := by + rcases hbin with h | h + · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h286 + exact zero_ne_one (h286.symm.trans hace) + · exact h + exact ⟨hcU_op, habscU hr⟩ + · have h := e357; simp only [circuit_norm] at h; exact bool_of_mul_pred h + case addr => + refine Or.inr ⟨fun hrae => ?_, ?_⟩ + · have h288 := e288; simp only [circuit_norm] at h288 + have hr : input_is_real = 1 := by + rcases hbin with h | h + · exfalso; rw [h_oir, h, mul_zero, neg_zero, add_zero] at h288 + exact zero_ne_one (h288.symm.trans hrae) + · exact h + exact ⟨hrcU hr, habsrU hr⟩ + · have h := e359; simp only [circuit_norm] at h; exact bool_of_mul_pred h + case lt => + -- E305: `remainder_check_multiplicity = (1 - is_c_0.result) * is_real`. + have h305 := e305; simp only [circuit_norm] at h305 + rw [iszeroword_result_proj] at h305 + simp only [Vector.getElem_mapRange, circuit_norm] at h305 + rw [h_oir] at h305 + refine Or.inr ⟨fun hrcm => ?_, ?_⟩ + · -- rcm = 1 → is_real = 1, then `abs_remainder`/`max_abs_c_or_1` are `isU64`. + have hr : input_is_real = 1 := by + rcases hbin with h | h + · exfalso; rw [h] at h305; exact one_ne_zero (by linear_combination -h305 - hrcm) + · exact h + exact ⟨habsrU hr, hmaxU hr⟩ + · -- rcm binary: `(1 - is_c_0) * is_real` with `is_real`/`is_c_0` binary. + rcases hbin with h | h + · left; rw [h] at h305; linear_combination -h305 + · have hsem := IsZeroWordOperation.result_semantic (h_isc0 (Or.inr h)) h + by_cases hcz : cop[0] = 0 ∧ cop[1] = 0 ∧ cop[2] = 0 + ∧ cop[3] = 0 + · left + rw [if_pos hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem + simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, + Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem + rw [h, hsem] at h305; linear_combination -h305 + · right + rw [if_neg hcz] at hsem; dsimp only at hsem; rw [field_fromElements_one] at hsem + simp only [Vector.getElem_cast, Vector.getElem_take, Vector.getElem_drop, + Vector.getElem_mapRange, Nat.reduceAdd, circuit_norm] at hsem + rw [h, hsem] at h305; linear_combination -h305 + case msb0 => + -- the U16MSB gadget tests the raw read `bpv[3]`, bounded directly by `isU64 (read)`. + refine Or.inr ⟨fun _ => ?_, hirnw⟩ + rw [hbb3]; exact (Word.lt_cases_of_isU64 hbU).2.2.2 + case msb1 => + refine Or.inr ⟨fun _ => ?_, hirnw⟩ + rw [hcc3]; exact (Word.lt_cases_of_isU64 hcU).2.2.2 + case msb2 => + refine Or.inr ⟨fun hirnwg => ?_, hirnw⟩ + obtain ⟨hr, -⟩ := hirnw_imp hirnwg + have hrneg' : -input_is_real = -1 := by rw [hr] + exact isU16_of_byteRowSpec (hb_r3 hrneg') + case msb3 => + refine Or.inr ⟨fun _ => ?_, he2⟩ + rw [hbb1]; exact (Word.lt_cases_of_isU64 hbU).2.1 + case msb4 => + refine Or.inr ⟨fun _ => ?_, he2⟩ + rw [hcc1]; exact (Word.lt_cases_of_isU64 hcU).2.1 + case msb5 => + exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2r1 (by linear_combination -he2g)), he2⟩ + case msb6 => + exact Or.inr ⟨fun he2g => isU16_of_byteRowSpec (hb_e2q1 (by linear_combination -he2g)), he2⟩ + +/-- The `Spec`-only obligation: the soundness goal *minus* the shared `Operations.Requirements` tail. +Each conjunct proves this (its own `Spec`); the tail is supplied once by `requirements_holds`. -/ +def SpecObligation (Spec : Inputs (ZMod p) → DivRemCols (ZMod p) → ProverData (ZMod p) → Prop) : Prop := + ∀ (i₀ : ℕ) (env : Environment (ZMod p)) (input_var : Var Inputs (ZMod p)) (input : Inputs (ZMod p)), + eval env input_var = input → Assumptions input env.data → + ConstraintsHold.Soundness env (main input_var |>.operations i₀) → + Spec input (eval env (ElaboratedCircuit.output main input_var i₀)) env.data + +/-- Reassemble a full `GeneralFormalCircuit.Soundness` from the per-conjunct `SpecObligation` and the +shared `requirements_holds` tail — the binders are still **raw** here (no `circuit_proof_start`), so +`requirements_holds` applies by-term without the decomposed-context mismatch. -/ +lemma soundness_of_specObligation + {Spec : Inputs (ZMod p) → DivRemCols (ZMod p) → ProverData (ZMod p) → Prop} + (h : SpecObligation (p := p) Spec) : + GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spec := by + intro i₀ env input_var input h_input h_assumptions h_holds + exact ⟨h i₀ env input_var input h_input h_assumptions h_holds, + (requirements_holds i₀ env input_var input h_input h_assumptions h_holds).2⟩ + +open Lean Elab Tactic + +/-- `circuit_proof_start` for a `SpecObligation` goal: mirrors the stock tactic's setup verbatim +(unfold `SpecObligation`, intro the seven binders, then the identical `circuit_norm`/`dsimp`/ +`provable_struct_simp` normalization) so each conjunct's existing spec proof runs unchanged. Must be +an `elab` using `mkIdent` (not a `macro`) so the introduced binders and the use-site `Spec`/`main`/ +`elaborated` identifiers resolve in the conjunct's namespace, exactly as `circuit_proof_start` does. +Used after `apply soundness_of_specObligation`. -/ +elab "spec_proof_start" : tactic => do + evalTactic (← `(tactic| unfold SpecObligation)) + for name in [`i₀, `env, `input_var, `input, `h_input, `h_assumptions, `h_holds] do + evalTactic (← `(tactic| intro $(mkIdent name):ident)) + try evalTactic (← `(tactic| simp only [circuit_norm] at $(mkIdent `input_var):ident)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm] at $(mkIdent `input):ident)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm] at $(mkIdent `h_input):ident)) catch _ => pure () + evalTactic (← `(tactic| try dsimp only [$(mkIdent `Assumptions):ident] at *)) + evalTactic (← `(tactic| try dsimp only [$(mkIdent `Spec):ident] at *)) + evalTactic (← `(tactic| try dsimp only [$(mkIdent `elaborated):ident] at *)) + evalTactic (← `(tactic| try dsimp only [$(mkIdent `main):ident] at *)) + try evalTactic (← `(tactic| dsimp only [ElaboratedCircuit.withData, ElaboratedCircuit.output])) catch _ => pure () + try evalTactic (← `(tactic| provable_struct_simp)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm] at $(mkIdent `h_input):ident)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm] at $(mkIdent `h_assumptions):ident)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm, $(mkIdent `h_input):ident] at $(mkIdent `h_holds):ident)) catch _ => pure () + try evalTactic (← `(tactic| simp only [circuit_norm, $(mkIdent `h_input):ident])) catch _ => pure () + +end SP1Clean.DivRemChip diff --git a/SP1Clean/Proofs/Chips/JalChip/Formal.lean b/SP1Clean/Proofs/Chips/JalChip/Formal.lean index 4fc60bfb..d7903ab3 100644 --- a/SP1Clean/Proofs/Chips/JalChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/JalChip/Formal.lean @@ -57,25 +57,16 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have h_op_a_0 : input_adapter_op_a_0 = 0 ∨ input_adapter_op_a_0 = 1 := h_jt.2.1 -- eval-of-pc rewrites: circuit's `a` operand `#v[eval pc[i], 0]` equals the concrete `pcWord`. have hpc : Vector.map (Expression.eval env) input_var_state_pc = input_state_pc := h_input.2.1.2.2.2 - have ep0 : Expression.eval env input_var_state_pc[0] = input_state_pc[0] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep1 : Expression.eval env input_var_state_pc[1] = input_state_pc[1] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep2 : Expression.eval env input_var_state_pc[2] = input_state_pc[2] := by - rw [← hpc]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env input_var_state_pc[i] = input_state_pc[i] := + fun i hi => by rw [← hpc]; simp only [Vector.getElem_map] have ha1eq : (#v[Expression.eval env input_var_state_pc[0], Expression.eval env input_var_state_pc[1], Expression.eval env input_var_state_pc[2], 0] : Word (ZMod p)) - = #v[input_state_pc[0], input_state_pc[1], input_state_pc[2], 0] := by rw [ep0, ep1, ep2] + = #v[input_state_pc[0], input_state_pc[1], input_state_pc[2], 0] := by + rw [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)] have ha1U : Word.isU64 (#v[Expression.eval env input_var_state_pc[0], Expression.eval env input_var_state_pc[1], Expression.eval env input_var_state_pc[2], 0] : Word (ZMod p)) := ha1eq ▸ h_pcU - -- `#v[4,0,0,0]` is a 64-bit word. - have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := by - have h4lt : (4 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - refine Word.isU64_of_cases ?_ ?_ ?_ ?_ <;> - simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, - List.getElem_cons_succ, show (4 : ZMod p) = ((4 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt h4lt, ZMod.val_zero] <;> norm_num + have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := Word.isU64_four -- the link gate `is_real - op_a_0` is binary on every row (real: `op_a_0` binary; padding: `op_a_0 = 0`). -- `is_real - op_a_0` is binary: on real rows from `op_a_0 ∈ {0,1}`, on padding from `h_pad`. have h_gate2 : input_is_real + -input_adapter_op_a_0 = 0 ∨ input_is_real + -input_adapter_op_a_0 = 1 := by @@ -116,25 +107,18 @@ theorem completeness : h_input.2.1.2.2.2 have hob : Vector.map (Expression.eval env.toEnvironment) input_var_adapter_op_b_imm = input_adapter_op_b_imm := h_input.2.2.2.2.2.1 - have ep0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hpc]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), + Expression.eval env.toEnvironment input_var_state_pc[i] = input_state_pc[i] := + fun i hi => by rw [← hpc]; simp only [Vector.getElem_map] have ha1eq : (#v[Expression.eval env.toEnvironment input_var_state_pc[0], Expression.eval env.toEnvironment input_var_state_pc[1], Expression.eval env.toEnvironment input_var_state_pc[2], 0] : Word (ZMod p)) - = #v[input_state_pc[0], input_state_pc[1], input_state_pc[2], 0] := by rw [ep0, ep1, ep2] + = #v[input_state_pc[0], input_state_pc[1], input_state_pc[2], 0] := by + rw [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)] have ha1U : Word.isU64 (#v[Expression.eval env.toEnvironment input_var_state_pc[0], Expression.eval env.toEnvironment input_var_state_pc[1], Expression.eval env.toEnvironment input_var_state_pc[2], 0] : Word (ZMod p)) := ha1eq ▸ h_pcU - have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := by - have h4lt : (4 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - refine Word.isU64_of_cases ?_ ?_ ?_ ?_ <;> - simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, - List.getElem_cons_succ, show (4 : ZMod p) = ((4 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt h4lt, ZMod.val_zero] <;> norm_num + have h4U : Word.isU64 (#v[(4 : ZMod p), 0, 0, 0] : Word (ZMod p)) := Word.isU64_four have hb1eq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_b_imm[0], Expression.eval env.toEnvironment input_var_adapter_op_b_imm[1], Expression.eval env.toEnvironment input_var_adapter_op_b_imm[2], diff --git a/SP1Clean/Proofs/Chips/LoadByteChip/Bridge.lean b/SP1Clean/Proofs/Chips/LoadByteChip/Bridge.lean index d3a6906a..b46ed2d8 100644 --- a/SP1Clean/Proofs/Chips/LoadByteChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/LoadByteChip/Bridge.lean @@ -137,7 +137,7 @@ theorem correct_load_byte_native h_htif_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hhtif h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega diff --git a/SP1Clean/Proofs/Chips/LoadByteChip/Formal.lean b/SP1Clean/Proofs/Chips/LoadByteChip/Formal.lean index 3f30b3ae..fad48ca9 100644 --- a/SP1Clean/Proofs/Chips/LoadByteChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LoadByteChip/Formal.lean @@ -38,22 +38,13 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe simp only [circuit_norm, byteChannel] at hu8 hmsb_rcv -- eval→value bridges (extracted directly from `h_input`; `tauto` over this context is too slow). obtain ⟨_, _, _, _, ⟨hmap_pv, _, _, _, _, _⟩, hmap_ob, _, _, _, _⟩ := h_input - have eob0 : Expression.eval env input_var_offset_bit[0] = input_offset_bit[0] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env input_var_offset_bit[1] = input_offset_bit[1] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env input_var_offset_bit[2] = input_offset_bit[2] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - simp only [eob1, eob2, epv0, epv1, epv2, epv3, ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 - simp only [eob0, ← sub_eq_add_neg] at hmux + have eob : ∀ i (hi : i < 3), Expression.eval env input_var_offset_bit[i] = input_offset_bit[i] := + fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + simp only [eob 1 (by omega), eob 2 (by omega), epv 0 (by omega), epv 1 (by omega), + epv 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 + simp only [eob 0 (by omega), ← sub_eq_add_neg] at hmux -- the byte-mux equation in value form. have hmux_eq : input_selected_byte = input_offset_bit[0] * ((input_selected_limb - input_selected_limb_low_byte) * (256 : ZMod p)⁻¹) @@ -86,23 +77,23 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe exact ⟨this.2.1, this.2.2⟩ -- the `AddressOperation` Assumptions (eval form). have hob0' : Expression.eval env input_var_offset_bit[0] = 0 - ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env input_var_offset_bit[1] = 0 - ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env input_var_offset_bit[2] = 0 - ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env input_var_offset_bit[0]).val + 2 * (Expression.eval env input_var_offset_bit[1]).val + 4 * (Expression.eval env input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env input_var_offset_bit[0], Expression.eval env input_var_offset_bit[1], Expression.eval env input_var_offset_bit[2]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, hob0', hob1', hob2', h_ge, h_off'⟩ have h_addr_spec := h_addr h_addr_as - simp only [eob0, eob1, eob2] at h_addr_spec + simp only [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)] at h_addr_spec have h_it := h_itype h_bin refine ⟨⟨h_addr_spec, h_mem h_bin, h_it, fun h1 => ⟨(h_u8 h1).1, (h_u8 h1).2, h_byte_lt h1⟩, h_msb_fact, @@ -161,37 +152,23 @@ theorem completeness : haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ have h_msb_lt : input_msb.val < 256 := by rcases h_msb_bin with h | h <;> rw [h] <;> simp [ZMod.val_one] - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env.toEnvironment input_var_offset_bit[0] - = input_offset_bit[0] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env.toEnvironment input_var_offset_bit[1] - = input_offset_bit[1] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env.toEnvironment input_var_offset_bit[2] - = input_offset_bit[2] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_offset_bit[i] + = input_offset_bit[i] := fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] have hob0' : Expression.eval env.toEnvironment input_var_offset_bit[0] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env.toEnvironment input_var_offset_bit[1] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env.toEnvironment input_var_offset_bit[2] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env.toEnvironment input_var_offset_bit[0]).val + 2 * (Expression.eval env.toEnvironment input_var_offset_bit[1]).val + 4 * (Expression.eval env.toEnvironment input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env.toEnvironment input_var_offset_bit[0], Expression.eval env.toEnvironment input_var_offset_bit[1], @@ -199,7 +176,7 @@ theorem completeness : ⟨ha, hb, hfit, hob0', hob1', hob2', h_ge, h_off'⟩ refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ?_, ?_, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · -- U8Range-pair receive obligation (real row); value is raw (`toRaw` (gated post-#398)). @@ -212,11 +189,11 @@ theorem completeness : exact (byteRowSpec_msb _ _).mpr ⟨⟨h_msb_lt, hbyte_pa⟩, h_msb_bin, h_msb_iff⟩ · exact hbin · exact h_it - · simp only [eob1, eob2, epv0, ← sub_eq_add_neg]; exact hsel0 - · simp only [eob1, eob2, epv1, ← sub_eq_add_neg]; exact hsel1 - · simp only [eob1, eob2, epv2, ← sub_eq_add_neg]; exact hsel2 - · simp only [eob1, eob2, epv3, ← sub_eq_add_neg]; exact hsel3 - · simp only [eob0, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hmux_pa + · simp only [eob 1 (by omega), eob 2 (by omega), epv 0 (by omega), ← sub_eq_add_neg]; exact hsel0 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 1 (by omega), ← sub_eq_add_neg]; exact hsel1 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 2 (by omega), ← sub_eq_add_neg]; exact hsel2 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg]; exact hsel3 + · simp only [eob 0 (by omega), ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hmux_pa · exact h_op_a_0 · exact h_msbgate · rcases h_lb_bin with h | h <;> rw [h] <;> simp diff --git a/SP1Clean/Proofs/Chips/LoadDoubleChip/Bridge.lean b/SP1Clean/Proofs/Chips/LoadDoubleChip/Bridge.lean index 766d7428..60997ce9 100644 --- a/SP1Clean/Proofs/Chips/LoadDoubleChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/LoadDoubleChip/Bridge.lean @@ -163,34 +163,34 @@ theorem correct_load_double_native h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } -- Register read of `rs1` survives the `nextPC` write. have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] -- The eight memory bytes on `sp` (same memory as `s`), in the `reg_val.toNat + offset.toNat` form. have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? - = some (BitVec.ofNat 8 loaded[0].val) := by rw [hmem_eq, ← hadd]; exact hmem₀ + = some (BitVec.ofNat 8 loaded[0].val) := by rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? - = some (BitVec.ofNat 8 (loaded[0].val >>> 8)) := by rw [hmem_eq, ← hadd]; exact hmem₁ + = some (BitVec.ofNat 8 (loaded[0].val >>> 8)) := by rwa [hmem_eq, ← hadd] have hm₂ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 2]? - = some (BitVec.ofNat 8 loaded[1].val) := by rw [hmem_eq, ← hadd]; exact hmem₂ + = some (BitVec.ofNat 8 loaded[1].val) := by rwa [hmem_eq, ← hadd] have hm₃ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 3]? - = some (BitVec.ofNat 8 (loaded[1].val >>> 8)) := by rw [hmem_eq, ← hadd]; exact hmem₃ + = some (BitVec.ofNat 8 (loaded[1].val >>> 8)) := by rwa [hmem_eq, ← hadd] have hm₄ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 4]? - = some (BitVec.ofNat 8 loaded[2].val) := by rw [hmem_eq, ← hadd]; exact hmem₄ + = some (BitVec.ofNat 8 loaded[2].val) := by rwa [hmem_eq, ← hadd] have hm₅ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 5]? - = some (BitVec.ofNat 8 (loaded[2].val >>> 8)) := by rw [hmem_eq, ← hadd]; exact hmem₅ + = some (BitVec.ofNat 8 (loaded[2].val >>> 8)) := by rwa [hmem_eq, ← hadd] have hm₆ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 6]? - = some (BitVec.ofNat 8 loaded[3].val) := by rw [hmem_eq, ← hadd]; exact hmem₆ + = some (BitVec.ofNat 8 loaded[3].val) := by rwa [hmem_eq, ← hadd] have hm₇ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 7]? - = some (BitVec.ofNat 8 (loaded[3].val >>> 8)) := by rw [hmem_eq, ← hadd]; exact hmem₇ + = some (BitVec.ofNat 8 (loaded[3].val >>> 8)) := by rwa [hmem_eq, ← hadd] -- The alignment fact in Sail form, and the range-subset PMA fact. have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 8 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 8) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 8 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 8 (by omega) h_lo (by rwa [hadd]) -- The read result on `sp`. have hread := run_vmem_read_of_width_8' rs1_idx reg_val (BitVec.signExtend 64 imm) (BitVec.ofNat 8 loaded[0].val) (BitVec.ofNat 8 (loaded[0].val >>> 8)) @@ -281,11 +281,11 @@ theorem ld_chip_reaches_sail cols.memory_access.prev_value pc s hs hconfig h_pc h_rs1 ?_ ?_ ?_ ?_ hloaded hmem₀ hmem₁ hmem₂ hmem₃ hmem₄ hmem₅ hmem₆ hmem₇ · -- alignment: `sum % 8 = 0` from the chip's `sum % 2^48 % 8 = 0` - rw [hreg, hoff, ← Nat.mod_mod_of_dvd _ (by norm_num : (8 : ℕ) ∣ 2 ^ 48)]; exact h_align48 + rwa [hreg, hoff, ← Nat.mod_mod_of_dvd _ (by norm_num : (8 : ℕ) ∣ 2 ^ 48)] · -- fits in 64 bits (from the read-fits bound) rw [hreg, hoff]; omega · -- read fits in the 48-bit physical window - rw [hreg, hoff]; exact h_hi + rwa [hreg, hoff] · -- non-reserved: `2^16 ≤ (reg_val + offset).toNat`, no wrap since `sum < 2^48` rw [BitVec.toNat_add, hreg, hoff, Nat.mod_eq_of_lt (by omega)]; omega diff --git a/SP1Clean/Proofs/Chips/LoadDoubleChip/Formal.lean b/SP1Clean/Proofs/Chips/LoadDoubleChip/Formal.lean index bb613b7f..f73b93ee 100644 --- a/SP1Clean/Proofs/Chips/LoadDoubleChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LoadDoubleChip/Formal.lean @@ -37,24 +37,18 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe -- nested `memory_access.prev_value` (which `ITypeReader.Spec`'s zeroing gates reference). have hmap : Vector.map (Expression.eval env) input_var_memory_access_prev_value = input_memory_access_prev_value := h_input.2.2.2.1 - have ev0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap]; simp only [Vector.getElem_map] - have ev2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap]; simp only [Vector.getElem_map] - have ev3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap]; simp only [Vector.getElem_map] + have ev : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap]; simp only [Vector.getElem_map] have h_it := h_itype h_bin - rw [ev0, ev1, ev2, ev3] at h_it + rw [ev 0 (by omega), ev 1 (by omega), ev 2 (by omega), ev 3 (by omega)] at h_it -- the `AddressOperation` Assumptions: operand `isU64`s + fits, the offset bits boolean (literal `0`), -- and the address-validity (non-reserved + 8-aligned, so the inverse gate / offset range check hold). have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, 0, 0⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, Or.inl rfl, Or.inl rfl, h_ge, by simp only [ZMod.val_zero]; omega⟩ - refine ⟨⟨h_addr h_addr_as, h_mem h_bin, h_it, h_op_a_0, h_bin⟩, ?_⟩ -- the per-subcircuit channel-requirement tail (`channels = [] ∨ .Assumptions`). - exact ⟨Or.inr h_bin, Or.inr h_addr_as, Or.inr h_bin, Or.inr h_bin⟩ + exact ⟨⟨h_addr h_addr_as, h_mem h_bin, h_it, h_op_a_0, h_bin⟩, + Or.inr h_bin, Or.inr h_addr_as, Or.inr h_bin, Or.inr h_bin⟩ /-- Prover-side row well-formedness: operand `isU64`s + address-fits bound plus the `is_real` binary selector. -/ def ProverAssumptions (input : Inputs (ZMod p)) (_ : ProverData (ZMod p)) (_ : ProverHint (ZMod p)) : Prop := @@ -86,30 +80,16 @@ theorem completeness : = input_memory_access_prev_value := h_input.2.2.2.1 have hmap_pc : Vector.map (Expression.eval env.toEnvironment) input_var_state_pc = input_state_pc := h_input.2.1.2.2.2 - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] - = input_state_pc[0] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] - = input_state_pc[1] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] - = input_state_pc[2] := by rw [← hmap_pc]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, 0, 0⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, Or.inl rfl, Or.inl rfl, h_ge, by simp only [ZMod.val_zero]; omega⟩ - refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, h_op_a_0, ?_⟩ - · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu - · exact hbin - · exact h_mem - · exact hbin - · simp only [epv0, epv1, epv2, epv3]; exact h_it + refine ⟨⟨hbin, ?_⟩, h_addr_as, ⟨hbin, h_mem⟩, ⟨hbin, ?_⟩, h_op_a_0, ?_⟩ + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu + · simp only [epv 0 (by omega), epv 1 (by omega), epv 2 (by omega), epv 3 (by omega)]; exact h_it · rcases hbin with h | h <;> rw [h] <;> simp /-- The `LoadDouble` chip row as a `GeneralFormalCircuit`; output is the extracted `LoadDoubleColumns`. -/ diff --git a/SP1Clean/Proofs/Chips/LoadHalfChip/Bridge.lean b/SP1Clean/Proofs/Chips/LoadHalfChip/Bridge.lean index 25a9b2b4..7ddace85 100644 --- a/SP1Clean/Proofs/Chips/LoadHalfChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/LoadHalfChip/Bridge.lean @@ -31,8 +31,7 @@ private lemma toNat_concat_half_bytes [NeZero p] (h : ZMod p) (hh : h.val < 6553 have h_decomp : h.val % 256 + (h.val >>> 8) * 256 = h.val := by rw [Nat.shiftRight_eq_div_pow]; omega simp only [BitVec.toNat_append, BitVec.toNat_ofNat, show (2 ^ 8 : ℕ) = 256 from rfl] - rw [Nat.mod_eq_of_lt h_hi] - rw [show h.val >>> 8 <<< 8 ||| h.val % 256 = h.val by + rw [Nat.mod_eq_of_lt h_hi, show h.val >>> 8 <<< 8 ||| h.val % 256 = h.val by rw [← Nat.shiftLeft_add_eq_or_of_lt (i := 8) (by omega), Nat.shiftLeft_eq]; omega] omit [Fact p.Prime] [Fact (2 ^ 17 < p)] in @@ -88,7 +87,7 @@ private lemma signExtend64_ofNat16_concat_of_ge_32768 [NeZero p] simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, List.getElem_cons_succ] have hp : 131072 < p := by have := Fact.out (p := 2 ^ 17 < p); omega have h65535 : (65535 : ZMod p).val = 65535 := by - rw [show (65535 : ZMod p) = ((65535 : ℕ) : ZMod p) from by norm_cast, ZMod.val_natCast, + rw [show (65535 : ZMod p) = ((65535 : ℕ) : ZMod p) by norm_cast, ZMod.val_natCast, Nat.mod_eq_of_lt (by omega)] rw [h65535] omega @@ -128,7 +127,7 @@ theorem correct_load_half_native (spec_lh imm rs1_idx rd_idx is_unsigned).run s = (sp1_lh rd_idx pc val64).run s := by have hse : (sign_extend imm : BitVec 64) = BitVec.signExtend 64 imm := by simp [sign_extend] have hpc_get : s.regs.get Register.PC (hs _) = pc := by - rw [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc; exact h_pc + rwa [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc set sp : SailState := { s with regs := s.regs.insert Register.nextPC (pc + 4#64) } with hsp have hsp_init : SailState.isInitialized sp := SailState.isInitialized_insert s hs Register.nextPC (pc + 4#64) @@ -141,26 +140,26 @@ theorem correct_load_half_native show (s.regs.insert Register.nextPC (pc + 4#64)).get reg _ = _ rw [Std.ExtDHashMap.get_insert]; simp [Ne.symm hne] exact - { h_cur_privilege := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hcp - h_mprv_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hmprv - h_mseccfg_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hmsec - h_htif_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hhtif - h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } + { h_cur_privilege := by rwa [key _ (hs _) (hsp_init _) (by decide)] + h_mprv_disabled := by rwa [key _ (hs _) (hsp_init _) (by decide)] + h_mseccfg_disabled := by rwa [key _ (hs _) (hsp_init _) (by decide)] + h_htif_disabled := by rwa [key _ (hs _) (hsp_init _) (by decide)] + h_pma_regions := by rwa [key _ (hs _) (hsp_init _) (by decide)] } have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? - = some data₀ := by rw [hmem_eq, ← hadd]; exact hmem₀ + = some data₀ := by rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? - = some data₁ := by rw [hmem_eq, ← hadd]; exact hmem₁ + = some data₁ := by rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 2 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 2) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 2 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 2 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_2' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ data₁ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ hm₁ simp only at hread diff --git a/SP1Clean/Proofs/Chips/LoadHalfChip/Formal.lean b/SP1Clean/Proofs/Chips/LoadHalfChip/Formal.lean index 1fe4f06a..c22fac9a 100644 --- a/SP1Clean/Proofs/Chips/LoadHalfChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LoadHalfChip/Formal.lean @@ -43,35 +43,28 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe h_input.2.2.2.2.2.1 have hmap_pv : Vector.map (Expression.eval env) input_var_memory_access_prev_value = input_memory_access_prev_value := h_input.2.2.2.2.1.1 - have eob0 : Expression.eval env input_var_offset_bit[0] = input_offset_bit[0] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env input_var_offset_bit[1] = input_offset_bit[1] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - simp only [eob0, eob1, epv0, epv1, epv2, epv3, ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 + have eob : ∀ i (hi : i < 2), Expression.eval env input_var_offset_bit[i] = input_offset_bit[i] := + fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + simp only [eob 0 (by omega), eob 1 (by omega), epv 0 (by omega), epv 1 (by omega), + epv 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 have h_it := h_itype h_bin -- the `AddressOperation` Assumptions (eval form, matching the subcircuit input). have hob0' : Expression.eval env input_var_offset_bit[0] = 0 - ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env input_var_offset_bit[1] = 0 - ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have h_off' : (0 : ZMod p).val + 2 * (Expression.eval env input_var_offset_bit[0]).val + 4 * (Expression.eval env input_var_offset_bit[1]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1]; simp only [ZMod.val_zero]; omega + rw [eob 0 (by omega), eob 1 (by omega)]; simp only [ZMod.val_zero]; omega have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, Expression.eval env input_var_offset_bit[0], Expression.eval env input_var_offset_bit[1]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, hob0', hob1', h_ge, h_off'⟩ have h_addr_spec := h_addr h_addr_as - simp only [eob0, eob1] at h_addr_spec + simp only [eob 0 (by omega), eob 1 (by omega)] at h_addr_spec -- `selected_half < 2^16` on a real row: it equals one of the four 16-bit limbs by the offset corner. have h_sel_lt : input_selected_half.val < 2 ^ 16 := by rcases hob0 with hb0 | hb0 @@ -158,32 +151,20 @@ theorem completeness : = input_offset_bit := h_input.2.2.2.2.2.1 have hmap_pv : Vector.map (Expression.eval env.toEnvironment) input_var_memory_access_prev_value = input_memory_access_prev_value := h_input.2.2.2.2.1.1 - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] - = input_state_pc[0] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] - = input_state_pc[1] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] - = input_state_pc[2] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env.toEnvironment input_var_offset_bit[0] - = input_offset_bit[0] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env.toEnvironment input_var_offset_bit[1] - = input_offset_bit[1] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 2), Expression.eval env.toEnvironment input_var_offset_bit[i] + = input_offset_bit[i] := fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] have hob0' : Expression.eval env.toEnvironment input_var_offset_bit[0] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env.toEnvironment input_var_offset_bit[1] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have h_off' : (0 : ZMod p).val + 2 * (Expression.eval env.toEnvironment input_var_offset_bit[0]).val + 4 * (Expression.eval env.toEnvironment input_var_offset_bit[1]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1]; simp only [ZMod.val_zero]; omega + rw [eob 0 (by omega), eob 1 (by omega)]; simp only [ZMod.val_zero]; omega have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, Expression.eval env.toEnvironment input_var_offset_bit[0], Expression.eval env.toEnvironment input_var_offset_bit[1]⟩ : AddressOperation.Inputs (ZMod p)) := @@ -211,16 +192,16 @@ theorem completeness : refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨⟨fun _ => h_sel_lt, h_lh_bin⟩, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, h_op_a_0, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · exact h_msb_spec · exact hbin · exact h_it - · simp only [eob0, eob1, epv0, ← sub_eq_add_neg]; exact hsel0 - · simp only [eob0, eob1, epv1, ← sub_eq_add_neg]; exact hsel1 - · simp only [eob0, eob1, epv2, ← sub_eq_add_neg]; exact hsel2 - · simp only [eob0, eob1, epv3, ← sub_eq_add_neg]; exact hsel3 + · simp only [eob 0 (by omega), eob 1 (by omega), epv 0 (by omega), ← sub_eq_add_neg]; exact hsel0 + · simp only [eob 0 (by omega), eob 1 (by omega), epv 1 (by omega), ← sub_eq_add_neg]; exact hsel1 + · simp only [eob 0 (by omega), eob 1 (by omega), epv 2 (by omega), ← sub_eq_add_neg]; exact hsel2 + · simp only [eob 0 (by omega), eob 1 (by omega), epv 3 (by omega), ← sub_eq_add_neg]; exact hsel3 · exact h_msbgate · rcases h_lh_bin with h | h <;> rw [h] <;> simp · rcases h_lhu_bin with h | h <;> rw [h] <;> simp diff --git a/SP1Clean/Proofs/Chips/LoadWordChip/Bridge.lean b/SP1Clean/Proofs/Chips/LoadWordChip/Bridge.lean index 8a987910..f9424a4f 100644 --- a/SP1Clean/Proofs/Chips/LoadWordChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/LoadWordChip/Bridge.lean @@ -112,7 +112,7 @@ private lemma signExtend64_ofNat32_concat_of_ge_32768 [NeZero p] simp only [Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, List.getElem_cons_succ] have hp : 131072 < p := by have := Fact.out (p := 2 ^ 17 < p); omega have h65535 : (65535 : ZMod p).val = 65535 := by - rw [show (65535 : ZMod p) = ((65535 : ℕ) : ZMod p) from by norm_cast, ZMod.val_natCast, + rw [show (65535 : ZMod p) = ((65535 : ℕ) : ZMod p) by norm_cast, ZMod.val_natCast, Nat.mod_eq_of_lt (by omega)] rw [h65535, Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega)] change x.val + y.val * 2 ^ 16 + (2 ^ 64 - 2 ^ (8 + 8 + 8 + 8)) = @@ -175,24 +175,24 @@ theorem correct_load_word_native h_htif_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hhtif h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? - = some data₀ := by rw [hmem_eq, ← hadd]; exact hmem₀ + = some data₀ := by rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? - = some data₁ := by rw [hmem_eq, ← hadd]; exact hmem₁ + = some data₁ := by rwa [hmem_eq, ← hadd] have hm₂ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 2]? - = some data₂ := by rw [hmem_eq, ← hadd]; exact hmem₂ + = some data₂ := by rwa [hmem_eq, ← hadd] have hm₃ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 3]? - = some data₃ := by rw [hmem_eq, ← hadd]; exact hmem₃ + = some data₃ := by rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 4 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 4) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 4 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 4 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_4' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ data₁ data₂ data₃ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ hm₁ hm₂ hm₃ diff --git a/SP1Clean/Proofs/Chips/LoadWordChip/Formal.lean b/SP1Clean/Proofs/Chips/LoadWordChip/Formal.lean index d9a4a277..ce39b0a1 100644 --- a/SP1Clean/Proofs/Chips/LoadWordChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LoadWordChip/Formal.lean @@ -42,23 +42,16 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe h_input.2.2.2.2.2.2.1 have hmap_pv : Vector.map (Expression.eval env) input_var_memory_access_prev_value = input_memory_access_prev_value := h_input.2.2.2.2.1.1 - have esw0 : Expression.eval env input_var_selected_word[0] = input_selected_word[0] := by - rw [← hmap_sw]; simp only [Vector.getElem_map] - have esw1 : Expression.eval env input_var_selected_word[1] = input_selected_word[1] := by - rw [← hmap_sw]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - simp only [esw0, esw1, epv0, epv1, epv2, epv3, ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 + have esw : ∀ i (hi : i < 2), Expression.eval env input_var_selected_word[i] + = input_selected_word[i] := fun i hi => by rw [← hmap_sw]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + simp only [esw 0 (by omega), esw 1 (by omega), epv 0 (by omega), epv 1 (by omega), + epv 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 simp only [← sub_eq_add_neg] at h_msbgate - rw [esw1] at h_msb + rw [esw 1 (by omega)] at h_msb have h_it := h_itype h_bin - simp only [esw0, esw1] at h_it + simp only [esw 0 (by omega), esw 1 (by omega)] at h_it -- the `AddressOperation` Assumptions: operand `isU64`s + fits, offset bits boolean (0, 0, the witnessed -- `offset_bit`), non-reserved, and the offset decomposition `4·offset_bit = addr % 8`. have h_off' : (0 : ZMod p).val + 2 * (0 : ZMod p).val + 4 * input_offset_bit.val @@ -90,7 +83,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [heq]; exact hpv3 -- eval-form variant of the 16-bit bound, for the `U16MSBOperation` channel-requirement tail. have h_sel1_lt_eval : (Expression.eval env input_var_selected_word[1]).val < 2 ^ 16 := by - rw [esw1]; exact h_sel1_lt + rw [esw 1 (by omega)]; exact h_sel1_lt have h_msb_as : U16MSBOperation.circuit.Assumptions (⟨input_selected_word[1], ⟨input_msb⟩, input_is_lw⟩ : U16MSBOperation.Inputs (ZMod p)) := ⟨fun _ => h_sel1_lt, h_lw_bin⟩ @@ -146,24 +139,12 @@ theorem completeness : = input_selected_word := h_input.2.2.2.2.2.2.1 have hmap_pv : Vector.map (Expression.eval env.toEnvironment) input_var_memory_access_prev_value = input_memory_access_prev_value := h_input.2.2.2.2.1.1 - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] - = input_state_pc[0] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] - = input_state_pc[1] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] - = input_state_pc[2] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have esw0 : Expression.eval env.toEnvironment input_var_selected_word[0] - = input_selected_word[0] := by rw [← hmap_sw]; simp only [Vector.getElem_map] - have esw1 : Expression.eval env.toEnvironment input_var_selected_word[1] - = input_selected_word[1] := by rw [← hmap_sw]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have esw : ∀ i (hi : i < 2), Expression.eval env.toEnvironment input_var_selected_word[i] + = input_selected_word[i] := fun i hi => by rw [← hmap_sw]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] -- the `AddressOperation` subcircuit `Assumptions`. have h_off' : (0 : ZMod p).val + 2 * (0 : ZMod p).val + 4 * input_offset_bit.val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by @@ -186,20 +167,20 @@ theorem completeness : · have hne : input_offset_bit ≠ 0 := by rw [h1]; exact one_ne_zero rw [sub_eq_zero.mp ((mul_eq_zero.mp hsel3).resolve_right hne)]; exact hpv3 have h_sel1_lt_eval : (Expression.eval env.toEnvironment input_var_selected_word[1]).val < 2 ^ 16 := by - rw [esw1]; exact h_sel1_lt + rw [esw 1 (by omega)]; exact h_sel1_lt refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨⟨fun _ => h_sel1_lt_eval, h_lw_bin⟩, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, h_op_a_0, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem - · simp only [esw1]; exact h_msb_spec + · simp only [esw 1 (by omega)]; exact h_msb_spec · exact hbin - · simp only [esw0, esw1]; exact h_it - · simp only [esw0, epv0, ← sub_eq_add_neg]; exact hsel0 - · simp only [esw1, epv1, ← sub_eq_add_neg]; exact hsel1 - · simp only [esw0, epv2, ← sub_eq_add_neg]; exact hsel2 - · simp only [esw1, epv3, ← sub_eq_add_neg]; exact hsel3 + · simp only [esw 0 (by omega), esw 1 (by omega)]; exact h_it + · simp only [esw 0 (by omega), epv 0 (by omega), ← sub_eq_add_neg]; exact hsel0 + · simp only [esw 1 (by omega), epv 1 (by omega), ← sub_eq_add_neg]; exact hsel1 + · simp only [esw 0 (by omega), epv 2 (by omega), ← sub_eq_add_neg]; exact hsel2 + · simp only [esw 1 (by omega), epv 3 (by omega), ← sub_eq_add_neg]; exact hsel3 · simp only [← sub_eq_add_neg]; exact h_msbgate · rcases h_lw_bin with h | h <;> rw [h] <;> simp · rcases h_lwu_bin with h | h <;> rw [h] <;> simp diff --git a/SP1Clean/Proofs/Chips/LoadX0Chip/Bridge.lean b/SP1Clean/Proofs/Chips/LoadX0Chip/Bridge.lean index 7fe02796..bf44d1a5 100644 --- a/SP1Clean/Proofs/Chips/LoadX0Chip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/LoadX0Chip/Bridge.lean @@ -70,7 +70,7 @@ private lemma persist_nextPC (rs1_idx : BitVec 5) (reg_val pc : BitVec 64) h_mseccfg_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hmsec h_htif_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hhtif h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } - · rw [SailState.get_reg?_insert_nextPC]; exact h_rs1 + · rwa [SailState.get_reg?_insert_nextPC] /-! ## The four width-core lemmas @@ -100,13 +100,13 @@ theorem loadX0_w1 (is_unsigned : Bool) = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? = some data₀ := by - rw [hmem_eq, ← hadd]; exact hmem₀ + rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 1 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 1) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 1 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 1 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_1' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ simp only at hread @@ -139,15 +139,15 @@ theorem loadX0_w2 (is_unsigned : Bool) = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? = some data₀ := by - rw [hmem_eq, ← hadd]; exact hmem₀ + rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? = some data₁ := by - rw [hmem_eq, ← hadd]; exact hmem₁ + rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 2 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 2) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 2 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 2 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_2' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ data₁ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ hm₁ simp only at hread @@ -183,19 +183,19 @@ theorem loadX0_w4 (is_unsigned : Bool) = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? = some data₀ := by - rw [hmem_eq, ← hadd]; exact hmem₀ + rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? = some data₁ := by - rw [hmem_eq, ← hadd]; exact hmem₁ + rwa [hmem_eq, ← hadd] have hm₂ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 2]? = some data₂ := by - rw [hmem_eq, ← hadd]; exact hmem₂ + rwa [hmem_eq, ← hadd] have hm₃ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 3]? = some data₃ := by - rw [hmem_eq, ← hadd]; exact hmem₃ + rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 4 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 4) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 4 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 4 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_4' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ data₁ data₂ data₃ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ hm₁ hm₂ hm₃ @@ -236,27 +236,27 @@ theorem loadX0_w8 (is_unsigned : Bool) = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have hm₀ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat]? = some data₀ := by - rw [hmem_eq, ← hadd]; exact hmem₀ + rwa [hmem_eq, ← hadd] have hm₁ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 1]? = some data₁ := by - rw [hmem_eq, ← hadd]; exact hmem₁ + rwa [hmem_eq, ← hadd] have hm₂ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 2]? = some data₂ := by - rw [hmem_eq, ← hadd]; exact hmem₂ + rwa [hmem_eq, ← hadd] have hm₃ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 3]? = some data₃ := by - rw [hmem_eq, ← hadd]; exact hmem₃ + rwa [hmem_eq, ← hadd] have hm₄ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 4]? = some data₄ := by - rw [hmem_eq, ← hadd]; exact hmem₄ + rwa [hmem_eq, ← hadd] have hm₅ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 5]? = some data₅ := by - rw [hmem_eq, ← hadd]; exact hmem₅ + rwa [hmem_eq, ← hadd] have hm₆ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 6]? = some data₆ := by - rw [hmem_eq, ← hadd]; exact hmem₆ + rwa [hmem_eq, ← hadd] have hm₇ : sp.mem[reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 7]? = some data₇ := by - rw [hmem_eq, ← hadd]; exact hmem₇ + rwa [hmem_eq, ← hadd] have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 8 = true := by - rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned + rwa [is_aligned_vaddr_iff_mod, hadd] have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 8) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := - range_subset_sp1_pma _ 8 (by omega) h_lo (by rw [hadd]; exact h_hi) + range_subset_sp1_pma _ 8 (by omega) h_lo (by rwa [hadd]) have hread := run_vmem_read_of_width_8' rs1_idx reg_val (BitVec.signExtend 64 imm) data₀ data₁ data₂ data₃ data₄ data₅ data₆ data₇ sp hsp_init hsp_rs1 h_align' hsp_config h_fits h_in_range hm₀ hm₁ hm₂ hm₃ hm₄ hm₅ hm₆ hm₇ diff --git a/SP1Clean/Proofs/Chips/LoadX0Chip/Formal.lean b/SP1Clean/Proofs/Chips/LoadX0Chip/Formal.lean index c33646fe..b19c2757 100644 --- a/SP1Clean/Proofs/Chips/LoadX0Chip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LoadX0Chip/Formal.lean @@ -36,34 +36,30 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have h_bin := bool_of_mul_pred h_gate -- eval→value bridge for the offset bits (the only nested vector field the gates reference in value form). obtain ⟨_, _, _, _, _, _, _, _, _, _, hmap_ob⟩ := h_input - have eob0 : Expression.eval env input_var_offset_bit[0] = input_offset_bit[0] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env input_var_offset_bit[1] = input_offset_bit[1] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env input_var_offset_bit[2] = input_offset_bit[2] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 3), Expression.eval env input_var_offset_bit[i] = input_offset_bit[i] := + fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] -- the `AddressOperation` Assumptions (eval form). have hob0' : Expression.eval env input_var_offset_bit[0] = 0 - ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env input_var_offset_bit[1] = 0 - ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env input_var_offset_bit[2] = 0 - ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env input_var_offset_bit[0]).val + 2 * (Expression.eval env input_var_offset_bit[1]).val + 4 * (Expression.eval env input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env input_var_offset_bit[0], Expression.eval env input_var_offset_bit[1], Expression.eval env input_var_offset_bit[2]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, hob0', hob1', hob2', h_ge, h_off'⟩ have h_addr_spec := h_addr h_addr_as - simp only [eob0, eob1, eob2] at h_addr_spec + simp only [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)] at h_addr_spec have h_it := h_itype h_bin -- the alignment gates, in value form. - simp only [eob0, eob1, eob2] at h_al0 h_al1 h_al2 + simp only [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)] at h_al0 h_al1 h_al2 simp only [← sub_eq_add_neg] at h_oa1 h_oa2 simp only [isReal, opcodeVal] refine ⟨⟨h_addr_spec, h_mem h_bin, h_it, @@ -113,29 +109,21 @@ theorem completeness : h_al2, h_al1, h_al0, h_oa1, h_oa2, h_cpu, h_mem, h_it⟩ := h_assumptions simp only [sub_eq_add_neg] at h_oa1 h_oa2 obtain ⟨_, _, _, _, _, _, _, ⟨_, _, _, hmap_pc⟩, _, _, hmap_ob⟩ := h_input - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env.toEnvironment input_var_offset_bit[0] - = input_offset_bit[0] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env.toEnvironment input_var_offset_bit[1] - = input_offset_bit[1] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env.toEnvironment input_var_offset_bit[2] - = input_offset_bit[2] := by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_offset_bit[i] + = input_offset_bit[i] := fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] have hob0' : Expression.eval env.toEnvironment input_var_offset_bit[0] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env.toEnvironment input_var_offset_bit[1] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env.toEnvironment input_var_offset_bit[2] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env.toEnvironment input_var_offset_bit[0]).val + 2 * (Expression.eval env.toEnvironment input_var_offset_bit[1]).val + 4 * (Expression.eval env.toEnvironment input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env.toEnvironment input_var_offset_bit[0], Expression.eval env.toEnvironment input_var_offset_bit[1], @@ -144,7 +132,7 @@ theorem completeness : refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · exact hbin @@ -157,9 +145,9 @@ theorem completeness : · rcases h_b5 with h | h <;> rw [h] <;> simp · rcases h_b6 with h | h <;> rw [h] <;> simp · rcases hbin with h | h <;> rw [h] <;> simp - · simp only [eob2]; exact h_al2 - · simp only [eob1]; exact h_al1 - · simp only [eob0]; exact h_al0 + · simp only [eob 2 (by omega)]; exact h_al2 + · simp only [eob 1 (by omega)]; exact h_al1 + · simp only [eob 0 (by omega)]; exact h_al0 · exact h_oa1 · exact h_oa2 diff --git a/SP1Clean/Proofs/Chips/LtChip/Formal.lean b/SP1Clean/Proofs/Chips/LtChip/Formal.lean index 28fc135f..c5a441af 100644 --- a/SP1Clean/Proofs/Chips/LtChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/LtChip/Formal.lean @@ -1,4 +1,5 @@ import SP1Clean.Native.Chips.LtChip.Defs +import SP1Clean.Math.EvalVec /-! # `SP1Clean.LtChip` — contract: `Assumptions` / soundness / completeness / `circuit` -/ @@ -115,24 +116,12 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe exact h2 (by exact_mod_cast h20) have h01 : (0 : ZMod p) ≠ 1 := by haveI : Fact (1 < p) := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ - intro h - have := congrArg ZMod.val h - rw [ZMod.val_zero, ZMod.val_one] at this - exact absurd this (by norm_num) + exact zero_ne_one rw [h_slt0] at h_lt_spec simp only [if_neg h01] at h_lt_spec simp only [resultWord, rv64_sltu_eq, Word.toBitVec64_toNat ha, Word.toBitVec64_toNat hb, toBitVec64_bitWord _ _ h_lt_spec] -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator (lets the witness -hint's `populate` operands, written `#v[env op_*_val[k]]` by the generator, be folded to `Vector.map`). -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - set_option maxHeartbeats 4000000 in theorem completeness : GeneralFormalCircuit.Completeness (ZMod p) main ProverAssumptions (fun _ _ _ => True) := by diff --git a/SP1Clean/Proofs/Chips/MemoryProvider.lean b/SP1Clean/Proofs/Chips/MemoryProvider.lean index c1cb0670..4dd813bf 100644 --- a/SP1Clean/Proofs/Chips/MemoryProvider.lean +++ b/SP1Clean/Proofs/Chips/MemoryProvider.lean @@ -70,7 +70,7 @@ theorem memRow_eq_of_key {r1 r2 : MemInitRow (ZMod p)} (h : memRowKey r1 = memRo · exact ZMod.val_injective p hv1 · exact ZMod.val_injective p hv2 · exact ZMod.val_injective p hv3 - · exact absurd hi (by omega) + · omega /-- The **`MemoryProvider`**: any Memory-bus contribution list whose every entry sits at the key of some `initSpec`-valid boundary record. The native, predicate-characterized content of SP1's preprocessed Memory @@ -86,7 +86,7 @@ cross-bus-disjointness input `memoryBalance_of_machine` consumes (mirrors `progr theorem memProvider_kind {initSpec : MemInitRow (ZMod p) → Prop} {prov : LookupAccessList} (h : MemoryProvider initSpec prov) : ∀ b ∈ prov, (keyOf b).1 = InteractionKind.Memory := by intro b hb - obtain ⟨row, _, hk⟩ := h b hb + obtain ⟨_, _, hk⟩ := h b hb rw [hk, memRowKey] /-- A boundary contribution whose key a `MemoryProvider` carries is an `initSpec`-valid record. (The @@ -97,7 +97,6 @@ theorem initRow_of_provider {initSpec : MemInitRow (ZMod p) → Prop} {row : Mem (hk : keyOf b = memRowKey row) : initSpec row := by obtain ⟨row', h_spec, h_key⟩ := h_prov b hb rw [hk] at h_key - rw [memRow_eq_of_key h_key] - exact h_spec + rwa [memRow_eq_of_key h_key] end SP1Clean.MemoryGlobalChip diff --git a/SP1Clean/Proofs/Chips/MulChip/Formal.lean b/SP1Clean/Proofs/Chips/MulChip/Formal.lean index 390272fd..08e25d44 100644 --- a/SP1Clean/Proofs/Chips/MulChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/MulChip/Formal.lean @@ -1,4 +1,5 @@ import SP1Clean.Native.Chips.MulChip.Defs +import SP1Clean.Math.EvalVec /-! # `SP1Clean.MulChip` — `Assumptions` / soundness / completeness / `circuit` -/ @@ -117,16 +118,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe List.getElem_cons_succ, Nat.reduceLT, dif_pos] <;> first | exact ha0 | exact ha1 | exact ha2 | exact ha3 -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator (folds the witness -hint's `populate` operands, written `#v[env op_*_val[k]]` by the generator, back to `Vector.map`; -mirrors `LtChip.vec4_eval`). -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - set_option maxHeartbeats 40000000 in theorem completeness : GeneralFormalCircuit.Completeness (ZMod p) main ProverAssumptions (fun _ _ _ => True) := by @@ -157,18 +148,16 @@ theorem completeness : have hmw' : env.get (i₀ + 4) = 1 → env.get i₀ + env.get (i₀ + 1) + env.get (i₀ + 2) + env.get (i₀ + 3) + env.get (i₀ + 4) = 1 := fun h => by rw [hsumc]; exact hmulw_real (by rw [← hflag4]; exact h) - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hpc, Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hpc, Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hpc, Vector.getElem_map] + have epc : ∀ (i : ℕ) (hi : i < 3), + Expression.eval env.toEnvironment input_var_state_pc[i] = input_state_pc[i] := + fun i hi => by rw [← hpc, Vector.getElem_map] have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] have hbool : ∀ x : ZMod p, x = 0 ∨ x = 1 → x * (x + -1) = 0 := by rintro x (h | h) <;> rw [h] <;> simp -- fold the witness hint's `populate` operands to the evaluated input words simp only [Inputs.op_b_val, Inputs.op_c_val, vec4_eval, hob, hoc] at h_env_cols - refine ⟨⟨hbin, by rw [← epc0, ← epc1, ← epc2] at h_cpu; exact h_cpu⟩, + rw [← epc 0 (by norm_num), ← epc 1 (by norm_num), ← epc 2 (by norm_num)] at h_cpu + refine ⟨⟨hbin, h_cpu⟩, ⟨⟨fun _ => ⟨hbU, hcU⟩, hsum01', hmw', hf0', hf1', hf2', hf3', hf4', hsum01'⟩, ?_⟩, by simpa using h_env_a 0, by simpa using h_env_a 1, by simpa using h_env_a 2, by simpa using h_env_a 3, diff --git a/SP1Clean/Proofs/Chips/ProgramChip.lean b/SP1Clean/Proofs/Chips/ProgramChip.lean index 21cbead0..30abf180 100644 --- a/SP1Clean/Proofs/Chips/ProgramChip.lean +++ b/SP1Clean/Proofs/Chips/ProgramChip.lean @@ -72,14 +72,14 @@ theorem programRow_eq_of_key {r1 r2 : ProgramRow (ZMod p)} (h : programRowKey r1 · exact ZMod.val_injective p hb1 · exact ZMod.val_injective p hb2 · exact ZMod.val_injective p hb3 - · exact absurd hi (by omega) + · omega · apply Vector.ext; intro i hi rcases i with _|_|_|_|i · exact ZMod.val_injective p hc0 · exact ZMod.val_injective p hc1 · exact ZMod.val_injective p hc2 · exact ZMod.val_injective p hc3 - · exact absurd hi (by omega) + · omega /-- The **rich ROM-membership predicate** SP1's program/decode chip establishes for every received row — the *received* facts `Foundations/Channels.lean`'s `ProgramMsg.Spec` defers: the register indices are @@ -105,7 +105,6 @@ theorem inROM_of_provider {inROM : ProgramRow (ZMod p) → Prop} {row : ProgramR (hk : keyOf b = programRowKey row) : inROM row := by obtain ⟨row', h_spec, h_key⟩ := h_prov b hb rw [hk] at h_key - rw [programRow_eq_of_key h_key] - exact h_spec + rwa [programRow_eq_of_key h_key] end SP1Clean.ProgramChip diff --git a/SP1Clean/Proofs/Chips/ShiftLeftChip/Core.lean b/SP1Clean/Proofs/Chips/ShiftLeftChip/Core.lean index 7bebe542..787d00e4 100644 --- a/SP1Clean/Proofs/Chips/ShiftLeftChip/Core.lean +++ b/SP1Clean/Proofs/Chips/ShiftLeftChip/Core.lean @@ -21,10 +21,7 @@ variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 17 < p)] /-! ## Field constants for the shift-amount modulus -/ -@[simp] lemma val_64_zmod_p [NeZero p] : (64 : ZMod p).val = 64 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (64 : ℕ) < p by omega) - +-- `val_64_zmod_p` now lives in `Math/Word.lean`; `val_64_ne_zero` is local. lemma val_64_ne_zero [NeZero p] : (64 : ZMod p) ≠ 0 := by have h : (64 : ZMod p).val = 64 := val_64_zmod_p intro hz; rw [hz] at h; simp at h @@ -259,6 +256,29 @@ lemma sll_within_byte_shift_1 /-! ## Byte-shift placement wrappers (one per `(cb4, cb5)` combination) -/ +/-- The bit-decomposition exponent `(cb0 + 2cb1 + 4cb2 + 8cb3).val = S` (the within-byte shift count), +shared by every close-case/sub-case wrapper. -/ +lemma inner_val {S : ℕ} (hS_le : S ≤ 15) {cb0 cb1 cb2 cb3 : ZMod p} + (h_inner_eq : cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) + cb3 * 8 + = ((S : ℕ) : ZMod p)) : + (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) + cb3 * 8 : ZMod p).val = S := by + have hp : 2 ^ 17 < p := Fact.out + haveI : NeZero p := ⟨by omega⟩ + rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) + +/-- The complementary high exponent `(16 - (cb0 + 2cb1 + 4cb2 + 8cb3)).val = 16 - S`. -/ +lemma inner_hi_val {S : ℕ} (hS_le : S ≤ 15) {cb0 cb1 cb2 cb3 : ZMod p} + (h_inner_eq : cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) + cb3 * 8 + = ((S : ℕ) : ZMod p)) : + (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) + cb3 * 8) : ZMod p).val + = 16 - S := by + have hp : 2 ^ 17 < p := Fact.out + haveI : NeZero p := ⟨by omega⟩ + rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) + + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by + rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] + exact ZMod.val_natCast_of_lt (by omega) + set_option maxHeartbeats 4000000 in /-- `cb4=cb5=0` (byte_shift=0): `limb_result` placed at limb 0. -/ lemma sll_close_cb4cb5_zero_case @@ -300,15 +320,8 @@ lemma sll_close_cb4cb5_zero_case rw [BitVec.toNat_shiftLeft] have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 lt_lh2 lt_lh3 rw [h_inner_hi_val] at lt_ll0 lt_ll1 lt_ll2 lt_ll3 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16 + cb5 * 32).val @@ -380,15 +393,8 @@ lemma sll_close_cb4cb5_one_one_case rw [BitVec.toNat_shiftLeft] have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 lt_lh2 lt_lh3 rw [h_inner_hi_val] at lt_ll0 lt_ll1 lt_ll2 lt_ll3 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16 + cb5 * 32).val @@ -460,15 +466,8 @@ lemma sll_close_cb4cb5_zero_one_case rw [BitVec.toNat_shiftLeft] have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 lt_lh2 lt_lh3 rw [h_inner_hi_val] at lt_ll0 lt_ll1 lt_ll2 lt_ll3 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16 + cb5 * 32).val @@ -540,15 +539,8 @@ lemma sll_close_cb4cb5_one_zero_case rw [BitVec.toNat_shiftLeft] have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 lt_lh2 lt_lh3 rw [h_inner_hi_val] at lt_ll0 lt_ll1 lt_ll2 lt_ll3 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16 + cb5 * 32).val @@ -979,15 +971,8 @@ lemma sllw_close_cb4_zero_case % 2 ^ 32 := by have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 rw [h_inner_hi_val] at lt_ll0 lt_ll1 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16).val = S := by @@ -1039,15 +1024,8 @@ lemma sllw_close_cb4_one_case % 2 ^ 32 := by have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq rw [h_inner_val] at lt_lh0 lt_lh1 rw [h_inner_hi_val] at lt_ll0 lt_ll1 have h_total_val : (cb0 + cb1 * (2 : ZMod p) + cb2 * 4 + cb3 * 8 + cb4 * 16).val = S + 16 := by @@ -1105,15 +1083,8 @@ lemma sllw_subcase_cb4_zero BitVec.setWidth 5 (HWord.toBitVec32 #v[c0, c1])) := by have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq have h_lt_ll0_N : ll0.val < N := by rw [h_N_eq]; rw [h_inner_hi_val] at lt_ll0; exact lt_ll0 have h_lt_ll1_N : ll1.val < N := by rw [h_N_eq]; rw [h_inner_hi_val] at lt_ll1; exact lt_ll1 have h_lt_lh0_M : hl0.val < M := by rw [h_M_eq]; rw [h_inner_val] at lt_lh0; exact lt_lh0 @@ -1191,15 +1162,8 @@ lemma sllw_subcase_cb4_one BitVec.setWidth 5 (HWord.toBitVec32 #v[c0, c1])) := by have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have h_inner_val : (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8 : ZMod p).val = S := by - rw [h_inner_eq]; exact ZMod.val_natCast_of_lt (by omega) - have h_inner_hi_val : (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p).val = 16 - S := by - rw [show (16 - (cb0 + cb1 * ((2 : ℕ) : ZMod p) + cb2 * ((4 : ℕ) : ZMod p) - + cb3 * 8) : ZMod p) = (((16 - S) : ℕ) : ZMod p) from by - rw [h_inner_eq, Nat.cast_sub (by omega : S ≤ 16)]; push_cast; ring] - exact ZMod.val_natCast_of_lt (by omega) + have h_inner_val := inner_val h_S_le h_inner_eq + have h_inner_hi_val := inner_hi_val h_S_le h_inner_eq have h_lt_ll0_N : ll0.val < N := by rw [h_N_eq]; rw [h_inner_hi_val] at lt_ll0; exact lt_ll0 have h_ll0v_val : (ll0 * v0123).val = ll0.val * M := by rw [ZMod.val_mul_of_lt, h_v_val]; rw [h_v_val]; nlinarith [h_lt_ll0_N, h_MN] diff --git a/SP1Clean/Proofs/Chips/ShiftLeftChip/Formal.lean b/SP1Clean/Proofs/Chips/ShiftLeftChip/Formal.lean index 56adcd5a..f28d9080 100644 --- a/SP1Clean/Proofs/Chips/ShiftLeftChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/ShiftLeftChip/Formal.lean @@ -1,6 +1,7 @@ import SP1Clean.Proofs.Chips.ShiftLeftChip.Defs import SP1Clean.Proofs.Chips.ShiftLeftChip.Soundness.Sll import SP1Clean.Proofs.Chips.ShiftLeftChip.Soundness.Sllw +import SP1Clean.Math.EvalVec /-! # `SP1Clean.ShiftLeftChip` — contract: soundness / completeness / `circuit` @@ -81,16 +82,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe · exact (SoundSllw.soundness i₀ env input_var input h_input h_assumptions h_holds).1 hr · exact (SoundSll.soundness i₀ env input_var input h_input h_assumptions h_holds).2 -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator (folds the witness -closures' operands, written `#v[env op_b[k]]` in `main`, back to `Vector.map`; mirrors -`MulChip.vec4_eval`). -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - set_option maxHeartbeats 16000000 in /-- Completeness: `main`'s honest `Populate` witness closures (flags from the `"shift_left_flags"` hint) satisfy every constraint under `ProverAssumptions`. -/ @@ -102,11 +93,9 @@ theorem completeness : obtain ⟨h_env_a, h_env_cb, h_env_v, h_env_s, h_env_lo, h_env_hi, h_env_lr, h_env_tail⟩ := h_env obtain ⟨h_env_msb, h_env_fl⟩ := h_env_tail -- project (not destructure — rcases on `h_input` disturbs the bound `h_env_*` hypotheses) - -- the three input equalities the proof reads have hpc := h_input.2.1.2.2.2 have hbpv := h_input.2.2.2.2.2.2.1.1 have hcpv := h_input.2.2.2.2.2.2.2.2.1.1 - -- fold the closures' eval'd operands to the value-level inputs have ec0 : Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[0] = input_adapter_op_c_memory_prev_value[0] := by rw [← hcpv, Vector.getElem_map] @@ -124,11 +113,9 @@ theorem completeness : rw [← hpc, Vector.getElem_map] simp only [vec4_eval, hbpv, ec0] at h_env_a h_env_cb h_env_v h_env_s h_env_lo h_env_hi simp only [vec4_eval, hbpv, ec0] at h_env_lr h_env_msb - -- the operand word / shift-amount limb / hint flags, value-level set B := input_adapter_op_b_memory_prev_value with hB set c0 := input_adapter_op_c_memory_prev_value[0] with hc0 set F := hintFlags env.hint with hF - -- pin every witnessed cell to its populate projection have hA0 : env.get i₀ = (populateA B c0 F)[0] := by simpa using h_env_a 0 have hA1 : env.get (i₀ + 1) = (populateA B c0 F)[1] := by simpa using h_env_a 1 have hA2 : env.get (i₀ + 2) = (populateA B c0 F)[2] := by simpa using h_env_a 2 @@ -172,11 +159,9 @@ theorem completeness : simpa using h_env_fl 1 have hfl2 : env.get (i₀ + 4 + 6 + 3 + 4 + 4 + 4 + 4 + 1 + 2) = F[1] * input_adapter_imm_c := by simpa using h_env_fl 2 - -- derived flag facts have hsum01 : F[0] + F[1] = 0 ∨ F[0] + F[1] = 1 := by rw [← hsum]; exact hbin have hbUw : Word.isU64 B := hbU obtain ⟨hc0v, -, -, -⟩ := Word.lt_cases_of_isU64 hcU - -- the constraint bundles at the populate values obtain ⟨hcba0, hcba1, hcba2, hcba3, hcba4, hcba5⟩ := cBits_asserts c0 obtain ⟨hsel0, hsb0, hsel1, hsb1, hsel2, hsb2, hsel3, hsb3, hone⟩ := shiftU16_asserts c0 F hf0 hsum01 @@ -186,7 +171,6 @@ theorem completeness : obtain ⟨hp1, hp2, hp3, hp4, hp5, hp6, hp7, hp8, hp9, hp10, hp11, hp12, hp13, hp14, hp15, hp16, hp17, hp18, hp19, hp20, hp21, hp22⟩ := place_asserts B c0 F hf0 hf1 hsum01 have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] - -- rewrite every witnessed cell in the goal to its populate value simp only [hA0, hA1, hA2, hA3, hcb0, hcb1, hcb2, hcb3, hcb4, hcb5, hv0, hv1, hv2, hs0, hs1, hs2, hs3, hlo0, hlo1, hlo2, hlo3, hhi0, hhi1, hhi2, hhi3, hlr0, hlr1, hlr2, hlr3, hmsbc, hfl0, hfl1, hfl2, heb0, heb1, heb2, heb3, ec0, epc0, epc1, epc2] @@ -210,8 +194,7 @@ theorem completeness : hp17, hp18, hp19, hp20, hp21, hp22, by simp, hop_a_0, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ - · -- the SLLW `U16MSBOperation` high-bit equation at the witnessed msb - rw [show sllwMsb B c0 F = U16MSBOperation.populate_msb (populateA B c0 F)[1] from by + · rw [show sllwMsb B c0 F = U16MSBOperation.populate_msb (populateA B c0 F)[1] by rw [sllwMsb, if_pos h1]] exact (U16MSBOperation.spec_populate (populateA_val_lt B c0 F hbUw 1 (by norm_num)) 1).2 rfl -- the nine `gate`-gated byte-range pulls: the populate bounds hold unconditionally diff --git a/SP1Clean/Proofs/Chips/ShiftLeftChip/Populate.lean b/SP1Clean/Proofs/Chips/ShiftLeftChip/Populate.lean index d86a97cd..c79ea4a7 100644 --- a/SP1Clean/Proofs/Chips/ShiftLeftChip/Populate.lean +++ b/SP1Clean/Proofs/Chips/ShiftLeftChip/Populate.lean @@ -13,8 +13,8 @@ variant flags are **not** computable from the row inputs — the prover supplies `"shift_left_flags"` `ProverHint` key (one-hot on real rows, absent/all-zero on padding); the committed `is_sllw_imm` is derived as `is_sllw · imm_c`. -Everything is computable (ℕ arithmetic on `.val`, cast back): `TraceGenTests` derives whole trace -rows from these closures and `native_decide`-checks them against SP1's real `generate_trace`. On +Everything is computable (ℕ arithmetic on `.val`, cast back): the `SP1CleanTest` trace-gen anchors +derive whole trace rows from these closures and check them against SP1's real `generate_trace`. On all-zero inputs + empty hint they reproduce SP1's `padded_row_template` (`v_01 = v_012 = v_0123 = 1`, everything else zero) — the `v_*` encodings are computed ungated because their constraints are ungated. -/ diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Core.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Core.lean index 0910b024..5592ebfc 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Core.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Core.lean @@ -28,22 +28,7 @@ variable {p : ℕ} [Fact (Nat.Prime p)] [Fact (2 ^ 17 < p)] local instance : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩ --- Local field-constant helpers not yet in `Foundations/Word.lean` (which has `val_2/65535/65536`). -@[simp] lemma val_4_zmod_p : (4 : ZMod p).val = 4 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (4 : ℕ) < p by omega) -@[simp] lemma val_8_zmod_p : (8 : ZMod p).val = 8 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (8 : ℕ) < p by omega) -@[simp] lemma val_16_zmod_p : (16 : ZMod p).val = 16 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (16 : ℕ) < p by omega) -@[simp] lemma val_32_zmod_p : (32 : ZMod p).val = 32 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (32 : ℕ) < p by omega) -@[simp] lemma val_64_zmod_p : (64 : ZMod p).val = 64 := by - have : (131072 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega - exact ZMod.val_natCast_of_lt (show (64 : ℕ) < p by omega) +-- Field constants `val_4/8/16/32/64_zmod_p` now live in `Math/Word.lean`; `val_64_ne_zero` is local. lemma val_64_ne_zero : (64 : ZMod p) ≠ 0 := by have h : (64 : ZMod p).val = 64 := val_64_zmod_p intro hz; rw [hz] at h; simp at h diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Flags.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Flags.lean index bd6d6558..3c5405d8 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Flags.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Flags.lean @@ -39,7 +39,6 @@ lemma single_flag {a b c d : ZMod p} (ha : a = 1) (hb : b = 0 ∨ b = 1) (hc : c = 0 ∨ c = 1) (hd : d = 0 ∨ d = 1) (hsum : a + b + c + d = 0 ∨ a + b + c + d = 1) : b = 0 ∧ c = 0 ∧ d = 0 := by have hp : 2 ^ 17 < p := Fact.out - haveI : NeZero p := ⟨by omega⟩ have hva : a.val = 1 := by rw [ha]; exact ZMod.val_one p have vb : b.val ≤ 1 := by rcases hb with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] have vc : c.val ≤ 1 := by rcases hc with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] @@ -64,7 +63,6 @@ lemma srlw_sraw_gate {a b c d : ZMod p} (hsum : a + b + c + d = 0 ∨ a + b + c + d = 1) (hcd : c + d = 1) : a = 0 ∧ b = 0 ∧ a + b + c + d = 1 := by have hp : 2 ^ 17 < p := Fact.out - haveI : NeZero p := ⟨by omega⟩ have va : a.val ≤ 1 := by rcases ha with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] have vb : b.val ≤ 1 := by rcases hb with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] have vc : c.val ≤ 1 := by rcases hc with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] @@ -90,7 +88,6 @@ lemma pair_flag {a b c d : ZMod p} (ha : a = 0 ∨ a = 1) (hb : b = 0 ∨ b = 1) (hc : c = 0 ∨ c = 1) (hd : d = 0 ∨ d = 1) (hsum : a + b + c + d = 0 ∨ a + b + c + d = 1) : c + d = 0 ∨ c + d = 1 := by have hp : 2 ^ 17 < p := Fact.out - haveI : NeZero p := ⟨by omega⟩ have va : a.val ≤ 1 := by rcases ha with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] have vb : b.val ≤ 1 := by rcases hb with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] have vc : c.val ≤ 1 := by rcases hc with rfl | rfl <;> simp [ZMod.val_zero, ZMod.val_one] diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Formal.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Formal.lean index d6e4f425..e6780a9f 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Formal.lean @@ -3,6 +3,7 @@ import SP1Clean.Proofs.Chips.ShiftRightChip.Soundness.Srl import SP1Clean.Proofs.Chips.ShiftRightChip.Soundness.Sra import SP1Clean.Proofs.Chips.ShiftRightChip.Soundness.Srlw import SP1Clean.Proofs.Chips.ShiftRightChip.Soundness.Sraw +import SP1Clean.Math.EvalVec /-! # `SP1Clean.ShiftRightChip` — contract: soundness / completeness / `circuit` @@ -85,15 +86,6 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe · exact (SoundSraw.soundness i₀ env input_var input h_input h_assumptions h_holds).1 hr · exact (SoundSrl.soundness i₀ env input_var input h_input h_assumptions h_holds).2 -set_option linter.unusedSectionVars false in -/-- A length-4 `#v` of pointwise evaluations is the `Vector.map` of the evaluator (folds the witness -closures' operands back to `Vector.map`; mirrors `ShiftLeftChip.vec4_eval`). -/ -private lemma vec4_eval (e : Environment (ZMod p)) (v : Vector (Expression (ZMod p)) 4) : - (#v[Expression.eval e v[0], Expression.eval e v[1], Expression.eval e v[2], - Expression.eval e v[3]] : Vector (ZMod p) 4) = Vector.map (Expression.eval e) v := by - ext k hk - interval_cases k <;> simp [Vector.getElem_map] - set_option maxHeartbeats 16000000 in /-- Completeness: `main`'s honest `Populate` witness closures (flags from the `"shift_right_flags"` hint) satisfy every constraint under `ProverAssumptions`. -/ @@ -108,7 +100,6 @@ theorem completeness : have hpc := h_input.2.1.2.2.2 have hbpv := h_input.2.2.2.2.2.2.1.1 have hcpv := h_input.2.2.2.2.2.2.2.2.1.1 - -- fold the closures' eval'd operands to the value-level inputs have ec0 : Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[0] = input_adapter_op_c_memory_prev_value[0] := by rw [← hcpv, Vector.getElem_map] @@ -129,7 +120,6 @@ theorem completeness : set B := input_adapter_op_b_memory_prev_value with hB set c0 := input_adapter_op_c_memory_prev_value[0] with hc0 set F := hintFlags env.hint with hF - -- pin every witnessed cell to its populate projection have hA0 : env.get i₀ = (populateA B c0 F)[0] := by simpa using h_env_a 0 have hA1 : env.get (i₀ + 1) = (populateA B c0 F)[1] := by simpa using h_env_a 1 have hA2 : env.get (i₀ + 2) = (populateA B c0 F)[2] := by simpa using h_env_a 2 @@ -202,7 +192,6 @@ theorem completeness : simp only [hA0, hA1, hA2, hA3, hbm, hsw, hcb0, hcb1, hcb2, hcb3, hcb4, hcb5, hsram, hv0, hv1, hv2, hlo0, hlo1, hlo2, hlo3, hhi0, hhi1, hhi2, hhi3, hlr0, hlr1, hlr2, hlr3, hs0, hs1, hs2, hs3, hfl0, hfl1, hfl2, hfl3, hfl4, heb0, heb1, heb2, heb3, ec0, epc0, epc1, epc2] - -- derived flag facts have hbUw : Word.isU64 B := hbU obtain ⟨hbU0, hbU1, hbU2, hbU3⟩ := Word.lt_cases_of_isU64 hbUw obtain ⟨ho0, ho1, ho2, ho3⟩ := one_hot_resolve F hf0 hf1 hf2 hf3 hsum01 @@ -221,7 +210,6 @@ theorem completeness : · obtain ⟨-, -, h3z⟩ := ho2 h2 right; rw [h2, h3z, add_zero] have hbmB := bMsb_bool B F hbUw hf1 hf3 (fun h => (ho1 h).2.2) - -- the constraint bundles at the populate values obtain ⟨hcba0, hcba1, hcba2, hcba3, hcba4, hcba5⟩ := ShiftLeftChip.cBits_asserts c0 obtain ⟨hsel0, hsb0, hsel1, hsb1, hsel2, hsb2, hsel3, hsb3, hone⟩ := shiftU16_asserts c0 F he14 hsum01 @@ -272,7 +260,7 @@ theorem completeness : rw [bMsb_eq_sraw B F h3 (ho3 h3).2.1] exact (U16MSBOperation.spec_populate hbU1 1).2 rfl · -- the word-variant sign witness is the high bit of the placed `a[1]` - rw [show srwMsb B c0 F = U16MSBOperation.populate_msb (populateA B c0 F)[1] from by + rw [show srwMsb B c0 F = U16MSBOperation.populate_msb (populateA B c0 F)[1] by rw [srwMsb, if_pos h13]] exact (U16MSBOperation.spec_populate (populateA_val_lt B c0 F hbUw hf0 hf1 hf2 hf3 hsum01 1 (by norm_num)) 1).2 rfl diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Math.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Math.lean index e51a893a..62201c09 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Math.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Math.lean @@ -67,8 +67,8 @@ lemma srl_div_to_bitvec (W rs1 rs2 : Word (ZMod p)) (h_rs2U : Word.isU64 rs2) Word.toBitVec64 W = RV64.srl (Word.toBitVec64 rs2) (Word.toBitVec64 rs1) := by have hsh : (Word.toBitVec64 rs2).toNat % 64 = rs2[0].val % 64 := by rw [Word.toBitVec64_toNat h_rs2U, Word.toNat_def, - show (2:ℕ)^16 = 65536 from by norm_num, show (2:ℕ)^32 = 4294967296 from by norm_num, - show (2:ℕ)^48 = 281474976710656 from by norm_num] + show (2:ℕ)^16 = 65536 by norm_num, show (2:ℕ)^32 = 4294967296 by norm_num, + show (2:ℕ)^48 = 281474976710656 by norm_num] omega apply BitVec.eq_of_toNat_eq rw [srl_toNat, hsh] @@ -83,8 +83,8 @@ lemma sra_div_to_bitvec_false (W rs1 rs2 : Word (ZMod p)) (h_rs2U : Word.isU64 r Word.toBitVec64 W = RV64.sra (Word.toBitVec64 rs2) (Word.toBitVec64 rs1) := by have hsh : (Word.toBitVec64 rs2).toNat % 64 = rs2[0].val % 64 := by rw [Word.toBitVec64_toNat h_rs2U, Word.toNat_def, - show (2:ℕ)^16 = 65536 from by norm_num, show (2:ℕ)^32 = 4294967296 from by norm_num, - show (2:ℕ)^48 = 281474976710656 from by norm_num] + show (2:ℕ)^16 = 65536 by norm_num, show (2:ℕ)^32 = 4294967296 by norm_num, + show (2:ℕ)^48 = 281474976710656 by norm_num] omega apply BitVec.eq_of_toNat_eq rw [sra_toNat_false _ _ h_msb, hsh] @@ -100,8 +100,8 @@ lemma sra_div_to_bitvec_true (W rs1 rs2 : Word (ZMod p)) (h_rs2U : Word.isU64 rs Word.toBitVec64 W = RV64.sra (Word.toBitVec64 rs2) (Word.toBitVec64 rs1) := by have hsh : (Word.toBitVec64 rs2).toNat % 64 = rs2[0].val % 64 := by rw [Word.toBitVec64_toNat h_rs2U, Word.toNat_def, - show (2:ℕ)^16 = 65536 from by norm_num, show (2:ℕ)^32 = 4294967296 from by norm_num, - show (2:ℕ)^48 = 281474976710656 from by norm_num] + show (2:ℕ)^16 = 65536 by norm_num, show (2:ℕ)^32 = 4294967296 by norm_num, + show (2:ℕ)^48 = 281474976710656 by norm_num] omega apply BitVec.eq_of_toNat_eq rw [sra_toNat_true _ _ h_msb, hsh] diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sra.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sra.lean index 09741849..c37fc996 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sra.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sra.lean @@ -163,9 +163,9 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have he := hbyte_fact h_byte0 rw [hc0e] at he have h10v : ((10 : ZMod p)).val = 10 := by - rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by have := Fact.out (p := 2 ^ 17 < p); omega) - rw [h10v, show (2 : ℕ) ^ 10 = 1024 from by norm_num] at he + rw [h10v, show (2 : ℕ) ^ 10 = 1024 by norm_num] at he rw [c0mod_inv_bridge]; exact he -- Bridge the byte-pull range facts to the `srl_close_su16_*_case` exponent form. rw [cb4sum_natCast] at lt_ll0 lt_ll1 lt_ll2 lt_ll3 @@ -175,7 +175,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) -- Alias the shift-result columns. @@ -496,7 +496,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[3] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 3, bool_of_mul_pred h_sra_b⟩ case msb2A => @@ -504,7 +504,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[1] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 1, bool_of_mul_pred h_sraw_b⟩ case msb3A => @@ -584,7 +584,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) obtain ⟨-, h_ll2_0⟩ := ShiftRightMath.higher_lower_zero b_cb0 b_cb1 b_cb2 b_cb3 diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sraw.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sraw.lean index 696d666b..020934bd 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sraw.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Sraw.lean @@ -143,7 +143,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) obtain ⟨-, h_ll2_0⟩ := ShiftRightMath.higher_lower_zero b_cb0 b_cb1 b_cb2 b_cb3 @@ -154,9 +154,9 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have he := hbyte_fact h_byte0 rw [hc0e] at he have h10v : ((10 : ZMod p)).val = 10 := by - rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) - rw [h10v, show (2 : ℕ) ^ 10 = 1024 from by norm_num] at he + rw [h10v, show (2 : ℕ) ^ 10 = 1024 by norm_num] at he rw [c0mod_inv_bridge]; exact he set su0 := env.get (i₀ + 4 + 1 + 1 + 6 + 1 + 3 + 4 + 4 + 4) with hsu0_def set su1 := env.get (i₀ + 4 + 1 + 1 + 6 + 1 + 3 + 4 + 4 + 4 + 1) with hsu1_def @@ -433,7 +433,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[3] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 3, bool_of_mul_pred h_sra_b⟩ case msb2A => @@ -441,7 +441,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[1] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 1, bool_of_mul_pred h_sraw_b⟩ case msb3A => @@ -521,7 +521,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) obtain ⟨-, h_ll2_0⟩ := ShiftRightMath.higher_lower_zero b_cb0 b_cb1 b_cb2 b_cb3 diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srl.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srl.lean index ff2379f8..e6748741 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srl.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srl.lean @@ -167,9 +167,9 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have he := hbyte_fact h_byte0 rw [hc0e] at he have h10v : ((10 : ZMod p)).val = 10 := by - rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by have := Fact.out (p := 2 ^ 17 < p); omega) - rw [h10v, show (2 : ℕ) ^ 10 = 1024 from by norm_num] at he + rw [h10v, show (2 : ℕ) ^ 10 = 1024 by norm_num] at he rw [c0mod_inv_bridge]; exact he rw [h_c0mod] -- Bridge the byte-pull range facts to the `srl_close_su16_*_case` exponent form. @@ -180,7 +180,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) -- Alias the shift-result columns. @@ -346,7 +346,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[3] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 3, bool_of_mul_pred h_sra_b⟩ case msb2A => @@ -354,7 +354,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[1] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 1, bool_of_mul_pred h_sraw_b⟩ case msb3A => @@ -434,7 +434,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) obtain ⟨-, h_ll2_0⟩ := ShiftRightMath.higher_lower_zero b_cb0 b_cb1 b_cb2 b_cb3 diff --git a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srlw.lean b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srlw.lean index c1628e28..e67241ba 100644 --- a/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srlw.lean +++ b/SP1Clean/Proofs/Chips/ShiftRightChip/Soundness/Srlw.lean @@ -147,7 +147,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) -- `ll2 = 0`. @@ -160,9 +160,9 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have he := hbyte_fact h_byte0 rw [hc0e] at he have h10v : ((10 : ZMod p)).val = 10 := by - rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (10 : ZMod p) = ((10 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) - rw [h10v, show (2 : ℕ) ^ 10 = 1024 from by norm_num] at he + rw [h10v, show (2 : ℕ) ^ 10 = 1024 by norm_num] at he rw [c0mod_inv_bridge]; exact he -- Alias the byte-shift selectors + limb_result; one-hot + reassembly. set su0 := env.get (i₀ + 4 + 1 + 1 + 6 + 1 + 3 + 4 + 4 + 4) with hsu0_def @@ -311,7 +311,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[3] - = input_adapter_op_b_memory_prev_value[3] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[3] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 3, bool_of_mul_pred h_sra_b⟩ case msb2A => @@ -319,7 +319,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨-, -, -, -, -, -, ⟨h_obmap, -, -⟩, -, -, -⟩ := h_input exact Or.inr ⟨fun _ => by rw [show Expression.eval env input_var_adapter_op_b_memory_prev_value[1] - = input_adapter_op_b_memory_prev_value[1] from by rw [← h_obmap]; simp only [Vector.getElem_map]] + = input_adapter_op_b_memory_prev_value[1] by rw [← h_obmap]; simp only [Vector.getElem_map]] exact h_rs1U 1, bool_of_mul_pred h_sraw_b⟩ case msb3A => @@ -399,7 +399,7 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe intro h; have := val_2_zmod_p (p := p); rw [h, ZMod.val_zero] at this; exact absurd this (by norm_num) have hne3 : (3 : ZMod p) ≠ 0 := by intro h; have h3 : (3 : ZMod p).val = 3 := by - rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by push_cast; rfl] + rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) by push_cast; rfl] exact ZMod.val_natCast_of_lt (by omega) rw [h, ZMod.val_zero] at h3; exact absurd h3 (by norm_num) obtain ⟨-, h_ll2_0⟩ := ShiftRightMath.higher_lower_zero b_cb0 b_cb1 b_cb2 b_cb3 diff --git a/SP1Clean/Proofs/Chips/StoreByteChip/Bridge.lean b/SP1Clean/Proofs/Chips/StoreByteChip/Bridge.lean index 1babf855..977f338f 100644 --- a/SP1Clean/Proofs/Chips/StoreByteChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/StoreByteChip/Bridge.lean @@ -47,7 +47,7 @@ theorem correct_store_byte_native (Sail.BitVec.extractLsb stored 7 0)).run s := by have hse : (sign_extend imm : BitVec 64) = BitVec.signExtend 64 imm := by simp [sign_extend] have hpc_get : s.regs.get Register.PC (hs _) = pc := by - rw [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc; exact h_pc + rwa [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc set sp : SailState := { s with regs := s.regs.insert Register.nextPC (pc + 4#64) } with hsp have hsp_init : SailState.isInitialized sp := SailState.isInitialized_insert s hs Register.nextPC (pc + 4#64) @@ -81,10 +81,6 @@ theorem correct_store_byte_native have hwrite := run_vmem_write_of_width_1 rs1_idx reg_val (BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 7 0) sp hsp_init hsp_rs1 h_align' hsp_config h_does_fit h_in_range simp only [hmem_eq] at hwrite - have hrx2 : rX_bits (regidx.Regidx rs2_idx) sp = .ok stored sp := by - have h := @run_rX_bits rs2_idx sp - simp only [EStateM.run] at h - rw [h, hsp_rs2] have hsp1 : (sp1_sb pc (reg_val + BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 7 0)).run s = .ok RETIRE_SUCCESS { sp with mem := diff --git a/SP1Clean/Proofs/Chips/StoreByteChip/Formal.lean b/SP1Clean/Proofs/Chips/StoreByteChip/Formal.lean index c3da7ef3..f0bcf771 100644 --- a/SP1Clean/Proofs/Chips/StoreByteChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/StoreByteChip/Formal.lean @@ -37,32 +37,18 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe hmap_ob, _, _, _, _, hmap_sv⟩ := h_input have eoap0 : Expression.eval env input_var_adapter_op_a_memory_prev_value[0] = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env input_var_offset_bit[1] = input_offset_bit[1] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env input_var_offset_bit[2] = input_offset_bit[2] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env input_var_offset_bit[0] = input_offset_bit[0] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have esv0 : Expression.eval env input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - simp only [eob1, eob2, epv0, epv1, epv2, epv3, ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 - simp only [eob0, ← sub_eq_add_neg] at hincr - simp only [esv0, esv1, esv2, esv3, epv0, epv1, epv2, epv3, eob1, eob2, ← sub_eq_add_neg] - at hr0 hr1 hr2 hr3 + have eob : ∀ i (hi : i < 3), Expression.eval env input_var_offset_bit[i] = input_offset_bit[i] := + fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + have esv : ∀ i (hi : i < 4), Expression.eval env input_var_store_value[i] = input_store_value[i] := + fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] + simp only [eob 1 (by omega), eob 2 (by omega), epv 0 (by omega), epv 1 (by omega), + epv 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg] at hsel0 hsel1 hsel2 hsel3 + simp only [eob 0 (by omega), ← sub_eq_add_neg] at hincr + simp only [esv 0 (by omega), esv 1 (by omega), esv 2 (by omega), esv 3 (by omega), + epv 0 (by omega), epv 1 (by omega), epv 2 (by omega), epv 3 (by omega), + eob 1 (by omega), eob 2 (by omega), ← sub_eq_add_neg] at hr0 hr1 hr2 hr3 -- the real-row byte bounds, from the two inline U8Range-pair receives. have h_bytes : input_is_real = 1 → input_register_low_byte.val < 256 ∧ @@ -78,23 +64,23 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe rw [show (2:ℕ)^8 = 256 from by norm_num, ← sub_eq_add_neg] at hbm exact ⟨hbr.1, hbr.2, hbm.1, hbm.2⟩ have hob0' : Expression.eval env input_var_offset_bit[0] = 0 - ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env input_var_offset_bit[1] = 0 - ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env input_var_offset_bit[2] = 0 - ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env input_var_offset_bit[0]).val + 2 * (Expression.eval env input_var_offset_bit[1]).val + 4 * (Expression.eval env input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env input_var_offset_bit[0], Expression.eval env input_var_offset_bit[1], Expression.eval env input_var_offset_bit[2]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, hob0', hob1', hob2', h_ge, h_off'⟩ have h_addr_spec := h_addr h_addr_as - simp only [eob0, eob1, eob2] at h_addr_spec + simp only [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)] at h_addr_spec have h_it := h_itype h_bin refine ⟨⟨h_addr_spec, h_mem h_bin, h_it, fun h1 => ⟨(h_bytes h1).1, (h_bytes h1).2.1, (h_bytes h1).2.2.1, (h_bytes h1).2.2.2⟩, @@ -156,45 +142,25 @@ theorem completeness : have eoap0 : Expression.eval env.toEnvironment input_var_adapter_op_a_memory_prev_value[0] = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] simp only [regHigh, memHigh] at hreghi_pa hmemhi_pa - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env.toEnvironment input_var_offset_bit[0] - = input_offset_bit[0] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env.toEnvironment input_var_offset_bit[1] - = input_offset_bit[1] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob2 : Expression.eval env.toEnvironment input_var_offset_bit[2] - = input_offset_bit[2] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have esv0 : Expression.eval env.toEnvironment input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env.toEnvironment input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env.toEnvironment input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env.toEnvironment input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_offset_bit[i] + = input_offset_bit[i] := fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + have esv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_store_value[i] + = input_store_value[i] := fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] have hob0' : Expression.eval env.toEnvironment input_var_offset_bit[0] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env.toEnvironment input_var_offset_bit[1] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have hob2' : Expression.eval env.toEnvironment input_var_offset_bit[2] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob2]; exact hob2 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[2] = 1 := by rw [eob 2 (by omega)]; exact hob2 have h_off' : (Expression.eval env.toEnvironment input_var_offset_bit[0]).val + 2 * (Expression.eval env.toEnvironment input_var_offset_bit[1]).val + 4 * (Expression.eval env.toEnvironment input_var_offset_bit[2]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1, eob2]; exact h_off + rw [eob 0 (by omega), eob 1 (by omega), eob 2 (by omega)]; exact h_off have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, Expression.eval env.toEnvironment input_var_offset_bit[0], Expression.eval env.toEnvironment input_var_offset_bit[1], @@ -202,7 +168,7 @@ theorem completeness : ⟨ha, hb, hfit, hob0', hob1', hob2', h_ge, h_off'⟩ refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · exact hbin @@ -213,15 +179,23 @@ theorem completeness : · intro _ simp only [byteChannel]; rw [← sub_eq_add_neg] exact (byteRowSpec_u8range_pair _ _).mpr ⟨hmem_pa, hmemhi_pa⟩ - · simp only [eob1, eob2, epv0, ← sub_eq_add_neg]; exact hsel0 - · simp only [eob1, eob2, epv1, ← sub_eq_add_neg]; exact hsel1 - · simp only [eob1, eob2, epv2, ← sub_eq_add_neg]; exact hsel2 - · simp only [eob1, eob2, epv3, ← sub_eq_add_neg]; exact hsel3 - · simp only [eob0, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hincr_pa - · simp only [esv0, eob1, eob2, epv0, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr0 - · simp only [esv1, eob1, eob2, epv1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr1 - · simp only [esv2, eob1, eob2, epv2, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr2 - · simp only [esv3, eob1, eob2, epv3, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr3 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 0 (by omega), ← sub_eq_add_neg]; exact hsel0 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 1 (by omega), ← sub_eq_add_neg]; exact hsel1 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 2 (by omega), ← sub_eq_add_neg]; exact hsel2 + · simp only [eob 1 (by omega), eob 2 (by omega), epv 3 (by omega), ← sub_eq_add_neg]; exact hsel3 + · simp only [eob 0 (by omega), ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hincr_pa + · simp only [esv 0 (by omega), eob 1 (by omega), eob 2 (by omega), epv 0 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr0 + · simp only [esv 1 (by omega), eob 1 (by omega), eob 2 (by omega), epv 1 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr1 + · simp only [esv 2 (by omega), eob 1 (by omega), eob 2 (by omega), epv 2 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr2 + · simp only [esv 3 (by omega), eob 1 (by omega), eob 2 (by omega), epv 3 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr3 · rcases hbin with h | h <;> rw [h] <;> simp /-- The `StoreByte` chip row as a `GeneralFormalCircuit`; output is the extracted `StoreByteColumns`. -/ diff --git a/SP1Clean/Proofs/Chips/StoreDoubleChip/Bridge.lean b/SP1Clean/Proofs/Chips/StoreDoubleChip/Bridge.lean index 64311c3a..b7e06789 100644 --- a/SP1Clean/Proofs/Chips/StoreDoubleChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/StoreDoubleChip/Bridge.lean @@ -83,29 +83,24 @@ theorem correct_store_double_native h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } -- Register reads of `rs1` (base) and `rs2` (stored value) survive the `nextPC` write. have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hsp_rs2 : sp.get_reg? rs2_idx = some (Word.toBitVec64 stored) := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs2 + rwa [hsp, SailState.get_reg?_insert_nextPC] -- The alignment fact in Sail form, and the range-subset PMA fact. have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega have h_align' : is_aligned_vaddr (virtaddr.Virtaddr (reg_val + BitVec.signExtend 64 imm)) 8 = true := by rw [is_aligned_vaddr_iff_mod, hadd]; exact h_aligned - have h_does_fit' : reg_val.toNat + (BitVec.signExtend 64 imm).toNat + 8 < 2 ^ 64 := _h_does_fit have h_in_range : range_subset (zero_extend (BitVec.addInt (reg_val + BitVec.signExtend 64 imm) 0)) (to_bits 8) (2#64 ^ 16) (2#64 ^ 48 - 2#64 ^ 16) = true := range_subset_sp1_pma _ 8 (by omega) h_lo (by rw [hadd]; exact h_hi) -- The write result on `sp` (same memory as `s`). have hwrite := run_vmem_write_of_width_8 rs1_idx reg_val (BitVec.signExtend 64 imm) - (Word.toBitVec64 stored) sp hsp_init hsp_rs1 h_align' hsp_config h_does_fit' h_in_range + (Word.toBitVec64 stored) sp hsp_init hsp_rs1 h_align' hsp_config _h_does_fit h_in_range simp only [hmem_eq] at hwrite - -- Resolve `rX_bits rs2` (the stored-value register) on `sp`, and collapse the `extractLsb`. - have hrx2 : rX_bits (regidx.Regidx rs2_idx) sp = .ok (Word.toBitVec64 stored) sp := by - have h := @run_rX_bits rs2_idx sp - simp only [EStateM.run] at h - rw [h, hsp_rs2] + -- Collapse the `extractLsb` on the stored value. have hext : Sail.BitVec.extractLsb (Word.toBitVec64 stored) 63 0 = Word.toBitVec64 stored := by simp [Sail.BitVec.extractLsb, BitVec.extractLsb, BitVec.extractLsb'] -- Reduce the SP1 side: thread the `nextPC` write, then run the `modify` of memory. @@ -128,9 +123,7 @@ theorem correct_store_double_native (BitVec.ofNat 8 ((Word.toBitVec64 stored).toNat >>> 48))).insert ((reg_val + BitVec.signExtend 64 imm).toNat + 7) (BitVec.ofNat 8 ((Word.toBitVec64 stored).toNat >>> 56))) } := by - rw [sp1_sd] - rw [EStateM.run_bind, run_writeReg] - rfl + rw [sp1_sd, EStateM.run_bind, run_writeReg]; rfl rw [hsp1] simp only [spec_sd] rw [EStateM.run_bind, run_readReg_of_isInitialized _ _ hs, hpc_get] @@ -158,7 +151,7 @@ theorem sd_chip_reaches_sail = (sp1_sd pc (Word.toBitVec64 input.op_b_val + BitVec.signExtend 64 imm) input.adapter.op_a_memory.prev_value).run s := by haveI : NeZero p := ⟨(Fact.out (p := p.Prime)).pos.ne'⟩ - obtain ⟨h_b, h_c, _h_fits48, h_nonres, h_align48⟩ := h_assum + obtain ⟨h_b, h_c, _h_fits48, _h_nonres, h_align48⟩ := h_assum have hreg : (Word.toBitVec64 input.op_b_val).toNat = Word.toNat input.op_b_val := Word.toBitVec64_toNat h_b have hoff : (BitVec.signExtend 64 imm).toNat = Word.toNat input.op_c_imm := by diff --git a/SP1Clean/Proofs/Chips/StoreDoubleChip/Formal.lean b/SP1Clean/Proofs/Chips/StoreDoubleChip/Formal.lean index 51867bec..483e55eb 100644 --- a/SP1Clean/Proofs/Chips/StoreDoubleChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/StoreDoubleChip/Formal.lean @@ -29,15 +29,14 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe obtain ⟨ha, hb, hfit, h_ge, h_align⟩ := h_assumptions obtain ⟨_h_cpu, h_addr, h_mem, h_itype, h_gate⟩ := h_holds have h_bin := bool_of_mul_pred h_gate - have h_it := h_itype h_bin -- the `AddressOperation` Assumptions: operand `isU64`s + fits, the offset bits boolean (literal `0`), -- and the address-validity (non-reserved + 8-aligned, so the inverse gate / offset range check hold). have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, 0, 0⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, Or.inl rfl, Or.inl rfl, h_ge, by simp only [ZMod.val_zero]; omega⟩ - refine ⟨⟨h_addr h_addr_as, h_mem h_bin, h_it, h_bin⟩, ?_⟩ -- the per-subcircuit channel-requirement tail (`channels = [] ∨ .Assumptions`). - exact ⟨Or.inr h_bin, Or.inr h_addr_as, Or.inr h_bin, Or.inr h_bin⟩ + exact ⟨⟨h_addr h_addr_as, h_mem h_bin, h_itype h_bin, h_bin⟩, + Or.inr h_bin, Or.inr h_addr_as, Or.inr h_bin, Or.inr h_bin⟩ /-- Prover-side row well-formedness (3-arg form): operand `isU64`s + address-fits bound + the reader clock/timestamp `Spec`s + `is_real` binary. -/ @@ -65,22 +64,13 @@ theorem completeness : -- eval→value bridge for the nested `pc` vector the CPUState `Spec` references. have hmap_pc : Vector.map (Expression.eval env.toEnvironment) input_var_state_pc = input_state_pc := h_input.2.1.2.2.2 - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] - = input_state_pc[0] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] - = input_state_pc[1] := by rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] - = input_state_pc[2] := by rw [← hmap_pc]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, 0, 0⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, Or.inl rfl, Or.inl rfl, h_ge, by simp only [ZMod.val_zero]; omega⟩ - refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, ?_⟩ - · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu - · exact hbin - · exact h_mem - · exact hbin - · exact h_it + refine ⟨⟨hbin, ?_⟩, h_addr_as, ⟨hbin, h_mem⟩, ⟨hbin, h_it⟩, ?_⟩ + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · rcases hbin with h | h <;> rw [h] <;> simp /-- The `StoreDouble` chip row as a `GeneralFormalCircuit`; output is the extracted `StoreDoubleColumns`. -/ diff --git a/SP1Clean/Proofs/Chips/StoreHalfChip/Bridge.lean b/SP1Clean/Proofs/Chips/StoreHalfChip/Bridge.lean index 14280af7..b24033dc 100644 --- a/SP1Clean/Proofs/Chips/StoreHalfChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/StoreHalfChip/Bridge.lean @@ -50,7 +50,7 @@ theorem correct_store_half_native (Sail.BitVec.extractLsb stored 15 0)).run s := by have hse : (sign_extend imm : BitVec 64) = BitVec.signExtend 64 imm := by simp [sign_extend] have hpc_get : s.regs.get Register.PC (hs _) = pc := by - rw [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc; exact h_pc + rwa [Std.ExtDHashMap.get?_eq_some_get (hs _), Option.some_inj] at h_pc set sp : SailState := { s with regs := s.regs.insert Register.nextPC (pc + 4#64) } with hsp have hsp_init : SailState.isInitialized sp := SailState.isInitialized_insert s hs Register.nextPC (pc + 4#64) @@ -84,10 +84,6 @@ theorem correct_store_half_native have hwrite := run_vmem_write_of_width_2 rs1_idx reg_val (BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 15 0) sp hsp_init hsp_rs1 h_align' hsp_config h_does_fit h_in_range simp only [hmem_eq] at hwrite - have hrx2 : rX_bits (regidx.Regidx rs2_idx) sp = .ok stored sp := by - have h := @run_rX_bits rs2_idx sp - simp only [EStateM.run] at h - rw [h, hsp_rs2] have hsp1 : (sp1_sh pc (reg_val + BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 15 0)).run s = .ok RETIRE_SUCCESS { sp with mem := diff --git a/SP1Clean/Proofs/Chips/StoreHalfChip/Formal.lean b/SP1Clean/Proofs/Chips/StoreHalfChip/Formal.lean index 98a9a867..7bcbdfae 100644 --- a/SP1Clean/Proofs/Chips/StoreHalfChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/StoreHalfChip/Formal.lean @@ -42,45 +42,32 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe = input_memory_access_prev_value := h_input.2.2.2.1.1 have hmap_oap : Vector.map (Expression.eval env) input_var_adapter_op_a_memory_prev_value = input_adapter_op_a_memory_prev_value := h_input.2.2.1.2.1.1 - have eob0 : Expression.eval env input_var_offset_bit[0] = input_offset_bit[0] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env input_var_offset_bit[1] = input_offset_bit[1] := by - rw [← hmap_ob]; simp only [Vector.getElem_map] - have esv0 : Expression.eval env input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 2), Expression.eval env input_var_offset_bit[i] = input_offset_bit[i] := + fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have esv : ∀ i (hi : i < 4), Expression.eval env input_var_store_value[i] = input_store_value[i] := + fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] have eoap0 : Expression.eval env input_var_adapter_op_a_memory_prev_value[0] = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] - simp only [esv0, esv1, esv2, esv3, epv0, epv1, epv2, epv3, eoap0, eob0, eob1, ← sub_eq_add_neg] - at hr0 hr1 hr2 hr3 + simp only [esv 0 (by omega), esv 1 (by omega), esv 2 (by omega), esv 3 (by omega), + epv 0 (by omega), epv 1 (by omega), epv 2 (by omega), epv 3 (by omega), + eoap0, eob 0 (by omega), eob 1 (by omega), ← sub_eq_add_neg] at hr0 hr1 hr2 hr3 have h_it := h_itype h_bin have hob0' : Expression.eval env input_var_offset_bit[0] = 0 - ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env input_var_offset_bit[1] = 0 - ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have h_off' : (0 : ZMod p).val + 2 * (Expression.eval env input_var_offset_bit[0]).val + 4 * (Expression.eval env input_var_offset_bit[1]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1]; simp only [ZMod.val_zero]; omega + rw [eob 0 (by omega), eob 1 (by omega)]; simp only [ZMod.val_zero]; omega have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, Expression.eval env input_var_offset_bit[0], Expression.eval env input_var_offset_bit[1]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, hob0', hob1', h_ge, h_off'⟩ have h_addr_spec := h_addr h_addr_as - simp only [eob0, eob1] at h_addr_spec + simp only [eob 0 (by omega), eob 1 (by omega)] at h_addr_spec refine ⟨⟨h_addr_spec, h_mem h_bin, h_it, ⟨sub_eq_zero.mp hr0, sub_eq_zero.mp hr1, sub_eq_zero.mp hr2, sub_eq_zero.mp hr3⟩, h_bin⟩, ?_⟩ exact ⟨Or.inr h_bin, Or.inr h_addr_as, Or.inr h_bin, Or.inr h_bin⟩ @@ -136,57 +123,47 @@ theorem completeness : = input_memory_access_prev_value := h_input.2.2.2.1.1 have hmap_oap : Vector.map (Expression.eval env.toEnvironment) input_var_adapter_op_a_memory_prev_value = input_adapter_op_a_memory_prev_value := h_input.2.2.1.2.1.1 - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have eob0 : Expression.eval env.toEnvironment input_var_offset_bit[0] - = input_offset_bit[0] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have eob1 : Expression.eval env.toEnvironment input_var_offset_bit[1] - = input_offset_bit[1] := by rw [← hmap_ob]; simp only [Vector.getElem_map] - have esv0 : Expression.eval env.toEnvironment input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env.toEnvironment input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env.toEnvironment input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env.toEnvironment input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have eob : ∀ i (hi : i < 2), Expression.eval env.toEnvironment input_var_offset_bit[i] + = input_offset_bit[i] := fun i hi => by rw [← hmap_ob]; simp only [Vector.getElem_map] + have esv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_store_value[i] + = input_store_value[i] := fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] have eoap0 : Expression.eval env.toEnvironment input_var_adapter_op_a_memory_prev_value[0] = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] have hob0' : Expression.eval env.toEnvironment input_var_offset_bit[0] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob0]; exact hob0 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[0] = 1 := by rw [eob 0 (by omega)]; exact hob0 have hob1' : Expression.eval env.toEnvironment input_var_offset_bit[1] = 0 - ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob1]; exact hob1 + ∨ Expression.eval env.toEnvironment input_var_offset_bit[1] = 1 := by rw [eob 1 (by omega)]; exact hob1 have h_off' : (0 : ZMod p).val + 2 * (Expression.eval env.toEnvironment input_var_offset_bit[0]).val + 4 * (Expression.eval env.toEnvironment input_var_offset_bit[1]).val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by - rw [eob0, eob1]; simp only [ZMod.val_zero]; omega + rw [eob 0 (by omega), eob 1 (by omega)]; simp only [ZMod.val_zero]; omega have h_addr_as : AddressOperation.circuit.Assumptions (⟨input_adapter_op_b_memory_prev_value, input_adapter_op_c_imm, 0, Expression.eval env.toEnvironment input_var_offset_bit[0], Expression.eval env.toEnvironment input_var_offset_bit[1]⟩ : AddressOperation.Inputs (ZMod p)) := ⟨ha, hb, hfit, Or.inl rfl, hob0', hob1', h_ge, h_off'⟩ refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · exact hbin · exact h_it - · simp only [esv0, epv0, eoap0, eob0, eob1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr0 - · simp only [esv1, epv1, eoap0, eob0, eob1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr1 - · simp only [esv2, epv2, eoap0, eob0, eob1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr2 - · simp only [esv3, epv3, eoap0, eob0, eob1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr3 + · simp only [esv 0 (by omega), epv 0 (by omega), eoap0, eob 0 (by omega), eob 1 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr0 + · simp only [esv 1 (by omega), epv 1 (by omega), eoap0, eob 0 (by omega), eob 1 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr1 + · simp only [esv 2 (by omega), epv 2 (by omega), eoap0, eob 0 (by omega), eob 1 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr2 + · simp only [esv 3 (by omega), epv 3 (by omega), eoap0, eob 0 (by omega), eob 1 (by omega), + ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr3 · rcases hbin with h | h <;> rw [h] <;> simp /-- The `StoreHalf` chip row as a `GeneralFormalCircuit`; output is the extracted `StoreHalfColumns`. -/ diff --git a/SP1Clean/Proofs/Chips/StoreWordChip/Bridge.lean b/SP1Clean/Proofs/Chips/StoreWordChip/Bridge.lean index d83cdead..48b8c11d 100644 --- a/SP1Clean/Proofs/Chips/StoreWordChip/Bridge.lean +++ b/SP1Clean/Proofs/Chips/StoreWordChip/Bridge.lean @@ -71,9 +71,9 @@ theorem correct_store_word_native h_htif_disabled := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hhtif h_pma_regions := by rw [key _ (hs _) (hsp_init _) (by decide)]; exact hpma } have hsp_rs1 : sp.get_reg? rs1_idx = some reg_val := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs1 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hsp_rs2 : sp.get_reg? rs2_idx = some stored := by - rw [hsp, SailState.get_reg?_insert_nextPC]; exact h_rs2 + rwa [hsp, SailState.get_reg?_insert_nextPC] have hadd : (reg_val + BitVec.signExtend 64 imm).toNat = reg_val.toNat + (BitVec.signExtend 64 imm).toNat := by rw [BitVec.toNat_add, Nat.mod_eq_of_lt]; omega @@ -86,10 +86,6 @@ theorem correct_store_word_native have hwrite := run_vmem_write_of_width_4 rs1_idx reg_val (BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 31 0) sp hsp_init hsp_rs1 h_align' hsp_config h_does_fit h_in_range simp only [hmem_eq] at hwrite - have hrx2 : rX_bits (regidx.Regidx rs2_idx) sp = .ok stored sp := by - have h := @run_rX_bits rs2_idx sp - simp only [EStateM.run] at h - rw [h, hsp_rs2] have hsp1 : (sp1_sw pc (reg_val + BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb stored 31 0)).run s = .ok RETIRE_SUCCESS { sp with mem := @@ -128,7 +124,7 @@ theorem sw_chip_reaches_sail = (sp1_sw pc (Word.toBitVec64 input.op_b_val + BitVec.signExtend 64 imm) (Sail.BitVec.extractLsb (Word.toBitVec64 input.adapter.op_a_memory.prev_value) 31 0)).run s := by haveI : NeZero p := ⟨(Fact.out (p := p.Prime)).pos.ne'⟩ - obtain ⟨h_b, h_c, _h_fits48, h_nonres, h_align4, _h_off⟩ := h_assum + obtain ⟨h_b, h_c, _h_fits48, _h_nonres, h_align4, _h_off⟩ := h_assum have hreg : (Word.toBitVec64 input.op_b_val).toNat = Word.toNat input.op_b_val := Word.toBitVec64_toNat h_b have hoff : (BitVec.signExtend 64 imm).toNat = Word.toNat input.op_c_imm := by diff --git a/SP1Clean/Proofs/Chips/StoreWordChip/Formal.lean b/SP1Clean/Proofs/Chips/StoreWordChip/Formal.lean index 68d0fdb3..39277250 100644 --- a/SP1Clean/Proofs/Chips/StoreWordChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/StoreWordChip/Formal.lean @@ -37,28 +37,15 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe = input_memory_access_prev_value := h_input.2.2.2.1.1 have hmap_oap : Vector.map (Expression.eval env) input_var_adapter_op_a_memory_prev_value = input_adapter_op_a_memory_prev_value := h_input.2.2.1.2.1.1 - have esv0 : Expression.eval env input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have eoap0 : Expression.eval env input_var_adapter_op_a_memory_prev_value[0] - = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] - have eoap1 : Expression.eval env input_var_adapter_op_a_memory_prev_value[1] - = input_adapter_op_a_memory_prev_value[1] := by rw [← hmap_oap]; simp only [Vector.getElem_map] - simp only [esv0, esv1, esv2, esv3, epv0, epv1, epv2, epv3, eoap0, eoap1, ← sub_eq_add_neg] - at hr0 hr1 hr2 hr3 + have esv : ∀ i (hi : i < 4), Expression.eval env input_var_store_value[i] = input_store_value[i] := + fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + have eoap : ∀ i (hi : i < 2), Expression.eval env input_var_adapter_op_a_memory_prev_value[i] + = input_adapter_op_a_memory_prev_value[i] := fun i hi => by rw [← hmap_oap]; simp only [Vector.getElem_map] + simp only [esv 0 (by omega), esv 1 (by omega), esv 2 (by omega), esv 3 (by omega), + epv 0 (by omega), epv 1 (by omega), epv 2 (by omega), epv 3 (by omega), + eoap 0 (by omega), eoap 1 (by omega), ← sub_eq_add_neg] at hr0 hr1 hr2 hr3 have h_it := h_itype h_bin have h_off' : (0 : ZMod p).val + 2 * (0 : ZMod p).val + 4 * input_offset_bit.val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by @@ -119,32 +106,14 @@ theorem completeness : = input_memory_access_prev_value := h_input.2.2.2.1.1 have hmap_oap : Vector.map (Expression.eval env.toEnvironment) input_var_adapter_op_a_memory_prev_value = input_adapter_op_a_memory_prev_value := h_input.2.2.1.2.1.1 - have epc0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have epc2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hmap_pc]; simp only [Vector.getElem_map] - have esv0 : Expression.eval env.toEnvironment input_var_store_value[0] = input_store_value[0] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv1 : Expression.eval env.toEnvironment input_var_store_value[1] = input_store_value[1] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv2 : Expression.eval env.toEnvironment input_var_store_value[2] = input_store_value[2] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have esv3 : Expression.eval env.toEnvironment input_var_store_value[3] = input_store_value[3] := by - rw [← hmap_sv]; simp only [Vector.getElem_map] - have epv0 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[0] - = input_memory_access_prev_value[0] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv1 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[1] - = input_memory_access_prev_value[1] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv2 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[2] - = input_memory_access_prev_value[2] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have epv3 : Expression.eval env.toEnvironment input_var_memory_access_prev_value[3] - = input_memory_access_prev_value[3] := by rw [← hmap_pv]; simp only [Vector.getElem_map] - have eoap0 : Expression.eval env.toEnvironment input_var_adapter_op_a_memory_prev_value[0] - = input_adapter_op_a_memory_prev_value[0] := by rw [← hmap_oap]; simp only [Vector.getElem_map] - have eoap1 : Expression.eval env.toEnvironment input_var_adapter_op_a_memory_prev_value[1] - = input_adapter_op_a_memory_prev_value[1] := by rw [← hmap_oap]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env.toEnvironment input_var_state_pc[i] + = input_state_pc[i] := fun i hi => by rw [← hmap_pc]; simp only [Vector.getElem_map] + have esv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_store_value[i] + = input_store_value[i] := fun i hi => by rw [← hmap_sv]; simp only [Vector.getElem_map] + have epv : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_memory_access_prev_value[i] + = input_memory_access_prev_value[i] := fun i hi => by rw [← hmap_pv]; simp only [Vector.getElem_map] + have eoap : ∀ i (hi : i < 2), Expression.eval env.toEnvironment input_var_adapter_op_a_memory_prev_value[i] + = input_adapter_op_a_memory_prev_value[i] := fun i hi => by rw [← hmap_oap]; simp only [Vector.getElem_map] have h_off' : (0 : ZMod p).val + 2 * (0 : ZMod p).val + 4 * input_offset_bit.val = (Word.toNat input_adapter_op_b_memory_prev_value + Word.toNat input_adapter_op_c_imm) % 2 ^ 48 % 8 := by simp only [ZMod.val_zero]; omega @@ -160,15 +129,19 @@ theorem completeness : ⟨ha, hb, hfit, Or.inl rfl, Or.inl rfl, h_off_bin, h_ge, h_off'⟩ refine ⟨⟨?_, ?_⟩, h_addr_as, ⟨?_, ?_⟩, ⟨?_, ?_⟩, ?_, ?_, ?_, ?_, ?_⟩ · exact hbin - · simp only [epc0, epc1, epc2]; exact h_cpu + · simp only [epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; exact h_cpu · exact hbin · exact h_mem · exact hbin · exact h_it - · simp only [esv0, epv0, eoap0, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr0 - · simp only [esv1, epv1, eoap1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr1 - · simp only [esv2, epv2, eoap0, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr2 - · simp only [esv3, epv3, eoap1, ← sub_eq_add_neg]; exact sub_eq_zero_of_eq hr3 + · simp only [esv 0 (by omega), epv 0 (by omega), eoap 0 (by omega), ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr0 + · simp only [esv 1 (by omega), epv 1 (by omega), eoap 1 (by omega), ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr1 + · simp only [esv 2 (by omega), epv 2 (by omega), eoap 0 (by omega), ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr2 + · simp only [esv 3 (by omega), epv 3 (by omega), eoap 1 (by omega), ← sub_eq_add_neg] + exact sub_eq_zero_of_eq hr3 · rcases hbin with h | h <;> rw [h] <;> simp /-- The `StoreWord` chip row as a `GeneralFormalCircuit`; output is the extracted `StoreWordColumns`. -/ diff --git a/SP1Clean/Proofs/Chips/SubChip/Formal.lean b/SP1Clean/Proofs/Chips/SubChip/Formal.lean index 18d716d0..d53f51fb 100644 --- a/SP1Clean/Proofs/Chips/SubChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/SubChip/Formal.lean @@ -31,18 +31,13 @@ theorem completeness : obtain ⟨ha, hb, hbin, hop_a_0, h_cpu, hrac_a, hrac_b, hrac_c⟩ := h_assumptions obtain ⟨-, -, -, -, -, -, ⟨hob, -, -⟩, -, hoc, -, -⟩ := h_input have hz : ∀ w : ZMod p, input_adapter_op_a_0 * w = 0 := fun w => by rw [hop_a_0, zero_mul] - have hbeq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_b_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_b_memory_prev_value := by - rw [← hob]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl - have hceq : (#v[Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[0], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[1], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[2], - Expression.eval env.toEnvironment input_var_adapter_op_c_memory_prev_value[3]] : Word (ZMod p)) - = input_adapter_op_c_memory_prev_value := by - rw [← hoc]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have mapEq : ∀ (vv : Word (Expression (ZMod p))) (v : Word (ZMod p)), + Vector.map (Expression.eval env.toEnvironment) vv = v → + (#v[Expression.eval env.toEnvironment vv[0], Expression.eval env.toEnvironment vv[1], + Expression.eval env.toEnvironment vv[2], Expression.eval env.toEnvironment vv[3]] : Word (ZMod p)) = v := + fun vv v h => by rw [← h]; apply Vector.ext; intro i hi; simp only [Vector.getElem_map]; interval_cases i <;> rfl + have hbeq := mapEq input_var_adapter_op_b_memory_prev_value _ hob + have hceq := mapEq input_var_adapter_op_c_memory_prev_value _ hoc have hval : (Vector.map (Expression.eval env.toEnvironment) (Vector.mapRange 4 fun i => var {index := i₀ + i}) : Word (ZMod p)) = SubOperation.populate input_adapter_op_b_memory_prev_value input_adapter_op_c_memory_prev_value := by diff --git a/SP1Clean/Proofs/Chips/UTypeChip/Formal.lean b/SP1Clean/Proofs/Chips/UTypeChip/Formal.lean index 5d51e681..adc08d5b 100644 --- a/SP1Clean/Proofs/Chips/UTypeChip/Formal.lean +++ b/SP1Clean/Proofs/Chips/UTypeChip/Formal.lean @@ -63,18 +63,14 @@ theorem soundness : GeneralFormalCircuit.Soundness (ZMod p) main Assumptions Spe have h_jt := h_jt0 h_bin have h_op_a_0 : input_adapter_op_a_0 = 0 ∨ input_adapter_op_a_0 = 1 := h_jt.2.1 have hpc : Vector.map (Expression.eval env) input_var_state_pc = input_state_pc := h_input.2.1.2.2.2 - have ep0 : Expression.eval env input_var_state_pc[0] = input_state_pc[0] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep1 : Expression.eval env input_var_state_pc[1] = input_state_pc[1] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep2 : Expression.eval env input_var_state_pc[2] = input_state_pc[2] := by - rw [← hpc]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), Expression.eval env input_var_state_pc[i] = input_state_pc[i] := + fun i hi => by rw [← hpc]; simp only [Vector.getElem_map] have ha0 : env.get i₀ = input_is_auipc * input_state_pc[0] := by - rw [ep0] at h_ad0; exact add_neg_eq_zero.mp h_ad0 + rw [epc 0 (by omega)] at h_ad0; exact add_neg_eq_zero.mp h_ad0 have ha1 : env.get (i₀ + 1) = input_is_auipc * input_state_pc[1] := by - rw [ep1] at h_ad1; exact add_neg_eq_zero.mp h_ad1 + rw [epc 1 (by omega)] at h_ad1; exact add_neg_eq_zero.mp h_ad1 have ha2 : env.get (i₀ + 2) = input_is_auipc * input_state_pc[2] := by - rw [ep2] at h_ad2; exact add_neg_eq_zero.mp h_ad2 + rw [epc 2 (by omega)] at h_ad2; exact add_neg_eq_zero.mp h_ad2 have haddend0 : input_is_auipc = 0 → (#v[env.get i₀, env.get (i₀ + 1), env.get (i₀ + 2), 0] : Word (ZMod p)) = #v[0, 0, 0, 0] := by intro h; rw [ha0, ha1, ha2, h]; simp @@ -112,12 +108,9 @@ theorem completeness : obtain ⟨h_imm, h_pcU, h_bin, h_iaui, h_op0, h_cpu, h_rac, _h_dec⟩ := h_assumptions obtain ⟨_, ⟨_, _, _, hpc⟩, ⟨_, _, _, hob, _⟩, _⟩ := h_input obtain ⟨he_addend, he_addval⟩ := h_env - have ep0 : Expression.eval env.toEnvironment input_var_state_pc[0] = input_state_pc[0] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep1 : Expression.eval env.toEnvironment input_var_state_pc[1] = input_state_pc[1] := by - rw [← hpc]; simp only [Vector.getElem_map] - have ep2 : Expression.eval env.toEnvironment input_var_state_pc[2] = input_state_pc[2] := by - rw [← hpc]; simp only [Vector.getElem_map] + have epc : ∀ i (hi : i < 3), + Expression.eval env.toEnvironment input_var_state_pc[i] = input_state_pc[i] := + fun i hi => by rw [← hpc]; simp only [Vector.getElem_map] have hg0 : env.get i₀ = input_is_auipc * Expression.eval env.toEnvironment input_var_state_pc[0] := by simpa using he_addend 0 have hg1 : env.get (i₀ + 1) = input_is_auipc * Expression.eval env.toEnvironment input_var_state_pc[1] := by @@ -149,7 +142,7 @@ theorem completeness : rw [e]; refine Word.isU64_of_cases ?_ ?_ ?_ ?_ <;> simp · have e : (#v[env.get i₀, env.get (i₀ + 1), env.get (i₀ + 2), 0] : Word (ZMod p)) = #v[input_state_pc[0], input_state_pc[1], input_state_pc[2], 0] := by - rw [hg0, hg1, hg2, h, ep0, ep1, ep2]; simp + rw [hg0, hg1, hg2, h, epc 0 (by omega), epc 1 (by omega), epc 2 (by omega)]; simp rw [e]; exact h_pcU have h_gate2 : input_is_real + -input_adapter_op_a_0 = 0 ∨ input_is_real + -input_adapter_op_a_0 = 1 := by rw [h_op0]; simpa using h_bin diff --git a/SP1Clean/Proofs/Operations/AddOperation/Formal.lean b/SP1Clean/Proofs/Operations/AddOperation/Formal.lean index a9111bd2..f6f4512d 100644 --- a/SP1Clean/Proofs/Operations/AddOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/AddOperation/Formal.lean @@ -29,24 +29,10 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨hia, hib, hiv, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast have h65536 : (2 : ℕ) ^ 16 = 65536 := by norm_num - -- vector-indexed inputs need manual eval rewrites (circuit_norm only handles scalar is_real). - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev2 : Expression.eval env input_var_cols_value[2] = input_cols_value[2] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev3 : Expression.eval env input_var_cols_value[3] = input_cols_value[3] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3, ev0, ev1, ev2, ev3] - at h_holds ⊢ + have ev {w : Word (Expression (ZMod p))} {v : Word (ZMod p)} (h : Vector.map (Expression.eval env) w = v) : + ∀ i (_ : i < 4), Expression.eval env w[i] = v[i] := fun i _ => by rw [← h, Vector.getElem_map] + simp only [circuit_norm, byteChannel, ev hia, ev hib, ev hiv] at h_holds ⊢ obtain ⟨hr0, hr1, hr2, hr3, _hbool, hgc0, hgc1, hgc2, hgc3⟩ := h_holds - -- `circuit_norm` already discharged the carry asserts' (empty) requirements, and post-#398 the byte - -- receives owe no padding requirement — the goal is exactly `Spec`. intro hr1eq obtain ⟨ha, hb⟩ := hab_imp hr1eq have hneg : -input_is_real = -1 := by rw [hr1eq] @@ -67,42 +53,20 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨hab_imp, hbin⟩ := h_assumptions obtain ⟨hia, hib, hiv, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env.toEnvironment input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env.toEnvironment input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev2 : Expression.eval env.toEnvironment input_var_cols_value[2] = input_cols_value[2] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev3 : Expression.eval env.toEnvironment input_var_cols_value[3] = input_cols_value[3] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3, ev0, ev1, ev2, ev3] + have ev {w : Word (Expression (ZMod p))} {v : Word (ZMod p)} + (h : Vector.map (Expression.eval env.toEnvironment) w = v) : + ∀ i (_ : i < 4), Expression.eval env.toEnvironment w[i] = v[i] := + fun i _ => by rw [← h, Vector.getElem_map] + simp only [circuit_norm, byteChannel, ev hia, ev hib, ev hiv] refine ⟨?_, ?_, ?_, ?_, ?_⟩ - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨hv0, _, _, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv0 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, hv1, _, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv1 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, _, hv2, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv2 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, _, _, hv3⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv3 - -- four gated carry asserts: vacuous on padding; from `carries_of_addSemantics` on real rows. - -- Carries arrive in `+ -` form; normalise with `sub_eq_add_neg`. + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.2.1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.2.2 · rcases hbin with h0 | h1 · simp [h0] · obtain ⟨hv, hbv⟩ := h_spec h1 diff --git a/SP1Clean/Proofs/Operations/AddwOperation/Formal.lean b/SP1Clean/Proofs/Operations/AddwOperation/Formal.lean index 95c34f7a..8482ac6b 100644 --- a/SP1Clean/Proofs/Operations/AddwOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/AddwOperation/Formal.lean @@ -40,13 +40,14 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨hia, hib, ⟨hiv, _⟩, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast have h65536 : (2 : ℕ) ^ 16 = 65536 := by norm_num - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, eb0, eb1, ev0, ev1] at h_holds ⊢ + have ea : ∀ i (hi : i < 4), Expression.eval env input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ev : ∀ i (hi : i < 2), + Expression.eval env input_var_cols_value[i] = input_cols_value[i] := by + intro i hi; rw [← hiv]; simp only [Vector.getElem_map] + simp only [circuit_norm, byteChannel, ea, eb, ev] at h_holds ⊢ obtain ⟨h_msb, hr0, hr1, _hgate, hgc0, hgc1⟩ := h_holds -- the U16MSB sub-assertion's `Assumptions` (gated `value[1] < 2^16` from the `value[1]` byte pull). have h_msb_as : U16MSBOperation.circuit.Assumptions @@ -77,12 +78,13 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨ha, hb, hbin⟩ := h_assumptions obtain ⟨hia, hib, ⟨hiv, _⟩, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast - have ev0 : Expression.eval env.toEnvironment input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env.toEnvironment input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] + have ev : ∀ i (hi : i < 2), + Expression.eval env.toEnvironment input_var_cols_value[i] = input_cols_value[i] := by + intro i hi; rw [← hiv]; simp only [Vector.getElem_map] + have ea : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] -- the converse facts, all valid only on real rows have key : input_is_real = 1 → (input_cols_value[0].val < 2 ^ 16 ∧ input_cols_value[1].val < 2 ^ 16) ∧ @@ -95,7 +97,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp List.getElem_cons_zero, List.getElem_cons_succ] at hUv0 hUv1 obtain ⟨h_raw, h_msbval⟩ := carries_of_addwSemantics ha hb hU hbveq exact ⟨⟨hUv0, hUv1⟩, h_raw, h_msbval⟩ - simp only [circuit_norm, byteChannel, ea0, ea1, eb0, eb1, ev0, ev1] + simp only [circuit_norm, byteChannel, ea, eb, ev] refine ⟨⟨⟨fun hr1 => (key hr1).1.2, hbin⟩, ⟨h_spec.1, fun hr1 => (key hr1).2.2⟩⟩, ?_, ?_, ?_, ?_, ?_⟩ · intro hneg have hr1 : input_is_real = 1 := neg_inj.mp hneg diff --git a/SP1Clean/Proofs/Operations/BitwiseOperation/Formal.lean b/SP1Clean/Proofs/Operations/BitwiseOperation/Formal.lean index 63c6fe25..c00eb6eb 100644 --- a/SP1Clean/Proofs/Operations/BitwiseOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/BitwiseOperation/Formal.lean @@ -27,33 +27,14 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := circuit_proof_start obtain ⟨hopcode, _hbin⟩ := h_assumptions obtain ⟨hia, hib, hir, _, _⟩ := h_input - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have ea4 : Expression.eval env input_var_a[4] = input_a[4] := by rw [← hia]; simp only [Vector.getElem_map] - have ea5 : Expression.eval env input_var_a[5] = input_a[5] := by rw [← hia]; simp only [Vector.getElem_map] - have ea6 : Expression.eval env input_var_a[6] = input_a[6] := by rw [← hia]; simp only [Vector.getElem_map] - have ea7 : Expression.eval env input_var_a[7] = input_a[7] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have eb4 : Expression.eval env input_var_b[4] = input_b[4] := by rw [← hib]; simp only [Vector.getElem_map] - have eb5 : Expression.eval env input_var_b[5] = input_b[5] := by rw [← hib]; simp only [Vector.getElem_map] - have eb6 : Expression.eval env input_var_b[6] = input_b[6] := by rw [← hib]; simp only [Vector.getElem_map] - have eb7 : Expression.eval env input_var_b[7] = input_b[7] := by rw [← hib]; simp only [Vector.getElem_map] - have er0 : Expression.eval env input_var_cols_result[0] = input_cols_result[0] := by rw [← hir]; simp only [Vector.getElem_map] - have er1 : Expression.eval env input_var_cols_result[1] = input_cols_result[1] := by rw [← hir]; simp only [Vector.getElem_map] - have er2 : Expression.eval env input_var_cols_result[2] = input_cols_result[2] := by rw [← hir]; simp only [Vector.getElem_map] - have er3 : Expression.eval env input_var_cols_result[3] = input_cols_result[3] := by rw [← hir]; simp only [Vector.getElem_map] - have er4 : Expression.eval env input_var_cols_result[4] = input_cols_result[4] := by rw [← hir]; simp only [Vector.getElem_map] - have er5 : Expression.eval env input_var_cols_result[5] = input_cols_result[5] := by rw [← hir]; simp only [Vector.getElem_map] - have er6 : Expression.eval env input_var_cols_result[6] = input_cols_result[6] := by rw [← hir]; simp only [Vector.getElem_map] - have er7 : Expression.eval env input_var_cols_result[7] = input_cols_result[7] := by rw [← hir]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, ea4, ea5, ea6, ea7, - eb0, eb1, eb2, eb3, eb4, eb5, eb6, eb7, - er0, er1, er2, er3, er4, er5, er6, er7] at h_holds ⊢ + have ea : ∀ i (hi : i < 8), Expression.eval env input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 8), Expression.eval env input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have er : ∀ i (hi : i < 8), + Expression.eval env input_var_cols_result[i] = input_cols_result[i] := by + intro i hi; rw [← hir]; simp only [Vector.getElem_map] + simp only [circuit_norm, byteChannel, ea, eb, er] at h_holds ⊢ obtain ⟨hg0, hg1, hg2, hg3, hg4, hg5, hg6, hg7⟩ := h_holds -- post-#398 the byte receives owe no padding requirement, so the goal is exactly `Spec`. intro h1 @@ -85,30 +66,13 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨hia, hib, hir, _, _⟩ := h_input have hp : 2 ^ 17 < p := Fact.out haveI : NeZero p := ⟨by omega⟩ - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have ea4 : Expression.eval env.toEnvironment input_var_a[4] = input_a[4] := by rw [← hia]; simp only [Vector.getElem_map] - have ea5 : Expression.eval env.toEnvironment input_var_a[5] = input_a[5] := by rw [← hia]; simp only [Vector.getElem_map] - have ea6 : Expression.eval env.toEnvironment input_var_a[6] = input_a[6] := by rw [← hia]; simp only [Vector.getElem_map] - have ea7 : Expression.eval env.toEnvironment input_var_a[7] = input_a[7] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have eb4 : Expression.eval env.toEnvironment input_var_b[4] = input_b[4] := by rw [← hib]; simp only [Vector.getElem_map] - have eb5 : Expression.eval env.toEnvironment input_var_b[5] = input_b[5] := by rw [← hib]; simp only [Vector.getElem_map] - have eb6 : Expression.eval env.toEnvironment input_var_b[6] = input_b[6] := by rw [← hib]; simp only [Vector.getElem_map] - have eb7 : Expression.eval env.toEnvironment input_var_b[7] = input_b[7] := by rw [← hib]; simp only [Vector.getElem_map] - have er0 : Expression.eval env.toEnvironment input_var_cols_result[0] = input_cols_result[0] := by rw [← hir]; simp only [Vector.getElem_map] - have er1 : Expression.eval env.toEnvironment input_var_cols_result[1] = input_cols_result[1] := by rw [← hir]; simp only [Vector.getElem_map] - have er2 : Expression.eval env.toEnvironment input_var_cols_result[2] = input_cols_result[2] := by rw [← hir]; simp only [Vector.getElem_map] - have er3 : Expression.eval env.toEnvironment input_var_cols_result[3] = input_cols_result[3] := by rw [← hir]; simp only [Vector.getElem_map] - have er4 : Expression.eval env.toEnvironment input_var_cols_result[4] = input_cols_result[4] := by rw [← hir]; simp only [Vector.getElem_map] - have er5 : Expression.eval env.toEnvironment input_var_cols_result[5] = input_cols_result[5] := by rw [← hir]; simp only [Vector.getElem_map] - have er6 : Expression.eval env.toEnvironment input_var_cols_result[6] = input_cols_result[6] := by rw [← hir]; simp only [Vector.getElem_map] - have er7 : Expression.eval env.toEnvironment input_var_cols_result[7] = input_cols_result[7] := by rw [← hir]; simp only [Vector.getElem_map] + have ea : ∀ i (hi : i < 8), Expression.eval env.toEnvironment input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 8), Expression.eval env.toEnvironment input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have er : ∀ i (hi : i < 8), + Expression.eval env.toEnvironment input_var_cols_result[i] = input_cols_result[i] := by + intro i hi; rw [← hir]; simp only [Vector.getElem_map] have key : input_is_real = 1 → ∀ i : Fin 8, ByteRowSpec (⟨input_opcode, input_cols_result[↑i], input_a[↑i], input_b[↑i]⟩ : ByteRow (ZMod p)) := by intro hr1 i @@ -123,9 +87,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp · rw [h, byteOp_two]; exact hxor (by rw [← hcast, h]; norm_num) i exact (byteRowSpec_byteOp _ _ _ hopcode).mpr ⟨⟨by rw [hres]; exact byteOp_lt256 _ _ _ hb.1 hb.2, hb.1, hb.2⟩, hres⟩ - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, ea4, ea5, ea6, ea7, - eb0, eb1, eb2, eb3, eb4, eb5, eb6, eb7, - er0, er1, er2, er3, er4, er5, er6, er7] + simp only [circuit_norm, byteChannel, ea, eb, er] refine ⟨fun hneg => ?_, fun hneg => ?_, fun hneg => ?_, fun hneg => ?_, fun hneg => ?_, fun hneg => ?_, fun hneg => ?_, fun hneg => ?_⟩ · exact key (neg_inj.mp hneg) 0 diff --git a/SP1Clean/Proofs/Operations/IsEqualWordOperation/Formal.lean b/SP1Clean/Proofs/Operations/IsEqualWordOperation/Formal.lean index 1726695d..8700011b 100644 --- a/SP1Clean/Proofs/Operations/IsEqualWordOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/IsEqualWordOperation/Formal.lean @@ -37,38 +37,19 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := circuit_proof_start obtain ⟨hsub, _hbool⟩ := h_holds obtain ⟨hia, hib, -⟩ := h_input - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp [Vector.getElem_map] - have ea2 : Expression.eval env input_var_a[2] = input_a[2] := by rw [← hia]; simp [Vector.getElem_map] - have ea3 : Expression.eval env input_var_a[3] = input_a[3] := by rw [← hia]; simp [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp [Vector.getElem_map] have S := hsub h_assumptions - simp only [ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3] at S refine ⟨?_, Or.inl rfl⟩ - simp only [diff, sub_eq_add_neg] - exact S + simpa only [diff, ← hia, ← hib, Vector.getElem_map, sub_eq_add_neg] using S omit [Fact (2 ^ 17 < p)] in set_option maxHeartbeats 1000000 in theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Spec := by circuit_proof_start obtain ⟨hia, hib, -⟩ := h_input - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_a[2] = input_a[2] := by rw [← hia]; simp [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_a[3] = input_a[3] := by rw [← hia]; simp [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp [Vector.getElem_map] refine ⟨⟨h_assumptions, ?_⟩, ?_⟩ - · simp only [ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3] - have hs := h_spec + · have hs := h_spec simp only [diff, sub_eq_add_neg] at hs - exact hs + simpa only [← hia, ← hib, Vector.getElem_map] using hs · rcases h_assumptions with h | h <;> simp [h] omit [Fact (2 ^ 17 < p)] in diff --git a/SP1Clean/Proofs/Operations/IsZeroOperation/Formal.lean b/SP1Clean/Proofs/Operations/IsZeroOperation/Formal.lean index 7ef9ed09..b24dced3 100644 --- a/SP1Clean/Proofs/Operations/IsZeroOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/IsZeroOperation/Formal.lean @@ -34,10 +34,8 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := intro hr1 simp only [circuit_norm, hr1, one_mul] at h_holds obtain ⟨h_eq, h_bool, h_mul⟩ := h_holds - have hA : AssertSpec input_a ⟨input_cols_inverse, input_cols_result⟩ := by - refine ⟨?_, bool_of_mul_pred h_bool, h_mul⟩ - show (1 : ZMod p) - input_cols_inverse * input_a - input_cols_result = 0 - simp only [sub_eq_add_neg]; exact h_eq + have hA : AssertSpec input_a ⟨input_cols_inverse, input_cols_result⟩ := + ⟨by simpa [sub_eq_add_neg] using h_eq, bool_of_mul_pred h_bool, h_mul⟩ exact ⟨isZero_of_assert hA, fun ha => inverse_of_assert hA ha⟩ omit [Fact (2 ^ 17 < p)] in @@ -49,8 +47,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp · obtain ⟨hres, hinv⟩ := h_spec h1 simp only [circuit_norm, h1, one_mul] refine ⟨?_, ?_, ?_⟩ - · -- 1 - inverse*a - result = 0 - by_cases ha : input_a = 0 + · by_cases ha : input_a = 0 · simp [hres, ha] · rw [hres, if_neg ha, hinv ha]; simp · rw [hres]; by_cases ha : input_a = 0 <;> simp [ha] @@ -63,9 +60,9 @@ theorem spec_populate (a is_real : ZMod p) : Spec (⟨a, populate a, is_real⟩ : Inputs (ZMod p)) := by intro _ by_cases ha : a = 0 - · subst ha; refine ⟨by simp [populate], ?_⟩; intro hne; exact absurd rfl hne - · refine ⟨by simp [populate, ha], ?_⟩ - intro _; simp only [populate, if_neg ha]; exact inv_mul_cancel₀ ha + · subst ha; exact ⟨by simp [populate], fun hne => absurd rfl hne⟩ + · refine ⟨by simp [populate, ha], fun _ => ?_⟩ + simp only [populate, if_neg ha]; exact inv_mul_cancel₀ ha /-- SP1's `IsZeroOperation::eval` as a Clean-native `FormalAssertion`. -/ def circuit : FormalAssertion (ZMod p) Inputs := diff --git a/SP1Clean/Proofs/Operations/IsZeroWordOperation/Formal.lean b/SP1Clean/Proofs/Operations/IsZeroWordOperation/Formal.lean index df02d4eb..872a8b65 100644 --- a/SP1Clean/Proofs/Operations/IsZeroWordOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/IsZeroWordOperation/Formal.lean @@ -42,14 +42,12 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := circuit_proof_start obtain ⟨hs0, hs1, hs2, hs3, _hbreal, hbres, hf, hsec, hg⟩ := h_holds obtain ⟨hia, -⟩ := h_input - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp [Vector.getElem_map] - have ea2 : Expression.eval env input_var_a[2] = input_a[2] := by rw [← hia]; simp [Vector.getElem_map] - have ea3 : Expression.eval env input_var_a[3] = input_a[3] := by rw [← hia]; simp [Vector.getElem_map] - have S0 := hs0 h_assumptions; rw [ea0] at S0 - have S1 := hs1 h_assumptions; rw [ea1] at S1 - have S2 := hs2 h_assumptions; rw [ea2] at S2 - have S3 := hs3 h_assumptions; rw [ea3] at S3 + have ea : ∀ i (hi : i < 4), Expression.eval env input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp [Vector.getElem_map] + have S0 := hs0 h_assumptions; rw [ea 0 (by norm_num)] at S0 + have S1 := hs1 h_assumptions; rw [ea 1 (by norm_num)] at S1 + have S2 := hs2 h_assumptions; rw [ea 2 (by norm_num)] at S2 + have S3 := hs3 h_assumptions; rw [ea 3 (by norm_num)] at S3 rw [← sub_eq_add_neg] at hf hsec refine ⟨⟨bool_of_mul_pred hbres, eq_of_sub_eq_zero hf, eq_of_sub_eq_zero hsec, S0, S1, S2, S3, ?_⟩, Or.inl rfl, Or.inl rfl, Or.inl rfl, Or.inl rfl⟩ @@ -63,15 +61,13 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp circuit_proof_start obtain ⟨hbres, hf, hsec, hS0, hS1, hS2, hS3, hg⟩ := h_spec obtain ⟨hia, -⟩ := h_input - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_a[2] = input_a[2] := by rw [← hia]; simp [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_a[3] = input_a[3] := by rw [← hia]; simp [Vector.getElem_map] + have ea : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp [Vector.getElem_map] refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ - · exact ⟨h_assumptions, by rw [ea0]; exact hS0⟩ - · exact ⟨h_assumptions, by rw [ea1]; exact hS1⟩ - · exact ⟨h_assumptions, by rw [ea2]; exact hS2⟩ - · exact ⟨h_assumptions, by rw [ea3]; exact hS3⟩ + · exact ⟨h_assumptions, by rw [ea 0 (by norm_num)]; exact hS0⟩ + · exact ⟨h_assumptions, by rw [ea 1 (by norm_num)]; exact hS1⟩ + · exact ⟨h_assumptions, by rw [ea 2 (by norm_num)]; exact hS2⟩ + · exact ⟨h_assumptions, by rw [ea 3 (by norm_num)]; exact hS3⟩ · rcases h_assumptions with h | h <;> simp [h] · rcases hbres with h | h <;> simp [h] · simp [hf] @@ -80,18 +76,24 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp · simp [h] · rw [h, one_mul, hg h]; simp +omit [Fact (2 ^ 17 < p)] in +/-- The populated `result` is boolean for **any** source word (a product of per-limb `0/1` +indicators) — the ungated structural fact `spec_populate_offGate` needs. -/ +theorem populate_result_bool (w : Word (ZMod p)) : + (populate w).result = 0 ∨ (populate w).result = 1 := by + simp only [populate, IsZeroOperation.populate] + by_cases h0 : w[0] = 0 <;> by_cases h1 : w[1] = 0 <;> by_cases h2 : w[2] = 0 <;> + by_cases h3 : w[3] = 0 <;> simp [h0, h1, h2, h3] + omit [Fact (2 ^ 17 < p)] in /-- The witnessed columns `populate a` satisfy the gadget `Spec` for any `is_real`. The composing op (`IsEqualWord`) / top-level chip uses this to discharge the `assertion IsZeroWordOperation.circuit` prover obligation. -/ theorem spec_populate (a : Word (ZMod p)) (is_real : ZMod p) : - Spec (⟨a, populate a, is_real⟩ : Inputs (ZMod p)) := by - refine ⟨?_, rfl, rfl, IsZeroOperation.spec_populate a[0] is_real, + Spec (⟨a, populate a, is_real⟩ : Inputs (ZMod p)) := + ⟨populate_result_bool a, rfl, rfl, IsZeroOperation.spec_populate a[0] is_real, IsZeroOperation.spec_populate a[1] is_real, IsZeroOperation.spec_populate a[2] is_real, IsZeroOperation.spec_populate a[3] is_real, fun _ => rfl⟩ - simp only [populate, IsZeroOperation.populate] - by_cases h0 : a[0] = 0 <;> by_cases h1 : a[1] = 0 <;> by_cases h2 : a[2] = 0 <;> - by_cases h3 : a[3] = 0 <;> simp [h0, h1, h2, h3] omit [Fact (2 ^ 17 < p)] in /-- Semantic exposure: on a real row the `Spec` forces `result` to be the word zero-indicator. -/ @@ -101,15 +103,6 @@ theorem result_semantic {input : Inputs (ZMod p)} (h : Spec input) (hr : input.i obtain ⟨_, hf, hs, hS0, hS1, hS2, hS3, hg⟩ := h exact result_collapse (hS0 hr).1 (hS1 hr).1 (hS2 hr).1 (hS3 hr).1 hf hs (hg hr) -omit [Fact (2 ^ 17 < p)] in -/-- The populated `result` is boolean for **any** source word (a product of per-limb `0/1` -indicators) — the ungated structural fact `spec_populate_offGate` needs. -/ -theorem populate_result_bool (w : Word (ZMod p)) : - (populate w).result = 0 ∨ (populate w).result = 1 := by - simp only [populate, IsZeroOperation.populate] - by_cases h0 : w[0] = 0 <;> by_cases h1 : w[1] = 0 <;> by_cases h2 : w[2] = 0 <;> - by_cases h3 : w[3] = 0 <;> simp [h0, h1, h2, h3] - omit [Fact (2 ^ 17 < p)] in /-- `Spec` with the gate off (`is_real = 0`) holds at the populate of **any** word `w` — not just the operand `a`. A composing chip can share one witnessed struct between two differently-gated diff --git a/SP1Clean/Proofs/Operations/LtOperationSigned/Formal.lean b/SP1Clean/Proofs/Operations/LtOperationSigned/Formal.lean index b3f34fb0..b4a8009e 100644 --- a/SP1Clean/Proofs/Operations/LtOperationSigned/Formal.lean +++ b/SP1Clean/Proofs/Operations/LtOperationSigned/Formal.lean @@ -187,25 +187,21 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := circuit_proof_start obtain ⟨hb_u64, hcc_u64, hir_bin, his_bin⟩ := h_assumptions obtain ⟨hib, hicc, ⟨⟨_, hflags, _, hcl⟩, _, _⟩, _, _⟩ := h_input - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ec0 : Expression.eval env input_var_cc[0] = input_cc[0] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec1 : Expression.eval env input_var_cc[1] = input_cc[1] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec2 : Expression.eval env input_var_cc[2] = input_cc[2] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec3 : Expression.eval env input_var_cc[3] = input_cc[3] := by rw [← hicc]; simp only [Vector.getElem_map] - have ef0 : Expression.eval env input_var_cols_result_u16_flags[0] = input_cols_result_u16_flags[0] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef1 : Expression.eval env input_var_cols_result_u16_flags[1] = input_cols_result_u16_flags[1] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef2 : Expression.eval env input_var_cols_result_u16_flags[2] = input_cols_result_u16_flags[2] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef3 : Expression.eval env input_var_cols_result_u16_flags[3] = input_cols_result_u16_flags[3] := by rw [← hflags]; simp only [Vector.getElem_map] - have ecl0 : Expression.eval env input_var_cols_result_comparison_limbs[0] = input_cols_result_comparison_limbs[0] := by rw [← hcl]; simp only [Vector.getElem_map] - have ecl1 : Expression.eval env input_var_cols_result_comparison_limbs[1] = input_cols_result_comparison_limbs[1] := by rw [← hcl]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ec : ∀ i (hi : i < 4), Expression.eval env input_var_cc[i] = input_cc[i] := by + intro i hi; rw [← hicc]; simp only [Vector.getElem_map] + have ef : ∀ i (hi : i < 4), + Expression.eval env input_var_cols_result_u16_flags[i] = input_cols_result_u16_flags[i] := by + intro i hi; rw [← hflags]; simp only [Vector.getElem_map] + have ecl : ∀ i (hi : i < 2), Expression.eval env input_var_cols_result_comparison_limbs[i] + = input_cols_result_comparison_limbs[i] := by + intro i hi; rw [← hcl]; simp only [Vector.getElem_map] obtain ⟨hb0, hb1, hb2, hb3⟩ := Word.lt_cases_of_isU64 hb_u64 obtain ⟨hc0, hc1, hc2, hc3⟩ := Word.lt_cases_of_isU64 hcc_u64 obtain ⟨h_msb_b, h_msb_c, h_lt, _, _, hE5, hE7, hE9⟩ := h_holds - simp only [id_eq, eb0, eb1, eb2, eb3, ec0, ec1, ec2, ec3, ef0, ef1, ef2, ef3, ecl0, ecl1, - vec4_eta, vec2_eta, ← sub_eq_add_neg] at h_msb_b h_msb_c h_lt hE5 hE7 hE9 ⊢ + simp only [id_eq, eb, ec, ef, ecl, vec4_eta, vec2_eta, ← sub_eq_add_neg] + at h_msb_b h_msb_c h_lt hE5 hE7 hE9 ⊢ -- the two `U16MSBOperation` sub-assertion `Assumptions` (16-bit operands, `is_signed` binary). have hAb : U16MSBOperation.circuit.Assumptions ⟨input_b[3], ⟨input_cols_b_msb_msb⟩, input_is_signed⟩ := ⟨fun _ => hb3, his_bin⟩ @@ -249,20 +245,17 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨hb_u64, hcc_u64, hir_bin, his_bin⟩ := h_assumptions obtain ⟨_, _, hbm_bool, hcm_bool, hg5, hg7, hg9, hbm_eq, hcm_eq, h_uns_spec⟩ := h_spec obtain ⟨hib, hicc, ⟨⟨_, hflags, _, hcl⟩, _, _⟩, _, _⟩ := h_input - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ec0 : Expression.eval env.toEnvironment input_var_cc[0] = input_cc[0] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec1 : Expression.eval env.toEnvironment input_var_cc[1] = input_cc[1] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec2 : Expression.eval env.toEnvironment input_var_cc[2] = input_cc[2] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec3 : Expression.eval env.toEnvironment input_var_cc[3] = input_cc[3] := by rw [← hicc]; simp only [Vector.getElem_map] - have ef0 : Expression.eval env.toEnvironment input_var_cols_result_u16_flags[0] = input_cols_result_u16_flags[0] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef1 : Expression.eval env.toEnvironment input_var_cols_result_u16_flags[1] = input_cols_result_u16_flags[1] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef2 : Expression.eval env.toEnvironment input_var_cols_result_u16_flags[2] = input_cols_result_u16_flags[2] := by rw [← hflags]; simp only [Vector.getElem_map] - have ef3 : Expression.eval env.toEnvironment input_var_cols_result_u16_flags[3] = input_cols_result_u16_flags[3] := by rw [← hflags]; simp only [Vector.getElem_map] - have ecl0 : Expression.eval env.toEnvironment input_var_cols_result_comparison_limbs[0] = input_cols_result_comparison_limbs[0] := by rw [← hcl]; simp only [Vector.getElem_map] - have ecl1 : Expression.eval env.toEnvironment input_var_cols_result_comparison_limbs[1] = input_cols_result_comparison_limbs[1] := by rw [← hcl]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ec : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_cc[i] = input_cc[i] := by + intro i hi; rw [← hicc]; simp only [Vector.getElem_map] + have ef : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_cols_result_u16_flags[i] + = input_cols_result_u16_flags[i] := by + intro i hi; rw [← hflags]; simp only [Vector.getElem_map] + have ecl : ∀ i (hi : i < 2), + Expression.eval env.toEnvironment input_var_cols_result_comparison_limbs[i] + = input_cols_result_comparison_limbs[i] := by + intro i hi; rw [← hcl]; simp only [Vector.getElem_map] obtain ⟨hb0, hb1, hb2, hb3⟩ := Word.lt_cases_of_isU64 hb_u64 obtain ⟨hc0, hc1, hc2, hc3⟩ := Word.lt_cases_of_isU64 hcc_u64 have hbtop : (input_b[3] + input_is_signed * 32768 - 65536 * input_cols_b_msb_msb).val < 2 ^ 16 := by @@ -277,8 +270,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp have hcm0 : input_cols_c_msb_msb = 0 := by simpa using h9 rw [h, hcm0]; simpa using hc3 · rw [h, one_mul, hcm_eq h]; simpa using (adj_limb hc3).2 - simp only [id_eq, eb0, eb1, eb2, eb3, ec0, ec1, ec2, ec3, ef0, ef1, ef2, ef3, ecl0, ecl1, - vec4_eta, vec2_eta, ← sub_eq_add_neg] at ⊢ + simp only [id_eq, eb, ec, ef, ecl, vec4_eta, vec2_eta, ← sub_eq_add_neg] refine ⟨⟨⟨fun _ => hb3, his_bin⟩, hbm_bool, hbm_eq⟩, ⟨⟨fun _ => hc3, his_bin⟩, hcm_bool, hcm_eq⟩, ⟨⟨fun _ => ⟨Word.isU64_of_cases (by simpa using hb0) (by simpa using hb1) (by simpa using hb2) diff --git a/SP1Clean/Proofs/Operations/LtOperationUnsigned/Formal.lean b/SP1Clean/Proofs/Operations/LtOperationUnsigned/Formal.lean index 335d40f7..cddb62ab 100644 --- a/SP1Clean/Proofs/Operations/LtOperationUnsigned/Formal.lean +++ b/SP1Clean/Proofs/Operations/LtOperationUnsigned/Formal.lean @@ -150,21 +150,17 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨hbcc_imp, hbin⟩ := h_assumptions obtain ⟨hib, hicc, hicols, hir⟩ := h_input obtain ⟨_hibit, hiflags, _hineinv, hicl⟩ := hicols - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ec0 : Expression.eval env input_var_cc[0] = input_cc[0] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec1 : Expression.eval env input_var_cc[1] = input_cc[1] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec2 : Expression.eval env input_var_cc[2] = input_cc[2] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec3 : Expression.eval env input_var_cc[3] = input_cc[3] := by rw [← hicc]; simp only [Vector.getElem_map] - have ef0 : Expression.eval env input_var_cols_u16_flags[0] = input_cols_u16_flags[0] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef1 : Expression.eval env input_var_cols_u16_flags[1] = input_cols_u16_flags[1] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef2 : Expression.eval env input_var_cols_u16_flags[2] = input_cols_u16_flags[2] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef3 : Expression.eval env input_var_cols_u16_flags[3] = input_cols_u16_flags[3] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ecl0 : Expression.eval env input_var_cols_comparison_limbs[0] = input_cols_comparison_limbs[0] := by rw [← hicl]; simp only [Vector.getElem_map] - have ecl1 : Expression.eval env input_var_cols_comparison_limbs[1] = input_cols_comparison_limbs[1] := by rw [← hicl]; simp only [Vector.getElem_map] - simp only [id_eq, eb0, eb1, eb2, eb3, ec0, ec1, ec2, ec3, ef0, ef1, ef2, ef3, ecl0, ecl1] at h_holds ⊢ + have eb : ∀ i (hi : i < 4), Expression.eval env input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ec : ∀ i (hi : i < 4), Expression.eval env input_var_cc[i] = input_cc[i] := by + intro i hi; rw [← hicc]; simp only [Vector.getElem_map] + have ef : ∀ i (hi : i < 4), + Expression.eval env input_var_cols_u16_flags[i] = input_cols_u16_flags[i] := by + intro i hi; rw [← hiflags]; simp only [Vector.getElem_map] + have ecl : ∀ i (hi : i < 2), + Expression.eval env input_var_cols_comparison_limbs[i] = input_cols_comparison_limbs[i] := by + intro i hi; rw [← hicl]; simp only [Vector.getElem_map] + simp only [id_eq, eb, ec, ef, ecl] at h_holds ⊢ obtain ⟨h_cmp, hE1, hE6, hE8, hE10, hE12, hE14, hE19, hE27, hE35, hE43, hE48, hE49, hE54⟩ := h_holds -- the composed `U16Compare` assertion's `Assumptions` (cl ranges on a real row, via the cores). have hCmpAs : U16CompareOperation.circuit.Assumptions @@ -199,20 +195,17 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨hbcc_imp, hbin⟩ := h_assumptions obtain ⟨hib, hicc, hicols, hir⟩ := h_input obtain ⟨_hibit, hiflags, _hineinv, hicl⟩ := hicols - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ec0 : Expression.eval env.toEnvironment input_var_cc[0] = input_cc[0] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec1 : Expression.eval env.toEnvironment input_var_cc[1] = input_cc[1] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec2 : Expression.eval env.toEnvironment input_var_cc[2] = input_cc[2] := by rw [← hicc]; simp only [Vector.getElem_map] - have ec3 : Expression.eval env.toEnvironment input_var_cc[3] = input_cc[3] := by rw [← hicc]; simp only [Vector.getElem_map] - have ef0 : Expression.eval env.toEnvironment input_var_cols_u16_flags[0] = input_cols_u16_flags[0] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef1 : Expression.eval env.toEnvironment input_var_cols_u16_flags[1] = input_cols_u16_flags[1] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef2 : Expression.eval env.toEnvironment input_var_cols_u16_flags[2] = input_cols_u16_flags[2] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ef3 : Expression.eval env.toEnvironment input_var_cols_u16_flags[3] = input_cols_u16_flags[3] := by rw [← hiflags]; simp only [Vector.getElem_map] - have ecl0 : Expression.eval env.toEnvironment input_var_cols_comparison_limbs[0] = input_cols_comparison_limbs[0] := by rw [← hicl]; simp only [Vector.getElem_map] - have ecl1 : Expression.eval env.toEnvironment input_var_cols_comparison_limbs[1] = input_cols_comparison_limbs[1] := by rw [← hicl]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ec : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_cc[i] = input_cc[i] := by + intro i hi; rw [← hicc]; simp only [Vector.getElem_map] + have ef : ∀ i (hi : i < 4), + Expression.eval env.toEnvironment input_var_cols_u16_flags[i] = input_cols_u16_flags[i] := by + intro i hi; rw [← hiflags]; simp only [Vector.getElem_map] + have ecl : ∀ i (hi : i < 2), + Expression.eval env.toEnvironment input_var_cols_comparison_limbs[i] + = input_cols_comparison_limbs[i] := by + intro i hi; rw [← hicl]; simp only [Vector.getElem_map] obtain ⟨hf0, hf1, hf2, hf3, hsum, hs3, hs2, hs1, hs0, hcl0eq, hcl1eq, hinv, hbitbool, hbitord⟩ := h_spec -- the composed `U16Compare` assertion's `Assumptions` ∧ `Spec` (cl ranges via the cores on a real row). have h_sel_real : input_is_real = 1 → Selectors input_b input_cc @@ -231,7 +224,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp have hCmpSpec : U16CompareOperation.circuit.Spec ⟨input_cols_comparison_limbs[0], input_cols_comparison_limbs[1], ⟨input_cols_u16_compare_operation_bit⟩, input_is_real⟩ := ⟨hbitbool, hbitord⟩ - simp only [id_eq, eb0, eb1, eb2, eb3, ec0, ec1, ec2, ec3, ef0, ef1, ef2, ef3, ecl0, ecl1] + simp only [id_eq, eb, ec, ef, ecl] refine ⟨⟨hCmpAs, hCmpSpec⟩, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩ · rcases hbin with h | h <;> rw [h] <;> ring · rcases hf0 with h | h <;> rw [h] <;> ring diff --git a/SP1Clean/Proofs/Operations/MulOperation/Formal.lean b/SP1Clean/Proofs/Operations/MulOperation/Formal.lean index 27bf595e..ad27f00c 100644 --- a/SP1Clean/Proofs/Operations/MulOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/MulOperation/Formal.lean @@ -217,7 +217,7 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := have hmsb_bool : input_cols_product_msb_msb = 0 ∨ input_cols_product_msb_msb = 1 := hpmspec.1 have hmsb : input_is_mulw = 1 → input_cols_product_msb_msb = if (input_cols_product[2] + input_cols_product[3] * 256).val ≥ 32768 then 1 else 0 := by - intro h; have := hpmspec.2 h; rw [ep2, ep3] at this; exact this + intro h; have := hpmspec.2 h; rwa [ep2, ep3] at this have hb_msb : input_cols_b_msb = if input_b[3].val ≥ 32768 then 1 else 0 := by obtain ⟨hlo3, hhi3, hreass3⟩ := hb_low 3 dsimp only at hlo3 hhi3 hreass3 diff --git a/SP1Clean/Proofs/Operations/SubOperation/Formal.lean b/SP1Clean/Proofs/Operations/SubOperation/Formal.lean index 27eed563..c02c84df 100644 --- a/SP1Clean/Proofs/Operations/SubOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/SubOperation/Formal.lean @@ -27,28 +27,15 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨hia, hib, hiv, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast have h65536 : (2 : ℕ) ^ 16 = 65536 := by norm_num - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev2 : Expression.eval env input_var_cols_value[2] = input_cols_value[2] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev3 : Expression.eval env input_var_cols_value[3] = input_cols_value[3] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3, - ev0, ev1, ev2, ev3] at h_holds ⊢ + have ev {w : Word (Expression (ZMod p))} {v : Word (ZMod p)} (h : Vector.map (Expression.eval env) w = v) : + ∀ i (_ : i < 4), Expression.eval env w[i] = v[i] := fun i _ => by rw [← h, Vector.getElem_map] + simp only [circuit_norm, byteChannel, ev hia, ev hib, ev hiv] at h_holds ⊢ obtain ⟨hr0, hr1, hr2, hr3, _hbool, hgc0, hgc1, hgc2, hgc3⟩ := h_holds - -- post-#398 the byte receives owe no padding requirement, so the goal is exactly `Spec`. intro hr1eq have hneg : -input_is_real = -1 := by rw [hr1eq] have R0 := hr0 hneg; have R1 := hr1 hneg; have R2 := hr2 hneg; have R3 := hr3 hneg rw [← c16] at R0 R1 R2 R3 rw [hr1eq, one_mul] at hgc0 hgc1 hgc2 hgc3 - -- The generated `main` decomposes the borrow constant as `+ 65536 - 1`; fold to RawSpec's `65535`. have c65535 : ∀ x : ZMod p, x + 65536 + -1 = x + 65535 := fun x => by ring simp only [c65535] at hgc0 hgc1 hgc2 hgc3 refine subSemantics_of_carries ha hb ?_ @@ -66,48 +53,26 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨ha, hb, hbin⟩ := h_assumptions obtain ⟨hia, hib, hiv, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have ea2 : Expression.eval env.toEnvironment input_var_a[2] = input_a[2] := by rw [← hia]; simp only [Vector.getElem_map] - have ea3 : Expression.eval env.toEnvironment input_var_a[3] = input_a[3] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have eb2 : Expression.eval env.toEnvironment input_var_b[2] = input_b[2] := by rw [← hib]; simp only [Vector.getElem_map] - have eb3 : Expression.eval env.toEnvironment input_var_b[3] = input_b[3] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env.toEnvironment input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env.toEnvironment input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev2 : Expression.eval env.toEnvironment input_var_cols_value[2] = input_cols_value[2] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev3 : Expression.eval env.toEnvironment input_var_cols_value[3] = input_cols_value[3] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, ea2, ea3, eb0, eb1, eb2, eb3, - ev0, ev1, ev2, ev3] + have ev {w : Word (Expression (ZMod p))} {v : Word (ZMod p)} + (h : Vector.map (Expression.eval env.toEnvironment) w = v) : + ∀ i (_ : i < 4), Expression.eval env.toEnvironment w[i] = v[i] := + fun i _ => by rw [← h, Vector.getElem_map] + simp only [circuit_norm, byteChannel, ev hia, ev hib, ev hiv] refine ⟨?_, ?_, ?_, ?_, ?_⟩ - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨hv0, _, _, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv0 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, hv1, _, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv1 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, _, hv2, _⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv2 - · intro hneg - have hr1 : input_is_real = 1 := neg_inj.mp hneg - obtain ⟨hv, _⟩ := h_spec hr1 - obtain ⟨_, _, _, hv3⟩ := Word.lt_cases_of_isU64 hv - rw [← c16]; exact (byteRowSpec_range _ h16p).mpr hv3 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.2.1 + · intro hneg; rw [← c16] + exact (byteRowSpec_range _ h16p).mpr (Word.lt_cases_of_isU64 (h_spec (neg_inj.mp hneg)).1).2.2.2 · rcases hbin with h0 | h1 · simp [h0] · obtain ⟨hv, hbv⟩ := h_spec h1 obtain ⟨hc0, hc1, hc2, hc3, _, _, _, _⟩ := carries_of_subSemantics ha hb hv hbv simp only [Nat.cast_ofNat, sub_eq_add_neg] at hc0 hc1 hc2 hc3 rw [h1] - -- The generated `main` decomposes the borrow constant as `+ 65536 - 1`; fold to RawSpec's `65535`. have c65535 : ∀ x : ZMod p, x + 65536 + -1 = x + 65535 := fun x => by ring simp only [c65535] refine ⟨?_, ?_, ?_, ?_, ?_⟩ diff --git a/SP1Clean/Proofs/Operations/SubwOperation/Formal.lean b/SP1Clean/Proofs/Operations/SubwOperation/Formal.lean index 3a2d5065..02c2e935 100644 --- a/SP1Clean/Proofs/Operations/SubwOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/SubwOperation/Formal.lean @@ -46,13 +46,14 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := obtain ⟨hia, hib, ⟨hiv, _⟩, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast have h65536 : (2 : ℕ) ^ 16 = 65536 := by norm_num - have ea0 : Expression.eval env input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] - have ev0 : Expression.eval env input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - simp only [circuit_norm, byteChannel, ea0, ea1, eb0, eb1, ev0, ev1] at h_holds ⊢ + have ea : ∀ i (hi : i < 4), Expression.eval env input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] + have ev : ∀ i (hi : i < 2), + Expression.eval env input_var_cols_value[i] = input_cols_value[i] := by + intro i hi; rw [← hiv]; simp only [Vector.getElem_map] + simp only [circuit_norm, byteChannel, ea, eb, ev] at h_holds ⊢ obtain ⟨h_msb, hr0, hr1, _hgate, hgc0, hgc1⟩ := h_holds have h_msb_as : U16MSBOperation.circuit.Assumptions (⟨input_cols_value[1], ⟨input_cols_msb_msb⟩, input_is_real⟩ : U16MSBOperation.Inputs (ZMod p)) := by @@ -91,12 +92,13 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp obtain ⟨ha, hb, hbin⟩ := h_assumptions obtain ⟨hia, hib, ⟨hiv, _⟩, _⟩ := h_input have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast - have ev0 : Expression.eval env.toEnvironment input_var_cols_value[0] = input_cols_value[0] := by rw [← hiv]; simp only [Vector.getElem_map] - have ev1 : Expression.eval env.toEnvironment input_var_cols_value[1] = input_cols_value[1] := by rw [← hiv]; simp only [Vector.getElem_map] - have ea0 : Expression.eval env.toEnvironment input_var_a[0] = input_a[0] := by rw [← hia]; simp only [Vector.getElem_map] - have ea1 : Expression.eval env.toEnvironment input_var_a[1] = input_a[1] := by rw [← hia]; simp only [Vector.getElem_map] - have eb0 : Expression.eval env.toEnvironment input_var_b[0] = input_b[0] := by rw [← hib]; simp only [Vector.getElem_map] - have eb1 : Expression.eval env.toEnvironment input_var_b[1] = input_b[1] := by rw [← hib]; simp only [Vector.getElem_map] + have ev : ∀ i (hi : i < 2), + Expression.eval env.toEnvironment input_var_cols_value[i] = input_cols_value[i] := by + intro i hi; rw [← hiv]; simp only [Vector.getElem_map] + have ea : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_a[i] = input_a[i] := by + intro i hi; rw [← hia]; simp only [Vector.getElem_map] + have eb : ∀ i (hi : i < 4), Expression.eval env.toEnvironment input_var_b[i] = input_b[i] := by + intro i hi; rw [← hib]; simp only [Vector.getElem_map] have key : input_is_real = 1 → (input_cols_value[0].val < 2 ^ 16 ∧ input_cols_value[1].val < 2 ^ 16) ∧ RawSpec input_a input_b ⟨input_cols_value, ⟨input_cols_msb_msb⟩⟩ ∧ @@ -108,7 +110,7 @@ theorem completeness : FormalAssertion.Completeness (ZMod p) main Assumptions Sp List.getElem_cons_zero, List.getElem_cons_succ] at hUv0 hUv1 obtain ⟨h_raw, h_msbval⟩ := carries_of_subwSemantics ha hb hU hbveq exact ⟨⟨hUv0, hUv1⟩, h_raw, h_msbval⟩ - simp only [circuit_norm, byteChannel, ea0, ea1, eb0, eb1, ev0, ev1] + simp only [circuit_norm, byteChannel, ea, eb, ev] refine ⟨⟨⟨fun hr1 => (key hr1).1.2, hbin⟩, ⟨h_spec.1, fun hr1 => (key hr1).2.2⟩⟩, ?_, ?_, ?_, ?_, ?_⟩ · intro hneg have hr1 : input_is_real = 1 := neg_inj.mp hneg diff --git a/SP1Clean/Proofs/Operations/U16CompareOperation/Formal.lean b/SP1Clean/Proofs/Operations/U16CompareOperation/Formal.lean index d8d84845..6c47ff4d 100644 --- a/SP1Clean/Proofs/Operations/U16CompareOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/U16CompareOperation/Formal.lean @@ -30,7 +30,6 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast simp only [circuit_norm, byteChannel] at h_holds ⊢ obtain ⟨hr, _hbool, hgc⟩ := h_holds - -- post-#398 the byte receive owes no padding requirement, so the goal is exactly `Spec`. refine ⟨bool_of_mul_pred hgc, ?_⟩ intro hr1eq obtain ⟨ha, hb⟩ := hab hr1eq diff --git a/SP1Clean/Proofs/Operations/U16MSBOperation/Formal.lean b/SP1Clean/Proofs/Operations/U16MSBOperation/Formal.lean index 70d481f4..129758d0 100644 --- a/SP1Clean/Proofs/Operations/U16MSBOperation/Formal.lean +++ b/SP1Clean/Proofs/Operations/U16MSBOperation/Formal.lean @@ -31,14 +31,12 @@ theorem soundness : FormalAssertion.Soundness (ZMod p) main Assumptions Spec := have c16 : ((16 : ℕ) : ZMod p) = (16 : ZMod p) := by norm_cast simp only [circuit_norm, byteChannel] at h_holds ⊢ obtain ⟨hr, _hbool, hgc⟩ := h_holds - -- post-#398 the byte receive owes no padding requirement, so the goal is exactly `Spec`. refine ⟨bool_of_mul_pred hgc, ?_⟩ intro hr1eq - have hav := ha hr1eq have hneg : -input_is_real = -1 := by rw [hr1eq] have R := hr hneg rw [← c16] at R - refine msb_of_raw hav ?_ + refine msb_of_raw (ha hr1eq) ?_ simp only [RawSpec, sub_eq_add_neg] exact ⟨bool_of_mul_pred hgc, (byteRowSpec_range _ h16p).mp R⟩ diff --git a/SP1Clean/Soundness/ByteConsistency.lean b/SP1Clean/Soundness/ByteConsistency.lean index 3fbcbfa0..e463011b 100644 --- a/SP1Clean/Soundness/ByteConsistency.lean +++ b/SP1Clean/Soundness/ByteConsistency.lean @@ -97,8 +97,7 @@ private theorem multiplicitySum_eq_zero_of_multOf {l : LookupAccessList} (k : Lo simp only [multiplicitySum] apply List.sum_eq_zero intro x hx - simp only [List.mem_map] at hx - obtain ⟨a, ha, rfl⟩ := hx + obtain ⟨a, ha, rfl⟩ := List.mem_map.1 hx exact h a (List.mem_of_mem_filter ha) omit [NeZero p] in @@ -130,11 +129,10 @@ theorem byteAccessValid_of_balance [Fact (1 < p)] TraceByteLink rows := by intro r hr h_real row h_row_mem set a := byteSend r.is_real row with ha_def - have ha_mem : a ∈ aggregateChipRows rows byteLookups := by - refine List.mem_flatMap.mpr ⟨r, hr, ?_⟩ - exact List.mem_map.mpr ⟨row, h_row_mem, rfl⟩ + have ha_mem : a ∈ aggregateChipRows rows byteLookups := + List.mem_flatMap.mpr ⟨r, hr, List.mem_map.mpr ⟨row, h_row_mem, rfl⟩⟩ have h_pos : 0 < multOf a := by - rw [ha_def]; simp only [byteSend, multOf, h_real, ZMod.val_one]; norm_num + simp only [ha_def, byteSend, multOf, h_real, ZMod.val_one]; norm_num have h_nonneg : ∀ b ∈ aggregateChipRows rows byteLookups, 0 ≤ multOf b := by intro b hb obtain ⟨r', _, hb'⟩ := List.mem_flatMap.mp hb diff --git a/SP1Clean/Soundness/ChipRegistry.lean b/SP1Clean/Soundness/ChipRegistry.lean index 8e449545..72cd4bfd 100644 --- a/SP1Clean/Soundness/ChipRegistry.lean +++ b/SP1Clean/Soundness/ChipRegistry.lean @@ -60,7 +60,7 @@ namespace SP1Clean.Soundness variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 24 < p)] -local instance : Fact (2 ^ 17 < p) := ⟨by have := Fact.out (p := 2 ^ 24 < p); omega⟩ +local instance : Fact (2 ^ 17 < p) := ⟨lt_of_le_of_lt (by norm_num) (Fact.out (p := 2 ^ 24 < p))⟩ /-- Every chip with a capstone-integration `ChipKind`. **Auditable:** one entry ⇔ one wired chip; to wire a new chip, define its `kind` in its `Bridge.lean` and add it here. (Not a dispatch table — the capstone diff --git a/SP1Clean/Soundness/Decode.lean b/SP1Clean/Soundness/Decode.lean index 4fc4e4a3..8c14b48c 100644 --- a/SP1Clean/Soundness/Decode.lean +++ b/SP1Clean/Soundness/Decode.lean @@ -356,32 +356,26 @@ theorem decode_bound (prog : GuestProgram) {pi : SP1PublicIO (ZMod p)} (hw : WalkOf pi rows path) {i : ℕ} (hi : i < path.length) (s : SailState) : DecodeOperandsBound prog (path[i]'hi) s := by intro hcfg w hfetch - set r := path[i]'hi with hr_def - have hr_path : r ∈ path := List.getElem_mem hi - -- the walk row is a real row of the trace + set r := path[i]'hi have hmem : r ∈ realRowEdges (rows.map ChipRow.view) := - Multiset.mem_of_le hw.2 (Multiset.mem_coe.mpr hr_path) + Multiset.mem_of_le hw.2 (Multiset.mem_coe.mpr (List.getElem_mem hi)) rw [realRowEdges, Multiset.mem_filter] at hmem obtain ⟨hr_coe, h_real⟩ := hmem - have hr_rows : r ∈ rows.map ChipRow.view := Multiset.mem_coe.mp hr_coe - -- its committed program columns lie in the decoded ROM have ha_mem : programAccess r ∈ aggregateProgramAccesses (rows.map ChipRow.view) := by - simp only [aggregateProgramAccesses]; exact List.mem_map_of_mem hr_rows + simp only [aggregateProgramAccesses]; exact List.mem_map_of_mem (Multiset.mem_coe.mp hr_coe) have h_real' : (programAccess r).is_real ≠ 0 := by simpa only [programAccess, stateAccess] using h_real obtain ⟨w₀, hfetch₀, hdec⟩ := h_link.rom_holds (programAccess r) ha_mem h_real' - -- the fetched words agree (same pc key) have hpc : pcBitsOfRow (programAccess r).toRow = rcvPcOf (stateAccess r) := by simp only [pcBitsOfRow, rcvPcOf, programAccess, ProgramAccess.toRow, stateAccess] rw [hpc, hfetch] at hfetch₀ obtain rfl : w = w₀ := Option.some.inj hfetch₀ - -- the pc vectors agree, so the projection's target is the committed row have hpcvec : rowPcVec (programAccess r).toRow = r.state.pc := by simp only [rowPcVec, programAccess, ProgramAccess.toRow] - apply Vector.ext; intro j hj + ext j hj interval_cases j <;> simp - obtain ⟨ii, s', hrun, hproj⟩ := hdec s hcfg - exact ⟨ii, s', hrun, by rw [hpcvec] at hproj; exact hproj⟩ + rw [hpcvec] at hdec + exact hdec s hcfg /-! ## The balance-level decode discharge (removing the threaded link) — W3 deliverable B -/ @@ -477,7 +471,7 @@ each row is discharged this way. -/ /-- A one-instruction guest program: `ADD x1, x2, x3` (`0x003100B3`) at pc 0. -/ def addProgram : GuestProgram := ⟨[(0#64, 0x003100B3#32)], 0, [], by simp, - by intro a ha; simp only [List.map_cons, List.map_nil, List.mem_singleton] at ha; subst ha; simp⟩ + by simp⟩ /-- The committed Program-bus row the ADD decodes to — the `instrToProgramRow` projection at pc 0 (`op_a = rd = x1`, `op_b[0] = rs1 = x2`, `op_c[0] = rs2 = x3`, opcode `ADD`). -/ @@ -498,10 +492,7 @@ ROM word at its pc, against the official Sail `ext_decode` — `decodedInROM` ho Sail model's decoder axioms. -/ theorem decodedInROM_addRow : decodedInROM addProgram (addRow (p := p)) := by refine ⟨0x003100B3#32, ?_, ?_⟩ - · show addProgram.fetchWord (pcBitsOfRow (addRow (p := p))) = some _ - have hpc : pcBitsOfRow (addRow (p := p)) = 0#64 := by - simp only [pcBitsOfRow, addRow, pcBitsOfVals, ZMod.val_zero]; rfl - rw [hpc]; rfl + · simp only [pcBitsOfRow, addRow, pcBitsOfVals, ZMod.val_zero]; rfl · intro s hcfg refine ⟨.RTYPE (regidx.Regidx 3#5, regidx.Regidx 2#5, regidx.Regidx 1#5, rop.ADD), s, SP1Clean.SailDecode.decode_ADD_example s hcfg.1 hcfg.2, ?_⟩ diff --git a/SP1Clean/Soundness/GatedVm/BalanceMod.lean b/SP1Clean/Soundness/GatedVm/BalanceMod.lean index fe140f0c..3ea3c6a0 100644 --- a/SP1Clean/Soundness/GatedVm/BalanceMod.lean +++ b/SP1Clean/Soundness/GatedVm/BalanceMod.lean @@ -53,7 +53,6 @@ theorem isConsistentBalanced_of_intCast_zero {p : ℕ} [NeZero p] intro k have hdvd : (p : ℤ) ∣ multiplicitySum accesses k := (ZMod.intCast_zmod_eq_zero_iff_dvd _ p).mp (hmod k) - -- |multiplicitySum| ≤ (filtered count) ≤ length < p have hbound : |multiplicitySum accesses k| ≤ ((filterKey accesses k).map multOf).length := by apply abs_sum_le_length_of_binary intro x hx @@ -61,13 +60,8 @@ theorem isConsistentBalanced_of_intCast_zero {p : ℕ} [NeZero p] exact hbin a (List.mem_of_mem_filter ha) have hlen2 : ((filterKey accesses k).map multOf).length ≤ accesses.length := by rw [List.length_map, filterKey]; exact List.length_filter_le _ _ - have habs : |multiplicitySum accesses k| < (p : ℤ) := by - have : ((filterKey accesses k).map multOf).length < p := by omega - have hc : (((filterKey accesses k).map multOf).length : ℤ) < (p : ℤ) := by exact_mod_cast this - omega - -- p ∣ x and |x| < p ⇒ x = 0 - by_contra h - exact absurd (Int.le_of_dvd (abs_pos.mpr h) ((dvd_abs _ _).mpr hdvd)) (by omega) + have habs : |multiplicitySum accesses k| < (p : ℤ) := by omega + exact Int.eq_zero_of_abs_lt_dvd hdvd habs /-! ## The Clean → native translation (the `toAccess` adapter, roadmap W1a) @@ -103,7 +97,6 @@ theorem intCast_multiplicitySum_map_toAccess haveI : NeZero p := ⟨(Fact.out (p := p.Prime)).ne_zero⟩ by_cases h_ex : ∃ i ∈ interactions, keyOf (Interaction.toAccess i) = k · obtain ⟨i₀, hi₀, hk₀⟩ := h_ex - -- same-channel keys separate exactly on the message have h_pred : ∀ i ∈ interactions, decide (keyOf (Interaction.toAccess i) = k) = decide (i.msg = i₀.msg) := by intro i hi @@ -115,7 +108,6 @@ theorem intCast_multiplicitySum_map_toAccess exact Array.toList_inj.mp (List.map_injective_iff.mpr (ZMod.val_injective p) h3) · intro h simp only [Interaction.toAccess, keyOf, h_channel i hi, h_channel i₀ hi₀, h] - -- the access-key filter is the `toAccess`-image of the message filter have h_filter : (interactions.map Interaction.toAccess).filter (fun a => keyOf a = k) = (interactions.filter (fun i => i.msg = i₀.msg)).map Interaction.toAccess := by rw [List.filter_map] @@ -154,8 +146,7 @@ theorem isConsistentBalanced_of_balancedInteractions isConsistentBalanced accesses := by haveI : NeZero p := ⟨(Fact.out (p := p.Prime)).ne_zero⟩ refine isConsistentBalanced_of_intCast_zero (p := p) accesses ?_ h_bin ?_ - · -- |accesses| = |interactions| < ringChar (ZMod p) = p - rcases h_bal.1 with hlt | hchar + · rcases h_bal.1 with hlt | hchar · rw [h_perm.length_eq, List.length_map] rwa [ZMod.ringChar_zmod_n] at hlt · rw [ZMod.ringChar_zmod_n] at hchar diff --git a/SP1Clean/Soundness/GatedVm/Chain.lean b/SP1Clean/Soundness/GatedVm/Chain.lean index f3755db3..5a17951d 100644 --- a/SP1Clean/Soundness/GatedVm/Chain.lean +++ b/SP1Clean/Soundness/GatedVm/Chain.lean @@ -67,8 +67,6 @@ lemma balanced_erase {E : Multiset α} {e : α} (he : e ∈ E) (edge : α → V = (indeg (E.erase e) edge v : ℤ) + (if (edge e).2 = v then 1 else 0) := by simp only [indeg]; exact_mod_cast card_filter_erase he (fun a => (edge a).2 = v) have hv := h v - -- the erased edge `e = src → (edge e).2`, so its source-indicator is `v = src` and its - -- target-indicator is `v = (edge e).2` have e1 : (if (edge e).1 = v then (1 : ℤ) else 0) = (if v = src then 1 else 0) := by rcases eq_or_ne v src with hc | hc · rw [if_pos (h1.trans hc.symm), if_pos hc] @@ -79,8 +77,6 @@ lemma balanced_erase {E : Multiset α} {e : α} (he : e ∈ E) (edge : α → V · rw [if_neg (fun hh => hc hh.symm), if_neg hc] rw [e1] at ho rw [e2] at hi - show (outdeg (E.erase e) edge v : ℤ) - indeg (E.erase e) edge v - = (if v = (edge e).2 then 1 else 0) - (if v = snk then 1 else 0) linarith [ho, hi, hv] /-- **Balance ⇒ trail (the Eulerian core).** From degree balance alone — no clock injectivity, no @@ -94,15 +90,13 @@ theorem exists_trail (edge : α → V × V) (E : Multiset α) : intro src snk h by_cases hss : src = snk · exact ⟨[], hss, by simp⟩ - · -- the source has an outgoing edge - have hsrc := h src + · have hsrc := h src rw [if_pos rfl, if_neg hss] at hsrc have hpos : 0 < outdeg E edge src := by omega simp only [outdeg] at hpos obtain ⟨e, he_filter⟩ := Multiset.exists_mem_of_ne_zero (Multiset.card_pos.mp hpos) rw [Multiset.mem_filter] at he_filter obtain ⟨he_mem, he1⟩ := he_filter - -- erase it; the remainder is balanced with the source shifted to `(edge e).2` have hbal' : Balanced (E.erase e) edge (edge e).2 snk := balanced_erase he_mem edge h he1 have hlt : E.erase e < E := by conv_rhs => rw [← Multiset.cons_erase he_mem] @@ -110,8 +104,7 @@ theorem exists_trail (edge : α → V × V) (E : Multiset α) : obtain ⟨path', hwalk', hsub'⟩ := IH (E.erase e) hlt (edge e).2 snk hbal' refine ⟨e :: path', ⟨he1, hwalk'⟩, ?_⟩ have hle : (e ::ₘ (↑path' : Multiset α)) ≤ e ::ₘ E.erase e := Multiset.cons_le_cons e hsub' - rw [Multiset.cons_erase he_mem] at hle - exact hle + rwa [Multiset.cons_erase he_mem] at hle end GatedVm diff --git a/SP1Clean/Soundness/GatedVm/Formal.lean b/SP1Clean/Soundness/GatedVm/Formal.lean index 73e97c91..c3908b11 100644 --- a/SP1Clean/Soundness/GatedVm/Formal.lean +++ b/SP1Clean/Soundness/GatedVm/Formal.lean @@ -33,9 +33,7 @@ def GatedVm.toFormalEnsemble (vm : GatedVm F PublicIO) Assumptions := Assumptions Spec := Spec soundness := by - intro publicInput hA hStmt - obtain ⟨witness, hpub, hC, hB⟩ := hStmt - subst hpub + rintro publicInput hA ⟨witness, rfl, hC, hB⟩ exact soundness witness hA hC hB @[simp] lemma GatedVm.toFormalEnsemble_ensemble (vm : GatedVm F PublicIO) diff --git a/SP1Clean/Soundness/GatedVm/SailDispatch.lean b/SP1Clean/Soundness/GatedVm/SailDispatch.lean index 2fc2fcfa..862da108 100644 --- a/SP1Clean/Soundness/GatedVm/SailDispatch.lean +++ b/SP1Clean/Soundness/GatedVm/SailDispatch.lean @@ -26,9 +26,8 @@ Sail spec (for any Sail state honouring that row's register/PC/memory reads, qua `sailEquiv`). Dispatched generically through `r.kind.reaches_sail`. -/ theorem chipRows_step_sound (rows : List (ChipRow p)) (data : ProverData (ZMod p)) (h_spec : ∀ r ∈ rows, r.chipSpec data) : - ∀ r ∈ rows, r.is_real = 1 → ∀ s : SailState, r.sailEquiv s := by - intro r hr h_real s - exact r.kind.reaches_sail r.inputs r.cols data s h_real (h_spec r hr) + ∀ r ∈ rows, r.is_real = 1 → ∀ s : SailState, r.sailEquiv s := + fun r hr h_real s => r.kind.reaches_sail r.inputs r.cols data s h_real (h_spec r hr) /-- Single-row form (the dispatch on one row), handy when the spec quantifies rows from a trail. -/ theorem chipRow_sailEquiv (r : ChipRow p) (data : ProverData (ZMod p)) diff --git a/SP1Clean/Soundness/GatedVm/StateBridge.lean b/SP1Clean/Soundness/GatedVm/StateBridge.lean index 49a97519..fc64bb09 100644 --- a/SP1Clean/Soundness/GatedVm/StateBridge.lean +++ b/SP1Clean/Soundness/GatedVm/StateBridge.lean @@ -143,18 +143,8 @@ theorem balanced_state_bus split_ifs <;> ring rw [hbd] at hb rw [outdeg_realRowEdges rows k, indeg_realRowEdges rows k] - have ei : (if (InteractionKind.State, "SP1State", initEntry) = k then (1 : ℤ) else 0) - = (if k = (InteractionKind.State, "SP1State", initEntry) then 1 else 0) := by - by_cases h : (InteractionKind.State, "SP1State", initEntry) = k - · rw [if_pos h, if_pos h.symm] - · rw [if_neg h, if_neg (fun hh => h hh.symm)] - have ef : (if (InteractionKind.State, "SP1State", finalEntry) = k then (1 : ℤ) else 0) - = (if k = (InteractionKind.State, "SP1State", finalEntry) then 1 else 0) := by - by_cases h : (InteractionKind.State, "SP1State", finalEntry) = k - · rw [if_pos h, if_pos h.symm] - · rw [if_neg h, if_neg (fun hh => h hh.symm)] - rw [ei, ef] at hb - linarith [hb] + simp only [eq_comm] at hb + linarith /-- **Balance ⇒ a real-row execution trail.** Combining `balanced_state_bus` with the abstract `GatedVm.exists_trail`: from the balanced state bus (+ genesis/finalization boundary) there is a walk of diff --git a/SP1Clean/Soundness/InstructionTrace.lean b/SP1Clean/Soundness/InstructionTrace.lean index ed2c77f8..e78506a4 100644 --- a/SP1Clean/Soundness/InstructionTrace.lean +++ b/SP1Clean/Soundness/InstructionTrace.lean @@ -21,7 +21,7 @@ namespace SP1Clean.Soundness -- stronger bound with the project-standard `Fact (2 ^ 17 < p)` derived locally. variable {p : ℕ} [Fact p.Prime] [Fact (2 ^ 24 < p)] -local instance : Fact (2 ^ 17 < p) := ⟨by have := Fact.out (p := 2 ^ 24 < p); omega⟩ +local instance : Fact (2 ^ 17 < p) := ⟨lt_of_le_of_lt (by norm_num) (Fact.out (p := 2 ^ 24 < p))⟩ /-! ## The decoded instruction -/ diff --git a/SP1Clean/Soundness/MemoryConsistency.lean b/SP1Clean/Soundness/MemoryConsistency.lean index e6f579be..1d91ab05 100644 --- a/SP1Clean/Soundness/MemoryConsistency.lean +++ b/SP1Clean/Soundness/MemoryConsistency.lean @@ -29,6 +29,15 @@ open SP1Clean.LookupAccessList variable {p : ℕ} +private theorem two_lt_p_aux [Fact (2 ^ 17 < p)] : 2 < p := + lt_of_lt_of_le (by norm_num) (Fact.out (p := 2 ^ 17 < p)).le + +/-- A nonzero `ZMod p` element has strictly-positive `ℤ`-cast value — the multiplicity-positivity +workhorse for the Memory-bus send contributions. -/ +private theorem valCast_pos_aux [NeZero p] {x : ZMod p} (hx : x ≠ 0) : 0 < (x.val : ℤ) := by + have : x.val ≠ 0 := fun hv => hx (ZMod.val_injective p (by rw [ZMod.val_zero]; exact hv)) + omega + /-- The recombined low clock for a row (`clk_0_16 + clk_16_24 * 2^16`), matching `Extracted/AddChip`'s CS2 `clk_low` argument. -/ def rowClkLow (r : Trace.RowView (ZMod p)) : ZMod p := @@ -203,10 +212,7 @@ theorem memoryLookups_eq_emitted (r : Trace.RowView (ZMod p)) (env : Environment memoryLookups r = (((Readers.RTypeReader.main input).operations offset).interactionsWith memoryChannel.toRaw).map (AbstractInteraction.toAccess env) := by - have hp2 : 2 < p := by - have h := Fact.out (p := 2 ^ 17 < p) - have h2 : (2 : ℕ) < 2 ^ 17 := by norm_num - omega + have hp2 : 2 < p := two_lt_p_aux -- The three `RegisterAccessCols` sub-assertions emit only `byteChannel`, and the five `op_a_0` Equality -- gates emit nothing — so their `memoryChannel` filter is empty (`filter_interactions_formalAssertion_eq_nil`). have hrac := fun (n : ℕ) (inp : Var Readers.RegisterAccessCols.Inputs (ZMod p)) => @@ -314,10 +320,7 @@ theorem memAccessLookups_eq_emitted (env : Environment (ZMod p)) (offset : ℕ) memAccessLookups mem clk_high clk_low addr0 addr1 addr2 new_value is_real = (((Readers.MemoryAccess.main input).operations offset).interactionsWith memoryChannel.toRaw).map (AbstractInteraction.toAccess env) := by - have hp2 : 2 < p := by - have h := Fact.out (p := 2 ^ 17 < p) - have h2 : (2 : ℕ) < 2 ^ 17 := by norm_num - omega + have hp2 : 2 < p := two_lt_p_aux -- the three `=== 0` timestamp gates compile to `Gadgets.Equality.circuit id` subcircuits, which emit -- nothing on `memoryChannel`; the two `byteChannel.pullIf`s are filtered by channel-distinctness. have heq := fun (n : ℕ) (inp : Var (ProvablePair id id) (ZMod p)) => @@ -469,6 +472,19 @@ address). The per-address single-address chain ranges over `(eventsAt rows addr) def eventsAt (rows : List (Trace.RowView (ZMod p))) (addr : ℕ) : List (MemEvent (ZMod p)) := (memEventsFiltered rows).filter (fun e => decide (e.addr.val = addr)) +/-- A per-address access (`∈ eventsAt`) is one of the trace's bus-backed events +(`∈ memEventsFiltered`). Shared by the single-address chain proof and the `isU64` recovery. -/ +theorem mem_memEventsFiltered_of_mem_eventsAt {rows : List (Trace.RowView (ZMod p))} + {addr : ℕ} {e : MemEvent (ZMod p)} (he : e ∈ eventsAt rows addr) : + e ∈ memEventsFiltered rows := by + rw [eventsAt] at he; exact List.mem_of_mem_filter he + +/-- Membership in `eventsAt rows addr` pins the event's address (`.val`). -/ +theorem addr_of_mem_eventsAt {rows : List (Trace.RowView (ZMod p))} + {addr : ℕ} {e : MemEvent (ZMod p)} (he : e ∈ eventsAt rows addr) : + e.addr.val = addr := by + rw [eventsAt] at he; have := (List.mem_filter.mp he).2; simpa using this + /-- The vendored `filterAddress` of the filtered access list is `eventsAt` mapped through `toAccess` — the bridge that lets the per-address single-address proof work at the event level (where `prevTs` lives). -/ @@ -518,10 +534,7 @@ theorem mem_opA_read_send_matched [NeZero p] (r.is_real.val : ℤ)) with hsend have h_send_mem : send ∈ aggregateChipRows rows memoryLookups ++ memProv := List.mem_append_left _ (List.mem_flatMap.mpr ⟨r, hr, by rw [hsend]; simp [memoryLookups]⟩) - have hvne : (r.is_real.val : ℤ) ≠ 0 := by - have : r.is_real.val ≠ 0 := fun hv => h_real (ZMod.val_injective p (by rw [ZMod.val_zero]; exact hv)) - exact_mod_cast this - have hpos : 0 < multOf send := by rw [hsend]; simp only [multOf]; omega + have hpos : 0 < multOf send := by rw [hsend]; simpa only [multOf] using valCast_pos_aux h_real obtain ⟨b, hb, hbk, hbneg⟩ := balanced_pos_has_neg (aggregateChipRows rows memoryLookups ++ memProv) (keyOf send) h_bal send h_send_mem rfl hpos @@ -566,11 +579,6 @@ theorem memEvent_send_pos_in_bus [NeZero p] (rows : List (Trace.RowView (ZMod p) obtain ⟨r, hr, he⟩ := he rw [List.mem_reverse] at hr simp only [aggregateChipRows] - -- helper: turn `is_real ≠ 0` (from the gate) into `0 < is_real.val` - have valpos : ∀ x : ZMod p, x ≠ 0 → 0 < ((x.val : ℤ)) := by - intro x hx - have hxv : x.val ≠ 0 := fun hv => hx (ZMod.val_injective p (by rw [ZMod.val_zero]; exact hv)) - omega rw [rowMemEventsGated] at he by_cases hreal : r.is_real = 0 · rw [if_pos hreal] at he; exact absurd he List.not_mem_nil @@ -584,7 +592,7 @@ theorem memEvent_send_pos_in_bus [NeZero p] (rows : List (Trace.RowView (ZMod p) (r.is_real.val : ℤ)), List.mem_flatMap.mpr ⟨r, hr, by simp [memoryLookups]⟩, ?_, ?_⟩ · simp [keyOf, memEvent_sendKey, opAEvent] - · simp only [multOf]; exact valpos _ hreal + · simp only [multOf]; exact valCast_pos_aux hreal rw [List.mem_append] at he rcases he with he | he · -- op_b: present only when imm_b = 0 @@ -598,7 +606,7 @@ theorem memEvent_send_pos_in_bus [NeZero p] (rows : List (Trace.RowView (ZMod p) ((r.is_real * (1 - r.adapter.imm_b)).val : ℤ)), List.mem_flatMap.mpr ⟨r, hr, by simp [memoryLookups]⟩, ?_, ?_⟩ · simp [keyOf, memEvent_sendKey, opBEvent] - · simp only [multOf, himmb, sub_zero, mul_one]; exact valpos _ hreal + · simp only [multOf, himmb, sub_zero, mul_one]; exact valCast_pos_aux hreal · rw [if_neg himmb] at he; exact absurd he List.not_mem_nil · -- op_c: present only when imm_c = 0 by_cases himmc : r.adapter.imm_c = 0 @@ -611,7 +619,7 @@ theorem memEvent_send_pos_in_bus [NeZero p] (rows : List (Trace.RowView (ZMod p) ((r.is_real * (1 - r.adapter.imm_c)).val : ℤ)), List.mem_flatMap.mpr ⟨r, hr, by simp [memoryLookups]⟩, ?_, ?_⟩ · simp [keyOf, memEvent_sendKey, opCEvent] - · simp only [multOf, himmc, sub_zero, mul_one]; exact valpos _ hreal + · simp only [multOf, himmc, sub_zero, mul_one]; exact valCast_pos_aux hreal · rw [if_neg himmc] at he; exact absurd he List.not_mem_nil /-- **Negative entry is a receive.** A strictly-negative `memoryLookups r` entry (under per-row @@ -778,9 +786,9 @@ theorem isConsistentSingleAddress_of_balance [NeZero p] (rows : List (Trace.RowV (h_sorted : MemoryAccessList.isTimestampSorted ((eventsAt rows addr).map MemEvent.toAccess)) : MemoryAccessList.isConsistentSingleAddress ((eventsAt rows addr).map MemEvent.toAccess) h_sorted := by have hsub : ∀ {e}, e ∈ eventsAt rows addr → e ∈ memEventsFiltered rows := by - intro e he; rw [eventsAt] at he; exact List.mem_of_mem_filter he + intro e he; exact mem_memEventsFiltered_of_mem_eventsAt he have haddr : ∀ {e}, e ∈ eventsAt rows addr → e.addr.val = addr := by - intro e he; rw [eventsAt] at he; have := (List.mem_filter.mp he).2; simpa using this + intro e he; exact addr_of_mem_eventsAt he refine isConsistentSingleAddress_of_chain (eventsAt rows addr) h_sorted ?_ ?_ · -- adjacency: each read equals the next (earlier) write rw [List.isChain_iff_getElem] diff --git a/SP1Clean/Soundness/MemoryIsU64.lean b/SP1Clean/Soundness/MemoryIsU64.lean index 8dd33211..5db700eb 100644 --- a/SP1Clean/Soundness/MemoryIsU64.lean +++ b/SP1Clean/Soundness/MemoryIsU64.lean @@ -70,12 +70,12 @@ theorem memEvent_prevValue_eq_writer [NeZero p] (rows : List (Trace.RowView (ZMo (h_prov : MemProviderGenesis memProv rows) (h_bal : isConsistentBalanced (aggregateChipRows rows memoryLookups ++ memProv)) : List.IsChain limbReadEq (eventsAt rows addr) := by - have hsub : ∀ {e}, e ∈ eventsAt rows addr → e ∈ memEventsFiltered rows := by - intro e he; rw [eventsAt] at he; exact List.mem_of_mem_filter he rw [List.isChain_iff_getElem] intro i hi - have he_mem := hsub (List.getElem_mem (show i < (eventsAt rows addr).length by omega)) - have he'_mem := hsub (List.getElem_mem (show i + 1 < (eventsAt rows addr).length from hi)) + have he_mem := mem_memEventsFiltered_of_mem_eventsAt + (List.getElem_mem (show i < (eventsAt rows addr).length by omega)) + have he'_mem := mem_memEventsFiltered_of_mem_eventsAt + (List.getElem_mem (show i + 1 < (eventsAt rows addr).length from hi)) have hprevts := h_prev addr i hi rcases memEvent_canceller rows memProv hwf h_bal _ he_mem with ⟨e'', he''_mem, he''_key⟩ | ⟨b, hb, hbkey, _⟩ @@ -101,14 +101,10 @@ theorem eventsAt_genesis_reads_zero [NeZero p] (rows : List (Trace.RowView (ZMod (h_bal : isConsistentBalanced (aggregateChipRows rows memoryLookups ++ memProv)) (hne : eventsAt rows addr ≠ []) : wordToNat ((eventsAt rows addr).getLast hne).prevValue = 0 := by - have hsub : ∀ {e}, e ∈ eventsAt rows addr → e ∈ memEventsFiltered rows := by - intro e he; rw [eventsAt] at he; exact List.mem_of_mem_filter he - have haddr : ∀ {e}, e ∈ eventsAt rows addr → e.addr.val = addr := by - intro e he; rw [eventsAt] at he; have := (List.mem_filter.mp he).2; simpa using this have h_sorted : MemoryAccessList.isTimestampSorted ((eventsAt rows addr).map MemEvent.toAccess) := by rw [← filterAddress_memAccessListFiltered] exact MemoryAccessList.filterAddress_sorted _ (memAccessListFiltered_isTimestampSorted rows h_clk) addr - have he_mem := hsub (List.getLast_mem hne) + have he_mem := mem_memEventsFiltered_of_mem_eventsAt (List.getLast_mem hne) rcases memEvent_canceller rows memProv hwf h_bal _ he_mem with ⟨e'', he''_mem, he''_key⟩ | ⟨b, hb, hbkey, _⟩ · exfalso @@ -116,7 +112,7 @@ theorem eventsAt_genesis_reads_zero [NeZero p] (rows : List (Trace.RowView (ZMod have he''_at : e'' ∈ eventsAt rows addr := by rw [eventsAt] exact List.mem_filter.mpr ⟨he''_mem, - decide_eq_true_eq.mpr (haddr'.trans (haddr (List.getLast_mem hne)))⟩ + decide_eq_true_eq.mpr (haddr'.trans (addr_of_mem_eventsAt (List.getLast_mem hne)))⟩ have hle := getLast_clk_le h_sorted hne he''_at have hlt := h_prevlt _ he_mem omega @@ -219,9 +215,7 @@ theorem eventsAt_values_isU64 [NeZero p] {rows : List (Trace.RowView (ZMod p))} h.balanced hne) ?_ intro e he hprevU - have he_mem : e ∈ memEventsFiltered rows := by - rw [eventsAt] at he - exact List.mem_of_mem_filter he + have he_mem := mem_memEventsFiltered_of_mem_eventsAt he rcases memEventsFiltered_value_cases rows e he_mem with ⟨r, hr, hreal, hev⟩ | hval · rw [hev] exact h.writeU64 r hr hreal diff --git a/SP1Clean/Soundness/ProgramConsistency.lean b/SP1Clean/Soundness/ProgramConsistency.lean index a697c958..3d379d66 100644 --- a/SP1Clean/Soundness/ProgramConsistency.lean +++ b/SP1Clean/Soundness/ProgramConsistency.lean @@ -140,9 +140,8 @@ theorem programConsistent_of_balance [NeZero p] (programAccess r).op_c[2].val, (programAccess r).op_c[3].val, (programAccess r).op_a_0.val, (programAccess r).imm_b.val, (programAccess r).imm_c.val], ((programAccess r).is_real.val : ℤ)) with hsend - have h_eq : programLookups r = [send] := by rw [hsend]; rfl have h_send_mem : send ∈ aggregateChipRows rows programLookups := - List.mem_flatMap.mpr ⟨r, hr, by rw [h_eq]; exact List.mem_singleton.mpr rfl⟩ + List.mem_flatMap.mpr ⟨r, hr, List.mem_singleton.mpr rfl⟩ have hvne : (programAccess r).is_real.val ≠ 0 := fun hv => h_real (ZMod.val_injective p (by rw [ZMod.val_zero]; exact hv)) have h_pos : 0 < multOf send := by rw [hsend]; simp only [multOf]; omega @@ -197,10 +196,7 @@ theorem programLookups_eq_emitted [Fact p.Prime] [Fact (2 ^ 17 < p)] have heq := fun (n : ℕ) (inp : Var (ProvablePair id id) (ZMod p)) => filter_interactions_formalAssertion_eq_nil (Gadgets.Equality.circuit id) programChannel.toRaw (n := n) inp List.not_mem_nil List.not_mem_nil - have hp2 : 2 < p := by - have h := Fact.out (p := 2 ^ 17 < p) - have h2 : (2 : ℕ) < 2 ^ 17 := by norm_num - omega + have hp2 : 2 < p := by have := Fact.out (p := 2 ^ 17 < p); omega -- reduce `interactionsWith` to the single program emit (RAC subs + Equality gates + memory emits drop; -- the memory emits' `if memoryChannel = programChannel` collapse via the channel distinctness + `if_false`) simp only [Readers.RTypeReader.main, circuit_norm, hrac, heq, diff --git a/SP1Clean/Soundness/ProgramProviderSpike.lean b/SP1Clean/Soundness/ProgramProviderSpike.lean index 0a206c03..0320e4dc 100644 --- a/SP1Clean/Soundness/ProgramProviderSpike.lean +++ b/SP1Clean/Soundness/ProgramProviderSpike.lean @@ -100,15 +100,13 @@ omit [NeZero p] in theorem demoRomRow_valid [Fact (2 ^ 17 < p)] : ProgramRowSpec (demoRomRow (p := p)) := by have h32 : (32 : ℕ) < p := by have := Fact.out (p := 2 ^ 17 < p); omega refine ⟨?_, ?_, ?_, ?_, ?_, ?_, Or.inl rfl⟩ <;> - simp only [demoRomRow, Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero] <;> - first - | (rw [show (1 : ZMod p) = ((1 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt (by omega)]; norm_num) - | (rw [show (2 : ZMod p) = ((2 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt (by omega)]; norm_num) - | (rw [show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by norm_cast, - ZMod.val_natCast_of_lt (by omega)]; norm_num) - | (rw [ZMod.val_zero]; norm_num) + simp only [demoRomRow, Vector.getElem_mk, List.getElem_toArray, List.getElem_cons_zero, + show (1 : ZMod p) = ((1 : ℕ) : ZMod p) from by norm_cast, + show (2 : ZMod p) = ((2 : ℕ) : ZMod p) from by norm_cast, + show (3 : ZMod p) = ((3 : ℕ) : ZMod p) from by norm_cast, + ZMod.val_natCast_of_lt (show (3 : ℕ) < p by omega), + ZMod.val_natCast_of_lt (show (2 : ℕ) < p by omega), + ZMod.val_natCast_of_lt (show (1 : ℕ) < p by omega), ZMod.val_zero] <;> omega omit [NeZero p] in /-- The constructed provider for the one-row demo ROM is a genuine `ProgramProvider` (no assumption) — @@ -116,6 +114,6 @@ the `programProvider_of_validRom` construction is inhabited, not vacuous. -/ theorem demoRom_programProvider [Fact (2 ^ 17 < p)] (mult : ProgramRow (ZMod p) → ℤ) : ProgramProvider (p := p) ProgramRowSpec (romContributions [demoRomRow (p := p)] mult) := programProvider_of_validRom [demoRomRow] mult - (by intro row hrow; simp only [List.mem_singleton] at hrow; subst hrow; exact demoRomRow_valid) + fun _ hrow => List.mem_singleton.1 hrow ▸ demoRomRow_valid end SP1Clean.Soundness diff --git a/SP1Clean/Soundness/SP1GatedVm.lean b/SP1Clean/Soundness/SP1GatedVm.lean index 95ec698f..62b5ffb1 100644 --- a/SP1Clean/Soundness/SP1GatedVm.lean +++ b/SP1Clean/Soundness/SP1GatedVm.lean @@ -253,9 +253,7 @@ theorem sp1_state_balance_of_balancedInteractions exact stateLookups_mult_binary hp v (hbin v hv) a hav · -- the two constant-`±1` boundary entries simp only [List.mem_cons, List.not_mem_nil, or_false] at ha - rcases ha with rfl | rfl - · right; right; rfl - · left; rfl + rcases ha with rfl | rfl <;> simp [multOf] /-- **The witness → gated-capstone bridge (assembled; `sorry`-free given the decode seam).** From the ensemble `Statement`'s per-table constraints and balanced channels, the heterogeneous trace decoded @@ -277,8 +275,8 @@ theorem sp1_gatedExecution_prereqs (witness : EnsembleWitness (sp1GatedVm (p := -- instantiate the balanced channels at the State channel (the ensemble channel-list head) have h_balanced : BalancedInteractions (witness.interactionsWith Channels.stateChannel.toRaw) := by - have h := (hB Channels.stateChannel.toRaw (List.mem_cons_self ..)).1 - rwa [EnsembleWitness.interactionsWith_allTablesWitness] at h + rw [← EnsembleWitness.interactionsWith_allTablesWitness] + exact (hB Channels.stateChannel.toRaw (List.mem_cons_self ..)).1 exact ⟨rows, data, h_spec, hbin, sp1_state_balance_of_balancedInteractions witness.publicInput rows hbin _ (fun i hi => EnsembleWitness.channel_eq_of_mem_interactionsWith hi) h_balanced h_corr⟩ diff --git a/SP1Clean/Soundness/StateConsistency.lean b/SP1Clean/Soundness/StateConsistency.lean index 47aee3ac..21fa013d 100644 --- a/SP1Clean/Soundness/StateConsistency.lean +++ b/SP1Clean/Soundness/StateConsistency.lean @@ -141,9 +141,7 @@ theorem stateLookups_mult_binary (hp : 2 < p) (r : Trace.RowView (ZMod p)) intro a ha have hv := val_of_binary hp h_real simp only [stateLookups, List.mem_cons, List.not_mem_nil, or_false] at ha - rcases ha with rfl | rfl <;> - · simp only [multOf, stateAccess] - omega + rcases ha with rfl | rfl <;> simp only [multOf, stateAccess] <;> omega /-- **Successor exists (from State balance).** On a balanced State bus, every real row `a`'s `send` key `(clk_high, clk_low+clk_inc, next_pc)` is cancelled by the `receive` of some real row `b` — so `b`'s @@ -253,8 +251,7 @@ theorem pcChainProp_iff_isChain (l : List (StateAccess (ZMod p))) : cases t with | nil => exact ⟨fun _ => List.isChain_singleton a, fun _ => trivial⟩ | cons b t' => - rw [List.isChain_cons_cons, ← IH] - rfl + simp only [List.isChain_cons_cons, ← IH, pcChainProp, pcStep] /-- **Clock injectivity, getElem form.** `clkInjective` rephrased over row indices: two rows with equal encoded clocks are the same index. The bridge `state_adjacent_pc_handoff` consumes. -/ @@ -383,10 +380,7 @@ theorem stateLookups_eq_emitted [Fact p.Prime] [Fact (2 ^ 17 < p)] h_np0, h_np1, h_np2, h_clk] -- only the multiplicities differ: `signedVal (∓eval is_real)` vs `∓is_real.val`. `h_ir` ties the eval to -- `r.is_real`; the `signedVal` lemmas evaluate the centered representative on the gate. - have hp2 : 2 < p := by - have h := Fact.out (p := 2 ^ 17 < p) - have h2 : (2 : ℕ) < 2 ^ 17 := by norm_num - omega + have hp2 : 2 < p := by have := Fact.out (p := 2 ^ 17 < p); omega rw [h_ir, signedVal_neg_is_real hp2 h_real, signedVal_is_real hp2 h_real] end SP1Clean.Soundness diff --git a/SP1Clean/Soundness/TargetVm.lean b/SP1Clean/Soundness/TargetVm.lean index 30add78f..96a173d0 100644 --- a/SP1Clean/Soundness/TargetVm.lean +++ b/SP1Clean/Soundness/TargetVm.lean @@ -255,7 +255,6 @@ private theorem chain_to_refines congr 1 exact sndPc_eq_rcvPc (isWalk_chain hw.1 i hi) · intro idx - have hi' : i < path.length := by omega by_cases hcond : (idx.toNat : ZMod p) = (path[i]'hi').adapter.op_a · rw [heff.regs.1 idx hcond, replayVal, dif_pos hi', if_pos hcond] · rw [heff.regs.2 idx hcond, href.frame idx, replayVal, dif_pos hi', if_neg hcond] diff --git a/SP1Clean/Soundness/ValueBound.lean b/SP1Clean/Soundness/ValueBound.lean index 3d5bf762..a7953ad7 100644 --- a/SP1Clean/Soundness/ValueBound.lean +++ b/SP1Clean/Soundness/ValueBound.lean @@ -93,9 +93,7 @@ lemma walk_clk_monotone {pi : SP1PublicIO (ZMod p)} {rows : List (ChipRow p)} {path : List (Trace.RowView (ZMod p))} (hw : WalkOf pi rows path) (i : ℕ) (hi : i + 1 < path.length) : sndClkOf (stateAccess (path[i]'(by omega))) = rcvClkOf (stateAccess (path[i + 1]'hi)) := by - have h := isWalk_chain hw.1 i hi - simp only [stateEdge] at h - exact sndClk_eq_rcvClk h + exact sndClk_eq_rcvClk (by simpa only [stateEdge] using isWalk_chain hw.1 i hi) /-! ## The concrete decode∧value `OperandsBound` -/ diff --git a/SP1Clean/Proofs/TraceGenTests/AddChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/AddChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/AddChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/AddChipTraceVectors.lean index 709ff786..30cdcd8c 100644 --- a/SP1Clean/Proofs/TraceGenTests/AddChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/AddChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/AddChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/AddChipTraceWitness.lean similarity index 93% rename from SP1Clean/Proofs/TraceGenTests/AddChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/AddChipTraceWitness.lean index 8eaa955d..635afe9c 100644 --- a/SP1Clean/Proofs/TraceGenTests/AddChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/AddChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.AddChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.AddChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.AddChipTraceVectors /-! # Whole-trace conformance anchor for `AddChip` — the circuit IS the trace generator. diff --git a/SP1Clean/Proofs/TraceGenTests/AddwChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/AddwChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/AddwChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/AddwChipTraceVectors.lean index 5e1ab794..b8940bf3 100644 --- a/SP1Clean/Proofs/TraceGenTests/AddwChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/AddwChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/AddwChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/AddwChipTraceWitness.lean similarity index 92% rename from SP1Clean/Proofs/TraceGenTests/AddwChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/AddwChipTraceWitness.lean index 285e32ee..40403737 100644 --- a/SP1Clean/Proofs/TraceGenTests/AddwChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/AddwChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.AddwChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.AddwChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.AddwChipTraceVectors /-! # Whole-trace conformance anchor for `AddwChip` — the first `ALUTypeReader` trace, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/BitwiseChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/BitwiseChipTraceVectors.lean index bfd28f3d..22c999ff 100644 --- a/SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/BitwiseChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/BitwiseChipTraceWitness.lean similarity index 93% rename from SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/BitwiseChipTraceWitness.lean index af31bda8..b5915a32 100644 --- a/SP1Clean/Proofs/TraceGenTests/BitwiseChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/BitwiseChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.BitwiseChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.BitwiseChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.BitwiseChipTraceVectors /-! # Whole-trace conformance anchor for `BitwiseChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/Conformance.lean b/SP1CleanTest/TraceGenTests/Conformance.lean similarity index 100% rename from SP1Clean/Proofs/TraceGenTests/Conformance.lean rename to SP1CleanTest/TraceGenTests/Conformance.lean diff --git a/SP1Clean/Proofs/TraceGenTests/DivRemChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/DivRemChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/DivRemChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/DivRemChipTraceVectors.lean index 9aa99581..c1f9dbb6 100644 --- a/SP1Clean/Proofs/TraceGenTests/DivRemChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/DivRemChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/DivRemChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/DivRemChipTraceWitness.lean similarity index 96% rename from SP1Clean/Proofs/TraceGenTests/DivRemChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/DivRemChipTraceWitness.lean index d02470e7..a7363784 100644 --- a/SP1Clean/Proofs/TraceGenTests/DivRemChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/DivRemChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Proofs.Chips.DivRemChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.DivRemChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.DivRemChipTraceVectors /-! # Whole-trace conformance anchor for `DivRemChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/EventPopulate.lean b/SP1CleanTest/TraceGenTests/EventPopulate.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/EventPopulate.lean rename to SP1CleanTest/TraceGenTests/EventPopulate.lean index 456d035d..3e17c7d6 100644 --- a/SP1Clean/Proofs/TraceGenTests/EventPopulate.lean +++ b/SP1CleanTest/TraceGenTests/EventPopulate.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.Conformance +import SP1CleanTest.TraceGenTests.Conformance /-! # Event → input-column mirrors for the trace-generation conformance layer. diff --git a/SP1Clean/Proofs/TraceGenTests/LtChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/LtChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/LtChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/LtChipTraceVectors.lean index 42039f76..3d728720 100644 --- a/SP1Clean/Proofs/TraceGenTests/LtChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/LtChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/LtChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/LtChipTraceWitness.lean similarity index 93% rename from SP1Clean/Proofs/TraceGenTests/LtChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/LtChipTraceWitness.lean index af57420f..d98c9cc8 100644 --- a/SP1Clean/Proofs/TraceGenTests/LtChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/LtChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.LtChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.LtChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.LtChipTraceVectors /-! # Whole-trace conformance anchor for `LtChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/MulChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/MulChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/MulChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/MulChipTraceVectors.lean index 219ef3c3..b4b63c04 100644 --- a/SP1Clean/Proofs/TraceGenTests/MulChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/MulChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/MulChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/MulChipTraceWitness.lean similarity index 94% rename from SP1Clean/Proofs/TraceGenTests/MulChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/MulChipTraceWitness.lean index c667bc5c..1b1d9450 100644 --- a/SP1Clean/Proofs/TraceGenTests/MulChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/MulChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.MulChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.MulChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.MulChipTraceVectors /-! # Whole-trace conformance anchor for `MulChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/ShiftLeftChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/ShiftLeftChipTraceVectors.lean index 50c6cefa..3d4c0db5 100644 --- a/SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/ShiftLeftChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/ShiftLeftChipTraceWitness.lean similarity index 95% rename from SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/ShiftLeftChipTraceWitness.lean index f6b99bbd..3e4a9b1c 100644 --- a/SP1Clean/Proofs/TraceGenTests/ShiftLeftChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/ShiftLeftChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Proofs.Chips.ShiftLeftChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.ShiftLeftChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.ShiftLeftChipTraceVectors /-! # Whole-trace conformance anchor for `ShiftLeftChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/ShiftRightChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/ShiftRightChipTraceVectors.lean index 45060fec..d9031777 100644 --- a/SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/ShiftRightChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/ShiftRightChipTraceWitness.lean similarity index 95% rename from SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/ShiftRightChipTraceWitness.lean index ebafcca4..6cc23dab 100644 --- a/SP1Clean/Proofs/TraceGenTests/ShiftRightChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/ShiftRightChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Proofs.Chips.ShiftRightChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.ShiftRightChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.ShiftRightChipTraceVectors /-! # Whole-trace conformance anchor for `ShiftRightChip` — hint-driven flags, unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/SubChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/SubChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/SubChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/SubChipTraceVectors.lean index 5d401c03..566b8498 100644 --- a/SP1Clean/Proofs/TraceGenTests/SubChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/SubChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/SubChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/SubChipTraceWitness.lean similarity index 91% rename from SP1Clean/Proofs/TraceGenTests/SubChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/SubChipTraceWitness.lean index 4cf4ece6..a209c4cd 100644 --- a/SP1Clean/Proofs/TraceGenTests/SubChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/SubChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.SubChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.SubChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.SubChipTraceVectors /-! # Whole-trace conformance anchor for `SubChip` — the circuit IS the trace generator. diff --git a/SP1Clean/Proofs/TraceGenTests/SubwChipTraceVectors.lean b/SP1CleanTest/TraceGenTests/SubwChipTraceVectors.lean similarity index 99% rename from SP1Clean/Proofs/TraceGenTests/SubwChipTraceVectors.lean rename to SP1CleanTest/TraceGenTests/SubwChipTraceVectors.lean index cf524512..a64804aa 100644 --- a/SP1Clean/Proofs/TraceGenTests/SubwChipTraceVectors.lean +++ b/SP1CleanTest/TraceGenTests/SubwChipTraceVectors.lean @@ -1,4 +1,4 @@ -import SP1Clean.Proofs.TraceGenTests.EventPopulate +import SP1CleanTest.TraceGenTests.EventPopulate /-! # AUTO-GENERATED — do not edit by hand. diff --git a/SP1Clean/Proofs/TraceGenTests/SubwChipTraceWitness.lean b/SP1CleanTest/TraceGenTests/SubwChipTraceWitness.lean similarity index 92% rename from SP1Clean/Proofs/TraceGenTests/SubwChipTraceWitness.lean rename to SP1CleanTest/TraceGenTests/SubwChipTraceWitness.lean index e8ca4437..e8b0318d 100644 --- a/SP1Clean/Proofs/TraceGenTests/SubwChipTraceWitness.lean +++ b/SP1CleanTest/TraceGenTests/SubwChipTraceWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Chips.SubwChip.Defs -import SP1Clean.Proofs.TraceGenTests.TraceGenerator -import SP1Clean.Proofs.TraceGenTests.SubwChipTraceVectors +import SP1CleanTest.TraceGenTests.TraceGenerator +import SP1CleanTest.TraceGenTests.SubwChipTraceVectors /-! # Whole-trace conformance anchor for `SubwChip` — a W-instruction trace, fully unmasked. diff --git a/SP1Clean/Proofs/TraceGenTests/TraceGenerator.lean b/SP1CleanTest/TraceGenTests/TraceGenerator.lean similarity index 100% rename from SP1Clean/Proofs/TraceGenTests/TraceGenerator.lean rename to SP1CleanTest/TraceGenTests/TraceGenerator.lean diff --git a/SP1Clean/Proofs/WitnessTests/AddOperationWitness.lean b/SP1CleanTest/WitnessTests/AddOperationWitness.lean similarity index 91% rename from SP1Clean/Proofs/WitnessTests/AddOperationWitness.lean rename to SP1CleanTest/WitnessTests/AddOperationWitness.lean index 31880f0d..f89646e2 100644 --- a/SP1Clean/Proofs/WitnessTests/AddOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/AddOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.AddOperation +import SP1CleanTest.WitnessTests.Vectors.AddOperation import SP1Clean.Native.Operations.AddOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `AddOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/AddrAddOperationWitness.lean b/SP1CleanTest/WitnessTests/AddrAddOperationWitness.lean similarity index 92% rename from SP1Clean/Proofs/WitnessTests/AddrAddOperationWitness.lean rename to SP1CleanTest/WitnessTests/AddrAddOperationWitness.lean index e9ebc82f..d8f87910 100644 --- a/SP1Clean/Proofs/WitnessTests/AddrAddOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/AddrAddOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.AddrAddOperation +import SP1CleanTest.WitnessTests.Vectors.AddrAddOperation import SP1Clean.Native.Operations.AddrAddOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `AddrAddOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/AddwOperationWitness.lean b/SP1CleanTest/WitnessTests/AddwOperationWitness.lean similarity index 92% rename from SP1Clean/Proofs/WitnessTests/AddwOperationWitness.lean rename to SP1CleanTest/WitnessTests/AddwOperationWitness.lean index ca18243b..592401b2 100644 --- a/SP1Clean/Proofs/WitnessTests/AddwOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/AddwOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.AddwOperation +import SP1CleanTest.WitnessTests.Vectors.AddwOperation import SP1Clean.Native.Operations.AddwOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `AddwOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/BitwiseOperationWitness.lean b/SP1CleanTest/WitnessTests/BitwiseOperationWitness.lean similarity index 93% rename from SP1Clean/Proofs/WitnessTests/BitwiseOperationWitness.lean rename to SP1CleanTest/WitnessTests/BitwiseOperationWitness.lean index 9d8963a8..70d2d028 100644 --- a/SP1Clean/Proofs/WitnessTests/BitwiseOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/BitwiseOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.BitwiseOperation +import SP1CleanTest.WitnessTests.Vectors.BitwiseOperation import SP1Clean.Native.Operations.BitwiseOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `BitwiseOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/IsEqualWordOperationWitness.lean b/SP1CleanTest/WitnessTests/IsEqualWordOperationWitness.lean similarity index 95% rename from SP1Clean/Proofs/WitnessTests/IsEqualWordOperationWitness.lean rename to SP1CleanTest/WitnessTests/IsEqualWordOperationWitness.lean index 278253e2..dc7fd535 100644 --- a/SP1Clean/Proofs/WitnessTests/IsEqualWordOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/IsEqualWordOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.IsEqualWordOperation +import SP1CleanTest.WitnessTests.Vectors.IsEqualWordOperation import SP1Clean.Native.Operations.IsEqualWordOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `IsEqualWordOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/IsZeroOperationWitness.lean b/SP1CleanTest/WitnessTests/IsZeroOperationWitness.lean similarity index 93% rename from SP1Clean/Proofs/WitnessTests/IsZeroOperationWitness.lean rename to SP1CleanTest/WitnessTests/IsZeroOperationWitness.lean index d7061470..b42ae61c 100644 --- a/SP1Clean/Proofs/WitnessTests/IsZeroOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/IsZeroOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.IsZeroOperation +import SP1CleanTest.WitnessTests.Vectors.IsZeroOperation import SP1Clean.Native.Operations.IsZeroOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `IsZeroOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/IsZeroWordOperationWitness.lean b/SP1CleanTest/WitnessTests/IsZeroWordOperationWitness.lean similarity index 94% rename from SP1Clean/Proofs/WitnessTests/IsZeroWordOperationWitness.lean rename to SP1CleanTest/WitnessTests/IsZeroWordOperationWitness.lean index 37946c1b..7825c1a8 100644 --- a/SP1Clean/Proofs/WitnessTests/IsZeroWordOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/IsZeroWordOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.IsZeroWordOperation +import SP1CleanTest.WitnessTests.Vectors.IsZeroWordOperation import SP1Clean.Native.Operations.IsZeroWordOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `IsZeroWordOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/LtOperationUnsignedWitness.lean b/SP1CleanTest/WitnessTests/LtOperationUnsignedWitness.lean similarity index 94% rename from SP1Clean/Proofs/WitnessTests/LtOperationUnsignedWitness.lean rename to SP1CleanTest/WitnessTests/LtOperationUnsignedWitness.lean index c70bd84d..51158d0c 100644 --- a/SP1Clean/Proofs/WitnessTests/LtOperationUnsignedWitness.lean +++ b/SP1CleanTest/WitnessTests/LtOperationUnsignedWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.LtOperationUnsigned +import SP1CleanTest.WitnessTests.Vectors.LtOperationUnsigned import SP1Clean.Native.Operations.LtOperationUnsigned.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `LtOperationUnsigned`. diff --git a/SP1Clean/Proofs/WitnessTests/MulOperationWitness.lean b/SP1CleanTest/WitnessTests/MulOperationWitness.lean similarity index 95% rename from SP1Clean/Proofs/WitnessTests/MulOperationWitness.lean rename to SP1CleanTest/WitnessTests/MulOperationWitness.lean index 6ed44575..7cda84dc 100644 --- a/SP1Clean/Proofs/WitnessTests/MulOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/MulOperationWitness.lean @@ -1,6 +1,6 @@ import SP1Clean.Native.Operations.MulOperation -import SP1Clean.Extracted.WitnessVectors.MulOperation -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.Vectors.MulOperation +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `MulOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/SubOperationWitness.lean b/SP1CleanTest/WitnessTests/SubOperationWitness.lean similarity index 91% rename from SP1Clean/Proofs/WitnessTests/SubOperationWitness.lean rename to SP1CleanTest/WitnessTests/SubOperationWitness.lean index 80b922e5..c26b2748 100644 --- a/SP1Clean/Proofs/WitnessTests/SubOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/SubOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.SubOperation +import SP1CleanTest.WitnessTests.Vectors.SubOperation import SP1Clean.Native.Operations.SubOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `SubOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/SubwOperationWitness.lean b/SP1CleanTest/WitnessTests/SubwOperationWitness.lean similarity index 92% rename from SP1Clean/Proofs/WitnessTests/SubwOperationWitness.lean rename to SP1CleanTest/WitnessTests/SubwOperationWitness.lean index 09bd35e7..4f902414 100644 --- a/SP1Clean/Proofs/WitnessTests/SubwOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/SubwOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.SubwOperation +import SP1CleanTest.WitnessTests.Vectors.SubwOperation import SP1Clean.Native.Operations.SubwOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `SubwOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/U16CompareOperationWitness.lean b/SP1CleanTest/WitnessTests/U16CompareOperationWitness.lean similarity index 92% rename from SP1Clean/Proofs/WitnessTests/U16CompareOperationWitness.lean rename to SP1CleanTest/WitnessTests/U16CompareOperationWitness.lean index 83321b07..a6fb9c3a 100644 --- a/SP1Clean/Proofs/WitnessTests/U16CompareOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/U16CompareOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.U16CompareOperation +import SP1CleanTest.WitnessTests.Vectors.U16CompareOperation import SP1Clean.Native.Operations.U16CompareOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `U16CompareOperation`. diff --git a/SP1Clean/Proofs/WitnessTests/U16MSBOperationWitness.lean b/SP1CleanTest/WitnessTests/U16MSBOperationWitness.lean similarity index 90% rename from SP1Clean/Proofs/WitnessTests/U16MSBOperationWitness.lean rename to SP1CleanTest/WitnessTests/U16MSBOperationWitness.lean index 9f1392a6..36fd363e 100644 --- a/SP1Clean/Proofs/WitnessTests/U16MSBOperationWitness.lean +++ b/SP1CleanTest/WitnessTests/U16MSBOperationWitness.lean @@ -1,6 +1,6 @@ -import SP1Clean.Extracted.WitnessVectors.U16MSBOperation +import SP1CleanTest.WitnessTests.Vectors.U16MSBOperation import SP1Clean.Native.Operations.U16MSBOperation.Populate -import SP1Clean.Proofs.WitnessTests.WitnessConformance +import SP1CleanTest.WitnessTests.WitnessConformance /-! # Witness-generation conformance anchor for `U16MSBOperation`. diff --git a/SP1Clean/Extracted/WitnessVectors/AddOperation.lean b/SP1CleanTest/WitnessTests/Vectors/AddOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/AddOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/AddOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/AddrAddOperation.lean b/SP1CleanTest/WitnessTests/Vectors/AddrAddOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/AddrAddOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/AddrAddOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/AddwOperation.lean b/SP1CleanTest/WitnessTests/Vectors/AddwOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/AddwOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/AddwOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/BitwiseOperation.lean b/SP1CleanTest/WitnessTests/Vectors/BitwiseOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/BitwiseOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/BitwiseOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/IsEqualWordOperation.lean b/SP1CleanTest/WitnessTests/Vectors/IsEqualWordOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/IsEqualWordOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/IsEqualWordOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/IsZeroOperation.lean b/SP1CleanTest/WitnessTests/Vectors/IsZeroOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/IsZeroOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/IsZeroOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/IsZeroWordOperation.lean b/SP1CleanTest/WitnessTests/Vectors/IsZeroWordOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/IsZeroWordOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/IsZeroWordOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/LtOperationUnsigned.lean b/SP1CleanTest/WitnessTests/Vectors/LtOperationUnsigned.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/LtOperationUnsigned.lean rename to SP1CleanTest/WitnessTests/Vectors/LtOperationUnsigned.lean diff --git a/SP1Clean/Extracted/WitnessVectors/MulOperation.lean b/SP1CleanTest/WitnessTests/Vectors/MulOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/MulOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/MulOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/SubOperation.lean b/SP1CleanTest/WitnessTests/Vectors/SubOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/SubOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/SubOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/SubwOperation.lean b/SP1CleanTest/WitnessTests/Vectors/SubwOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/SubwOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/SubwOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/U16CompareOperation.lean b/SP1CleanTest/WitnessTests/Vectors/U16CompareOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/U16CompareOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/U16CompareOperation.lean diff --git a/SP1Clean/Extracted/WitnessVectors/U16MSBOperation.lean b/SP1CleanTest/WitnessTests/Vectors/U16MSBOperation.lean similarity index 100% rename from SP1Clean/Extracted/WitnessVectors/U16MSBOperation.lean rename to SP1CleanTest/WitnessTests/Vectors/U16MSBOperation.lean diff --git a/SP1Clean/Proofs/WitnessTests/WitnessConformance.lean b/SP1CleanTest/WitnessTests/WitnessConformance.lean similarity index 100% rename from SP1Clean/Proofs/WitnessTests/WitnessConformance.lean rename to SP1CleanTest/WitnessTests/WitnessConformance.lean diff --git a/docs/agents/README.md b/docs/agents/README.md index 8bbef2f5..bc04985a 100644 --- a/docs/agents/README.md +++ b/docs/agents/README.md @@ -8,7 +8,7 @@ reader-facing docs one level up (start with [`../architecture.md`](../architectu ## Index - [porting-recipe.md](porting-recipe.md) — step-by-step checklist to port a new chip from the Add/Bitwise template: create the four artifacts, wire the root import, verify build + axioms. -- [proof-patterns.md](proof-patterns.md) — the witnessed-`FormalCircuit` soundness/completeness skeleton; `ZMod p` / `Word` / `circuit_proof_start` landmines; `maxHeartbeats` floors; the `ElaboratedCircuit` field-obligation recipe. +- [proof-patterns.md](proof-patterns.md) — the witnessed-`FormalCircuit` soundness/completeness skeleton; `ZMod p` / `Word` / `circuit_proof_start` landmines; `maxHeartbeats` floors; the `ElaboratedCircuit` field-obligation recipe; the **Golf & cleanup discipline** section (how to golf/clean proofs safely). - [lean-sail-notes.md](lean-sail-notes.md) — the Lean 4.28 environment: toolchain pins, why public Clean `main`, the GitHub-fetched Sail deps, the `lake update` toolchain-bump trap, and the Clean-main ↔ Batteries `Fin.foldl` collision and its fix. - [extraction.md](extraction.md) — the constraint-extraction pipeline (`sp1-constraint-compiler` → `update_extracted.py` → Lean) and the DSL contract. Point-in-time snapshots (regenerate before release) live under [`../snapshots/`](../snapshots/): diff --git a/docs/agents/proof-patterns.md b/docs/agents/proof-patterns.md index 0c7f512a..ffdea34c 100644 --- a/docs/agents/proof-patterns.md +++ b/docs/agents/proof-patterns.md @@ -534,6 +534,132 @@ first ``` See `Proofs/Chips/AddChip.lean` and `Native/Readers/RegisterAccessCols.lean` for the worked examples. +## Compile-time / performance landmines + +The compile-time profile lives in `docs/snapshots/compile-profile.md` (regenerate with +`scripts/profile_compile.sh`). These are the durable, still-true lessons behind it — apply them when +adding a chip or chasing a slow file. The broad attribute/macro wins have already been harvested +(below); remaining cost is term-intrinsic in the DivRem/Shift/Mul heavies, so new slowness almost +always means a *local* regression against one of these. + +- **The `v[i]` index-bound tax — already fixed by the `decide` fast path.** Every `v[i]` elaborates + an `i < n` bound side-goal; in Lean 4.28's Std the slice-support `get_elem_tactic_extensible` rule + does ~11 full-context traversals (`rw … at *`, `dsimp … at *`, a slice `simp`) per index — ~0.34s + for `1 < 4` inside a hypothesis-heavy soundness proof, paid in every `have` type. `Math/GetElemFastPath.lean` + registers one `macro_rules | get_elem_tactic_extensible => decide` line *above* Std's (so it's tried + first among the extensible rules, still after `done`/`assumption`): literal bounds close by kernel + `Nat.decLt` in ~26 heartbeats, non-literals fall through. This single line halved the swept set when + it landed; **don't regress it** (e.g. by importing a module that re-shadows the rule below Std's). If + a `have`-dense proof suddenly slows, suspect the fast path isn't in scope. + +- **`circuit_proof_start` / bind-chain normalization is NOT the bottleneck — don't chase it.** + Measured at 6k–32k heartbeats on medium chips, ~320k (~90s) even on DivRem's `main` (the repo's + biggest). The once-per-chip `main_ops_eq` lemma would save ~4% — not worth it. Slow soundness files + are slow in their *proof body* (the arithmetic / product-glue `simpa`s), not in the start tactic. + +- **`ElaboratedCircuit` `localLength_eq`'s `rfl` default whnf-unfolds `main` — seconds on a big main.** + On a 17-op `main` the default `rfl` costs ~15s; `channelsLawful`'s default fails outright on + channel-heavy mains. Hand-write the simp-route `localLength_eq` (and the `channelsWith*_eq` / + `circuit_localLength` rfl-lemmas) on every chip with a non-trivial `main` so the defaults stay cheap + — see the "ElaboratedCircuit field obligations" section above for the full recipe. Chips with small + mains (2–5 ops) can keep the `rfl` default; it's cheap there. + +- **`set_option linter.all false` on the generated `Extracted/` modules removes the linter tax.** The + ~76 auto-gen modules carry it in their headers; keep it when regenerating (the linter passes over the + monolithic generated terms were a measurable chunk of their cost). + +- **`maxHeartbeats` tightening is the *wrong* lever — don't chase the ceilings.** The heavy Shift/DivRem + proofs are kernel / type-checking-bound, not heartbeat-bound: `ShiftLeftChip/Soundness/Sll.soundness` + measures at **72** elaboration heartbeats against its 4M ceiling. The high ceilings are non-binding safety + margins; lowering them has no wall-clock effect and only risks a future spike. Real cost is term-intrinsic + (the `2^64` reductions, the product-glue `simpa`s) — chase *that* (the abstract-`BitVec` helpers + shared-tail + dedup below), not the `set_option` numbers. + +- **Shared-tail dedup for many-conjunct soundness proofs (the DivRem pattern).** When N conjunct files + each prove `GeneralFormalCircuit.Soundness` over the *same* `main`, they each re-elaborate the + byte-identical post-instruction "requirements tail" (readers + `is_real` + channel obligations) at a + high `maxHeartbeats` — N× the same work. Extract it once: a `requirements_holds` lemma proving the + tail with **raw** (un-`circuit_proof_start`'d) binders, a `SpecObligation Spec` wrapper, and a + `soundness_of_specObligation` that reassembles a full `Soundness` from a per-conjunct `SpecObligation` + plus the shared tail. Each conjunct then proves only its chip-specific `Spec` via a `spec_proof_start` + elab tactic (mirrors `circuit_proof_start`'s setup but unfolds `SpecObligation`, so it does *less* + work). See `Proofs/Chips/DivRemChip/Soundness/Tail.lean`. **Landmine:** `requirements_holds` must be + applied *by-term* on raw binders — after `circuit_proof_start` the decomposed context (destroyed + `input_var` binders, consumed `h_holds`) no longer matches, which is exactly why the tail lemma takes + raw binders and the `SpecObligation` indirection exists. The ShiftRight/ShiftLeft conjuncts share an + analogous `cpuA/msb*/aluA` block that is a candidate for the same treatment. + +## Golf & cleanup discipline + +How to golf/clean a proof without breaking the repo's invariants (axiom-clean, 0-warning, no `info:`). +Distilled from the 2026-06-22/23 cleanup campaign (109 files, −591 lines, axiom-cleanliness preserved). The +remaining deferred cleanup TODOs live under `docs/roadmap.md` § "Cleanup / polish backlog"; the available +cleanup skills are catalogued at the end of this section. + +**Instant, always-safe wins** (the bulk of the line savings): +- `:= by rfl` → `:= rfl`; `show T from by tac` → `show T by tac`; `rw […] at h; exact h` → `rwa […] at h`; + `refine F ?_; exact e` → `exact F e`; `simp only […] at h ⊢; exact h` → `simpa only […] using h`. +- Merge adjacent identical `simp only`/`rw`; inline a single-use `have x := e` that has **no** `by` body. +- Reach for mathlib instead of a hand proof: `zero_ne_one`, `Int.eq_zero_of_abs_lt_dvd`, etc. + +**The dominant structural win — eval-map factoring.** Chip/op `Formal.lean` proofs repeat a per-limb +`have eX : Expression.eval env input_var_X[i] = input_X[i] := by rw [← hX]; simp [Vector.getElem_map]`, one per +limb. Collapse the copies into ONE quantified helper +`have eX : ∀ i (hi : i < n), … := by intro i hi; rw [← hX]; simp [Vector.getElem_map]`, then call `eX i (by omega)` +at each site (~12–25%/file on Load/Store/op `Formal.lean`). A *global* lemma for the per-limb `eX` helper was +investigated and is **not** worth it: it saves only ~1 line/helper while re-churning ~36 already-clean +`Formal.lean` files plus a foundational rebuild, at form-variation risk. (The narrower length-4 `#v` → `Vector.map` +fold *was* worth hoisting — it's the shared `SP1Clean.vec4_eval` in `Math/EvalVec.lean`, used across the +Mul/Lt/Bitwise/Shift `Formal` proofs + the DivRem completeness `Driver`.) + +**Kernel-safe dedup on the bit-shift / DivRem cores** (the `2^64` landmine zone — read the "Bit-shift chip +soundness" § first). A heavy file may repeat a byte-identical `have` block across N sibling lemmas. You can +factor it into a single helper **iff the block is pure `ZMod.val` / `Nat` arithmetic with no `2^64`/`BitVec` +reduction** — extract it as a lemma **over loose variables** and apply it symbolically. This is kernel-safe +because a lemma application instantiates an *already-checked* body, so the kernel does no *extra* reduction +(no `skipKernelTC`; elaboration time does not regress). **Hard constraint:** the helper's conclusion must match +the original `have`'s type **character-for-character** — it's a downstream `rw`-target, and `(16 : ZMod p) - …` +is **not** interchangeable with `(16 - … : ZMod p)`. The heavy `2^64`-scale work itself is already isolated in +abstract-`BitVec` helpers (`srl_toNat`/`sra_toNat`) — leave those alone. Worked example: `inner_val`/`inner_hi_val` +in `Proofs/Chips/ShiftLeftChip/Core.lean`. + +**Traps — `have`s that look dead but are load-bearing** (verify with `lean_goal` / a build before removing): +- `have hp : 2 ^ 17 < p := Fact.out` (and `have : 131072 < p`) — feeds a downstream `omega` that needs the + magnitude; grep shows one occurrence (its own line) yet `omega` consumes it implicitly. +- `haveI : NeZero p := ⟨by have := Fact.out (p := 2 ^ 17 < p); omega⟩` — supplies an instance to later + `ZMod.val`/`omega` steps. There is **no** global `NeZero p` instance, *on purpose*: a `Fact (2^17 < p)`-derived + one would make the pervasive `omit [Fact (2^17 < p)] in` clauses illegal (`Model/ByteTable.lean:84`). A + `Fact p.Prime`-derived global instance *might* survive the `omit` pattern (it depends on primality, not the + magnitude) and eliminate most of the ~185 `haveI` copies — but it cuts against the documented local-instance + discipline and risks instance-resolution surprises, so it's an owner decision, not a drive-by change. + +**Don't golf:** +- **`Faithful/*` anchors** — conservative only (drop `by exact` / dead `let` / `from by`); never restructure + proof terms or statement forms; they are *syntactic* faithfulness anchors. +- **`Spec`/`Assumptions`/statement forms**, `set_option`/`maxHeartbeats`, `ElaboratedCircuit` field structure. +- **Auto-gen** — `Extracted/`, `*Vectors.lean`, `Native/Operations/*/RawSpec`, etc.; banner-check the header. +- **Narrative comments on kernel-sensitive Shift/DivRem files** — they document the `2^64` / `id (ZMod p)` + landmines + proof roadmaps; they are the institutional memory of *why* the proof is shaped that way. (A + blanket comment-strip on `ShiftLeftChip/Soundness/Sll` was reverted for exactly this.) + +**Verify every batch:** `lake build SP1Clean` clean (0 warn, no `info:`), `bash scripts/check_no_skipkerneltc.sh`, +`sorry` grep = only `SP1GatedVm.lean`, then `scripts/run_audit.sh` periodically (the axiom census must stay +identical). On heavy files watch the per-file elaboration time in the build log and **revert on regression**. + +**Merge gotcha (post-`git merge`):** an auto-merge can *silently duplicate* a lemma that both branches added +near each other — no conflict marker, but `lake build` fails with "`` has already been declared". Always +run the full `lake build` after a merge even when `git status` shows no conflicts (`Proofs/Chips/BitwiseChip/ +Bridge.lean` hit this when upstream #101's immediate-type bridges met a golfed copy). + +**Available cleanup skills:** +- **`/cleanup`** — the per-file 7-phase workflow (style audit → per-decl golf → simplify → verify). Best for a + handful of named files. +- **`/cleanup-all`** — the orchestrator marathon (dispatches per-batch workers across the whole tree). Best for a + project-wide sweep; honor the repo guardrails (auto-gen exclusion, axiom-clean, heavy-core caution). +- **`/decompose-proof`** — break one long proof into named sub-lemmas. +- **`/split-file`** — split an over-long file along namespace/section seams. +- **`Skill(simplify)`** — a holistic reuse/altitude review pass on a file (invoked inside `/cleanup` Phase 6.5). + ## "emitted = projection": `Lookups_eq_emitted` (proving a trace projection IS the emission) Goal: prove a hand-written trace-level projection (`Soundness/*Consistency.lean`'s `stateLookups`, diff --git a/docs/architecture.md b/docs/architecture.md index bb21bbd7..7e2a2234 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -95,10 +95,10 @@ For an operation `` (e.g. `Add`, `BitwiseU16`): Each new module's import goes into the root `SP1Clean.lean`. -## Trace Generation (`Proofs/TraceGenTests/`) +## Trace Generation (`SP1CleanTest/TraceGenTests/`) Partially deriving SP1's `generate_trace` from the circuit definition. -`Proofs/TraceGenTests/TraceGenerator.lean` (axiom-clean, +`SP1CleanTest/TraceGenTests/TraceGenerator.lean` (axiom-clean, generic): `circuitTraceRow` seeds Clean's `FlatOperation.dynamicWitnesses` env-threading fold with one row's input column values, runs `main`'s own witness closures in emission order, then evaluates `main`'s **output struct** under the final environment — the output layout *is* the row layout, so @@ -113,12 +113,16 @@ matrix equals, cell-for-cell, whole traces dumped from SP1's **real** `MachineAi column layout, and padding at once; the per-`populate` vector anchors check only the witness formulas pointwise. -`Proofs/TraceGenTests/` is kept **whole** in the `Proofs/` pillar: its auto-gen -`ChipTraceVectors.lean` batteries import the hand-written scaffold types (`EventPopulate` / -`Conformance`), so — unlike `Extracted/WitnessVectors/` — they are *not* split into the `Extracted/` -auto-gen pillar (that would invert the pillar layering). Its `Conformance.lean` carries the -concrete-prime scaffold; the layer is independent of `Proofs/WitnessTests/` — the older -per-operation `populate` conformance layer, kept as a temporary operation-level bridge. +This trace-gen layer and the older per-operation `populate`-conformance layer (`WitnessTests/`) both +live in the **top-level `SP1CleanTest` test library** (the `lake test` target), *not* the main +`SP1Clean/` tree. The reason is `native_decide`: the `TraceWitness.lean` / `Witness.lean` +anchors are the project's only uses of it (it trusts the whole compiler, adding +`Lean.ofReduceBool`/`Lean.trustCompiler`), so the entire layer — anchors, the `Conformance.lean` / +`EventPopulate.lean` concrete-prime scaffold, and the auto-gen `ChipTraceVectors.lean` / +`WitnessTests/Vectors/.lean` batteries — is quarantined in a library that imports `SP1Clean` but +is never imported by it. `scripts/check_no_native_decide.sh` keeps the main build clean. (Because the +vectors travel with the anchors, the old "can't split the vectors into `Extracted/` without inverting +the pillar layering" tension is moot — the whole test layer is one self-contained library.) Status (10 chips, 44 events + 20 padding rows each, **all unmasked** — every column of every covered chip is compared cell-for-cell): diff --git a/docs/release-audit.md b/docs/release-audit.md index 43304428..d9d783dc 100644 --- a/docs/release-audit.md +++ b/docs/release-audit.md @@ -6,7 +6,7 @@ whole-machine claim. Unlike its predecessor, **every quantitative claim in this machine-derived** — by `scripts/run_audit.sh`, whose raw census output is committed at `snapshots/axiom-census.txt`. Re-run the harness before citing any number. -**Pins (audit of 2026-06-10).** +**Pins (snapshot of 2026-06-10; census regenerated 2026-06-23 — re-run `scripts/run_audit.sh` for current numbers).** | What | Value | Command | |---|---|---| @@ -23,8 +23,9 @@ project's own branch (it carries the `sp1-constraint-compiler` and witness-dump > **Read this first if you read nothing else.** *Soundness is `sorry`-free across every wired chip* — > each chip `soundness` theorem and each Sail bridge is axiom-clean (the three Lean axioms, plus > `bv_decide`/`native_decide`'s two trusted axioms where used, plus Sail-model platform axioms where the -> bridge reaches the Sail spec). The only `sorry`s are **three completeness/liveness holes and one -> capstone-packaging premise** (§III.2). The machine-level *target theorem* is now **stated in Lean and +> bridge reaches the Sail spec). The only `sorry` is **one capstone-packaging premise** — `sp1_witness_decode`, +> the W1b/W1c decode seam (§III.2); the DivRem/ShiftLeft/ShiftRight/Mul completeness holes that this report's +> 2026-06-10 snapshot listed were **closed** (2026-06-12 / 06-18). The machine-level *target theorem* is now **stated in Lean and > its simulation induction proved** (`Soundness/TargetVm.lean`): what separates today's capstone from > "the real Sail interpreter executes the guest program to the committed exit code" is exactly the > **named obligations** of `TargetObligations` (Part I) plus the one capstone premise — each mapped to diff --git a/docs/roadmap.md b/docs/roadmap.md index 9d014fd4..2f7474e8 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -318,3 +318,34 @@ and retires the K6 sampled-conformance reliance for those chips. - The witness-vector battery and `Extracted/` currency are re-checked by `scripts/run_audit.sh` §A4 + CI `lake build`; the `SP1_PINNED_COMMIT` assertion in `update_extracted.py` keeps extraction provenance explicit. Remaining: a CI job that re-extracts and diffs per-PR. + +--- + +## Cleanup / polish backlog (non-blocking) + +Deferred quality/perf TODOs — none gate the VM theorem; pick up opportunistically. The *how-to-golf-safely* +lessons (heavy-core caution, kernel-safe dedup, the `maxHeartbeats`-is-the-wrong-lever finding, the available +`/cleanup` skills) live in `docs/agents/proof-patterns.md` § "Compile-time / performance landmines" + "Golf & +cleanup discipline". + +- **`linter.style.longLine`** — the one remaining syntactic linter not yet enabled (it's the last candidate + noted in AGENTS.md § Linters). ~1080 lines exceed 100 chars (`Native/` ~817, `FormalModel/` ~263). Enable it + alone on the core pillar lake libraries, then reflow or per-file-suppress back to 0 warnings. Heavy, mechanical. +- **Shift soundness tail-dedup** — the real build-time prize. Extract the byte-identical `cpuA/msb*/aluA` + requirements tail shared across the 6 Shift soundness conjuncts into a `requirements_holds`/`SpecObligation` + helper, mirroring `Proofs/Chips/DivRemChip/Soundness/Tail.lean` (recipe: proof-patterns § "Shared-tail + dedup"). Structural, multi-hour, overlaps the recently-golfed Shift soundness files — do it as a dedicated + pass, not a drive-by. +- **`/decompose-proof` candidates** — long proof bodies worth splitting into named sub-lemmas: + `ShiftLeftChip`/`ShiftRightChip` `Formal.lean` `completeness` (~123/~180 lines), `LoadHalfChip`'s 4-way + `h_sel_lt` offset-selection case-bash (near-verbatim across soundness + completeness), `BranchChip` + `soundness`/`completeness` (~156/~290 lines of per-column `env.get` plumbing). Several are perf-tuned — + decompose with care and watch elaboration time. +- **SailState-staging bridge preamble** — the `hpc_get`/`key`/`hsp_config` preamble recurs across ~10 + store/jal/load `Bridge.lean` files → a shared lemma. **Re-examine the shape first** — upstream #101/#102 + rewrote several bridges in the 2026-06-23 merge, so the pre-merge duplication may have shifted. +- **Namespace-isolate the auto-gen (linter hardening, Option B)** — the `sp1Lint` exclusion is a *soft* + module-path filter. A *hard* boundary would relocate all auto-gen to a separate root namespace + `SP1Extracted.*` so the stock `runLinter` excludes it by construction (no custom filter). Cost: ~87 module + renames + import-line edits + `update_extracted.py` writer paths + lakefile globs. Not worth it for linting + alone; reconsider only if a hard auto-gen/hand-written namespace split is wanted for other reasons. diff --git a/docs/snapshots/axiom-census.txt b/docs/snapshots/axiom-census.txt index ab47d583..18614798 100644 --- a/docs/snapshots/axiom-census.txt +++ b/docs/snapshots/axiom-census.txt @@ -1,5 +1,5 @@ # Raw #print axioms census — generated by scripts/run_audit.sh -# sp1-lean 4e50238719001da34a63697ea40b65b7f87e2bf9 · 2026-06-19 +# sp1-lean 7b6476af3985b131ca51b9d427d839182adf1caf · 2026-06-25 'SP1Clean.AddChip.soundness' depends on axioms: [propext, Classical.choice, Quot.sound] 'SP1Clean.AddChip.completeness' depends on axioms: [propext, Classical.choice, Quot.sound] 'SP1Clean.AddiChip.soundness' depends on axioms: [propext, Classical.choice, Quot.sound] diff --git a/docs/snapshots/axiom-ledger.md b/docs/snapshots/axiom-ledger.md index 1186fb60..670f5540 100644 --- a/docs/snapshots/axiom-ledger.md +++ b/docs/snapshots/axiom-ledger.md @@ -1,10 +1,16 @@ # Axiom & trust ledger > **Machine-generated snapshot — regenerate with `scripts/run_audit.sh` before relying on it.** -> Snapshot of 2026-06-10 (sp1-lean `e076768` + audit changes + the W1a capstone-premise split); raw -> probe output: [`axiom-census.txt`](axiom-census.txt) (314 probes, generated by -> `scripts/gen_axiom_probe.py`, gated by `scripts/run_audit.sh` — the gate **passes**: `sorryAx` -> reachable only from the known-debt set). +> The detailed per-bucket tables below are the **2026-06-10** snapshot (sp1-lean `e076768`). The raw probe +> output [`axiom-census.txt`](axiom-census.txt) was regenerated **2026-06-23** (now **366 probes**, generated by +> `scripts/gen_axiom_probe.py`); `scripts/run_audit.sh` still **passes** (`sorryAx` reachable only from the +> known debt). +> +> **What changed since the 2026-06-10 tables:** DivRem / ShiftLeft / ShiftRight / Mul completeness are now +> **closed** (2026-06-12 / 06-18), so `DivRemChip.completeness` is **no longer a `sorryAx` carrier**. The single +> remaining textual `sorry` is `sp1_witness_decode` (the W1b/W1c decode seam), and the `sorryAx` set is now just +> the capstone chain reaching it — **5 carriers** in the current census, all allowed. Re-run the audit for the +> current per-bucket membership before relying on the detailed tables below. ## How to read the axiom sets diff --git a/docs/snapshots/compile-profile.md b/docs/snapshots/compile-profile.md index 43eaca2a..c476828b 100644 --- a/docs/snapshots/compile-profile.md +++ b/docs/snapshots/compile-profile.md @@ -2,35 +2,27 @@ # Compile-time profile — SP1Clean -Per-module wall-clock elaboration profile, with per-tactic attribution for the worst offenders and -the common threads behind them. Measured **2026-06-10** (Lean v4.28.0, warm olean cache, branch -`full-clean-dsl-implementation`), **after** the get_elem fast-path / linter / `localLength_eq` -optimization batch landed that same day. The pre-fix baseline, per-module delta table, probe -evidence, and drafted upstream reports live in `docs/snapshots/profile-baseline-2026-06-10/` -(`findings.md` is the diagnosis writeup). - -**Headline: the 2026-06-10 batch cut isolated elaboration of the swept set in half — 3422s → 1689s -(−50.7%), no regression beyond ±2% noise.** The three fixes, by measured leverage: - -1. **`v[i]` index-bound `decide` fast path** (`Foundations/GetElemFastPath.lean`, one `macro_rules` - line): every `v[i]` elaboration was paying ~11 `rw/dsimp … at *` context traversals inside Std's - slice `get_elem_tactic_extensible` rule — ~0.34s per `[i]` under a fat proof context, ~0.8s per - `have`. This single fix produced most of the −50%: medium chip Formal files −70%, generated - `Extracted/` files −87…93% (their `#v[…][k]` projection chains were this, not "monolithic term - elaboration"), `BranchChip.Formal` −77%, `OwnAsserts` −92%. -2. **`set_option linter.all false` on the 76 auto-generated modules** (~1.6s/file linter - interpretation tax; emitted by the `update_extracted.py` templates). -3. **`localLength_eq := by intros; simp +arith […]`** on the two ALU readers — the silent - `by intros; rfl` field default whnf-unfolds all of `main` (334k heartbeats ≈ 15.5s each; the simp - route is 109× cheaper). Both readers now fit the **default** heartbeat/recdepth budgets. +Per-module wall-clock elaboration profile, with per-tactic attribution for the worst offenders. +Measured **2026-06-22** (Lean v4.28.0, 24-core host, warm olean cache, branch +`dtumad/div-rem-perf` / PR #103). This run measures **all** modules in isolation, including the nine +DivRem soundness conjuncts (previously excluded and quoted from a contention-inflated parallel build +log — they are now clean isolated numbers). + +The durable proof-engineering lessons that came out of profiling (the `v[i]` fast path, +`circuit_proof_start` is not the bottleneck, the `localLength_eq` `rfl` cost, the shared-tail dedup +pattern) live in `docs/agents/proof-patterns.md` § "Compile-time / performance landmines". An older +pre-refactor baseline is archived under `docs/snapshots/profile-baseline-2026-06-10/`. ## How to re-run ```sh -scripts/profile_compile.sh # whole library (warm-builds first, then sweeps) -SKIP_BUILD=1 scripts/profile_compile.sh # cache already warm -SKIP_BUILD=1 scripts/profile_compile.sh Operations/ # scope to one subdir -EXCLUDE_RE='Chips/DivRemChip/Soundness/' scripts/profile_compile.sh Chips/ # skip the conjuncts +scripts/profile_compile.sh # warm-builds first, then sweeps every module +SKIP_BUILD=1 scripts/profile_compile.sh # cache already warm — skip the warm build +SKIP_BUILD=1 scripts/profile_compile.sh Operations/ # scope to one subdir +# The nine DivRem soundness conjuncts are 277–307s EACH in isolation (~40 min for the nine). To skip +# them for a quick non-conjunct pass and take their cost from a build log instead: +SKIP_BUILD=1 EXCLUDE_RE='Soundness/(Div|Divu|Divuw|Divw|Reader|Rem|Remu|Remuw|Remw)\.lean$' \ + scripts/profile_compile.sh ``` Outputs land in `build/profile/`: @@ -45,103 +37,108 @@ After one warm `lake build`, each module is re-elaborated in isolation with `lake env lean -Dprofiler=true -Dprofiler.threshold=50 `. Every dependency loads from its cached `.olean`, so the timing isolates **that one file's** elaboration. Caveats: -1. **Import tax is a sweep artifact** (~1s/file, shared in a real parallel build). It dominates the - cheap files; the ranking of expensive files is accurate. +1. **Import tax is a sweep artifact** (~0.8s/file, shared in a real parallel build): 314.6s across the + 391 modules here. It dominates the cheap files; the ranking of expensive files is accurate. 2. **Profiler sums over-count** (nested/cumulative) — per-category numbers are dominance signals, - not an additive budget. -3. **`lake env lean` exits 0 even on a stack overflow** — the script records exit codes; this run - had **0 nonzero exits** across all 260 modules. -4. **The nine `Chips/DivRemChip/Soundness/*.lean` conjuncts are excluded** (each is 18–33 min to - elaborate in isolation); their costs are taken from `lake build` logs instead — see below. - -## Headline numbers (post-fix sweep) - -- **260 modules, ~1689s total** (sequential, isolated; excludes the DivRem conjuncts). - Mean 6.5s, **median 2.7s**, p95 28.3s, **max 118s** (was max 462s). -- 156/260 modules elaborate in <3s (≈ import floor). -- Generated modules (`Extracted/`, `Operations/*/Extracted`, witness vectors): **245.6s across 77 - files — was 1178.8s.** - -### Time by subsystem (vs pre-fix baseline) - -| Subsystem | Time | was | Files | -| --- | ---: | ---: | ---: | -| `Chips/` (excl. DivRem conjuncts) | 1041s | 1598s | 95 | -| `Operations/` | 285s | 553s | 62 | -| `Extracted/` | 146s | 1013s | 50 | -| `Foundations/` | 91s | 90s | 16 | -| `WitnessTests/` | 85s | 88s | 27 | -| `Readers/` | 40s | 81s | 10 | - -### DivRem soundness conjuncts (from `lake build` logs, ≤5-way-parallel — contention-inflated) - -Rem 1997s, Div 1849s, Remu 1706s, Divu 1651s, Divw 1535s, Divuw 1494s, Remw 1256s, Remuw 1090s, -Reader 720s — **~3.7 CPU-hours, the library's dominant remaining cost.** (Pre-fix sum of the same -seven measured files was ~15% higher.) Probe-measured: `circuit_proof_start` is only ~90s of each; -the rest is per-conjunct omega/bitvec/carry proof bodies at 128M maxHeartbeats. - -## Top offenders (post-fix) + not an additive budget (a file's summed "simp took" can exceed its own total time). +3. **`lake env lean` exits 0 even on a stack overflow** — the script records exit codes; this run had + **0 nonzero exits** across all 391 modules. +4. **The nine DivRem soundness conjuncts are included** and measured in isolation this run (each + 277–307s; the slowest single files in the library). They are *not* run in parallel here, so these + are clean per-file costs rather than contention-inflated build-log wall times. + +## Headline numbers + +- **391 modules, 3964.6s total** (sequential, isolated). Mean 10.14s, **median 1.84s**, p90 5.98s, + p95 25.31s, **max 306.65s**. +- **307/391 modules (79%) elaborate in <3s** (≈ the import floor). +- The eight DivRem div/rem soundness conjuncts alone are **2334.6s (59% of the total)**. + +### Time by subsystem + +| Subsystem | Time | Files | +| --- | ---: | ---: | +| `Proofs/` (of which: 8 DivRem conjuncts 2334.6s, Reader 68.3s, Tail 123.6s) | 3376.4s | 147 | +| `Proofs/` excluding those ten DivRem files | 849.9s | 137 | +| `Native/` | 190.6s | 66 | +| `Extracted/` | 165.9s | 77 | +| `Faithful/` | 114.9s | 50 | +| `Soundness/` | 44.9s | 27 | +| `Model/` | 42.6s | 11 | +| `FormalModel/` | 12.6s | 6 | +| `Math/` | 11.1s | 6 | + +### DivRem soundness conjuncts (isolated) + +Divw 306.7s · Remw 305.4s · Div 298.5s · Rem 297.8s · Divuw 286.5s · Remuw 284.8s · Divu 278.3s · +Remu 276.8s — mean **291.8s** across the eight — plus **Reader 68.3s** and the shared +**`Tail.lean` 123.6s** (proved once). Each conjunct now proves only its chip-specific `Spec`; the +~265-line `Operations.Requirements` tail is delegated to `requirements_holds` in `Soundness/Tail.lean` +and paid **once** instead of inline in all nine files. + +## Top offenders | # | Time | Module | Note | | ---: | ---: | --- | --- | -| 1 | 118s | `Chips.DivRemChip.Defs` | instance-field simps + **kernel type-checking** (~40s) — next target | -| 2 | 81s | `Chips.ShiftRightChip.Soundness.Sra` | conjunct proof body | -| 3 | 73s | `Chips.ShiftRightChip.Soundness.Sraw` | conjunct proof body | -| 4 | 73s | `Chips.ShiftRightChip.Soundness.Srl` | conjunct proof body | -| 5 | 69s | `Chips.ShiftRightChip.Soundness.Srlw` | conjunct proof body | -| 6 | 45s | `Chips.ShiftRightChip.Core` | arithmetic lemma farm | -| 7 | 45s | `Chips.ShiftLeftChip.Soundness.Sll` | conjunct proof body | -| 8 | 41s | `Chips.ShiftLeftChip.Soundness.Sllw` | conjunct proof body | -| 9 | 41s | `Operations.MulOperation.RawSpec` | | -| 10 | 40s | `Operations.MulOperation.Formal` | was 121s (−67%) | -| 11 | 37s | `Chips.ShiftRightChip.Dispatch` | | -| 12 | 31s | `Extracted.DivRemChip` | **was 462s (−93%)** | -| 13 | 29s | `Chips.ShiftLeftChip.Core` | | -| 14 | 28s | `Chips.ShiftRightChip.Defs` | was 60s | -| 15 | 27s | `Operations.LtOperationUnsigned.RawSpec` | | -| 16 | 27s | `Foundations.SailWrap` | 232 mathlib-style simp calls — not Clean-related | -| 17 | 25s | `Chips.DivRemChip.Formal` | | -| 18 | 23s | `Foundations.Register` | mathlib-style — not Clean-related | -| 19 | 23s | `WitnessTests.MulOperationWitnessVectors` | `native_decide` anchor data | -| 20 | 21s | `Chips.BranchChip.Formal` | was 95s (−77%) | - -## What the 2026-06-10 diagnosis established (kill-list of myths) - -Full evidence: `profile-baseline-2026-06-10/findings.md`. In brief: - -- **`circuit_proof_start` / bind-chain normalization is NOT the bottleneck** — 6k–32k heartbeats on - medium chips (~10% of file cost), 320k (~90s) even on DivRem's `main`, the repo's biggest. A - once-per-chip "normalized ops" lemma (main_ops_eq) would save only ~13 min across DivRem's nine - files — parked (`scratch/design-main-ops-eq-divrem.md` at the measurement commit). -- **The dominant medium-chip cost was `v[i]` elaboration**, fixed by the decide fast path. -- **`ElaboratedCircuit` field defaults can silently cost seconds**: `localLength_eq`'s `rfl` default - whnf-unfolds `main`; the `channelsLawful` default outright fails on channel-heavy mains. Watch for - this on every new chip with a non-trivial `main` (hand-write the simp-route `localLength_eq`). -- The old threads C/E ("linter tax" / "Extracted monolithic terms") are **resolved** — E was mostly - the `v[i]` tax in disguise. +| 1 | 306.7s | `Proofs.Chips.DivRemChip.Soundness.Divw` | conjunct `Spec` proof; Mul product-glue `simpa`s + `instantiate metavars` | +| 2 | 305.4s | `Proofs.Chips.DivRemChip.Soundness.Remw` | " | +| 3 | 298.5s | `Proofs.Chips.DivRemChip.Soundness.Div` | " | +| 4 | 297.8s | `Proofs.Chips.DivRemChip.Soundness.Rem` | " | +| 5 | 286.5s | `Proofs.Chips.DivRemChip.Soundness.Divuw` | " | +| 6 | 284.8s | `Proofs.Chips.DivRemChip.Soundness.Remuw` | " | +| 7 | 278.3s | `Proofs.Chips.DivRemChip.Soundness.Divu` | " | +| 8 | 276.8s | `Proofs.Chips.DivRemChip.Soundness.Remu` | " | +| 9 | 123.6s | `Proofs.Chips.DivRemChip.Soundness.Tail` | **the shared tail, now paid once** (`requirements_holds`). `instantiate metavars` 61.2s + the two big `obtain`s + `rewriteSeq` — term-intrinsic | +| 10 | 100.2s | `Proofs.Chips.DivRemChip.Completeness.Driver` | the DivRem completeness driver (sub-op col folding) | +| 11 | 68.3s | `Proofs.Chips.DivRemChip.Soundness.Reader` | the reader conjunct | +| 12 | 50.7s | `Proofs.Chips.ShiftRightChip.Soundness.Sra` | conjunct proof body | +| 13 | 46.9s | `Proofs.Chips.ShiftRightChip.Soundness.Sraw` | " | +| 14 | 46.5s | `Proofs.Chips.ShiftRightChip.Soundness.Srl` | " | +| 15 | 44.5s | `Proofs.Chips.ShiftRightChip.Soundness.Srlw` | " | +| 16 | 39.0s | `Proofs.Chips.DivRemChip.Defs` | instance-field simps + kernel type-checking | +| 17 | 29.0s | `Proofs.Chips.ShiftLeftChip.Soundness.Sll` | conjunct proof body | +| 18 | 28.3s | `Proofs.Chips.ShiftRightChip.Formal` | | +| 19 | 27.1s | `Proofs.Chips.ShiftLeftChip.Soundness.Sllw` | conjunct proof body | +| 20 | 26.6s | `Native.Operations.MulOperation.RawSpec` | | +| 21 | 25.3s | `Proofs.Operations.MulOperation.Formal` | | +| 22 | 19.8s | `Extracted.DivRemChip` | generated | +| — | 13.9s | `Model.SailWrap` | mathlib-style simp/`grind` — not Clean-related | +| — | 12.0s | `Model.Register` | 62 `fin_cases <;> trivial` register lemmas — not Clean-related | + +### Project-wide hot tactic categories (summed across all 391 logs, >50ms entries) + +`simpa` 1349.5s (36 calls) · `instantiate metavars` 718.1s · `simp` 374.2s (821 calls) · `import` +314.6s (sweep artifact) · `obtain` 275.0s · `rewriteSeq` 170.4s · type-checking 132.1s · `nlinarith` +interpretation 64.7s. + +The dominant category, `simpa`, is **99.97% concentrated in the eight div/rem conjuncts** (1349.1s of +1349.5s) — the Mul product-glue `simpa`s establishing `r_k = product[2k] + product[2k+1]·256` against +the 16-element witnessed product vectors. `instantiate metavars` (70% in DivRem soundness), `obtain` +(88%), and `rewriteSeq` (88%) are likewise concentrated there; `nlinarith` interpretation sits almost +entirely in the two Shift `Core` lemma farms. `simp` is the only broadly-spread category (821 calls, +~0.4s/file). **No broad attribute/macro/instance lever remains** — the remaining cost is term-intrinsic +in the DivRem/Shift/Mul heavies, not a project-wide pattern. ## Where to optimize next (by leverage) -1. **DivRem conjunct proof bodies** (~3.7 CPU-h): the omega/carry work in heavy contexts. Known - pattern from the files' own comments: extract minimal-context pure-ℕ lemmas so omega certificates - shrink. Large effort, largest prize. *Open.* -2. **`Chips.DivRemChip.Defs` (118s)**: instance-field `simp` proofs 20s+ each plus ~40s kernel - type-checking — investigate proof-term size of the field proofs (`share common exprs` lines). - *Open.* -3. **ShiftRight/Left conjunct bodies** (~70–81s each) — same family as (1). *Open.* -4. **maxHeartbeats ratcheting**: post-fix, ceilings everywhere are loose (e.g. 16M-HB files now run - in seconds). Mechanical sweep with `#count_heartbeats`, restores regression-alarm value. *Open.* -5. **Upstream filings** (`profile-baseline-2026-06-10/upstream-notes.md`): lean4/Std slice-rule - `at *` report; Clean `localLength_eq`/`channelsLawful` default reports. *Drafted, not filed.* -6. `Foundations.SailWrap` / `Foundations.Register` (~50s combined): mathlib-style simp-heavy proofs, - independent of Clean. *Open, low priority.* +1. **The eight DivRem soundness conjuncts (277–307s each, 2334.6s combined) + `Tail.lean` (123.6s).** + The cost is the Mul product-glue `simpa`s + `instantiate metavars` — term-intrinsic (swapping the + heavy `circuit_norm` simp set for the minimal one builds green with *zero* speedup). Reducing it + needs restructuring how the Mul product columns are reduced. Largest effort, largest prize. *Open.* +2. **`Proofs.Chips.DivRemChip.Completeness.Driver` (100.2s)** — the sub-op col-folding driver; same + heavy-term family. *Open.* +3. **ShiftRight/Left conjunct bodies (~27–51s each)** — same family; the shared + `cpuA/msb1A/msb2A/msb3A/aluA` block (byte-identical across Srl/Sra/Srlw/Sraw) is a tail-dedup + candidate mirroring this PR's DivRem dedup, a smaller prize. *Open.* +4. **maxHeartbeats ratcheting** — mechanical `#count_heartbeats` sweep to tighten the now-loose + ceilings on the heavies; restores regression-alarm value (not a speedup). *Open.* +5. **`Model.SailWrap` / `Model.Register` (~26s combined)** — mathlib-style simp/`fin_cases` proofs, + independent of Clean; editing `Model/` triggers a full rebuild, so low priority. *Open.* ## Verification of this run -- Coverage: 260 modules swept, 0 nonzero exits; DivRem conjuncts excluded by `EXCLUDE_RE` (costs - recorded from build logs). -- Post-fix gate: `lake build SP1Clean` green (3630 jobs), only the five pre-existing `sorry` - warnings (ShiftLeft/ShiftRight/Mul/DivRem `Formal`, `SP1GatedVm`), 0 new warnings, 0 `info:` notes. -- Axiom audit: representative headline theorems (StoreByte soundness/completeness + bridge, both ALU - readers, Jalr, DivRem soundness, `correct_add_native`) all match the axiom ledger — no `sorryAx`, - no new axioms from the decide-based proofs. +- Coverage: 391 modules swept, **0 nonzero exits** (no silent stack overflow). +- Build gate: `lake build SP1Clean` green (**3676 jobs**), the **single** remaining `sorry` + (`Soundness/SP1GatedVm.lean:220`), 0 errors, 0 new warnings, 0 `info:` notes. +- Axiom audit: `DivRemChip.requirements_holds` (the dedup'd tail) is + `[propext, Classical.choice, Quot.sound]` — no `sorryAx`. diff --git a/lakefile.toml b/lakefile.toml index 9d1ac302..05a34424 100644 --- a/lakefile.toml +++ b/lakefile.toml @@ -3,9 +3,24 @@ defaultTargets = ["SP1Clean"] moreLeanArgs = ["--tstack=400000"] -# Built-in options only (no Mathlib linter options here — those require a Mathlib -# linter module to be imported before the package -D flags are validated, which a -# Clean-only file does not pull in; see the sp1-lean Step 0 finding). +# `lake lint` driver — the project-local environment-linter pass (`scripts/sp1Lint.lean`). It runs a +# curated subset of the Batteries `#lint` linters over the hand-written `SP1Clean.*` declarations only, +# excluding the auto-generated `Extracted/`+`*Vectors` code. We use a thin custom driver (not the stock +# `runLinter` exe) because `runLinter` scopes by namespace *root*, which cannot exclude `SP1Clean.Extracted` +# while keeping `SP1Clean.Proofs`. Needs a built project first (env linters read oleans); run after `lake build`. +lintDriver = "sp1Lint" + +# `lake test` driver — builds/elaborates the top-level `SP1CleanTest` library (the witness- and +# whole-trace conformance anchors). Those anchors are the project's ONLY `native_decide` (which trusts +# the whole Lean compiler via `Lean.ofReduceBool`/`Lean.trustCompiler`), so they are quarantined in +# this test library, OUT of the main `SP1Clean` library — kept `native_decide`-free by the +# `scripts/check_no_native_decide.sh` source gate (the CI `guards` job + the audit). Run with `lake test`. +testDriver = "SP1CleanTest" + +# Built-in options only — the Mathlib style linters are applied per-lib via `moreLeanArgs` +# (see the pillar libraries below), NOT here at package scope: that keeps the auto-gen +# `SP1Extracted` library cleanly out of the linter set. (Every hand-written pillar transitively +# imports Mathlib, so the `-D linter.*` options register fine when applied per-lib.) [leanOptions] pp.unicode.fun = true synthInstance.maxHeartbeats = 1000000 @@ -33,6 +48,14 @@ name = "RISCV" git = "https://github.com/succinctlabs/riscv-lean" rev = "dtumad/clean-native" +# The `lake lint` driver executable (see `lintDriver` above). A thin wrapper around the Batteries +# `#lint` framework; `supportInterpreter` is required because the linters' `test` functions are run +# through the interpreter. Built by `lake build` along with the libraries; invoked by `lake lint`. +[[lean_exe]] +name = "sp1Lint" +srcDir = "scripts" +supportInterpreter = true + # Umbrella library — root module `SP1Clean.lean` imports every module; `lake build` (the default # target) builds everything through it. Package-level `moreLeanArgs`/`[leanOptions]` above apply to # all libraries, so the layer libraries below inherit `--tstack`/heartbeats with no duplication. @@ -42,15 +65,37 @@ name = "SP1Clean" # Layer build-targets (select modules by submodule glob; module names stay `SP1Clean.*`, so these add # no import churn). They make each pillar independently buildable — `lake build SP1Extracted`, etc. # NOTE: isolation is by convention, not enforced — Lake does not forbid cross-layer imports within one -# package. The `Native`/`Proofs` pillar libraries are added once those directories land. +# package. +# +# Lib-scoped Mathlib style linters. Every hand-written core pillar (Math/Model/FormalModel/Native + +# Proofs/Faithful/Soundness) plus the `SP1CleanTest` test lib carries the SAME `moreLeanArgs` linter set +# below; they all transitively import Mathlib (Native via Clean), so the `-D linter.*` options are +# registered and these flags apply during `lake build SP1Clean` / `lake test`. All eight are at zero +# violations. `linter.style.longLine` is the remaining candidate (real fallout, concentrated in +# Native/FormalModel) — see `docs/roadmap.md` § "Cleanup / polish backlog". The auto-gen `SP1Extracted` library is +# deliberately left OUT (it carries per-file `set_option linter.all false` instead), as are the +# auto-gen `*Vectors` modules inside `SP1CleanTest` (same per-file suppression). KEEP THE SIX COPIES IN SYNC. [[lean_lib]] name = "SP1Math" globs = ["SP1Clean.Math.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] [[lean_lib]] name = "SP1Model" globs = ["SP1Clean.Model.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] +# Auto-gen pillar — NO linter args (the generated modules carry `set_option linter.all false`). [[lean_lib]] name = "SP1Extracted" globs = ["SP1Clean.Extracted.+"] @@ -58,13 +103,49 @@ globs = ["SP1Clean.Extracted.+"] [[lean_lib]] name = "SP1FormalModel" globs = ["SP1Clean.FormalModel.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] [[lean_lib]] name = "SP1Native" globs = ["SP1Clean.Native.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] # The proof pillar: the sound/complete proofs (Proofs/), the faithfulness anchors (Faithful/), -# and the whole-machine/trace layer (Soundness/). +# and the whole-machine/trace layer (Soundness/). Same linter set as the other core pillars above. [[lean_lib]] name = "SP1Proofs" globs = ["SP1Clean.Proofs.+", "SP1Clean.Faithful.+", "SP1Clean.Soundness.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] + +# The test pillar (the `lake test` driver, see `testDriver` above) — the witness-generation and +# whole-trace conformance anchors (`SP1CleanTest.{WitnessTests,TraceGenTests}.*`), which import the +# main `SP1Clean` library and check it against batteries dumped from SP1's real Rust prover. This is +# the ONLY library that may use `native_decide`; it is NOT imported by the umbrella `SP1Clean`, so the +# default `lake build` stays `native_decide`-free. Same syntactic linter set as the core pillars; the +# auto-gen `SP1CleanTest.WitnessTests.Vectors.*` / `*TraceVectors` modules self-suppress with their own +# `set_option linter.all false`. (The `lake lint` env-linter pass skips this lib too — `sp1Lint` keeps +# only `SP1Clean`-prefixed declarations, and `SP1CleanTest` is not such a prefix.) +[[lean_lib]] +name = "SP1CleanTest" +globs = ["SP1CleanTest.+"] +moreLeanArgs = [ + "-Dlinter.style.lambdaSyntax=true", "-Dlinter.style.dollarSyntax=true", + "-Dlinter.style.refine=true", "-Dlinter.style.cases=true", + "-Dlinter.style.induction=true", "-Dlinter.style.admit=true", + "-Dlinter.oldObtain=true", "-Dlinter.style.cdot=true", +] diff --git a/scripts/axiom_probe.lean b/scripts/axiom_probe.lean index b9a2729c..df8a7d81 100644 --- a/scripts/axiom_probe.lean +++ b/scripts/axiom_probe.lean @@ -1,4 +1,27 @@ import SP1Clean +import SP1CleanTest.WitnessTests.AddOperationWitness +import SP1CleanTest.WitnessTests.AddrAddOperationWitness +import SP1CleanTest.WitnessTests.AddwOperationWitness +import SP1CleanTest.WitnessTests.BitwiseOperationWitness +import SP1CleanTest.WitnessTests.IsEqualWordOperationWitness +import SP1CleanTest.WitnessTests.IsZeroOperationWitness +import SP1CleanTest.WitnessTests.IsZeroWordOperationWitness +import SP1CleanTest.WitnessTests.LtOperationUnsignedWitness +import SP1CleanTest.WitnessTests.MulOperationWitness +import SP1CleanTest.WitnessTests.SubOperationWitness +import SP1CleanTest.WitnessTests.SubwOperationWitness +import SP1CleanTest.WitnessTests.U16CompareOperationWitness +import SP1CleanTest.WitnessTests.U16MSBOperationWitness +import SP1CleanTest.TraceGenTests.AddChipTraceWitness +import SP1CleanTest.TraceGenTests.AddwChipTraceWitness +import SP1CleanTest.TraceGenTests.BitwiseChipTraceWitness +import SP1CleanTest.TraceGenTests.DivRemChipTraceWitness +import SP1CleanTest.TraceGenTests.LtChipTraceWitness +import SP1CleanTest.TraceGenTests.MulChipTraceWitness +import SP1CleanTest.TraceGenTests.ShiftLeftChipTraceWitness +import SP1CleanTest.TraceGenTests.ShiftRightChipTraceWitness +import SP1CleanTest.TraceGenTests.SubChipTraceWitness +import SP1CleanTest.TraceGenTests.SubwChipTraceWitness /-! Auto-generated by `scripts/gen_axiom_probe.py` — do not edit by hand. Run via `lake env lean scripts/axiom_probe.lean` (see `scripts/run_audit.sh`). -/ diff --git a/scripts/check_no_native_decide.sh b/scripts/check_no_native_decide.sh new file mode 100755 index 00000000..d48ee800 --- /dev/null +++ b/scripts/check_no_native_decide.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Gate: no `native_decide` anywhere in the main `SP1Clean/` library. +# +# `native_decide` discharges a goal by running compiled code, so it trusts the **entire Lean +# compiler** (it adds the `Lean.ofReduceBool` / `Lean.trustCompiler` axioms) rather than just the +# kernel — the same reason mathlib bans it from its main library. Every headline soundness theorem +# here must stay `[propext, Classical.choice, Quot.sound]`-clean, so `native_decide` is confined to +# the separate top-level `SP1CleanTest` test library (the witness/trace conformance anchors, run by +# `lake test`), which imports `SP1Clean` but is never imported by it. +# +# Scope is `SP1Clean/**/*.lean` only — the `SP1CleanTest/` test library is the sanctioned home, and +# `docs/`/`scripts/` legitimately *mention* the token, so scanning just the main source tree avoids +# those false positives. There are zero legitimate uses in `SP1Clean/`, so any case-insensitive hit +# fails the gate. +# +# Used by `scripts/run_audit.sh` (A2 gate) and `.github/workflows/lean_action_ci.yml` +# (the lightweight `guards` job). Exit 0 = clean, 1 = reintroduced. +# +# Usage: scripts/check_no_native_decide.sh (from the repo root) + +set -uo pipefail +cd "$(dirname "$0")/.." + +if grep -rniE 'native_decide' SP1Clean --include='*.lean'; then + echo "FAIL: native_decide found in SP1Clean/ (see lines above)." >&2 + echo " native_decide trusts the whole compiler (adds Lean.ofReduceBool) — it must not appear" >&2 + echo " in the main library. Move the check into the SP1CleanTest test library (run by" >&2 + echo " 'lake test'), or prove the goal in the kernel (decide / a real proof) instead." >&2 + exit 1 +fi +exit 0 diff --git a/scripts/gen_axiom_probe.py b/scripts/gen_axiom_probe.py index 00e75d47..0a62ee1b 100644 --- a/scripts/gen_axiom_probe.py +++ b/scripts/gen_axiom_probe.py @@ -23,8 +23,10 @@ ("SP1Clean/Proofs/Chips/*/Bridge.lean", r"theorem\s+(correct_\w+|\w*reaches_sail\w*)\b"), ("SP1Clean/Proofs/Chips/*/Bridge.lean", r"def\s+(kind)\b"), ("SP1Clean/Faithful/*.lean", r"theorem\s+(\w*faithful\w*)\b"), - ("SP1Clean/Proofs/WitnessTests/*.lean", r"theorem\s+(\w*conforms\w*)\b"), - ("SP1Clean/Proofs/TraceGenTests/*.lean", r"theorem\s+(\w*conforms\w*)\b"), + # The witness/trace conformance anchors now live in the separate `SP1CleanTest` test library (the + # native_decide quarantine); the census still probes them to disclose their ofReduceBool axioms. + ("SP1CleanTest/WitnessTests/*.lean", r"theorem\s+(\w*conforms\w*)\b"), + ("SP1CleanTest/TraceGenTests/*.lean", r"theorem\s+(\w*conforms\w*)\b"), ("SP1Clean/Soundness/GatedVm/*.lean", r"theorem\s+(exists_trail|chipRows_step_sound|state_trail_of_balance|" r"gatedExecution_of_specs_and_balance|gatedExecution_allChips|" @@ -88,21 +90,37 @@ def fqns_in(path: Path, decl_re: re.Pattern) -> list[str]: def main() -> None: fqns: list[str] = [] + extra_imports: list[str] = [] # modules outside the `SP1Clean` umbrella (e.g. `SP1CleanTest.*`) + seen_imports: set[str] = set() for glob, pattern in TARGETS: decl_re = re.compile(pattern) for path in sorted(ROOT.glob(glob)): - fqns.extend(fqns_in(path, decl_re)) + found = fqns_in(path, decl_re) + if not found: + continue + fqns.extend(found) + # `import SP1Clean` (the umbrella) covers every main-library declaration, but NOT the + # `SP1CleanTest` conformance anchors (that test library is not imported by the umbrella — + # it is the native_decide quarantine). Import each such module explicitly so its FQNs + # resolve in the probe. + rel = path.relative_to(ROOT) + if rel.parts[0] != "SP1Clean": + mod = ".".join(rel.with_suffix("").parts) + if mod not in seen_imports: + seen_imports.add(mod) + extra_imports.append(mod) # dedupe, keep order seen, ordered = set(), [] for f in fqns: if f not in seen: seen.add(f) ordered.append(f) - lines = ["import SP1Clean", - "", - "/-! Auto-generated by `scripts/gen_axiom_probe.py` — do not edit by hand.", - "Run via `lake env lean scripts/axiom_probe.lean` (see `scripts/run_audit.sh`). -/", - ""] + lines = ["import SP1Clean"] + lines += [f"import {m}" for m in extra_imports] + lines += ["", + "/-! Auto-generated by `scripts/gen_axiom_probe.py` — do not edit by hand.", + "Run via `lake env lean scripts/axiom_probe.lean` (see `scripts/run_audit.sh`). -/", + ""] lines += [f"#print axioms {f}" for f in ordered] OUT.write_text("\n".join(lines) + "\n") print(f"wrote {OUT.relative_to(ROOT)} with {len(ordered)} probes") diff --git a/scripts/nolints.json b/scripts/nolints.json new file mode 100644 index 00000000..c2328cd5 --- /dev/null +++ b/scripts/nolints.json @@ -0,0 +1,20 @@ +[["defLemma", "SP1Clean.Soundness.memBalanceHyps_of_genesis"], + ["defLemma", "SP1Clean.Soundness.Target.targetObligations_full"], + ["defLemma", "SP1Clean.Soundness.Target.targetObligations_of_decode"], + ["simpComm", "Sail.writeReg_writeReg_comm"], + ["simpComm", "Sail.write_reg'_write_reg'_comm"], + ["simpComm", "Sail.write_reg_write_reg_comm"], + ["simpComm", "Std.ExtDHashMap.insert_insert_comm"], + ["simpNF", "ExtDHashMap_insert_insert_self"], + ["simpNF", "Sail.map_const_run_readReg"], + ["simpNF", "Sail.run_readReg_insert_of_ne"], + ["simpNF", "Sail.run_readReg_insert_self"], + ["simpNF", "Sail.run_writeReg_bind"], + ["simpNF", "Sail.write_reg'_write_reg'_comm"], + ["simpNF", "Sail.write_reg_eq_write_reg'"], + ["simpNF", "Sail.write_reg_write_reg_comm"], + ["simpNF", "SailState.isInitialized_insert"], + ["simpNF", "SP1Clean.DivRemChip.val_16_zmod_p"], + ["simpNF", "Std.ExtDHashMap.insert_inj"], + ["simpNF", "Std.ExtDHashMap.insert_insert_comm"], + ["simpNF", "LeanRV64D.Functions.bool_bit_backwards.eq_2"]] diff --git a/scripts/run_audit.sh b/scripts/run_audit.sh index e14699dc..5488ab2e 100755 --- a/scripts/run_audit.sh +++ b/scripts/run_audit.sh @@ -49,8 +49,17 @@ if scripts/check_no_skipkerneltc.sh; then else echo "FAIL: skipKernelTC reintroduced (see above)"; fail=1 fi -echo "native_decide occurrences (disclosed, witness battery + Sail memory bridges):" -grep -rn 'native_decide' SP1Clean --include='*.lean' | wc -l + +echo +echo "== A2 native_decide guard (gate: none in SP1Clean/) ==" +if scripts/check_no_native_decide.sh; then + echo "PASS: no native_decide in the main library" +else + echo "FAIL: native_decide in SP1Clean/ (see above)"; fail=1 +fi +echo "native_decide occurrences in SP1CleanTest/ (disclosed — the witness/trace conformance battery," +echo "the sole sanctioned native_decide; adds Lean.ofReduceBool/trustCompiler, confined off the main library):" +grep -rn 'native_decide' SP1CleanTest --include='*.lean' | wc -l echo echo "== A3 axiom census (the authoritative oracle) ==" diff --git a/scripts/sp1Lint.lean b/scripts/sp1Lint.lean new file mode 100644 index 00000000..5d424936 --- /dev/null +++ b/scripts/sp1Lint.lean @@ -0,0 +1,133 @@ +import Batteries.Tactic.Lint + +/-! +# sp1Lint — the project's `lake lint` driver (environment linters) + +A thin wrapper around the Batteries `#lint` framework (`getChecks` / `lintCore`), adapted for this +project in the two ways the stock `runLinter` exe cannot do: + +* **Decl filter by full module path.** `runLinter` scopes by namespace *root* (it lints + `getDeclsInPackage module.getRoot`), so pointing it anywhere in this package lints every `SP1Clean.*` + declaration — including the auto-generated `SP1Clean.Extracted.*` modules and the `*Vectors` trace + batteries. We instead keep only hand-written declarations (`getHandwrittenDecls`). NOTE: the per-file + `set_option linter.all false` headers on the generated files gate only the *syntactic* linters during + elaboration; they do nothing against these *environment* linters, which run post-import over the built + environment. `nolints.json` / `@[nolint]` are the only env-linter suppressions. + + INVARIANT: `getHandwrittenDecls` keeps only `SP1Clean.*` declarations, minus the auto-gen ones — + the `SP1Clean.Extracted` prefix (the constraint structs + the main-lib `Circuit/` forms) and any + module ending in "Vectors". The whole top-level **test** library `SP1CleanTest.*` (the witness/trace + conformance anchors + their `update_extracted.py`-written `…Vectors`/`Vectors.*` modules) is excluded + automatically, since `SP1Clean` is not a prefix of `SP1CleanTest` — so `lake lint` covers the main + library only (matching mathlib/batteries, which don't env-lint their test libs). + +* **Curated linter set.** We run a low-noise subset via `runOnly`, omitting the doc-coverage linters + (`docBlame`/`docBlameThm`/`tacticDocs`) that would swamp a proofs project. See `curatedLinters` + below; grow it deliberately (and see AGENTS.md § Linters). + +Wired as the package `lintDriver`, so `lake lint` runs it (after `lake build`, which the env linters need +for oleans). Residue is recorded in `scripts/nolints.json`; `lake exe sp1Lint --update` snapshots the +current violation set into it (the standard ratchet). Without `--update`, any *new* violation fails (exit 1). +-/ + +open Lean Core Elab Batteries.Tactic.Lint +open System (FilePath) + +/-- The curated environment-linter set (short names, as registered by `@[env_linter]`). The +doc-coverage linters (`docBlame`, `docBlameThm`, `tacticDocs`) are intentionally omitted — they would +swamp a proofs project — see AGENTS.md § Linters. `structureInType` and `deprecatedNoSince` (both +Mathlib `@[env_linter]`s, reached via the transitive Mathlib import) ARE included: low-noise hygiene +checks for structures that should be `Prop` and `@[deprecated]` attributes missing a `since` date. + +`unusedArguments` is also intentionally omitted: it flags only the project's *uniform* signature args +(`{p} [Fact p.Prime] [Fact (2^17 < p)]` / `NeZero p` instance binders, and the `ProverData`/`ProverHint` +args every chip `Spec`/`Assumptions`/`ProverAssumptions` carries by interface) — all structurally +required, none a real defect — and it grows a fresh false-positive per new chip, so even nolinting it +would be a permanent tax. Re-run it ad hoc (`getChecks` + this set + `unusedArguments`) if hunting +genuinely-dead args. -/ +def curatedLinters : List Name := + [`dupNamespace, `defLemma, `unusedHavesSuffices, + `checkUnivs, `checkType, `synTaut, `impossibleInstance, `nonClassInstance, + `simpNF, `simpVarHead, `simpComm, + `structureInType, `deprecatedNoSince] + +/-- The list of `nolints` pulled from the `nolints.json` file (linter short-name × declaration). -/ +abbrev NoLints := Array (Name × Name) + +/-- Read the given file path and deserialize it as JSON. -/ +def readJsonFile (α) [FromJson α] (path : FilePath) : IO α := do + let _ : MonadExceptOf String IO := ⟨throw ∘ IO.userError, fun x _ => x⟩ + liftExcept <| fromJson? <|← liftExcept <| Json.parse <|← IO.FS.readFile path + +/-- Serialize the given value `a : α` to the file as JSON. -/ +def writeJsonFile [ToJson α] (path : FilePath) (a : α) : IO Unit := + IO.FS.writeFile path <| toJson a |>.pretty.push '\n' + +/-- Hand-written `SP1Clean.*` declarations only: drops `SP1Clean.Extracted.*` and the `*Vectors` +auto-gen batteries. Mirrors `Batteries.Tactic.Lint.getDeclsInPackage`, but filters on the full module +path instead of just the namespace root. -/ +def getHandwrittenDecls : CoreM (Array Name) := do + let env ← getEnv + let keep := env.header.moduleNames.map fun m => + (`SP1Clean).isPrefixOf m + && !(`SP1Clean.Extracted).isPrefixOf m + && !m.toString.endsWith "Vectors" + return env.constants.map₁.fold (init := #[]) fun decls declName _ => + if keep[env.const2ModIdx[declName]?.get! (α := Nat)]! then decls.push declName else decls + +/-- +Usage: `sp1Lint [--update] [-v | --trace]` + +Runs the curated environment linters on the hand-written `SP1Clean.*` declarations (excluding the +auto-generated `Extracted/` modules and `*Vectors` batteries). The project must already be built +(`lake build SP1Clean`) — the env linters read the oleans. + +If `--update` is set, `scripts/nolints.json` is overwritten with the current violation set (the standard +ratchet: accept the current state, then fail on any *new* violation). Otherwise, exits 1 if any violation +remains after subtracting `nolints.json`. +-/ +unsafe def main (args : List String) : IO Unit := do + let update := args.contains "--update" + let verbose := args.contains "-v" || args.contains "--trace" + initSearchPath (← findSysroot) + let projectModule := `SP1Clean + let lintModule := `Batteries.Tactic.Lint + -- The env linters need built oleans; require them rather than driving a build from here. + for m in [projectModule, lintModule] do + let olean ← findOLean m + unless (← olean.pathExists) do + IO.eprintln s!"sp1Lint: missing olean for `{m}` at:\n {olean}\n\ + Run `lake build SP1Clean` first." + IO.Process.exit 1 + let nolintsFile : FilePath := "scripts/nolints.json" + let nolints ← if ← nolintsFile.pathExists then readJsonFile NoLints nolintsFile else pure #[] + unsafe Lean.enableInitializersExecution + let env ← importModules #[projectModule, lintModule] {} (trustLevel := 1024) (loadExts := true) + let opts : Options := if verbose then ({} : Options).setBool `trace.Batteries.Lint true else {} + let ctx := { fileName := "", fileMap := default, options := opts } + let state := { env } + Prod.fst <$> (CoreM.toIO · ctx state) do + let decls ← getHandwrittenDecls + traceLint s!"Linting {decls.size} hand-written declarations…" + (inIO := true) (currentModule := projectModule) + let linters ← getChecks (slow := true) (runOnly := some curatedLinters) (runAlways := none) + let results ← lintCore decls linters (inIO := true) (currentModule := projectModule) + if update then + traceLint s!"Updating nolints file at {nolintsFile}" + (inIO := true) (currentModule := projectModule) + writeJsonFile (α := NoLints) nolintsFile <| + .qsort (lt := fun (a, b) (c, d) => a.lt c || (a == c && b.lt d)) <| + .flatten <| results.map fun (linter, decls) => + decls.fold (fun res decl _ => res.push (linter.name, decl)) #[] + let results := results.map fun (linter, decls) => + .mk linter <| nolints.foldl (init := decls) fun decls (linter', decl') => + if linter.name == linter' then decls.erase decl' else decls + let failed := results.any (!·.2.isEmpty) + if failed then + let fmt ← formatLinterResults results decls (groupByFilename := true) (useErrorFormat := true) + "in SP1Clean (hand-written)" (runSlowLinters := true) .medium linters.size + IO.print (← fmt.toString) + IO.Process.exit 1 + else + IO.println "-- sp1Lint: linting passed." + IO.Process.exit 0 diff --git a/update_extracted.py b/update_extracted.py index b506e3a2..f616f3a6 100644 --- a/update_extracted.py +++ b/update_extracted.py @@ -103,8 +103,8 @@ } # Chips with a **whole-trace** dumper in the `witness_vectors` binary (`--chip `) → an extra -# `Proofs/TraceGenTests/ChipTraceVectors.lean` (the event battery + the full padded matrix from -# SP1's real `generate_trace`) plus a hand-written `Proofs/TraceGenTests/ChipTraceWitness.lean` +# `SP1CleanTest/TraceGenTests/ChipTraceVectors.lean` (the event battery + the full padded matrix from +# SP1's real `generate_trace`) plus a hand-written `SP1CleanTest/TraceGenTests/ChipTraceWitness.lean` # anchor that re-derives every row from the chip's own circuit (`TraceGenerator.lean` + # `EventPopulate.lean`) and checks the matrices match. Value = (expected row width — a layout-drift # tripwire; the dumper also asserts it, reader kind — picks the dumped record type and the Lean @@ -126,7 +126,7 @@ # Ordered JSON keys of one dumped event, per reader kind = the field order of Lean's # `AluEventRec` / `AluTypeEventRec` / `AluEventOpRec` / `AluTypeOpEventRec` -# (`Proofs/TraceGenTests/EventPopulate.lean`). +# (`SP1CleanTest/TraceGenTests/EventPopulate.lean`). TRACE_EVENT_KEYS: Dict[str, List[str]] = { "RType": [ "clk", "pc", "a", "b", "c", "opA", "opB", "opC", @@ -225,8 +225,11 @@ # regenerated files so the extraction provenance is always recorded in-repo. SP1_PINNED_COMMIT = "9d249b8d4fb7d00156bf77f5d295d1dbcaaf4136" EXTRACTED_DIR = os.path.join("SP1Clean", "Extracted") -WITNESS_DIR = os.path.join("SP1Clean", "Extracted", "WitnessVectors") -TRACEGEN_DIR = os.path.join("SP1Clean", "Proofs", "TraceGenTests") +# The witness/trace conformance vectors are test-only data: they live in the top-level +# `SP1CleanTest` test library (built by `lake test`), not the main `SP1Clean` library — which is +# gated `native_decide`-free (the anchors that consume these vectors are the only `native_decide`). +WITNESS_DIR = os.path.join("SP1CleanTest", "WitnessTests", "Vectors") +TRACEGEN_DIR = os.path.join("SP1CleanTest", "TraceGenTests") COMMON_IMPORTS = """import SP1Clean.Math.Word import SP1Clean.Extracted.ExtractionDSL @@ -657,7 +660,7 @@ def render_witness_vectors(operation: str, data: dict) -> str: f"imperative code and cannot be symbolically extracted; these vectors instead tie the Lean\n" f"witness function to the Rust source by **conformance** (agreement on the sampled inputs —\n" f"edge cases + a seeded LCG — not an all-inputs proof). Each entry is `{tuple_desc}`. The\n" - f"check lives in `SP1Clean/WitnessTests/{operation}Witness.lean`. Regenerate with\n" + f"check lives in `SP1CleanTest/WitnessTests/{operation}Witness.lean`. Regenerate with\n" f"`SP1_DIR=… python3 update_extracted.py`. -/" ) return ( @@ -708,12 +711,12 @@ def render_trace_vectors(chip: str, width: int, kind: str, data: dict) -> str: f"`MachineAir::generate_trace` by `update_extracted.py` (the `witness_vectors` binary,\n" f"`--chip {chip}`): the deterministic event battery and the full padded {width}-column\n" f"row-major matrix (including SP1's zero padding rows). The anchor\n" - f"`SP1Clean/Proofs/TraceGenTests/{chip}ChipTraceWitness.lean` re-derives every row from the\n" + f"`SP1CleanTest/TraceGenTests/{chip}ChipTraceWitness.lean` re-derives every row from the\n" f"chip's own circuit (`TraceGenerator.lean`) and checks the matrices match. Regenerate\n" f"with `SP1_DIR=… python3 update_extracted.py`. -/" ) return ( - "import SP1Clean.Proofs.TraceGenTests.EventPopulate\n\n" + "import SP1CleanTest.TraceGenTests.EventPopulate\n\n" + doc + "\n\n" + "namespace SP1Clean.TraceGenTests\nopen SP1Clean\n\n" + LINTERS_OFF + "\n\n"