mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
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:
+3
-3
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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 /
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user