INV-* -> Evidence Traceability Matrix#
Generated by
scripts/security/build_traceability.pythen hand-reviewed. TheCoverage classcolumn is hand-verified against what each cited harness/test actually proves — re-running the generator emits provisional classes (a mechanical grep heuristic) that must be re-reviewed. CI runsbuild_traceability.py --check, which validates only that everyINV-*indocs/security/invariants.mdhas a row here (ID-set coverage), never cell content.
Reading the classes. proven = a Kani proof whose domain is full-type
(CBMC explores every value of the input type). boundedly-verified = a Kani proof
whose domain is protocol-bounded / representative / unwind- or capacity-bounded
(a faithful but non-exhaustive envelope — e.g. price ∈ (0, P_MAX), an unwind(2)–unwind(4)
crossing sequence, or a symbolic-tag address model). tested = runtime/property/unit
tests exercise it with no machine-checked proof. partially-covered = the per-instruction
slice is proven/tested but the full aggregate/composed form the invariant states is
deferred (release-test/proof obligation, not a runtime scan). assumption-dependent =
the stated form is a cross-account aggregate that holds only under an off-chain
reconciliation currently gated on OPS-02. not-yet-verified = enforced on-chain but with
no targeted proof or test evidence yet. not-practically-formalizable = a proof would
be semantically vacuous or out of scope (documented rationale). The classes are
deliberately not collapsed to a single percentage — a percentage destroys exactly the
distinction between "proven full-type", "proven only within a bound", and "not verified".
Honest-bounds callouts (this plan's frozen A–C state). Orderbook no-cross/conservation
is boundedly-verified — the crossing/fill traversal is explored under #[kani::unwind(2)]
(one fill) through #[kani::unwind(4)] (two fills) plus a concrete three-fill fixture, never
exhaustively; a place-order crossing ≥4 makers is outside the verified envelope. INV-M6
oracle feed-identity pinning is not-practically-formalizable — a unit proof over the
[u8;32] equality helper would re-prove a one-liner against a copy of itself, so it is
backed by instruction-level tests instead (H-01 rationale; the deleted P-LOADER-01 harness
is the same class of vacuity). Several DOB-v3 and spline invariants remain not-yet-verified
or tested-only: ~9 instructions have zero targeted runtime coverage and 13+ invariants have
zero Kani coverage.
| Invariant ID | Statement | Disposition | Kani harnesses | Tests | Coverage class |
|---|---|---|---|---|---|
INV-X1 | Bounded Order-Placement Account Surface | enforced-on-chain | — | litesvm_place_order_full_surface.rs<br>loader_discriminator_owner_matrix.rs<br>adversarial_account_aliasing.rs | tested |
INV-X2 | No Crossed or Unsorted Book on Commit | enforced-on-chain (subset) + accepted (full form) | orderbook_invariants.rs::crossed_book_rejected_by_invariant_checker<br>orderbook_invariants.rs::unsorted_bids_rejected_by_invariant_checker<br>processor_flow.rs::matching_book_buyyes_cap2_preserves_price_time_order | — | boundedly-verified |
INV-X3 | Single Canonical Maker-Delta Applicator | enforced-on-chain | reduce_order_release.rs::slot_routed_reduce_release_rejects_exactly_ask_sides<br>cancel_ff_route_to_slot.rs::apply_maker_fill_delta_to_slot conservation | litesvm_cancel_order_refund_surface.rs<br>litesvm_force_close_refund_harm.rs | boundedly-verified |
INV-X4 | Emergency Status Fails Closed | enforced-on-chain | — | — | not-yet-verified |
INV-X5 | Maker Fill Credits Are Single-Application | enforced-on-chain | — | — | not-yet-verified |
INV-G1 | Account Ownership | enforced-on-chain | — | loader_discriminator_owner_matrix.rs<br>adversarial_account_aliasing.rs | tested |
INV-G2 | PDA Authenticity | enforced-on-chain | account_validation.rs::canonical_bump_required_biconditional (fixed-PDA bump comparison only)<br>create_market_pda.rs::build_market_seeds_emits_canonical_layout<br>create_market_pda.rs::build_orderbook_seeds_emits_canonical_layout | pda.rs::bump_verification_tests::non_canonical_bump_never_yields_canonical_address<br>security_pda_validation.rs<br>adversarial_account_aliasing.rs | tested |
INV-G3 | Discriminator Integrity | enforced-on-chain | — | loader_discriminator_owner_matrix.rs | tested |
INV-M1 | Epoch Alignment | enforced-on-chain | create_market_pda.rs::validate_market_id_matches_creation_window_duration_900<br>create_market_pda.rs::validate_market_id_window_boundary_is_closed | inv_m1_epoch_alignment.rs | boundedly-verified |
INV-M2 | Snapshot Immutability | enforced-on-chain | snapshot_write_path.rs::snapshot_write_captured_different_value_rejected<br>snapshot_start_write_path.rs::snapshot_start_write_captured_different_value_rejected | test_risk_oracle_adversarial.rs | boundedly-verified |
INV-M3 | Resolution Determinism | enforced-on-chain | state_machine.rs::outcome_up_iff_gte<br>resolve_logic.rs::compute_outcome_equal_expo_full_i64 | differential_reference_tests.rs | proven |
INV-M4 | State Monotonicity | enforced-on-chain | state_machine.rs::valid_transitions_are_forward<br>state_machine.rs::no_backward_transitions<br>state_machine.rs::closed_is_terminal | integration_lifecycle.rs<br>adversarial_lifecycle.rs | boundedly-verified |
INV-M5 | Resolution Rule | enforced-on-chain | state_machine.rs::outcome_up_iff_gte<br>resolve_logic.rs::compute_outcome_canonical_matches_normalize | differential_reference_tests.rs | proven |
INV-M6 | Oracle Feed-Identity Pinning | enforced-on-chain | — (intentionally not formalized — see note) | security_wrong_feed_id.rs<br>test_risk_oracle_adversarial.rs | not-practically-formalizable |
INV-EXT-1 | Core Resolution Is Source-Agnostic | enforced-on-chain | — | adversarial_external_resolver.rs | tested |
INV-EXT-2 | Resolve and Halt Require the Committed Registered Resolver | enforced-on-chain | — | adversarial_external_resolver.rs | tested |
INV-EXT-3 | External Outcome Is Written Once | enforced-on-chain | — | adversarial_external_resolver.rs<br>adversarial_lifecycle.rs | tested |
INV-EXT-4 | One Market per Resolver, Platform, and External Reference | enforced-on-chain | — | adversarial_external_resolver.rs | tested |
INV-EXT-5 | Unknown Market Kinds Fail Closed | enforced-on-chain | — | adversarial_external_resolver.rs | tested |
INV-EXT-6 | Halt Blocks Placement but Preserves Exits | enforced-on-chain | external_halt.rs::external_halt_live_applier_is_idempotent | adversarial_external_resolver.rs | boundedly-verified |
INV-EXT-7 | External Payouts Come Only from Authenticated Numerators | enforced-on-chain | settlement_conservation.rs::scalar_paired_payout_conserves_collateral | adversarial_external_resolver.rs | boundedly-verified |
INV-RECLAIM-1 | Claim Identity Is Fully Bound | enforced-on-chain | — | program/tests/reclaim_v1_cpi_gate.rs<br>program/tests/reclaim_v1_transition_litesvm.rs<br>reclaim-verifier-core/tests/mutation_matrix.rs | tested |
INV-RECLAIM-2 | Attestor Signature Policy Is Exact | enforced-on-chain | — | reclaim-verifier-core/tests/signature_policy.rs<br>reclaim-verifier-core/tests/verification_policy.rs<br>programs/reclaim-verifier/tests/governance_contract.rs | tested |
INV-RECLAIM-3 | Epoch Snapshot Rotation Fails Closed | enforced-on-chain | — | programs/reclaim-verifier/tests/governance_contract.rs<br>programs/reclaim-verifier/tests/governance_litesvm.rs | tested |
INV-RECLAIM-4 | Verifier Receipt Consumption Is One Shot | enforced-on-chain | — | programs/reclaim-verifier/tests/verify_contract.rs<br>programs/reclaim-verifier/tests/receipt_litesvm.rs<br>program/tests/reclaim_v1_transition_litesvm.rs | tested |
INV-EVENT-SEQ-1 | A Sequence Number Is Consumed Only When a Batch Is Emitted | enforced-on-chain | — | security_event_sequence_tail_gate.rs<br>security_event_recorder.rs<br>agentflow_fresh_16_reduce_events.rs | tested |
INV-EVENT-SEQ-2 | Emitted Sequences Are Strictly Increasing Per Subject | enforced-on-chain | — | agentflow_fresh_14_event_sequence.rs<br>agentflow_fresh_16_reduce_events.rs<br>spline_lifecycle.rs | tested |
INV-O1 | No Crossed Book | enforced-on-chain | orderbook_invariants.rs::crossed_book_rejected_by_invariant_checker<br>orderbook_invariants.rs::insert_bid_no_crossing<br>processor_flow.rs::apply_orderbook_updates_resting_bid_preserves_sort_or_rejects_cross | — | boundedly-verified |
INV-O2 | Price Bounds | enforced-on-chain (subset) + accepted (full form) | processor_flow.rs::place_order_validation_price_gate_matches_open_interval<br>orderbook_invariants.rs::bid_price_above_p_max_rejected_by_invariant_checker | differential_reference_tests.rs | boundedly-verified |
INV-O3 | Quantity Positive | enforced-on-chain | processor_flow.rs::place_order_validation_rejects_zero_quantity<br>processor_flow.rs::place_order_validation_quantity_gate_matches_bounds | integration_withdraw_shares.rs | boundedly-verified |
INV-O4 | Order Ownership | accepted (test/proof-only) | order_ownership_reconciliation.rs::owner_matched_active_slot_binds_to_position_owner<br>order_ownership_reconciliation.rs::foreign_active_slot_is_not_attributed | — | boundedly-verified |
INV-O5 | Sorted Orders | enforced-on-chain (subset) + accepted (full form) | orderbook_invariants.rs::unsorted_bids_rejected_by_invariant_checker<br>orderbook_invariants.rs::insert_bid_maintains_sort<br>orderbook_invariants.rs::insert_ask_maintains_sort | — | boundedly-verified |
INV-P1 | Non-Negative Shares | enforced-on-chain | mint_shares_pair_mint.rs::mint_yes_overflow_rejected<br>supply_reconciliation.rs::ff_fill_burn_yes_insolvency_rejected_without_debit<br>withdraw_shares_promotion.rs::debit_withdraw_share_slot_rejects_over_available | — | boundedly-verified |
INV-P2 | Locked Less Than Total | accepted (test/proof-only) | order_ownership_reconciliation.rs::locked_yes_shares_dominate_active_sellyes_slots<br>order_ownership_reconciliation.rs::locked_no_shares_dominate_active_buyno_slots<br>order_ownership_reconciliation.rs::position_locked_never_exceeds_total_via_accessors | — | boundedly-verified |
INV-P3 | Single Settlement | enforced-on-chain | single_settlement.rs::redeem_settled_byte_monotone_full_symbolic<br>single_settlement.rs::redeem_two_call_replay_is_idempotent | adversarial_lifecycle.rs | boundedly-verified |
INV-P4 | Collateral Coverage | enforced-on-chain (subset) + accepted (full form) | collateral_liability_induction.rs::empty_position_liability_equality_base<br>collateral_liability_induction.rs::cancel_side_release_equals_canonical_c<br>order_ownership_reconciliation.rs::locked_collateral_dominates_active_bid_slots | — | boundedly-verified |
INV-V1 | Solvency | enforced-on-chain | solvency_fee_inclusive.rs::solvency_fee_inclusive_redeem<br>processor_flow.rs::place_order_post_cpi_solvency_gate_matches_market_check | security_solvency_stateful_fuzz.rs | boundedly-verified |
INV-V2 | Conservation | enforced-on-chain (subset) + accepted (full form) | settlement_collateral_conservation.rs::tripwire_accepts_only_exact_pay<br>settlement_collateral_conservation.rs::tripwire_rejects_non_exact_pay<br>conservation_multifill_widening.rs (bounded 3-fill fixture) | — | partially-covered |
INV-V3 | Empty at Close | enforced-on-chain | empty_at_close_convergence.rs::closed_state_implies_zero_supply_floor<br>empty_at_close_convergence.rs::close_gate_accept_implies_discharged_tracked_obligations<br>close_market_gate.rs::close_market_gate_rejects_unresolved | litesvm_close_position_cpi.rs<br>adversarial_lifecycle.rs | boundedly-verified |
INV-V4 | Share Balance | accepted (test/proof-only) | mint_shares_supply_reconciliation.rs::mint_supply_update_increments_both_sides_by_amount<br>redeem_reconciliation.rs::redeem_market_update_monotone_decreasing<br>supply_reconciliation.rs::ff_fill_burn_decrements_supply_by_burn (per-instruction inductive steps) | security_solvency_stateful_fuzz.rs | partially-covered |
INV-V5 | Settlement/Share Decimal Identity | enforced-on-chain | — | integration_initialize_config.rs<br>security_init_config.rs | tested |
INV-T1 | Snapshot Timing | enforced-on-chain | snapshot_timing_matrix.rs::snapshot_end_already_captured_is_immutable<br>expire_late_capture.rs (late-capture boundary proofs) | — | boundedly-verified |
INV-T2 | Trading Window | enforced-on-chain | processor_flow.rs::place_order_validation_trading_window_matches_half_open_interval | property_tests.rs | proven |
INV-T3 | Settlement After Resolution | enforced-on-chain | force_close_gate.rs::force_close_gate_rejects_resolved<br>force_close_gate.rs::force_close_gate_ok_implies_all_guards_pass<br>processor_flow.rs::mark_position_settled_terminal_gate_rejects_unresolved | litesvm_force_close_refund_harm.rs | proven |
INV-F1 | Capped-Linear-Decay Curve | enforced-on-chain | fee_curve.rs::fee_bps_bounded_by_cap<br>fee_curve_widening.rs::fee_bps_clamped_to_cap_full_price_grid<br>fee_curve_widening.rs::fee_bps_matches_capped_decay_definition_full_price_grid | — | boundedly-verified |
INV-F2 | Three-Way Fee Split | enforced-on-chain | config_validation.rs::fee_split_invariant_holds<br>fee_curve.rs::apply_split_conserves_total | litesvm_place_order_fee_split_runtime.rs | proven |
INV-F3 | Referral Attribution Lifetime | enforced-on-chain | — | bootstrap_market_local_referral.rs<br>security_referral_shard1_pr245.rs | tested |
INV-F4 | Referrer Earnings Solvency | accepted (test/proof-only) | — | security_referrer_claim.rs<br>security_referral_shard1_pr245.rs | assumption-dependent |
INV-FEE-T1 | Config holds exactly 8 treasury recipients | enforced-on-chain | config_validation.rs::treasury_recipient_accessor_matches_fixed_slots<br>config_validation.rs::update_treasury_recipients_replaces_all_eight_slots | integration_initialize_config.rs | boundedly-verified |
INV-FEE-T2 | Every fee-paying instruction binds the recipient to the configured set | enforced-on-chain | — | adversarial_ix_data.rs<br>agentflow_fresh_15_place_ff_wrappers.rs | tested |
INV-FEE-T3 | System-wide solvency reconciliation across all shards | accepted (test/proof-only) | — | integration_multi_treasury_settlement.rs | assumption-dependent |
INV-FEE-T4 | UpdateTreasuryRecipients rejects zero pubkeys | enforced-on-chain | config_validation.rs::update_treasury_recipients_rejects_any_zero_slot_atomically | security_update_treasury_recipients.rs<br>property_tests_treasury_recipients.rs<br>mutation_killers.rs | boundedly-verified |
INV-FEE-T5 | UpdateTreasuryRecipients rejects pairwise duplicates | enforced-on-chain | config_validation.rs::update_treasury_recipients_rejects_any_duplicate_pair_atomically | security_update_treasury_recipients.rs<br>property_tests_treasury_recipients.rs<br>mutation_killers.rs | boundedly-verified |
INV-REFERRAL-T1 | Every ReferrerEarningsAccount has a valid bound shard | enforced-on-chain | — | agentflow_fresh_9_multi_fee_recipients_atomicity.rs<br>agentflow_fresh_11_referral_init_loaders.rs | tested |
INV-REFERRAL-T2 | Market-local deferred liabilities reconcile | enforced-on-chain | — | security_referral_shard1_pr245.rs<br>security_referrer_claim_shard3_targeted.rs<br>security_shard4_compound.rs | tested |
INV-REFERRAL-T3 | Rollup preserves treasury coverage | enforced-on-chain | — | security_referral_shard1_pr245.rs<br>security_referrer_claim.rs | tested |
INV-BAND-1 | No-panic on any u16 input | accepted (test/proof-only) | price_band.rs::proof_compute_band_no_panic | — | proven |
INV-BAND-2 | lo ≤ hi always | accepted (test/proof-only) | price_band.rs::proof_compute_band_bounds_invariant | — | boundedly-verified |
INV-BAND-3 | Band width bounded by 2 × MAKER_BAND_BPS when dynamic | accepted (test/proof-only) | price_band.rs::proof_band_width_bounded_by_2x_band_bps | — | boundedly-verified |
INV-BAND-4 | Midpoint lies within [lo, hi] when band is dynamic and unclipped | accepted (test/proof-only) | price_band.rs::proof_band_contains_mid_when_not_clamped | — | boundedly-verified |
INV-BAND-5 | IOC orders bypass the gate unconditionally | enforced-on-chain | — | concurrency_price_band.rs | tested |
INV-BAND-6 | Gate executes after canonical conversion + tick alignment, before matching | enforced-on-chain | — | concurrency_price_band.rs<br>determinism_price_band.rs | tested |
INV-SPLINE-1 | Aggregate commitment conservation | enforced-on-chain | — | spline_lifecycle.rs<br>spline_matching_integration.rs | tested |
INV-SPLINE-2 | Shape exposure is commitment-bounded | enforced-on-chain | spline_matching.rs::one_region_single_snapshot_walk_within_exposure | spline_lifecycle.rs | boundedly-verified |
INV-SPLINE-3 | Extended solvency and pair mint | enforced-on-chain (subset) + accepted (full form) | spline_matching.rs::spline_pair_mint_pure_floor_ceil_identity | spline_lifecycle.rs | boundedly-verified |
INV-SPLINE-4 | Virtual quotes never cross | enforced-on-chain (subset) + accepted (full form) | — | spline_matching_equivalence.rs<br>spline_matching_integration.rs | tested |
INV-SPLINE-5 | Settlement retires both full inventories | enforced-on-chain | — | spline_lifecycle.rs | tested |
INV-DOB-V3-1 | Four Logical Lanes, Two Shared Physical Arenas | enforced-on-chain | dob_v3_proofs.rs::dob_lane_total_involution | — | boundedly-verified |
INV-DOB-V3-2 | Lane and Capacity Aggregate Accounting | enforced-on-chain | — | — | not-yet-verified |
INV-DOB-V3-3 | Single-Lane Slot Reachability | enforced-on-chain (subset) + accepted (full form) | — | — | not-yet-verified |
INV-DOB-V3-4 | Lane Authority Is the Stored Order, Never the Caller | enforced-on-chain | — | — | not-yet-verified |
INV-DOB-V3-5 | Exact-Complementary Matching Only | enforced-on-chain | dob_v3_proofs.rs::dob_lane_total_involution | — | boundedly-verified |
INV-DOB-V3-6 | Merged Canonical Reads Preserve Strict Price-Time Order | enforced-on-chain (subset) + accepted (full form) | — | — | not-yet-verified |
INV-DOB-V3-7 | Order-ID Formula Is Lane-Free and Byte-Compatible | enforced-on-chain | — | — | not-yet-verified |
INV-DOB-V3-8 | Mutation Rollback Restores Every Lane Byte-for-Byte | enforced-on-chain | — | — | not-yet-verified |
INV-DOB-V3-9 | Reclaim-Gated Structural Best/Worst Fails Closed | enforced-on-chain | dob_v3_proofs.rs::dob_structural_best_fail_closed<br>dob_v3_proofs.rs::dob_structural_worst_fail_closed | — | boundedly-verified |
INV-DOB-V3-10 | Account Version 1, Layout Sizes Unchanged | enforced-on-chain | — | — | not-yet-verified |
Coverage-class distribution (75 invariants)#
| Coverage class | Count |
|---|---|
| proven | 6 |
| boundedly-verified | 31 |
| tested | 24 |
| partially-covered | 2 |
| assumption-dependent | 2 |
| not-yet-verified | 9 |
| not-practically-formalizable | 1 |
proven (full-type Kani domain, 5 value proofs): INV-M3, INV-M5, INV-T2,
INV-T3, INV-F2. INV-BAND-1 also carries the proven label but in the narrower
no-panic-over-all-u16 sense — a smoke-domain proof (absence of panics), not a value
property — so the "5 full-type value proofs" figure is not overstated by the 6th row.
The not-yet-verified set is INV-X4, INV-X5, and INV-DOB-V3-{2,3,4,6,7,8,10}.
Notes on specific rows#
INV-G2(tested, downgraded fromprovenon 2026-08-26). PDA verification no longer re-runs the canonical bump search on non-creation paths:verify_*_pdarecomputes the address from the stored bump withcreate_program_addressand the caller compares it against the account's actual address.account_validation.rs::canonical_bump_required_biconditionalstill proves the bump comparison, but that comparison now guards only the fixed-seed PDA fast path and the creation paths — it is no longer the load-bearing gate for INV-G2. The load-bearing gate is address equality, and it is runtime-tested, not proven: a full-u8sweep inprogram/src/utils/pda.rs::bump_verification_tests::non_canonical_bump_never_yields_canonical_addressshows every non-canonical bump either errors or yields a different address, andsecurity_pda_validation.rsplusadversarial_account_aliasing.rsexercise the same gate through the real loaders on BPF. A Kani proof of the address-equality form would have to model SHA-256, which is not tractable — the twocreate_market_pda.rsharnesses that remain prove seed-layout emission only.INV-M6(not-practically-formalizable).docs/security/invariants.mdstates a Kani harness overvalidate_pyth_feed_address/validate_pyth_feed_idwould only re-prove a one-line[u8;32]equality against a copy of itself (semantically vacuous per the H-01 finding). The security-relevant assurance — every call site passes the creation-bound expected value — is verified at the instruction level bysecurity_wrong_feed_id.rsand theexpire_marketlate-capture unit tests. The deletedP-LOADER-01harness (Task 17) is the same vacuity class.INV-V2,INV-V4(partially-covered). The per-instruction post-CPI conservation tripwire (V2) and per-mutation supply-reconciliation deltas (V4) are proven, but the composed all-instruction balance-sheet (V2) and the closed cross-position share aggregate (V4) are explicit release-test/proof obligations, not runtime scans — V4's aggregate reconciliation job is still gated on OPS-02.INV-F4,INV-FEE-T3(assumption-dependent). The system-wide referrer-treasury and protocol-shard sums are cross-account totals no single instruction can check. The hourlyreconcileTreasuryShardsjob records drift but only logs it; the paged alert is gated on OPS-02, so the stated aggregate form holds only under that off-chain reconciliation. The on-chain per-shard subset is enforced (INV-REFERRAL-T3,INV-FEE-T4/T5).- Orderbook / DOB-v3 bounds.
INV-O1/O2/O3/O5andINV-X2areboundedly-verified: the invariant checker and placement-gate proofs are exhaustive only within the orderbook array bounds and the crossing traversal's unwind envelope; the full every-occupied-slot scan isvalidate_fullrelease evidence.INV-DOB-V3-{2,3,4,6,7,8,10}have neither a Kani harness nor a targeted runtime test yet.