mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
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.
87 lines
4.3 KiB
Go
87 lines
4.3 KiB
Go
// Copyright (C) 2025-2026, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package contract
|
|
|
|
import (
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/vm/chains/atomic"
|
|
)
|
|
|
|
// AtomicState is the OPTIONAL host capability a precompile type-asserts when it
|
|
// must move value across chains via the primary network's atomic shared memory —
|
|
// the same import/export shared-memory primitive platformvm and the dexvm use.
|
|
//
|
|
// It is intentionally NOT part of AccessibleState: the vast majority of
|
|
// precompiles never touch shared memory, and AccessibleState has many test/mock
|
|
// implementers. A precompile that needs it does:
|
|
//
|
|
// atomicState, ok := accessibleState.(contract.AtomicState)
|
|
//
|
|
// and reverts cleanly when ok is false (the host did not wire a shared-memory
|
|
// capable runtime — single-chain dev / a non-atomic harness). The host's
|
|
// concrete AccessibleState (the EVM's accessibleStateAdapter, via the registry
|
|
// bridge) implements this by sourcing the runtime from the consensus context.
|
|
//
|
|
// THE INVARIANT THIS ENABLES (the DEX 0x9999 ship rule): a precompile can credit
|
|
// a local-chain balance from a cross-chain settlement ONLY by consuming a peer's
|
|
// atomic export object out of AtomicMemory() (Get + a Remove applied via Apply),
|
|
// and can fund a peer's chain ONLY by writing an atomic export object into
|
|
// AtomicMemory() (a Put applied via Apply). No value crosses without an atomic
|
|
// shared-memory object on the wire.
|
|
type AtomicState interface {
|
|
// AtomicMemory returns this chain's atomic shared-memory handle, or nil when
|
|
// the host wired no shared memory (single-chain dev). A nil return MUST make
|
|
// the calling precompile revert rather than fabricate value.
|
|
AtomicMemory() atomic.SharedMemory
|
|
|
|
// NetworkID is the numeric network identifier (1=mainnet, 2=testnet, ...). It
|
|
// scopes a cross-chain object's identity so an object minted on one network can
|
|
// never be consumed on another.
|
|
NetworkID() uint32
|
|
|
|
// ChainID is THIS chain's id (the C-Chain / EVM chain executing the precompile).
|
|
// It is the source chain id a C->D export object is keyed under and the
|
|
// destination chain id a D->C settlement object names.
|
|
ChainID() ids.ID
|
|
|
|
// CChainID is the C-Chain id used as the cross-chain settlement peer's view of
|
|
// this chain. On the C-Chain itself this equals ChainID(); it is surfaced
|
|
// separately so the binding is explicit and survives chains that distinguish
|
|
// the two.
|
|
CChainID() ids.ID
|
|
|
|
// GovernanceController is the DEX governance authority address — the ONLY caller
|
|
// permitted to toggle the 0x9999 settlement kill switches (setHaltGlobal/Market/
|
|
// Asset) and seed the settlement pots. It is a per-NETWORK governance CONTRACT
|
|
// (Governor/Timelock/multisig) resolved by the host from its deployment topology,
|
|
// supplied here through the SAME runtime seam as CChainID/DChainID — ZERO per-net
|
|
// config file. It is NEVER an EOA derivable from a dev mnemonic.
|
|
//
|
|
// The zero address means NO governance authority is configured on this network;
|
|
// the calling precompile MUST then treat every halt/seed call as fail-closed
|
|
// (revert), so an unset authority can DoS no one — there is no single admin key
|
|
// that could halt the DEX. A halt is governance, never a hardcoded key.
|
|
GovernanceController() common.Address
|
|
|
|
// DChainID is the D-Chain (dexvm) blockchain id the C<->D atomic seam routes
|
|
// objects to/from, resolved by the host from the chain topology (the consensus
|
|
// context's blockchain-alias lookup of "D"). It is ids.Empty on a network with
|
|
// no dexvm deployed; the calling precompile MUST revert in that case rather than
|
|
// guess a peer (the on-ramp is honestly closed until D exists). This is the
|
|
// runtime-resolved replacement for a per-network configured D target — the DEX
|
|
// settlement seam is always-on with ZERO per-net config, and the D peer is a
|
|
// deterministic property of the running network, identical on every validator.
|
|
DChainID() ids.ID
|
|
|
|
// TxID is the id of the transaction currently executing this precompile call.
|
|
// Combined with CallIndex it makes a per-call cross-chain object id injective.
|
|
TxID() ids.ID
|
|
|
|
// CallIndex is this precompile invocation's index within the current tx
|
|
// (0-based, monotonic). Two invocations in one tx get distinct indices so their
|
|
// derived cross-chain object ids never collide.
|
|
CallIndex() uint32
|
|
}
|