Files
precompile/dex/zzblueF_admit_test.go
zeekay 1362566d34 Merge github/main into the hardening branch
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.
2026-08-28 20:13:25 -07:00

1834 lines
75 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package dex
import (
"errors"
"math/big"
"testing"
"github.com/holiman/uint256"
"github.com/luxfi/crypto"
dexcore "github.com/luxfi/dex/pkg/dex"
"github.com/luxfi/geth/common"
ethtypes "github.com/luxfi/geth/core/types"
"github.com/luxfi/ids"
"github.com/luxfi/precompile/contract"
"github.com/luxfi/vm/chains/atomic"
)
// zzblueF_admit_test.go pins the ADMISSION and ATTESTATION gates of 0x9999: the
// live-code asset verifier (asset_onchain_verifier.go), the process-global resolver
// latch (asset_resolver.go), the broker fill attestation lifecycle
// (fill_attestation.go), the cross-domain staging window (native_staging.go), and
// the arithmetic domain boundaries the swap path sits on.
//
// Every gate here is pinned by what it REFUSES. An admission check that is only
// exercised on its happy path proves nothing: the property is "a fabricated asset
// cannot trade", "a resolver bound to another chain cannot be installed", "a staged
// object is consumed exactly once", and each of those is a statement about a
// refusal.
// ---------------------------------------------------------------------------
// Shared fixtures
// ---------------------------------------------------------------------------
// blueFPoisoned returns b backed by an array whose spare capacity is filled with
// 0xA5, modelling the attacker-controlled EVM memory that sits past a precompile
// input in production (geth hands Run the two-index slice m.store[off:off+size], so
// cap(input) is the rest of memory). A read past len does not panic there — it
// silently returns bytes the caller chose. A handler that bounds on len yields the
// same verdict for a poisoned input as for a clean one.
func blueFPoisoned(b []byte, spare int) []byte {
full := make([]byte, len(b)+spare)
for i := range full {
full[i] = 0xA5
}
copy(full, b)
return full[:len(b)]
}
// blueFPlainState is a dex.StateDB with NO code-size capability: it embeds the
// StateDB INTERFACE, so only StateDB's own methods are promoted and CodeSizeOf is
// not among them. It models a host that cannot answer EXTCODESIZE, which must make
// the value path fail closed rather than admit an unproven asset.
type blueFPlainState struct{ StateDB }
// blueFNoCodeStateDB is a contract.StateDB with NO GetCodeSize: same embedding
// trick, one level down. A poolStateAdapter over it reports CodeSizeOf == -1, which
// codeStaterFor must read as "not code-capable" (fail closed), never as "every
// address has zero code" (which would refuse even the native coin).
type blueFNoCodeStateDB struct{ contract.StateDB }
// blueFPlainEnv is a precompile environment with no Call surface — the non-DEX
// precompile case the ERC-20 seam must refuse rather than sub-call.
type blueFPlainEnv struct{}
func (blueFPlainEnv) ReadOnly() bool { return false }
// blueFCallEnv is a scriptable Call surface: it records the last sub-call and
// returns the programmed (ret, err), so the ERC-20 legs' fail-secure arms can be
// driven directly.
type blueFCallEnv struct {
ret []byte
err error
gasLeft uint64
lastAddr common.Address
lastIn []byte
calls int
}
func (e *blueFCallEnv) ReadOnly() bool { return false }
func (e *blueFCallEnv) Call(addr common.Address, input []byte, gas uint64, value *big.Int) ([]byte, uint64, error) {
e.calls++
e.lastAddr = addr
e.lastIn = append([]byte(nil), input...)
return e.ret, e.gasLeft, e.err
}
// blueFEnvState is a contract.AccessibleState that hands back a chosen precompile
// environment, so the adapter's callable() resolution can be driven across all
// three of its arms (no accessible state / nil env / non-callable env).
type blueFEnvState struct {
contract.AccessibleState
env contract.PrecompileEnvironment
}
func (s *blueFEnvState) GetPrecompileEnv() contract.PrecompileEnvironment { return s.env }
// blueFIsolateResolver takes the process-global asset-resolver latch private to the
// calling test: it saves the prior value, restores it on cleanup, and clears the
// latch FIRST so this test's install cannot be refused by whatever identity a
// previous test left installed. Every test in this file that touches
// installedAssetResolver calls this before it mutates.
func blueFIsolateResolver(t *testing.T) {
t.Helper()
prev := installedAssetResolver.Load()
t.Cleanup(func() { installedAssetResolver.Store(prev) })
installedAssetResolver.Store(nil)
}
// blueFTestResolver is a minimal permissionless dexcore.AssetResolver: it resolves
// any well-formed reference and never carries a membership set, so admission is
// decided by the on-chain code proof alone.
type blueFTestResolver struct {
networkID uint32
chainID ids.ID
}
func (r *blueFTestResolver) ResolveAsset(kind dexcore.AssetKind, ref []byte) (ids.ID, uint8, error) {
id, err := dexcore.DeriveAssetID(r.networkID, r.chainID, kind, ref)
if err != nil {
return ids.Empty, 0, err
}
if kind == dexcore.AssetKindEVMNative {
return id, 18, nil
}
return id, 0, nil
}
// blueFCodeState is a code-size oracle whose answers can change between calls, so a
// verifier that caches a reality proof instead of reading live state is caught.
type blueFCodeState struct {
StateDB
sizes map[common.Address]int
}
func (s *blueFCodeState) CodeSizeOf(addr common.Address) int { return s.sizes[addr] }
func blueFNewCodeState() *blueFCodeState {
return &blueFCodeState{StateDB: NewMockStateDB(), sizes: map[common.Address]int{}}
}
// ---------------------------------------------------------------------------
// asset_onchain_verifier.go — the live-code reality gate
// ---------------------------------------------------------------------------
// TestBlueF_VerifierRefusesAssetWithNoCode is the core admission property: an
// ERC-20 trades only if its 20-byte address carries contract bytecode in the state
// THIS block is mutating. Every address below is a shape an attacker can name — a
// never-deployed address, an ASCII ticker left-padded into an address, the
// precompile's own address, a self-destructed token — and each must be refused
// while it has no code.
func TestBlueF_VerifierRefusesAssetWithNoCode(t *testing.T) {
st := blueFNewCodeState()
v := onChainVerifierFor(st)
if v == nil {
t.Fatal("a code-capable StateDB must yield a verifier")
}
var ticker common.Address
copy(ticker[16:], []byte("LUSD")) // an ASCII ticker masquerading as a token address
for _, tc := range []struct {
name string
addr common.Address
}{
{"never deployed", common.HexToAddress("0xdead00000000000000000000000000000000beef")},
{"ascii ticker as address", ticker},
{"the precompile's own address", poolManagerAddr9999},
{"the settlement config address", settlementAddr},
{"one bit off a real token", common.HexToAddress("0x0000000000000000000000000000000000000003")},
} {
err := v.VerifyOnChainAsset(dexcore.AssetKindERC20, tc.addr.Bytes())
if !errors.Is(err, ErrERC20NoOnChainCode) {
t.Fatalf("%s: a code-less address must be refused with ErrERC20NoOnChainCode, got %v", tc.name, err)
}
// And the refusal must survive the value path's join, which is what the
// swap handler actually matches on.
if joined := dexcore.RequireRealOnChain(v, dexcore.AssetKindERC20, tc.addr.Bytes()); !errors.Is(joined, dexcore.ErrAssetNotOnChain) {
t.Fatalf("%s: RequireRealOnChain must join ErrAssetNotOnChain, got %v", tc.name, joined)
}
}
}
// TestBlueF_VerifierReadsLiveCodeNotACachedProof pins that the verdict is taken from
// state at CALL time. The window this gate exists to close is exactly a code size
// that changes between registration and the swap: a token that gains code must
// become admissible, and a token that SELFDESTRUCTs must stop being admissible with
// no other state change.
func TestBlueF_VerifierReadsLiveCodeNotACachedProof(t *testing.T) {
st := blueFNewCodeState()
v := onChainVerifierFor(st)
tok := common.HexToAddress("0x00000000000000000000000000000000000000A1")
if err := v.VerifyOnChainAsset(dexcore.AssetKindERC20, tok.Bytes()); !errors.Is(err, ErrERC20NoOnChainCode) {
t.Fatalf("before deployment the token must be refused, got %v", err)
}
st.sizes[tok] = 42 // the contract is deployed
if err := v.VerifyOnChainAsset(dexcore.AssetKindERC20, tok.Bytes()); err != nil {
t.Fatalf("after deployment the token must be admitted, got %v", err)
}
st.sizes[tok] = 0 // SELFDESTRUCT
if err := v.VerifyOnChainAsset(dexcore.AssetKindERC20, tok.Bytes()); !errors.Is(err, ErrERC20NoOnChainCode) {
t.Fatalf("after SELFDESTRUCT the token must be refused again, got %v", err)
}
// A code size cannot be negative in the EVM, but a host that reports one must
// not be read as "has code": the check is > 0, not != 0.
st.sizes[tok] = -1
if err := v.VerifyOnChainAsset(dexcore.AssetKindERC20, tok.Bytes()); !errors.Is(err, ErrERC20NoOnChainCode) {
t.Fatalf("a negative code size must refuse, got %v", err)
}
}
// TestBlueF_VerifierRefusesIllFormedRef sweeps the reference SHAPE for all three
// kinds. An ill-formed reference must be refused BEFORE the code read: the verifier
// must never read code at an address it synthesised from the wrong number of bytes.
func TestBlueF_VerifierRefusesIllFormedRef(t *testing.T) {
st := blueFNewCodeState()
// Seed code at the zero address and at the address a short ref would left-pad
// to, so a length-sloppy verifier would ADMIT and this test would catch it.
st.sizes[common.Address{}] = 99
st.sizes[common.BytesToAddress([]byte{0x01, 0x02, 0x03})] = 99
v := onChainVerifierFor(st)
twenty := common.HexToAddress("0x00000000000000000000000000000000000000A2").Bytes()
thirtyTwo := make([]byte, 32)
thirtyTwo[31] = 7
for _, tc := range []struct {
name string
kind dexcore.AssetKind
ref []byte
}{
{"ERC20 ref too short", dexcore.AssetKindERC20, twenty[:19]},
{"ERC20 ref too long", dexcore.AssetKindERC20, append(append([]byte(nil), twenty...), 0x00)},
{"ERC20 ref empty", dexcore.AssetKindERC20, nil},
{"ERC20 ref is the zero address", dexcore.AssetKindERC20, make([]byte, 20)},
{"ERC20 ref is a 32-byte assetID", dexcore.AssetKindERC20, thirtyTwo},
{"native ref not the zero marker", dexcore.AssetKindEVMNative, twenty},
{"native ref wrong width", dexcore.AssetKindEVMNative, make([]byte, 32)},
{"UTXO ref wrong width", dexcore.AssetKindUTXO, twenty},
{"UTXO ref all zero", dexcore.AssetKindUTXO, make([]byte, 32)},
} {
if err := v.VerifyOnChainAsset(tc.kind, tc.ref); err == nil {
t.Fatalf("%s: an ill-formed reference must be refused, got nil", tc.name)
}
// A poisoned backing array must not change the verdict: the shape check
// reads len, never the spare capacity an attacker controls.
if err := v.VerifyOnChainAsset(tc.kind, blueFPoisoned(tc.ref, 64)); err == nil {
t.Fatalf("%s: poisoned spare capacity must not admit an ill-formed reference", tc.name)
}
}
}
// TestBlueF_VerifierAdmitsTheTwoNonEVMKinds pins the two kinds whose reality is NOT
// an EVM-state fact: the chain's own coin (not a contract, so no code to read) and
// an X-Chain UTXO asset (proven at the registry against the X-Chain). Both must be
// admitted on a well-formed reference and must NOT consult code size — an oracle
// that answers "no code" for every address must still admit them.
func TestBlueF_VerifierAdmitsTheTwoNonEVMKinds(t *testing.T) {
st := blueFNewCodeState() // every address reports zero code
v := onChainVerifierFor(st)
if err := v.VerifyOnChainAsset(dexcore.AssetKindEVMNative, dexcore.EVMNativeMarker); err != nil {
t.Fatalf("the native coin must always be real, got %v", err)
}
utxo := make([]byte, 32)
utxo[0] = 0xAB
if err := v.VerifyOnChainAsset(dexcore.AssetKindUTXO, utxo); err != nil {
t.Fatalf("a well-formed UTXO assetID must be admitted (X-Chain proof, not EVM state), got %v", err)
}
if err := dexcore.RequireRealOnChain(v, dexcore.AssetKindUTXO, utxo); err != nil {
t.Fatalf("RequireRealOnChain must admit a well-formed UTXO ref, got %v", err)
}
}
// TestBlueF_VerifierRefusesUnknownKind pins that a kind outside the closed set of
// three is refused with ErrInvalidKind for every reference shape. The refusal comes
// from the canonical-reference check, which runs first — so the switch's own
// default arm is unreachable (recorded in the report, not patched here).
func TestBlueF_VerifierRefusesUnknownKind(t *testing.T) {
st := blueFNewCodeState()
st.sizes[common.HexToAddress("0x00000000000000000000000000000000000000A3")] = 1
v := onChainVerifierFor(st)
for _, kind := range []dexcore.AssetKind{0, 4, 5, 200, 255} {
for _, ref := range [][]byte{
nil,
make([]byte, 20),
make([]byte, 32),
common.HexToAddress("0x00000000000000000000000000000000000000A3").Bytes(),
} {
err := v.VerifyOnChainAsset(kind, ref)
if !errors.Is(err, dexcore.ErrInvalidKind) {
t.Fatalf("kind %d ref %d bytes: must be refused with ErrInvalidKind, got %v", kind, len(ref), err)
}
}
if kind.Valid() {
t.Fatalf("kind %d must not report itself valid", kind)
}
}
}
// TestBlueF_CodeCapabilityResolution pins which StateDB shapes carry the EXTCODESIZE
// capability. The decisive property is fail-closed: a host that cannot answer must
// yield NO verifier (so RequireRealOnChain refuses with ErrNoOnChainVerifier), never
// a verifier that answers "no code" for everything — which would refuse the native
// coin and, worse, read as a real proof.
func TestBlueF_CodeCapabilityResolution(t *testing.T) {
mock := NewMockStateDB()
// 1. A StateDB that implements CodeSizeOf directly is code-capable.
if cs, ok := codeStaterFor(mock); !ok || cs == nil {
t.Fatal("a StateDB implementing CodeSizeOf must be code-capable")
}
if onChainVerifierFor(mock) == nil {
t.Fatal("a code-capable StateDB must yield a verifier")
}
// 2. A StateDB with no code capability is NOT capable, yields no verifier, and
// the value path fails closed.
plain := blueFPlainState{StateDB: mock}
if cs, ok := codeStaterFor(plain); ok || cs != nil {
t.Fatal("a StateDB without CodeSizeOf must not be reported code-capable")
}
if v := onChainVerifierFor(plain); v != nil {
t.Fatal("a code-incapable StateDB must yield NO verifier (fail closed)")
}
if err := dexcore.RequireRealOnChain(onChainVerifierFor(plain), dexcore.AssetKindEVMNative, dexcore.EVMNativeMarker); !errors.Is(err, dexcore.ErrNoOnChainVerifier) {
t.Fatalf("no verifier must fail closed with ErrNoOnChainVerifier, got %v", err)
}
// 3. The production adapter over a StateDB that CAN report code size is capable.
h := newSettleHarness(t)
adapter := newPoolStateAdapter(h.state)
if cs, ok := codeStaterFor(adapter); !ok || cs == nil {
t.Fatal("the adapter over a GetCodeSize-capable StateDB must be code-capable")
}
// 4. The adapter over a StateDB that CANNOT report code size reports -1, which
// must read as not-capable — not as "zero code everywhere".
blind := &poolStateAdapter{stateDB: blueFNoCodeStateDB{StateDB: h.state.GetStateDB()}}
if got := blind.CodeSizeOf(common.Address{}); got != -1 {
t.Fatalf("an adapter over a code-blind StateDB must report -1, got %d", got)
}
if cs, ok := codeStaterFor(blind); ok || cs != nil {
t.Fatal("a code-blind adapter must not be reported code-capable")
}
if v := onChainVerifierFor(blind); v != nil {
t.Fatal("a code-blind adapter must yield NO verifier (fail closed)")
}
}
// TestBlueF_OnChainAssetErrIsMatchable pins the refusal cause as a distinct, stable
// value: the swap path matches it with errors.Is under the joined
// dexcore.ErrAssetNotOnChain, and its message must name the reason.
func TestBlueF_OnChainAssetErrIsMatchable(t *testing.T) {
msg := ErrERC20NoOnChainCode.Error()
if msg == "" {
t.Fatal("the refusal cause must carry a message")
}
if !errors.Is(ErrERC20NoOnChainCode, ErrERC20NoOnChainCode) {
t.Fatal("the refusal cause must match itself")
}
other := errInvalidOnChainAsset("dex: a different cause")
if errors.Is(other, ErrERC20NoOnChainCode) {
t.Fatal("two distinct causes must not match each other")
}
if other.Error() != "dex: a different cause" {
t.Fatalf("the cause must render its own message, got %q", other.Error())
}
}
// ---------------------------------------------------------------------------
// asset_resolver.go — the process-global install latch
// ---------------------------------------------------------------------------
// TestBlueF_InstallAssetResolverRefusals pins the boot-time latch. It is a
// process-global with no uninstall, so a wrong install is permanent for the life of
// the node: the refusals are the whole safety story. A resolver bound to a
// DIFFERENT chain must never replace one already installed, while re-installing the
// SAME identity stays idempotent so a benign re-init does not fail boot.
func TestBlueF_InstallAssetResolverRefusals(t *testing.T) {
blueFIsolateResolver(t)
chainA := ids.ID{0xC0}
chainB := ids.ID{0xC1}
rA := &blueFTestResolver{networkID: 7, chainID: chainA}
rB := &blueFTestResolver{networkID: 7, chainID: chainB}
if AssetResolverInstalled() {
t.Fatal("the latch must start clear inside an isolated test")
}
// A nil resolver is refused and installs nothing.
if err := InstallAssetResolver(nil, 7, chainA); err == nil {
t.Fatal("a nil resolver must be refused")
}
if AssetResolverInstalled() {
t.Fatal("a refused install must leave the latch clear")
}
// An empty C-Chain id is refused: the id enters every derived AssetID, so an
// empty one would make every asset identity collide.
if err := InstallAssetResolver(rA, 7, ids.Empty); err == nil {
t.Fatal("an empty C-Chain id must be refused")
}
if AssetResolverInstalled() {
t.Fatal("a refused install must leave the latch clear")
}
// The first well-formed install takes.
if err := InstallAssetResolver(rA, 7, chainA); err != nil {
t.Fatalf("the first install must be accepted: %v", err)
}
if !AssetResolverInstalled() {
t.Fatal("the latch must report installed after a successful install")
}
// Re-installing the SAME identity is idempotent (a benign re-init).
if err := InstallAssetResolver(rB, 7, chainA); err != nil {
t.Fatalf("a same-identity re-install must be allowed: %v", err)
}
// A DIFFERENT identity is refused on either coordinate, and the refusal must
// not swap the installed resolver.
for _, tc := range []struct {
name string
networkID uint32
chainID ids.ID
}{
{"different C-Chain id", 7, chainB},
{"different network id", 8, chainA},
{"both different", 8, chainB},
} {
if err := InstallAssetResolver(rA, tc.networkID, tc.chainID); err == nil {
t.Fatalf("%s: a re-install bound to another chain must be refused", tc.name)
}
got, err := resolverForRuntime(7, chainA)
if err != nil || got == nil {
t.Fatalf("%s: the original install must survive a refused re-install (%v)", tc.name, err)
}
}
}
// TestBlueF_ResolverForRuntimeIdentityBinding pins the swap-time half of the same
// property: the installed resolver is usable ONLY on the chain it was built for. A
// mismatch is a hard refusal, not a fallback — a resolver bound to another network
// derives different AssetIDs and would admit assets that do not exist here.
func TestBlueF_ResolverForRuntimeIdentityBinding(t *testing.T) {
blueFIsolateResolver(t)
chainA := ids.ID{0xA0}
chainB := ids.ID{0xB0}
// No resolver installed: nil resolver, nil error — the caller maps that to the
// fail-closed ErrNoAssetResolver.
got, err := resolverForRuntime(1, chainA)
if got != nil || err != nil {
t.Fatalf("an empty latch must yield (nil, nil), got (%v, %v)", got, err)
}
if e := func() error {
_, _, e := dexcore.RequireResolvedAsset(nil, dexcore.AssetKindEVMNative, dexcore.EVMNativeMarker)
return e
}(); !errors.Is(e, dexcore.ErrNoAssetResolver) {
t.Fatalf("a nil resolver must fail closed with ErrNoAssetResolver, got %v", e)
}
if err := InstallAssetResolver(&blueFTestResolver{networkID: 1, chainID: chainA}, 1, chainA); err != nil {
t.Fatalf("install: %v", err)
}
for _, tc := range []struct {
name string
networkID uint32
chainID ids.ID
ok bool
}{
{"exact identity", 1, chainA, true},
{"wrong network", 2, chainA, false},
{"wrong chain", 1, chainB, false},
{"both wrong", 2, chainB, false},
{"empty chain id", 1, ids.Empty, false},
} {
r, err := resolverForRuntime(tc.networkID, tc.chainID)
if tc.ok {
if err != nil || r == nil {
t.Fatalf("%s: must resolve, got (%v, %v)", tc.name, r, err)
}
continue
}
if !errors.Is(err, ErrAssetResolverIdentityMismatch) {
t.Fatalf("%s: must refuse with ErrAssetResolverIdentityMismatch, got %v", tc.name, err)
}
if r != nil {
t.Fatalf("%s: a refused runtime lookup must hand back no resolver", tc.name)
}
}
}
// TestBlueF_AssetSideForCurrency pins the V4-currency -> (kind, ref) mapping the
// value path admits each side through. The zero address is the native coin and
// everything else is an ERC-20 whose reference IS its contract address; the mapping
// must be injective on the address so two currencies never share an identity, and
// must copy the address so a later mutation of the returned ref cannot retarget it.
func TestBlueF_AssetSideForCurrency(t *testing.T) {
native := assetSideForCurrency(Currency{Address: common.Address{}})
if native.Kind != dexcore.AssetKindEVMNative {
t.Fatalf("the zero address must map to EVM_NATIVE, got %v", native.Kind)
}
if _, err := dexcore.CanonicalRefFor(native.Kind, native.Ref); err != nil {
t.Fatalf("the native side must carry the canonical native marker: %v", err)
}
tok := common.HexToAddress("0x00000000000000000000000000000000000000C7")
side := assetSideForCurrency(Currency{Address: tok})
if side.Kind != dexcore.AssetKindERC20 {
t.Fatalf("a non-zero address must map to ERC20, got %v", side.Kind)
}
if common.BytesToAddress(side.Ref) != tok {
t.Fatalf("the ERC-20 reference must be the token address, got %x", side.Ref)
}
// The ref must be a copy: mutating it must not have retargeted the currency.
side.Ref[0] ^= 0xFF
again := assetSideForCurrency(Currency{Address: tok})
if common.BytesToAddress(again.Ref) != tok {
t.Fatal("assetSideForCurrency must copy the address, not alias it")
}
}
// ---------------------------------------------------------------------------
// settle_addr.go — the commitment helper
// ---------------------------------------------------------------------------
// TestBlueF_Keccak32MatchesKeccak256 pins the settlement commitment helper against
// an independently computed keccak256 and against collision on the inputs the
// settle path commits. A commitment helper that truncated, padded, or reused a
// buffer would show up here as a mismatch or a collision.
func TestBlueF_Keccak32MatchesKeccak256(t *testing.T) {
inputs := [][]byte{
nil,
{0x00},
{0x01},
[]byte("dex.precompile.v1.9999."),
make([]byte, 32),
make([]byte, 33),
bytes32Seq(200),
}
seen := map[[32]byte]int{}
for i, in := range inputs {
got := keccak32(in)
want := crypto.Keccak256(in)
if len(want) != 32 {
t.Fatalf("keccak256 must be 32 bytes, got %d", len(want))
}
for j := range got {
if got[j] != want[j] {
t.Fatalf("input %d: keccak32 != keccak256 at byte %d (%x vs %x)", i, j, got, want)
}
}
if prev, dup := seen[got]; dup {
t.Fatalf("input %d collides with input %d — the commitment is not injective", i, prev)
}
seen[got] = i
}
// One flipped bit must change the commitment (no truncation to a short prefix).
a := bytes32Seq(64)
b := append([]byte(nil), a...)
b[len(b)-1] ^= 0x01
if keccak32(a) == keccak32(b) {
t.Fatal("a one-bit change must change the commitment")
}
}
// bytes32Seq builds a deterministic n-byte fixture.
func bytes32Seq(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = byte(i*7 + 3)
}
return b
}
// ---------------------------------------------------------------------------
// fill_attestation.go — the broker fill lifecycle
// ---------------------------------------------------------------------------
// blueFAttest is the fixture for the attestation tests: a fresh state, a manager,
// and a registered attester.
type blueFAttest struct {
db *MockStateDB
mgr *SettlementManager
attester common.Address
rando common.Address
user common.Address
}
func blueFNewAttest(t *testing.T) *blueFAttest {
t.Helper()
f := &blueFAttest{
db: NewMockStateDB(),
mgr: NewSettlementManager(),
attester: common.HexToAddress("0xBF00000000000000000000000000000000000001"),
rando: common.HexToAddress("0xBF00000000000000000000000000000000000002"),
user: common.HexToAddress("0xBF00000000000000000000000000000000000003"),
}
if err := f.mgr.SetAttester(f.db, common.Address{}, f.attester); err != nil {
t.Fatalf("SetAttester: %v", err)
}
return f
}
// setBlock moves the recorded block height the fraud window is measured against.
func (f *blueFAttest) setBlock(n uint64) {
var w common.Hash
new(big.Int).SetUint64(n).FillBytes(w[:])
f.db.SetState(fillAttestAddr, makeStorageKey(fillConfigPrefix, []byte("block")), w)
}
// TestBlueF_AttestRefusesBeforeAnAttesterExists pins the genesis hole shut: with no
// attester registered, NOBODY may attest — including the zero address, which is what
// an unset attester slot reads back as. Without this arm a caller whose address
// compares equal to the empty slot would mint attestations on a fresh deployment.
func TestBlueF_AttestRefusesBeforeAnAttesterExists(t *testing.T) {
db := NewMockStateDB()
mgr := NewSettlementManager()
order := OrderIDFromString("blueF-no-attester")
amount := big.NewInt(3)
price := big.NewInt(5)
user := common.HexToAddress("0xBF00000000000000000000000000000000000003")
for _, caller := range []common.Address{
{},
common.HexToAddress("0xBF00000000000000000000000000000000000009"),
} {
err := mgr.Attest(db, caller, order, SymbolToBytes32("AAPL"), amount, price, user, 1)
if !errors.Is(err, ErrUnauthorizedAttester) {
t.Fatalf("caller %s: attesting with no registered attester must be refused, got %v", caller, err)
}
}
if att := mgr.GetAttestation(db, order); att != nil {
t.Fatal("a refused attestation must record nothing")
}
if out := mgr.GetOutstanding(db); out.Sign() != 0 {
t.Fatalf("a refused attestation must not move outstanding, got %s", out)
}
}
// TestBlueF_ChallengeRefusals pins every arm that must turn a reversal away. The
// authorization check is what stands between a broker fill and an arbitrary caller
// burning a user's tokens, so each refusal is asserted together with the invariant
// that a refused challenge changes NOTHING: not the status, not the outstanding.
func TestBlueF_ChallengeRefusals(t *testing.T) {
f := blueFNewAttest(t)
order := OrderIDFromString("blueF-challenge")
amount := big.NewInt(50)
price := big.NewInt(800)
fill := new(big.Int).Mul(amount, price)
// An unknown order cannot be challenged.
if err := f.mgr.Challenge(f.db, f.attester, OrderIDFromString("blueF-absent"), []byte("proof")); !errors.Is(err, ErrAttestationNotFound) {
t.Fatalf("challenging an unknown order must be refused, got %v", err)
}
if err := f.mgr.Attest(f.db, f.attester, order, SymbolToBytes32("NVDA"), amount, price, f.user, 1); err != nil {
t.Fatalf("Attest: %v", err)
}
// A caller who is not the attester cannot reverse a fill.
if err := f.mgr.Challenge(f.db, f.rando, order, []byte("proof")); !errors.Is(err, ErrUnauthorizedAttester) {
t.Fatalf("a non-attester must not reverse a fill, got %v", err)
}
// An empty reversal proof is refused.
for _, proof := range [][]byte{nil, {}} {
if err := f.mgr.Challenge(f.db, f.attester, order, proof); !errors.Is(err, ErrInvalidReversalProof) {
t.Fatalf("an empty reversal proof must be refused, got %v", err)
}
}
// Every refusal above left the attestation pending and the outstanding intact.
if att := f.mgr.GetAttestation(f.db, order); att == nil || att.Status != FillPending {
t.Fatalf("a refused challenge must leave the attestation pending, got %+v", att)
}
if out := f.mgr.GetOutstanding(f.db); out.Cmp(fill) != 0 {
t.Fatalf("a refused challenge must not move outstanding: got %s want %s", out, fill)
}
// The accepted challenge reverses exactly once; a second one is refused.
if err := f.mgr.Challenge(f.db, f.attester, order, []byte("alpaca-reversal")); err != nil {
t.Fatalf("the attester's challenge must be accepted: %v", err)
}
if err := f.mgr.Challenge(f.db, f.attester, order, []byte("alpaca-reversal")); !errors.Is(err, ErrAttestationReversed) {
t.Fatalf("a reversed attestation must not be reversed twice, got %v", err)
}
if out := f.mgr.GetOutstanding(f.db); out.Sign() != 0 {
t.Fatalf("outstanding must be released exactly once, got %s", out)
}
// And the state, not the manager's cache, carries the verdict: a manager that
// never saw this order must read the same status.
fresh := NewSettlementManager()
if att := fresh.GetAttestation(f.db, order); att == nil || att.Status != FillReversed {
t.Fatalf("a fresh manager must read FillReversed from state, got %+v", att)
}
}
// TestBlueF_FinalizeRefusals pins the finalize arms. Finalize is callable by anyone,
// so its only protections are the fraud window and the terminal statuses — both must
// hold, and a refused finalize must not release the outstanding.
func TestBlueF_FinalizeRefusals(t *testing.T) {
f := blueFNewAttest(t)
if err := f.mgr.SetFraudWindow(f.db, f.attester, 10); err != nil {
t.Fatalf("SetFraudWindow: %v", err)
}
order := OrderIDFromString("blueF-finalize")
amount := big.NewInt(25)
price := big.NewInt(400)
fill := new(big.Int).Mul(amount, price)
if err := f.mgr.Finalize(f.db, OrderIDFromString("blueF-absent-2")); !errors.Is(err, ErrAttestationNotFound) {
t.Fatalf("finalizing an unknown order must be refused, got %v", err)
}
if err := f.mgr.Attest(f.db, f.attester, order, SymbolToBytes32("MSFT"), amount, price, f.user, 1); err != nil {
t.Fatalf("Attest: %v", err)
}
// Sweep the fraud-window boundary rather than probing one point. The
// attestation was recorded at block 1 with a 10-block window, so 11 is the
// first block at which it may finalize and 10 must still refuse.
for blk := uint64(1); blk <= 10; blk++ {
f.setBlock(blk)
if err := f.mgr.Finalize(f.db, order); !errors.Is(err, ErrFraudWindowActive) {
t.Fatalf("block %d is inside the fraud window and must refuse, got %v", blk, err)
}
if out := f.mgr.GetOutstanding(f.db); out.Cmp(fill) != 0 {
t.Fatalf("block %d: a refused finalize must not release outstanding", blk)
}
}
f.setBlock(11)
if err := f.mgr.Finalize(f.db, order); err != nil {
t.Fatalf("block 11 is the first block past the window and must finalize: %v", err)
}
if out := f.mgr.GetOutstanding(f.db); out.Sign() != 0 {
t.Fatalf("finalize must release the outstanding, got %s", out)
}
// The terminal statuses are terminal in both directions.
if err := f.mgr.Finalize(f.db, order); !errors.Is(err, ErrAttestationFinalized) {
t.Fatalf("a finalized attestation must not finalize twice, got %v", err)
}
if err := f.mgr.Challenge(f.db, f.attester, order, []byte("late-reversal")); !errors.Is(err, ErrAttestationFinalized) {
t.Fatalf("a finalized attestation must not be reversed, got %v", err)
}
// A reversed attestation must not finalize either.
rev := OrderIDFromString("blueF-reversed-then-finalize")
if err := f.mgr.Attest(f.db, f.attester, rev, SymbolToBytes32("TSLA"), amount, price, f.user, 1); err != nil {
t.Fatalf("Attest: %v", err)
}
if err := f.mgr.Challenge(f.db, f.attester, rev, []byte("reversal")); err != nil {
t.Fatalf("Challenge: %v", err)
}
f.setBlock(100)
if err := f.mgr.Finalize(f.db, rev); !errors.Is(err, ErrAttestationReversed) {
t.Fatalf("a reversed attestation must not finalize, got %v", err)
}
}
// TestBlueF_FraudWindowDefaultUntilSet pins the sentinel: an unset fraud window
// means the ~3-day default, not zero. A zero default would make every attestation
// finalizable in the same block it was recorded, which is the whole protection
// gone. Setting it explicitly to 0 must be honoured — that is what the sentinel is
// for.
func TestBlueF_FraudWindowDefaultUntilSet(t *testing.T) {
f := blueFNewAttest(t)
if got := f.mgr.getFraudWindow(f.db); got != DefaultFraudWindowBlocks {
t.Fatalf("an unset fraud window must read the default %d, got %d", DefaultFraudWindowBlocks, got)
}
order := OrderIDFromString("blueF-default-window")
if err := f.mgr.Attest(f.db, f.attester, order, SymbolToBytes32("AAPL"), big.NewInt(1), big.NewInt(1), f.user, 1); err != nil {
t.Fatalf("Attest: %v", err)
}
// Under the default window, a block just short of it must still refuse.
f.setBlock(DefaultFraudWindowBlocks)
if err := f.mgr.Finalize(f.db, order); !errors.Is(err, ErrFraudWindowActive) {
t.Fatalf("the default window must still be open at block %d, got %v", DefaultFraudWindowBlocks, err)
}
// An explicit zero is a real value, not "unset": the sentinel says so.
if err := f.mgr.SetFraudWindow(f.db, f.attester, 0); err != nil {
t.Fatalf("SetFraudWindow(0): %v", err)
}
if got := f.mgr.getFraudWindow(f.db); got != 0 {
t.Fatalf("an explicit zero fraud window must read back as 0, got %d", got)
}
if err := f.mgr.Finalize(f.db, order); err != nil {
t.Fatalf("with a zero window the attestation must finalize: %v", err)
}
}
// TestBlueF_AttestationAdminAuthorization pins who may move the two governance
// values. The first attester is set by anyone (genesis), and every later change —
// attester, ceiling, fraud window — requires the CURRENT attester. A refused admin
// call must leave the stored value untouched.
func TestBlueF_AttestationAdminAuthorization(t *testing.T) {
f := blueFNewAttest(t)
ceiling := new(big.Int).Mul(big.NewInt(1_000), big.NewInt(1e18))
if err := f.mgr.SetCeiling(f.db, f.attester, ceiling); err != nil {
t.Fatalf("the attester must be able to set the ceiling: %v", err)
}
if got := f.mgr.GetCeiling(f.db); got.Cmp(ceiling) != 0 {
t.Fatalf("GetCeiling must read back the stored ceiling: got %s want %s", got, ceiling)
}
for _, caller := range []common.Address{f.rando, {}, f.user} {
if err := f.mgr.SetCeiling(f.db, caller, big.NewInt(1)); !errors.Is(err, ErrUnauthorizedAttester) {
t.Fatalf("caller %s must not set the ceiling, got %v", caller, err)
}
if err := f.mgr.SetFraudWindow(f.db, caller, 1); !errors.Is(err, ErrUnauthorizedAttester) {
t.Fatalf("caller %s must not set the fraud window, got %v", caller, err)
}
if err := f.mgr.SetAttester(f.db, caller, caller); !errors.Is(err, ErrUnauthorizedAttester) {
t.Fatalf("caller %s must not seize the attester role, got %v", caller, err)
}
}
if got := f.mgr.GetCeiling(f.db); got.Cmp(ceiling) != 0 {
t.Fatalf("a refused admin call must not change the ceiling: got %s want %s", got, ceiling)
}
if got := f.mgr.getFraudWindow(f.db); got != DefaultFraudWindowBlocks {
t.Fatalf("a refused admin call must not change the fraud window, got %d", got)
}
if got := f.mgr.getAttester(f.db); got != f.attester {
t.Fatalf("a refused admin call must not change the attester, got %s", got)
}
// The ceiling is enforced against the SUM of outstanding fills, not each fill
// alone: two fills that each fit but together do not must be refused on the
// second.
small := new(big.Int).Mul(big.NewInt(600), big.NewInt(1e18))
if err := f.mgr.Attest(f.db, f.attester, OrderIDFromString("blueF-ceil-1"), SymbolToBytes32("A"), big.NewInt(1), small, f.user, 1); err != nil {
t.Fatalf("the first fill fits under the ceiling: %v", err)
}
err := f.mgr.Attest(f.db, f.attester, OrderIDFromString("blueF-ceil-2"), SymbolToBytes32("A"), big.NewInt(1), small, f.user, 1)
if !errors.Is(err, ErrPrefundCeilingExceeded) {
t.Fatalf("the second fill must breach the ceiling, got %v", err)
}
if out := f.mgr.GetOutstanding(f.db); out.Cmp(small) != 0 {
t.Fatalf("a refused attestation must not add to outstanding: got %s want %s", out, small)
}
}
// TestBlueF_OutstandingNeverGoesNegative pins the release arms' floor. Outstanding
// is a stored 32-byte word; releasing more than it holds must clamp at zero rather
// than wrap to a near-2^256 balance, which would silently disable the prefund
// ceiling for every later fill.
func TestBlueF_OutstandingNeverGoesNegative(t *testing.T) {
for _, release := range []string{"challenge", "finalize"} {
f := blueFNewAttest(t)
order := OrderIDFromString("blueF-clamp-" + release)
amount := big.NewInt(10)
price := big.NewInt(7)
if err := f.mgr.Attest(f.db, f.attester, order, SymbolToBytes32("AAPL"), amount, price, f.user, 1); err != nil {
t.Fatalf("%s: Attest: %v", release, err)
}
// Model an outstanding that has already been drawn down elsewhere, so the
// release is larger than the balance it reduces.
f.mgr.setOutstanding(f.db, big.NewInt(1))
var err error
if release == "challenge" {
err = f.mgr.Challenge(f.db, f.attester, order, []byte("reversal"))
} else {
if serr := f.mgr.SetFraudWindow(f.db, f.attester, 1); serr != nil {
t.Fatalf("SetFraudWindow: %v", serr)
}
f.setBlock(1000)
err = f.mgr.Finalize(f.db, order)
}
if err != nil {
t.Fatalf("%s: must succeed: %v", release, err)
}
out := f.mgr.GetOutstanding(f.db)
if out.Sign() != 0 {
t.Fatalf("%s: outstanding must clamp at zero, got %s", release, out)
}
if out.BitLen() > 256 {
t.Fatalf("%s: outstanding must never wrap, got a %d-bit value", release, out.BitLen())
}
}
}
// TestBlueF_AttestationStateIsTheSourceOfTruth pins that the attestation a manager
// writes is fully recoverable from state by a manager that never saw it. Two
// validators run separate processes over the same state; if any field lived only in
// one manager's memory they would disagree about a fill.
func TestBlueF_AttestationStateIsTheSourceOfTruth(t *testing.T) {
f := blueFNewAttest(t)
order := OrderIDFromString("blueF-roundtrip")
symbol := SymbolToBytes32("GOOG")
amount := big.NewInt(123)
price := new(big.Int).Mul(big.NewInt(456), big.NewInt(1e18))
if err := f.mgr.Attest(f.db, f.attester, order, symbol, amount, price, f.user, 1711036800); err != nil {
t.Fatalf("Attest: %v", err)
}
fresh := NewSettlementManager()
got := fresh.GetAttestation(f.db, order)
if got == nil {
t.Fatal("a fresh manager must find the attestation in state")
}
if got.Status != FillPending {
t.Fatalf("status must round-trip, got %d", got.Status)
}
if got.Amount.Cmp(amount) != 0 || got.Price.Cmp(price) != 0 {
t.Fatalf("amount/price must round-trip: got %s/%s want %s/%s", got.Amount, got.Price, amount, price)
}
if got.User != f.user || got.Attester != f.attester {
t.Fatalf("addresses must round-trip: got user %s attester %s", got.User, got.Attester)
}
if got.Symbol != symbol {
t.Fatalf("symbol must round-trip, got %x", got.Symbol)
}
if got.Timestamp != 1711036800 {
t.Fatalf("timestamp must round-trip, got %d", got.Timestamp)
}
if got.Block != 1 {
t.Fatalf("recording block must round-trip, got %d", got.Block)
}
// The duplicate check reads state too: a fresh manager must refuse the same
// order id.
if err := fresh.Attest(f.db, f.attester, order, symbol, amount, price, f.user, 1); !errors.Is(err, ErrDuplicateAttestation) {
t.Fatalf("a fresh manager must refuse a duplicate order id, got %v", err)
}
}
// TestBlueF_OrderIDIsInjective pins the order-id derivation. The id is the only
// thing standing between two different broker fills, so distinct provider ids must
// not collide and the same id must be stable across calls.
func TestBlueF_OrderIDIsInjective(t *testing.T) {
seen := map[[32]byte]string{}
for _, s := range []string{"", "a", "b", "alpaca-1", "alpaca-2", "alpaca-1 ", " alpaca-1"} {
id := OrderIDFromString(s)
if again := OrderIDFromString(s); again != id {
t.Fatalf("%q: the order id must be deterministic", s)
}
if prev, dup := seen[id]; dup {
t.Fatalf("%q collides with %q", s, prev)
}
seen[id] = s
}
// The symbol encoding is a left-aligned truncation to 32 bytes; a symbol longer
// than 32 bytes must not overflow the array.
long := SymbolToBytes32("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if long[0] != 'A' || long[31] != '5' {
t.Fatalf("a symbol longer than 32 bytes must truncate to the first 32, got %q", long)
}
}
// ---------------------------------------------------------------------------
// native_staging.go — the cross-domain staging window
// ---------------------------------------------------------------------------
// blueFStageObject builds a canonical C->D atomic object for the harness's owner.
func blueFStageObject(owner common.Address, asset [32]byte, amount uint64) []byte {
return encodeClaim(owner, asset, amount)
}
// TestBlueF_StagedRemoveRoundTrip pins the D->C REMOVE half of the staging window.
// A remove consumes a cross-chain object, so its record must decode to exactly the
// (destination chain, key) it was staged with — a misrouted or mis-keyed remove
// either consumes someone else's object or silently consumes nothing.
func TestBlueF_StagedRemoveRoundTrip(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
keyA := ids.ID{0x01, 0x02}
keyB := ids.ID{0x03, 0x04}
other := ids.ID{0xEE}
stageAtomicRemove(kv, h.dChainID, keyA)
stageAtomicRemove(kv, other, keyB)
if got := stageSeq(kv); got != 2 {
t.Fatalf("two staged ops must advance the seq to 2, got %d", got)
}
reqs, err := CollectStagedAtomicRange(kv, 0, 2)
if err != nil {
t.Fatalf("collect: %v", err)
}
if len(reqs) != 2 {
t.Fatalf("two destination chains must yield two request sets, got %d", len(reqs))
}
for chain, key := range map[ids.ID]ids.ID{h.dChainID: keyA, other: keyB} {
r := reqs[chain]
if r == nil {
t.Fatalf("chain %s must have a request set", chain)
}
if len(r.PutRequests) != 0 {
t.Fatalf("chain %s: a remove must not stage a put", chain)
}
if len(r.RemoveRequests) != 1 {
t.Fatalf("chain %s: expected exactly one remove, got %d", chain, len(r.RemoveRequests))
}
if string(r.RemoveRequests[0]) != string(key[:]) {
t.Fatalf("chain %s: remove key mismatch: got %x want %x", chain, r.RemoveRequests[0], key[:])
}
}
// The window is half-open on both ends: a sub-window selects exactly its ops,
// and an empty window selects none.
first, err := CollectStagedAtomicRange(kv, 0, 1)
if err != nil || len(first) != 1 {
t.Fatalf("the window [0,1) must select exactly the first op, got %d (%v)", len(first), err)
}
if empty, err := CollectStagedAtomicRange(kv, 2, 2); err != nil || len(empty) != 0 {
t.Fatalf("an empty window must select nothing, got %d (%v)", len(empty), err)
}
if past, err := CollectStagedAtomicRange(kv, 5, 2); err != nil || len(past) != 0 {
t.Fatalf("an inverted window must select nothing, got %d (%v)", len(past), err)
}
}
// TestBlueF_StagedOpMalformedFailsAndAppliesNothing pins the fatal arm: a staged
// record the flush cannot decode must FAIL block accept, never be skipped. Skipping
// one would move value on one side of the seam and not the other. The second half of
// the property matters just as much — a refused flush must leave shared memory
// untouched, including the ops that were well formed.
func TestBlueF_StagedOpMalformedFailsAndAppliesNothing(t *testing.T) {
goodObject := blueFStageObject(common.HexToAddress("0x1111111111111111111111111111111111111111"), [32]byte{0x01}, 5)
for _, tc := range []struct {
name string
stage func(kv stateKV, dChainID ids.ID)
}{
{"put record with an over-long object", func(kv stateKV, d ids.ID) {
rec := packPut(d, ids.ID{0x09}, append(append([]byte(nil), goodObject...), 0x00))
writeBytesToSlots(kv, stagePutPrefix, 0, rec)
markStageKind(kv, 0, stageKindPut)
setStageSeq(kv, 1)
}},
{"put record truncated below the object width", func(kv stateKV, d ids.ID) {
rec := packPut(d, ids.ID{0x09}, goodObject[:len(goodObject)-1])
writeBytesToSlots(kv, stagePutPrefix, 0, rec)
markStageKind(kv, 0, stageKindPut)
setStageSeq(kv, 1)
}},
{"remove record too short", func(kv stateKV, d ids.ID) {
writeBytesToSlots(kv, stageRemovePrefix, 0, []byte{stagedOpVersion, 0x01, 0x02})
markStageKind(kv, 0, stageKindRemove)
setStageSeq(kv, 1)
}},
{"remove record with an unknown version", func(kv stateKV, d ids.ID) {
rec := packRemove(d, ids.ID{0x09})
rec[0] = stagedOpVersion + 1
writeBytesToSlots(kv, stageRemovePrefix, 0, rec)
markStageKind(kv, 0, stageKindRemove)
setStageSeq(kv, 1)
}},
{"remove record with no bytes at all", func(kv stateKV, d ids.ID) {
markStageKind(kv, 0, stageKindRemove)
setStageSeq(kv, 1)
}},
{"seq slot with an unrecognised kind", func(kv stateKV, d ids.ID) {
markStageKind(kv, 0, 0x7F)
setStageSeq(kv, 1)
}},
{"seq slot with no kind at all", func(kv stateKV, d ids.ID) {
setStageSeq(kv, 1)
}},
} {
h := newSettleHarness(t)
kv := h.state.stateDB
tc.stage(kv, h.dChainID)
if _, err := CollectStagedAtomicRange(kv, 0, 1); !errors.Is(err, ErrStagedOpMalformed) {
t.Fatalf("%s: must fail with ErrStagedOpMalformed, got %v", tc.name, err)
}
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: 0}, kv, h.cSM, nil); !errors.Is(err, ErrStagedOpMalformed) {
t.Fatalf("%s: the flush must fail block accept, got %v", tc.name, err)
}
// Nothing may have reached the D side.
probeKey := ids.ID{0x09}
vals, gerr := h.dSM.Get(h.cChainID, [][]byte{probeKey[:]})
if gerr == nil && len(vals) > 0 && len(vals[0]) > 0 {
t.Fatalf("%s: a refused flush must apply nothing to shared memory", tc.name)
}
}
}
// TestBlueF_CollectStagedAtomicPanicsOnMalformed pins the test-harness convenience
// wrapper's documented contract: it has no error return, so a malformed op is a
// panic rather than a silently empty request set. An empty set would read as "this
// block staged nothing" and quietly drop value.
func TestBlueF_CollectStagedAtomicPanicsOnMalformed(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
markStageKind(kv, 0, 0x5A) // an unrecognisable kind
setStageSeq(kv, 1)
defer func() {
r := recover()
if r == nil {
t.Fatal("CollectStagedAtomic must panic on a malformed staged op, not return an empty set")
}
if err, ok := r.(error); !ok || !errors.Is(err, ErrStagedOpMalformed) {
t.Fatalf("the panic must carry ErrStagedOpMalformed, got %v", r)
}
}()
_ = CollectStagedAtomic(kv)
}
// TestBlueF_CollectStagedAtomicWholeWindow pins the wrapper's happy path: it reads
// the whole [0, seq) window, so a harness sees every op the block staged.
func TestBlueF_CollectStagedAtomicWholeWindow(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
owner := common.HexToAddress("0x2222222222222222222222222222222222222222")
stageAtomicPut(kv, h.dChainID, ids.ID{0xA1}, blueFStageObject(owner, [32]byte{0x02}, 11))
stageAtomicRemove(kv, h.dChainID, ids.ID{0xA2})
reqs := CollectStagedAtomic(kv)
r := reqs[h.dChainID]
if r == nil {
t.Fatal("the staged ops must route to the D chain")
}
if len(r.PutRequests) != 1 || len(r.RemoveRequests) != 1 {
t.Fatalf("expected one put and one remove, got %d/%d", len(r.PutRequests), len(r.RemoveRequests))
}
putKey := ids.ID{0xA1}
if string(r.PutRequests[0].Key) != string(putKey[:]) {
t.Fatalf("put key mismatch: %x", r.PutRequests[0].Key)
}
// The destination indexes the object by RECIPIENT: the trait must be the
// owner decoded from the object, not the key and not the caller.
if len(r.PutRequests[0].Traits) != 1 || string(r.PutRequests[0].Traits[0]) != string(owner.Bytes()) {
t.Fatalf("the put trait must be the object's owner, got %x", r.PutRequests[0].Traits)
}
// Clearing resets the window to empty, and clearing again is harmless.
ClearStagedAtomic(kv)
if got := stageSeq(kv); got != 0 {
t.Fatalf("clear must reset the seq, got %d", got)
}
if left := CollectStagedAtomic(kv); len(left) != 0 {
t.Fatalf("clear must leave no staged ops, got %d", len(left))
}
ClearStagedAtomic(kv)
}
// TestBlueF_ClearStagedSkipsUnknownKinds pins that the harness reset walks past a
// slot it cannot classify instead of stopping: the seq counter must still come back
// to zero, or the next test's window would start mid-history.
func TestBlueF_ClearStagedSkipsUnknownKinds(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
owner := common.HexToAddress("0x3333333333333333333333333333333333333333")
stageAtomicPut(kv, h.dChainID, ids.ID{0xB1}, blueFStageObject(owner, [32]byte{0x03}, 3))
markStageKind(kv, 1, 0x6D) // an unclassifiable slot in the middle
setStageSeq(kv, 2)
stageAtomicRemove(kv, h.dChainID, ids.ID{0xB2})
ClearStagedAtomic(kv)
if got := stageSeq(kv); got != 0 {
t.Fatalf("clear must reset the seq past an unknown kind, got %d", got)
}
if got := readBytesFromSlots(kv, stagePutPrefix, 0); got != nil {
t.Fatalf("clear must zero the put record, got %x", got)
}
if got := readBytesFromSlots(kv, stageRemovePrefix, 2); got != nil {
t.Fatalf("clear must zero the remove record, got %x", got)
}
}
// TestBlueF_ReadBytesFromUnwrittenSlotIsEmpty pins that an unwritten staging slot
// reads as nothing rather than as a 32-byte zero record. A zero-length record is
// what makes the malformed check fire; a zero-FILLED one would decode as a version-0
// op and could be mistaken for a real staged object.
func TestBlueF_ReadBytesFromUnwrittenSlotIsEmpty(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
for _, prefix := range [][]byte{stagePutPrefix, stageRemovePrefix} {
for _, seq := range []uint64{0, 1, 1 << 40} {
if got := readBytesFromSlots(kv, prefix, seq); got != nil {
t.Fatalf("an unwritten slot must read as nil, got %x", got)
}
}
}
// A written record reads back byte-identical across the multi-slot boundary.
rec := packPut(h.dChainID, ids.ID{0xC1}, blueFStageObject(common.Address{0x44}, [32]byte{0x04}, 9))
writeBytesToSlots(kv, stagePutPrefix, 3, rec)
got := readBytesFromSlots(kv, stagePutPrefix, 3)
if string(got) != string(rec) {
t.Fatalf("a staged record must round-trip through the slots: got %x want %x", got, rec)
}
}
// TestBlueF_StagedObjectIsConsumedExactlyOnce pins the exactly-once property the
// whole staging design exists for. Shared memory is NOT idempotent — re-applying a
// window errors — so the safety comes from the parent->current seq window advancing.
// The test proves both halves: the window applies once, and the NEXT window is empty
// so nothing is applied twice.
func TestBlueF_StagedObjectIsConsumedExactlyOnce(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
owner := common.HexToAddress("0x5555555555555555555555555555555555555555")
key := ids.ID{0xD1}
stageAtomicPut(kv, h.dChainID, key, blueFStageObject(owner, [32]byte{0x05}, 21))
to := ReadStagedAtomicSeq(kv)
if to != 1 {
t.Fatalf("one staged op must advance the seq to 1, got %d", to)
}
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: 0}, kv, h.cSM, nil); err != nil {
t.Fatalf("the first flush must apply: %v", err)
}
vals, err := h.dSM.Get(h.cChainID, [][]byte{key[:]})
if err != nil {
t.Fatalf("the D side must see the object: %v", err)
}
if len(vals) != 1 || len(vals[0]) != claimSize {
t.Fatalf("the D side must see exactly one canonical object, got %d values", len(vals))
}
// Re-applying the SAME window is refused by shared memory: the window boundary
// is what makes the flush exactly-once, not replay idempotency.
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: 0}, kv, h.cSM, nil); err == nil {
t.Fatal("re-applying an already-applied window must be refused by shared memory")
}
// The next block's window starts where this one ended and applies nothing.
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: to}, kv, h.cSM, nil); err != nil {
t.Fatalf("the next window must be an empty no-op: %v", err)
}
// A seq that went BACKWARDS is fatal, never papered over.
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: 5}, kv, h.cSM, nil); !errors.Is(err, ErrAtomicSeqRegression) {
t.Fatalf("a seq regression must be fatal, got %v", err)
}
// And with no shared memory wired the flush is a no-op rather than a panic.
if err := FlushAcceptedAtomicOps(&seqOnlyState{seq: 0}, kv, nil, nil); err != nil {
t.Fatalf("a nil shared memory must be a no-op, got %v", err)
}
}
// TestBlueF_FlushViewIsReadOnly pins that the host's accept-time view cannot write.
// The staged records are part of the state root, fixed at block production; a flush
// that mutated state would change the root after the fact and fork the chain.
func TestBlueF_FlushViewIsReadOnly(t *testing.T) {
h := newSettleHarness(t)
kv := h.state.stateDB
ro := roStateKV{r: kv}
slot := makeStorageKey([]byte("blueF.readonly"), []byte{0x01})
var poison common.Hash
poison[31] = 0xFF
ro.SetState(poolManagerAddr9999, slot, poison)
if got := kv.GetState(poolManagerAddr9999, slot); got != (common.Hash{}) {
t.Fatalf("the accept-time view must not write through, got %x", got)
}
// And it reads through faithfully.
kv.SetState(poolManagerAddr9999, slot, poison)
if got := ro.GetState(poolManagerAddr9999, slot); got != poison {
t.Fatalf("the accept-time view must read through, got %x", got)
}
if got := ReadStagedAtomicSeq(ro); got != stageSeq(kv) {
t.Fatalf("the seq read through the view must match state, got %d", got)
}
}
// TestBlueF_StagedPutRejectsANonCanonicalObject pins the object-shape check on the
// PUT leg. The object is the value that lands in the destination chain's shared
// memory, so a record whose object is not EXACTLY the canonical width must fail the
// flush rather than be reinterpreted into a credit of the wrong amount.
func TestBlueF_StagedPutRejectsANonCanonicalObject(t *testing.T) {
h := newSettleHarness(t)
owner := common.HexToAddress("0x6666666666666666666666666666666666666666")
good := blueFStageObject(owner, [32]byte{0x06}, 4)
for n := 0; n <= claimSize+2; n++ {
if n == claimSize {
continue // the canonical width is covered by the round-trip test
}
hh := newSettleHarness(t)
kv := hh.state.stateDB
object := make([]byte, n)
copy(object, good)
writeBytesToSlots(kv, stagePutPrefix, 0, packPut(hh.dChainID, ids.ID{0xE1}, object))
markStageKind(kv, 0, stageKindPut)
setStageSeq(kv, 1)
if _, err := CollectStagedAtomicRange(kv, 0, 1); !errors.Is(err, ErrStagedOpMalformed) {
t.Fatalf("a %d-byte object must fail the flush (canonical is %d), got %v", n, claimSize, err)
}
}
_ = h
}
// ---------------------------------------------------------------------------
// module_erc20.go — the token sub-call seam
// ---------------------------------------------------------------------------
// TestBlueF_ERC20SeamFailsSecureWithoutACallSurface pins the seam's fail-secure
// arms. Every one of them is a case where the precompile cannot reach the token
// contract; the only safe answer is "no balance" and "transfer refused", because
// reporting a balance it could not read, or a transfer it did not make, mints an
// unbacked claim.
func TestBlueF_ERC20SeamFailsSecureWithoutACallSurface(t *testing.T) {
h := newSettleHarness(t)
token := common.HexToAddress("0x00000000000000000000000000000000000000F1")
owner := common.HexToAddress("0x00000000000000000000000000000000000000F2")
base := blueFNoCodeStateDB{StateDB: h.state.GetStateDB()}
for _, tc := range []struct {
name string
adapter *poolStateAdapter
}{
{"no accessible state at all", &poolStateAdapter{stateDB: base}},
{"an environment that is nil", &poolStateAdapter{stateDB: base, accessibleState: &blueFEnvState{AccessibleState: h.state, env: nil}}},
{"an environment with no Call surface", &poolStateAdapter{stateDB: base, accessibleState: &blueFEnvState{AccessibleState: h.state, env: blueFPlainEnv{}}}},
} {
if c := tc.adapter.callable(); c != nil {
t.Fatalf("%s: must expose no Call surface", tc.name)
}
if got := tc.adapter.TokenBalanceOf(token, owner); got.Sign() != 0 {
t.Fatalf("%s: an unreadable balance must be 0, got %s", tc.name, got)
}
if err := tc.adapter.TransferTokenFrom(token, owner, poolManagerAddr9999, big.NewInt(1)); !errors.Is(err, ErrERC20VaultUnavailable) {
t.Fatalf("%s: a pull with no Call surface must refuse, got %v", tc.name, err)
}
if err := tc.adapter.TransferTokenTo(token, owner, big.NewInt(1)); !errors.Is(err, ErrERC20VaultUnavailable) {
t.Fatalf("%s: a push with no Call surface must refuse, got %v", tc.name, err)
}
}
}
// TestBlueF_ERC20BalanceReadFailsSecure pins the balance read over a LIVE Call
// surface. A reverted or short return must read as zero — a deposit sized from a
// balance the precompile could not actually read is an unbacked credit.
func TestBlueF_ERC20BalanceReadFailsSecure(t *testing.T) {
h := newSettleHarness(t)
token := common.HexToAddress("0x00000000000000000000000000000000000000F3")
owner := common.HexToAddress("0x00000000000000000000000000000000000000F4")
base := blueFNoCodeStateDB{StateDB: h.state.GetStateDB()}
full := make([]byte, 32)
full[31] = 0x2A // 42
for _, tc := range []struct {
name string
ret []byte
err error
want int64
}{
{"the token reverted", nil, errors.New("revert"), 0},
{"a short return", make([]byte, 31), nil, 0},
{"an empty return", nil, nil, 0},
{"a well-formed balance", full, nil, 42},
} {
env := &blueFCallEnv{ret: tc.ret, err: tc.err}
a := &poolStateAdapter{stateDB: base, accessibleState: &blueFEnvState{AccessibleState: h.state, env: env}}
got := a.TokenBalanceOf(token, owner)
if got.Int64() != tc.want {
t.Fatalf("%s: got %s want %d", tc.name, got, tc.want)
}
if env.calls != 1 {
t.Fatalf("%s: the balance read must make exactly one sub-call, got %d", tc.name, env.calls)
}
if env.lastAddr != token {
t.Fatalf("%s: the sub-call must target the token, got %s", tc.name, env.lastAddr)
}
if len(env.lastIn) != 36 || string(env.lastIn[:4]) != string(selERC20BalanceOf) {
t.Fatalf("%s: the sub-call must be balanceOf(address), got %x", tc.name, env.lastIn)
}
if common.BytesToAddress(env.lastIn[4:36]) != owner {
t.Fatalf("%s: the sub-call must ask about the owner, got %x", tc.name, env.lastIn[4:36])
}
}
}
// TestBlueF_ERC20TransferReturnSemantics pins SafeERC20 semantics on the push leg:
// a revert and an explicit false are both FAILED transfers; a bare success with no
// return data is accepted (non-compliant tokens). Treating a false return as success
// is how a token that silently refuses gets counted as delivered.
func TestBlueF_ERC20TransferReturnSemantics(t *testing.T) {
h := newSettleHarness(t)
token := common.HexToAddress("0x00000000000000000000000000000000000000F5")
to := common.HexToAddress("0x00000000000000000000000000000000000000F6")
base := blueFNoCodeStateDB{StateDB: h.state.GetStateDB()}
trueWord := make([]byte, 32)
trueWord[31] = 1
for _, tc := range []struct {
name string
ret []byte
err error
ok bool
}{
{"revert", nil, errors.New("revert"), false},
{"explicit false", make([]byte, 32), nil, false},
{"malformed short return", make([]byte, 4), nil, false},
{"explicit true", trueWord, nil, true},
{"no return data", nil, nil, true},
} {
env := &blueFCallEnv{ret: tc.ret, err: tc.err}
a := &poolStateAdapter{stateDB: base, accessibleState: &blueFEnvState{AccessibleState: h.state, env: env}}
err := a.TransferTokenTo(token, to, big.NewInt(7))
if tc.ok && err != nil {
t.Fatalf("%s: must succeed, got %v", tc.name, err)
}
if !tc.ok && err == nil {
t.Fatalf("%s: must be treated as a FAILED transfer", tc.name)
}
if len(env.lastIn) != 68 || string(env.lastIn[:4]) != string(selERC20Transfer) {
t.Fatalf("%s: the sub-call must be transfer(address,uint256), got %x", tc.name, env.lastIn)
}
}
}
// ---------------------------------------------------------------------------
// types.go / module.go — calldata shape refusals
// ---------------------------------------------------------------------------
// TestBlueF_PoolKeyFromBytesRefusesShortInput sweeps the length boundary of the
// stored pool-key decoder. The read is 66 bytes wide, so every length below that
// must be refused on len — never on capacity, which in production is attacker-chosen
// EVM memory rather than the end of the record.
func TestBlueF_PoolKeyFromBytesRefusesShortInput(t *testing.T) {
key := PoolKey{
Currency0: Currency{Address: common.HexToAddress("0x00000000000000000000000000000000000000D1")},
Currency1: Currency{Address: common.HexToAddress("0x00000000000000000000000000000000000000D2")},
Fee: 3000,
TickSpacing: 60,
Hooks: common.HexToAddress("0x00000000000000000000000000000000000000D3"),
}
full := key.ToBytes()
if len(full) != 66 {
t.Fatalf("the stored pool key must be 66 bytes, got %d", len(full))
}
for n := 0; n < 66; n++ {
if _, err := PoolKeyFromBytes(full[:n]); err == nil {
t.Fatalf("a %d-byte record must be refused", n)
}
// The same verdict must hold when the bytes past len are attacker-chosen.
if _, err := PoolKeyFromBytes(blueFPoisoned(full[:n], 128)); err == nil {
t.Fatalf("a %d-byte record with poisoned spare capacity must still be refused", n)
}
}
got, err := PoolKeyFromBytes(full)
if err != nil {
t.Fatalf("the exact width must decode: %v", err)
}
if got.Currency0.Address != key.Currency0.Address || got.Currency1.Address != key.Currency1.Address {
t.Fatalf("currencies must round-trip, got %+v", got)
}
if got.Fee != key.Fee || got.TickSpacing != key.TickSpacing || got.Hooks != key.Hooks {
t.Fatalf("fee/spacing/hooks must round-trip, got %+v", got)
}
// Poisoning past the exact width must not change the decoded key either.
poisoned, err := PoolKeyFromBytes(blueFPoisoned(full, 128))
if err != nil || poisoned != got {
t.Fatalf("poisoned spare capacity must not change the decoded key (%v)", err)
}
}
// TestBlueF_DecodeInputRefusesShortCalldata sweeps the two V4 decoders' length
// boundaries. Both read fixed-width fields at absolute offsets, so a caller that
// declares less calldata than the layout needs must be refused on len — otherwise
// the decode reads whatever the caller left in EVM memory past the declared input.
func TestBlueF_DecodeInputRefusesShortCalldata(t *testing.T) {
swap := make([]byte, 256)
copy(swap[12:32], common.HexToAddress("0x00000000000000000000000000000000000000D4").Bytes())
copy(swap[44:64], common.HexToAddress("0x00000000000000000000000000000000000000D5").Bytes())
swap[191] = 1 // zeroForOne
for _, n := range []int{0, 1, 159, 160, 191, 255} {
if _, _, _, err := DecodeSwapInput(swap[:n]); err == nil {
t.Fatalf("a %d-byte swap input must be refused", n)
}
if _, _, _, err := DecodeSwapInput(blueFPoisoned(swap[:n], 512)); err == nil {
t.Fatalf("a %d-byte swap input with poisoned spare capacity must still be refused", n)
}
}
key, params, hookData, err := DecodeSwapInput(swap)
if err != nil {
t.Fatalf("the exact width must decode: %v", err)
}
if !params.ZeroForOne {
t.Fatal("zeroForOne must decode from its own word")
}
if hookData != nil {
t.Fatalf("a swap with no trailing bytes must carry no hook data, got %x", hookData)
}
if key.Currency0.Address != common.HexToAddress("0x00000000000000000000000000000000000000D4") {
t.Fatalf("currency0 must decode, got %s", key.Currency0.Address)
}
modify := make([]byte, 288)
copy(modify[12:32], common.HexToAddress("0x00000000000000000000000000000000000000D6").Bytes())
for _, n := range []int{0, 159, 255, 287} {
if _, _, _, err := DecodeModifyLiquidityInput(modify[:n]); err == nil {
t.Fatalf("a %d-byte modifyLiquidity input must be refused", n)
}
if _, _, _, err := DecodeModifyLiquidityInput(blueFPoisoned(modify[:n], 512)); err == nil {
t.Fatalf("a %d-byte modifyLiquidity input with poisoned spare capacity must still be refused", n)
}
}
if _, _, _, err := DecodeModifyLiquidityInput(modify); err != nil {
t.Fatalf("the exact width must decode: %v", err)
}
}
// ---------------------------------------------------------------------------
// tick_math.go / tick_bitmap_math.go / swap_math.go / liquidity_amounts.go
// ---------------------------------------------------------------------------
// TestBlueF_Msb256Domain sweeps the most-significant-bit helper across its whole
// domain. It feeds the tick derivation, so a wrong answer moves a pool's price: the
// sweep pins every power of two, the boundary at zero, and the non-positive inputs
// that must answer 0 rather than fall through to BitLen()-1 == -1.
func TestBlueF_Msb256Domain(t *testing.T) {
for _, x := range []*big.Int{
big.NewInt(0),
big.NewInt(-1),
new(big.Int).Neg(new(big.Int).Lsh(big.NewInt(1), 200)),
} {
if got := msb256(x); got != 0 {
t.Fatalf("msb256(%s) must be 0 for a non-positive input, got %d", x, got)
}
}
for i := 0; i < 300; i++ {
p := new(big.Int).Lsh(big.NewInt(1), uint(i))
if got := msb256(p); got != i {
t.Fatalf("msb256(2^%d) must be %d, got %d", i, i, got)
}
// One below a power of two has the next-lower msb.
if i > 0 {
if got := msb256(new(big.Int).Sub(p, big.NewInt(1))); got != i-1 {
t.Fatalf("msb256(2^%d-1) must be %d, got %d", i, i-1, got)
}
}
}
}
// TestBlueF_Lsb256Domain sweeps the least-significant-bit helper. It is scanned over
// exactly 256 bit positions, so a value whose only set bits sit at or above 256 has
// no answer in range and must fall out as 0 rather than run off the end.
func TestBlueF_Lsb256Domain(t *testing.T) {
for _, x := range []*big.Int{big.NewInt(0), big.NewInt(-1), big.NewInt(-1 << 20)} {
if got := lsb256(x); got != 0 {
t.Fatalf("lsb256(%s) must be 0 for a non-positive input, got %d", x, got)
}
}
for i := 0; i < 256; i++ {
p := new(big.Int).Lsh(big.NewInt(1), uint(i))
if got := lsb256(p); got != i {
t.Fatalf("lsb256(2^%d) must be %d, got %d", i, i, got)
}
// Setting a higher bit as well must not move the lowest one.
q := new(big.Int).Or(p, new(big.Int).Lsh(big.NewInt(1), 255))
if got := lsb256(q); got != i {
t.Fatalf("lsb256(2^%d | 2^255) must be %d, got %d", i, i, got)
}
}
// A positive value with no set bit below 256: the scan finds nothing and the
// helper answers 0 rather than reporting a bit it never saw.
for _, shift := range []uint{256, 257, 300} {
if got := lsb256(new(big.Int).Lsh(big.NewInt(1), shift)); got != 0 {
t.Fatalf("lsb256(2^%d) has no bit in range and must answer 0, got %d", shift, got)
}
}
}
// TestBlueF_TickAtSqrtRatioDomain sweeps the tick derivation across its declared
// domain and both sides of each edge. The derivation is the price->tick half of the
// market-open path, so an out-of-domain price must be REFUSED rather than folded
// into a tick, and every admitted price must land on a tick inside [MinTick,
// MaxTick] — including the top of the range, where the high-tick candidate itself
// falls out of range and the derivation must fall back rather than propagate.
func TestBlueF_TickAtSqrtRatioDomain(t *testing.T) {
for _, bad := range []*big.Int{
big.NewInt(0),
big.NewInt(1),
new(big.Int).Sub(MinSqrtRatio, big.NewInt(1)),
new(big.Int).Set(MaxSqrtRatio),
new(big.Int).Add(MaxSqrtRatio, big.NewInt(1)),
new(big.Int).Lsh(big.NewInt(1), 300),
} {
if _, err := GetTickAtSqrtRatio(bad); err == nil {
t.Fatalf("sqrtPriceX96 %s is outside the domain and must be refused", bad)
}
}
// Sweep the whole admitted range, including both edges, and assert the derived
// tick is in range and consistent with the forward map.
one := big.NewInt(1)
probes := []*big.Int{
new(big.Int).Set(MinSqrtRatio),
new(big.Int).Add(MinSqrtRatio, one),
new(big.Int).Set(Q96),
new(big.Int).Sub(MaxSqrtRatio, one),
new(big.Int).Sub(MaxSqrtRatio, big.NewInt(2)),
}
for i := 1; i < 160; i++ {
p := new(big.Int).Lsh(big.NewInt(1), uint(i))
if p.Cmp(MinSqrtRatio) >= 0 && p.Cmp(MaxSqrtRatio) < 0 {
probes = append(probes, p)
}
}
for _, p := range probes {
tick, err := GetTickAtSqrtRatio(p)
if err != nil {
t.Fatalf("sqrtPriceX96 %s is inside the domain and must derive a tick: %v", p, err)
}
if tick < MinTick || tick > MaxTick {
t.Fatalf("sqrtPriceX96 %s derived tick %d outside [%d, %d]", p, tick, MinTick, MaxTick)
}
// The derived tick's own price must not exceed the probe: the derivation
// must round DOWN, or a swap could cross a tick it never paid for.
at, err := GetSqrtRatioAtTick(tick)
if err != nil {
t.Fatalf("the derived tick %d must itself be in the tick domain: %v", tick, err)
}
if at.Cmp(p) > 0 {
t.Fatalf("sqrtPriceX96 %s derived tick %d whose price %s is above it", p, tick, at)
}
}
// The forward map's own domain is closed on both ends.
for _, bad := range []int32{MinTick - 1, MaxTick + 1, -1 << 30, 1 << 30} {
if _, err := GetSqrtRatioAtTick(bad); err == nil {
t.Fatalf("tick %d is outside [%d, %d] and must be refused", bad, MinTick, MaxTick)
}
}
for _, ok := range []int32{MinTick, MinTick + 1, 0, MaxTick - 1, MaxTick} {
v, err := GetSqrtRatioAtTick(ok)
if err != nil {
t.Fatalf("tick %d is inside the domain and must map: %v", ok, err)
}
if v.Sign() <= 0 {
t.Fatalf("tick %d mapped to a non-positive price %s", ok, v)
}
}
}
// TestBlueF_SwapStepClampsRatherThanPropagate pins the fallback in the exact-output
// step: when the next price cannot be computed, the step CLAMPS to the target
// instead of propagating a failure. The clamp is the difference between a swap that
// stops at its limit and one that reverts a whole block's settlement.
func TestBlueF_SwapStepClampsRatherThanPropagate(t *testing.T) {
// Sweep the well-formed domain first: the clamp must NOT fire there — every
// step inside a real tick range must reach a price between current and target.
liquidity := new(big.Int).Lsh(big.NewInt(1), 100)
current := new(big.Int).Set(Q96)
for _, target := range []*big.Int{
new(big.Int).Div(Q96, big.NewInt(2)),
new(big.Int).Sub(Q96, big.NewInt(1)),
new(big.Int).Mul(Q96, big.NewInt(2)),
new(big.Int).Add(Q96, big.NewInt(1)),
} {
for _, amount := range []*big.Int{big.NewInt(-1), big.NewInt(-1e9), big.NewInt(1), big.NewInt(1e9)} {
res := ComputeSwapStep(current, target, liquidity, amount, 3000)
if res.SqrtRatioNextX96 == nil || res.SqrtRatioNextX96.Sign() <= 0 {
t.Fatalf("target %s amount %s: the next price must be positive, got %v", target, amount, res.SqrtRatioNextX96)
}
lo, hi := current, target
if lo.Cmp(hi) > 0 {
lo, hi = hi, lo
}
if res.SqrtRatioNextX96.Cmp(lo) < 0 || res.SqrtRatioNextX96.Cmp(hi) > 0 {
t.Fatalf("target %s amount %s: the next price %s left [%s, %s]", target, amount, res.SqrtRatioNextX96, lo, hi)
}
if res.AmountIn.Sign() < 0 || res.AmountOut.Sign() < 0 || res.FeeAmount.Sign() < 0 {
t.Fatalf("target %s amount %s: a step must not produce negative amounts (%s/%s/%s)",
target, amount, res.AmountIn, res.AmountOut, res.FeeAmount)
}
}
}
// Now the fallback itself. A target BELOW the valid price domain makes the
// exact-output price computation underflow; the step must clamp to the target
// rather than hand back a nil or a price it could not compute.
belowDomain := new(big.Int).Neg(Q96)
liq := new(big.Int).Set(Q96)
amountOut := new(big.Int).Div(new(big.Int).Mul(liq, big.NewInt(3)), big.NewInt(2)) // 1.5 * liq
res := ComputeSwapStep(current, belowDomain, liq, amountOut, 3000)
if res.SqrtRatioNextX96.Cmp(belowDomain) != 0 {
t.Fatalf("an uncomputable next price must clamp to the target %s, got %s", belowDomain, res.SqrtRatioNextX96)
}
if res.AmountOut.Cmp(amountOut) > 0 {
t.Fatalf("the clamped step must not hand out more than was asked: got %s want <= %s", res.AmountOut, amountOut)
}
}
// TestBlueF_LiquidityForAmountsTakesTheBindingSide pins the in-range branch: with
// the price inside the range, the mintable liquidity is the MINIMUM of what each
// side funds. Taking the maximum would mint liquidity one side cannot back.
func TestBlueF_LiquidityForAmountsTakesTheBindingSide(t *testing.T) {
a := new(big.Int).Set(Q96)
b := new(big.Int).Mul(Q96, big.NewInt(4))
mid := new(big.Int).Mul(Q96, big.NewInt(2))
big0 := new(big.Int).Lsh(big.NewInt(1), 90)
small := big.NewInt(1000)
// token0 binding: a tiny amount0 against a large amount1.
got := GetLiquidityForAmounts(mid, a, b, small, big0)
want0 := GetLiquidityForAmount0(mid, b, small)
if got.Cmp(want0) != 0 {
t.Fatalf("token0 must bind: got %s want %s", got, want0)
}
// token1 binding: the mirror case.
got = GetLiquidityForAmounts(mid, a, b, big0, small)
want1 := GetLiquidityForAmount1(a, mid, small)
if got.Cmp(want1) != 0 {
t.Fatalf("token1 must bind: got %s want %s", got, want1)
}
// Whichever side binds, the answer is never more than either side funds.
for _, amt0 := range []*big.Int{small, big0, big.NewInt(1)} {
for _, amt1 := range []*big.Int{small, big0, big.NewInt(1)} {
l := GetLiquidityForAmounts(mid, a, b, amt0, amt1)
l0 := GetLiquidityForAmount0(mid, b, amt0)
l1 := GetLiquidityForAmount1(a, mid, amt1)
if l.Cmp(l0) > 0 || l.Cmp(l1) > 0 {
t.Fatalf("in-range liquidity %s exceeded a funding side (%s / %s)", l, l0, l1)
}
}
}
// Reversing the range bounds must not change the answer: the helper sorts.
if GetLiquidityForAmounts(mid, b, a, small, big0).Cmp(want0) != 0 {
t.Fatal("the range bounds must be order-independent")
}
// Outside the range only one side funds.
below := GetLiquidityForAmounts(a, a, b, small, big.NewInt(0))
if below.Cmp(GetLiquidityForAmount0(a, b, small)) != 0 {
t.Fatalf("below the range only token0 funds, got %s", below)
}
above := GetLiquidityForAmounts(b, a, b, big.NewInt(0), small)
if above.Cmp(GetLiquidityForAmount1(a, b, small)) != 0 {
t.Fatalf("above the range only token1 funds, got %s", above)
}
}
// blueFUnusedGuards keeps the imports honest for helpers used only in some builds.
var (
_ = uint256.NewInt
_ ethtypes.Log
_ atomic.Requests
)
func TestBlueFProbeTickHigh(t *testing.T) {
factor, _ := new(big.Int).SetString("255738958999603826347141", 10)
sub1, _ := new(big.Int).SetString("3402992956809132418596140100660247210", 10)
add1, _ := new(big.Int).SetString("291339464771989622907027621153398088495", 10)
maxU := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
calc := func(sq *big.Int) (int32, int32) {
ratio := new(big.Int).Lsh(sq, 32)
msb := uint(msb256(ratio))
if msb >= 128 {
ratio.Rsh(ratio, msb-127)
} else {
ratio.Lsh(ratio, 127-msb)
}
u256 := new(big.Int).Lsh(big.NewInt(1), 256)
log2 := big.NewInt(int64(msb) - 128)
log2.Lsh(log2, 64)
if log2.Sign() < 0 {
log2.Add(log2, u256)
}
for i := uint(63); i >= 50; i-- {
ratio.Mul(ratio, ratio)
ratio.Rsh(ratio, 127)
f := new(big.Int).Rsh(ratio, 128)
log2.Or(log2, new(big.Int).Lsh(f, i))
if f.Sign() > 0 {
ratio.Rsh(ratio, uint(f.Uint64()))
}
if i == 50 {
break
}
}
log2.And(log2, maxU)
ls := new(big.Int).Mul(uint256ToInt256(log2), factor)
return signedRsh128(new(big.Int).Sub(ls, sub1)), signedRsh128(new(big.Int).Add(ls, add1))
}
span := new(big.Int).Sub(MaxSqrtRatio, MinSqrtRatio)
minHi, maxHi := int32(1<<30), int32(-1<<30)
bad := 0
for i := 0; i < 4000; i++ {
off := new(big.Int).Div(new(big.Int).Mul(span, big.NewInt(int64(i))), big.NewInt(4000))
sq := new(big.Int).Add(MinSqrtRatio, off)
_, hi := calc(sq)
if hi < minHi {
minHi = hi
}
if hi > maxHi {
maxHi = hi
}
if hi < MinTick || hi > MaxTick {
bad++
}
}
// also log-spaced probes
for b := 33; b < 161; b++ {
sq := new(big.Int).Lsh(big.NewInt(1), uint(b))
if sq.Cmp(MinSqrtRatio) < 0 || sq.Cmp(MaxSqrtRatio) >= 0 {
continue
}
_, hi := calc(sq)
if hi < minHi {
minHi = hi
}
if hi > maxHi {
maxHi = hi
}
if hi < MinTick || hi > MaxTick {
bad++
}
}
t.Logf("tickHigh range over the domain: [%d, %d]; out-of-range count=%d; MinTick=%d MaxTick=%d", minHi, maxHi, bad, MinTick, MaxTick)
// swap_math exact-input clamp probe: the only route needs feePips > feeDenominator.
func() {
defer func() { t.Logf("feePips>1e6 exact-input outcome: recover=%v", recover()) }()
res := ComputeSwapStep(new(big.Int).Mul(Q96, big.NewInt(2)), new(big.Int).Set(Q96), big.NewInt(0), big.NewInt(-1000), 2_000_000)
t.Logf("no panic: next=%s in=%s out=%s fee=%s", res.SqrtRatioNextX96, res.AmountIn, res.AmountOut, res.FeeAmount)
}()
}