mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
Both sides carried work. Ours is the security pass: the stableswap amp=0 and empty-pool halts, the wei-per-swap leak, uint256 truncation in math, three verifiers that returned a verdict without verifying, the cgo/non-cgo consensus split in cggmp21, and the starkfri soundness floor. Theirs is the fhe parameter set, the mldsa crafted-length refusal, and the naming sweep. Resolved per hunk rather than by side. Where both fixed one defect the better fix survives alone, never two guards for one bug.
119 lines
5.3 KiB
Go
119 lines
5.3 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package dex
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
|
|
"github.com/holiman/uint256"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/ids"
|
|
)
|
|
|
|
// native_seed_test.go is the FIX-4 suite: the swap rail's first matched settlement of
|
|
// an output asset is backed by a PRODUCTION operator seed (seedSeamReserve), not a
|
|
// test-only state poke. It proves:
|
|
// - an UN-seeded first fill reverts ErrCustodyUnbacked (no mint), and
|
|
// - after the operator seeds seamReserve through the real gated selector, the first
|
|
// matched swap settles, and
|
|
// - the seed is operator-gated (a non-operator caller is refused) and value-backed
|
|
// (a native seed with no delivered msg.value reverts), and
|
|
|
|
// TestFIX4_FirstFillRevertsWithoutSeed — before any opposing-direction order lock or
|
|
// operator seed, seamReserve[assetOut] is empty, so the first matched swap settlement
|
|
// (consuming a real D->C object) reverts ErrCustodyUnbacked. No mint, no
|
|
// raid of another pot — the credit needs the seam's OWN backing.
|
|
func TestFIX4_FirstFillRevertsWithoutSeed(t *testing.T) {
|
|
h := newSettleHarness(t)
|
|
h.registerMarket(t)
|
|
native := h.inAssetID()
|
|
db := newPoolStateAdapter(h.state)
|
|
|
|
if loadCustody(db, native).Sign() != 0 {
|
|
t.Fatal("seam reserve must start empty (no seed, no opposing lock)")
|
|
}
|
|
// A real D->C object exists, but custody[native] is empty.
|
|
obj := ids.ID{0xF1, 0x00, 0x01}
|
|
h.putDtoCObject(t, h.caller, obj, native, 250)
|
|
if _, err := h.c.atomicImport(h.state, Claim{
|
|
ID: obj, Asset: native, Beneficiary: h.caller,
|
|
Object: encodeClaim(h.caller, native, 250),
|
|
}); err != ErrCustodyUnbacked {
|
|
t.Fatalf("an unseeded first fill MUST revert ErrCustodyUnbacked, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestFIX4_OperatorSeedBacksFirstFill — the operator seeds seamReserve through the
|
|
// REAL seedSeamReserve selector (the production path), and the first matched swap then
|
|
// settles. The seed moves real value into the vault and is reflected in seamReserve;
|
|
// the vault-account invariant holds throughout.
|
|
func TestFIX4_OperatorSeedBacksFirstFill(t *testing.T) {
|
|
h := newSettleHarness(t)
|
|
h.registerMarket(t)
|
|
native := h.inAssetID()
|
|
db := newPoolStateAdapter(h.state)
|
|
|
|
// Operator seeds 1000 native counterparty backing via the real selector.
|
|
h.fundVaultNativeOut(1000)
|
|
if loadCustody(db, native).Int64() != 1000 {
|
|
t.Fatalf("seedSeamReserve must set custody[native]=1000, got %s", loadCustody(db, native))
|
|
}
|
|
h.vaultInvariantNative(t, "after operator seed")
|
|
|
|
// The FIRST matched swap now settles from the seeded reserve.
|
|
obj := ids.ID{0xF2, 0x00, 0x01}
|
|
h.putDtoCObject(t, h.caller, obj, native, 250)
|
|
before := h.state.stateDB.GetBalance(h.caller).ToBig()
|
|
credited, err := h.c.atomicImport(h.state, Claim{
|
|
ID: obj, Asset: native, Beneficiary: h.caller,
|
|
Object: encodeClaim(h.caller, native, 250),
|
|
})
|
|
if err != nil || credited != 250 {
|
|
t.Fatalf("a seam-seeded first fill must settle 250: credited=%d err=%v", credited, err)
|
|
}
|
|
if new(big.Int).Sub(h.state.stateDB.GetBalance(h.caller).ToBig(), before).Int64() != 250 {
|
|
t.Fatal("the first matched swap must credit the taker 250 from the seeded reserve")
|
|
}
|
|
if loadCustody(db, native).Int64() != 750 {
|
|
t.Fatalf("seam reserve must fall to 750 after the 250 settlement, got %s", loadCustody(db, native))
|
|
}
|
|
h.vaultInvariantNative(t, "after first fill")
|
|
}
|
|
|
|
// TestFIX4_SeedIsOperatorGatedAndValueBacked — the seed selector is gated to the
|
|
// protocolFeeController and refuses a bookkeeping (value-less) native seed.
|
|
func TestFIX4_SeedIsOperatorGatedAndValueBacked(t *testing.T) {
|
|
h := newSettleHarness(t)
|
|
h.registerMarket(t)
|
|
|
|
// (a) a NON-operator caller is refused (ErrUnauthorized) — only the operator seeds.
|
|
notOp := common.HexToAddress("0xBADBAD0000000000000000000000000000000001")
|
|
data := make([]byte, 64)
|
|
big.NewInt(500).FillBytes(data[32:64]) // native asset (word0 zero), amount 500
|
|
// The non-operator "delivers" value too (so the only failure is the auth gate).
|
|
h.state.stateDB.AddBalance(poolManagerAddr9999, uint256.NewInt(500))
|
|
if _, _, err := h.c.Run(h.state, notOp, poolManagerAddr9999,
|
|
prependSelector(SelectorSeedSeamReserve, data), 5_000_000, false); err != ErrUnauthorized {
|
|
t.Fatalf("a non-operator seed must revert ErrUnauthorized, got: %v", err)
|
|
}
|
|
|
|
// (b) the operator seeding native with NO delivered value reverts (no mint): the
|
|
// observed-delta is 0 against a value==0 call. (We seeded the vault in (a) for the
|
|
// auth test, so first drain it back to a known baseline by recording it as a real
|
|
// seed, then attempt a value-less seed.)
|
|
// Reset: treat the 500 already in the vault as a legitimate prior seed.
|
|
okData := make([]byte, 64)
|
|
big.NewInt(500).FillBytes(okData[32:64])
|
|
if _, _, err := h.c.Run(h.state, h.operator(), poolManagerAddr9999,
|
|
prependSelector(SelectorSeedSeamReserve, okData), 5_000_000, false); err != nil {
|
|
t.Fatalf("operator seed of the already-delivered 500 must succeed, got: %v", err)
|
|
}
|
|
// Now a SECOND native seed with NO new delivered value must revert (delivered=0).
|
|
if _, _, err := h.c.Run(h.state, h.operator(), poolManagerAddr9999,
|
|
prependSelector(SelectorSeedSeamReserve, okData), 5_000_000, false); err != ErrSeedUndelivered {
|
|
t.Fatalf("a value-less native seed must revert ErrSeedUndelivered (no mint), got: %v", err)
|
|
}
|
|
}
|