Decomplect obtain-fills (proposer, BuildBlock, once network-wide) from settle-from-carried-
fills (deterministic, Accept). No validator relays at Verify/Accept -> block output is a pure
fn of (height, carried ts, tx bytes, carried fills) -> byte-identical StateRoot. NEW
carried_fills.go wire section (block + vertex) with a RESERVED fill-signature field for the
future trustless path (no 2nd wire bump). #7 computeStateRoot completed (commits state+atomic).
TestRED_PerValidatorRelay_SplitsConsensus PASS (0 cross-validator relay); redteam 21/0 (37/0);
conservation/determinism green; race-clean. NETWORK-UPGRADE-GATED wire-format change.
orderbook/perpetuals/lending/mev dirs + backend.go deleted; vm.go 2230->830 lines.
NEW atomic.go (shared-memory import/export at Block.Accept) + relay.go (rpc.ZAPDial,
frame byte-identical to zapwire, no luxfi/dex dep). State holds only nonce:/receipt:/
consumed: -- no order/pool/position keys. txs reduced to import/export/relay. NEW
conservation_test.go::TestEndToEndAtomicValueConservation. Verified -tags dchain build+tests
green -race; grep matchAllOrders|tryMatch|crossOrders empty.
Replaces non-canonical scale-set / org-prefixed labels with the
existing labels every arcd host registers with. Matches evo for
amd64 and spark for arm64. No new labels added.
Picks up the 2026-06-06 relicensing:
- luxfi/vm v1.2.0 → Lux Ecosystem License v1.2 (was Lux Research v1.0).
License-only retag of the extracted runtime; patent reservation
preserved for runtime optimization surfaces, royalty-free for
Descending Chains.
- luxfi/sampler v1.1.0 → BSD-3-Clause (was Lux Research v1.0).
License restored to match the luxfi/node provenance (originally
extracted from node/utils/sampler).
- luxfi/staking v1.5.0 → BSD-3-Clause (was Lux Research v1.0).
License restored to match the luxfi/node provenance.
No code changes in this commit — go.mod/go.sum only.
OSS public chains module must not reference the private GPU plugin
implementation by repo name or filesystem path. Two leak types fixed:
1. Hard compile-time dependency in quantumvm:
`#cgo CFLAGS: -I${SRCDIR}/../../../lux-private/gpu-kernels/include`
Vendor the public ABI v14 header (backend_plugin.h + crypto.h, 1475
LOC total) into chains/internal/luxgpu/include/lux/gpu/ and point
CFLAGS at the in-tree path. Plugin DSO is still dlopen'd at runtime
— only the public C ABI surface is vendored.
2. Soft leakage — `LUX_PRIVATE_GPU_KERNELS_DIR` env vocabulary, dev-tree
probe paths to ~/work/lux-private/gpu-kernels/build/..., prose
mentions of "lux-private/gpu-kernels" in package comments.
All replaced with `LUX_GPU_PLUGIN_DIR` (universal env) and generic
"GPU plugin" prose.
Bridges affected: aivm, bridgevm, dexvm, quantumvm, thresholdvm.
All 139 tests PASS post-scrub under both build tags. No functional
behavior change — only path / env / prose.
Adds four Go files to chains/quantumvm so the Q-Chain VM can call into
the lux-private/gpu-kernels plugin's batched ML-DSA verify, ML-DSA sign,
and SLH-DSA verify slots without a build-time pkg-config dependency:
* backend.go cross-build types: Backend enum, GPUBackend
interface, MLDSAMode + MLDSA65* size constants,
SLHDSAVariant enum + per-variant pk/sig widths
(FIPS 204 / FIPS 205), ErrGPUNotAvailable,
AutoBackend(). Build-tag-free so vm.go can
reference the surface regardless of CGO_ENABLED.
* quantumvm_gpu.go cgo dlopen probe at init() in order
(cgo) cuda -> hip -> metal -> vulkan -> webgpu,
resolving lux_gpu_backend_init and verifying
the ABI v14 cookies (abi_version + vtbl_size)
plus the load-bearing op_mldsa_verify_batch
slot before opening a LuxBackendContext. Three
vtbl-slot wrappers dispatch through C
trampolines into op_mldsa_verify_batch,
op_mldsa_sign_batch, and the SHAKE-128f /
SHAKE-192f variants of op_slhdsa_verify_batch.
* quantumvm_gpu_nocgo.go symmetric noGPUBackend stub returning
ErrGPUNotAvailable on every method so the
package surface is identical under !cgo.
* quantumvm_gpu_test.go round-trip test: dlopens the configured
plugin (LUX_GPU_PLUGIN_DIR override or default
loader path), generates an ML-DSA-65 keypair +
signs a 64-byte digest via circl, then submits
the triple through MLDSAVerifyBatch and asserts
results[0] == true. Cleanly SKIPs when no
plugin loaded or when the backend returns
NOT_SUPPORTED for op_mldsa_verify_batch (e.g.
plugins built without LUX_GPU_LINK_CRYPTO).
The probe is header-only: cgo CFLAGS points at
${SRCDIR}/../../../lux-private/gpu-kernels/include for the v14 vtbl
layout, but the .dylib / .so is dlopen'd at runtime - no pkg-config,
no static link, no luxcpp/install dependency. Existing quantumvm
vm.go, quantum/signer.go, and quantumvm/quasar.go are untouched;
consumers opt in explicitly when ready. Both 'go build ./quantumvm/...'
and 'CGO_ENABLED=1 go build ./quantumvm/...' are green.
Test output with LUX_GPU_PLUGIN_DIR pointing at a freshly-built
libluxgpu_backend_vulkan.dylib:
--- PASS: TestAutoBackend_Probes (0.00s) [backend=vulkan]
--- PASS: TestMLDSAVerifyBatch_RoundTrip (5.56s) [backend=vulkan,
op_mldsa_verify_batch honest verify -> true]
--- PASS: TestMLDSAVerifyBatch_LengthMismatch (0.00s)
--- PASS: TestMLDSAVerifyBatch_NonMLDSA65Skips (0.00s)
Adds four Go files to chains/dexvm so the DEX VM can call into the
lux-private/gpu-kernels plugin DEX launchers (AMM xyk + CLOB matcher +
BookArena lifecycle) without a build-time pkg-config dependency:
* backend.go cross-build types: GPUBackend enum, LuxAmmReservePair
(16 bytes, byte-equal to include/lux/gpu/dex.h),
CLOB ABI constants, ErrGPUNotAvailable.
* dexvm_gpu.go (cgo) dlopen probe at init() in order
cuda -> hip -> metal -> vulkan -> webgpu, resolving
lux_<backend>_{amm_xyk_batch,dex_clob_match,
dex_clob_arena_create,dex_clob_arena_destroy}.
Exposes AutoBackend / AMMSwap / CLOBMatch /
ArenaCreate / ArenaDestroy. unsafe.Sizeof guards
the AMM struct layout against silent ABI drift.
* dexvm_gpu_nocgo.go symmetric stubs returning ErrGPUNotAvailable so
the package surface is identical under both tags.
* dexvm_gpu_test.go round-trip test: dlopens the metal plugin (or any
other found under LUX_GPU_PLUGIN_DIR), runs a
single-pair AMM swap, asserts byte-equal output
to the CPU oracle (constant-product 1000 -> 1998
at rx=1M, ry=2M).
Existing dexvm vm.go / orderbook logic is untouched - consumers opt in
explicitly when ready. Both 'go build ./dexvm/...' and
'go build -tags cgo ./dexvm/...' are green.
Test output with LUX_GPU_PLUGIN_DIR=$HOME/work/lux-private/gpu-kernels/build-metal/backends/metal:
--- PASS: TestGPUAMMSwapRoundTrip (0.08s) [backend=metal]
--- PASS: TestGPUAMMSwapEmpty (0.00s)
Mirrors the evm/cevm pattern (cevm_cgo.go / cevm_nocgo.go +
backend_cgo.go). Three new files in bridgevm/:
backend.go — Backend enum, layout structs (byte-equal to
ops/bridgevm/cuda/bridgevm_kernels_common.cuh),
ErrGPUNotAvailable, GPUBackend interface,
unsafe.Sizeof asserts in init().
bridgevm_gpu.go cgo — direct dlopen(3) + dlsym(3) for the per-backend
plugin libluxgpu_backend_<bk>.{so,dylib}.
Probes cuda → hip → metal → vulkan → webgpu in
order; first plugin exposing all five
lux_<bk>_bridgevm_* launchers wins.
bridgevm_gpu_nocgo.go — stub returning ErrGPUNotAvailable on every method.
Direct dlopen (NOT pkg-config) so the public chains module builds without
the private gpu-kernels repo present. Search path: \$LUX_GPU_PLUGIN_DIR,
\$LUXCPP_PREFIX/lib/lux-gpu, \$LUXCPP_PREFIX/lib, the dev tree under
~/work/lux-private/gpu-kernels/build/backends/<bk>, CWD, and finally the
system loader's default.
Five launcher signatures dispatched via cgo trampolines (one per shape) —
matches the C ABI in include/lux/gpu/bridgevm.h. Layout structs include
explicit '_pad*' fields to keep Go sizeof() byte-equal to the C++
alignas(16) layout; the init() sizeof asserts catch any drift at process
start with a per-struct panic message.
vm.go / bridge_signing.go / mpc.go are NOT touched — this is a thin
substrate; the round applier opts in later when it's ready to route
through ActiveGPUBackend().
bridgevm_gpu_test.go — four tests under //go:build cgo:
TestActiveBackend — AutoBackend() returns a known tag.
TestStubReturnsErrGPUNotAvailable
— BackendNone path returns the sentinel.
TestLayoutSizesMatchHeader — runtime-equivalent of the C++ static_asserts.
TestSignerApplyZeroFixture — dlopen + dlsym + zero-fixture round trip
through lux_<best>_bridgevm_signer_apply.
Builds verified under both CGO_ENABLED=0 (nocgo path active) and
CGO_ENABLED=1 -tags cgo (cgo path active). All four cgo-tagged tests
pass on the local dev box (Vulkan plugin auto-detected from the
gpu-kernels build tree).
Mirrors chains/evm/cevm/cevm_cgo.go + cevm_nocgo.go but uses pure
dlopen/dlsym instead of pkg-config so the chains module compiles
without the lux-private/gpu-kernels build tree present.
New files
=========
* aivm_gpu.go (cgo) — GPUBackend with six host-launcher
wrappers (AttestationApply,
ProvenanceApply, AnchorApply,
EpochTransition, InferenceStep,
ProofVerify). Layout structs match
ops/aivm/cuda/aivm_kernels_common.cuh
byte-for-byte; an init() panics on drift.
* aivm_gpu_nocgo.go — stub GPUBackend returning
ErrGPUNotAvailable so vm.go's gpu paths
degrade cleanly.
* backend.go (cgo) — dlopen probe at process start. Walks
libluxgpu_backend_cuda.so → hip.so →
metal.dylib → vulkan.{so,dylib} →
webgpu.{so,dylib}. Honors
LUX_GPU_PLUGIN_DIR /
LUX_PRIVATE_GPU_KERNELS_DIR /
LUXCPP_PREFIX env. Exposes the public
parallel.SetBackend(AutoAIVM) analogue
(Mode / SetBackend / ActiveMode /
EffectiveBackendKind).
* aivm_gpu_test.go (cgo) — layout-drift guard + dlopen round-trip:
opens the best-available plugin and
calls lux_<kind>_aivm_attestation_apply
with a zero-input fixture, asserts rc=0
and applied_out=0.
vm.go is NOT modified. Two internal hooks (gpuAvailable() and
gpuTransitionApply(...)) are exported under both build tags so
Build/Verify/Accept can opt into the GPU transition when the wiring
on the consumer side is ready.
Test output (M1 Max)
====================
* CGO_ENABLED=0 go build ./aivm/... → clean
* CGO_ENABLED=1 go build -tags cgo ./aivm/... → clean
* CGO_ENABLED=0 go test ./aivm/ → ok
* CGO_ENABLED=1 LUX_PRIVATE_GPU_KERNELS_DIR=...
go test -tags cgo ./aivm/ → ok
TestLayoutSizes PASS
TestRoundTripAttestation PASS
aivm: using plugin metal at
…/build/metal-only/backends/metal/libluxgpu_backend_metal.dylib
TestRoundTripProofVerifyZeroOps PASS
TestNoPluginFallback PASS
TestModeAPI PASS
Was using canonical workflow defaults (hanzoai-scoped labels) which
do not dispatch for luxfi org. Switched to round-robin pool labels:
runner-amd64: luxfi-amd64 (evo)
runner-arm64: luxfi-arm64 (spark + dbc, round-robin)
runner-deploy: lux-deploy-linux-amd64 (DO ARC pool)
The luxfi/bridge daemon's bchain client (internal/bchain.Client) calls
bridge_getInfo / bridge_getSupportedChains / bridge_getChainConfig /
bridge_getSignature against the B-Chain RPC for permissionless discovery
+ MPC signature retrieval. Until now BridgeVM only implemented the
settlement subset (estimateFee, submitRequest, getStatus, cancelRequest,
health, getMPCPublicKey) — daemons fell back to assumed local values
for the discovery surface, a centralization vector.
This commit:
- Adds GetBridgeInfo, GetSupportedChains, GetChainConfig, GetSignature
to bridgevm.Service.
- Wires all four into the JSON-RPC dispatcher (bridge_* and bridge.*
aliases).
- GetSignature reads from the swap-store record (populated by the MPC
signing pipeline once the threshold quorum aggregates shares).
Distinguishes "no such swap" from "still signing" via separate errors
so callers can poll without dispatching on a generic error code.
- GetChainConfig matches the ChainID case-insensitively and surfaces
the requested id in the error message when the chain is not in
SupportedChains.
- Adds 6 table-driven tests covering happy paths, not-found, empty
params, and the not-yet-available signature poll.
After this lands the luxfi/bridge daemon stops needing any cached
fallback for these four endpoints — the B-Chain VM is authoritative
end-to-end. Daemon code itself needs no change; its bchain.Client
already calls these method names.
Tests: go test ./bridgevm/... — 36/36 pass (30 pre-existing + 6 new).
The luxfi/bridge daemon's bchain client (internal/bchain.Client) calls
bridge_getInfo / bridge_getSupportedChains / bridge_getChainConfig /
bridge_getSignature against the B-Chain RPC for permissionless discovery
+ MPC signature retrieval. Until now BridgeVM only implemented the
settlement subset (estimateFee, submitRequest, getStatus, cancelRequest,
health, getMPCPublicKey) — daemons fell back to assumed local values
for the discovery surface, a centralization vector.
This commit:
- Adds GetBridgeInfo, GetSupportedChains, GetChainConfig, GetSignature
to bridgevm.Service.
- Wires all four into the JSON-RPC dispatcher (bridge_* and bridge.*
aliases).
- GetSignature reads from the swap-store record (populated by the MPC
signing pipeline once the threshold quorum aggregates shares).
Distinguishes "no such swap" from "still signing" via separate errors
so callers can poll without dispatching on a generic error code.
- GetChainConfig matches the ChainID case-insensitively and surfaces
the requested id in the error message when the chain is not in
SupportedChains.
- Adds 6 table-driven tests covering happy paths, not-found, empty
params, and the not-yet-available signature poll.
After this lands the luxfi/bridge daemon stops needing any cached
fallback for these four endpoints — the B-Chain VM is authoritative
end-to-end. Daemon code itself needs no change; its bchain.Client
already calls these method names.
Tests: go test ./bridgevm/... — 36/36 pass (30 pre-existing + 6 new).
The bridge is permissionless and non-custodial. Authoritative quote
math and swap state must live where the validator quorum runs
consensus — in this VM — not in any single daemon's local cache.
This change moves both into bridgevm and exposes them via JSON-RPC
so any client can reach the same settlement decisions.
New RPC methods:
bridge_estimateFee — authoritative quote computation
bridge_submitRequest — create + quote-snapshot
bridge_getStatus — read swap by id
bridge_cancelRequest — idempotent cancel
bridge_health — liveness probe
bridge_getMPCPublicKey — threshold group public key
Architecture:
- QuoteEngine (quote.go) StaticPriceFeed → quorum oracle later
- SwapStore (swap_store.go) in-memory genesis impl; block-persistence next
- Service methods (rpc.go) JSON-RPC dispatch (snake + dot aliases)
Attestation aggregation already lived in bridge_signing.go via
BridgeSigner + BridgeMessageValidator — no changes needed there.
LP-333 signer-set methods unchanged.
The cross-chain GPU-resident block builder is proprietary. The package
has been relocated to lux-private/multichain. Public chains repo keeps
only the per-chain VM implementations (evm, dexvm, fhevm, etc.) — those
stay OSS and are what external operators interact with.
What's still public here:
evm/cevm GPU EVM block dispatch (V4 ABI)
dexvm Lux DEX VM (CLOB + AMM)
bridgevm, thresholdvm,
zkvm, quantumvm, ... other per-chain VMs
What moved private:
multichain Cross-chain atomic block builder
(the in-memory GPU multi-chain coordinator
that runs C+D+F transitions in one block)
Public consumers that need cross-chain orchestration can still use the
per-chain Execute APIs directly. The atomic multi-chain coordinator is
the closed-source piece.
New package github.com/luxfi/chains/multichain that runs C-Chain (EVM),
D-Chain (DEX), F-Chain (FHE) transitions atomically in one block. A tx
that calls a cross-chain precompile no longer bounces CPU→GPU→CPU→GPU
for each chain boundary — the Coordinator orchestrates the per-chain
kernels via a fixed-point loop until every declared CrossRef resolves,
then seals all three sub-blocks together.
API:
CrossChainBatch { CChainTxs, DChainOps, FChainOps, CrossRefs }
MultiChainBlock { CChainBlock, DChainBlock, FChainBlock, Receipts, StateRoots }
ChainKernel.Execute(in) → (out, err)
ChainKernel.Seal() → (subBlock, stateRoot, err)
Coordinator.ExecuteBlock(batch) → (*MultiChainBlock, err)
Atomicity: any kernel error or unresolved CrossRef rejects the entire
block — no partial commit. CrossRefs in the input batch are ASSERTIONS
(post-conditions) verified at seal time; they are NOT pre-dispatched
queues. The kernel is the single source of truth for cross-chain calls.
Phase 1 (now): host coordinates sequential per-chain dispatch with
goroutine parallelism between independent chain kernels each round.
Phase 2 (luxcpp shared-MvMemory, future): the same BlockBuilder API is
satisfied by a single GPU kernel that runs all three chains over shared
device memory. Callers don't change — only the underlying dispatch.
Tests: 4 subtests cover standalone chains, cross-chain dispatch with
cref resolution, unresolved-cref rejection, and missing-kernel detection.
All pass.
Collapses the Go-side cgo entry points to a single canonical path:
ExecuteBlock(backend, threads, txs, ctx, state)
└─> C.gpu_execute_block_v4(...)
ExecuteBlockV1(backend, txs) -> ExecuteBlock + downcast
ExecuteBlockV2(backend, threads, txs) -> ExecuteBlock(nil, nil)
ExecuteBlockV3(backend, threads, txs, ctx) -> ExecuteBlock(ctx, nil)
ExecuteBlockV4 = ExecuteBlock (rename)
No more parallel C cgo calls. The luxcpp-side gpu_execute_block / _v2 / _v3
entries are now dead and can be removed in the next luxcpp release.
Health probe: the `callBridge` exception now applies on every backend
instead of GPU-only. The old V2 path masked kernel status with TxOK; the
canonical path surfaces the real CallNotSupported. Until the V5 kernel
implements CALL/CREATE on device, the probe accepts any clean return as
"dispatcher reached the bridge" — truth, not a mask.
Per "kill this version proliferation and have one and one way only":
- ExecuteBlock(backend, threads, txs, ctx, state) is the single canonical
Go-side entry. Takes everything, returns everything.
- ExecuteBlockV1/V2/V3/V4 are marked Deprecated and retained as test-file
compatibility shims pointing at the same underlying C ABI. New code MUST
use ExecuteBlock.
- chains/evm/cevm/parallel/parallel.go switched to ExecuteBlock. parallel
test suite still green.
The C-side ABI sprawl (gpu_execute_block / _v2 / _v3 / _v4) stays for now —
deleting them is a luxcpp/cevm refactor that ripples through internal
dispatch. The Go surface that consumers see is single-entry today; the C
cleanup is the next bounded follow-up in luxcpp.
CALL/CREATE on device — the "100% GPU EVM" gap — remains kernel-side work
in luxcpp/cevm/lib/evm/gpu/kernel/. The host-side scaffold (call_frame.hpp,
code_resolver.cpp, evmc_bridge.cpp, the GpuHostBridge type) is real but
the kernel CALL handler still emits TxCallNotSupported. That's the next
multi-week kernel push, not a Go-side change.
Per "no LUX_ in front of shit for no reason" — the strict-mode knob is
cevm-specific, not Lux-specific.
LUX_CEVM_STRICT → CEVM_STRICT
Default unchanged (strict=on). Docs updated.
Removes the silent (nil, nil) "fall through to Go EVM" return at the three
points where cevm V4 cannot complete a block on device:
- tx value exceeds uint64
- kernel returns TxCallNotSupported (CALL/CREATE family)
- block has logs (V4 ABI has no per-tx logs)
Replaces each with declineBlock(reason, block, tx_index):
* Default (LUX_CEVM_STRICT=1): returns ErrGPUEVMRequired so the caller
cannot silently shadow-execute on the Go EVM. The lie that hid the
missing GPU CALL/CREATE is removed.
* Legacy (LUX_CEVM_STRICT=0): returns (nil, nil) for V4→V5 transition
rollback only.
Every fallback records backend.RecordFallback(FallbackUnsupported, "cevm:<reason>")
so production observability surfaces the rate at which blocks decline.
V5 kernel spec lands at evm/cevm/V5_ABI.md: gpu_execute_block_v5 with
code-table, storage_root threading, per-tx logs. Strict mode becomes
unconditional once V5 ships and the env var retires.
The user's verbatim demand:
> "fix ALL of a-chain so it works in MULTI-CHAIN for any Lux public L1 L2
> we must work without any trusted daler or other"
WHAT CHANGED
DefaultConfig (PUBLIC-BFT-SAFE DEFAULTS):
- VerificationMode: ModeOptimistic (default) — providers post results
with bond; N-block challenge period; uncontested = finalised;
challenged = fraud-proof flow with slashing. NO trusted dealer,
NO TEE assumption, NO single point of trust.
- RequireTEEAttestation: false (was true) — TEE is OPTIONAL
acceleration, NOT a registration gate. Providers without TEE can
register and operate under the optimistic-verification flow.
- HostChainID: "primary" — AIVM is chain-agnostic. Override per
L1/L2 instance (Hanzo, Zoo, Pars, Liquidity, etc.).
- ChallengeWindowBlocks: 100 (~5-10 min finality)
- RedundancyFactor: 3 (for ModeMultiPartyRedundant — M-of-N consensus)
- MinProviderBond: 1000 LUX equivalent (slashed on fraud proof or
majority divergence)
DefaultPermissionedConfig (OPT-IN, NOT public mainnet):
- VerificationMode: ModeTEEAttested
- RequireTEEAttestation: true
- For regulated/KYC'd subnets where TEE-vendor trust is acceptable
by policy
THREE VERIFICATION MODES
1. ModeOptimistic (default, public-BFT): post + challenge + fraud proof.
2. ModeMultiPartyRedundant: M-of-N validators run same task, consensus
on output hash. Public-BFT-safe. M× compute cost.
3. ModeTEEAttested: opt-in, permissioned subnets only. TEE is folded
into the trust score as ACCELERATION (skip challenge for high
trust score); NEVER changes the trust root.
RegisterProvider:
- Public-chain path: no TEE required. Provider bond + challenge flow
enforce correctness.
- Permissioned path (RequireTEEAttestation=true): TEE attestation
required, must meet MinTrustScore. Error message points operators
at the public-chain alternative.
MULTI-CHAIN DEPLOYABILITY
- HostChainID tells AIVM which L1/L2 it's running on.
- "primary" = anchor to Q-Chain via MerkleAnchorFreq blocks.
- White-label L1/L2 = anchor to host chain's own finality (Pulsar
for PQ-finality, Corona for Corona-finality, etc.).
- AIVM code is identical across all chains; only the HostChainID
config field differs.
TESTS
- TestDefaultConfig pins public-BFT-safe defaults: TEE NOT required,
mode is Optimistic, HostChainID is "primary".
- TestDefaultPermissionedConfig pins the opt-in permissioned path.
- TestVerificationMode_PublicBFTSafe asserts default mode is always
one of (Optimistic, MultiPartyRedundant), NEVER TEE-attested as
the trust root.
go.mod refresh as side-effect of adding luxfi/node@latest for
vms/types/fee.
graphvm: G-Chain is read-only (graphql.go explicitly rejects `mutation`
operations). Wires NoUserTxPolicy{} so any attempted user-tx is refused
at the boundary. Field + Initialize gate added; fee.Validate ensures
the policy choice is sound at boot.
oraclevm + relayvm: these chains are thin re-export shims of the
canonical impls in luxfi/oracle/vm and luxfi/relay/vm (which already
have feegate.go). Added in-file pointers documenting where the gate
lives, so future readers don't add a duplicate stub here.
Closes the FeePolicy migration: every VM in luxfi/chains/ either has
its own feegate.go (8 VMs), points to the canonical impl (oraclevm +
relayvm), or is exempt via native gas (evm). The matrix at
node/CLAUDE.md FeePolicy section is now complete.
Replace the hand-rolled ${SRCDIR}/../../../../luxcpp/... CFLAGS+LDFLAGS
blocks in cevm_cgo.go and cevm_secp256k1.go with the canonical .pc
bundles. External Go consumers building from \$GOMODCACHE no longer
need a luxcpp checkout next to lux/; they only need the install prefix
on \$PKG_CONFIG_PATH.
cevm_cgo.go consumes lux-cevm (libevm + libevm-gpu + transitive
GPU-host static archives).
cevm_secp256k1.go consumes lux-crypto-secp256k1 (libsecp256k1_cpu +
libkeccak_cpu). libsecp256k1_metal stays on the build-tree path with
the existing darwin-only anchor file until a separate .pc bundle for
the Metal driver lands.
M-Chain is committee-driven — keygen and signing requests come from
validators via consensus, not a user mempool. Attach the
fee.NoUserTxPolicy sentinel in Initialize so the boot-time
fee.Validate explicitly approves the no-user-tx posture and any
caller that reaches the gate gets ErrChainAcceptsNoUserTxs.
The 4 mutating-RPC tests (CreateIdentity, RegisterIssuer,
IssueCredential, RevokeCredential) were asserting zero-fee acceptance
— that was the bug. Add "fee": 1000000 to each test JSON so the
contract matches the new gate.
I-Chain's mutating RPCs (CreateIdentity, IssueCredential,
RevokeCredential, CreateProof, RegisterIssuer) are user-tx
admission points. Add a Fee uint64 field to each mutating Args
struct and gate via FlatPolicy at MinTxFeeFloor. Read-only RPCs
(GetIdentity, GetCredential, VerifyCredential, GetIssuer,
ListIssuers, ResolveIdentity, Health) are unchanged.
Add Fee() to the Transaction interface and introduce VM.IssueTx as
the canonical user-tx admission point. The FeePolicy gate refuses
zero-fee tx before they reach the parallel quantum verification
pool. Consensus-internal callers bypass via txPool.AddTransaction.
InitiateBridgeTransfer is the canonical user-tx admission point on
B-Chain — it drives MPC signing capacity. Add a Fee uint64 field to
BridgeMessage and attach a FlatPolicy at MinTxFeeFloor in
Initialize. The fee gate refuses zero-fee transfers before any MPC
signing capacity is consumed. Consensus-internal callers bypass
via pendingBridges / bridgeSigner direct access.
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.
SubmitTask is the canonical user-task admission point on A-Chain — the
HTTP /tasks handler routes through here. Attach a FlatPolicy at
MinTxFeeFloor (1 mLUX) in Initialize and refuse zero-fee tasks before
they enqueue. Fee gate runs after the initialized check so the
existing ErrNotInitialized contract for SubmitTask(nil) on a
shut-down VM is preserved.
The /sendTransaction HTTP entry is the user-mempool admission point on
Z-Chain — it decodes a shielded Transaction and inserts into the
priority-heap mempool. Attach a FlatPolicy at MinTxFeeFloor (1 mLUX)
in Initialize and refuse zero-fee txs at the handler before mempool
pressure. Consensus replay paths bypass the gate.
SubmitTx is the canonical user-mempool entry on D-Chain — every public
submission funnels through here. Attach a FlatPolicy at MinTxFeeFloor
(1 mLUX) in Initialize and gate SubmitTx through ValidateFee so
zero-fee user txs are refused before they touch pendingTxs. Internal
consensus-engine -> VM paths bypass the gate.
The local replace block (luxfi/{evm,precompile,threshold} => ../*) was
transient pending tags that include LP-4200 precompile registry + corona
keyera-lifecycle. All three lines now have shipped tags:
- luxfi/evm v0.18.13
- luxfi/precompile v0.5.23
- luxfi/threshold v1.8.0
Local replaces removed → repo builds cleanly in any isolated context
(Docker, fresh GOPATH, etc.).
Verified locally with GOWORK=off go build ./cmd/plugin per chain:
aivm/bridgevm/dexvm/graphvm/identityvm/keyvm/oraclevm/relayvm/
thresholdvm/zkvm → OK
quantumvm → FAIL (Corona→Corona source migration deferred —
references quasar.CoronaSignature/qcore.CoronaRound1 that
threshold v1.8.0 renamed).
Detailed invention disclosures are sensitive pre-filing material. Moved to
lux-private/patents to preserve international filing rights (EU/JP/KR/CN
absolute-novelty rule). Cross-ref: github.com/lux-private/patents/bundles/