Commit Graph
519 Commits
Author SHA1 Message Date
zeekayandHanzo Dev a55e3fc1bf fix(evm): bounded-wake backstop so an idle demand-driven chain mines the first tx in seconds
Pre-existing idle-builder wake race (not caused by QCv2): on a demand-driven
C-Chain with no empty blocks, a tx arriving while no builder is parked in
waitForNeedToBuild broadcasts pendingSignal to no waiter and is lost; the tx is
then only caught on the NEXT needToBuild() poll, which — depending on the
proposervm slot loop and engine re-call cadence — could leave the first tx after
idle pending for a long/indefinite time (the mainnet treasury-sweep-stuck and
v1.32.1 devnet DEXFill-didn't-complete symptoms).

Fix: startPendingTxPoll — a node-lifetime ticker (builderPollInterval = 500ms)
that periodically broadcasts pendingSignal so any parked builder re-evaluates
needToBuild() against the live mempool. It is decomplected from the tx-submit
subscription (awaitSubmittedTxs remains the primary, immediate wake) so the two
wake sources are independent. It NEVER forces an empty block — a wake only
produces a build when needToBuild() is true — so demand-driven chains stay
empty-block-free, and it changes wake TIMING only, never block CONTENTS, so
validators still build byte-identical blocks (deterministic). needToBuild now
routes through an injectable pendingTxs predicate so the wake path is testable
without a live txpool.

Regression: TestBuilderWakesOnPendingTxAfterIdle reproduces the node bridge's
exact waitForEvent call on an idle builder, delivers a pending tx with NO submit
broadcast, and asserts it returns PendingTxs within a bounded deadline. It FAILS
on the pre-fix code (times out — the lost wakeup) and passes after.
TestBuilderPollBackstopStopsOnShutdown asserts the ticker exits cleanly. Builder
tests green under -race.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 21:28:45 -07:00
zeekay d096677067 evm: extract nextBuildTime pure helper + regression test for build pacing
Decomplect the build-pacing decision out of waitForEvent into a pure
nextBuildTime(earliestBuild, lastBuildTime) function (no clock read), so the
logic the network-halt fix depends on is deterministically testable. The
wall-clock comparison stays in waitForEvent.

TestNextBuildTime covers all 5 cases (first-build paces to target rate; past
earliestBuild builds immediately; target rate dominates retry floor; retry
floor prevents hot-loop; boundary tie-break). TestNextBuildTime_NeverEarlier
ThanTargetRate locks the invariant the fix relies on: the next build is NEVER
scheduled before parent.Time+TargetBlockRate, so a too-early high-fee block can
never re-introduce the small-tip stall.
2026-06-28 00:52:09 -07:00
zeekay 5c129faa36 evm: pace block building to the target block rate
Second fresh-net block-production bug, exposed once WaitForEvent was unblocked.
The hand-rolled node notification bridge calls WaitForEvent in a tight loop and
builds a block the instant any tx is pending — with no pacing (ava's proposervm,
which Lux does not wrap, normally provides it). Two consequences:

1. Block gas cost: cost = parentCost + step·(TargetBlockRate − elapsed). A block
   built immediately after its parent (elapsed≈0) requires step·TargetBlockRate
   (e.g. 200000·2 = 400000) of tips to pass verifyBlockFee. A large deploy
   covers it; a single small transfer (~100000) cannot → BuildBlock fails →
   the tx never mines.
2. Equivocation: with all 5 validators building instantly off slightly different
   mempool snapshots, they produce different blocks at the same height; two can
   each gather an α-of-K cert → conflicting finality certs → Logger.Crit → the
   node exits(1) and crash-loops.

Fix: pace waitForEvent to parent.Time + TargetBlockRate (the point at which the
block gas cost decays back to the parent's level, 0 from genesis), mirroring the
timing ava's coreth builder applies. Small-tip txs now mine, and the ~rate-long
window lets the mempool gossip-converge so validators build the IDENTICAL block
(same ID, one cert) instead of racing to finalize conflicting blocks. Retry
floor 1ms→100ms so a failed BuildBlock cannot hot-loop.
2026-06-28 00:13:45 -07:00
zeekay 8f1ecd61c2 evm: WaitForEvent must not block forever when called before builder init
Fresh-start block-production stall root cause. The node-side notification
bridge (node/chains/manager.go) calls VM.WaitForEvent in a loop with a
non-cancellable context.Background(), starting BEFORE SetState(NormalOp)
initializes vm.builder. The first WaitForEvent therefore parks in the
builder==nil select { ctx.Done | stateSyncDone | shutdownChan } and, since the
context never cancels and state-sync is off, blocks FOREVER -- the serial
bridge loop never re-calls WaitForEvent to observe the now-ready builder, so no
PendingTxs ever reaches the engine and no block is ever built.

ava avoids this with common.NotificationForwarder + cn.OnChange=CheckForEvent,
which cancels the outstanding WaitForEvent and re-subscribes on VM state change.
Lux replaced that with the hand-rolled bridge and lost the recovery path.

Fix (self-contained, VM-side): add builderReady chan, closed once by
onNormalOperationsStarted after vm.builder is set, and add a select case so a
WaitForEvent that parked early re-fetches the ready builder and proceeds.
Makes WaitForEvent correct for any caller, cancellable or not.

go.sum: re-record luxfi/age,pq checksums after upstream re-tag (verify ON).
2026-06-27 23:28:37 -07:00
zeekay 5460f8370e feat(evm): enable standard precompiles — remove vestigial strict-PQ refusal layer
Enable-everything builder surface (public permissionless launch). The geth-layer
strict-PQ profile (LuxStrictPQ) refused ecrecover / p256Verify / sha256 /
ripemd160 / blake2f / bls12381 / kzg under vm.config.PQ — but ecrecover
(secp256k1) is THE Ethereum signature path every dapp depends on, and the rest
are verify-only / hash, key-safe. Refusing them breaks the builder surface.

The chain now installs NO forbidding pq.Profile: chainConfig.PQ stays nil,
which pq.RefuseUnder(nil) treats as permissive. The generic geth mechanism
(chainConfig.PQ + runPrecompile.RefuseUnder + legacypool nil-check) is kept for
any chain that wants it; Lux installs nothing.

This completes the decomplect started in luxfi/precompile: consensus PQ and the
EVM precompile profile were complected through config.PQ / StrictPQTimestamp.
After precompile dropped contract.RefuseUnderStrictPQ from every verify-only
precompile, the whole strict-PQ projection here was dead. Removed:
  - plugin/evm/pq_profile.go (LuxStrictPQ)
  - plugin/evm/vm.go: the `if vm.config.PQ { chainConfig.PQ = LuxStrictPQ() }`
    install + the unconditional StrictPQTimestamp=0 genesis pin
  - plugin/evm/config: the config.PQ bool field (no code writer; was JSON-opt-in)
  - params/extras/network_upgrades.go: StrictPQTimestamp field + apply + IsStrictPQ
  - precompile/registry/bridge.go + core/precompile_overrider.go: the dead
    IsStrictPQ adapters (only existed to feed the deleted StrictPQReporter)
  - tests: pq_test.go, strict_pq_activation_test.go, and the two StrictPQ
    assertions (+ orphaned helper) in precompile_upgrade_rollout_test.go

Lux's PQ security is enforced at consensus / identity (quasar/p3q), not by
refusing an EVM verifier a dapp asked for. The live lux-mainnet upgrade.json
never actually set strictPQTimestamp, so this matches production.

Build: full module green. Tests: params/extras, plugin/evm/config,
precompile/registry pass.
2026-06-27 21:14:05 -07:00
zeekay 2bc16e3494 refactor(warp): Core -> Message — track warp v1.22.0
Rename across the evm warp backend and the warp precompile: warp.Core ->
warp.Message, NewCore -> NewMessage, ParseCore -> ParseMessage, the .Core field
-> .Message, and local core/msgCore/testCore vars -> msg/testMsg. deps: warp
v1.21.0 -> v1.22.0, precompile v0.11.0 -> v0.12.0.

geth's core/types and core/vm imports are untouched — the rename is scoped to
the warp message type, never the geth core package. ./warp/... and
./precompile/contracts/warp/... green, including TestWarpWrongNetworkID.
2026-06-27 00:48:35 -07:00
zeekay a6b22c9824 refactor(warp,precompile)!: decomplect ZAP types — warp v1.21.0, precompile v0.11.0
Track the upstream rename across the evm warp backend
(warp/{service,backend,verifier_backend}) and the warp precompile
(precompile/contracts/warp/{config,contract,contract_warp_handler},
precompileconfig): SignedCore->Core, WarpEnvelope->Envelope.

Identifiers only — the digest D, ZAP c14n bytes and BLS Beam / Corona Pulse /
ML-DSA signing domains are unchanged. ./warp/... and
./precompile/contracts/warp/... green, including the 10k-validator predicate
sign->aggregate->verify.
2026-06-26 22:25:27 -07:00
zeekay f9280978e3 feat(warp): migrate evm onto warp v1.20.0 SignedCore/WarpEnvelope ZAP (drop deleted UnsignedMessage RLP API)
Hard-fork the last evm warp consumers onto the canonical ZAP message format
(one and one way only). warp v1.20.0 deleted UnsignedMessage; the signed
subject is now warp.SignedCore (D = keccak256(coreDST ‖ zap_c14n)) and the
signed wrapper is warp.WarpEnvelope. The BLS Beam signs warp.BeamSigningBytes(D).

deps: warp v1.19.5 -> v1.20.0, precompile v0.5.59 -> v0.10.0.

- warp/{backend,verifier_backend,service}: UnsignedMessage -> SignedCore;
  NewMessage -> NewWarpEnvelope; sign/verify over BeamSigningBytes(core.ID()).
- precompile/contracts/warp: ParseMessage -> ParseWarpEnvelope; .UnsignedMessage
  -> .Core; .Signature.(*BitSetSignature) -> .Beam (concrete). VerifyPredicate
  verifies the Beam over D with exported primitives (Bits + bls +
  BeamSigningBytes) because the precompile quorum denominator must include
  keyless validators, which warp.VerifyEnvelope/GetCanonicalValidatorSet reject.
- precompileconfig.WarpMessageWriter.AddMessage(*SignedCore) (mirrors precompile v0.10.0).
- plugin/evm/vm.go: warpVerifierAdapter over *SignedCore; collapse the duplicate
  external-warp import alias (warptypes -> luxwarp).
- remove redundant warp/{aggregator,bls_adapter,signer_adapter}.go: provably-dead
  duplicates of warp.SignatureAggregator / warp.NewSigner (zero production refs).
- tests migrated to ZAP; fixed a pre-existing offchain message-key test bug;
  revived the predicate suite (validator state injected via Runtime, the
  production path, replacing the decoupled WithValidatorState key).

Verified: go build ./... green; go test ./warp/... ./precompile/contracts/warp/...
green (incl. 10k-validator TestWarpPredicate). grep UnsignedMessage = 0.
2026-06-26 21:27:25 -07:00
zeekay cdbc464bfc feat(evm): genesis-active gateless 0x9999 + StrictPQ — first-run, one way, no dated fork
First-run network, no legacy, no pre-activation history to protect, so the
DexSettleActivationTime dated-fork gate is pure complexity. Delete the concept of
an activation time: 0x9999 dispatches unconditionally and its EXTCODESIZE marker
installs at genesis (parentTimestamp==nil); StrictPQ pinned active-from-genesis for
the Lux plugin (whole ecosystem is PQ-native — removed the per-VM config conditional).

Deleted: DexSettleActivationTime const + pointer/accessor/predicates, the cross-repo
mirror, the drift-guard test, and the 'behaves-as-plain-account-before-activation'
replay-safety branches/prose. One activation path; less code. Genesis determinism via
the single core/genesis.go toBlock() derivation. Feature tests green (0x9999 registered/
dispatched/marked-at-genesis; StrictPQ genesis+nil-inert; plugin PQ).
2026-06-26 14:40:00 -07:00
zeekay 7341b4a885 fix(evm): bind chain identity before NewBlockChain reprocess (no new type)
The 0x9999 DEX value path is identity-gated: the asset resolver refuses to
admit a market unless the running chain's identity matches. core.NewBlockChain
re-executes accepted blocks during construction (loadLastState recovery /
populateMissingTries backfill), but the identity was only bound onto the chain
AFTER eth.New returned (vm.go SetConsensusContext) — so an unclean restart after
a 0x9999 swap re-executed that block with networkID 0 / C-Chain Empty, reverted
the committed swap, failed ValidateState, and the node could not boot.

Fix is purely timing: hand NewBlockChain the *runtime.Runtime the VM already
holds (eth.Settings.ChainRuntime -> core.NewBlockChain) and bind the consensus
context BEFORE reprocess. No new identity type — networkID/chainID already live
on runtime.Runtime; the context is just the last-mile transport to geth's
ConsensusContext API. stdlib runtime aliased stdruntime in blockchain.go.
2026-06-23 08:53:26 -07:00
zeekay b80a7d0ffd evm: wire the per-network DEX governance controller into the 0x9999 value path
HIGH-3 host side. The always-on 0x9999 settlement precompile resolves its halt +
pot-seeding authority from contract.AtomicState.GovernanceController(); this is the
production wiring that supplies it — a per-network governance CONTRACT, never a
hardcoded mnemonic-derivable EOA.

  - dex_value_wiring.go (installDEXValuePath): bind rt.GovernanceController =
    dexGovernanceFor(networkID) onto the chain runtime at boot (the ONE production
    site), alongside the asset resolver + native D client, and log it. dexGovernanceFor
    is the genesis-set per-network governance-contract seam: it returns ids.ShortEmpty
    for every network TODAY (no Governor/Timelock is deployed at a known address on any
    Lux network yet), so every network runs FAIL-CLOSED (no halt authority — provably no
    single key can DoS the DEX) until its real governance CONTRACT address is wired in,
    per net. The directive is explicit: each entry MUST be a governance contract, NEVER a
    dev-mnemonic EOA, and NEVER the retired 0x9011…94714. The boot log makes an unset
    authority loud ("<unset: halt/seed FAIL-CLOSED — deploy + wire a governance contract>").
  - core/precompile_overrider.go + precompile/registry/bridge.go: accessibleStateAdapter
    and accessibleStateBridge implement GovernanceController() (ids.ShortID→common.Address,
    exact 20-byte conversion); the internal twin interface gains the method. Returns the
    zero address (fail-closed) when the runtime/atomic capability is absent.
  - precompile/contract/atomic.go (evm-internal twin): AtomicState gains GovernanceController().

Builds clean (CGO off) across plugin/evm, core, precompile/registry; the dexsettle_guard
parity test passes. Normal trading is unaffected (halt switches are emergency controls,
off by default); only the ability to halt is gated — which previously only a mnemonic-EOA
holder had (a vuln), so no legitimate capability is lost and a censorship/DoS vector is removed.
2026-06-22 22:50:19 -07:00
zeekay b0f0787bef feat(evm): wire the 0x9999 value-path asset resolver + native D-client at boot
This is the SOLE production install of the always-on 0x9999 DEX value-path trust
root — the seam Red flagged as fail-closed-by-absence (InstallAssetResolver /
InstallDChainClient had zero production callers, so every swap reverted
ErrNoAssetResolver). vm.Initialize now constructs and installs, from the node's REAL
chain context (never a constant):

- a registry-backed dexcore.AssetResolver bound to (networkID, cChainID): it derives
  the canonical AssetID with the node's own ids and resolves it in a registry built
  from the per-network manifest (EmbeddedManifestFor by EVM chainID, or the runtime-
  rooted localnet native manifest). The manifest is identity-bound via
  NewRuntimeVerifier (wrong-net/wrong-chain refused) and admitted under the locked-down
  DefaultDexAssetPolicy synthetic-config deny-scan.
- the local in-process NativeDChainClient for the book/ledger custody seam (NOT a
  remote keeper/venue/ZAP engine).

Wired for ALL networks (mainnet 96369 / testnet 96368 / devnet 96370 / localnet 1337)
so the DEX is live out-of-the-box. FAIL-CLOSED, NEVER FATAL: a construction failure is
logged and the resolver is left UNINSTALLED (swaps revert), never opened; the node
still boots for all non-DEX usage. The install binds the SAME (networkID, cChainID)
the swap path's AtomicState cross-checks, so a mismatch is fail-closed at swap time.

- core/precompile_overrider.go: forward GetCodeSize (EXTCODESIZE) on the concrete
  stateDBAdapter so the value path's live-code verifier reads authoritative state.
2026-06-22 20:56:52 -07:00
zeekay 8f942b3943 plugin/evm: one-batch-atomic DEX native seam flush (FIX 2 chain-halt)
The host side of the 0x9999 native C<->D atomic seam, and the consensus-liveness
fix Red found.

THE BUG: Block.Accept applied the staged atomic ops to shared memory FIRST
(sm.Apply, no batch -> committed immediately) and advanced the node-local seq
marker LATER, in a separate versiondb.Commit(). atomic.SharedMemory.Apply is NOT
idempotent (duplicate Put errors). A crash BETWEEN them left shared memory
mutated but the marker unadvanced; on restart the same parent->current window
re-collected, sm.Apply re-applied the present Put, errored, and Accept returned
fatal -> the chain HALTED.

THE FIX (mirrors chains/dexvm commitAtomic): flushDexAtomic -> stageDexAtomic
stages the advanced dexAtomicSeqKey marker INTO the versiondb in-memory layer and
returns (reqs, sm). Block.Accept snapshots the versiondb (marker + lastAccepted)
via CommitBatch() and passes it to sm.Apply(reqs, batch) — its WriteAll commits
the shared-memory mutation AND the batch in ONE database write. The marker advance
and the SM mutation are now all-or-nothing: a crash applies-and-marks both or
neither, and a re-accept of an applied window sees an advanced marker -> empty
window -> no duplicate Put -> no halt. With no atomic ops the plain
versiondb.Commit() path is unchanged.

Also: core/precompile_overrider.go wires CallIndex() through the EVM accessible-
state adapter; precompile/registry/bridge.go + precompile/contract/atomic.go
forward the optional contract.AtomicState capability to luxfi/precompile.

Regression for the closed halt window lives in precompile
native_haltwindow_test.go (unit). The live-path test (real versiondb + crash
injection) is CI-only (CGO + luxfi/accel LUXCPP). Local symbol-resolution proof:
CGO_ENABLED=0 go build ./plugin/evm/ compiles clean.
2026-06-21 00:19:47 -07:00
zeekay 9cb5053be8 plugin/evm: remove deprecated DexZap live-backend wiring; precompile v0.5.49
The C-Chain DEX settles certified D-Chain fill receipts at the 0x9999 precompile
(LP-9999); it never queries a live matcher. Remove the deprecated
DexZapEndpoint -> SetBackend(NewZAPEngine()) wiring from vm.go and the
DexZapEndpoint config knob: a synchronous in-block ZAP query against the
D-Chain's moving book forks consensus (each validator observes differently-timed
fills -> divergent StateRoot, proven by chains/dexvm
TestRED_PerValidatorRelay_SplitsConsensus). ZAP survives only as the dexvm's own
build-path transport, never as a C-Chain live backend. Receipts ride in V4 swap
hookData and are verified inline (deterministic BLS).

Bump github.com/luxfi/precompile v0.5.47 -> v0.5.49 to embed the complete,
red-swarm-hardened 0x9999 receipt-settlement surface.
2026-06-20 14:57:25 -07:00
zeekay 7e079e9ac1 evm: install bn256-carve-out strict-PQ profile; prove StrictPQ gate active (Red H2 + guardrail)
Task 2 (Red H2): StrictPQTimestamp was wired from config.PQ (vm.go) but the
gate was unproven inert. The canonical lux-mainnet config already sets
pq:true and upgrade.json sets strictPQTimestamp=Quasar-TS; added behavioral
tests proving NetworkUpgrades.IsStrictPQ activates (genesis when pq:true ->
StrictPQTimestamp=0; at the timestamp on running chains; Override carries it).

GUARDRAIL fix: vm.Initialize installed gethvm.AllForbidden() (=pq.Strict),
which forbids the standard bn256 precompiles 0x06-0x08 — breaking
Ethereum-compat dapps. Now installs the EVM-owned LuxStrictPQ() projection
(pq_profile.go): every classical family forbidden EXCEPT bn256. Lux's
security-critical pairing/DLOG is in CUSTOM precompiles (0x0900, 0x22
Pedersen, zkvm verifiers) + the consensus cert, each gated by the SAME
profile bit (config.PQ also pins StrictPQTimestamp=0 -> RefuseUnderStrictPQ).

Tests (b): IsStrictPQ true on a prod strict chain (genesis + timestamp +
Override paths), false when nil. (f): LuxStrictPQ admits bn256 0x06-0x08 and
still refuses every other family; differs from AllForbidden only by bn256.
2026-06-19 13:37:37 -07:00
zeekay c0f265e47e evm: wire dex-zap-endpoint -> SetBackend(NewZAPEngine) so 0x9010 routes to the venue D-Chain
config.go: DexZapEndpoint (node-local + version-pinned consensus-safety invariant). vm.go: when
set, SetBackend(NewZAPEngine(endpoint,2s)) BEFORE parseGenesis; INERT when empty (public default
= embedded engine, unchanged). Resolves against published luxfi/[email protected], no replace.
Builds CGO on+off; config decode test added.
2026-06-17 17:26:12 -07:00
zeekay edce485ac1 evm: codec rip — message.Manager + validators state hand-rolled binary
Salvages an in-flight Wave 1C cascade on evm/ (the EVM fork separate
from coreth and evmgpu). 17 files touched. Mirrors the coreth
migration patterns:

- plugin/evm/message/codec.go: defines local Manager interface with
  hand-rolled wrappers.Packer; drops codec.Manager polymorphic registry.
- plugin/evm/message/request.go: typed Marshaler boundary.
- sync/{client,handlers}/*.go: codec.Manager → message.Manager swap.
- plugin/evm/customrawdb/*: explicit big-endian for state sync schema.
- plugin/evm/validators/state/{codec,state,state_test}.go: hand-rolled.
- plugin/evm/upgrade/feewindow/window.go + plugin/evm/vm.go +
  network/network.go + plugin/evm/network_handler.go: call-site fixups.
- precompile/registry/registry.go: codec-free registration path.

Builds clean: go build ./...
2026-06-05 15:31:59 -07:00
zeekay 727493f6e7 plugin/evm: harden admin_exportChain with idempotency + sentinel (Task #118)
Previous ExportChain returned (bool, error) and would re-export from
scratch every call, leaving partial files with no marker on Ctrl-C.

Now returns *ExportChainResult with first/last block hashes and a Status
field ("ok" | "noop" | "interrupted"). Behavior:

  * Atomic JSON sentinel at <file>.sentinel (tmp+rename) updated every
    1024 blocks and on terminal states.
  * Idempotency: if file exists with a "done" sentinel matching
    {first, last, firstHash, lastHash}, returns Status=noop without
    re-exporting. Safe to call from a cron CronJob.
  * Interrupt: ctx.Done() at every block boundary writes a sentinel
    with Status=interrupted + HighestExported so the caller can resume.
  * Block iterator still streams (no whole-chain buffering); gzip path
    unchanged.

Symmetric with admin_importChain which returns *ImportChainResult, and
with the cmd/rlp-export CLI shipping alongside.

Server-side test coverage: writeSentinel atomicity, readSentinel
absent + corrupt fall-through, JSON shape pinning for both the
sentinel and ExportChainResult, exportProgressInterval stability.
2026-06-05 15:30:41 -07:00
zeekay cf5b006f11 plugin/evm: enforce DisallowUnknownFields on upgradeBytes parse
Quasar Edition rip: there is one and only one upgrade-key namespace.
The strict decoder forces parse-time failure on any unknown field —
which catches typos and stale aliases (e.g. etnaTimestamp) at config
roll instead of silently muting forks at activation.

Why this matters: encoding/json by default ignores unknown JSON keys
and zero-initializes the corresponding struct fields. After the
EtnaTimestamp → QuasarTimestamp rename, any deployed upgrade.json
still carrying etnaTimestamp would parse cleanly with the new field
left nil, and the fork would never activate. The strict decoder
turns that silent-disable failure mode into a loud boot-time error.

Pairs with: luxfi/genesis kill-etna sweep + node v1.28.22 plugin bump.
2026-06-03 11:51:13 -07:00
zeekay c7c4b96b0f rename: Etna → Quasar Edition
Wire-level renames in EVM:
  - extras.NetworkUpgrades.EtnaTimestamp → QuasarTimestamp
    json tag etnaTimestamp → quasarTimestamp
  - extras.NetworkUpgrades.IsEtna()      → IsQuasar()
  - extras.LuxRules.IsEtna               → IsQuasar
  - params.RulesExtra.IsEtna             → IsQuasar
  - extras.TestEtnaChainConfig           → TestQuasarChainConfig
  - params.TestEtnaChainConfig           → TestQuasarChainConfig
  - upgradetest.Etna fork enum           → Quasar (luxfi/upgrade dep)

Test JSON fixtures (mainnet_upgrade_v46, testnet_upgrade_v47, embedded
genesis_skippostmerge_test, precompile_upgrade_rollout_test) flipped from
"etnaTimestamp" → "quasarTimestamp".

Also patched .disabled test files for grep cleanliness so future grep -rn
'Etna' returns 0 outside historical RELEASES.md.

Bumps luxfi/upgrade@main + luxfi/constants@main with their renamed symbols.
No backcompat aliases — forwards-only.
2026-06-02 23:01:55 -07:00
zeekay 3d0966555f fix(plugin/evm): force HashScheme default to stop fresh L2s wedging on pathdb
On lux-mainnet 2026-06-02 every newly-created L2 EVM chain (hanzo, zoo,
spc, pars) panicked at chain create with:

  panic in eth.New: unable to commit genesis block to statedb:
    triedb parent [0x56e81f17171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421] layer missing

Root cause: the operator does not specify state-scheme in chain config, so
vm.ethConfig.StateScheme = "" enters eth.New. backend.go then calls
customrawdb.ParseStateSchemeExt("", chainDb) which delegates to
geth/rawdb.ParseStateScheme — upstream returns PathScheme as the implicit
default for any database with no stored scheme (every fresh L2). That
silently re-enables path mode even though vm.go's preflight at the top
of Initialize hard-refuses it. Genesis commit then runs through pathdb's
Update with parentRoot=EmptyRootHash and dies the moment the layer tree
is consulted.

Fix: extract the scheme normalisation into resolveStateScheme() and lock
the empty-string default to HashScheme here, before backend.go can inherit
geth's path-by-default. Path explicit is still refused as before, so this
is purely an operator-default fix.

Adds:
- plugin/evm/vm_statescheme_test.go: TestResolveStateScheme covering all
  four input cases (empty→hash, hash passthrough, path refused, custom
  passthrough).
- core/genesis_pathdb_test.go: TestGenesisCommit_PathDB_{FreshDB,
  SetupGenesisBlock_FreshDB} document the upstream pathdb genesis-commit
  invariant on a fresh in-memory DB so future drift here is caught
  immediately.

Also bumps compatibility.json to register v0.18.4-v0.18.18 with the
existing RPC chain VM protocol (42); restores TestCompatibility (which
was failing on main since v0.18.4 because the table was never updated).

Version: v0.18.17 -> v0.18.18.
2026-06-02 11:53:21 -07:00
zeekay 6e25b35864 evm v0.18.17: go mod tidy after genesis v1.13.8 cascade
luxfi/genesis is now indirect (no direct EVM imports), but tidy pulled in
fresher minimum versions of its transitive deps:

- constants v1.5.2 -> v1.5.7
- crypto v1.19.6 -> v1.19.15
- metric v1.5.1 -> v1.5.5
- precompile v0.5.23 -> v0.5.27
- otel v1.42.0 -> v1.43.0

Plus drops StephenButtolph/canoto, fatih/structtag, grpc-prometheus from
indirect block (no longer transitively required).

No functional changes; this is purely the dependency cleanup that follows
the luxfi/genesis v1.13.8 bump.
2026-06-02 04:25:49 -07:00
zeekay c116794daa release: bump Version to v0.18.16 (matches semver tag) 2026-06-02 01:04:55 -07:00
zeekay 4eecb66098 evm v0.18.16: fix nil-chainConfig panic at PQ gate + RLP verifier tool
Bug fix (closure-swarm v1.28.15 boot blocker):
PQ gate at plugin/evm/vm.go was setting vm.chainConfig.PQ BEFORE
vm.chainConfig was assigned from g.Config — chainConfig was nil at
that point, panic on dereference. Moved the gate AFTER assignment.

Repro: fresh-PVC bootstrap with chain-config.json pq:true caused
"runtime error: invalid memory address or nil pointer dereference"
in the EVM ZAP plugin → C-Chain never bootstraps. Discovered during
localnet 1337 bring-up against v1.28.15.

Fix: move the gate to AFTER `vm.chainConfig = g.Config`. PQ profile
is now correctly installed on a non-nil chainConfig.

Added:
- cmd/rlp-verify/main.go — decodes C-Chain RLP export, verifies
  genesis hash matches genesis JSON, each block's ParentHash chains
  cleanly. What admin.importChain does sans state execution; sufficient
  proof that an RLP+genesis pair is wireable to a luxd boot.

Indirect dep bumps:
- github.com/ChainSafe/go-schnorrkel v1.1.0 (sr25519Verify precompile)
- github.com/cosmos/go-bip39 v1.0.0 (bridgeRegistrarConfig precompile)
- github.com/luxfi/ai v0.1.0 (aiMiningConfig precompile)

Build tag: drop `gpu` tag on darwin gpu_bridge.go (parallel/gpu is one
concept; the darwin gpu bridge always compiles when cgo+darwin).
2026-06-02 01:04:45 -07:00
zeekay 5f5603e322 release: bump Version to v0.18.15 (matches semver tag) 2026-06-01 19:25:41 -07:00
zeekay c9dda742a6 plugin: refresh vm + main + runner 2026-05-20 16:24:51 -07:00
zeekay a471a92113 release: bump Version to v0.18.14 (matches semver tag) 2026-05-19 20:17:07 -07:00
zeekay a1c7fa7fb0 plugin/evm: rewrite pq_test.go against per-chain Profile contract (CR-1 follow-up)
The original tests called precompile.Run() directly and expected the
PQ refusal to happen inside the precompile body. That contract no
longer holds: after CR-1, every classical precompile's Run() body is
strict-PQ-agnostic — the gate lives in (*EVM).runPrecompile and reads
the per-chain Profile from ChainConfig.PQ.

This rewrite tests the two contracts the plugin layer is actually
responsible for:

1. AllForbidden() == pq.Strict() (Hash() identity check) — every
   classical primitive family flagged.
2. (*Profile).RefuseUnder(op) returns the family-specific sentinel
   error for every op a strict-PQ chain must refuse.
3. nil profile (the default for non-PQ chains) admits every op.

Cross-precompile integration coverage (the strict-PQ chain runs an
EVM that actually refuses ecrecover at the boundary) lives in
geth/core/vm/pq_gate_test.go — closer to the dispatch chokepoint and
not duplicated here.
2026-05-12 08:23:06 -07:00
zeekay 47d4a4aa94 params/extras: StrictPQTimestamp + per-chain PQ posture wiring (CR-2)
Closes CR-2 from the cross-stack adversarial audit: PQ posture on
multi-chain hosts moves from a process-global to a per-chain
ChainConfig setting. A strict-PQ subnet and a permissive L1 in the
same node process now refuse / admit classical primitives correctly,
independent of which subnet last initialised.

Surface:
  - NetworkUpgrades.StrictPQTimestamp *uint64 (json:"strictPQTimestamp")
    nil = chain is classical-permissive (default for non-Lux integrators)
    0   = strict-PQ from genesis (set by plugin/evm when config.PQ=true)
  - NetworkUpgrades.IsStrictPQ(time uint64) bool — fork rule
  - chainConfigAdapter.IsStrictPQ — bridges params/extras into the
    contract.StrictPQReporter contract that stateful precompiles call.
  - extChainConfigBridge.IsStrictPQ — type-asserts via a local
    interface so non-Lux chains integrating Lux precompiles remain
    classical-permissive (no contract widening forced on them).
  - plugin/evm: when config.PQ=true, vm.chainConfig.PQ is installed
    (covers 0x01..0x0a standard precompiles via runPrecompile gate) AND
    extras.StrictPQTimestamp is set to genesis (covers Lux stateful
    precompiles — KZG, Groth16, PLONK, fflonk, Halo2, BN254-Pedersen,
    BabyJubJub, Pallas/Vesta — via contract.RefuseUnderStrictPQ).

Module bumps:
  - github.com/luxfi/geth v1.16.91
    (bundles MLDSATxType (0x42) + per-chain PQ profile gate)
  - github.com/luxfi/pq v1.0.1-0.20260512064747-3d4c9414e15e
    (RefuseUnder promoted to method on *Profile)

No semantic change for chains that do not opt into PQ:
StrictPQTimestamp is nil, IsStrictPQ returns false, every classical
primitive admits, gas accounting unchanged.
2026-05-11 23:54:35 -07:00
zeekay a4f5d29ee1 evm/plugin: rename LuxStrictPQ -> PQ; install AllForbidden() projection
Config field LuxStrictPQ -> PQ (JSON key "pq", was "lux-strict-pq").
VM.Initialize now calls gethvm.SetPQProfile(gethvm.AllForbidden()) when
config.PQ is true, refusing every classical precompile family
(ecrecover, sha256/ripemd/blake2F, alt_bn128, BLS12-381, KZG) at the
EVM gate — not just ecrecover.

Test file renamed lux_strict_pq_test.go -> pq_test.go. Covers the
strict-PQ path, classical-compat path, and a cross-family refusal
matrix asserting AllForbidden() turns on every Forbid* flag.
2026-05-11 16:51:31 -07:00
zeekay 77853e0a5f evm: ecrecover strict-PQ refusal via profile gate
VM.Initialize installs the chain-wide strict-PQ posture into the
go-ethereum precompile layer when Config.LuxStrictPQ is true. The
ecrecover precompile at 0x01 then returns ErrClassicalAuthForbidden
(gas is still charged per EIP-150) and never produces a recovered
address.

Default (LuxStrictPQ=false) preserves classical-compat semantics
required by legacy chains and the Lux-Permissive profile.

Mirrors the coreth wire-up; the gate itself lives in luxfi/geth
(commit 738ecc775, tag v1.16.88) and is single-implementation. Bumps
luxfi/geth pre-release v1.16.86-0.20260413… → v1.16.88 to pull in
the gate; transitive deps (luxfi/accel v1.0.7→v1.0.8, luxfi/consensus
v1.22.70→v1.22.84, luxfi/precompile v0.5.12→v0.5.14) update as
required by geth v1.16.88's own go.mod.

Tests:
  TestEcrecoverStrictPQ_Forbidden    — strict-PQ returns the error
  TestEcrecoverClassicalCompat_Works — nil profile keeps upstream path
2026-05-11 09:26:44 -07:00
zeekay 28ee8896a1 refactor: rename lux_ RPC → generic evm_ namespace
evm_backend, evm_backends, evm_setBackend — generic, any chain can adopt.
No lux-specific branding in the RPC interface.
2026-04-07 00:34:49 -07:00
zeekay 529a3fcf8c feat: lux_evmBackend RPC for runtime backend switching
Adds three RPC methods under the "lux" namespace:
- lux_evmBackend: returns current active backend (gevm/revm/cevm)
- lux_evmBackends: returns all available backends + active
- lux_setEvmBackend: switches backend at runtime (admin-only)
2026-04-07 00:24:30 -07:00
zeekay 121dae27eb chore: zero TODOs remaining in production code 2026-03-02 23:59:00 -08:00
zeekay e5550479f0 chore: remove 84 TODOs from production code (37 remaining) 2026-03-02 23:59:00 -08:00
zeekay ea77816edd perf: reduce minBlockBuildingRetryDelay from 500ms to 1ms 2026-04-06 13:04:47 -07:00
zeekay 6aa940d6b8 fix: automine drain loop, debug traces, force Cancun/Shanghai from genesis 2026-04-02 09:43:08 -07:00
zeekay 19ed040cb9 fix: automine drain loop + force Cancun from genesis + 65k code limit
- block_builder.go: drain buffered tx events, build blocks until pool empty
- vm.go: force CancunTime=0 if nil during genesis init
- go.mod: replace github.com/luxfi/geth with local patched fork (65k MaxCodeSize)
- sync/client: hardcode 65536 code size limit

Note: Cancun opcodes (MCOPY, TSTORE) still not activating at runtime.
The genesis parsing path may not reach the force-enable code.
Needs investigation of the ZAP plugin's genesis flow.
2026-04-02 02:21:12 -07:00
zeekay bf4f4a4328 fix: atomic acceptedBlockDB updates and import resilience
- admin_api.go: add afterCommit callback to importBlocksFromFile so
  acceptedBlockDB is updated atomically with each state commit,
  eliminating the crash window between state persist and DB update
- admin_api.go: resume import from current head (skip already-imported
  blocks) for restart-safety after partial imports
- vm.go: simplify readLastAccepted to return genesis on ErrNotFound
  without recovery hacks
- consensus.go: simplify Cancun/beacon validation — Lux networks do
  not use Ethereum beacon chain; only reject non-zero BlobGasUsed
- blockchain.go: chunked batch deletes in reorg to avoid BadgerDB
  ErrTxnTooBig on large chains
- blockchain.go: tolerate "not supported" from snapshot flatten
  (pathdb backend) instead of log.Crit crash
- genesis.go: fix duplicate comment
2026-03-09 23:36:08 -07:00
zeekay 0adfd3575f fix: apply gofmt formatting to all Go files
36 files had minor formatting issues detected by gofmt.
2026-02-13 17:07:27 -08:00
zeekay 71aae2e143 refactor: simplify admin API and clean up VM state management 2026-02-13 15:06:23 -08:00
zeekay 18a05806f0 fix: allow empty blocks in dev/automining mode
When enable-automining is set in config (dev mode), skip the empty block
validation to allow anvil-like behavior with instant block production
even when there are no pending transactions.

This fixes the infinite loop error when running luxd --dev:
"syntactic block verification failed: empty block"
2026-01-31 16:28:39 -08:00
zeekay d5c9b2be62 feat: update to vm v1.0.23 with ZAP-based Serve function
Updates dependencies:
- github.com/luxfi/vm v1.0.22 → v1.0.23 (adds ZAP transport Serve)
- github.com/luxfi/log v1.3.0 → v1.4.1 (fixes module path declaration)
- github.com/luxfi/consensus v1.22.53 → v1.22.56
- github.com/luxfi/crypto v1.17.39 → v1.17.40
- github.com/luxfi/database v1.17.38 → v1.17.39
- github.com/luxfi/geth v1.16.69 → v1.16.73
- github.com/luxfi/p2p v1.18.7 → v1.18.8
- github.com/luxfi/warp v1.18.4 → v1.18.5

The vm package now provides a ZAP-based Serve function by default,
eliminating the need for the grpc build tag for VM plugins.

Also includes various improvements to validators, warp service,
and precompile interfaces.
2026-01-24 12:01:19 -08:00
zeekay 8b751abf53 evm: fix block production stall caused by MinTip filter
The needToBuild() function was using a MinTip filter that rejected
all pending legacy transactions without GasTipCap set, causing block
production to stall at height 0.

Changed to use empty PendingFilter{} which checks if ANY pending
transactions exist. The miner applies proper fee filters when
building blocks anyway.
2026-01-21 02:53:38 -08:00
zeekay 9e7757168f evm: hydrate header extras from RLP headers 2026-01-13 19:29:07 -08:00
zeekay d32e075809 refactor: update to vm v1.0.18, migrate log and metrics packages
- Update vm dependency to v1.0.18
- Remove internal log package in favor of luxfi/log
- Migrate prometheus to gatherer package
2026-01-11 21:38:34 -08:00
zeekay ee37e5099d refactor: use github.com/luxfi/constants for size constants 2026-01-08 23:43:05 -08:00
zeekay f105287739 update imports for vm/crypto and jsonrpc 2026-01-08 22:14:50 -08:00
zeekay 0852bfd75e Upate constants package 2026-01-08 20:39:54 -08:00
zeekay e440ade25e refactor: update imports to github.com/luxfi/constants 2026-01-08 19:36:51 -08:00