Skip to content

Wave 6a: phantom-dust 4-field schema (KL-PHANTOM-DUST-SCHEMA-1 REVOKED) - #95

Merged
dcccrypto merged 1 commit into
mainfrom
feat/eng-phantom-dust-schema-wave6a
May 10, 2026
Merged

Wave 6a: phantom-dust 4-field schema (KL-PHANTOM-DUST-SCHEMA-1 REVOKED)#95
dcccrypto merged 1 commit into
mainfrom
feat/eng-phantom-dust-schema-wave6a

Conversation

@dcccrypto

@dcccrypto dcccrypto commented May 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the long-standing phantom-dust schema divergence between fork and toly (Phase 2 KL-PHANTOM-DUST-SCHEMA-1). Adopts toly's 4-field schema (certified + potential per side, toly:783-786). Net RiskEngine growth: +32 bytes (2 × u128 → 4 × u128).

  • phantom_dust_bound_<side>_qphantom_dust_potential_<side>_q (rename only, semantically identical upper bound)
  • New phantom_dust_certified_<side>_q per side — always 0 on this branch (no B-tracking-aware liquidation logic); exists for wire-format alignment with toly + NFT vendored-bytes mirror (Wave 6c)
  • 4 new toly-aligned helpers: get/set_phantom_dust_certified, get/set_phantom_dust_potential
  • 6 callsites updated (fee-sync, liquidation step 10, full-drain reset, §5.7 OI-cap, resolve_market zeroing)
  • New defense-in-depth invariant in assert_public_postconditions: certified <= potential per side
  • 2 new Kani harnesses prove the invariant at genesis + reject violations

Migration

Path B per pre-decision in FULL_SYNC_PLAN.md: force-resolve + rebuild J51cB2 during deployment prep. No on-chain migration instruction. Only 1 mainnet market live + closed beta. Operationally trivial.

Verification

  • cargo build --release clean
  • 335 tests passing (no regressions vs baseline)
  • Engine Kani proof count: 311 → 313 (+2 new harnesses)
  • scripts/verify-engine-preservation.sh silent
  • scripts/verify-engine-deferred-absent.sh silent
  • KEEP_LIST.md updated: KL-PHANTOM-DUST-SCHEMA-1 marked REVOKED

Test plan

  • cargo build --release
  • cargo test --features test --release (335 pass)
  • verify-engine-preservation.sh
  • verify-engine-deferred-absent.sh
  • CI green
  • Admin-merge after CI passes

Follow-ups (Wave 6 sub-PRs)

  • Wave 6a-ii (this PR's wrapper companion): 2-line offset-print rename in percolator-prog/tests/unit.rs — coupled via path = "../percolator", must merge immediately after this PR to keep wrapper test build green
  • Wave 6b: dynamic-fee API rename + per-call threading
  • Wave 6c: percolator-nft/src/slab_types.rs mirror update for 4-field schema
  • Wave 6d: wrapper integration + SLAB_LEN bump

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced phantom dust tracking with a new 4-field schema providing separate certified and potential bounds for each side.
  • Tests

    • Added new validation tests for the phantom dust schema including genesis state and invariant enforcement.
    • Updated existing tests to verify dust tracking and reset behavior with the new field structure.

Review Change Stack

…SCHEMA-1 REVOKED)

Adopts toly's 4-field phantom-dust schema (certified + potential per
side, toly:783-786). Closes the long-standing single-bound vs split
divergence flagged in Phase 2.

Schema:
- `phantom_dust_bound_<side>_q` → `phantom_dust_potential_<side>_q`
  (renamed; semantically identical upper-bound tracking)
- Added `phantom_dust_certified_<side>_q` per side — lower-bound dust
  certified by B-tracking-aware liquidation step 7 (toly:5030-5118).
  Always 0 on this branch: B-tracking subsystem isn't ported, so the
  certified fields exist purely for wire-format alignment with toly +
  NFT vendored-bytes mirroring (Wave 6c). When B-tracking lands, the
  certified field starts receiving non-zero values from the new
  liquidation logic with no schema break.
- Net RiskEngine growth: +32 bytes (2 × u128 → 4 × u128).

Helpers:
- `inc_phantom_dust_bound` → `inc_phantom_dust_potential` (rename only)
- `inc_phantom_dust_bound_by` → `inc_phantom_dust_potential_by`
  (fork-only liquidation step-10 helper, kept; toly inlines the
  equivalent at toly:5099 but uses a different post-state formula)
- New toly-aligned: `get/set_phantom_dust_certified`,
  `get/set_phantom_dust_potential` (toly:3353-3378). Setters are
  grounded in resolve_market zeroing; getters in the new
  `assert_public_postconditions` invariant.

Callsites updated:
- `sync_account_fee_to_slot_not_atomic` precision-boundary increment
- `try_close_position_at_oracle` step-10 dust bump (uses _by helper)
- `begin_full_drain_reset` full-drain side zeroing (now clears both
  certified + potential per side, matching toly's
  `begin_terminal_epoch_exhaustion_reset` semantics)
- `schedule_end_of_instruction_resets` §5.7 A/B/C — OI-cap uses
  `phantom_dust_potential_<side>_q` (renamed bound)
- `resolve_market_not_atomic` clears all 4 fields per side on
  `pre_stored_<side> == 0` (matches toly:9637-9644)

New invariant:
- `assert_public_postconditions` now asserts `certified <= potential`
  per side as a forward-looking defense-in-depth gate. Trivially holds
  on this branch (certified always 0); guards against off-by-one /
  step-ordering bugs once B-tracking lands.

Kani:
- 2 new harnesses in proofs_invariants.rs:
  - `proof_phantom_dust_certified_le_potential_at_genesis`
  - `proof_phantom_dust_certified_gt_potential_rejects`
- Total engine Kani proof count: 311 → 313.

KEEP_LIST:
- KL-PHANTOM-DUST-SCHEMA-1 marked REVOKED.

Verify scripts:
- verify-engine-preservation.sh: §A.1/§A.2 entries removed (fields
  renamed); §F.4 updated to track inc_phantom_dust_potential; §F.5
  updated to track inc_phantom_dust_potential_by (still fork-only).
- verify-engine-deferred-absent.sh: phantom_dust_{certified,potential}
  EXPECT_ABSENT entries removed (fields now legitimately present).

Migration:
- Path B per Wave 6 pre-decision: force-resolve + rebuild J51cB2
  during deployment prep, no on-chain migration instruction in code.
  Only 1 mainnet market live + closed beta. Operationally trivial.

Tests: 335 passing, 0 regressions.
Build: cargo build --release clean.
Verify: both engine verify scripts silent.

Follow-ups (Wave 6 sub-PRs):
- Wave 6b: dynamic-fee API rename + per-call threading
- Wave 6c: percolator-nft slab_types mirror update for 4-field schema
- Wave 6d: wrapper integration + SLAB_LEN bump (includes the 2-line
  wrapper offset-print rename — already coupled to this engine PR via
  path = "../percolator")

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request migrates the RiskEngine phantom-dust schema from a single per-side bound field to a four-field certified/potential pair. The schema change cascades through struct initialization, helper functions, runtime dust accounting, cleanup/reset paths, invariant checks, market resolution, and all supporting test proofs.

Changes

Wave 6a Phantom-Dust 4-Field Schema

Layer / File(s) Summary
Schema Definition
src/percolator.rs
RiskEngine struct replaces phantom_dust_bound_long_q and phantom_dust_bound_short_q with four fields: phantom_dust_certified_long_q, phantom_dust_potential_long_q, phantom_dust_certified_short_q, phantom_dust_potential_short_q.
Constructor & Initialization
src/percolator.rs
Both new_with_market and init_in_place initialize the four phantom-dust fields to zero instead of the removed bound fields.
Helper Functions & Accessors
src/percolator.rs
Dust increment helpers replace inc_phantom_dust_bound* with inc_phantom_dust_potential*; new get/set accessors added for certified and potential fields.
Runtime Dust Accounting
src/percolator.rs
Three paths switch from inc_phantom_dust_bound* to inc_phantom_dust_potential*: effective position remainder tracking, side settlement effects, and ADL enqueuing.
End-of-Instruction Cleanup
src/percolator.rs
Keeper/touched-flow reset path updates bilateral and unilateral dust clearance to use phantom_dust_potential_*_q when deciding OI zeroing.
Full Drain Reset
src/percolator.rs
Full-drain reset now clears both certified and potential fields for the reset side.
Public State Invariants
src/percolator.rs
Postcondition validator adds invariant ensuring phantom_dust_certified_*_q ≤ phantom_dust_potential_*_q for each side.
Market Resolution
src/percolator.rs
Resolution path now clears all four phantom-dust fields when pre_stored_* == 0.
Correctness & Spec Proofs
tests/proofs_instructions.rs
Eleven instruction proofs update dust-tracking assertions across initialization, remainder accounting, dynamic induction, decay, truncation, and clearance scenarios.
Initialization, Drain & Resolution Proofs
tests/proofs_instructions.rs
Six instruction proofs update seed/assertion logic for orphan resolution, corruption guarding, and full-drain reset.
Invariant & Safety Proofs
tests/proofs_invariants.rs, tests/proofs_safety.rs, tests/proofs_liveness.rs
Two new Kani proofs verify genesis compliance and corruption detection; three safety proofs update init canonicalization and dust clearance assertions.
Example Output
examples/detailed_offsets.rs
Offset printing updates to reference new phantom_dust_potential_*_q fields.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • dcccrypto/percolator#88: Both PRs perform the same schema migration—replacing single per-side phantom_dust_bound_* fields with a four-field certified/potential structure in RiskEngine and updating all related logic and tests.
  • dcccrypto/percolator#90: Both PRs modify examples/detailed_offsets.rs and src/percolator.rs phantom-dust field references and offset reporting.
  • dcccrypto/percolator#60: Both PRs affect RiskEngine struct layout and field offset assertions, as the main PR expands phantom-dust fields and example output depends on offset changes.

Poem

🐰 A rabbit hops through fields of dust,
Four columns now—certified we trust.
From bound to potential, the schema grows,
While invariants bloom wherever it goes.
Wave 6a dances, clean and bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: migrating from a single-bound phantom-dust schema to a 4-field schema for Wave 6a, directly matching the primary refactoring across RiskEngine and related test files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/eng-phantom-dust-schema-wave6a

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/percolator.rs (1)

668-688: Coordinate this #[repr(C)] layout bump with the wrapper/schema consumers.

Adding four u128 fields here shifts every trailing RiskEngine offset. Please make sure the matching wrapper/SLAB_LEN/schema-version change ships atomically with this, otherwise older zero-copy readers will deserialize garbage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/percolator.rs` around lines 668 - 688, Adding four u128 fields
(phantom_dust_certified_long_q, phantom_dust_certified_short_q,
phantom_dust_potential_long_q, phantom_dust_potential_short_q) changes the
#[repr(C)] layout and shifts trailing RiskEngine offsets; update all consumers
atomically: bump the schema version constant and SLAB_LEN used by the zero-copy
wrapper, adjust any wrapper/readers that compute field offsets for RiskEngine,
and ensure the wrapper serialization/deserialization schemas and tests are
updated to expect the new field order/size so older readers cannot misparse;
coordinate these changes together in the same commit/release.
examples/detailed_offsets.rs (1)

65-66: ⚡ Quick win

Include certified phantom-dust offsets in this schema dump.

With the 4-field schema, this diagnostic output should also print phantom_dust_certified_* so layout checks cover all migrated fields, not only potential fields.

Suggested diff
+    println!(
+        "phantom_dust_certified_long_q:{}",
+        offset_of!(RiskEngine, phantom_dust_certified_long_q)
+    );
+    println!(
+        "phantom_dust_certified_short_q:{}",
+        offset_of!(RiskEngine, phantom_dust_certified_short_q)
+    );
     println!("phantom_dust_potential_long_q:{}", offset_of!(RiskEngine, phantom_dust_potential_long_q));
     println!("phantom_dust_potential_short_q:{}", offset_of!(RiskEngine, phantom_dust_potential_short_q));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/detailed_offsets.rs` around lines 65 - 66, The schema dump is
missing the certified phantom-dust fields; add prints for the offsets of
RiskEngine::phantom_dust_certified_long_q and
RiskEngine::phantom_dust_certified_short_q using the same offset_of! pattern as
the existing phantom_dust_potential_* lines so the layout check covers all four
migrated fields.
tests/proofs_safety.rs (1)

2364-2365: ⚡ Quick win

Extend canonicalization proof to include certified phantom-dust fields.

Given the 4-field migration, this harness should also dirty and assert-reset phantom_dust_certified_long_q and phantom_dust_certified_short_q; otherwise init_in_place regressions on certified fields won’t be caught.

Suggested diff
+    engine.phantom_dust_certified_long_q = 70;
+    engine.phantom_dust_certified_short_q = 80;
     engine.phantom_dust_potential_long_q = 50;
     engine.phantom_dust_potential_short_q = 60;
@@
+    assert!(engine.phantom_dust_certified_long_q == 0);
+    assert!(engine.phantom_dust_certified_short_q == 0);
     assert!(engine.phantom_dust_potential_long_q == 0);
     assert!(engine.phantom_dust_potential_short_q == 0);

Also applies to: 2408-2409

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/proofs_safety.rs` around lines 2364 - 2365, The test harness currently
dirties and resets only phantom_dust_potential_long_q and
phantom_dust_potential_short_q; extend the canonicalization proof to also touch
and assert-reset the certified fields by setting
engine.phantom_dust_certified_long_q and engine.phantom_dust_certified_short_q
to nonzero values before canonicalization and adding corresponding assertions
after init_in_place to ensure they were reset; update the same pattern at the
other occurrence (the block around the similar assignments at the later lines)
so both locations include phantom_dust_certified_long_q and
phantom_dust_certified_short_q alongside phantom_dust_potential_*.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/percolator.rs`:
- Around line 4439-4451: The current invariant only checks certified <=
potential; strengthen it to fail-fast if any phantom_dust_certified value is
non-zero on this branch: after or instead of the existing comparison, call
get_phantom_dust_certified(Side::Long) and
get_phantom_dust_certified(Side::Short) and return Err(RiskError::CorruptState)
if either is != 0 (enforcing the documented phantom_dust_certified_*_q == 0
contract), so migrations/wrapper-layout bugs cannot seed a surviving non-zero
certified value that schedule_end_of_instruction_resets() or the OI-cap logic
would ignore. Ensure the check references get_phantom_dust_certified and the
same RiskError used elsewhere.

---

Nitpick comments:
In `@examples/detailed_offsets.rs`:
- Around line 65-66: The schema dump is missing the certified phantom-dust
fields; add prints for the offsets of RiskEngine::phantom_dust_certified_long_q
and RiskEngine::phantom_dust_certified_short_q using the same offset_of! pattern
as the existing phantom_dust_potential_* lines so the layout check covers all
four migrated fields.

In `@src/percolator.rs`:
- Around line 668-688: Adding four u128 fields (phantom_dust_certified_long_q,
phantom_dust_certified_short_q, phantom_dust_potential_long_q,
phantom_dust_potential_short_q) changes the #[repr(C)] layout and shifts
trailing RiskEngine offsets; update all consumers atomically: bump the schema
version constant and SLAB_LEN used by the zero-copy wrapper, adjust any
wrapper/readers that compute field offsets for RiskEngine, and ensure the
wrapper serialization/deserialization schemas and tests are updated to expect
the new field order/size so older readers cannot misparse; coordinate these
changes together in the same commit/release.

In `@tests/proofs_safety.rs`:
- Around line 2364-2365: The test harness currently dirties and resets only
phantom_dust_potential_long_q and phantom_dust_potential_short_q; extend the
canonicalization proof to also touch and assert-reset the certified fields by
setting engine.phantom_dust_certified_long_q and
engine.phantom_dust_certified_short_q to nonzero values before canonicalization
and adding corresponding assertions after init_in_place to ensure they were
reset; update the same pattern at the other occurrence (the block around the
similar assignments at the later lines) so both locations include
phantom_dust_certified_long_q and phantom_dust_certified_short_q alongside
phantom_dust_potential_*.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13658c5c-0b00-4dd2-8d0e-e00582e0dc78

📥 Commits

Reviewing files that changed from the base of the PR and between a67ff66 and 6149b03.

📒 Files selected for processing (6)
  • examples/detailed_offsets.rs
  • src/percolator.rs
  • tests/proofs_instructions.rs
  • tests/proofs_invariants.rs
  • tests/proofs_liveness.rs
  • tests/proofs_safety.rs

Comment thread src/percolator.rs
Comment on lines +4439 to +4451
// Wave 6a / KL-PHANTOM-DUST-SCHEMA-1 (REVOKED): certified is a lower
// bound, potential is the upper bound; certified must never exceed
// potential on either side. On this branch certified is always 0
// (see field doc), so the invariant is `0 <= potential` — trivially
// true — but the explicit check grounds the get helpers and matches
// toly's defense-in-depth posture once B-tracking lands.
if self.get_phantom_dust_certified(Side::Long)
> self.get_phantom_dust_potential(Side::Long)
|| self.get_phantom_dust_certified(Side::Short)
> self.get_phantom_dust_potential(Side::Short)
{
return Err(RiskError::CorruptState);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce the branch’s certified == 0 contract here.

This check only guarantees certified <= potential, but the surrounding docs say phantom_dust_certified_*_q is always zero until B-tracking is ported. If a migration or wrapper-layout mistake seeds a non-zero certified value, schedule_end_of_instruction_resets() and the OI-cap logic ignore it, so the bad state can survive as a “valid” market. Fail fast on any non-zero certified dust on this branch, or gate the relaxed invariant behind the future B-tracking port.

Suggested tightening for the current branch
-        if self.get_phantom_dust_certified(Side::Long)
-            > self.get_phantom_dust_potential(Side::Long)
-            || self.get_phantom_dust_certified(Side::Short)
-                > self.get_phantom_dust_potential(Side::Short)
+        if self.get_phantom_dust_certified(Side::Long) != 0
+            || self.get_phantom_dust_certified(Side::Short) != 0
+            || self.get_phantom_dust_certified(Side::Long)
+                > self.get_phantom_dust_potential(Side::Long)
+            || self.get_phantom_dust_certified(Side::Short)
+                > self.get_phantom_dust_potential(Side::Short)
         {
             return Err(RiskError::CorruptState);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Wave 6a / KL-PHANTOM-DUST-SCHEMA-1 (REVOKED): certified is a lower
// bound, potential is the upper bound; certified must never exceed
// potential on either side. On this branch certified is always 0
// (see field doc), so the invariant is `0 <= potential` — trivially
// true — but the explicit check grounds the get helpers and matches
// toly's defense-in-depth posture once B-tracking lands.
if self.get_phantom_dust_certified(Side::Long)
> self.get_phantom_dust_potential(Side::Long)
|| self.get_phantom_dust_certified(Side::Short)
> self.get_phantom_dust_potential(Side::Short)
{
return Err(RiskError::CorruptState);
}
// Wave 6a / KL-PHANTOM-DUST-SCHEMA-1 (REVOKED): certified is a lower
// bound, potential is the upper bound; certified must never exceed
// potential on either side. On this branch certified is always 0
// (see field doc), so the invariant is `0 <= potential` — trivially
// true — but the explicit check grounds the get helpers and matches
// toly's defense-in-depth posture once B-tracking lands.
if self.get_phantom_dust_certified(Side::Long) != 0
|| self.get_phantom_dust_certified(Side::Short) != 0
|| self.get_phantom_dust_certified(Side::Long)
> self.get_phantom_dust_potential(Side::Long)
|| self.get_phantom_dust_certified(Side::Short)
> self.get_phantom_dust_potential(Side::Short)
{
return Err(RiskError::CorruptState);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/percolator.rs` around lines 4439 - 4451, The current invariant only
checks certified <= potential; strengthen it to fail-fast if any
phantom_dust_certified value is non-zero on this branch: after or instead of the
existing comparison, call get_phantom_dust_certified(Side::Long) and
get_phantom_dust_certified(Side::Short) and return Err(RiskError::CorruptState)
if either is != 0 (enforcing the documented phantom_dust_certified_*_q == 0
contract), so migrations/wrapper-layout bugs cannot seed a surviving non-zero
certified value that schedule_end_of_instruction_resets() or the OI-cap logic
would ignore. Ensure the check references get_phantom_dust_certified and the
same RiskError used elsewhere.

@dcccrypto
dcccrypto merged commit 8a8776e into main May 10, 2026
1 check passed
@dcccrypto
dcccrypto deleted the feat/eng-phantom-dust-schema-wave6a branch May 10, 2026 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant