chains: scrub private-repo leakage from GPU bridges

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.
This commit is contained in:
2026-06-05 15:46:00 -07:00
parent 083543f918
commit b8a2a5c4dc
16 changed files with 1521 additions and 121 deletions
+3 -3
View File
@@ -5,7 +5,7 @@
// Unlike cevm/cevm_cgo.go (which links libevm + libevm-gpu via pkg-config),
// AIVM resolves its GPU backend at PROCESS START via dlopen/dlsym against
// the lux-gpu-kernels plugin DSOs. This keeps the chains module compilable
// without lux-private/gpu-kernels present in the build tree — the plugin
// without the lux GPU plugin present in the build tree — the plugin
// is fully optional.
//
// Lookup order (handled by backend.go):
@@ -201,7 +201,7 @@ func (k BackendKind) String() string {
// Layout-drift guards — match ops/aivm/cuda/aivm_kernels_common.cuh exactly.
//
// The struct bytes Go hands to C MUST match the on-disk layout file at
// ~/work/lux-private/gpu-kernels/ops/aivm/op.yaml — every kernel reads them
// the GPU plugin install tree ops/aivm/op.yaml — every kernel reads them
// via reinterpret_cast. A silent layout shift produces consensus-divergent
// state roots. init() refuses to load if any size drifts.
// =============================================================================
@@ -436,7 +436,7 @@ func init() {
panic(fmt.Sprintf(
"aivm: layout drift — Go sizeof(%s)=%d but on-device layout=%d. "+
"Re-sync chains/aivm/aivm_gpu.go against "+
"~/work/lux-private/gpu-kernels/ops/aivm/cuda/aivm_kernels_common.cuh.",
"the GPU plugin install tree ops/aivm/cuda/aivm_kernels_common.cuh.",
c.name, c.got, c.want))
}
}
+2 -2
View File
@@ -78,14 +78,14 @@ func locatePlugin(t *testing.T) (BackendKind, string) {
// We use the FULL openGPUBackend path (the same one production init()
// uses) rather than rolling our own dlsym so the test exercises the real
// resolution sequence. If openGPUBackend can't find the plugin file
// (CI without lux-private/gpu-kernels), we Skip.
// (CI without the lux GPU plugin), we Skip.
func TestRoundTripAttestation(t *testing.T) {
// Force a re-probe in case the package-level activeBackend was loaded
// before the test environment was set up.
kind, path := locatePlugin(t)
if kind == AvailableNone || path == "" {
t.Skip("aivm: no lux-gpu-kernels plugin DSO found on this host " +
"(set LUX_PRIVATE_GPU_KERNELS_DIR or LUX_GPU_PLUGIN_DIR)")
"(set LUX_GPU_PLUGIN_DIR or LUX_GPU_PLUGIN_DIR)")
}
t.Logf("aivm: using plugin %s at %s", kind, path)
+7 -7
View File
@@ -23,7 +23,7 @@ import (
// Search rules:
//
// 1. LUX_GPU_PLUGIN_DIR (env) — direct directory.
// 2. LUX_PRIVATE_GPU_KERNELS_DIR (env) — root of lux-private/gpu-kernels.
// 2. LUX_GPU_PLUGIN_DIR (env) — root of the lux GPU plugin.
// Probes `<dir>/build/backends/<kind>/<filename>` and
// `<dir>/build/metal-only/backends/<kind>/<filename>`.
// 3. Default install: $LUXCPP_PREFIX/lib (when LUXCPP_PREFIX is set).
@@ -78,9 +78,9 @@ func platformCandidates() []backendCandidate {
//
// - LUX_GPU_PLUGIN_DIR/<filename>
// - LUX_GPU_PLUGIN_DIR/backends/<subdir>/<filename>
// - LUX_PRIVATE_GPU_KERNELS_DIR/build/backends/<subdir>/<filename>
// - LUX_PRIVATE_GPU_KERNELS_DIR/build/metal-only/backends/<subdir>/<filename>
// - LUX_PRIVATE_GPU_KERNELS_DIR/build/vulkan-m1/backends/<subdir>/<filename>
// - LUX_GPU_PLUGIN_DIR/build/backends/<subdir>/<filename>
// - LUX_GPU_PLUGIN_DIR/build/metal-only/backends/<subdir>/<filename>
// - LUX_GPU_PLUGIN_DIR/build/vulkan-m1/backends/<subdir>/<filename>
// - LUXCPP_PREFIX/lib/<filename>
// - bare filename (DT_RUNPATH / DT_RPATH / system search)
//
@@ -93,8 +93,8 @@ func candidatePaths(c backendCandidate) []string {
filepath.Join(dir, "backends", c.subdir, c.filename),
)
}
if dir := os.Getenv("LUX_PRIVATE_GPU_KERNELS_DIR"); dir != "" {
// Match the on-disk layout shipped by ~/work/lux-private/gpu-kernels:
if dir := os.Getenv("LUX_GPU_PLUGIN_DIR"); dir != "" {
// Match the on-disk layout shipped by the GPU plugin install tree:
// `build/backends/<kind>/<filename>` from the default CMake build,
// plus the per-flavor subdirs the kernel team uses for matrix
// builds (metal-only, vulkan-m1, …).
@@ -157,7 +157,7 @@ func autoLoadBackend() *GPUBackend {
// to decide whether to opt into the GPU path.
func ActiveGPUBackend() *GPUBackend {
if activeBackend == nil {
// Defensive: if init() ran before LUX_PRIVATE_GPU_KERNELS_DIR was
// Defensive: if init() ran before LUX_GPU_PLUGIN_DIR was
// set (rare, mostly tests), re-probe via the once-guarded loader.
// Once activeBackend is set the once-guard keeps subsequent calls
// cheap.
+1 -1
View File
@@ -6,7 +6,7 @@ package bridgevm
// BridgeVM GPU substrate — host-side ABI for the five BridgeVM kernels
// (signer_apply, liquidity_apply, message_inbox, message_outbox, transition).
//
// The kernels live in ~/work/lux-private/gpu-kernels under
// The kernels live in the GPU plugin install tree under
// ops/bridgevm/<backend>/ + backends/<backend>/src/bridgevm_launchers.{cpp,mm}
// and are shipped inside the plugin shared libraries libluxgpu_backend_<x>.so /
// .dylib. The Go side dlopens whichever plugin is on disk at process start
+3 -10
View File
@@ -10,7 +10,7 @@ package bridgevm
// resolves the five lux_<bk>_bridgevm_* host launcher symbols via dlsym(3).
//
// Direct dlopen — NOT pkg-config. The plugin lives in
// ~/work/lux-private/gpu-kernels (a private repo); the public chains module
// the GPU plugin install tree (a private repo); the public chains module
// must build without it present. Linking via pkg-config would couple the
// public build to a private dep; dlopen + dlsym lets us probe at runtime
// and degrade cleanly to ErrGPUNotAvailable when the plugin is absent.
@@ -20,7 +20,7 @@ package bridgevm
// 1. $LUX_GPU_PLUGIN_DIR (explicit override, useful in tests / CI).
// 2. $LUXCPP_PREFIX/lib/lux-gpu/ (the install tree).
// 3. $LUXCPP_PREFIX/lib/ (legacy install tree).
// 4. $HOME/work/lux-private/gpu-kernels/build/backends/<bk>/ (dev tree).
// 4. $HOME/work/the lux GPU plugin build/backends/<bk>/ (dev tree).
// 5. The current working directory.
// 6. The system loader's default path (dlopen with bare name).
//
@@ -225,14 +225,7 @@ func candidatePluginPaths(bk Backend) []string {
paths = append(paths, filepath.Join(prefix, "lib", name))
}
// 4) Dev tree — the canonical layout from gpu-kernels' build/.
if home, err := os.UserHomeDir(); err == nil {
paths = append(paths,
filepath.Join(home, "work", "lux-private", "gpu-kernels",
"build", "backends", bk.String(), name))
}
// 5) CWD — last resort before falling back to the loader default.
// 4) CWD — last resort before falling back to the loader default.
if cwd, err := os.Getwd(); err == nil {
paths = append(paths, filepath.Join(cwd, name))
}
+2 -2
View File
@@ -11,7 +11,7 @@ package bridgevm
// cleanly when AutoBackend() == BackendNone (no libluxgpu_backend_*.so on
// the search path), which is the common case in a public chains checkout.
//
// When a plugin IS present (e.g. dev box with ~/work/lux-private/gpu-kernels
// When a plugin IS present (e.g. dev box with the GPU plugin install tree
// built and $LUX_GPU_PLUGIN_DIR set to the build/backends/<bk>/ dir), the
// test exercises signer_apply with a zero descriptor + zero ops + a sized
// signers arena and asserts:
@@ -111,7 +111,7 @@ func TestSignerApplyZeroFixture(t *testing.T) {
bk := AutoBackend()
if bk == BackendNone {
t.Skip("no GPU plugin loaded; set LUX_GPU_PLUGIN_DIR=" +
os.Getenv("HOME") + "/work/lux-private/gpu-kernels/build/backends/<bk> to exercise")
os.Getenv("HOME") + "/work/the lux GPU plugin build/backends/<bk> to exercise")
}
// Zero descriptor → signer_op_count=0, kernel walks no ops. The mode
+5 -5
View File
@@ -14,11 +14,11 @@
// lux_<backend>_dex_clob_arena_destroy
//
// The plugin sources live at:
// ~/work/lux-private/gpu-kernels/backends/<X>/src/dex_launchers.{mm,cpp}
// ~/work/lux-private/gpu-kernels/ops/dex/<X>/...
// the GPU plugin install tree backends/<X>/src/dex_launchers.{mm,cpp}
// the GPU plugin install tree ops/dex/<X>/...
//
// Public ABI header (single source of truth for byte layouts):
// ~/work/lux-private/gpu-kernels/include/lux/gpu/dex.h
// the GPU plugin install tree include/lux/gpu/dex.h
//
// The cgo file (dexvm_gpu.go) does the dlopen + dlsym. The nocgo file
// (dexvm_gpu_nocgo.go) is a thin stub returning ErrGPUNotAvailable.
@@ -83,9 +83,9 @@ var ErrGPUNotAvailable = errors.New("dexvm: GPU plugin not available")
// LuxAmmReservePair mirrors include/lux/gpu/dex.h::LuxAmmReservePair.
// 16 bytes, packed (no padding), little-endian uint64 fields.
// MUST match:
// ~/work/lux-private/gpu-kernels/include/lux/gpu/dex.h::LuxAmmReservePair
// the GPU plugin install tree include/lux/gpu/dex.h::LuxAmmReservePair
// ~/work/lux/dex/pkg/lx/amm.go::ReservePair
// ~/work/lux-private/gpu-kernels/ops/dex/{cuda,metal,wgsl}/amm_xyk*
// the GPU plugin install tree ops/dex/{cuda,metal,wgsl}/amm_xyk*
type LuxAmmReservePair struct {
ReserveX uint64
ReserveY uint64
+1 -1
View File
@@ -16,7 +16,7 @@
//
// Host launcher ABI — exposed by every plugin under the per-backend
// symbol prefix `lux_<backend>_*`. The C signatures come straight from
// ~/work/lux-private/gpu-kernels/backends/{metal/src/dex_launchers.mm,
// the GPU plugin install tree backends/{metal/src/dex_launchers.mm,
// vulkan/src/dex_launchers.cpp}:
//
// int amm_xyk_batch(const void* reserves, const void* amounts,
+2 -47
View File
@@ -19,8 +19,6 @@
package dexvm
import (
"os"
"path/filepath"
"testing"
)
@@ -45,18 +43,11 @@ func constantProductOutCPU(rx, ry, amount uint64) uint64 {
// compares the kernel output to the CPU oracle.
//
// To run: build the plugin (e.g. via cmake at
// ~/work/lux-private/gpu-kernels/build/) and set
// the GPU plugin build tree ) and set
//
// export LUX_GPU_PLUGIN_DIR=$HOME/work/lux-private/gpu-kernels/build-metal/backends/metal
// export LUX_GPU_PLUGIN_DIR=/path/to/dir/containing/libluxgpu_backend_metal.dylib
// go test -tags cgo ./dexvm/ -run TestGPUAMMSwapRoundTrip -v
func TestGPUAMMSwapRoundTrip(t *testing.T) {
if os.Getenv("LUX_GPU_PLUGIN_DIR") == "" {
// Best-effort: try the common build-tree paths so the test
// runs without explicit env setup when run from a dev box
// with a fresh checkout.
findAndSetPluginDir(t)
}
backend := AutoBackend()
if backend == GPUBackendNone {
t.Skipf("no GPU plugin loaded — set LUX_GPU_PLUGIN_DIR to a directory "+
@@ -127,39 +118,3 @@ func TestGPUAMMSwapEmpty(t *testing.T) {
}
}
// findAndSetPluginDir best-effort probe of the well-known build-tree
// locations under ~/work/lux-private/gpu-kernels. Sets
// LUX_GPU_PLUGIN_DIR if a candidate dylib exists. The test still
// skips cleanly if none is found.
func findAndSetPluginDir(t *testing.T) {
t.Helper()
home, err := os.UserHomeDir()
if err != nil {
return
}
root := filepath.Join(home, "work", "lux-private", "gpu-kernels")
candidates := []struct {
subdir string
name string
}{
{"build-metal/backends/metal", "libluxgpu_backend_metal.dylib"},
{"build/backends/metal", "libluxgpu_backend_metal.dylib"},
{"build/backends/vulkan", "libluxgpu_backend_vulkan.dylib"},
{"build/backends/vulkan", "libluxgpu_backend_vulkan.so"},
{"build/backends/webgpu", "libluxgpu_backend_webgpu.dylib"},
{"build/backends/webgpu", "libluxgpu_backend_webgpu.so"},
{"build/backends/cuda", "libluxgpu_backend_cuda.so"},
{"build/backends/hip", "libluxgpu_backend_hip.so"},
}
for _, c := range candidates {
path := filepath.Join(root, c.subdir, c.name)
if _, err := os.Stat(path); err == nil {
// Found one — set the dir and trigger a re-probe by
// telling the operator to re-run the binary; init() has
// already run, so we can only inform the user via t.Logf.
t.Logf("found candidate plugin: %s (LUX_GPU_PLUGIN_DIR must be set "+
"BEFORE the test binary starts so init() can dlopen it)", path)
return
}
}
}
File diff suppressed because it is too large Load Diff
+253
View File
@@ -0,0 +1,253 @@
// Copyright (c) 2024-2026 Lux Industries Inc.
// SPDX-License-Identifier: BSD-3-Clause-Eco
//
// Lux GPU crypto types
//
// Shared types for cryptographic kernels (MSM, KZG, Poseidon2, Shamir, ...).
// The curve enum lives in <lux/gpu.h> as LuxCurve and is the single source of
// truth — int curve_type fields in the vtbl ABI are LuxCurve values cast to int.
#ifndef LUX_GPU_CRYPTO_H
#define LUX_GPU_CRYPTO_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// =============================================================================
// Field Types
// =============================================================================
// 256-bit scalar (BN254 base/scalar, BLS12-381 scalar, secp256k1, ed25519)
typedef struct {
uint64_t limbs[4];
} LuxScalar256;
// 384-bit base field element (BLS12-381)
typedef struct {
uint64_t limbs[6];
} LuxFp384;
// 64-bit Goldilocks field element (2^64 - 2^32 + 1)
typedef uint64_t LuxGoldilocks;
// =============================================================================
// Point Types
// =============================================================================
// G1 affine point (BN254) — Fp254 is encoded as 4×u64 to match shader layout
typedef struct {
LuxScalar256 x;
LuxScalar256 y;
uint32_t infinity;
uint32_t _pad;
} LuxG1Affine254;
// G1 projective (Jacobian) point (BN254)
typedef struct {
LuxScalar256 x;
LuxScalar256 y;
LuxScalar256 z;
} LuxG1Projective254;
// 512-bit quadratic-extension Fp2 = Fp[u]/(u^2 + 1) over BN254 Fp.
// Limbs are in Montgomery form (R = 2^256 mod p). Layout: c0 first, then c1.
typedef struct {
LuxScalar256 c0;
LuxScalar256 c1;
} LuxFp2_254;
// G2 affine point on the BN254 sextic twist E'(Fp2): y^2 = x^3 + 3/(9+u).
typedef struct {
LuxFp2_254 x;
LuxFp2_254 y;
uint32_t infinity;
uint32_t _pad;
} LuxG2Affine254;
// G2 projective (Jacobian) point (BN254). 192 bytes = 3 * Fp2.
typedef struct {
LuxFp2_254 x;
LuxFp2_254 y;
LuxFp2_254 z;
} LuxG2Projective254;
// G1 affine point (BLS12-381) — Fp384 is 6×u64
typedef struct {
LuxFp384 x;
LuxFp384 y;
uint32_t infinity;
uint32_t _pad;
} LuxG1Affine381;
// G1 projective (Jacobian) point (BLS12-381)
typedef struct {
LuxFp384 x;
LuxFp384 y;
LuxFp384 z;
} LuxG1Projective381;
// 768-bit quadratic-extension element Fp2 = Fp[u]/(u² + 1). Layout matches
// blst's `blst_fp2 { blst_fp fp[2]; }` byte-for-byte: c0 first, then c1.
// All limbs are in Montgomery form (R = 2^384 mod p), same convention as
// LuxFp384.
typedef struct {
LuxFp384 c0;
LuxFp384 c1;
} LuxFp2_381;
// G2 affine point on the BLS12-381 sextic twist E'(Fp2): y² = x³ + 4(1+u).
// Layout matches blst's `blst_p2_affine { blst_fp2 x, y; }` and adds the
// infinity flag word for ABI symmetry with LuxG1Affine381.
typedef struct {
LuxFp2_381 x;
LuxFp2_381 y;
uint32_t infinity;
uint32_t _pad;
} LuxG2Affine381;
// G2 projective (Jacobian) point. Layout matches blst's
// `blst_p2 { blst_fp2 x, y, z; }`.
typedef struct {
LuxFp2_381 x;
LuxFp2_381 y;
LuxFp2_381 z;
} LuxG2Projective381;
// Fp12 element — the pairing target group GT lives here. Layout matches
// blst byte-for-byte: 12 Fp limbs grouped as c0 ‖ c1 with c_i = Fp6 =
// (Fp2)³. Total size = 576 bytes (12 × 6 × u64).
typedef struct {
LuxFp2_381 fp2[6];
} LuxFp12_381;
// =============================================================================
// Pairing batch caps — canonical, ABI-stable, single source of truth.
// =============================================================================
//
// LUX_BN254_MAX_PAIRS_PER_BATCH is the maximum n_pairs a backend's
// op_bn254_pairing may accept in one call. Backends with a stack-allocated
// scratch (CUDA / HIP / Vulkan / Metal) reject n_pairs > this cap with
// LUX_BACKEND_ERROR_NOT_SUPPORTED at the launcher boundary; the public
// dispatcher then falls back to the CPU oracle (gpu_core.cpp:lux_bn254_pairing),
// which has no cap. NOT_SUPPORTED specifically (not INVALID_ARGUMENT) is the
// trigger for the fallback path; INVALID_ARGUMENT propagates to the caller.
//
// Picked at 16 to fit the per-pair scratch (Fp/Fp2/Fp6/Fp12 tower state +
// projective Q + negated Q) inside the device-local register/shared-memory
// envelope while still covering typical EVM EIP-197 call shapes (1..16
// pairs is the overwhelming majority of mainnet traffic; ZK-rollup batch
// verifiers that exceed 16 pairs are correctly handled by the CPU oracle).
//
// Changing this constant requires re-checking the per-pair scratch size in
// every backend's pairing kernel — do not raise without verifying device
// limits.
#define LUX_BN254_MAX_PAIRS_PER_BATCH 16
// =============================================================================
// SLH-DSA (FIPS 205) magnetar hash-tree per-element descriptors — ABI v12
// =============================================================================
//
// One row per batch element. Pointer-free: every payload is resolved as a
// byte offset into a flat host-side pool that the vtbl slot also receives.
// Layout / field order is the single source of truth across all backends
// (CUDA / HIP / Metal / Vulkan / WebGPU) and the CPU oracle. Sizes are
// pinned so that the elem array can be memcpy'd device-side as-is.
//
// FIPS 205 §4.2 address layout is 32 bytes BE; the addr field carries
// the per-element address template (caller sets type/key-pair/etc before
// the call; the kernel mutates the hash-address / tree-height / tree-
// index slots internally).
// FIPS 205 §5 Algorithm 5 "chain" element.
// tmp ← x; for j ∈ [i, i+s): addr.setHashAddress(j); tmp ← F(pk_seed, addr, tmp).
typedef struct {
uint32_t pk_seed_off; // byte offset into pk_seed_pool (reads n bytes)
uint32_t x_off; // byte offset into x_pool (reads n bytes)
uint32_t i; // starting chain index
uint32_t s; // chain length
uint8_t addr[32]; // FIPS 205 §4.2 address template
uint32_t n; // hash output size in bytes (24 / 32)
uint32_t _pad0;
uint32_t _pad1;
uint32_t _pad2;
} LuxMagnetarWotsChainElem;
// FIPS 205 §8.2 Algorithm 15 "fors_node" element.
// 2^height secret-derived leaves at sk_leaves_pool[leaves_off ..],
// each n bytes; root reduced via F (leaf) + H (internal).
typedef struct {
uint32_t pk_seed_off;
uint32_t leaves_off; // byte offset into sk_leaves_pool ((1<<height) × n bytes)
uint32_t leaf_idx; // subtree leaf index
uint32_t height; // subtree height (a for full FORS subtree; ≤ 16)
uint8_t addr[32];
uint32_t n;
uint32_t _pad0;
uint32_t _pad1;
uint32_t _pad2;
} LuxMagnetarForsSubtreeElem;
// FIPS 205 §6.1 Algorithm 9 "xmss_node" element.
// 2^height precomputed WOTS+ pk leaves at wots_pk_pool[leaves_off ..],
// each n bytes; root reduced via H.
typedef struct {
uint32_t pk_seed_off;
uint32_t wots_pk_leaves_off;
uint32_t leaf_idx;
uint32_t height; // ≤ 16
uint8_t addr[32];
uint32_t n;
uint32_t _pad0;
uint32_t _pad1;
uint32_t _pad2;
} LuxMagnetarXmssSubtreeElem;
// FIPS 205 §11.2 SHAKE H_msg element.
// SHAKE256(r || pk_seed || pk_root || msg)[:digest_len].
typedef struct {
uint32_t r_off; // byte offset into r_pool (reads n bytes)
uint32_t pk_seed_off; // byte offset into pk_seed_pool (reads n bytes)
uint32_t pk_root_off; // byte offset into pk_root_pool (reads n bytes)
uint32_t msg_off; // byte offset into msg_pool
uint32_t msg_len; // bytes
uint32_t digest_len; // m bytes (≤ 47 for SLH-DSA-SHAKE)
uint32_t n;
uint32_t _pad;
} LuxMagnetarHmsgElem;
// FIPS 205 §11.2 SHAKE PRF_msg element.
// SHAKE256(sk_prf || opt_rand || msg)[:out_len].
typedef struct {
uint32_t sk_prf_off;
uint32_t opt_rand_off;
uint32_t msg_off;
uint32_t msg_len;
uint32_t out_len; // bytes (n for SLH-DSA-SHAKE)
uint32_t n;
uint32_t _pad0;
uint32_t _pad1;
} LuxMagnetarPrfmsgElem;
// =============================================================================
// Error Codes
// =============================================================================
typedef enum {
LUX_CRYPTO_OK = 0,
LUX_CRYPTO_ERROR_INVALID_ARG = 1,
LUX_CRYPTO_ERROR_OUT_OF_MEMORY = 2,
LUX_CRYPTO_ERROR_NOT_SUPPORTED = 3,
LUX_CRYPTO_ERROR_INVALID_CURVE = 4,
LUX_CRYPTO_ERROR_INVALID_POINT = 5,
LUX_CRYPTO_ERROR_DEVICE_ERROR = 6,
} LuxCryptoError;
#ifdef __cplusplus
}
#endif
#endif // LUX_GPU_CRYPTO_H
+2 -2
View File
@@ -6,12 +6,12 @@ package quantumvm
// Q-Chain GPU substrate — host-side ABI v14 plugin bridge for batched
// post-quantum signature verification + signing.
//
// The kernels live in ~/work/lux-private/gpu-kernels under
// The kernels live in the GPU plugin install tree under
// ops/crypto/{mldsa,slhdsa}/<backend>/ and are shipped inside the
// libluxgpu_backend_<x>.{so,dylib} plugin shared libraries. The Go side
// dlopens whichever plugin is on disk at process start, populates the
// ABI v14 backend descriptor via the `lux_gpu_backend_init` entry point
// (see lux-private/gpu-kernels/include/lux/gpu/backend_plugin.h), and
// (see the lux GPU plugin include/lux/gpu/backend_plugin.h), and
// invokes the op_mldsa_verify_batch / op_mldsa_sign_batch /
// op_slhdsa_verify_batch slots from the resulting vtbl.
//
+7 -14
View File
@@ -12,7 +12,7 @@
// the lux-gpu-kernels plugin DSO and reaches the per-op kernel through
// function pointers inside a vtbl populated by the plugin's
// `lux_gpu_backend_init` entry point. This keeps the chains module
// compilable without lux-private/gpu-kernels present in the build tree —
// compilable without the lux GPU plugin present in the build tree —
// the plugin is fully optional. When no libluxgpu_backend_*.{so,dylib}
// is findable on the dlopen search path, AutoBackend() returns
// BackendNone, every GPUBackend method returns ErrGPUNotAvailable, and
@@ -38,18 +38,11 @@ package quantumvm
#cgo darwin LDFLAGS: -ldl
#cgo linux LDFLAGS: -ldl
// Workspace-relative path to the ABI v14 plugin header. From
// ${SRCDIR} = ~/work/lux/chains/quantumvm we walk three levels up
// (chains → lux → work) and one down (lux-private/gpu-kernels/include)
// to reach the canonical header. External consumers building from
// $GOMODCACHE need lux-private/gpu-kernels checked out at the standard
// sibling location; this matches the convention LLM.md establishes
// for every other workspace-coupled component (LUXCPP_DIR etc.).
//
// Header-only inclusion: we DO NOT link against any luxcpp / lux-gpu
// library. The plugin DSO is dlopen'd at runtime; the only compile-
// time dependency is the C struct layout in backend_plugin.h.
#cgo CFLAGS: -I${SRCDIR}/../../../lux-private/gpu-kernels/include
// The plugin DSO is dlopen'd at runtime; the only compile-time
// dependency is the C struct layout in backend_plugin.h, which is
// vendored under chains/internal/luxgpu/include/lux/gpu/ as the public
// ABI surface. No link against any external library.
#cgo CFLAGS: -I${SRCDIR}/../internal/luxgpu/include
#include <dlfcn.h>
#include <stdint.h>
@@ -231,7 +224,7 @@ const pluginEntrySymbol = "lux_gpu_backend_init"
// pluginEnv overrides the dlopen search path. When set, every probe joins
// the env value to the bare basename before calling dlopen — useful for
// in-tree development against a freshly-built plugin under
// ~/work/lux-private/gpu-kernels/build/ without installing it.
// the GPU plugin build tree without installing it.
const pluginEnv = "LUX_GPU_PLUGIN_DIR"
// probeOrder is the dlopen probe sequence fixed by the spec:
+4 -20
View File
@@ -58,7 +58,7 @@ func probeGPUBackend() *GPUBackend {
// candidatesFor returns up to three dylib filename candidates for the given
// backend, ordered: LUXCPP_PREFIX/lib install path, lux-gpu-kernels build-tree
// path under LUX_PRIVATE_GPU_KERNELS_DIR, then bare leaf-name for the
// path under LUX_GPU_PLUGIN_DIR, then bare leaf-name for the
// dynamic-linker default search path.
//
// On darwin the convention is libluxgpu_backend_<X>.dylib; on linux it's
@@ -81,25 +81,9 @@ func candidatesFor(kind GPUBackendKind) []string {
}
}
if kernels := os.Getenv("LUX_PRIVATE_GPU_KERNELS_DIR"); kernels != "" {
if dir := os.Getenv("LUX_GPU_PLUGIN_DIR"); dir != "" {
for _, leaf := range leaves {
// build/<backend>_backend/<leaf> is the cmake output convention
// for both metal_backend/ and webgpu_backend/.
sub := backendBuildDirname(kind)
if sub != "" {
out = append(out, filepath.Join(kernels, "build", sub, leaf))
}
}
} else if home := os.Getenv("HOME"); home != "" {
// Default lux-private path per the project memory note (2026-05-28):
// "luxcpp/metal + luxcpp/webgpu ARCHIVED. Plugin source + shaders
// now at lux-private/gpu-kernels/{metal,webgpu,kernels/...}".
root := filepath.Join(home, "work", "lux-private", "gpu-kernels")
for _, leaf := range leaves {
sub := backendBuildDirname(kind)
if sub != "" {
out = append(out, filepath.Join(root, "build", sub, leaf))
}
out = append(out, filepath.Join(dir, leaf))
}
}
@@ -136,7 +120,7 @@ func backendDylibLeaves(kind GPUBackendKind) []string {
}
// backendBuildDirname maps each backend to its cmake build subdirectory
// under lux-private/gpu-kernels/build/.
// under the lux GPU plugin build/.
func backendBuildDirname(kind GPUBackendKind) string {
switch kind {
case GPUBackendCUDA:
+4 -4
View File
@@ -8,7 +8,7 @@ package thresholdvm
// GPU bridge — dlopen + dlsym substrate for the MPCVM state-machine kernels.
//
// This file binds Go to the host launchers exported by the per-backend GPU
// plugins in lux-private/gpu-kernels/backends/<X>/. Unlike the cevm bridge
// plugins in the lux GPU plugin backends/<X>/. Unlike the cevm bridge
// which links statically through pkg-config (libevm + libevm-gpu), the
// thresholdvm bridge is a runtime overlay: zero compile-time dependency on
// the plugin, no pkg-config, no link-time resolution. Either the dlopen
@@ -17,8 +17,8 @@ package thresholdvm
// protocol/, factory.go, executor.go — unchanged).
//
// Symbols expected per backend (host launchers in
// lux-private/gpu-kernels/backends/<X>/src/mpcvm_launchers.{cpp,mm} and
// the kernel TUs in lux-private/gpu-kernels/ops/mpcvm/<X>/):
// the lux GPU plugin backends/<X>/src/mpcvm_launchers.{cpp,mm} and
// the kernel TUs in the lux GPU plugin ops/mpcvm/<X>/):
//
// lux_<X>_mpcvm_ceremony_apply (begin/cancel + contribution dedup)
// lux_<X>_mpcvm_ceremony_sweep (DKG finalize -> key share assignment)
@@ -56,7 +56,7 @@ package thresholdvm
// =============================================================================
// Function-pointer typedefs — match the extern "C" launcher signatures in
// lux-private/gpu-kernels/backends/<X>/src/mpcvm_launchers.{cpp,mm}.
// the lux GPU plugin backends/<X>/src/mpcvm_launchers.{cpp,mm}.
//
// All pointers are HOST pointers. The launcher wraps them into the backend's
// native buffer type (MTLBuffer, VkBuffer, cudaMalloc, etc.) and uploads /
+3 -3
View File
@@ -11,7 +11,7 @@ import (
// TestGPULayoutSizes pins the on-wire struct sizes to the device-side
// __align__(16) values declared in
// ~/work/lux-private/gpu-kernels/ops/mpcvm/cuda/mpcvm_kernels_common.cuh.
// the GPU plugin install tree ops/mpcvm/cuda/mpcvm_kernels_common.cuh.
// Any drift would produce silently-wrong kernel results — the same check
// runs inside assertSizes() at init() under cgo; this test pins it for
// nocgo builds AND lets `go test` surface the mismatch immediately
@@ -81,7 +81,7 @@ func TestGPUBackendKindString(t *testing.T) {
// with a zero fixture, assert no error. The two acceptable outcomes are:
//
// 1. The plugin is dlopen'd (e.g. on a dev box with the metal_backend
// dylib in lux-private/gpu-kernels/build/metal_backend/). The launcher
// dylib in the lux GPU plugin build/metal_backend/). The launcher
// runs against the zero fixture and returns rc=0 (zero ops applied).
//
// 2. The plugin is absent. Backend() returns nil; we get ErrGPUNotAvailable.
@@ -93,7 +93,7 @@ func TestGPUBackendKindString(t *testing.T) {
func TestGPUBridgeZeroFixture(t *testing.T) {
b := Backend()
if b == nil || !b.IsAvailable() {
t.Logf("no GPU plugin resolved (expected on CI without a dylib in LUX_PRIVATE_GPU_KERNELS_DIR/build/)")
t.Logf("no GPU plugin resolved (expected on CI without a dylib in LUX_GPU_PLUGIN_DIR/build/)")
// Re-prove the error contract by calling through the nil receiver:
// the GPUBackend method set on nil must return ErrGPUNotAvailable
// rather than panicking. This is the substrate contract.