Completes the "one and only one way" wire migration: every VM in this repo now
serializes blocks and txs through luxfi/zap struct-is-wire at fixed field
offsets — no encoding/json, no hand-rolled big-endian, no cursor codec.
- aivm: Block + AIVertex + nested CIntent native-encoded. Fixes a latent
data-loss bug — AIVertex had unexported fields, so encoding/json silently
marshalled it as {}; the native encoder round-trips every field.
- keyvm: Transaction (signing preimage = SigningBytes(), excludes Auth/Sig;
full wire = signing || sig object, so the signed bytes stay a genuine prefix
and authenticate() re-derives a byte-identical preimage) + Block.
- quantumvm: Block + BaseTransaction. Block wire is the quantum-signature
preimage (deterministic fixed offsets, ordered tx list).
- dexvm: block envelope (txs were already ZAP). Preserves UnixNano timestamps,
deterministic tx order, and content-addressed carried fills.
Hardening from the adversarial crypto review (must-holds all verified: no
keyvm signature malleability/replay, no aivm CIntent id-forgery/escrow tamper,
no dexvm ordering/fill tamper, all parsers fail closed):
- keyvm (M1): make the wire strictly canonical. zap follows the root offset
and ignores unreferenced padding inside a message's declared size, so a twin
buffer could decode to identical fields with a different hash (id-malleability).
ParseTransaction now binds the id to the re-serialized (canonical) form and
rejects any non-canonical input at the door — exactly one byte-string
authenticates per logical tx. Regression test: the padded twin is rejected.
- quantumvm (M2): derive block + tx ids as sha256(Bytes()), matching the other
three VMs. The prior ids.ToID(parent||height) over 40 bytes (and ids.ToID over
the >=32-byte tx wire) silently yielded ids.Empty for every block/tx,
collapsing the block store and tx pool to a single slot. The block id is no
longer stored in the wire, so the invariant id == sha256(Bytes()) holds
unconditionally. Regression tests: ids are non-Empty and collision-distinct.
Not in scope (tracked separately, fail-closed today): aivm RewardPerOperator is
escrowed but not bound by ComputeIntentID — a pre-existing design gap that needs
a coordinated A-side + C-side intent-id preimage bump before any non-fail-closed
CCommitVerifier ships.
Builds + tests green under the canonical CGO=0 -mod=readonly recipe; go.mod/go.sum
are the go mod tidy canonical set (indirect deps resolved forward, all within
major). Cryptographer verdict: SAFE TO PUBLISH.
Co-authored-by: Hanzo Dev <[email protected]>
K-Chain's mutating service RPCs (CreateKey, DeleteKey, Encrypt) are
the user-tx admission points. Attach a FlatPolicy at MinTxFeeFloor
in Initialize and add a Fee uint64 field to each mutating Args
struct. The fee gate runs before any key material is allocated or
MPC capacity consumed. Read-only RPCs (ListKeys, GetKey*) are
unchanged — they consume no chain state.
VM consolidation (one module, one place only):
- Removed per-VM go.mod/go.sum/VERSION from {aivm,bridgevm,dexvm,evm,
graphvm,identityvm,keyvm,oraclevm,quantumvm,relayvm,thresholdvm,zkvm}
- All VMs now compile under github.com/luxfi/chains root module
- aivm/, dexvm/, identityvm/, keyvm/, oraclevm/ factory.go + vm.go updated
for AccelHost wiring
cevm Go-side (evm/cevm/):
- cevm.go, cevm_cgo.go, cevm_nocgo.go: ExecuteBlockV4(backend, threads,
txs, ctx, state) with StateAccount snapshot
- cevm_secp256k1.go: BatchRecoverSenders Metal LDFLAGS
- cevm_secp256k1_metal_anchor_darwin.{c,go}: anchor to keep linker from
dropping libsecp256k1_metal.a
- cevm_secp256k1_nocgo.go: stub for non-cgo builds
- plugin.go, fetch-luxcpp.sh, luxcpp-SHA256SUMS, LUXCPP_VERSION:
pinned luxcpp consumption
- evm/cevm/parallel/parallel.go: BlockExecutor adapter calls
cevm.ExecuteBlockV4 with state snapshot built from txs+statedb
Quantum/ZK witness:
- quantumvm/quasar_witness.go, zkvm/zwitness.go
go.mod: removed dead `replace github.com/ethereum/go-ethereum => luxfi/geth`
directive (caused dual-path conflict after math semver bump; no remaining
ethereum/go-ethereum imports in source). go mod tidy clean.
Tests: TestBatchRecoverSenders PASS through Metal anchor (cevm package).
VMs now live in ONE place: chains/<name>/ owns the full implementation.
Previously split across node/vms/<name>/ (impl) + chains/<name>/main.go
(plugin wrapper). Now consolidated.
New layout:
chains/<name>vm/*.go — package <name>vm (VM code)
chains/<name>vm/cmd/plugin/main.go — package main (plugin binary)
chains/<name>vm/go.mod — module github.com/luxfi/chains/<name>vm
Moved 11 VMs: aivm, bridgevm, dexvm, graphvm, identityvm, keyvm,
oraclevm, quantumvm, relayvm, thresholdvm, zkvm.
External callers now import github.com/luxfi/chains/<name> directly.
Each VM builds as a standalone binary that the Lux node loads via
--plugin-dir. Node no longer bakes optional VMs in-process.
VMs: evm, dexvm, aivm, bridgevm, graphvm, identityvm, keyvm,
oraclevm, quantumvm, relayvm, servicenodevm, teleportvm,
thresholdvm, zkvm.
All 13 node-dependent VMs compile and report version correctly.
EVM requires CGo (luxcpp) — builds in CI only.