Commit Graph
196 Commits
Author SHA1 Message Date
zeekayandHanzo Dev e77b696662 fix(deps): re-pin luxfi/consensus to v1.36.10 — v1.36.9 silently wedges a chain
main pinned consensus v1.36.9, which contains the silent-rebuild-storm bug that
took devnet down for four and a half days. Any network that upgraded from HEAD
would have wedged the same way.

The bug: buildBlocksLocked keeps pendingBuildBlocks after a failed build, and
proposervm truncates the block timestamp to a whole second — so every rebuild
inside that second re-mints an IDENTICAL blkID. consensus.AddBlock rejects it as
"already exists" and a bare `continue` skips Propose/RequestVotes entirely. The
block is never sent, never voted, never decided, while pending txs keep
re-notifying. Measured on devnet: one node re-built the same blkID 2,287 times
at height 512, with ZERO "proposed block to validators" and ZERO "finalized
block via quorum cert" lines across 200k log lines fleet-wide.

It also leaves a durable trap. The on-disk vote-once guard binds those storm
blkIDs to heights that can never be re-proposed, so reserveSlotForSign refuses
every later block at those heights — the chain stays wedged even after the
binary is fixed. Devnet needed a hand-repaired guard on all five nodes to
recover; that repair has no code path yet (the voteguard docstring points at
engine/chain/lock_migration.go, which does not exist).

This is a REGRESSION, not a stale pin: v1.36.24 and v1.36.25 both pinned
v1.36.10 and finalize normally — devnet runs v1.36.25 today and is producing
blocks. v1.36.26, v1.36.27, v1.36.28 and HEAD went back to v1.36.9.

v1.36.10 is the latest consensus release, so this moves forward. Verified
against an EMPTY module cache (the cold path the in-cluster builder takes), and
./vms/... and ./chains/... build clean.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-26 19:51:24 -07:00
zeekayandHanzo Dev 3a44f0dcd3 genesis: refuse an M-Chain policy the validator set cannot satisfy
M-Chain's committee is the validator set, and RunKeygen refuses a policy
needing more parties than the committee has. mainnet's mchain.json asked
for 7-of-10 against five genesis validators, so a custody key could never
have been generated — the failure would have surfaced the first time
someone tried to bridge, not at deploy.

Adds the invariant as a test over every network's built genesis, and
picks up luxfi/genesis v1.16.4 where the policies are sized to fit
(3-of-5 on mainnet/testnet/devnet, 2-of-3 on localnet).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 14:59:04 -07:00
zeekayandHanzo Dev d5eff75934 genesis: install the M-Chain plugin under the vmID genesis actually declares
The build installed M-Chain's plugin binary as
tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t — the CB58 of the
retired `thresholdvm` identifier — while the genesis builder declares the
chain with constants.MPCVMID (qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS).
The plugin registry resolves a CreateChainTx's vmID to an implementation
by filename, so the two never met: M-Chain was declared in genesis and no
node could start it. The Dockerfile comment had already been renamed to
"mpcvm" without the CB58 being recomputed, which is why it read as correct.

Fixes all five sites (plugin build target, the build-verify list, the
runtime COPY, the comment, and publish_plugin_set.sh) and adds
genesis/builder/mchain_test.go, which pins the vmID literally and asserts
M-Chain is present in the height-0 chain set for mainnet, testnet and
local. A vmID is an immutable one-way door once a chain is created with
it, so it is now covered by a test rather than by five copies of a string.

Bumps luxfi/genesis to v1.16.3, where mchain.json states its quorum as
"policy": "7-of-10" instead of a bare mpcThreshold that reads as the
signer count to an operator and as the polynomial degree to a library.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-25 14:53:07 -07:00
zeekayandHanzo Dev 4a3a131757 go.mod: consume consensus v1.36.9 + chains v1.7.9 (finality-halt fix + native-ZAP VMs)
Propagates two just-published upstream fixes into the node (luxd) binary:

- consensus v1.36.7 → v1.36.9: the chain engine no longer os.Exit(1)-kills a
  validator on a SetPreference orphan-refusal — it reconciles the VM to the
  certified block when the diverged tip is provably uncertified, and halts
  fail-closed only on a genuine double-finalization (v1.36.8). Also unifies
  consensus's transitive geth pin to v1.20.1, matching node/evm (v1.36.9). The
  new PreferenceReconciler VM interface is optional, so node's VMs are
  unaffected (they take the non-fatal defer path); no code change required here.
- chains v1.7.7 → v1.7.9: every VM (aivm/keyvm/quantumvm/dexvm) now serializes
  through native luxfi/zap struct-is-wire, with canonical-id hardening.

go.mod/go.sum only — dependency-graph resolution, no node code change. Transitive
indirects move forward within-major (bft, cockroachdb/errors, sentry-go,
prometheus/common, go-internal); no major-version bumps. geth stays v1.20.1
(already node's pin). Verified: GOWORK=off CGO_ENABLED=0 go build ./... clean.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-16 16:14:50 -07:00
zeekayandHanzo Dev d83191e286 feat(proposervm): configurable window duration (fast local cadence) + strict-PQ classical-proposer refusal
Two changes, both node-local:

1. CADENCE — proposer.WindowDuration (the proposer-slot spacing, a validator's min build
   delay = slot index x WindowDuration) was a hardcoded 5s const tuned for mainnet-scale
   validator sets, flooring small/local block cadence at 5s per slot. It is now a
   startup-configurable var (default 5s, unchanged for mainnet) set via the new
   --proposervm-window-duration flag → node.Config → ManagerConfig → proposervm.Config →
   proposer.SetWindowDuration at VM init. Read by both the windower delay math and
   TimeToSlot, so they stay consistent. Measured on a 5-node strict-PQ net: 5s→1s took
   sustained C-Chain from 44 TPS / 14s-per-block to 104 TPS / 3.8s-per-block, still 5/5
   byte-identical finality.

2. SECURITY (cryptographer MEDIUM-1) — postForkCommonComponents.Verify now refuses a block
   carrying a CLASSICAL secp256k1 proposer identity when the chain is strict-PQ
   (StakingMLDSASigner set), UNCONDITIONALLY (before the consensusState==Ready gate, so it
   also holds during bootstrap/state-sync). Fills the documented-but-unwired 'proposer'
   SchemeGate site: the downgrade defense is now an explicit fail-closed in-perimeter gate,
   not merely emergent from 20-byte NodeID collision-resistance + upstream enforcement.
   Adds SignedBlock.HasClassicalProposer(). Mirrors contract.RefuseUnderStrictPQ.

Pins consensus v1.36.7 (consume-on-error build loop — kills the non-leader BuildBlock spin).
Block + proposervm VM tests green.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-15 22:24:36 -07:00
zeekayandHanzo Dev 13b6e80a78 fix(proposervm): strict-PQ proposer identity — sign+derive with ML-DSA-65 to match the windower
Under strict-PQ the canonical NodeID is ML-DSA-65-derived (config/node DeriveNodeID →
DeriveMLDSA), so the P-chain validator set and the proposervm windower are ML-DSA-keyed.
But proposervm signed post-fork blocks with the classical TLS leaf and derived the block
Proposer() via ids.NodeIDFromCert — a different value than DeriveMLDSA — so every signed
block (height ≥ 2) failed verifyPostDurangoBlockDelay with errUnexpectedProposer, was
dropped, and rebuilt in an unbounded storm (block 1 survived only because the unsigned
transition block skips the proposer comparison).

The block's offCert slot now carries a scheme-tagged proposer identity [scheme:1B|identity]:
0x90 classical (cert DER → NodeIDFromCert, ECDSA verify) or 0x42 strict-PQ (raw ML-DSA-65
pubkey → DeriveMLDSA(ids.Empty,pub), ML-DSA verify). ML-DSA signing/verification uses a
FIPS 204 §5.2 domain-separation context ('lux-proposervm-block-v1') so a proposer signature
can never be replayed as another ML-DSA message. proposervm.Config gains StakingMLDSASigner
/StakingMLDSAPub, plumbed from StakingConfig through ManagerConfig; exactly one scheme is
active per chain. K=1 nets are unaffected (transition + no-window blocks are unsigned).

Proven on a 5-node strict-PQ local net: sustained multi-block production, every block
built once, gossiped, and finalized byte-identical on all 5 (no automine). Pins consensus
v1.36.6 (engine logger + logged build-drops) which made this diagnosable.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-15 17:59:16 -07:00
zeekay 57792ee625 chore(deps): bump geth v1.20.1 + luxfi deps — stack unification 2026-07-15 11:20:41 -07:00
zeekayandHanzo Dev b34dcd1558 v1.36.10: CHAINS_REF v1.7.6 — all VM plugins ZAP-native in the image
Bumps chains v1.7.5→v1.7.6 (dexvm/schain/identityvm/graphvm json→ZAP migrations)
+ zap v1.2.5. CHAINS_REF=v1.7.6 so the baked VM plugins carry the ZAP-native tx/
block wire. Node builds clean; xvm 12 pkgs green. Plugin subgraph now references
only surviving tags (chains v1.7.6 → node v1.36.9 → utxo v0.5.7).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-14 15:56:44 -07:00
zeekayandHanzo Dev 3f65eb486d v1.36.8: zap v1.2.4 value ObjectBuilder — faster wire build node-wide
zap v1.2.4 (eager-reserve + value-type ObjectBuilder: zero defer-slice, zero
per-StartObject heap alloc) + utxo v0.5.7. Byte-identical wire — 21 platformvm/
xvm/components-lux/da packages green. Node-side setEnvelope/setValidator/setID/
setOwner/setSecurity/writeIDInto helpers take zap.ObjectBuilder by value (its
methods mutate through ob.b, so value + pointer are equivalent). Speeds every
ZAP object build (P/X txs, blocks, warp, da), not just X-tx. X-tx wire composite
922->655ns / 11->5 allocs.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-14 09:37:11 -07:00
zeekayandHanzo Dev 4002a59aa6 go.sum: record complete dependency hashes (go mod tidy)
Superset of transitive module hashes go resolves at v1.36.7; -mod=readonly build
+ full 155-package test suite green. No go.mod change.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-14 07:00:10 -07:00
zeekayandHanzo Dev 97e0a0b704 v1.36.7: xvm native-nested composites — 2.8x X-tx build, import/export too
Consume utxo v0.5.6 + zap v1.2.3. X-chain BaseTx / ExportTx / ImportTx now build
their transferable out/in lists as native ZAP AddObjectPtr object-lists (no
per-container envelope prefix, no blob concat, no length lists) via
wire.AppendTransferable{Out,In} + TransferableXFromObject. baseTxWire composes
wire.XVMTransferOut/In directly. Removed the standalone transferableOut/InBytes
node helpers. Composite money-move build 2551ns/37allocs (byte-blob) ->
913ns/11allocs (native-nested), 2.8x. Full xvm suite green (round-trip/state/
block/executor/import/export); components-lux + wallet-x + platformvm-txs green.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-14 06:38:42 -07:00
zeekayandHanzo Dev 24b67abdd0 v1.36.6: consume faster write path — zap v1.2.2 + utxo v0.5.3
zap v1.2.2 (zero-copy SetBytes + Builder pool) + utxo v0.5.3 (pooled wire
builders + SetBytesFixed) cut X-chain tx wire composition ~1.9x (2551->1345ns,
37->19 allocs on the isolated composite; the X build path benefits proportionally
without touching P-chain parse, which stays at 705ns/3allocs). P/X/xvm/
components-lux suites all green against the new deps.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-13 03:45:37 -07:00
zeekayandHanzo Dev 7ee56895d9 v1.36.5: no-replace hermetic build — chains v1.7.5 (pins real node v1.36.4), revert Dockerfile node replace
Supersedes v1.36.4's build recipe which used a build-time 'replace node => /build'
(forbidden: no local replace directives). Instead, chains v1.7.5 pins the real,
published node v1.36.4 tag, so the baked VM plugins resolve node the normal way —
Dockerfile just clones chains and builds; CHAINS_REF v1.7.4 -> v1.7.5; go.mod
chains v1.7.4 -> v1.7.5; genproto realigned. Zero replace directives anywhere.
tidy + -mod=readonly + cold 'go mod download all' clean.

Note: luxfi node/geth/utxo git tags are being periodically wiped by an external
tag-sync (root cause of the 'unknown revision' failures); all four pinned tags
(node v1.36.4, chains v1.7.5, utxo v0.5.1, geth v1.17.12) re-verified present
immediately before this build races the sync window.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-12 21:43:55 -07:00
zeekayandHanzo Dev 9930e98d26 fix(deps): pin published utxo v0.5.1, drop local ../utxo replace — unbreak hermetic build
The xvm codec kill depends on utxo/wire's TransferableOut/In + nftfx/propertyfx
envelopes, which were only in the local ~/work/lux/utxo working tree (pinned via
'replace github.com/luxfi/utxo => ../utxo'). That local-path replace works for a
local build but breaks the hermetic Docker/CI build ('open /utxo/go.mod: no such
file or directory'). Published those two additive wire commits as utxo v0.5.1
(clean ff on utxo main, +2 over v0.5.0) and pin it here — the exact code node was
built+tested against. Dropped the replace. Cold-cache 'go mod download all' is now
clean; xvm/components/lux/wallet tests green against v0.5.1.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-12 17:17:36 -07:00
zeekayandHanzo Dev 64a770b4cc vms/pcodecs: DELETE the last reflection dispatcher — ZAP native everywhere (v1.36.4)
The codec kill is complete. Every node VM (platformvm, xvm, warp, proposervm,
components/lux) and every chains app-chain VM (bridgevm, zkvm, mpcvm — now native
in chains v1.7.4) marshals via native ZAP struct-is-wire. Nothing imports
node/vms/pcodecs anymore, so the package — the last reflection/serialize-tag
dispatcher (a thin alias over proto/zap_codec's LinearCodec) — is deleted.

- rm vms/pcodecs + vms/pcodecs/pcodecsmock (zero consumers; verified whole tree).
- go.mod: chains v1.7.2 -> v1.7.4 (the pcodecs-free chains), precompile
  v0.19.0 -> v0.19.1. Realigned genproto so the split googleapis/rpc module
  resolves by longest-prefix (no monolith ambiguity); tidy clean, -mod=readonly
  build clean.
- version -> v1.36.4 (constants.go defaultPatch, compatibility.json under the
  same RPCChainVM protocol as v1.36.3 — no protocol change, only a codec rip).

There is one and only one way to put a struct on the wire: ZAP.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-12 16:54:25 -07:00
zeekayandHanzo Dev 3f890bc98e Integrate main's Quasar-export + EVM v1.104.8 content into the codec-kill branch
The non-codec main changes (Dockerfile EVM_VERSION v1.104.8, chains/manager
GetContext size-chunking for behind-validator resync + Quasar EXPORT frontier
bridge, warp/signature, rpcchainvm/zap client) that the branch lacked. The
codec-era main files (codec.go/parse.go/tx.go etc.) are intentionally
superseded by this branch's native-ZAP struct-is-wire — not reintroduced.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-11 22:48:27 -07:00
zeekayandHanzo Dev b4992860f6 deps: publishable pins — zap v1.2.0, drop local replaces
Node builds + full test sweep green (155 ok / 0 FAIL) against PUBLISHED
modules only: consensus v1.36.1, zap v1.2.0, utxo v0.3.7. The local zap/utxo
replaces are gone; the upstream nftfx/propertyfx wire (utxo e790d39) ships as
v0.3.8 with the xvm struct-is-wire patch, which is the only remaining pcodecs
consumer and lands in the next release.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-11 22:32:00 -07:00
zeekayandHanzo Dev 926ddaafa4 go.mod: replace luxfi/utxo -> local (nftfx/propertyfx wire envelopes)
Consumes the upstream fx-wire addition (utxo commit e790d39: TypeKindNFT/
Property + 6 shapes + NextEnvelope) needed for the xvm struct-is-wire
migration. Local replace like zap; publish as utxo v0.3.8 with the node
release.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-11 17:55:48 -07:00
zeekayandHanzo Dev 9626ca6c2f Bump consensus v1.35.37 -> v1.36.1 + rip node-side view-change plumbing
v1.36 upstreamed round-scoped view-change INTO the engine (internal prevote/POL
in attestation/reconcile/cert), dropping the external hooks config.Parameters.
ViewChange + Runtime.HandleIncomingPrevote. Ripped the node's now-redundant
external plumbing: the ViewChange enable block (LUX_CONSENSUS_VIEW_CHANGE gate),
the quorumKindPrevote gossip routing + HandleIncomingPrevote call, the dead
BroadcastPrevote gossiper method, and the quorumKindPrevote envelope kind. The
engine owns view-change natively now (fail-secure halt on 2a-n>f unchanged).

Whole node builds EXIT 0 on v1.36.1; luxd binary compiles (55M).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-10 21:07:38 -07:00
zeekayandHanzo Dev 74be89840c v1.34.28: consensus v1.35.36 -> v1.35.37 (F1 stale-alias prevote-lock rebase)
Third-layer mainnet finality fix. v1.34.27 (consensus v1.35.36) proved the
dynamic committee (alpha=4 n=5) and topology.go cert-receive canonical resolve
work live — consensus climbed past 1085755 to round-height 1085761 — but hit the
next layer: a stale-alias prevote-lock. viewForLocked seeds lockBlock from a
pre-canonical-fix durable committedSlot (an OUTER proposervm-wrapper id); a
round-0 lock on an outer alias makes prevoteTarget compare the stale outer id
against the inner-canonical winner by raw id, mismatch forever, no POL, freeze.

v1.35.37 (742696baf): stepViewChange rebases a stale-alias lock onto the inner
canonical winner IFF it resolves (via the same vmCanonicalResolver the cert
rebase uses) to exactly winnerCanon — a genuinely different inner (real fork) or
unresolvable lock is left fail-closed frozen, never merged. lockRound preserved,
2a-n>f untouched, alpha inner-precommits were always inner (CanonicalVoteMessage
binds inner, excludes outer). Full engine/chain green (356s, 0 fail); -race
clean; RED safety gate proves divergent inners still refused.

Roll note: luxd-4 first releases its lock (log 'vc stale-alias lock REBASED');
finality past 1085755 needs >=4 nodes on this build, so height climbs as 3->2->
1->0 come up. EVM stays v1.104.7 (head-pin).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-09 12:05:51 -07:00
zeekayandHanzo Dev 9a0800065f v1.34.27: consensus v1.35.33 -> v1.35.36 (EVM-accept unstick + dynamic committee + snowman purge)
Unsticks mainnet C-Chain finality. Root cause (consensus-layer, proven): under
pChainHeight=0 anyone-can-propose, each validator wraps the same inner block in
its own outer proposervm envelope; votes are canonical-keyed so an α-of-K cert
forms network-wide, but HandleIncomingCert did a strict envelope-id lookup — a
node holding a different alias of the same inner block missed it and requested
catchup instead of finalizing the local wrapper it held, so VM.Accept was never
called and the EVM head froze at 1085755 while consensus logged 'finalized
voters=4'. v1.35.34 (66438a23b) resolves the local wrapper by canonical id and
rebases the verified cert (outer ids unsigned; α votes verify unchanged), no
fork (per-height gate keys on canonical id, idempotent).

Also: dynamic committee 1→N proven (v1.35.35, effectiveCommittee sizes cert +
view-change from live validator count; n=1→1/1,2→2/2,3→3/3,4→3/4,5→4/5 — the
BFT-safe ⌊2n/3⌋+1); view-change safety gate realigned to the effective
committee (RED#2); snowman comment-purge (Quasar/Nova); dynamic committee logs.
Node commits: 4e71814e58 (proposervm→EVM Accept-cascade test, RED#1),
c13681108f (purge), a02611413e (logs). EVM stays v1.104.7 (head-pin). Full
engine/chain suite green under -race (369s, 0 fail/0 race); RED adversarial
suite proves scale-invariance 1→1M + RLP seam byte-exact on real mainnet state.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-09 11:11:58 -07:00
zeekayandHanzo Dev 7720d00baa v1.34.26: consensus v1.35.29 -> v1.35.33 (finality committee sizing) + quorum ValidatorCount
Restores mainnet C-Chain finality. The prior stall: with 5 live validators but
MainnetParams K=21/alpha=15, BOTH finality gates (assembleCertLocked cert-alpha
AND view-change POL) required 15 distinct votes — impossible from 5 validators,
so the chain froze (safe, no fork). consensus v1.35.33 sizes both gates from the
live validator count via effectiveCommittee (alpha=4 for n=5), inheriting the
minBFTCommittee K=4/alpha=3 floor (1085013 self-finality guard preserved);
Snowman K=21 sample untouched. node chains/quorum.go adds
validatorStakeSource.ValidatorCount (height-indexed, deterministic). EVM stays
v1.104.7 (head-state pin). Test: 5-validator MainnetParams control freezes,
fix converges in 0.35s.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-09 08:59:42 -07:00
zeekayandHanzo Dev aa884628a0 v1.34.22: consensus v1.35.28 -> v1.35.29 (complete fix: self-finality safety + canonical cert aggregation + clone-resign inner->outer)
The COMPLETE mainnet reliability build. v1.35.29 adds FIX #1 (self-finality floor — a
transient bftCommittee count can never self-finalize without quorum; the 1085016 accept-
without-quorum safety hole) + FIX #3 (canonical vote aggregation — sibling wrappers of the
same inner block combine into ONE cert; the block-288 wrapper-split cert-termination stall)
+ the v4 clone-resign inner->outer fix (my v1.35.28 re-sign silently no-op'd on real wrapped
blocks). Matcher EVM v1.104.3-hotfix + Bug B UNCHANGED — execution-identical to mainnet.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-07 12:33:23 -07:00
zeekayandHanzo Dev a8971630b5 v1.34.21: bump consensus v1.35.24 -> v1.35.28 (#2 steer-guard + vote-guard clone fix)
MAINNET-COMPATIBLE node-only patch: the v1.34.14 line + catch-up/preference fixes + vote-
guard, EVM PLUGIN UNCHANGED (EVM_VERSION=v1.104.3, precompile v0.19.0, chains v1.7.2, vm
v1.2.5 — byte-identical to mainnet; NO settle-only, NO state-transition change). go.mod delta
vs v1.34.14 = consensus ONLY. Consensus v1.35.28 touches vote/cert LIVENESS only (engine is
byte-identical across the lines); it carries #2 (GetBlock-guarded build-tip steer) + the
vote-guard clone-artifact re-sign fallback. Cherry-picked #1/#3/#5 (proposervm validate-
before-assign, EmptyNodeID->real peers, have-block!=not-behind cert-fetch) on the prior commit.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-05 20:02:08 -07:00
zeekayandHanzo Dev a2f5c6c37b build: node canary v1.34.13 — consensus v1.35.24 (fix BLS double-register panic)
v1.34.12 crash-looped on boot:
  panic: threshold: scheme BLS already registered

consensus <= v1.35.22 blank-imported the OLD crypto/threshold/bls in
quasar.go while node imports the NEW threshold/scheme/bls; both register
BLS into the crypto/threshold registry -> panic. consensus v1.35.24
repoints quasar.go to threshold/scheme/bls.

Verified on the FULL node binary dep closure (go list -deps ./main, 1028
pkgs): luxfi/crypto/threshold/bls = 0, luxfi/threshold/scheme/bls = 1 —
exactly one BLS registration path, the panic cannot recur.

Pins: consensus v1.35.24, chains v1.7.2, evm v1.104.3.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 21:14:17 -07:00
zeekayandHanzo Dev b3e92ce979 build: node canary v1.34.12 — chains v1.7.2 (pulsar-path fix) + consensus v1.35.22
Supersedes the v1.34.11 image build, which failed in the Docker plugin
stage: chains v1.7.1 pulled consensus v1.25.35, whose polaris.go imports
the removed pulsar/ref/go/pkg/pulsar path — the quantumvm + mpcvm plugins
could not build and their hard COPY failed.

- chains v1.7.1 -> v1.7.2 (consensus bumped to v1.35.22 there; consistent
  pulsar graph; all 10 chain VM plugins verified building standalone).
- Dockerfile CHAINS_REF v1.7.1 -> v1.7.2.
- consensus v1.35.21 -> v1.35.22 (Pike-clean of the ProposalKey liveness
  fix; logic identical to v1.35.21).

Pins: consensus v1.35.22, chains v1.7.2, evm v1.104.3. Off origin/main,
no WIP.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 20:50:05 -07:00
zeekayandHanzo Dev 0428445890 build: node canary v1.34.11 — buildable image with decomplected consensus
IMAGE/BUILD change (packaging only — the consensus behavior change ships
separately as luxfi/consensus v1.35.21). Two edits make the deployable
node image build with the fixed consensus and the current chain plugins:

- consensus pin v1.35.20 -> v1.35.21 (ProposalKey decomplection, commit
  776a5119c: outer block IDs are transport aliases, proposal identity is
  ProposalKey{ParentID,Height} — proposervm envelope churn can no longer
  spawn own sibling candidates that split votes; the mainnet 1082880 fix).
- Dockerfile ARG CHAINS_REF v1.7.0 -> v1.7.1. v1.7.0 still shipped the
  pre-rename `thresholdvm` dir, so the Dockerfile's `cd /tmp/chains/mpcvm`
  plugin build produced nothing (swallowed by `|| echo WARN`) and the hard
  COPY of the mpcvm plugin (tGVBwRxp...) failed — the v1.34.10 Docker build
  break. v1.7.1 carries the mpcvm rename; the mpcvm plugin builds clean
  (verified standalone, 16MB) and all 10 chain plugins are present.

Pins verified for the canary image: consensus v1.35.21 (=776a5119c),
chains v1.7.1, evm v1.104.3, cevm v0.19.0, dex v1.5.15. Built off
origin/main — no WIP from any other worktree.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 19:38:25 -07:00
zeekayandHanzo Dev 86a342ae7b deps: bump to released graph — ids v1.3.1, constants v1.6.1, genesis v1.16.1, chains v1.7.1, consensus v1.35.20, threshold v1.12.1
Pins node to the published tags carrying this session's LP-0130 work:
ids/constants add M/F chain identifiers (drop T-Chain), genesis adds
M/FChainGenesis, chains has the mpcvm rename, consensus has the
mempool-churn pre-build gate + threshold scheme/bls repoint.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 19:08:20 -07:00
zeekayandHanzo Dev b4720c9f57 node v1.34.8: consensus v1.35.16 -> v1.35.17 (VC liveness) + proposervm slot-snap
Pairs the proposervm stale-parent slot-snap churn-fix (f840336167) with consensus
v1.35.17 (view-change abandonment must never silence a height). Together they close the
distributed VC liveness stall that keeps mainnet C-Chain safe-but-paused: slot-snap kills
the stale-parent rebuild sibling explosion at the source (one stable candidate per slot),
and v1.35.17 keeps the round machinery driving the height to convergence instead of going
silent after rePoll's 8-attempt cap. Liveness-only; decided-floor / no-double-finalize
safety invariant untouched. Gated on the live idle-inclusive devnet re-storm + RED before
any testnet/mainnet roll.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-03 11:57:47 -07:00
zeekayandHanzo Dev 6a87c69010 node: signed-checkpoint bootstrap anchor (INVARIANT 4) + consensus v1.35.16
Harden the bootstrap checkpoint — the one path that bypasses the beacon quorum —
into a cryptographically SIGNED weak-subjectivity anchor.

- Checkpoint carries an authority Signature over a domain-separated canonical
  (id,height); CheckpointVerifier (injected, backed by a proven primitive —
  Ed25519/BLS, never custom crypto) authenticates it. The policy stays crypto-free
  exactly like AncestrySource/heightOf.
- AcceptsFrontier trusts a pinned checkpoint ONLY when the configured authority
  signed that exact (id,height). Present-but-unsigned, signed-by-a-non-authority,
  signature-transplanted-to-another-(id,height), and no-verifier-wired all FAIL
  CLOSED. A compromised flag cannot inject a false sync anchor without also forging
  the authority's signature. It is a cryptographic vouch, NEVER a ⅔-live-stake tally.
- No production path pins a checkpoint yet (opt-in operator escape hatch), so the
  invariant is enforced for when it is activated; nothing to wire now.

Folds consensus v1.35.16 (single ⅔ formula + leaf-lock doctrine) into the node.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 17:49:54 -07:00
zeekayandHanzo Dev 15ff32fbdc node: bootstrap/finality type split (FinalityQuorum vs BootstrapTrust) + consensus v1.35.15
Make the bootstrap-trust vs live-finality boundary a HARD API split so neither
can impersonate the other — the mass-recovery deadlock was bootstrap reusing the
finality quorum (>2/3 of CURRENT stake), which is unsatisfiable when the
recovery targets are themselves down validators.

- FinalityQuorum.HasFinality(weight,total): the live rule, strict >2/3 of current
  stake (unchanged). Renamed from ConsensusQuorum to the reviewer's exact spec.
- BootstrapTrust.AcceptsFrontier(replies): selects a weak-subjective sync anchor
  from AUTHENTICATED CONFIGURED beacons — a response FLOOR (MinResponses) plus a
  supermajority over the RESPONDERS, NEVER over the whole set. Distinct type, so
  bootstrap cannot call the finality predicate (INVARIANT 3: acceptance != finality;
  the node re-executes every synced block before re-entering consensus).
- Contrast proven: 3-of-5 bootstrap ACCEPTS a frontier, yet HasFinality(3/5) is
  false and STAYS false after sync — bootstrap is not a finality bypass.

Folds consensus v1.35.15 (the global unlock-before-call-out invariant + the
n=1..10 quorum acceptance matrix) into the node binary.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 16:49:47 -07:00
zeekayandHanzo Dev ec5cf94145 node: v1.34.3 — decomplected single-validator DECIDE fix + evm accept-empty pin
consensus v1.35.13 -> v1.35.14 (decomplect: removed the selfVoter shortcut that
self-deadlocked on t.mu.RLock — the live n=1 freeze; K==1 now finalizes solely via the
inline finalizer, one path). EVM_VERSION v1.103.0 -> v1.104.0 (= v1.103.0 + accept-empty:
a consensus-finalized empty block Accepts, so VM.Accept can never fail-closed on an empty
first block — the stale-evm-pin gap). Unblocks single-validator Zoo mainnet/testnet +
Hanzo (all frozen at block 0).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 15:29:50 -07:00
zeekayandHanzo Dev 5e44cd67dc node: bump consensus v1.35.11 -> v1.35.13 — LIVE single-validator DECIDE deadlock fix (v1.34.2)
The v1.34.1 n=1 fix passed unit tests but the LIVE single-validator chains still
froze (Zoo mainnet/testnet, Hanzo, Pars, all block-0). Root cause (neo's live repro,
confirmed through the full runtime path): a reentrant-RWMutex self-deadlock — the
single-node selfVoter called engine.ReceiveVote synchronously while buildBlocksLocked
held t.mu, so ReceiveVote's t.mu.RLock deadlocked on the same goroutine (node hangs at
'single-node mode: self-voting', inline finalize never runs, block never decides).

consensus v1.35.13 dispatches the selfVoter's ReceiveVote on a fresh goroutine (no
reentrant lock) — the block now DECIDES through the real NewRuntime path (proven by
TestBlue_SingleValidator_DecidesThroughFullRuntimePath: hangs pre-fix, passes post-fix).
The bump also carries the n=1 synthesize fix (v1.35.11) and the dormant 1→N
decentralization guard (v1.35.12). EVM_VERSION unchanged (v1.103.0) — single-validator
blocks are tx-driven / non-empty.

Gates Zoo mainnet + Zoo testnet + Hanzo (all single-validator).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 15:01:00 -07:00
zeekayandHanzo Dev 1b6487e0b7 node: bump consensus v1.35.8 -> v1.35.11 (n=1 single-validator DECIDE stall fix) — v1.34.1
Prod-line (v1.34.x finality-admission) bump carrying the consensus fix for the
single-validator (K=1) DECIDE stall that wedged all three sovereign
single-validator chains (Zoo 200200, Hanzo 36963, Pars 494949): a K==1 block now
finalizes even when its self-vote is unverifiable against a not-yet-resolvable
single-validator set (synthesized 1-of-1 cert; per-height FinalizeBranch gate is
the safety). n>1 C-Chains (mainnet/testnet) unaffected — they never hit the K()==1
branch.

The bump also carries the (dormant) bounded phantom-floor reconcile
(v1.35.9/v1.35.10) — additive engine methods that this prod-line node never calls
(manager.go on main has no ReconcilePhantomFloor wiring), so behavior is unchanged.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 13:55:06 -07:00
zeekayandHanzo Dev 6424671eb7 deploy: bump DEX stack to fork-safe integer wire (chains v1.7.0, evm v1.103.0, precompile v0.19.0)
Deploys the launch-blocker fixes: F1-F5 cross-arch execRoot fork (exact-integer
ZAP wire, no float64 in consensus) + F9 infinite-money unbacked-deposit (authority
gate, fail-closed). Rebuilds the bundled DexVM + EVM plugins on the reconciled
dep chain (dex v1.14.0 lx→dex fold). Cross-arch execRoot proven identical.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 11:03:04 -07:00
zeekay 29c623880c go.mod: bump consensus v1.35.7 -> v1.35.8 (fail-closed finalize->VM-accept, fix 2b diagnostic) 2026-07-02 09:23:31 -07:00
zeekayandHanzo Dev ab4201301e chains: opt-in round-scoped view-change enablement (LUX_CONSENSUS_VIEW_CHANGE) + consensus v1.35.7
Adds the per-deployment opt-in flag: when LUX_CONSENSUS_VIEW_CHANGE=true and K>1, sets
consensusParams.ViewChange=true so devnet/testnet activate the round-scoped view-change
without a mainnet default (mainnet owner-gated). The engine fail-secure HALTS if the committee
fails 2a-n>f, so enabling never weakens safety. Bumps consensus v1.35.6->v1.35.7 (removes dead
safeConfig dup). This is what makes v1.32.13's prevote transport actually activatable.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-02 08:20:09 -07:00
zeekay c3f037c737 go.mod: bump consensus v1.35.5 -> v1.35.6 (round-scoped view-change) 2026-07-02 08:07:52 -07:00
zeekayandHanzo Dev 363a5a8591 bump(consensus): v1.35.2 -> v1.35.5 — boot-seed decidedFloor from vm.LastAccepted (clean in-place v1->v2 upgrade)
consensus v1.35.5 is a patch-only fix (go.mod byte-identical to v1.35.2..v1.35.4): the
decided-height sign-gate floor is now seeded DIRECTLY from vm.LastAccepted at engine Start
(before signing) and in SyncState, so a node upgrading IN PLACE from a legacy v1 vote-guard
file (floor 0) has a real decided floor from the first instant of boot — closing the
mainnet v1->v2 upgrade window where the floor was 0 until the first post-upgrade finalize.
Sign-gate-only (never enters byHeight/ledger.Height; PART-A intact; only refuses more).

Supersedes the un-rolled node v1.32.9 (consensus v1.35.4) for the mainnet-first roll. No
node code change (fix is internal to the consensus engine). go mod tidy drops an orphaned
luxfi/bft indirect (pre-existing). Full node builds clean on linux/amd64 (cgo off).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-01 23:58:52 -07:00
zeekayandHanzo Dev 9eb4b64f4e node recovery (testnet): v1.32.7 tree (evm v1.101.2 MaterializeState) + consensus v1.35.2
RECOVERY BUILD for the frozen testnet C-Chain. Carries BOTH fresh-multi-node fixes:
  - STATE halt (missing trie node) — evm v1.101.2 MaterializeState, from the v1.32.7 tree.
  - CONSENSUS double-finalization + liveness stall — consensus v1.35.2 (epoch-blind
    vote-once + per-height vote convergence + RED-round hardening).

Deliberately based on v1.32.7, NOT node v1.33.0: v1.33.0 is a mis-numbered DISPROVEN
build (older commit, MISSING the evm v1.101.2 MaterializeState commit, pins the buggy
consensus v1.33.3) — basing on it would regress the state-halt fix. This branch also
EXCLUDES node-main's /ext->/v1 route migration to keep the incident recovery minimal
and avoid breaking /ext/bc/C/rpc tooling mid-recovery.

Recommended tag: node v1.33.1 (forward past the v1.33.0 revert-magnet so the image
tooling cannot revert testnet to the disproven build). NOT tagged here — cutting the
tag may auto-trigger a live deploy; that is a live-incident action requiring explicit
owner authorization.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-01 15:11:45 -07:00
zeekayandHanzo Dev c6cbee935a fix(chains): wire durable vote-once guard; bump consensus v1.33.3 (Red HIGH-1)
Pick up consensus v1.33.3 (durable + epoch-keyed + funnel-pruned per-height
vote-once guard) and ACTIVATE its durability in production: buildChain now opens a
per-chain VoteGuardStore (chain.OpenVoteGuard at <chainDataDir>/vote-guard) for
every K>1 signing chain and passes it to the engine via NetworkConfig.VoteGuard.

This closes Red's HIGH-1 on the wire that matters: a signing validator's
(height,epoch)→canonical bindings are fsync'd before it votes and reloaded on
startup, so a crash between casting a vote and finalizing the height cannot forget
the binding and let the restarted node sign a conflicting sibling — the cross-node
fork with zero Byzantine intent under a rolling upgrade / eviction / OOM. The store
is opened at the node boundary so the fail-closed-on-corrupt decision (a signer
refusing to start with equivocation memory it cannot trust) lives here, not in the
engine. luxd builds against pinned v1.33.3.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-30 23:21:17 -07:00
zeekay 72233ed59e bump(consensus): v1.33.1 -> v1.33.2 (per-height vote-once — closes the cross-node fork)
consensus v1.33.2 closes the CRITICAL cross-node fork Red found in v1.33.1: the
finality layer now enforces per-height vote-once (an honest validator signs at most
one canonical per height, both signing sites through reserveSlotForSign), so two
conflicting siblings can no longer both gather a >=2/3-stake cert. All 3 Red fork
repros GREEN, emergent probe 15x no fork, engine/chain suite green under -race.
luxd builds clean. Ships with the proposervm liveness fix + luxd-3 ZAP block-id guard.
2026-06-30 22:06:01 -07:00
zeekayandHanzo Dev bb43458e8c bump(consensus): v1.32.1 -> v1.33.1 (proposer-liveness re-solicit + multi-node suite; QCv2 canonical finality)
Adopts consensus v1.33.1: the build-path re-solicit of an undecided own
proposal (avalanche repoll alignment) plus the v1.33.0 QCv2 canonical-
commitment finality + incident-1082814 verify-before-slash. Pulls transitive
ids v1.3.0, warp v1.24.0, pulsar v1.9.0, threshold v1.12.0. luxd builds
(GOWORK=off, pinned deps). No node code changes required — consensus public
API (NewRuntime/NetworkConfig/Runtime) is stable across the bump.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-30 20:49:04 -07:00
zeekayandHanzo Dev 4d46b3c6c7 bump(consensus): v1.31.1 -> v1.32.1 (QCv2 canonical-commitment finality + RED-round H1 verify-before-slash)
Pins the incident-1082814 durable fix. consensus v1.32.1 supersedes both the
down-leader line (v1.31.1, what main pinned) and the QCv2-core-only line
(v1.31.2, what node v1.31.5 shipped). Finality keys on the canonical inner
execution commitment; equivocation evidence requires a VERIFIED alpha-of-K cert
over a DIFFERENT canonical, removing the false-slash fatal-exit crash that
destabilized mainnet under DEX-fill load on v1.31.5.

Adds indirect github.com/luxfi/bft v0.1.5 (cert verify gate). Full node module
compiles clean; consensus proof suite 377 subtests + race + vet + fuzz green.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-30 15:22:09 -07:00
zeekayandHanzo Dev eac47cbf93 bump: latest dealerless PQ (corona v0.10.3, pulsar v1.8.0)
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-30 13:28:50 -07:00
zeekay cb9030ecf6 bump(consensus): v1.31.0 -> v1.31.1 (down-leader sibling convergence)
consensus v1.31.1 fixes the down-leader HALT: when a slot-leader proposer is
down, validators built competing siblings at the same height, the alpha-of-K
votes split, no cert assembled, and the chain stalled (devnet stuck at height
36 with 4/5 healthy). The fix converges all validators onto one deterministic
build tip (PreferredBuildTip) so blocks finalize through a validator going down.

Transitive: corona v0.8.0->v0.10.2, dkg v0.3.0->v0.3.5, pulsar v1.2.0->v1.7.1
(the dealerless PQ line consensus v1.31.1 is built against). luxd builds clean.
2026-06-29 01:12:33 -07:00
zeekay e11b29dafa fix(go.sum): refresh luxfi/pq v1.0.3 content hash (upstream tag force-moved)
luxfi/pq's v1.0.3 git tag was force-moved upstream after this go.sum was
recorded. go.sum kept the originally-published zip hash
h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs= (still served by
proxy.golang.org / sum.golang.org). The release builds fetch luxfi modules
direct from GitHub (GOPRIVATE=github.com/luxfi/* => GONOPROXY) on a cold
cache, bypassing the immutable proxy; the moved tag now hashes to
h1:ksw1dmfTR0dqqNMRS7BjGcprCO2Fhc+3Iiq2/NMuONw=, so the goreleaser
darwin_amd64 build (.github/workflows/build.yml) failed strict
-mod=readonly verification:

    verifying github.com/luxfi/pq@v1.0.3: checksum mismatch
        downloaded: h1:ksw1dmfTR0dqqNMRS7BjGcprCO2Fhc+3Iiq2/NMuONw=
        go.sum:     h1:pFlQm1+5FuKTDUh2y/23bXWkN4I2Rc5iuxJypwDFFMs=
    SECURITY ERROR

=> v* tags produced no macOS artifacts. (The "downloading luxfi/log" line in
the CI log was a concurrent-download red herring; pq is the culprit.) The
Docker image escapes this because the Dockerfile strips first-party go.sum
lines and rebuilds them under -mod=mod.

Refresh only pq's content hash to the current tag. Version stays v1.0.3 and
the /go.mod hash is unchanged (pq's deps did not change). Keeps the release
build strict and consistent with the Docker image.

Verified: fresh direct fetch verifies readonly against the new go.sum
(exit 0); the exact goreleaser target
(CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags='-s -w'
./main) compiles to a Mach-O x86_64 binary; cold clean-cache darwin
`go mod download` no longer raises SECURITY ERROR.
2026-06-28 17:07:40 -07:00
zeekay 9d358d1ec2 build(consensus): bump v1.30.0 -> v1.31.0 (pure-fold finality decomplect + RED perf/defensive fixes)
consensus v1.31.0 on top of the v1.30.0 sibling-tolerant finality already shipped in
node v1.31.1: the finality decomplect (pure Ledger,Cert,DAG->Ledger',Plan fold;
markFinalized unwriteable), topological.go preference layer, the RED HIGH-1 clone()
O(window) perf fix (was O(chain height) — 86.7ms/100MB per finalize at 10^6 heights),
and RED LOW-1/LOW-2 hardening. Backward-compatible: node compiles with zero API drift;
proposervm + chains green. No transitive crypto-dep bumps.
2026-06-28 15:57:57 -07:00
zeekay 507f2ee08c build(consensus): bump v1.29.1 -> v1.30.0 (sibling-tolerant finality)
Consume the sibling-tolerant finality fix: cert-driven FinalizeBranch reorg
replaces the per-height admission gate that deadlocked the proposervm
pre-fork->post-fork transition (devnet stuck at finalized height 1 while
blocks produced past 10). Base = v1.31.0 (= v1.30.101 union quasar PQ-finality
+ frontier self-vote) + the repliedCovers eclipse-defense cherry-pick.

Verified: go build ./... clean; chains + proposervm tests green against v1.30.0.
2026-06-28 14:09:07 -07:00
zeekay c00172bda7 Merge v1.30.101 into quasar PQ-finality (StakingTLSSigner post-fork signing + fresh-chain init tolerance) for v1.31.0
# Conflicts:
#	go.mod
#	go.sum
2026-06-28 12:47:07 -07:00