162 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 4ba9544b51 bridgevm: B<->M release path + canonical attestation seam
Completes the bridge cross-chain release leg the recon flagged as missing.

- internal/bridgeattest: the ONE canonical definition of the bridge-transfer
  attestation seam between B (bridgevm) and M (mpcvm). M threshold-signs a
  domain-bound bridge-transfer digest; B and every EVM gateway holding the group
  key's address verifies before mint/release. Domain-bound + replay-safe; test
  proves a signature over one transfer verifies for THAT transfer and no other.
- evmclient.go: the concrete EVM ChainClient B instantiates per external EVM
  (Zoo 200201, Lux testnet C 96368) — chainClients was never populated because
  the interface had SendTransaction but no concrete EVM client; B could not
  broadcast a release. Now wired via evmByChainID in factory.
- release.go: on a confirmed lock/burn in an accepted block, B builds the
  destination release call, obtains + verifies M's threshold attestation, and
  broadcasts to the destination gateway. Both directions (Zoo<->Lux).
- attest.go: injects the two runtime deps (Warp->M, KMS relayer key) as clean
  interface boundaries rather than hard-wiring transports.

Co-authored-by: Hanzo Dev <[email protected]>
v1.7.4
2026-07-12 16:44:17 -07:00
zeekayandHanzo Dev d682216d8c mpcvm: native ZAP struct-is-wire + cross-validator threshold signing
Kills node/vms/pcodecs from the M-Chain (MPC custody VM) — the LAST of the three
app-chain VMs blocking pcodecs deletion. bridgevm + zkvm already native.

Codec kill (wire.go): the legacy pcodecs LinearCodec keyed on serialize:"true"
tags; every mpcvm type carries only json: tags, so the old codec marshaled ZERO
fields — a Block round-tripped to all-zero and every block ID collided at
sha256(0x0000). This is a latent corruption bug the native wire FIXES. wire.go
owns Marshal/parse for Block (nested []*Operation as u32-length-list + concat
sub-object blob), ManagedKey (PartyIDs []party.ID as packed string list;
CreatedAt/LastUsedAt as UnixNano; Config/CMPConfig json:"-" excluded),
CrossChainMPCRequest, Operation. Block.ID_ is derived (computeID=sha256(Marshal))
so it is excluded from wire; recomputed on ParseBlock, set from DB key on getBlock.
ThresholdConfig (init.Config) + Genesis stay JSON at the boundary. Deleted
mpcvm/codec.go; swapped 9 call sites. Parse rejects trailing bytes. Round-trips
green (Operation/Block/ManagedKey/CrossChainMPCRequest incl empty-ops nil-slice).

Threshold signing (sign_distributed.go + transport/attest_bridge): CGGMP21/CMP
threshold-ECDSA ceremony across the M committee over native app-gossip; each
validator runs its own executor + per-session gossipRouter; honest signers derive
the SAME standard secp256k1 signature. B→M attestation seam is domain-bound and
replay-safe. Tests: transport-free seam (bridge_seam_test) + full 3-validator
in-process fabric driving the real Broadcast→Gossip→router.Deliver path
(bridge_transport_test).

3 of 3 chains VMs native. chains repo now has ZERO pcodecs imports.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-12 16:44:17 -07:00
zeekayandHanzo Dev fc6083cbae zkvm: native ZAP struct-is-wire — kill node/vms/pcodecs dependency
Z-Chain fully native: wire.go with Marshal/parse for Transaction (nested
TransparentInput/Output, ShieldedOutput, ZKProof, FHEData; Nullifiers [][]byte;
optional-pointer proofs as presence-by-length), Block, UTXO, PrivateAddress.
Nested slices packed as u32-length-list + concat sub-object blob; generic
packObjs/unpackObjs + packBytesList helpers. ZConfig + Genesis are JSON at the
init.Config/init.Genesis boundary (dropped codec fallback). Deleted zkvm/codec.go;
swapped 10 call sites. Parse rejects trailing bytes. Round-trip tests (incl deep
nested tx) green. 2 of 3 chains VMs.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-12 16:44:17 -07:00
zeekayandHanzo Dev 3a21d0cf4a bridgevm: native ZAP struct-is-wire — kill node/vms/pcodecs dependency
Block + BridgeRequest get native zap Marshal/Parse (offset objects; requests
as packed length-prefixed wire objects; MPC sig map as sorted nodeID blob +
length-prefixed sig blob). Genesis stays JSON at the config boundary (vm.go).
Deleted bridgevm/codec.go (linearcodec via node/vms/pcodecs). Parse rejects
trailing bytes (canonical). No pcodecs, no reflection, no codec registry.
Build + tests green. 1 of 3 chains VMs (zkvm/mpcvm remain) that block deleting
the node's pcodecs package.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-12 16:44:17 -07:00
zeekayandHanzo Dev 0b49832959 deps: bump evm v1.104.4 + precompile v0.19.1 (dex settle-only decomplect)
Kills the stale evm v1.99.51 / precompile v0.16.0 (matcher-bearing) pins. chains
now transitively carries precompile v0.19.1 (settle-only 0x9999, no embedded
matcher). go.sum regenerated (rm + go mod tidy -e; the -e tolerates a pre-existing
genproto/googleapis/rpc/status TEST-import ambiguity in aivm/cmd/plugin, unrelated
to this bump — chains builds clean). luxfi re-tag checksums (zap/age) refreshed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-12 16:44:17 -07:00
zeekay a93a588ab2 ci: run linux jobs on lux-build-amd64 ARC scale set (no GitHub-hosted builders) 2026-07-11 00:20:59 -07:00
zeekayandHanzo Dev 9861e9daab deps: consensus v1.25.35 -> v1.35.22 (fix pulsar import-path skew)
chains pinned consensus v1.25.35, whose protocol/quasar/polaris.go imports
the OLD github.com/luxfi/pulsar/ref/go/pkg/pulsar path that pulsar v1.9.0
removed (it moved to pulsar/pkg/pulsar). The quantumvm + mpcvm plugins —
which transitively pull that consensus — failed to build in the node
Docker image ("module luxfi/pulsar found, but does not contain package
.../ref/go/pkg/pulsar"), breaking the node:v1.34.10/11 image builds.

Bump to consensus v1.35.22 (current path + the ProposalKey liveness fix).
go mod tidy resolves a consistent graph; all 10 chain VM plugins
(aivm/bridgevm/graphvm/identityvm/keyvm/oraclevm/quantumvm/relayvm/mpcvm/
zkvm) build clean standalone (GOWORK=off, -mod=mod, verified).

Co-authored-by: Hanzo Dev <[email protected]>
v1.7.2
2026-07-03 20:49:14 -07:00
zeekayandHanzo Dev 810fe38893 deps: require luxfi/threshold v1.12.1 (scheme/bls home)
Co-authored-by: Hanzo Dev <[email protected]>
v1.7.1
2026-07-03 15:59:50 -07:00
zeekayandHanzo Dev 38e66384f1 bridgevm: rip out vestigial BLS custody subsystem — custody is M-Chain
B-Chain does NOT do threshold custody keygen/signing. Custody is
M-Chain's job: dealerless CGGMP21/FROST distributed keygen via
luxfi/threshold (cmp.Keygen/frost.Keygen — multi-round protocols over a
message router; no trusted dealer, the group secret is never assembled
by any validator). B-Chain requests keygen/reshare/sign from M-Chain
over Warp (CrossChainMPCRequest) and only VERIFIES the resulting CMP
threshold signatures (block.Verify → mpcConfig, accel_verify.go).

bridgevm carried a SECOND, parallel MPC subsystem — a BLS MPCKeyManager
that did its own in-VM keygen (until this session, via a trusted dealer)
feeding BridgeSigner/DeliveryConfirmationSigner/BridgeMessageValidator.
It duplicated custody, used the wrong scheme (BLS vs the live CMP path),
and was never in block.Verify. Deleted entirely — one and one way.

Removed:
- mpc.go (MPCKeyManager, SigningSession)
- bridge_signing.go (BridgeSigner, DeliveryConfirmationSigner,
  BridgeMessageValidator, MPCCoordinator, BridgeMessage/DeliveryConfirmation)
- vm_mpc_integration.go (InitializeMPCKeys, TriggerKeygen, ProcessBridgeMessage)
- vm.go: the 5 BLS fields + their init block (no key material is
  generated on B-Chain now)
- feegate.go: gateUserBridgeMessage (BLS-message fee gate) → replaced by
  gateUserBridgeFee(uint64) on the live path (LP-0130 §8)

Relocated / repointed:
- ErrInvalidBridgeSignature → accel_verify.go (the live CMP verify path)
- rpc.go MPCReady/MPCPublicKey → vm.mpcGroupPublicKey() (reads the CMP
  custody group point from mpcConfig, i.e. M-Chain's key), never a
  B-Chain-held secret

Build + vet + full bridgevm tests green.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 13:57:33 -07:00
zeekayandHanzo Dev 844c0d5d71 bridgevm: remove trusted dealer from B-Chain custody keygen
B-Chain is a public permissionless chain; custody keygen must never
run through a trusted dealer. GenerateKeys now runs a dealerless
Pedersen-VSS DKG (no party holds the group secret) instead of
scheme.NewTrustedDealer. Build + bridgevm MPC tests pass.

This is the one production chains/node path that USED a trusted dealer
(the dealer remains available as a library option in crypto/threshold
per CTO direction — it is simply never used by chains/node).

Follow-up (threshold-architecture consolidation): route this keygen
through luxfi/dkg, the canonical dealerless DKG engine, once B-Chain
custody's signature scheme (classical BLS vs PQ corona/pulsar) is
fixed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 12:47:21 -07:00
zeekayandHanzo Dev 5c72db8860 quantumvm: enforce LP-0130 §6 — no user-payable blockspace on Q-Chain
Flip the Q-Chain fee gate from FlatPolicy (user-fee metering) to the
committee-only fee.NoUserTxPolicy sentinel. Finality-cert inclusion is
a validator obligation paid via P-Chain reward distribution; a fee
market on Q would make finality hostage to blockspace pricing — the
exact failure mode LP-0130 §6 eliminates. Consensus-internal cert
aggregation reaches txPool.AddTransaction directly and is unaffected.

Tests updated: policy is the NoUserTxPolicy sentinel and IssueTx
refuses every user tx (zero-fee, floor-fee, and 1000x floor) with
ErrChainAcceptsNoUserTxs. go test ./quantumvm/ passes; build + vet
clean.

Closes the top divergence from the 2026-07-03 LP-vs-code audit.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 12:32:41 -07:00
zeekayandHanzo Dev bbce4b6975 chains: rename thresholdvm → mpcvm (LP-134 T-Chain retirement finished)
Under LP-0130's canonical roster, chains/thresholdvm/ hosts the M-Chain
VM (MPC threshold signing). The package name "thresholdvm" was the last
piece of T-Chain nomenclature still shipping in code — legacy from the
pre-LP-134 shared-substrate design. Renamed to mpcvm/ so the package
name matches the M-Chain letter and LP-7100 spec.

- chains/thresholdvm/ → chains/mpcvm/ (dir + inner files + package decl)
- Sed-replace thresholdvm → mpcvm across .go / .md / .yml / .json / .sh
- Fee/settlement primitive references now say mpcvm
- LLM.md canonical chain roster (LP-0130) already points at mpcvm/

F-Chain runtime pieces (fhe/, protocol/tfhe_keygen/,
runtime/f_chain_adapter.go) stay under mpcvm/ for now — they consume
the same shared threshold primitives. A follow-up extract to
chains/fhevm/ can happen once F-Chain runtime graduates.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 01:22:30 -07:00
zeekayandHanzo Dev c2d052afc3 chains/thresholdvm: reconcile LLM.md naming to M/F/library (LP-134)
Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 01:05:24 -07:00
zeekayandHanzo Dev 0b704eded5 chains/thresholdvm: carve ThresholdService/MPCService/FHEService surfaces
The decomplection proper: separate what the threshold primitive IS from where
it is APPLIED (LP-134 / LP-7050). service.go carves three orthogonal service
interfaces directly from the methods the single overloaded *VM already
implements:

- ThresholdService — pure primitives (DKG/keygen/refresh/reshare, key lookup).
  The substrate M and F both consume.
- MPCService (embeds ThresholdService) — threshold signing + bridge-custody
  attestation. M-Chain's surface (LP-7100).
- FHEService (embeds ThresholdService) — confidential compute over encrypted
  state. F-Chain's surface (LP-8200); FHE-execution methods are added as the
  physical fhevm package is carved out.

*VM is asserted to satisfy all three at compile time (the compatibility
bridge). This changes NO behavior and NO genesis — the full test suite stays
green — but it establishes the exact interface boundary the physical VM split
(github.com/luxfi/chains/{mpcvm,fhevm} each backed only by the substrate it
needs) will implement, so the split cannot silently reach across surfaces.
service_test.go locks the embedding relationship (MPC/FHE ⊃ Threshold) so a
future split can't drop the substrate dependency.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 00:48:06 -07:00
zeekayandHanzo Dev 1c591e326c chains/thresholdvm: reconcile naming to LP-134/LP-7050 (kill teleportvm falsehood)
thresholdvm is a shared LIBRARY substrate, not a chain. Fix the stale/false
naming across the package to the canonical model:

- factory.go: the VMID comment claimed "T-Chain is reserved for TeleportVM,
  not yet built" — FALSE per LP-7050. There is no teleportvm and no T-Chain;
  teleport IS bridgevm (B-Chain, LP-6000). Rewrote to describe the value as
  the shared substrate VM consumed by M-Chain (mpcvm) + F-Chain (fhevm).
- vm.go: package doc dropped the "mode"/"T-Chain label" framing for the
  library-substrate description; both M and F consume it, neither is T.
- README.md / DESIGN.md / docs/M_CHAIN_INTEGRATION.md: removed the repeated
  false claim that the "T-Chain" name is retained for a standalone
  teleportvm (LP-6332). LP-6332 is superseded by LP-6000 (bridgevm) per
  LP-7050 §3. Reframed the M/F split as two chains consuming this library
  rather than one VM switched by mode.
- LLM.md: corrected the "deprecated T-Chain shim / one-epoch grace-window
  migration" prose — T-Chain was never launched with live state (clean
  forward split, no migration), and vm.go/factory.go/fhe/ are the shared
  library substrate M/F consume, not a T-Chain shim.

No code behavior changes; comments/docs only.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-03 00:42:35 -07:00
zeekay c63135e724 chore: gitignore stray plugin build artifact 2026-07-02 12:02:03 -07:00
zeekayandHanzo Dev 1676121436 fix(dexvm): exact-integer ZAP wire — match dex v1.14.0, kill cross-arch fork
The dex venue (v1.14.0) froze its ZAP wire to EXACT INTEGERS: order/fill
price is uint64 fixed-point ×1e8 (the PriceInt grid), size is uint64 atomic
base units — no IEEE-754 float64. This eliminated every value-bearing
float→int conversion from the settlement path, whose out-of-range result is
implementation-defined per the Go spec and differs by CPU arch (arm64
saturates, amd64 wraps) — the cross-arch consensus fork (F1-F5).

The stateless atomic ZAP proxy still encoded/decoded the OLD float wire, so
it could no longer interoperate with the new venue. This propagates the
frozen frame byte-identically:

- relay.go: Fill{Price,Size} float64 -> uint64; DecodeFills reads big-endian
  uint64s and rejects zero price/size. New PriceScale const == zapwire.PriceScale.
- carried_fills.go: block-carried fills encode/decode as exact uint64s.
- atomic.go: drop the float→int rounding machinery (quantToCredit/quantToCharge/
  nearestIntWithin/settlementRoundEpsilon/maxSettlementUnit/float64FromBits/
  isFinitePositive). Add quoteUnits (floor(size×price/PriceScale) in big.Int,
  mirroring dex v4BalanceDelta / the matcher's quoteUnitsFor) and settleUint64
  (refuses an aggregate that exceeds uint64 rather than wrapping).
- vm.go settleFromFills: aggregate base=Σsize, quote=Σ per-fill floor as exact
  integers; slippage limit compared as fixed-point uint64; no float. Refund/
  proceeds are the venue's own integers — escrow-truncation mint is structurally
  impossible; spent>locked still refused.
- vm.go encodeCLOBPlace: price = tx.Price×PriceScale, size passthrough (no float).
- feegate.go: swapNativeFeeToLUX takes a fixed-point Fill.Price and floors via
  quoteUnits.
- txs/tx.go: RelayOrderTx.PriceLimit is fixed-point ×PriceScale (was float64-bits).

Dep: github.com/luxfi/dex v1.5.20 (indirect) -> v1.14.0 (direct, test imports
the pure-Go zapwire leaf as the parity oracle).

Tests: all conservation/atomicity/redteam tests translated to the integer
semantics WITHOUT weakening any invariant (the no-mint / overflow-refusal /
mixed-side / slippage / determinism proofs are preserved, re-anchored to the
venue's exact per-fill floor). New frame_parity_test.go asserts the proxy's
Place/Submit/Fill frames are BYTE-IDENTICAL to dex v1.14.0 zapwire.

Co-authored-by: Hanzo Dev <[email protected]>
v1.7.0
2026-07-02 10:40:05 -07:00
zeekayandHanzo Dev 3893cdfe3b test(dexvm): prove settlement conservation is independent of trust
Add redteam-tagged Byzantine settlement tests proving the chains/dexvm
conservation bound holds even against a genuinely-signing byzantine venue
(co-located with the proposer, so every fabricated fill carries a VALID
Ed25519 attestation). The attestation gate proves a fill came from the
venue key; these prove that even a validly-signed fill cannot mint.

dexvm/byzantine_settlement_test.go (//go:build redteam):
  - OverNotionalAttestedFillCannotMint: a validly-attested BUY spending
    1200 quote against 1000 locked is refused by settleFromFills
    ("spent > locked"); 0 minted, escrow intact (keystone)
  - WithinCollateralAttestedFillSettles: positive control — same venue,
    a within-collateral fill settles (400 base + 200 refund). The
    discriminator is spent<=locked, not the attestation
  - MixedSideAttestedFillRefused: a validly-attested [BUY,SELL] stream is
    refused by the single-side guard; no over-credit
  - DeterministicRegardlessOfFillOrder: settle is a pure function of the
    fill SET — two orderings settle to byte-identical export legs (RED #9)

Co-authored-by: Hanzo Dev <[email protected]>
v1.6.0
2026-07-01 20:53:15 -07:00
zeekayandHanzo Dev 76966c1b7a bump: latest dealerless PQ (corona v0.10.3, pulsar v1.8.0)
Co-authored-by: Hanzo Dev <[email protected]>
v1.5.0
2026-06-30 13:26:09 -07:00
zeekay a6359ae8a6 docs(brand): add hero banner 2026-06-28 20:35:28 -07:00
zeekay 079522eb32 chore(brand): dynamic hero banner 2026-06-28 20:35:27 -07:00
zeekay b190a322cb bump: dealerless pulsar v1.7.1 / corona v0.10.2 / dkg v0.3.5 / threshold v1.10.2
Go layer dealerless-clean (quasar/bridgevm/threshold protocols build; only the
separate wgpu GPU backend needs its native lib, unrelated to PQ).
2026-06-28 19:39:51 -07:00
zeekay 942e1bb200 deps: evm v1.99.48 -> v1.99.51 (block-production stall fix)
Picks up the WaitForEvent builder-ready race fix + target-rate build pacing
that un-freezes the C-Chain (it stalled at the imported frontier: WaitForEvent
blocked forever on a nil builder, and too-early high-fee blocks rejected
small-tip txs). Realigned go.sum for the upstream luxfi/age v1.5.0 tag-move
(zip content hash G69H... -> zC/Fw...; matches evm v1.99.51 + the live module
source). C-Chain VM plugin builds clean.
v1.4.8
2026-06-28 00:56:03 -07:00
zeekay 3e8821a3d5 decomplect(warp): one warp (luxfi/warp v1.23.0) + one signed-message helper across bridgevm/zkvm/thresholdvm; deps evm v1.99.48 + precompile v0.16.0
Standardize all of chains on github.com/luxfi/warp v1.23.0 (the standalone
ZAP-native warp that precompile/evm/bridgevm already use). zkvm/fhe and
thresholdvm/fhe are migrated OFF github.com/luxfi/node/vms/platformvm/warp;
there are now ZERO platformvm/warp imports in chains.

The build->sign->wrap sequence (previously copy-pasted in three places and
split across two warp libraries) now lives in exactly one place:
internal/warpmsg.BuildSigned(signer, networkID, sourceChainID, payload)
-> (*warp.Envelope, error). It builds a content Message (NewMessage), signs
it over the Beam domain with the node BLS key, and wraps the signature at
validator bit index 0 into a single-signer Envelope. All three send sites
call it:
  - bridgevm/vm.go        reshare-request gossip
  - zkvm/fhe/coprocessor   FHE task-result callback
  - thresholdvm/fhe/relayer decryption fulfillment

v1.23.0 split the signature from the Message: the signed/transmittable unit
is now *warp.Envelope (Message + Beam BitSetSignature + PQ lanes), not a
combined Message. The receive path moves with it: onMessage callbacks and
WarpHandler.HandleMessage now carry *warp.Envelope and read authenticated
content from env.Message. MPC/BLS single-signer semantics are preserved
exactly (bit-0 signer, Beam-domain BLS, receiver aggregates/verifies the
BitSetSignature against the canonical validator set via warp.VerifyEnvelope).

CGO_ENABLED=0 build/vet clean; bridgevm/zkvm/thresholdvm test suites green.
v1.4.7
2026-06-27 21:49:08 -07:00
zeekay 938a158076 fix(corona_general): label Corona as Module-LWE, not Ring-LWE
Corona is the Ringtail/Raccoon Module-LWE threshold scheme
(q=0x1000000004A01, module dims M=8,N=7 over Z_q[X]/(X^256+1)) — a
relic "Ring-LWE" label in the M-Chain general-purpose lane package doc.
Comment-only; interface-only file, no behavior change.
2026-06-27 16:31:14 -07:00
zeekay 40d29ef0f6 fix(quantumvm): drop trusted-dealer dual-key keygen; build against consensus v1.25.35
InitializeDualThreshold called consensus quasar.GenerateDualKeys, a
trusted-dealer keygen (one process mints+holds every validator's BLS+Corona
share, defeating threshold security). Consensus relocated that helper to
test-only (keygen_testsupport_test.go) as corona-genesis hardening, so
production chains no longer compiles against it.

The method was dead: zero callers anywhere, and it already discarded the
generated signer (_ = signer, used only for two log lines) and inited fake
v0/v1/v2 validator IDs. The live Quasar bridge signs through the consensus
core with per-validator keys (ML-DSA fallback on the VM paths); real
validators join via AddValidator. Removing it is a no-op at runtime and
restores the build against consensus v1.25.35. Any future per-epoch group
key must come from a dealerless DKG, never a trusted dealer.
v1.4.6
2026-06-26 22:26:24 -07:00
zeekay 9361c09127 wip(chains): keyvm gas/fees/settlement/auth-only (in-flight checkpoint) 2026-06-26 19:10:13 -07:00
zeekay 8235dd1ce8 Merge graphvm genesis last-accepted block (fixes G-chain ZAP init: GetLastAccepted not implemented) v1.4.5 2026-06-26 15:53:06 -07:00
zeekay b50d044ba1 graphvm: resolve genesis as last-accepted block (fix G-Chain init)
The G-Chain VM declared chain.ChainVM but stubbed the block methods:
GetBlock/ParseBlock returned errNotImplemented, LastAccepted returned the
unset preferredID (ids.Empty), and Block.Status() returned choices.Status
(not the uint8 the block.Block interface requires, so *Block never satisfied
the interface).

At boot the ZAP VM server (vm/rpc/vm_server_zap.go handleInitialize) calls
LastAccepted() then GetBlock(lastAccepted); the GetBlock stub produced
'get last accepted block: not implemented', failing G-Chain initialization
and the lux-mainnet node health check.

The G-Chain is a read-only query/index VM that never advances past genesis,
but consensus still requires a resolvable last-accepted block. Build a
deterministic genesis block (height 0, content-addressed ID = SHA-256 of its
canonical encoding) in Initialize, pin it as lastAccepted, and serve it from
GetBlock/ParseBlock/GetBlockIDAtHeight. Fix Status() to return uint8 and add
the var _ block.Block compile assertion so this can't regress.

Tests reproduce the exact handleInitialize sequence plus byte/ID round-trip
and cross-validator determinism.
2026-06-26 15:39:05 -07:00
zeekay 71225a5a3d feat(dexvm): DEX fees settle to canonical LUX (network-independent) — L2/L3 ready
D-Chain fees denominate in the ONE canonical L0 asset constants.UTXO_ASSET_ID (the
brand-neutral LUX UTXO literal), not the local network's UTXO asset. So any L2/L3
building on LUX with its own native currency still settles DEX fees to LUX. The fee
policy is network-independent (newFeePolicy() takes no networkID); the MinTxFeeFloor
gate is enforced in LUX on every chain. UTXO_ASSET_ID is the correct reference (D-Chain
is UTXO; an EVM-context DEX would use the EVM-native LUX). networkID retained as chain
identity only. Tests: fee asset is canonical LUX not local native; floor holds on an L2;
native->LUX deterministic; zero-fee rejected; min-fee accepted. dexvm green.
v1.4.4
2026-06-26 15:30:05 -07:00
zeekay e19c11951a fix(dexvm): restore white-label universe deny-scan (refuse Liquidity/Liquid/partner)
The dexcore->dex refactor gutted the chain-label scan in AssertNoForbiddenAssetRefs
(it discarded chainLabel via '_ = chainLabel'), so the DEX startup gate no longer
refused a source chain labeled a 'Liquidity (white-label) universe' even though the
token was real on-chain — a regression of the never-mix-Liquidity boundary.

Restore the negative gate: IsForbiddenUniverseLabel scans the chain's human label for
forbidden off-network brands (liquid -> catches Liquid EVM/DEX/Liquidity; partner),
case-insensitive, and AssertNoForbiddenAssetRefs refuses by name. Positive ChainVerifier
proves existence; this polices brand. Closes the 3 regressed gate tests.
v1.4.3
2026-06-26 11:55:15 -07:00
zeekay e44493230e dexvm: dexcore → dex rename (dex/pkg/dexcore → dex/pkg/dex) 2026-06-25 23:15:44 -07:00
zeekay f5e958cc95 deps: fix consensus go.sum integrity break (re-tagged modules), bump consensus v1.25.29
The full chains build hit a go.sum SECURITY ERROR — luxfi/[email protected] was
re-tagged (recorded hash no longer matched served bits), blocking everything that
pulls the consensus-adjacent module tree. Fixed PROPERLY, NO bypass
(GONOSUMCHECK/GOSUMDB=off never used):
- staking v1.5.0 → v1.5.1 (clean tag), records the real hash
- consensus v1.25.21 → v1.25.29 (was drifted behind; CROSS-REPO-VERSION-PIN), pulls
  crypto v1.19.26
- added luxfi/dex v1.5.20 require + recorded the transitive sums (shopspring/decimal
  et al) the evm/precompile dex wiring needs

Consensus path now builds + tests clean: schain + schain/cmd/plugin exit 0, full
./schain/... suite green. (Remaining full-tree ./... failure is the luxfi/evm+dex
CGO native link needing luxcpp prebuilt Metal/secp256k1 libs — a heavyweight-VM
native-build concern, NOT consensus and NOT in the schain path.)
2026-06-25 17:19:23 -07:00
zeekay 349e27b10c consensus parity: quantumvm joins the linear cert path; dexvm doc fix
quantumvm/vm.go: remove GetEngine() so the chain manager routes Q-Chain through
the linear consensuschain cert path (the same deterministic 2/3-stake BFT
certificate finality as C/D/P), not the undriven DAG engine. quantumvm already
implements the full chain.ChainVM/BlockBuilder surface; dropping GetEngine is the
whole change. Adds the _ chain.ChainVM + _ consensuschain.BlockBuilder asserts.

dexvm/factory.go: correct the stale 'Quasar consensus' comment — dexvm finalizes
via the linear cert path (Photon->Wave->Focus + 2/3-stake cert), same as C-Chain.
v1.4.2
2026-06-25 16:07:07 -07:00
zeekay 6552bd69cd schain: Stage 2 — cryptographically verifiable signed pinned-writer (deletes raft's serialized writer)
The Stage-1 allocate gate was owner-gated in LOGIC (pinning.IsOwner over the
block's threaded proposer) but UNENFORCEABLE by a verifying node: a Lux block
carries no verifiable proposer, and resolving one (GetValidatorSet) can be a
network call, so the gate could not run inside the pure, local block apply. The
chain was therefore unsafe for >1 validator.

Make ownership self-attested and cryptographically verifiable:

- AllocateTx gains Signer/SignerScheme/SignerPubKey/Sig + Epoch/Nonce/Fingerprint.
  Sig is an ML-DSA signature (FIPS 204, the SAME scheme the validator NodeID
  derives from) over the SP 800-185-framed bytes Range|Count|Epoch|Nonce|
  Fingerprint. The owning proposer signs at BuildBlock with its staking key.

- Verify (applyAllocate) is pure + local: recompute owner = pinning.Owner(R,
  V@epoch); require Signer == owner; re-derive the NodeID from the carried public
  key (ids.DeriveMLDSA — the NodeID is a SHAKE256-384 commitment to the key) and
  require it to equal Signer; ML-DSA-verify the signature. Any failure rejects the
  whole block (fail closed).

- Validator-set locality: GetValidatorOutput carries no ML-DSA key, so Verify
  binds the key via the NodeID commitment instead of a set lookup, and uses the
  EpochFingerprint carried in the tx (recomputed from the local snapshot) as the
  DESIGN 6.4 guard — the network call (set resolution) happens in BuildBlock,
  never in Verify.

A non-owner's forged allocate is unverifiable by construction: it cannot sign as
the owner (no key), and claiming the owner's NodeID with a foreign key fails the
re-derivation. This is the property that replaces raft's serialized writer.

Tests (real ML-DSA keys, production NodeID derivation, no bypass): owner-signed
accepted + contiguous/monotonic; non-owner-with-VALID-key rejected; forged sig
rejected; foreign-key-for-owner-NodeID rejected; tampered tx rejected; epoch and
fingerprint mismatch rejected; verifier accepts owner-signed block regardless of
relay; state-root/manifest tests stay green. Full schain suite green under -race.
2026-06-25 15:47:37 -07:00
zeekay 64163f8e02 schain: Stage 1 — leaderless pinned-writer allocator (AllocateTx + owner gate)
Replaces raft's serialization role for volume/fileId allocation with
owner-gated VM state riding native Lux consensus. The HRW owner of a range
is the only emitter of allocate mutations, enforced at block Verify — no
leader election.

- txs: AllocateTx{Range,Count} mirrors PutManifestTx codec (type byte + JSON);
  Verify rejects empty range / zero count. Owner gate is block-level, not in
  the tx (a tx cannot see the block's validator set).
- state: per-range counter alloc/<range> (GetAlloc/SetAlloc), folded into the
  SHAKE256 state Root() alongside manifests via rootPrefixes; domain bumped to
  SCHAIN_STATE_ROOT_V2 so the root covers allocator divergence.
- vm: applyAllocate increments alloc/<range> by Count, reserving [base,base+Count)
  — pure function of committed state, deterministic, version-layer only, no I/O.
  Owner gate: pinning.IsOwner(range, proposer, members) in ProcessBlock; a
  non-owner (or no-validator-set) allocate fails the whole block. ProcessBlock
  now Aborts staged state first so read-modify-write applies over COMMITTED
  state (idempotent across BuildBlock+Verify re-application).
- BlockContext{Members,Proposer,Epoch} threads the validator set + proposer as
  explicit deterministic inputs (no network I/O in Verify). BlockContextBuilder
  is the seam to the real consensus runtime (master-cutover stage); empty
  context = M0/no-allocate path, AllocateTx fails closed.

Tests (green, no go.sum bypass):
- owner allocates: contiguous + monotonic across blocks ([0,8) then [8,13)).
- non-owner rejected: proposer-side (BuildBlock) and verifier-side (Verify) →
  errNonOwnerAllocate; counter untouched; nothing leaks to durable base.
- empty validator set: fails closed (errNoValidatorSet).
- parallel distinct ranges: independent owners allocate independently.
- same range: serializes through its one owner.
- state root covers alloc: counter change moves Root(); Verify rejects a
  tampered claimed root (errStateRootMismatch).
2026-06-25 15:15:37 -07:00
zeekay f6ea13a04f chore: drop stray aivm-gov-mcp build binary from v1.4.1, gitignore it
git add -A in the v1.4.1 commit swept in a 12.6MB local Mach-O build artifact.
Remove it from the tree and ignore it so binaries never land in the release again.
v1.4.1
2026-06-25 14:43:29 -07:00
zeekay 1a7723684e schain v1.4.1: state-root hardening + go.sum integrity fix + CI plugin path
CRYPTO (Red/cryptographer HIGH): manifest state root was a hand-rolled
len||k||len||v SHA-256 fold (length-extendable, no domain sep). Now SHAKE256 +
SP 800-185 leftEncode framing + 'SCHAIN_STATE_ROOT_V1' domain, mirroring the
validator NodeID scheme (ids/node_id_scheme.go) — not length-extendable,
domain-separated, PQ-strength. Full schain suite green (determinism +
change-sensitivity + Verify-rejects-tampered-root), NO sum bypass.

DEPS (integrity, NOT bypassed): luxfi/[email protected] had been re-tagged (go.sum
recorded an immutable-tag hash that no longer matched the served bits). Fixed
PROPERLY by moving to the clean v0.8.11 and recording the real hashes; added the
private hanzos3 org to GOPRIVATE (it was missing) so luxfi/zapdb's hanzos3/go-sdk
dep resolves via git+go.sum, not the public sumdb. go.sum is now correct, not skipped.

CI: registered schain in .github/images.json (build matrix); added the canonical
schain/cmd/plugin/main.go (ZAP-native vm/rpc.Serve, no build tag — the path the
release workflow builds), removing M0's grpc-tagged rpcchainvm plugin. Plugin
builds clean via ./schain/cmd/plugin.
2026-06-25 14:42:37 -07:00
zeekay 6c984d3862 schain: HRW pinning primitive + leaderless pinned-writer design
Pure weighted-rendezvous (HRW) owner assignment over the P-Chain validator set
(schain/pinning): Owner/IsOwner/EpochFingerprint — single-writer-per-range as a
function of (range, validatorSet@pChainHeight), no leader election, parallel across
ranges, same-object double-write impossible. 9 tests green; nothing wired yet (safe).
DESIGN_pinned_writer.md: the spec to replace the s3 master's raft (the last gRPC
server). v1 on linear ChainVM (already leaderless). TWO HARD blockers gate cutover:
(1) off-chain blob durability must be ordered before the on-chain manifest commit;
(2) validator-set lookup in Verify must be a pure local read (determinism). Staged:
AllocateTx → shadow mode (after blockers) → kill raft.
v1.4.0
2026-06-25 13:29:53 -07:00
zeekay 929bf86938 schain: M1 — manifest state root (multi-validator safety) + S3 object split
PART A — manifest STATE ROOT (the >1-validator prerequisite M0 omitted):
- state.Root() hashes the committed manifest keyspace deterministically via the
  zapdb prefix iterator NewIteratorWithStartAndPrefix(nil, prefixManifest),
  folding each entry length-prefixed in lexicographic key order. Mirrors
  dexvm/state/state.go:395 StateHash, narrowed to the one keyspace.
- VM.computeStateRoot folds blockHash+height with state.Root() (dexvm/vm.go:1260);
  ProcessBlock stamps BlockResult.StateRoot (dexvm/vm.go:584).
- The root travels in the block HEADER (new 32B field after parentID; block id
  commits to it). Block.Verify recomputes it and REJECTS a mismatch
  (errStateRootMismatch) + Aborts staged writes — the safety gate dexvm computed
  but never compared.
- Tests: determinism across write order, change-sensitivity (object set / etag /
  size / fileIds), Verify-rejects-tampered-root.

PART B — S3 OBJECT path, on-chain-metadata / off-chain-blob split, end to end
(schain/object/):
- Store.PutObject streams the blob to a Volume (OFF chain) -> fid, then commits
  only PutManifest{bucket,object,[fid],size,etag} to the VM through a real block
  (ON chain). GetObject reads the manifest back and reconstructs from the volume.
- object.Volume is the seam; MemVolume satisfies it for M1 with a fid shape
  mirroring hanzo/s3 needle.FileId and etag = base64(md5(blob)) (= ContentMd5).
- M2 PLUG POINT (object/volume.go): swap MemVolume for a github.com/hanzoai/s3
  s3/operation.SubmitFiles adapter; nothing in the VM or object.Store changes.
- Test proves blob bytes appear in NO accepted block, the block stays
  manifest-small, the manifest IS on chain, and GET byte-reconstructs.

luxfi packages only; zapdb-native; no protobuf in state; SDKROOT needed on macOS.
2026-06-25 13:28:14 -07:00
zeekay 0a20786095 schain: M0 storage-chain VM skeleton (manifest → zapdb through a real block)
Fork dexvm's commit discipline into a new storage VM (VMID 'schain'): dual-DB
(durable zapdb base + per-block versiondb), pure-deterministic ProcessBlock, ONE
CommitBatch at Accept (mirrors dexvm/vm.go:1194, block.go:159). M0 op: PutManifest
{bucket,object,fileIds,size,etag} staged in the version layer, durable only after
Accept; GetManifest reads committed state. Proves the on-chain-metadata / off-chain-
blob architecture for an embedded decentralized S3 'S-chain'.

schain_test.go (real on-disk zapdb.New): manifest absent in base DB before Accept,
present after — versiondb→CommitBatch proven end-to-end; +Reject-aborts-staged and
tx-codec round-trip. go vet + -race clean.

M1 notes (in-code): add a manifest STATE ROOT before multi-validator consensus
(dexvm computeStateRoot pattern); keep any future cross-chain blob-ref leg separate
from the manifest commit; add genesis/config. Next: wire the S3 Sig-V4 gateway
(PUT→needle off-chain + PutManifest on-chain; GET→manifest+needle stream).
2026-06-25 12:44:03 -07:00
zeekay 2b42aa7f12 decomplect: drop redundant brand denylist from dexvm/registry
forbiddenChainTokens + IsForbiddenChainRef named the off-network white-label
universe by string to reject it. That negative gate is redundant: the
positive gate (ChainVerifier) already admits only real on-chain Lux assets,
so an off-network universe can never pass regardless. Removed the brand list
and the func; kept the genuinely-distinct guards (mock/synthetic liquidity,
ASCII-ticker-as-id). AssertNoForbiddenAssetRefs keeps its signature for the
gate's call shape. No brand strings remain; builds clean.
v1.3.21
2026-06-24 11:10:32 -07:00
zeekay 276ab4ac19 deps: bump precompile v0.5.59 + consensus v1.25.21 + evm v1.99.39 (no stragglers) 2026-06-23 09:28:09 -07:00
zeekay af19a32452 deps: go mod tidy after merging consensus v1.25.19 2026-06-23 01:05:24 -07:00
zeekay f29fe9895c merge: origin/main (consensus v1.25.19 Round1 fix, afaeeef) into dex-real-assets-registry 2026-06-23 01:04:04 -07:00
zeekay c5de091147 dexvm: Gate-A real-assets-only startup enforcement + LABELED rename
VM.Initialize now runs enforceRealAssetsOnly before marking initialized:
fail-closed over the consensus-supplied network identity (never operator-
spoofable). Refuses synthetic flags on a value net, value activation with no
legal consensus mode, a wrong-net/Liquidity manifest, value with no real
assets, bad allowed-kinds. Config gains the real-assets fields (manifest
path + SHA-256 pin, synthetic flags, consensus mode, caps/halt). Renames
HONEST_VALIDATOR_LAUNCH -> HONEST_VALIDATOR_LABELED across the registry
(value qualified by what it IS — labeled CFT parity — not when it runs).
Adds cmd/dex-assets-validate (CI proves every manifest entry real against
the target net RPC) + the validate-asset-manifests workflow. dexvm tests +
registry gate tests green.
2026-06-23 01:03:49 -07:00
zeekay e590e4a91c registry(dexvm): golden KAT — AssetID byte-parity with dexcore (MED-1)
Mirror the canonical cross-home golden KAT: registry.DeriveAssetID must reproduce
the EXACT 32-byte AssetID bytes that luxfi/dex pkg/dexcore.DeriveAssetID asserts
(AssetIDGoldenVectors). Fixed (networkID=2, source chain all-0x11, {ERC20, EVM_NATIVE,
UTXO}) → expected id vectors, byte-identical to the dexcore side. This locks the
resolve↔register equivalence the 0x9999 value path depends on: a registered AssetID
(here) and a swap-derived AssetID (dexcore, via the precompile resolver) name the SAME
asset by the SAME id. A drift in either home's preimage discipline now fails CI in BOTH
suites — a real consensus-identity divergence caught in test, never in prod.

Verified: CGO_ENABLED=0 go test ./dexvm/registry -run GoldenKAT green; the same three
vectors pass in dex/pkg/dexcore.
2026-06-22 22:51:00 -07:00
zeekay 5ff9aca513 feat(dex/registry): embed per-network asset manifests + localnet native builder
Make the per-network asset manifests (mainnet/testnet/devnet) available to a node
binary with no filesystem dependency, so the EVM plugin can build the 0x9999
value-path AssetResolver from the CI-approved, content-hashed asset set compiled
into the binary. Selection is by EVM chainID (96369/96368/96370). Localnet (1337),
whose C-Chain id is environment-specific, gets a runtime-rooted native-only manifest
(LocalnetNativeManifest) synthesised from the node's live cChainID — never a constant.

- embedded.go: //go:embed the three manifests; EmbeddedManifestFor(evmChainID) +
  LocalnetNativeManifest(networkID, cChainID).
- manifest.go: split decodeManifestBytes out of loadManifestBytes so the embed path
  and the disk path share one decoder (shape-validate + content-hash + unknown-field
  rejection identical for both).
2026-06-22 20:56:23 -07:00
zeekay 33634aad32 revert(dexvm): drop C<->D keeper RPC seam (dex.submitTx + dex.getSettlement)
The keeper/venue model is abandoned. The D-Chain (dexvm) trades via its own
consensus-driven state transition (matcher-at-Verify), not via an off-chain
keeper polling dex.getSettlement to drive 0x9999. Revert the three v1.3.16
commits' additions to the v1.3.15 working state:

  - vm.go:           remove GetSettlement + the proceeds-coordinate record
  - chainvm.go:      remove the SubmitTx RPC seam additions
  - api/service.go:  remove dex.submitTx + dex.getSettlement handlers
  - state/state.go:  remove PutSettlement/GetSettlement settlement index
  - api/submit_test.go, state/settlement_test.go: remove keeper-RPC tests

Deps are unchanged from v1.3.15 (go.mod/go.sum identical). bridgevm + dexvm
build clean and all tests pass.
2026-06-22 03:11:57 -07:00
zooqueen 557a6767f2 deps: bump consensus v1.25.18 -> v1.25.19 (Round1 API fix; pulsar v1.1.5)
consensus v1.25.18 quasar.go/epoch.go call corona/threshold.Round1 single-value,
but corona's Round1 now returns (Round1Data, error) -> the quantumvm plugin (and
any consensus consumer) fails to compile. v1.25.19 has the Round1 fix. All 11 VM
plugins build clean.
v1.3.17
2026-06-22 03:11:46 -07:00