28 Commits
Author SHA1 Message Date
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
zeekay 3da812d7d9 contract: an unanchored signature regex accepts a selector no caller sends
CalculateFunctionSelector keccaks the whole string, so a signature carrying
stray whitespace or a copy-paste artefact yields a selector nobody will ever
send. The regex matched a substring, so the panic that exists to catch exactly
that never fired. Anchored. Every caller is a package initialiser over a
literal, so this is a boot failure on a constant, unreachable from calldata.

RequireGasLimit's nil check was dead: a nil interface satisfies no interface,
so the assertion below already answered it.
2026-08-28 15:39:14 -07:00
zeekay fe4430cec6 dex, fhe, contract: a 256-bit word narrowed to a machine integer without saying so
big.Int.Uint64 and .Int64 are documented "if x cannot be represented, the result
is undefined". On calldata that is not a lossy conversion to reason about, it is
a value substitution, and the substitution happens twice in series: Uint64 drops
everything above bit 63, and the Go conversion that follows it -- uint24(x),
which is uint32(x) because uint24 is an alias -- drops everything above bit 31.
A reader who checks only the first concludes the site is safe.

contract.Unsigned and contract.Signed refuse a word that does not fit its
declared width, once, where the bytes become a value. Six production sites use
them; nothing downstream has to remember which of its arguments were bounded.

DecodePoolKey read fee and tickSpacing as 24-bit fields and enforced neither.
Above 2^32 the narrowing substituted: a tickSpacing word of 2^32+6 arrived as 6,
and 2^32+60 produced the same pool id as 60. Between 2^24 and 2^32 the field held
the caller's value faithfully while EncodePoolKeyABI wrote only its low three
bytes, so Fee 3000 and Fee 2^24+3000 hash to the SAME id -- PoolKey.ID is not
injective over its own Go type, and the decoder handed back a key whose fields
are not the ones its id was derived from. The encoder is unchanged; changing it
renames every pool that exists. What changed is that no such key can be built
from calldata, which empties the collision class at the only boundary that
produces PoolKeys from untrusted bytes. Same bound on the two tick fields in
DecodeModifyLiquidityInput and decodePMLifecycle.

Three engines narrowed INSIDE the comparison meant to bound the value, so the
check did not merely miss, it inverted. A margin removal to 2^64x leverage read
as leverage zero and passed; a perp open at 2^64 in hundredths did the same;
increasePosition wrapped the quotient into a uint32 field that then set the
liquidation price. Leverage is now compared at full width. CalculateFee clamped
AFTER narrowing, so a violent enough move came out cheaper than a calm one; it
clamps in 256 bits and narrows once the clamp proves it fits. GetTimeToRepayment
saturates rather than wrapping a horizon into a small number of blocks.

fhe declares euint128/160/256 and loads them through EncryptUint64, whose bit
loop reads value>>i off a uint64 -- zero for every i above 63. The container is
genuinely wider (tfheRandom fills all 256 bits), so the missing bits were not
rounded away, they were encrypted AS ZERO: measured, asEuint256(2^200) decrypted
to 0, and asEaddress on two addresses sharing their last eight bytes produced the
same plaintext, so a homomorphic equality over encrypted addresses answered true
for two different accounts. Casts to a narrower type still truncate, matching
Solidity and the existing asEuint4/8/16/32 masks; what is refused is a plaintext
wider than the loader, which is not a cast but a value the encryptor cannot
carry. Widening it needs an EncryptBig upstream -- BitCiphertext's fields are
unexported and it has no constructor from bits. Same bound on the scalar ops,
once ahead of the switch rather than five times inside it.

Consensus-visible: words that decoded now refuse. Fee or tickSpacing wider than
24 bits, a tick wider than int24, an FHE plaintext or scalar wider than 64 bits.
Every one of them previously resolved to a different value than the caller sent.

Four prior lanes had pinned three of these as characterisation tests naming
themselves DEFECT, each instructing that the test be inverted when the bound
arrived; they are inverted here rather than deleted, so the history of why stays
legible. 24 of 24 mutations caught, no survivors. contract 100.0%, dex 99.1 ->
99.2. Both CGO settings, -count=1, -count=2 and -race.
2026-08-28 15:08:29 -07:00
zeekay 414aa8fa29 precompiles: parse calldata through one cursor, answer with one verdict
Class 1. Every parser walked its wire format with hand-written slice
expressions, and the reason that is dangerous is not the one written in the
old fixtures' comments. A precompile's input is the two-index slice
Memory.GetPtr returns; nothing on the path to Run copies it. len is what the
caller declared and paid for, cap is the rest of EVM memory, and the caller
MSTOREd it. So s[a:b] past len does not panic and does not halt a validator --
it returns bytes the attacker chose and the verifier answers over them.
Reverting one cursor read in frost and declaring the input one byte short
returns verdict word 1 over a byte nobody sent: an accept, not a crash.

contract.Cursor bounds every read against len, caps what it returns so a field
cannot be re-sliced forward, and refuses a declared count before it sizes
anything -- Fields divides rather than multiplying, because n*w wraps and a
wrapped product compares as small. STARKVerifyPrecompile did make([]uint64, n)
for an attacker-chosen uint32, up to 34 GB, and only then checked.

Class 2. (bool, error) has four states and three meanings: (false, nil) is
both "verified and rejected" and "nothing ran". That collision is why one
verifier could fold its rounds, discard the result, return true, and read as
ordinary code. contract.Verdict carries the missing bit, so its zero value
denies and a refusal is distinguishable from a rejection. A positive can only
come from Checked, and Checked(true) fails the build -- reintroduced in
fflonkVerify, the compiler and go vet both pass it, the gate names the line.

Nine parse functions across eight packages carry zero hand-written bounds now.
The class rests on two checks in Cursor; deleting either fails every suite,
including with fixtures degraded back to plain append -- which is what the
class used to depend on.
2026-08-28 12:10:59 -07:00
zeekay 9cc8cc7bb7 contract: one cursor holds every calldata bound, one verdict cannot be faked
A precompile's input is the two-index slice Memory.GetPtr returns, and
nothing on the path to Run copies it. len is what the caller declared and
paid for; cap is the rest of EVM memory, which that same caller filled with
MSTORE. So a parser reaching past its input does not panic and does not halt
a validator -- it reads bytes the attacker chose and answers over them.
Measured, not assumed: TestCalldataCarriesCallerChosenSpareCapacity drives
the VM and reads back len=8 cap=256 with 0xA5 past the end.

Cursor bounds every read against len, never against cap and never by letting
a slice expression refuse, and caps what it returns so a field cannot be
re-sliced forward. Verdict carries the bit (bool, error) was missing -- did a
verifier run -- so "nothing checked this" stops borrowing the spelling of
"this failed", and its zero value denies. Poisoned is the one definition of
the adversarial fixture: append-built input has spare capacity too, but
zeroed, so an over-read reads as a harmless run of zeros.

p3q parses through the cursor. Its six hand-written bounds become zero; the
class now rests on the single check in Cursor.Bytes, and deleting that check
fails both suites -- including with fixtures degraded back to plain append,
which is what the class used to depend on.
2026-08-28 11:47:04 -07:00
zeekayandHanzo Dev 2537155c73 contract: drop AtomicStager
An interface with no implementation and no caller. The staging capability it
described is the LP-311 stateless-conduit design, which has never landed on
main: nothing here asserts to it, nothing satisfies it, and no build or CI step
names it.

It is still used by the unmerged dex/stateless-conduit-carry-fills branch, which
carries its own copy of this file. That branch has to restore or re-land the
interface with the conduit that needs it — main does not hold a contract for
code that is not here.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-07 16:37:35 -07:00
zeekay b313c73bfc feat(precompile): enable-everything builder surface — drop strict-PQ refusal from verify-only precompiles
Public permissionless launch policy: enable basically every precompile for
builder convenience, ESPECIALLY wallet-curve VERIFY so users sign natively on
Lux from other chains (ed25519=Solana, sr25519=Polkadot, secp256r1=WebAuthn,
secp256k1/ecrecover=Ethereum). Disable ONLY actual security risks. Lux's own
consensus and identity stay PQ (quasar/p3q) — enforced in the consensus
layer, never by refusing an EVM verifier a dapp asked for.

Two layers, decomplected:
  - builder EVM precompile surface : enable-all-verify  (this change)
  - chain consensus / finality     : PQ-strict          (consensus module, untouched)

Removed the RefuseUnderStrictPQ gate from 17 verify-only / key-safe custom
precompiles: ed25519, sr25519, secp256r1, bls12381 (EIP-2537, x7 ops),
kzg4844 (EIP-4844), blake3, poseidon, pedersen, babyjubjub, pasta, ring,
vrf, hpke, curve25519, x25519, cggmp21, frost, and the classical SNARK
verifiers in zk (Groth16/PLONK/Halo2/KZG/IPA/range/batch/commitment).

zk fflonk (0x03) stays DISABLED — but on its OWN forge-bug mechanism
(ErrFflonkDisabled, returned at dispatch), NOT strict-PQ: verifyFflonk has a
nil-vk soundness hole that forges any statement. Security disable, fully
PQ-independent.

The RefuseUnderStrictPQ helper + ErrClassicalForbiddenInPQ + StrictPQReporter
had zero remaining code callers (evm uses a local structural interface) —
deleted contract/strict_pq.go and its test. Rewrote zk's gate test to assert
the new policy (classical ops enabled, fflonk disabled). Removed the now-
orphaned isPedersenCommitment; fixed stale comments referencing deleted symbols.

Build: full module green. Tests: contract + zk + all 17 edited packages pass.

NOTE: the STANDARD eth precompiles (ecrecover, p256Verify, sha256, ripemd160,
blake2f, bls12381, kzg) are still refused by LuxStrictPQ() in the evm plugin —
a follow-up commit flips that to Permissive so ecrecover (every Ethereum dapp)
works at launch.
2026-06-27 21:00:43 -07:00
zeekay 7ae03fe157 feat(precompile): enable-everything builder surface — drop strict-PQ refusal from verify-only precompiles
Public permissionless launch policy: enable basically every precompile for
builder convenience, ESPECIALLY wallet-curve VERIFY so users sign natively on
Lux from other chains (ed25519=Solana, sr25519=Polkadot, secp256r1=WebAuthn,
secp256k1/ecrecover=Ethereum). Disable ONLY actual security risks. Lux's own
consensus and identity stay PQ (quasar/p3q) — enforced in the consensus
layer, never by refusing an EVM verifier a dapp asked for.

Two layers, decomplected:
  - builder EVM precompile surface : enable-all-verify  (this change)
  - chain consensus / finality     : PQ-strict          (consensus module, untouched)

Removed the RefuseUnderStrictPQ gate from 17 verify-only / key-safe custom
precompiles: ed25519, sr25519, secp256r1, bls12381 (EIP-2537, x7 ops),
kzg4844 (EIP-4844), blake3, poseidon, pedersen, babyjubjub, pasta, ring,
vrf, hpke, curve25519, x25519, cggmp21, frost, and the classical SNARK
verifiers in zk (Groth16/PLONK/Halo2/KZG/IPA/range/batch/commitment).

zk fflonk (0x03) stays DISABLED — but on its OWN forge-bug mechanism
(ErrFflonkDisabled, returned at dispatch), NOT strict-PQ: verifyFflonk has a
nil-vk soundness hole that forges any statement. Security disable, fully
PQ-independent.

The RefuseUnderStrictPQ helper + ErrClassicalForbiddenInPQ + StrictPQReporter
had zero remaining code callers (evm uses a local structural interface) —
deleted contract/strict_pq.go and its test. Rewrote zk's gate test to assert
the new policy (classical ops enabled, fflonk disabled). Removed the now-
orphaned isPedersenCommitment; fixed stale comments referencing deleted symbols.

Build: full module green. Tests: contract + zk + all 17 edited packages pass.

NOTE: the STANDARD eth precompiles (ecrecover, p256Verify, sha256, ripemd160,
blake2f, bls12381, kzg) are still refused by LuxStrictPQ() in the evm plugin —
a follow-up commit flips that to Permissive so ecrecover (every Ethereum dapp)
works at launch.
2026-06-27 21:00:43 -07:00
zeekay bd66095726 dex: LP-311 stateless 0x9999 conduit + host AtomicStager seam + real-StateDB reap-noop proof
Decomplect step 1 (LP-311 §5, the gate to live). The 0x9999 settlement conduit
drifted into a SECOND venue: it kept an order book + a per-asset/per-account ledger
+ atomic-staging records in its OWN account storage. Because 0x9999 is AlwaysOn and
dispatched by address, an ERC-20-only deposit leaves it EMPTY (nonce==0 ∧ balance==0
∧ codeHash==∅; storage is NOT part of EIP-158 empty()), so geth Finalise(true) reaps
the account AND its storage -> seamReserve stranded, zero fill. The shipped "fix" was
a nonce marker on 0x9999 (preserves the complect).

This adds the LP-311 STATELESS CONDUIT that dissolves the hazard at its cause:

- contract/atomic_stager.go: AtomicStager optional host capability (StageExport/
  ConsumeImport). The conduit moves cross-chain value objects through the HOST's
  non-empty staging account, never its own (reapable) account.

- dex/conduit.go: conduitForwardCredit / conduitApplyRelease. Escrow is native
  balance or the TOKEN's trie; the ledger + book live on the D-Chain venue; cross-
  domain objects are staged via AtomicStager. ZERO durable SetState(0x9999). The
  nonce marker is unnecessary and unused on this path. Reuses the proven C<->D object
  codec (native_wire.go), DeriveIntentID, and the raw escrow helpers (native balance /
  pullERC20Terminal under-delivery guard).

- dex/conduit_reap_test.go: the INVERSION of native_zap_realstate_test.go over a REAL
  *state.StateDB (EIP-158 on) + REAL cross-chain atomic.SharedMemory + a minimal-but-
  real D-Chain ledger venue. Drives ERC-20 deposit -> D-credit -> withdraw and proves:
  0x9999 holds ZERO own storage, is reaped at Finalise(true) as a NO-OP, escrow (token
  trie) + claim (D ledger) intact + withdrawable, conservation holds (no mint/loss).
  Plus the crisp RED/GREEN discriminator: same record reaped on empty 0x9999, survives
  on the non-empty host account.

Tests: TestConduit_OwnStorageReapIsNoOp_RealStateDB,
TestConduit_ReapDiscriminator_HostAccountVsConduit — GREEN (GOWORK=off CGO_ENABLED=0);
full dex suite green (no regressions). SYNC carry-fills no-fork primitive verified
green in chains/dexvm (TestRED_PerValidatorRelay_SplitsConsensus: identical roots, 0
venue calls at Verify).

Forward-only, patch-bump cascade. Old braided money path retained pending cutover
(repoint dispatch + delete C-side ledger -> D, embedded-venue deletion).
2026-06-27 16:59:13 -07:00
zeekay 6ea3143281 dex: LP-311 stateless 0x9999 conduit + host AtomicStager seam + real-StateDB reap-noop proof
Decomplect step 1 (LP-311 §5, the gate to live). The 0x9999 settlement conduit
drifted into a SECOND venue: it kept an order book + a per-asset/per-account ledger
+ atomic-staging records in its OWN account storage. Because 0x9999 is AlwaysOn and
dispatched by address, an ERC-20-only deposit leaves it EMPTY (nonce==0 ∧ balance==0
∧ codeHash==∅; storage is NOT part of EIP-158 empty()), so geth Finalise(true) reaps
the account AND its storage -> seamReserve stranded, zero fill. The shipped "fix" was
a nonce marker on 0x9999 (preserves the complect).

This adds the LP-311 STATELESS CONDUIT that dissolves the hazard at its cause:

- contract/atomic_stager.go: AtomicStager optional host capability (StageExport/
  ConsumeImport). The conduit moves cross-chain value objects through the HOST's
  non-empty staging account, never its own (reapable) account.

- dex/conduit.go: conduitForwardCredit / conduitApplyRelease. Escrow is native
  balance or the TOKEN's trie; the ledger + book live on the D-Chain venue; cross-
  domain objects are staged via AtomicStager. ZERO durable SetState(0x9999). The
  nonce marker is unnecessary and unused on this path. Reuses the proven C<->D object
  codec (native_wire.go), DeriveIntentID, and the raw escrow helpers (native balance /
  pullERC20Terminal under-delivery guard).

- dex/conduit_reap_test.go: the INVERSION of native_zap_realstate_test.go over a REAL
  *state.StateDB (EIP-158 on) + REAL cross-chain atomic.SharedMemory + a minimal-but-
  real D-Chain ledger venue. Drives ERC-20 deposit -> D-credit -> withdraw and proves:
  0x9999 holds ZERO own storage, is reaped at Finalise(true) as a NO-OP, escrow (token
  trie) + claim (D ledger) intact + withdrawable, conservation holds (no mint/loss).
  Plus the crisp RED/GREEN discriminator: same record reaped on empty 0x9999, survives
  on the non-empty host account.

Tests: TestConduit_OwnStorageReapIsNoOp_RealStateDB,
TestConduit_ReapDiscriminator_HostAccountVsConduit — GREEN (GOWORK=off CGO_ENABLED=0);
full dex suite green (no regressions). SYNC carry-fills no-fork primitive verified
green in chains/dexvm (TestRED_PerValidatorRelay_SplitsConsensus: identical roots, 0
venue calls at Verify).

Forward-only, patch-bump cascade. Old braided money path retained pending cutover
(repoint dispatch + delete C-side ledger -> D, embedded-venue deletion).
2026-06-27 16:59:13 -07:00
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
zeekay 73f1772a64 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
zeekay 9cfae862cf dex(0x9999): always-on settlement precompile, runtime params, zero per-net config
0x9999 — the native C<->D atomic DEX settlement money path — is now ALWAYS-ON:
present + active on every chain that loads the EVM plugin, from genesis, with NO
dexSettleConfig genesis/upgrade entry. This is the production-launch design; the
per-net config-gate is killed. Forward-perfect, one activation way.

Activation: modules.Module gains an AlwaysOn flag (+ modules.AlwaysOnModules()).
SettleModule sets AlwaysOn=true. The Configurator is retained only to satisfy the
interface; for an always-on module the host never invokes Configure (no activating
config). SettleConfig collapses to a parameter-free {Upgrade} marker that always
Verifies — 0x9999 takes no per-net parameters.

Params resolved at RUNTIME, not config (Configure has no consensus context, so it
cannot resolve them):
  - protocolFeeController: built-in DefaultDAOTreasury (set in init). One canonical
    DAO governance authority across every network; not a compromised dev key.
  - networkID / cChainID: already sourced at runtime via contract.AtomicState.
  - dChainID (THE crux): contract.AtomicState gains DChainID(). The 4 native-client
    sites that read loadDChainTarget(stateDB) now read atomicState.DChainID(), which
    the host resolves from the chain topology (consensus-context "D"/dexvm alias).
    ids.Empty (no dexvm on this network) closes the seam — fail-secure, no mint.

Money-path logic is byte-for-byte unchanged: DeriveIntentID/DerivePositionCommitID,
lock/stage/credit, conservation, exactly-once, rail discriminator, FIX-2 one-batch
flush, nonReentrant/custody guards all untouched. The ONLY change is HOW the
precompile activates and obtains its params (runtime, not config). The Empty-dChain
revert is preserved (strengthened: SubmitSwapIntent/SubmitPositionCommit now revert
on Empty instead of falling back to the C-Chain's own id).

Dead config machinery removed (one way, no dead code): SetSettle{ChainIdentity,
DChainTarget}, loadDChainTarget, loadSettleChainIdentity, cfg{Net,CChain,DChain}IDKey
slots, the STM read-set entries for them, and the now-unused kvAdapter Configure bridge.

Tests: full dex suite 335/335 PASS, 0 fail, 0 skip (CGO_ENABLED=0; the test binary's
only CGO dep is luxfi/accel, unrelated to the pure-Go money path). Harness feeds the
D peer via the runtime AtomicState capability, mirroring production exactly — proof
that the money path is intact with zero config.

go.sum: reconcile e2e threshold v1.9.4 to the origin-canonical hash (upstream re-tag).
2026-06-21 09:03:37 -07:00
zeekay 91539c69bc dex(0x9999): always-on settlement precompile, runtime params, zero per-net config
0x9999 — the native C<->D atomic DEX settlement money path — is now ALWAYS-ON:
present + active on every chain that loads the EVM plugin, from genesis, with NO
dexSettleConfig genesis/upgrade entry. This is the production-launch design; the
per-net config-gate is killed. Forward-perfect, one activation way.

Activation: modules.Module gains an AlwaysOn flag (+ modules.AlwaysOnModules()).
SettleModule sets AlwaysOn=true. The Configurator is retained only to satisfy the
interface; for an always-on module the host never invokes Configure (no activating
config). SettleConfig collapses to a parameter-free {Upgrade} marker that always
Verifies — 0x9999 takes no per-net parameters.

Params resolved at RUNTIME, not config (Configure has no consensus context, so it
cannot resolve them):
  - protocolFeeController: built-in DefaultDAOTreasury (set in init). One canonical
    DAO governance authority across every network; not a compromised dev key.
  - networkID / cChainID: already sourced at runtime via contract.AtomicState.
  - dChainID (THE crux): contract.AtomicState gains DChainID(). The 4 native-client
    sites that read loadDChainTarget(stateDB) now read atomicState.DChainID(), which
    the host resolves from the chain topology (consensus-context "D"/dexvm alias).
    ids.Empty (no dexvm on this network) closes the seam — fail-secure, no mint.

Money-path logic is byte-for-byte unchanged: DeriveIntentID/DerivePositionCommitID,
lock/stage/credit, conservation, exactly-once, rail discriminator, FIX-2 one-batch
flush, nonReentrant/custody guards all untouched. The ONLY change is HOW the
precompile activates and obtains its params (runtime, not config). The Empty-dChain
revert is preserved (strengthened: SubmitSwapIntent/SubmitPositionCommit now revert
on Empty instead of falling back to the C-Chain's own id).

Dead config machinery removed (one way, no dead code): SetSettle{ChainIdentity,
DChainTarget}, loadDChainTarget, loadSettleChainIdentity, cfg{Net,CChain,DChain}IDKey
slots, the STM read-set entries for them, and the now-unused kvAdapter Configure bridge.

Tests: full dex suite 335/335 PASS, 0 fail, 0 skip (CGO_ENABLED=0; the test binary's
only CGO dep is luxfi/accel, unrelated to the pure-Go money path). Harness feeds the
D peer via the runtime AtomicState capability, mirroring production exactly — proof
that the money path is intact with zero config.

go.sum: reconcile e2e threshold v1.9.4 to the origin-canonical hash (upstream re-tag).
2026-06-21 09:03:37 -07:00
zeekay 49fe03803c dex: native C<->D atomic settlement seam (0x9999) + Red fixes 2/3/4
The 0x9999 money path: V4 ABI swap -> NativeDChainClient -> C staged ->
shared-memory export/import -> D dexvm atomic import/export (Mode A async,
no BLS). C SETTLES, C never matches. Value crosses ONLY as a primary-network
atomic shared-memory object. The BLS cert / VerifierRegistry / DFillReceipt
value path is ripped (quarantined under dex/_deprecated_bls/).

Ship rule (structural, not trust): C credited ONLY by consuming a D->C object
(ImportSettlement); D funded ONLY by consuming a C->D object (SubmitSwapIntent
-> dexvm executeImport). Atomic ops are STAGED in EVM state during execution
(revert-aware) and flushed to shared memory at block accept over the
deterministic parent->current seq window.

contract.AtomicState: the optional host capability a precompile type-asserts to
reach atomic shared memory (the platformvm/dexvm import/export primitive).

Red fixes applied to the seam:

FIX 2 (consensus-liveness chain-halt): native_staging.go FlushAcceptedAtomicOps
already takes a batch; the flush is now one-batch-atomic at the host (see
evm block.go/vm.go) — the SM Apply and the seq-marker advance commit all-or-
nothing, closing the crash-between-Apply-and-marker -> duplicate-Put -> halt
window. Regression: native_haltwindow_test.go (re-accept after a committed
window is a clean no-op; crash-before-commit re-applies cleanly; the marker
must ride the Apply batch).

FIX 3 (conservation blast-radius): native_state.go adds seamReserve[a], the
seam's OWN pot, decoupled from the depositor pot (settleVault) and the maker
pot (makerLockedVault). lockIntentInput funds seamReserve; creditSettlementOutput
checks+debits seamReserve. A settlement credit can never raid a depositor's
claim; a withdraw can never strand a backed settlement. Invariant
realHolding == settleVault + makerLockedVault + seamReserve. Regression:
native_conservation_test.go (both subsystems, same asset).

FIX 4 (reentrancy defense-in-depth): SettleSwap now takes the same single
custody guard slot as deposit/withdraw/modifyLiquidity (enterCustodyKV), so the
whole 0x9999 money surface is single-in-flight and an ERC-20 transfer callback
cannot re-enter. Regression: native_callindex_test.go (callIndex determinism +
intent-id collision-freedom across two swaps + a reverted sub-frame + a
STATICCALL; SettleSwap non-reentrant + guard released for sequential calls).

305 prior + 10 new dex tests pass (CGO_ENABLED=0). Live-EVM-call-tree and
live-crash-injection tests are CI-only (CGO + luxfi/accel LUXCPP).
2026-06-21 00:18:54 -07:00
zeekay 51828edc63 dex: native C<->D atomic settlement seam (0x9999) + Red fixes 2/3/4
The 0x9999 money path: V4 ABI swap -> NativeDChainClient -> C staged ->
shared-memory export/import -> D dexvm atomic import/export (Mode A async,
no BLS). C SETTLES, C never matches. Value crosses ONLY as a primary-network
atomic shared-memory object. The BLS cert / VerifierRegistry / DFillReceipt
value path is ripped (quarantined under dex/_deprecated_bls/).

Ship rule (structural, not trust): C credited ONLY by consuming a D->C object
(ImportSettlement); D funded ONLY by consuming a C->D object (SubmitSwapIntent
-> dexvm executeImport). Atomic ops are STAGED in EVM state during execution
(revert-aware) and flushed to shared memory at block accept over the
deterministic parent->current seq window.

contract.AtomicState: the optional host capability a precompile type-asserts to
reach atomic shared memory (the platformvm/dexvm import/export primitive).

Red fixes applied to the seam:

FIX 2 (consensus-liveness chain-halt): native_staging.go FlushAcceptedAtomicOps
already takes a batch; the flush is now one-batch-atomic at the host (see
evm block.go/vm.go) — the SM Apply and the seq-marker advance commit all-or-
nothing, closing the crash-between-Apply-and-marker -> duplicate-Put -> halt
window. Regression: native_haltwindow_test.go (re-accept after a committed
window is a clean no-op; crash-before-commit re-applies cleanly; the marker
must ride the Apply batch).

FIX 3 (conservation blast-radius): native_state.go adds seamReserve[a], the
seam's OWN pot, decoupled from the depositor pot (settleVault) and the maker
pot (makerLockedVault). lockIntentInput funds seamReserve; creditSettlementOutput
checks+debits seamReserve. A settlement credit can never raid a depositor's
claim; a withdraw can never strand a backed settlement. Invariant
realHolding == settleVault + makerLockedVault + seamReserve. Regression:
native_conservation_test.go (both subsystems, same asset).

FIX 4 (reentrancy defense-in-depth): SettleSwap now takes the same single
custody guard slot as deposit/withdraw/modifyLiquidity (enterCustodyKV), so the
whole 0x9999 money surface is single-in-flight and an ERC-20 transfer callback
cannot re-enter. Regression: native_callindex_test.go (callIndex determinism +
intent-id collision-freedom across two swaps + a reverted sub-frame + a
STATICCALL; SettleSwap non-reentrant + guard released for sequential calls).

305 prior + 10 new dex tests pass (CGO_ENABLED=0). Live-EVM-call-tree and
live-crash-injection tests are CI-only (CGO + luxfi/accel LUXCPP).
2026-06-21 00:18:54 -07:00
zeekay 636478f0aa fix: gofmt -s across repo (CI format check) 2026-06-02 23:27:11 -07:00
zeekay aadb570cbd precompile/fhe: close Mul DoS — re-derive gas at real 12M mainnet limit + activation gate
The pre-2026 adversarial test sized the FHE Mul gas (750_000) against a
30M block gas limit, but the real Lux primary-network C-Chain genesis
gasLimit is 12_000_000 (genesis/configs/mainnet/cchain.json line 16).
At 750k gas/Mul + 12M block limit, 16 Muls fit per tx; at ~78s per Mul
on commodity ARM (Apple M1 Max, measured 2026-06-01), one tx stalls a
validator for >20 minutes against a 2s block-target. Sustained: chain
halt at <0.4 LUX cost.

Fix is two-axis and bench-driven:

1. Re-derive every FHE Gas* constant against measured wall-clock and a
   1ms-per-(12_000-gas) calibration. New GasMul = 936_000_000 (78s ×
   12k); GasAdd = 180_000_000 (15s × 12k); etc. These exceed the 12M
   block-gas-limit, which is the protection: zero Muls fit per block,
   so no DoS surface remains.

2. Add an activation gate in fhe.module.Configure that refuses fheConfig
   when the chain's effective gasLimit < GasMul × MinSafeGasMulRatio.
   The gate uses a new feature-detection interface
   contract.FeeConfigReporter (mirrors the StrictPQReporter pattern):
   ChainConfigs that implement it engage the gate; those that don't
   (cross-chain integrators) remain permissive. The error type is
   contract.ErrFHEUnsafeGasLimit.

Tests:
- adversarial_test.go reworked into three explicit fixtures:
  TestFHEGasCost_Mul_DoSResistance_{30M_Reference,12M_Mainnet,
  20M_PostFeeManager}. Each asserts max ops/block × per-op-ms <= budget.
- module_test.go: 7 activation-gate cases (refuses at 12M/20M/30M;
  permits at >= GasMul; permissive without FeeConfigReporter; nil chain
  config; wrong config type).
- dos_audit.go + dos_audit_test.go: in-tree cross-precompile DoS audit
  table walked by tests at both 12M and 20M gasLimit. ML-DSA, SLH-DSA,
  Magnetar, Pulsar, BLS12-381 pairing, ZK Groth16, FROST, CGGMP21, VRF,
  HQC, P3Q all measured safe; FHE rows priced out (the activation gate
  is what surfaces them explicitly).
- bench_test.go: BenchmarkFHE{Mul,Add}_Uint8 — the canonical
  measurement-derivation hooks. Re-run on each arch to regenerate
  WallClockMs* constants.
- contract/feeconfig_test.go: 8 cases for RequireGasLimit (no reporter;
  nil chainConfig; reporter above/at/below threshold; reporter
  abstains; time-aware feeConfigManagerConfig flip; nil block context).

The e2e/fhe_private_auction test was pinning the OLD gas constants
verbatim; updated to assert the bench-derived calibration so a future
constant drift surfaces as test failure rather than silent regression.

NO CAVEAT. Either the gas is sufficient (gasLimit >= GasMul ×
MinSafeGasMulRatio) OR the precompile refuses to activate. The FHE
precompile is currently in the "refuses to activate" regime on real
mainnet C-Chain (gasLimit=12M < 936M required). Recovery is operator-
side: raise gasLimit via feeConfigManagerConfig OR land a faster TFHE
evaluator (see memory/vulkan_tfhe_pbs_fast_path.md — 20.4x landed,
need 78x to close the gap).
2026-06-01 21:25:19 -07:00
zeekayandGitHub f69b524211 precompile: decomplect strict-PQ gate across every classical Run() (#2)
* precompile: pulsar threshold ML-DSA at LP-4200 0x012204

Adds the Pulsar precompile slot for threshold ML-DSA verification.
Pulsar's wire format is byte-equal to FIPS 204 ML-DSA (Class N1
manifesto), so the verification op dispatches to luxfi/crypto/mldsa
under a Pulsar-specific domain-separation context.

- Address: 0x0000000000000000000000000000000000012204 (LP-4200 unified
  PQCrypto block; companion to ML-KEM 0x012201, ML-DSA 0x012202,
  SLH-DSA 0x012203).
- Three modes: Pulsar-44 / Pulsar-65 / Pulsar-87 mirroring ML-DSA
  Categories 2 / 3 / 5. Production target is Pulsar-65.
- Gas: per-mode base + 10/byte (matches ML-DSA precompile tier).
- Context: lux-evm-precompile-pulsar-v1 to keep on-chain transcripts
  unambiguous vs. single-party ML-DSA at 0x012202.

Tests: 5 passing (address pin, gas table, round-trip across 3 modes,
tampered-signature rejection, invalid-mode rejection). GOWORK=off
go test -count=1 ./pulsar/... is green.

Imports github.com/luxfi/pulsar-m (pre-rename); will be retargeted to
github.com/luxfi/pulsar when the cross-repo move lands.

* precompile/LLM.md: mark Pulsar 0x012204 as IMPLEMENTED

Pulsar precompile now lives in precompile/pulsar/ and dispatches to
luxfi/crypto/mldsa under a Pulsar-specific context. Update the
LP-4200 canonical note to reflect deployment status alongside the
other three PQCrypto slots.

* precompile/zk: rip classical pairing-based opcodes (PQ-only)

Shor breaks Groth16/PLONK/fflonk/Halo2/KZG/IPA/Bulletproofs.
The 0x0900 EVM precompile now exposes only the PQ-safe ops:
hash-based nullifiers (0x21) and Merkle commitment inclusion (0x22).
PQ STARK verification is dispatched via the Z-Chain envelope path
(consensus/protocol/zchain/verify.go), not from this precompile.

* precompile/kzg4844: retire BLS12-381 KZG blob-opening precompile

KZG point-evaluation verifies via the BLS12-381 pairing
e(C - [y]G1, G2) == e(W, [tau - z]G2). The discrete-log assumption
that anchors BLS12-381's security is broken by Shor's algorithm, so
EIP-4844-style blob commitments give zero soundness against a future
quantum adversary. The Lux EVM is PQ-only.

Strip the implementation (gokzg4844, accel GPU MSM, all CPU opcodes).
The module still registers the address 0xB002 so the precompile
registry numbering stays stable, but every call to the precompile
returns the new contract.ErrClassicalForbiddenInPQ sentinel. The
minimum 21000 gas is deducted on every call to prevent the retired
address being used as a zero-cost probe.

Blob availability on the Lux EVM is now provided by hash-based
commitments (Blake3 / Poseidon2) and STARK-validated state roots.

* precompile/zk: strip Go-level classical pairing SNARK verifiers

The Run() path of the 0x0900 ZK precompile only exposes hash-based
nullifier checks and Merkle commitment inclusion, both PQ-safe. The
remaining Go-level methods on ZKVerifier (VerifyGroth16, VerifyPlonk,
VerifyFflonk, VerifyKZG, VerifyRangeProof, VerifyRollupBatch and
their helpers) all dispatched through a BN254 pairing check or a
BLS12-381 KZG opening, both broken by Shor's algorithm.

Strip those methods, their bn256 / kzg4844 / accel-MSM dependencies,
and the rollup-batch types (RollupBatch, RollupConfig, StateTransition,
KZGSetup, BN254P) that only existed to feed the pairing verifier.
Update verifier_test.go to cover only the PQ-safe surface that
remains: NewZKVerifier, RegisterVerifyingKey (Go-side bookkeeping),
nullifier check/spend, confidential pool create/add-commitment,
hash-based Merkle inclusion.

PQ STARK verification continues to be handled by the P3Q backend on
the Z-chain envelope path (consensus/protocol/zchain/verify.go); it
was never wired through this Go struct.

* precompile/babyjubjub: retire BN254-companion twisted Edwards curve

Baby Jubjub is the ZK-friendly twisted Edwards curve aligned to BN254's
scalar field (used by circom / iden3 / Polygon zkEVM / Semaphore /
RLN / EdDSA-on-BN254). Its security reduces to discrete-log on a curve
of BN254 scalar order; Shor breaks that in poly time. Every consumer
of Baby Jubjub is a classical pairing-based SNARK gadget with no PQ
analogue, so the Lux EVM retires the precompile rather than offer a
quantum-broken primitive.

Strip the gnark-crypto twisted-edwards implementation. The module
still registers address 0x0500..0007 so the precompile registry
numbering stays stable, but every call returns
contract.ErrClassicalForbiddenInPQ with 21000 minimum gas deducted.

* precompile/pedersen: retire BN254 Pedersen commitment precompile

The implementation built homomorphic Pedersen commitments over BN254 G1
(C = v*G + r*H, plus a 32-slot vector commitment). Both binding and
hiding reduce to the discrete-log assumption on BN254, which Shor's
algorithm breaks. The Lux EVM is PQ-only and retires the precompile
rather than offer a quantum-broken commitment.

Strip the gnark-crypto BN254 implementation and the 34 deterministic
generators. The module still registers address 0x0500..0006 so the
precompile registry numbering stays stable, but every call returns
contract.ErrClassicalForbiddenInPQ with the 21000 minimum gas
deducted.

PQ-safe commitment schemes on the Lux EVM use hash-based primitives
(Blake3 0x3202, Poseidon2 0x3201) and STARK gadgets on Z-chain.

* Revert "precompile/pedersen: retire BN254 Pedersen commitment precompile"

This reverts commit cc0db71edf.

* Revert "precompile/babyjubjub: retire BN254-companion twisted Edwards curve"

This reverts commit e666729d6b.

* Revert "precompile/zk: strip Go-level classical pairing SNARK verifiers"

This reverts commit 92e5ac182b.

* Revert "precompile/kzg4844: retire BLS12-381 KZG blob-opening precompile"

This reverts commit 5786c8f57e.

* Revert "precompile/zk: rip classical pairing-based opcodes (PQ-only)"

This reverts commit 555a9b68ff.

* precompile: profile gate + P3Q + classical-primitive integration

Strict-PQ gate (contract.RefuseUnderStrictPQ) at the top of every
classical pairing/DLOG precompile's Run() refuses the call when the
chain's ChainConfig implements StrictPQReporter and reports true at
the current timestamp. Non-Lux chains that integrate Lux precompiles
without wiring StrictPQReporter see the classical primitives execute
normally -- forward perfection on Lux, full compatibility elsewhere.

Wired into kzg4844, zk (classical opcodes only -- Nullifier and
Merkle Commitment stay open), babyjubjub, pedersen, pasta.

P3Q strict-PQ STARK verifier at LP-4200 0x012205. Plonky3 fork with
cSHAKE256 Merkle / Goldilocks field / FRI low-degree test. Verifier
callback registered at node startup; default returns
ErrVerifierNotRegistered. 8 tests pass.

Gate semantics:
  RefuseUnderStrictPQ(nil) → nil  (test-permissive)
  ChainConfig without StrictPQReporter → nil  (non-Lux chains)
  StrictPQReporter false → nil  (pre-strict-PQ activation timestamp)
  StrictPQReporter true → ErrClassicalForbiddenInPQ

Composable, orthogonal, complete. One and only one way.

* precompile: gate every classical Run() via contract.RefuseUnderStrictPQ

Decomplect: profile-gating logic stays in one place (contract.RefuseUnderStrictPQ);
each classical precompile calls it once at the top of Run() and otherwise stays in
its lane. Quasar/P3Q/Pulsar/ML-KEM/ML-DSA/SLH-DSA/Corona/X-Wing/FHE precompiles
are post-quantum and skip the gate.

Wired into:
  bls12381 (7 EIP-2537 ops: G1/G2 add/mul/MSM + pairing)
  ed25519 (signature verify)
  cggmp21 (ECDSA threshold)
  frost (Schnorr threshold)
  ring (LSAG classical schemes; lattice scheme stays open)
  vrf (ECVRF EDWARDS25519)
  sr25519 (Schnorrkel/Ristretto255)
  x25519 (DH)
  curve25519 (raw Edwards25519 ops)
  hpke (classical KEMs; Kyber/X-Wing hybrid stays open)
  blake3 (classical hash)
  poseidon (BN254 algebraic hash)

Already gated upstream: kzg4844, zk (classical opcodes), babyjubjub, pedersen, pasta.

secp256r1 uses the non-stateful Run([]byte) interface (EIP-7212 standard).
Gating that precompile requires hooking the geth EVM precompile overrider, not
this package -- left for the LuxPrecompileOverrider follow-up.

Tests pass across all twelve gated dirs plus contract/.
2026-05-11 15:10:04 -07:00
zeekay e79a887d0e fix: P0-P2 hardening — consensus safety, OOM prevention, error unification
P0 consensus-critical:
- FHE: deterministic keygen from SHA-256("LUX_FHE_KEYGEN_v1"), StateDB
  ciphertext storage (replaces ephemeral in-memory ctStore), remove
  crypto/rand fallback
- time.Now() eliminated from 20 state-modifying paths across bridge (12),
  dex (7), attestation (5) — all now use blockTimestamp parameter
- WebSocket: 1024-client semaphore, 64KB read limit, 60s deadline, pong
  handler, origin check on chain/dag/evm indexer subscribers

P1 correctness:
- I-Chain indexer: identity.Health → identity.getLatestBlock
- Graph pagination: min(limit, 1000) cap across 25 resolver files
- Error sentinels: 31 packages unified to contract.ErrInvalidInput

P2 standardization:
- 14 packages migrated from manual gas checks to contract.DeductGas()
- Gas naming: {Name}Gas → Gas{Name} in secp256r1, ed25519, math, quasar
- Registry address format comment clarified (PCII vs legacy ranges)

37/37 precompile tests pass.
2026-03-02 10:11:20 +00:00
zeekay 0e44530458 chore: remove AI slop, fix stubs, delete dead code
Cleanup:
- Delete docs/ (Next.js site in Go repo)
- Delete 11 package-level README.md files (AI-generated filler)
- Remove orphaned SelectorDonate and runDonate stub from dex
- Remove dead applyChainAdjustment (every branch returned input unchanged)
- Remove unused GetChainId (hardcoded to 96369, never called)
- Remove stale TODOs from fhe/module.go and contract/utils.go

Fixes:
- ai/verifyTEESignature: enforce min 64-byte signature length instead
  of accepting any non-empty bytes (was a security hole)
- ai/ai_mining_test.go: use 64-byte test signature to match new check

All 28 packages pass.
2026-03-02 20:32:47 +00:00
zeekay dde5838640 chore: sync dependencies and format code 2026-02-04 23:48:56 +00:00
zeekay 7ec8994ab0 chore: sync with latest dependencies and add tests
- Update frost, mldsa, mlkem contracts
- Add contract tests
- Add threshold accel support
2026-01-26 17:45:39 +00:00
zeekay acd2aaf533 feat: rename module to github.com/luxfi/precompile
BREAKING CHANGE: Module renamed from precompiles to precompile (singular)

- Remove lattice/v6 indirect dependency
- Exclude lattice/v6 versions to prevent transitive imports
- Only lattice/v7 is used now
2025-12-31 06:28:04 +00:00
zeekay 941c515a08 fix(contract): simplify PrecompileEnvironment interface
The minimal interface only requires ReadOnly() which is sufficient
for stateless precompiles like PQ crypto.

This removes the need for complex adapters in geth.
2025-12-25 08:18:09 +00:00
zeekay 2148cab0ca fix(contract): break import cycle with geth/core/vm
- Define PrecompileEnvironment locally instead of importing from geth/core/vm
- Define ErrOutOfGas locally instead of using vm.ErrOutOfGas
- Update crypto dependency to v1.17.26

This fixes the import cycle: precompiles/contract -> geth/core/vm -> precompiles
2025-12-25 08:15:52 +00:00
zeekay 53c9e7d62c chore: update PQ crypto precompiles to use luxfi/crypto wrappers
- mldsa: use luxfi/crypto/mldsa wrapper
- pqcrypto: use luxfi/crypto for all PQ operations
- corona: update imports
- cggmp21: update imports
- contract: update interfaces and utils
2025-12-25 07:53:12 +00:00
zeekay 16ff682fd7 feat: consolidate all precompiles from standard repo 2025-12-19 23:02:39 +00:00