Formal verification of OpenCSV

What is proven, what is assumed, and exactly where the gap between the two lives. Mechanized in Lean 4 — sorry-free, no mathlib, every cryptographic assumption labeled.

← back to OpenCSV · opencsvnet/opencsv-formal

In plain language

The full tables are below. This is the three-sentence version.

What you still trust, and what breaks if it's wrong

Every proof rests on assumptions. We don't hide them — each is named, and each failure mode is visible rather than silent.

You trust that…If it breaks…Mitigation
Poseidon2 hashes are collision-resistantcounterfeiting becomes possibleconservative parameters; swap hash without protocol change
The FRI proof system is soundfake validity proofs could passtransparent setup; audits re-check proofs publicly
The issuer keeps its mint key safeinflation — but visible, never hiddenpublic mint stream; key rotation cuts off rogue mints
At least one of N indexers is honesta double-spend could be hidden from youcross-check independent indexers; zero-trust self-scan per receipt
Bitcoin isn't reorged past k confirmationsaccepted payments can be undonesame assumption as any Bitcoin payment

What the theorems say

Four headline results about the protocol logic — the layer where catastrophic bugs (inflation, double-spend acceptance) would live, independent of any cryptography.

T1Inflation soundness

Along any valid trace, per asset: net value in circulation equals Σ mints − Σ redeems, and every mint carries a valid issuer signature. Nobody prints money — not the users, not a forger, and not the issuer beyond its own signed, publicly anchored mints. Paper §5.1.

T2Conservation

Every transfer step preserves per-asset totals: outputs sum to inputs, so shielded transfers can neither create nor destroy value — which is what makes the public mint/redeem stream an exact audit of supply. Paper §4.5.

T3Nullifier uniqueness & first occurrence

One coin yields exactly one nullifier, so a double-spend is an observable conflict to anyone holding nf (owner and consignment recipients), and the first occurrence resolves it deterministically: two recipients cannot both finally accept. On-chain payloads are context-bound (P = H(nf, ctx)), and both grief directions are theorems: a copied record under a different context is provably invisible (griefer_copy_invisible), and an adversary who doesn't know nf can forge no occurrence at all (no_occurrence_without_knowledge). Paper §5.2, §4.7.

T4Receiver correctness

The receiver's Accept checklist (verify proof, check anchor, check no earlier occurrence, check ownership) succeeds only on transactions that extend a valid trace — recipients can't be fooled into accepting counterfeit coins, modulo proof-system soundness. Paper §4.8, §5.1.

Where the cryptographic hardness lives

Every assumption is an explicitly labeled axiom — the build prints an axiom audit (#print axioms) per theorem. Nothing is hidden in the proof tactics.

AssumptionFormUsed by
assetId_injectivecollision resistance of the genesis hashasset binding
commitHash_injectivebinding of coin commitmentscoin identity
ownerKey_injectivecollision resistance of owner = H(osk)T3
sig_unforgeableEUF-CMA of the issuer scheme, abstractlyT1
ProofSystem.soundsoundness of Π, as a structure fieldT4
bindHash_injectivecollision resistance of the anchor payload H(nf, ctx) in both argumentsT3 (conflict soundness)
occurrence_requires_knowledgepreimage resistance: a fresh well-formed entry implies knowledge of nfT3 (anti-grief)

Notable by absence: the first-occurrence lemmas are pure list reasoning and depend on no cryptographic axioms at all.

Live from the build

Regenerated weekly by CI from axiom-audit.txt in opencsv-formal — this block can never drift from the actual proofs.

TheoremAxiom dependencies
OpenCsv.Batch.batch_commit_uniquepropext, Classical.choice, OpenCsv.bindHash, OpenCsv.bindHash_injective, Quot.sound
OpenCsv.Batch.batch_exclusion_soundpropext, OpenCsv.bindHash, Quot.sound
OpenCsv.Batch.batch_occurrence_iffOpenCsv.bindHash
OpenCsv.Batch.chainOccurrence_expandWindowpropext, OpenCsv.bindHash, Quot.sound
OpenCsv.Batch.coordinator_cannot_forgeOpenCsv.KnowsRawNf, OpenCsv.bindHash, OpenCsv.occurrence_requires_knowledge
OpenCsv.Batch.coordinator_envelope_no_occurrenceOpenCsv.KnowsRawNf, OpenCsv.bindHash, OpenCsv.occurrence_requires_knowledge
OpenCsv.Scan.anchor_bearing_is_candidatepropext, Quot.sound
OpenCsv.Scan.filter_absence_trustlesspropext, Quot.sound
OpenCsv.Scan.filter_no_false_negativespropext, Quot.sound
OpenCsv.Scan.marker_zero_authorityOpenCsv.bindHash
OpenCsv.Scan.scan_exclusion_soundpropext, OpenCsv.bindHash, Quot.sound
OpenCsv.Scan.served_list_falsifiablepropext, Classical.choice, Quot.sound
OpenCsv.Value.carry_complete_singlepropext, Quot.sound
OpenCsv.Value.carry_soundpropext, Classical.choice, Quot.sound
OpenCsv.Value.difference_interval_within_half_fieldnone
OpenCsv.Value.no_wrappropext, Quot.sound
OpenCsv.Value.range_checked_represents_exactlypropext, Quot.sound
OpenCsv.accepted_trace_supplypropext, OpenCsv.Sig, OpenCsv.SigVerify, OpenCsv.assetId, OpenCsv.bindHash, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey, Quot.sound
OpenCsv.copied_record_not_wellformedOpenCsv.bindHash, OpenCsv.bindHash_injective
OpenCsv.double_spend_conflictOpenCsv.bindHash, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey, OpenCsv.ownerKey_injective
OpenCsv.griefer_copy_invisiblepropext, OpenCsv.bindHash, OpenCsv.bindHash_injective
OpenCsv.inflation_soundnesspropext, OpenCsv.Sig, OpenCsv.SigVerify, OpenCsv.assetId, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey, Quot.sound
OpenCsv.later_occurrence_rejectedOpenCsv.bindHash
OpenCsv.mints_authorizedpropext, OpenCsv.Sig, OpenCsv.SigVerify, OpenCsv.SignedByIssuer, OpenCsv.assetId, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey, OpenCsv.sig_unforgeable
OpenCsv.no_occurrence_without_knowledgeOpenCsv.KnowsRawNf, OpenCsv.bindHash, OpenCsv.occurrence_requires_knowledge
OpenCsv.payload_binds_one_nullifierOpenCsv.bindHash, OpenCsv.bindHash_injective
OpenCsv.receiver_correctnesspropext, OpenCsv.Sig, OpenCsv.SigVerify, OpenCsv.assetId, OpenCsv.bindHash, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey
OpenCsv.transfer_conservationpropext, OpenCsv.Sig, OpenCsv.SigVerify, OpenCsv.assetId, OpenCsv.commitHash, OpenCsv.nullHash, OpenCsv.ownerKey, Quot.sound
OpenCsv.unknowing_adversary_entry_invisiblepropext, OpenCsv.KnowsRawNf, OpenCsv.bindHash, OpenCsv.occurrence_requires_knowledge

29 theorems, audited 2026-08-02 from axiom-audit.txt @ 2b42286

regenerated 2026-08-02 01:34 UTC · build green · opencsv-formal@2b42286

The honesty gaps

"Formally verified" always means less than it sounds. Here is precisely what it means here — three layers, stated plainly.

Roadmap

Tracked as opencsv-formal#1. One item already shipped; item 1 converts our scariest informal argument into a theorem.

  1. CI axiom gating — shipped

    Every headline theorem's #print axioms output is normalized into a checked-in baseline; CI fails if any assumption set changes without a human regenerating it in the same commit. The honesty table on this page is an enforced invariant, not a claim.

  2. Limb-arithmetic soundness — shipped

    The u64 conservation gadget's carry argument (24/24/16-bit limbs, field equality ⟺ integer equality) is mechanized (b036cf3) — wrap-around cannot fake balance. Honest bonus: the general converse is false (completeness, not soundness) — documented.

  3. Scan soundness — shipped, roadmap item eliminated

    OpenCsv.Scan (00edaed): no-false-negatives of filter discovery, scan-exclusion soundness (scan-first ≡ full-block scanning), marker zero-authority, accelerator fraud-provability. The N-of-M honesty hypothesis is eliminated — the architecture got better, so the assumptions got fewer.

  4. Differential testing, model ↔ code

    Make the Lean model executable, generate random valid/invalid traces, and run them against the Rust accept() driver — catches drift between the verified model and the implementation empirically.

  5. Verify-then-adopt: the kernel becomes the shipped code

    The kernel (opencsv-kernel) is already proved equal to this specification in formal-aeneas. The remaining step is adoption: opencsv-core and the FFI call the verified kernel as the production implementation, and the accept() decision driver gets its own pure kernel (the big design item, A5). "The model is the code" by verification-and-adoption, not by extraction.

  6. PCD-tree model

    Upgrade the state machine from linear traces to the tree structure of the real recursion, matching the paper's §5.1 induction.

  7. Verified predicate→AIR compiler (research track)

    Rather than verifying hand-written AIR constraints after the fact: define predicates in a Lean-embedded DSL, prove the translation sound once, generate circuits. Every future predicate inherits the proof.