1921 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 6e53c6fb95 deps: api v1.0.15 -> v1.0.16 — align InitializeResponse wire with node
api v1.0.16 added Capabilities uint64 to zap InitializeResponse (Quasar-export
capability bit). The plugin built at v1.0.15 answered Initialize 8 bytes short,
so node-side Decode died with unexpected EOF — the cross-process ZAP e2e
(node vms/rpcchainvm/zap TestCEvm_DialsZAPdbServer) caught it. Additive field;
plugin leaves the bit clear until the EVM wires CapQuasarExport.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-14 06:59:42 -07:00
zeekay 7a4ed8bff0 ci: run linux jobs on lux-build-amd64 ARC scale set (no GitHub-hosted builders) 2026-07-11 00:19:03 -07:00
zeekayandHanzo Dev 6cbb326615 consensus v1.36 export-boundary: close 3 LOW items for tag (DRY clamp, one admin_importChain, locked export writes + boot-wiring tests)
Two-tier finality (Nova = local majority accept, drives VM.Accept; Quasar =
⅔-by-stake export) requires that NO export surface ever names a block above the
served accept tip. RED's final pass approved SHIP with three LOW items to close
first.

LOW-1 — DRY the accept-tip clamp into ONE place. New eth.Ethereum.ClampedQuasarHeight()
= min(LastQuasarHeight, LastAccepted.Number) is the single clamp; BOTH export
surfaces read it: EthAPIBackend.quasarHeight() (RPC finalized/safe) and
warpBlockClient.LastQuasarHeight() (warp export gate, via the BlockClient
interface). Removes the second inlined clamp copy that could drift. LastQuasarHeight()
is documented as RAW (node boot re-seed only, where reconcile has already run).

LOW-2 — exactly ONE admin_importChain. Deleted the eth-package AdminAPI.ImportChain
(which neither reconciled the export height nor skipped <=head blocks); the plugin VM's
(plugin/evm/admin_api.go) is the sole one — it runs under vmLock and calls
reconcileQuasarWithAccept after each SetLastAcceptedBlockDirect. Left a NOTE so it is
not re-added and reopens the rewind hole. Dropped the now-unused rlp import.

LOW-3 — exclude concurrent export-height writers + prove the real boot wiring.
SetLastQuasarFinalized now takes vmLock (the same lock reconcileQuasarWithAccept's
admin_importChain caller holds), so the observer callback's durable Put+Store can never
interleave with a re-import's Reset(0)+Delete on the same key. Fixed a boot ordering
gap: the startup auto-import (importBlocksFromFile) runs AFTER initializeChain's
reconcile, so a --import-chain-data rewind could leave a stale export height — added a
reconcile right after the auto-import. New eth.NewTestEthereum test-support ctor.

Tests (all green): eth -race adds TestClampedQuasarHeight_RealRewind (genuine
SetLastAcceptedBlockDirect rewind; belt clamps finalized/safe to the rewound tip while
raw is unchanged); plugin/evm adds TestBootReconcile_RealLastAccepted_ClearsStaleQuasarOnRewind
(drives the REAL restored LastAcceptedBlock through belt + suspenders) and
TestSetLastQuasarFinalized_LockedAgainstReconcile (concurrency: durable key <-> in-mem
always consistent). gofmt/vet/ontology clean.

Co-authored-by: Hanzo Dev <[email protected]>
v1.104.8
2026-07-10 09:22:12 -07:00
zeekayandHanzo Dev bdebad3632 eth,plugin/evm: clamp finalized/safe to accept tip + clear export height on rewind (RED MEDIUM/LOW)
Follow-up to the export-boundary re-point, closing RED's re-review MEDIUM (a finality-safety hole
reachable via our own EVM-rewind→re-import restore runbook) and LOW.

MEDIUM — the rewind path. A rewind / RLP re-import (SetLastAcceptedBlockDirect) can lower the accept
tip BELOW the persisted EXPORT-FINAL (Quasar) height; the monotone SetLastQuasarHeight + advance-only
SyncQuasarFrontier then keep it stuck high, and after the chain rebuilds past that height with a
DIFFERENT canonical, `finalized` would name a block that was NEVER ⅔-certified — reported export-final
to every bridge/DEX consumer. Two parts, both needed:
- Belt: api_backend quasarHeight() resolves finalized/safe at min(LastQuasarHeight, LastAccepted) —
  finalized/safe can NEVER exceed the served accept tip, even with a stale-high durable key.
- Suspenders: VM.reconcileQuasarWithAccept clears the durable quasarHeightKey + the in-mem height
  (eth.ResetLastQuasarHeight, bypassing the monotone guard) when the accept tip is below the export
  height — the ONE legal regression of the otherwise-irreversible frontier. Conservative reset to 0
  (nothing export-final until the rebuilt chain re-certifies). Called on boot (against the restored
  accept tip — catches an offline rewind) and after admin_importChain (the live re-import). On the
  next boot the node re-seeds consensus' export frontier from the cleared (0) height, so the
  advance-only SyncQuasarFrontier yields to the rewind without touching the four shipped fixes.

LOW — SetLastQuasarFinalized now PERSISTS the durable key BEFORE advancing the in-mem height, so the
durable key is the source of truth: a crash between the two can only leave in-mem at/behind the
durable value on boot, never ahead of it (no monotonicity dip). A persist failure does not advance.

Tests: augmented TestFinalizedSafeResolveToQuasarNeverAboveIt with the belt case (seed export height
ABOVE the accept tip → finalized CLAMPS to the tip — the case RED flagged as false confidence, now
ENFORCED); new TestReconcileQuasarWithAccept_ClearsStaleHeightOnRewind (durable key + in-mem cleared
on an accept regression) and TestSetLastQuasarFinalized_PersistsBeforeAdvancing (persist-first,
no-advance-on-failure). eth suite green under -race.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-10 05:49:40 -07:00
zeekayandHanzo Dev 9c6d66ef6b eth,warp: gate EXPORT surfaces on the ⅔-stake Quasar tier, not the Nova accept tip (v1.36)
The two-tier consensus (v1.36) advances VM.Accept at a BARE MAJORITY (Nova), which is reorgable
and can fork under f=1 equivocation. The Ethereum `finalized`/`safe` tags and warp cross-chain
exports were still reading the accept tip — the "semantic collapse" the split exists to prevent.
Re-point every EXPORT surface onto the export-final (Quasar, ⅔-by-stake) frontier consensus
already exposes.

#1 CRITICAL — eth `finalized`/`safe` tags:
- eth.Ethereum gains a DURABLE, monotone lastQuasarHeight (SetLastQuasarHeight/LastQuasarHeight),
  pushed by consensus strictly AFTER accept. The plugin VM's SetLastQuasarFinalized persists it to
  acceptedBlockDB and reloads it on boot (readLastQuasarHeight), so the export surface never
  regresses on restart.
- api_backend HeaderByNumber/BlockByNumber resolve Finalized(-3)/Safe(-4) to GetBlockByNumber(
  LastQuasarHeight) BEFORE the IsAccepted() catch-all — NEVER a block above the Quasar height.
  `latest`/`pending` stay on the accept tip (correct — not finality tags).
- Test: TestFinalizedSafeResolveToQuasarNeverAboveIt proves finalized == the Quasar height,
  monotone, and never above the accepted tip.

#2 HIGH — warp cross-chain export double-spend:
- warp.verifyBlockMessage now requires the block be EXPORT-final (height ≤ BlockClient.
  LastQuasarHeight), so a validator signs a block hash only at Quasar finality, never on a
  reorgable Nova accept (two conflicting Nova forks could otherwise each aggregate a valid export).
- WarpQuorumNumeratorMinimum floored 33 → 67 (⅔): any warp message needs ≥⅔ stake to verify, so at
  most one of two conflicting exports can validate (two ⅔ sets intersect on >⅓ honest stake) —
  restoring the ⅔-accept safety the pre-split accept tier gave for free.

The consensus engine pushes the frontier via NetworkConfig.QuasarObserver; the node bridges it into
the VM (separate node commit). Nova-only chains (no export surface) are unaffected.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-10 04:58:54 -07:00
zeekayandHanzo Dev 1f8281597a chore(deps): luxfi/vm v1.2.5 -> v1.2.6 (ZAP VM-state serialization fix, parity with v1.104.3-hotfix)
Co-authored-by: Hanzo Dev <[email protected]>
v1.104.7
2026-07-07 20:33:18 -07:00
zeekayandHanzo Dev a0cecf5c7c core: head-pin failures degrade non-fatally (match acceptor backend tolerance)
Red finding: pathdb Reference returns 'not supported'; a fatal pin would wedge
Accept/startup on that backend. Warn + continue = pre-fix behavior at worst.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-07 20:31:28 -07:00
zeekayandHanzo Dev 0f90bd00f6 core: pin accepted head state root so idle GC can't evict it (durable freeze fix)
The C-Chain froze at an idle tip with "missing trie node" for the head's
state root: coreth's cappedMemoryTrieWriter keeps only StateHistory(32)
roots referenced via the tipBuffer and commits to disk every
CommitInterval(4096); when the chain sits idle at a non-commit height the
head root can be dereferenced/capped before it is persisted, so the next
BuildBlock cannot materialize the parent state and the chain wedges. The
prior insert-time Reference (writeBlockAndSetHead) is refcount-BALANCED and
is consumed as the block ages through the tipBuffer, so it does not protect
an idle head — with duplicate empty-block roots the tipBuffer Dereference
lands on the live head.

Fix: hold a dedicated, UNBALANCED GC reference on the accepted head's state
root, transferred head-to-head (Reference the new head, then Dereference the
previous head) so exactly one live head pin exists at all times on
lastAccepted.Root(). Established in Accept, in the two direct setters, and on
the loaded head at startup, so the invariant "accepted head state root is
never GC-eligible" holds across idle windows, duplicate roots, cold caches,
small StateHistory, and restart. No-op on pathdb (Reference/Dereference are
no-ops there); active only for the hashdb dirty cache where the wedge occurs.

Restart-then-reexecute remains valid RECOVERY, but is no longer the fix: the
head can no longer be GC'd while idle. DRY via one pinAcceptedHead helper.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-07 17:47:03 -07:00
zeekay 9383cf03d1 go.sum: refresh luxfi/zap hash (re-tag staleness) — no code/execution change 2026-07-07 10:59:42 -07:00
zeekayandHanzo Dev 6f80479bf5 core: pin head state root in triedb dirty cache (Bug B — EVM head-state Reference fix)
hashdb.Update references storage-trie roots but never the STATE root, so every processed
root has parents==0; the StateHistory-deep tipBuffer Dereferences roots as they age out and
on an idle chain that Dereference lands on the live head and evicts it — at a non-CommitInterval
height the head was never flushed, yielding 'missing trie node' at tip → block production
stalls + the accepted tip is lost on restart (compounds the consensus catch-up split).

Fix: one Reference(block.Root(), common.Hash{}) in writeBlockWithState BEFORE InsertTrie
(hash-scheme only), refcount-balanced against the matching AcceptTrie/RejectTrie eviction.
Touches ONLY in-memory GC pinning — never a state root, block hash, gas value, or on-disk byte.

PROVEN (blue abeb6333): execution-IDENTICAL (golden roots identical RED-unpatched vs
GREEN-patched — cannot change mainnet history); RED reproduces at StateHistory=32 idle blocks,
GREEN passes after 96 idle; no-leak bounded over 4100 roots + across the 4096 boundary; refcount
balanced. head_pin test green (StateAvailableAfterIdle, NoLeakOverLongIdle, ExecutionIdentical,
Across4096Boundary_NoLeak, RestartAtNonBoundary).

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-07 10:59:15 -07:00
zeekayandHanzo Dev ed0715a31d test: fix stale golden vectors + txpool test baseline (all green)
Three deterministic test reds, all test-only (no production source change):
- plugin/evm/message: regenerate 6 LE golden vectors (utils LP-023 BE→LE
  codec migration @v1.2.0; oracle-verified, not tautological) + fix stale
  big-endian doc comments in codec.go.
- plugin/evm/validators/state: convert TestParseValidator fixtures BE→LE
  (same codec migration) + doc comments.
- core/txpool/legacypool: decouple testTxPoolConfig from the production
  DefaultConfig (which was deliberately bumped in fdc840067 for throughput);
  pin canonical geth test limits. NOT a regression — production tx-acceptance
  is correct; the test baseline was self-contradictory (per-account min >
  global max). Also fixes a 600s package timeout → 20s.

Co-authored-by: Hanzo Dev <[email protected]>
v1.104.2
2026-07-02 22:17:00 -07:00
zeekayandHanzo Dev 98c42cef80 evm: bump plugin VM version v0.18.18 -> v0.18.19 for 0x9999 genesis-injection fix
rpcChainVM protocol unchanged (42) — node compatibility preserved. Pairs with
the genesis-injection fix (0bd9927a2) for evm module tag v1.104.1.

Co-authored-by: Hanzo Dev <[email protected]>
v1.104.1
2026-07-02 16:57:41 -07:00
zeekayandHanzo Dev d260656efc evm: gate DEX 0x9999 genesis injection on protocol ActivationTime (preserve pre-2025 genesis)
The AlwaysOn DEX settlement precompile (0x9999) had its EXTCODESIZE marker
baked into EVERY chain's genesis state unconditionally (parent==nil in
ApplyPrecompileActivations), mutating the genesis state root. This changed the
genesis hash of every chain genesised before the DEX activation, breaking RLP
re-import of the real archived Lux mainnet (0x3f4fa2a0), Lux testnet
(0x1c5fe377) and Zoo mainnet (0x7c548af4) — all re-genesised Nov 2024, before
the Dec-2025 activation.

Fix: an AlwaysOn system precompile carries a protocol ActivationTime (Dec 2025
for 0x9999, set by the registry bridge — a fork-like constant, not per-net
config). Its genesis marker is installed ONLY IF ActivationTime <= the genesis
timestamp; otherwise it activates later, on the block transition that crosses
ActivationTime during history replay. Dispatch (GetExtrasRules) is gated on the
same timestamp so a codeless address never dispatches. A chain born before
activation reproduces its ORIGINAL genesis byte-for-byte; a fresh chain born
at/after activation still gets 0x9999 at genesis.

Verified against the real RLP block-0 of all three chains (byte-identical
reproduction) plus a fresh post-activation chain. Corrects two pre-existing
tests that had hardcoded the buggy with-0x9999 hashes as "canonical".

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-02 16:50:11 -07:00
zeekayandHanzo Dev b3b98b1ea9 evm: empty-block check is BUILD-only — finalized empty block Accepts (forward-integrate v1.101.4)
Forward-integration onto main (v1.103.0 line) of the accept-empty recovery fix
released as v1.101.4. errEmptyBlock moves from SyntacticVerify (parse/verify/
accept) to the build path only, so a consensus-finalized empty block Accepts
while a demand-driven proposer still never produces one.

Co-authored-by: Hanzo Dev <[email protected]>
v1.104.0
2026-07-02 12:19:36 -07:00
zeekayandHanzo Dev 32c041cb8a deps: dex v1.14.0 + precompile v0.19.0 — dexcore→dex fold alias (fork-safe integer wire)
The dex lx/dexcore→dex fold + exact-integer wire (F1-F5 fork fix, F9 mint fix)
requires bumping dex→v1.14.0 + precompile→v0.19.0. dex_value_wiring.go aliases
the folded matcher pkg as dexcore so the precompile 'dex' package stays distinct.

Co-authored-by: Hanzo Dev <[email protected]>
v1.103.0
2026-07-02 11:01:40 -07:00
zeekayandHanzo Dev 0121536b30 fix(evm): stateless ParseBlock — decomplect BlockGasCost from parse (restore upstream)
ROOT CAUSE of the C-Chain bootstrap ancestry-descent stall: ParseBlock was STATEFUL.
BlockGasCost is NOT in the RLP block — it is DERIVED from the parent header. UnmarshalBlock
computed it via vm.blockChain.GetHeaderByHash(parent), and SyntacticVerify REQUIRED it non-nil
(errNilBlockGasCostEVM). For a bootstrap descent parsing ancestry blocks AHEAD of the accepted
height the parent is not present, so every such block failed to parse — masked by the ZAP
errorToZAP mapping as the misleading "malformed block id (does not connect to accepted chain)".
The Lux bootstrapper indexes the whole fetched batch by (id,height,parent) up front, so it could
not index the ancestry, never found the frontier tip in the batch, and re-requested forever ->
ErrStalled. Confirmed on devnet: "syntactic block verification failed: nil blockGasCost is
invalid after EVM activation".

FIX — decomplect parse from verify (matches upstream avalanchego coreth: parse decodes, Verify
enforces connectivity):
  - block.go: new ensureBlockGasCost() fills BlockGasCost from the parent header WHEN PRESENT
    (SetHeaderExtra is hash-keyed, so it persists for a block first parsed while its parent was
    absent). Best-effort at parse; authoritative at Verify (AcceptBootstrapBlock calls blk.Verify
    only after the contiguity gate height==finalized+1, so the parent is ACCEPTED/present).
  - block_verification.go: SyntacticVerify no longer requires BlockGasCost (stateless,
    block-intrinsic checks only); keeps the uint64 sanity check when it IS set.
  - vm.go UnmarshalBlock: replace the inline parent-lookup with block.ensureBlockGasCost().

SAFETY (parse != accept): parse only decodes + stateless syntactic checks; no non-connecting or
gas-cost-invalid block can be ACCEPTED just because it now parses. Verify/Accept
(InsertBlockManual -> consensus/dummy header verification) recomputes the expected BlockGasCost
from the (present) parent and rejects a mismatch; the bootstrap contiguity gate accepts blocks
oldest-first (parent finalized). A wrong-hash block is rejected off-path by the content-addressed
descent. BlockGasCost is not in the block hash -> no malleability.

evm plugin/evm tests pass.

Co-authored-by: Hanzo Dev <[email protected]>
v1.101.3
2026-07-02 03:23:08 -07:00
zeekayandHanzo Dev 919f3559c0 core+miner: rebuild pruned parent state on demand at BuildBlock (post-accept state-loss recovery)
The fresh-multi-node block-production halt is NOT an accept-time commit gap (the
accept-path guard added earlier never fires — state IS present at accept). The
loss is POST-accept: the reorg/reject churn equivocation produces on a fresh
chain dereferences an accepted block's trie AFTER it becomes the head, before the
next BuildBlock reads it, wedging the chain with 'missing trie node' / 'failed to
create new current environment'. Verified live: fresh v1.101.1 devnet re-genesis
halted at block 8 with the head-root missing on all 5 nodes and the accept guard
at 0 firings.

Fix: BlockChain.MaterializeState rebuilds the pruned state range on demand from
the nearest ancestor whose state is present; the miner's createCurrentEnvironment
calls it on a StateAt miss so block production continues instead of halting. No-op
on a healthy chain. reprocessBlock's ValidateState re-checks each rebuilt root
against its header. Keeps the accept-path guard as defence-in-depth.

Co-authored-by: Hanzo Dev <[email protected]>
v1.101.2
2026-07-01 10:25:32 -07:00
zeekay 054123295a merge fix/accept-materialize-root-v1.99.52 to main v1.102.0 2026-07-01 06:29:58 -07:00
zeekay eeca28efb5 Merge branch 'fix/accept-materialize-root-v1.99.52'
# Conflicts:
#	go.mod
#	go.sum
2026-07-01 06:29:46 -07:00
zeekayandHanzo Dev f510f35b41 core: materialize accepted block state on the accept path (equivocation-commit-gap backstop)
Under equivocation on a fresh chain, the consensus engine can finalize (via the
α-of-K cert / catch-up path) a block that this node verified only as a transient
non-canonical sibling — whose state trie was then dereferenced when that sibling
was rejected — WITHOUT re-verifying it on the finalize path. The node's head then
points at a block whose state root is absent from the trie database, so the next
BuildBlock fails 'failed to create new current environment: missing trie node'
and the chain wedges. This is the fresh re-genesised testnet/devnet block-2 halt
(node v1.32.4 / evm v1.99.52); mainnet is unaffected (its genesis lacks the fresh
precompile suite and its mature operation does not equivocate at the tip).

Fix: in BlockChain.Accept, if the accepted block's state root is not present, re-
execute it on the parent (== last accepted, whose state is present by construction)
before it becomes the head. reprocessBlock's ValidateState re-checks the computed
root against the header, so a non-deterministic transition surfaces loudly rather
than silently. On the common (non-equivocation) path this is a cheap HasState no-op.
The WARN log fires ONLY on the commit-gap, so it doubles as confirming instrumentation.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-01 06:23:49 -07:00
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]>
v1.99.52
2026-06-30 21:28:45 -07:00
zeekayandHanzo Dev edfef5c9cd 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]>
v1.101.0
2026-06-30 20:24:31 -07:00
zeekayandHanzo Dev b1ae152443 fix(deps): repair vanished pins / go.sum drift ( gosum-refresh) + build green
Co-authored-by: Hanzo Dev <[email protected]>
v1.100.0
2026-06-30 13:55:51 -07:00
zeekayandHanzo Dev 4ce8cda073 bump: latest dealerless PQ (corona v0.10.3, pulsar v1.8.0)
Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 13:26:33 -07:00
zeekay 72c61b82d3 docs(brand): add hero banner 2026-06-28 20:35:22 -07:00
zeekay 9f54f2df6e chore(brand): dynamic hero banner 2026-06-28 20:35:21 -07:00
zeekay 832f920c65 bump: dealerless pulsar/corona/dkg/mlwe (no trusted dealer, no-reconstruct)
Move to the dealerless threshold-signature stack: corona v0.10.2 (Module-LWE
Ringtail, dealerless Pedersen-VSS) + pulsar v1.7.1 (dealerless Mithril RSS keygen
+ no-reconstruct signing, trusted dealer ripped) + dkg v0.3.5 + mlwe v0.2.1.
2026-06-28 19:41:22 -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.
v1.99.51
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.
v1.99.50
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).
v1.99.49
2026-06-27 23:28:37 -07:00
zeekay b2afccc5e2 deps: precompile v0.15.0 -> v0.16.0 (enable-everything surface)
Picks up the enable-everything custom-precompile change (RefuseUnderStrictPQ
removed from all verify-only precompiles; fflonk fail-closed; accel byte-
identity; dex/stableswap/graph determinism). Pairs with this module's standard-
precompile enable so the full builder surface is live in one plugin build.
v1.99.48
2026-06-27 21:28:22 -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 d3ec032775 evm: drop cmd/dexlive one-off — replaced by net-agnostic luxfi/trader 2026-06-27 19:27:13 -07:00
zeekay 06091588fc evm: precompile v0.15.0 (active-only DEX money path) + retire pqcrypto bundle (standalone mldsa/mlkem/slhdsa); no dormant AtomicStager, no backward compat v1.99.47 2026-06-27 18:48:21 -07:00
zeekay 143c389e3f chore(deps): bump warp v1.22.0 -> v1.23.0, precompile v0.12.0 -> v0.13.0
warp v1.23.0 = typed finality-evidence model + Pulse->Corona relabel.
No evm consumer references the renamed symbols; warp/, warp/validators/, and
precompile/contracts/warp/ tests all pass. Pure dependency bump.
v1.99.46
2026-06-27 14:45:17 -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.
v1.99.45
2026-06-27 00:48:35 -07:00
zeekay 4d4e961f81 fix(warp): VerifyPredicate enforces Core.NetworkID == local (red LOW)
Red's re-review of the ZAP hard fork found the bespoke EVM verify path dropped
the NetworkID equality check that canonical warp.VerifyEnvelope enforces — a
defense-in-depth gap vs cross-network replay (mitigated by D folding NetworkID
and globally-unique SourceChainIDs, hence LOW, but the two verify paths must
enforce identical checks — one and one way).

VerifyPredicate now rejects Core.NetworkID != GetNetworkID(ctx). The predicate
test runtime's NetworkID is aligned to constants.UnitTestID (it defaulted to 1
while cores are built with UnitTestID, a latent harness mismatch the missing
check had masked); new TestWarpWrongNetworkID proves the rejection.
v1.99.44
2026-06-26 23:00:50 -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.
v1.99.43
2026-06-26 22:25:27 -07:00
zeekay 8e79dae137 merge: warp v1.20.0 ZAP migration (SignedCore/WarpEnvelope) into main v1.99.42 2026-06-26 21:27:49 -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 dd70b64d16 deps(evm): bump chains v1.3.19 -> v1.3.22 (restores dropped forbidden.go)
chains v1.3.19's dexvm/registry references AssertNoForbiddenAssetRefs,
looksLikeASCIITickerID, toHex and fromHex, but the file defining them
(forbidden.go) was dropped in that re-tagged release, so plugin/evm failed to
build (undefined symbols). The helpers are present again from v1.3.21; bump to
the latest v1.3 patch, v1.3.22.

MVS pulls the patch-level deps chains v1.3.22 requires: consensus v1.25.29,
crypto v1.19.26, dex v1.5.20 (zap v0.8.11 indirect). All x.x.x+ patch bumps, no
minor/major. Full repo builds; core/params/snapshot suites green and unchanged.
v1.99.41
2026-06-26 20:03:21 -07:00
zeekay 5fa8db6037 test(evm): fix snapshot-recovery prepare for non-empty (genesis-active) genesis
GenerateChain forcibly uses a hash-scheme trie db to read parent state, but the
snapshot suite runs the chain under both hash and path schemes. With an empty
genesis (pre genesis-active) parent.Root() is the empty root -- readable in any
scheme -- so block generation worked. With the genesis-active 0x9999 marker the
genesis state is non-empty, and a path-scheme chain commits its genesis nodes in
path format, invisible to GenerateChain's hash-scheme reader. state.New then
errored and GenerateChain returned 0 blocks (the error was discarded), so prepare
built an empty chain and restart recovered to genesis (head 0) instead of C4.

Generate blocks on a dedicated hash-scheme genDb via GenerateChainWithGenesis
(the pattern used by the rest of core's tests); the scheme-agnostic block
structures are re-executed into the chain's own db by InsertChain. The gapped and
wipe-crash variants source their extra blocks from genDb too.

The recovery engine is unchanged and verified correct for a non-empty genesis
under both hash and path schemes: on restart driven by lastAcceptedHash=C4 the
head, header and snapshot disk layer all land at C4 and block[4] is present.
2026-06-26 20:03:13 -07:00
zeekay 1c691fb210 test(evm): regen genesis hashes for genesis-active 0x9999 + fix MaxCodeSize drift
genesis-active 0x9999 puts the precompile marker in every genesis, so the genesis
hash changes (correct for a first-run, always-on network): TestSkipPostMergeFields
GenesisHash 0x3f4f->0x5609, TestGenesisBlockForTesting 0x517c->0x4e97. Also fix
pre-existing TestUpstreamParamsValues drift: Lux raises MaxCodeSize to 64KB (65536)
and MaxInitCodeSize to 2x (131072) vs Ethereum's 24576 — the test asserted stale
Ethereum values. params + core-genesis now GREEN.

REMAINING (not merged): the snapshot-recovery tests (TestRestartWithNewSnapshot ...)
fail — a geth snapshot-engine limitation with a non-empty genesis (block[N] of the
snapshot disk layer goes missing on restart). genesis-active EXPOSES it (marker makes
genesis non-empty) but does not cause it; it's a foundational geth-engine fix.
2026-06-26 18:34:33 -07:00
zeekay aed4b24f74 fix(evm): reconcile re-tagged go.sum (zap/dex/chains/...) + drop stale ChainConfig.DurangoTimestamp in test
The re-tag automation staled multiple luxfi module hashes ([email protected], [email protected],
[email protected], ...) so evm would not build/test (SECURITY ERROR). Re-recorded go.sum
fresh (GOPRIVATE bypasses sumdb for luxfi; public deps re-verified). Also removed a
stale DurangoTimestamp from a geth ChainConfig literal in config_test.go (the field
moved to extras.NetworkUpgrades). Unblocks the build. NOTE: params/extras (the
genesis-active StrictPQ work) passes; full evm green still needs the genesis-hash
regen (0x9999 genesis-active) + pre-existing failures (TestUpstreamParamsValues
param drift, snapshot geth-limitation) addressed — separate foundational pass.
2026-06-26 18:23:50 -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 b02e2879a6 deps: bump chains v1.3.19 + consensus v1.25.21 + precompile v0.5.59 (no stragglers) v1.99.40 2026-06-23 09:29:06 -07:00
zeekay 12a9fbcc9a test(evm): regression guard — chain identity bound before NewBlockChain reprocess
Proves the unclean-restart fix: a non-nil *runtime.Runtime is readable on the
chain the instant construction returns (so it was present for any in-construction
reprocess), and nil stays nil for chains with no identity-gated precompiles.
v1.99.39
2026-06-23 08:54:50 -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 f849f49cdd deps: pin precompile v0.5.58 + runtime v1.1.3 (closes release-gate-b)
precompile v0.5.58 carries the 0x9999 value-path soundness floor (Block-STM
access-set, decentralized halt, in-state-vault/EVM-Call resolution; contains
the gate-b commit 23f49b6) and runtime v1.1.3 the GovernanceController seam
the value path reads. Builds standalone without go.work; gate-b closed
(evm no longer links the pre-fix precompile v0.5.57).
v1.99.38
2026-06-23 01:08:42 -07:00
zeekay 8272580165 evm: thread real consensus identity + GetCodeSize into 0x9999 value path
The 0x9999 DEX precompile must see the true (networkID, cChainID) and prove
an ERC-20 has live code, on BOTH paths:

- block processing: ApplyTransactionWithResults now threads bc.ConsensusContext()
  onto blockContext.ConsensusContext when absent (nil-guarded; the miner's
  NewEVMBlockContext already sets it). The stale "StatefulPrecompileHook not
  exposed" note is removed — vm.NewEVM preserves ConsensusContext onto evm.Context.
- eth_call/estimateGas: ChainContext.ConsensusContext now returns the backend
  blockchain's consensus context (which embeds the chain Runtime via
  runtime.WithContext), not the RPC request context — the latter made every
  identity-reading precompile see networkID 0 / C-Chain Empty (fail-closed) on
  eth_call. EthAPIBackend.ConsensusContext exposes it.
- stateDBBridge.GetCodeSize forwards EXTCODESIZE to the internal StateDB so the
  OnChainAssetVerifier's C1 live-code proof works; returns -1 (not 0) when
  unavailable so the value path fails CLOSED rather than admit a fabricated zero.

Adds cmd/dexlive (real end-to-end 0x9999 value-path harness over a running
localnet C-Chain: deploy 2 ERC-20s, open market, seed maker, taker swap, read
DEXFill + balance deltas; synthetic/no-code negative control must revert) and
the consensus-identity unit tests for both paths.
2026-06-23 01:06:53 -07:00