mirror of
https://github.com/luxfi/accel.git
synced 2026-10-30 17:03:45 +00:00
Pulsar audit (commit 2dd1383) identified these as the missing wiring blocking
GPU acceleration for ML-DSA threshold signing. This change ships the C ABI
plumbing end-to-end:
1. internal/capi/include/lux/accel/c_api.h
- declares lux_mldsa_sign_batch, lux_mldsa_verify_batch (FIPS 204; modes
2/3/5 = ML-DSA-44/65/87)
- declares lux_lattice_ntt_mldsa_batch (in-place forward/inverse NTT
over Z_q[X]/(X^256+1), q=8380417)
2. internal/capi/stub.go — adds weak-symbol stubs returning LUX_NO_BACKEND
so the build succeeds without libluxaccel.
3. internal/capi/capi.go — replaces the placeholder _ = stubs that returned
ErrNotSupported with real cgo calls. Reorders the header search path so
the vendored c_api.h takes precedence over older installed snapshots
(single source of truth: ${SRCDIR}/include/lux/accel/c_api.h).
4. ops_lattice.go — adds LatticeNTTMLDSABatch to the LatticeOps interface,
consumed by both cgoLatticeOps (ops_c.go) and stubLatticeOps (ops.go).
5. accel_c.go — top-level Go API:
MLDSAVerifyBatch(mode, msgs, sigs, pks, msgWidth)
MLDSASignBatch(mode, msgs, sks, msgWidth)
LatticeNTTMLDSABatch(polys, inverse)
Below MLDSABatchThreshold=8 each returns ErrNotSupported so callers
route to their per-element CPU oracle (pulsar's existing fallback).
6. errors.go — adds MLDSAMode{44,65,87}, per-mode pk/sk/sig widths,
MLDSANTTPolyLen=256, MLDSABatchThreshold=8.
7. ops/lattice/mldsa_ntt.go — Go CPU oracle byte-equal to
PQCLEAN_MLDSA65_CLEAN_ntt and PQCLEAN_MLDSA65_CLEAN_invntt_tomont.
Same 256-entry zeta table the lux-private/gpu-kernels/ops/lattice/
ntt_mldsa kernels embed across every backend (CUDA/HIP/Metal/WGSL).
Tests:
- ops/lattice/mldsa_ntt_test.go: round-trip identity, zero-input, constant-
poly evaluation, zeta-table endpoint pins, Montgomery-reduce edge case,
batch=22 (Pulsar Round-2 shape) byte-equality to per-poly oracle.
- accel_test.go: MLDSAVerifyBatch/MLDSASignBatch/LatticeNTTMLDSABatch
surface checks, FIPS 204 width constants.
Verified:
- go test ./... PASS on cgo + CGO_ENABLED=0 + -tags=accel.
- pulsar TestPulsar_GPU_ByteEqual PASS (~/work/lux/pulsar regression).
- go vet clean, gofmt clean.
186 lines
9.0 KiB
Go
186 lines
9.0 KiB
Go
//go:build !cgo
|
|
|
|
package accel
|
|
|
|
// Stub implementations that return ErrNoBackends for all operations.
|
|
// These allow the package to compile without CGO while providing
|
|
// meaningful error messages at runtime.
|
|
|
|
type stubMLOps struct{}
|
|
|
|
func (o *stubMLOps) MatMul(a, b, c *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubMLOps) MatMulTranspose(a, b, c *UntypedTensor, transposeA, transposeB bool) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) ReLU(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubMLOps) GELU(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubMLOps) Softmax(input, output *UntypedTensor, axis int) error { return ErrNoBackends }
|
|
func (o *stubMLOps) LayerNorm(input, gamma, beta, output *UntypedTensor, eps float32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) Attention(q, k, v, output *UntypedTensor, scale float32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) Conv2D(input, kernel, output *UntypedTensor, stride, padding [2]int) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) MaxPool2D(input, output *UntypedTensor, kernelSize, stride [2]int) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) BatchNorm(input, gamma, beta, mean, variance, output *UntypedTensor, eps float32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubMLOps) Dropout(input, output *UntypedTensor, p float32) error { return ErrNoBackends }
|
|
func (o *stubMLOps) Add(a, b, c *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubMLOps) Multiply(a, b, c *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubMLOps) Sum(input, output *UntypedTensor, axes []int) error { return ErrNoBackends }
|
|
func (o *stubMLOps) Mean(input, output *UntypedTensor, axes []int) error { return ErrNoBackends }
|
|
|
|
type stubCryptoOps struct{}
|
|
|
|
func (o *stubCryptoOps) SHA256(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubCryptoOps) Keccak256(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubCryptoOps) Poseidon(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubCryptoOps) ECDSAVerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubCryptoOps) Ed25519VerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubCryptoOps) BLSVerifyBatch(messages, signatures, pubkeys, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubCryptoOps) BLSAggregate(signatures, aggregated *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubCryptoOps) MerkleRoot(leaves, root *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubCryptoOps) MerkleBatch(leavesSet, roots *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubCryptoOps) MerkleProof(leaves *UntypedTensor, leafIndex int, proof *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
|
|
type stubZKOps struct{}
|
|
|
|
func (o *stubZKOps) NTT(input, output, roots *UntypedTensor, modulus uint64) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubZKOps) INTT(input, output, invRoots *UntypedTensor, modulus uint64) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubZKOps) MSM(scalars, bases, result *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubZKOps) MSMBatch(scalars, bases, results *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubZKOps) PolyMul(a, b, c *UntypedTensor, modulus uint64) error { return ErrNoBackends }
|
|
func (o *stubZKOps) PolyEval(coeffs, points, results *UntypedTensor, modulus uint64) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubZKOps) CommitPoly(coeffs, srs, commitment *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubZKOps) FFT(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubZKOps) IFFT(input, output *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubZKOps) FieldAdd(a, b, c *UntypedTensor, modulus uint64) error { return ErrNoBackends }
|
|
func (o *stubZKOps) FieldMul(a, b, c *UntypedTensor, modulus uint64) error { return ErrNoBackends }
|
|
func (o *stubZKOps) FieldInv(a, b *UntypedTensor, modulus uint64) error { return ErrNoBackends }
|
|
|
|
type stubLatticeOps struct{}
|
|
|
|
func (o *stubLatticeOps) KyberKeyGen(pk, sk *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) KyberKeyGenBatch(pk, sk *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) KyberEncaps(pk, ct, ss *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) KyberEncapsBatch(pk, ct, ss *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) KyberDecaps(ct, sk, ss *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) KyberDecapsBatch(ct, sk, ss *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) DilithiumKeyGen(pk, sk *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) DilithiumSign(msg, sk, sig *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) DilithiumSignBatch(msgs, sk, sigs *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) DilithiumVerify(msg, sig, pk *UntypedTensor) (bool, error) {
|
|
return false, ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) DilithiumVerifyBatch(msgs, sigs, pks, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) MLDSAVerifyBatch(mode int, msgs, sigs, pks, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) MLDSASignBatch(mode int, msgs, sks, sigs *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) SLHDSASignBatch(mode int, msgs, sks, sigs *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) SLHDSAVerifyBatch(mode int, msgs, sigs, pks, results *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) PolynomialNTT(input, output *UntypedTensor, q uint32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) PolynomialINTT(input, output *UntypedTensor, q uint32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubLatticeOps) PolynomialMul(a, b, c *UntypedTensor, q uint32) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) PolynomialAdd(a, b, c *UntypedTensor, q uint32) error { return ErrNoBackends }
|
|
func (o *stubLatticeOps) LatticeNTTMLDSABatch(polys *UntypedTensor, inverse bool) error {
|
|
return ErrNoBackends
|
|
}
|
|
|
|
type stubFHEOps struct{}
|
|
|
|
func (o *stubFHEOps) BFVEncrypt(plaintext, pk, ciphertext *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) BFVEncryptBatch(plaintexts, pk, ciphertexts *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) BFVDecrypt(ciphertext, sk, plaintext *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) BFVAdd(ct1, ct2, result *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) BFVMultiply(ct1, ct2, relinKey, result *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) BFVMultiplyPlain(ct, plain, result *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) BFVRotate(ct, galoisKey *UntypedTensor, steps int, result *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) CKKSEncrypt(plaintext, pk *UntypedTensor, scale float64, ciphertext *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) CKKSDecrypt(ciphertext, sk, plaintext *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) CKKSAdd(ct1, ct2, result *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) CKKSMultiply(ct1, ct2, relinKey, result *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) CKKSRescale(ct, result *UntypedTensor) error { return ErrNoBackends }
|
|
func (o *stubFHEOps) CKKSRotate(ct, galoisKey *UntypedTensor, steps int, result *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubFHEOps) Bootstrap(ct, bootstrapKey, result *UntypedTensor) error { return ErrNoBackends }
|
|
|
|
type stubDEXOps struct{}
|
|
|
|
func (o *stubDEXOps) ConstantProductSwap(reserveX, reserveY, amountIn *UntypedTensor, xToY bool, amountOut *UntypedTensor, fee float32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) ConstantProductSwapBatch(reserves, swaps, amounts *UntypedTensor, fee float32) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) ComputeTWAP(prices, timestamps *UntypedTensor, start, end uint64, twap *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) MatchOrders(bids, asks, matches, prices, amounts *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) MatchOrdersWithPriority(bids, asks, matches *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) ComputeLiquidity(tickLower, tickUpper, amounts, liquidity *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) ComputePositionValue(liquidity, tickLower, tickUpper *UntypedTensor, currentTick int32, values *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) CalculateFees(liquidity, feeGrowthInside0, feeGrowthInside1, fees *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|
|
func (o *stubDEXOps) BatchSettlement(trades, balances, newBalances *UntypedTensor) error {
|
|
return ErrNoBackends
|
|
}
|