mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
Fixes the fail-safe liveness edge the Byzantine suite found: a self-cross-heavy stream could wedge one later submit at BuildBlock with "resting order missing full settlement identity (orderuser)" / "insufficient locked balance" (seed 42, ~step 153; same class as seed-4242). Root cause: pkg/lx/orderbook.go tryMatchImmediateLocked's self-trade skip removed the self-crossed maker from the IN-MEMORY book (removeOrder + delete Orders + ordersMap.Delete) without a matching consensus state write. The in-memory book feeds the execution root via BookToRows(ob), but the durable order:/orderuser:/ reserve rows were left untouched — so the two diverged, and a later cross tripped the settle-time invariants. Fix (one and one way — route through the existing cancel-persist path): - pkg/lx/orderbook.go: the matcher RECORDS each self-cancelled maker id (ob.selfCanceled, reset per match) and exposes DrainSelfCanceled(). No fill is produced; self-trade prevention is unchanged. - pkg/dchain/execute.go: applySubmit drains the self-cancelled ids into applyResult.SelfCanceled. - pkg/dchain/block.go: extract releaseOrderReserve() — the single canonical cancel-persist primitive (release reserve locked->available, delete reserve + orderuser: rows, fail-closed on missing identity). TxCancel now calls it; a submit's self-cancelled makers call it too, and execute deletes their order: rows in lockstep. In-memory and durable state now move together within the block. Properties preserved: - Determinism: the self-cross block's execRoot is UNCHANGED (BookToRows already excluded the maker); every validator drops the same maker + releases the same reserve. No fork. - Conservation-exact: the released reserve moves locked->available; sum unchanged, no mint/burn. - Self-trade prevention: self-crosses still never trade (zero fills). - Anti-theft: owner resolved exclusively via the orderuser: index, fail-closed. TDD (CGO_ENABLED=0 GOWORK=off GOFLAGS=-mod=mod): - TestByzantineConservation_AdversarialMatcherStream: 1280 ops, 10 -> 0 fail-safe refusals (now asserts zero as a regression guard); I=A+L + no-fork every op. - TestByzantineMatcher_SelfCrossCancelsRestingMakerConsistently: new pin — the partial-fill -> self-cross -> external-cross wedge shape now cancels the maker (8 locked base -> available), later crosses run clean, collateral withdrawable. - Full pkg/lx, pkg/dchain, pkg/consensus suites green; chains/dexvm unaffected. Co-authored-by: Hanzo Dev <[email protected]>