Commit Graph
157 Commits
Author SHA1 Message Date
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
zeekay 2a99ad8f20 merge: dexvm C<->D keeper RPC seam (dex.submitTx + dex.getSettlement)
Exposes the two RPCs the off-chain dexkeeper drives the 0x9999 two-phase atomic
DEX seam with:
  - dex.submitTx: mempool entry so the keeper can inject ImportTx (consume the
    C->D atomic object) + the settling RelayOrderTx (clob_submit, CollateralRef-bound)
  - dex.getSettlement(collateralRef): returns the D->C proceeds object's
    (outputID, amount) recorded deterministically in settleFromFills, the Phase-B input

State.Put/GetSettlement write through versiondb (commit-atomic, consensus-safe).
RelayOrderTx.Verify permits an unsigned relay (authority binds to the escrow owner,
not From). dexvm/api + dexvm/state tests pass.
v1.3.16
2026-06-22 02:43:37 -07:00
zeekay 99ccfa4766 dexvm: record proceeds coordinate + dex.getSettlement RPC (keeper Phase-B input)
settleFromFills now records the proceeds D->C object's (outputID, amount) under the
collateral ref (the C->D intentID) via state.PutSettlement, written into the versiondb
so it commits atomically with the block. outputID = deriveUTXOID(settlementExportTx.ID(),
0) — the SAME key executeExport puts into shared memory — and the amount is the realized
proceeds; both are pure functions of consensus-agreed inputs (blockHash+txIndex seed the
export identity), so every validator writes the identical record and the StateRoot stays
consensus-safe. The record is purely informational (moves no value; never consulted by
the deterministic settle/consume path).

dex.getSettlement(collateralRef) reads it back (VM.GetSettlement -> state.GetSettlement)
so the off-chain keeper, after submitting the ImportTx + settling RelayOrderTx and the
proposer settles, learns the D->C object's outputID + amount and builds the C-side Phase-B
ImportSettlement (DS01 outputID|amount|intentID) that consumes the object and emits DEXFill.
This closes the keeper's export-discovery gap.

Tests: 3 state round-trip/isolation/escrow-coexistence + 3 RPC pending/found/bad-ref, all
pass (CGO_ENABLED=0; local cgo accel toolchain is broken — CI builds linux/amd64).
2026-06-22 02:23:46 -07:00
zeekay 0d8a06af7d dexvm: expose SubmitTx RPC (the C<->D keeper mempool seam)
The dexvm is a stateless atomic ZAP proxy whose ImportTx/RelayOrderTx/ExportTx
are deterministic block inputs, drawn from the proposer's pending pool. That pool
(ChainVM.SubmitTx -> pendingTxs + engine notify) was unreachable from outside the
process: CreateHandlers exposed only Ping/Status/Relay, so an off-chain keeper had
no way to inject the ImportTx that consumes the C->D atomic object the C-side
0x9999 SubmitSwapIntent writes, nor the settling RelayOrderTx. The full two-phase
seam therefore could not land a DEXFill.

This adds a dex.submitTx JSON-RPC method (api.Service.SubmitTx) that forwards
wire-encoded tx bytes to an api.TxSubmitter and returns the canonical
ids.Checksum256 tx id. The submitter is wired ONLY on the node path
(ChainVM.CreateHandlers builds the service with the ChainVM as submitter); the
inner VM's read-only CreateHandlers (standalone/test) reports ErrSubmitUnavailable.
Bootstrap-gated; admission/fee gate stays in ChainVM.SubmitTx. 6 unit tests cover
unavailable/not-bootstrapped/bad-hex/empty/success(verbatim+TxID)/submitter-error.
2026-06-22 01:23:00 -07:00
zeekay d0124f77f8 deps: consume evm v1.99.36 + precompile v0.5.56 (dexvm swap-seam parity) v1.3.15 2026-06-21 22:48:26 -07:00
zeekay a40305d743 deps: bump vm v1.2.5 + api v1.0.15 (UTXOAssetID rename — fixes LuxAssetID build break, latest patch) 2026-06-21 22:44:55 -07:00