Files
zeekay a081f8489b dex(0x9999): decentralize halt authority + commit the value-path proof tests
HIGH-3 (centralized key): the 0x9999 emergency-halt authority was a single
hardcoded EOA (DefaultDAOTreasury 0x9011…94714), which per memory is the devnet
"Maker" derivable from LUX_MNEMONIC — so anyone with the dev mnemonic could
setHaltAsset(real,true) (censor) or setHaltGlobal(true) (DoS the whole DEX). It
also gated seedSeamReserve/creditPositionFee. A single admin key, not governance.

Fix: the halt + pot-seeding authority is now the per-NETWORK DEX governance
controller resolved at RUNTIME from contract.AtomicState.GovernanceController()
(the same seam networkID/cChainID/dChainID flow through) — a governance CONTRACT
the host binds from its deployment topology, NEVER a dev-mnemonic EOA.

  - contract.AtomicState gains GovernanceController() common.Address.
  - halt9999.go: new governanceController(state) helper + ErrSettleNoGovernance;
    fail-closed when no AtomicState or the network configured no controller (zero
    address) — every halt/seed reverts, no default key to abuse. Strictly safer
    than a default key: an unset network can be neither censored nor DoS'd.
  - settle_module.go: DELETE DefaultDAOTreasury (the mnemonic EOA) and the
    SettleContract.protocolFeeController field; SettleContract is now stateless
    w.r.t. authority. The 4 gates (setHaltGlobal/Scoped, seedSeamReserve,
    creditPositionFee) resolve gov from runtime, then check caller == gov.
  - access_observer.go: observingAccessibleState forwards GovernanceController to
    the inner (fail-closed when non-atomic) so the L2 write-set wrapper preserves
    the capability.
  - Conservation is unchanged: only the authority resolution moved; value-movement
    logic (observed-delta seed, the pots, checkHalt) is untouched.

HIGH-1/HIGH-2 (integrity): commit the RED #5/#8/#11 proof tests that were
untracked — gatec_red_probe_test.go + gatec_red_dos_test.go — so the value path's
behavior is reproducible at this commit.

LOW-1: swap_sync.go doc comment updated (pre-permissionless "registry admission"
wording → the permissionless canonical resolver) so doc + behavior agree.

TDD (halt_governance_test.go + dex_test.go rewrite, all green):
  - the retired 0x9011 EOA can NO LONGER halt (ErrUnauthorized);
  - an arbitrary EOA cannot halt;
  - ONLY the configured governance controller can halt (global/asset/market) and
    the halt BITES — TestHIGH3_HaltBitesARealFill: governance halts a real market,
    a fillable swap reverts ErrMarketHalted, the retired EOA cannot lift it, only
    governance lifts it, then the swap fills;
  - with no governance configured, NOBODY can halt (fail-closed, ErrSettleNoGovernance);
  - the retired EOA cannot seed pots either;
  - RED CASE 6 (governance cannot mint/move funds) still passes under the new authority.
2026-06-22 22:50:00 -07:00

112 lines
5.3 KiB
Go

// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package dex
import (
"errors"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/precompile/modules"
)
// 0x9010 is REMOVED as a callable precompile (no Run, not registered, no forward).
// 0x9999 is the SOLE canonical DEX precompile and takes NO per-net config FILE: its
// settlement-governance authority is the per-NETWORK governance controller resolved at
// RUNTIME from contract.AtomicState.GovernanceController() (a governance CONTRACT, never
// a hardcoded mnemonic-derivable EOA). There is therefore no single admin key baked into
// the binary — the centralized-key footgun is structurally eliminated. These tests pin
// that model: there is no DefaultDAOTreasury, the SettleContract carries no authority
// field, and only the runtime-supplied governance address may halt.
var lxPoolAddr9010 = common.HexToAddress(LXPoolAddress)
// retiredHardcodedHaltEOA is the address that USED to be the hardcoded 0x9999 halt
// authority (the mnemonic-derivable DefaultDAOTreasury). It is the devnet "Maker"
// derivable from LUX_MNEMONIC, so anyone with that mnemonic could halt the DEX. After
// HIGH-3 it has NO special power: the halt authority is the runtime governance
// controller, and this EOA is not it. The governance tests prove it can no longer halt.
var retiredHardcodedHaltEOA = common.HexToAddress("0x9011E888251AB053B7bD1cdB598Db4f9DEd94714")
// TestDex9010_NotRegistered asserts 0x9010 is NOT a registered precompile — it has no
// module, so the EVM never dispatches it and eth_getCode(0x9010) is 0x. 0x9999 IS
// registered and is the sole DEX money path.
func TestDex9010_NotRegistered(t *testing.T) {
if _, ok := modules.GetPrecompileModuleByAddress(lxPoolAddr9010); ok {
t.Fatal("0x9010 must NOT be a registered precompile — it is removed; 0x9999 is the sole DEX precompile")
}
if _, ok := modules.GetPrecompileModuleByAddress(poolManagerAddr9999); !ok {
t.Fatal("0x9999 must be the registered DEX precompile")
}
}
// TestDex9999_GovernanceIsRuntimeResolved_NotHardcoded pins the HIGH-3 model at the
// STATIC level: 0x9999 carries NO hardcoded halt authority. The SettleContract is a
// zero-field struct (no protocolFeeController), so a single process-global precompile
// instance serves every network and there is no compromised default key on the struct.
// The authority is supplied per network at runtime via AtomicState (proven dynamically
// in the harness governance tests). A grep-level guard: this file no longer references a
// DefaultDAOTreasury symbol (it is deleted); if anyone re-introduces a hardcoded halt
// EOA, the harness halt tests below (only-governance-can-halt) fail.
func TestDex9999_GovernanceIsRuntimeResolved_NotHardcoded(t *testing.T) {
// The 0x9999 contract is registered and is the sole DEX money path...
if _, ok := modules.GetPrecompileModuleByAddress(poolManagerAddr9999); !ok {
t.Fatal("0x9999 must be the registered DEX precompile")
}
// ...and a SettleContract value is constructible with NO authority field — i.e. the
// authority is not a struct field set from a hardcoded key. (A non-zero-field struct
// would not compile as SettleContract{}.)
c := &SettleContract{}
_ = c
// The fail-closed authority error exists and is distinct — an unconfigured network
// cannot halt at all (no default key to fall back to).
if ErrSettleNoGovernance == nil {
t.Fatal("ErrSettleNoGovernance must exist — the fail-closed authority error for an unset governance controller")
}
}
// TestDexAdmin_OnlyConfiguredController verifies that the shared PoolManager admin
// functions (PauseDEX, ResumeDEX, PausePool, ResumePool, FreezePool) refuse
// unauthorized callers — only the configured protocolFeeController is allowed. This is
// the authorization gate 0x9999's halt/governance selectors rely on.
func TestDexAdmin_OnlyConfiguredController(t *testing.T) {
configured := common.HexToAddress("0x9011E888251AB053B7bD1cdB598Db4f9DEd94714")
random := common.HexToAddress("0xBADBADBADBADBADBADBADBADBADBADBADBADBADB")
pm := NewPoolManager(&mockEngine{})
pm.protocolFeeController = configured
stateDB := NewMockStateDB()
// Initialize a pool so freeze/pause-pool have a target.
keyAB, poolID, _, _ := initTwoPoolsPF(t, pm, stateDB)
_ = keyAB
// Each admin call by `random` must return ErrUnauthorized.
cases := []struct {
name string
fn func() error
}{
{"PauseDEX", func() error { return pm.PauseDEX(stateDB, random) }},
{"ResumeDEX", func() error { return pm.ResumeDEX(stateDB, random) }},
{"PausePool", func() error { return pm.PausePool(stateDB, random, poolID) }},
{"ResumePool", func() error { return pm.ResumePool(stateDB, random, poolID) }},
{"FreezePool", func() error { return pm.FreezePool(stateDB, random, poolID) }},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if err := tc.fn(); !errors.Is(err, ErrUnauthorized) {
t.Fatalf("expected ErrUnauthorized for random caller on %s, got: %v", tc.name, err)
}
})
}
// And the configured controller can pause/resume successfully.
if err := pm.PauseDEX(stateDB, configured); err != nil {
t.Fatalf("configured controller PauseDEX should succeed, got: %v", err)
}
if err := pm.ResumeDEX(stateDB, configured); err != nil {
t.Fatalf("configured controller ResumeDEX should succeed, got: %v", err)
}
}