Root cause of the never-passing 'HQC PQClean cgo e2e (NIST KAT
roundtrip)' job, in two layers:
1. The lux-build runner had no C compiler (gcc/cc/clang absent), so cgo
aborted before any code ran. Fixed in prior commits by installing
build-essential + selecting an available CC.
2. With a compiler present, the cgo build then failed on
accel@v1.1.9/ops/code/code_cpu.go's '#include "lux/gpu/hqc.h"' and
'-lluxgpu_hqc' — the native HQC library (built from luxfi/mlx) that
is NOT present in CI (nor in most consumers). accel v1.1.9 gated that
native path on '//go:build cgo', so every cgo build hard-required the
native lib/header.
Fix: bump github.com/luxfi/accel v1.1.9 -> v1.2.4, which gates the
native path behind '-tags=lux_hqc_native' and otherwise returns
code.ErrNativeHQCUnavailable (documented: 'callers should fall back to
their own CPU KEM path'). crypto/hqc now honours that contract:
- gpu.go: batchEncapsulateGPU/batchDecapsulateGPU treat
ErrNativeHQCUnavailable as 'declined' (return false,nil) so callers
fall back to the per-item PQClean path. Secret buffers are zeroised on
the decline path.
- gpu_cgo.go: GF2Polymul falls back to the pure-Go Karatsuba path on
ErrNativeHQCUnavailable. Removed the duplicated vecNSize64 /
redMaskForMode / GF2Add (now shared).
- gpu_polymul_purego.go (new, tag-neutral): the canonical pure-Go,
PQClean-byte-identical GF(2)^N polymul (Karatsuba + reduce + base_mul),
extracted from gpu_nocgo.go so BOTH builds share one implementation.
- gpu_nocgo.go: trimmed to the thin !cgo public wrappers over the shared
helpers.
- randombytes_shim.c: split. Default (hqc_pqclean, no native) forwards
randombytes -> hqcGoRandombytes only; the native seed-buffer dispatch
(which references lux_hqc_randombytes / lux_hqc_seed_has_bytes from the
native lib) moves to randombytes_shim_native.c, gated on lux_hqc_native.
Previously the default build referenced those native-only symbols
unconditionally, which would not link without the native lib.
NOT a crypto/KAT regression: the NIST KAT roundtrip (TestKAT_HQC128/192/
256), TestRoundTrip_AllModes, TestDeterminism, and the GF2 polymul
known-answer vectors all pass byte-for-byte. The GPU batch tests skip
gracefully when the native kernel is absent (their existing t.Skip path).
Verified: CGO_ENABLED=1 go test -tags=hqc_pqclean ./hqc/ passes from a
clean cache; cgo (with/without hqc_pqclean) and nocgo builds all green;
broader suite unchanged (only pre-existing pkg-config failures in cgo/
and hash/poseidon2 remain, unrelated).
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
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.
The user's call: "ethcrypto is not acceptable. It encodes the wrong
domain. The fact that Keccak/ECDSA are used for EVM address derivation
does not make the package Ethereum, and it definitely does not justify
an eth* alias inside a repo trying to purge ETH naming. decomplect"
Decomplecting (Rich Hickey: separate the value from the brand):
1. ecies/params.go: drop `ethcrypto "github.com/luxfi/crypto"` import
alias. Replace with direct `"github.com/luxfi/crypto/secp256k1"`
import — the file uses `S256()` (the secp256k1 curve), so it
imports the curve provider directly. The "ethcrypto" alias was
gratuitous branding; secp256k1 is a curve, not an Ethereum
primitive.
2. secp256k1/keys.go: rename `EthAddress()` → `KeccakAddress()` on
both PrivateKey and PublicKey. The derivation is "last 20 bytes of
Keccak256(uncompressed_pubkey)"; that primitive predates and is
consumed by every EVM chain (Lux C-Chain, Polygon, BSC, Partner EVM,
Hanzo EVM, etc.). The "Eth" label braids the value with one brand.
KeccakAddress names the VALUE; the docstring notes EVM-compatible
chains consume it but no longer implies Ethereum-specificity.
`EthAddress()` retained as a `// Deprecated:` alias delegating to
KeccakAddress so downstream callers (node, cli, mpc) don't break
in one wave. Linter will flag usages; downstream migrations land
at their own pace.
3. Address() vs KeccakAddress(): the existing Address() returns the
Lux native X-Chain / P-Chain format (SHA256+RIPEMD160 → ShortID).
Both methods are now brand-neutral and describe the derivation,
not the consumer.
The banderwagon test seed "eth_verkle_oct_2021" is left as-is — it
is an external constant from the Ethereum Foundation Verkle research,
not a Lux naming choice; renaming would break the KAT invariant.
Per CLAUDE.md x.x.x+1.
Adds GPU/CPU split for HQC's hot inner-loop primitives:
gpu_nocgo.go Pure-Go bit-sliced Karatsuba mod (X^n - 1), byte-equal
to PQClean's PQCLEAN_HQC{128,192,256}_CLEAN_vect_mul.
Constant-time on secret operands (masked nibble table
lookups; no data-dependent branches or indices).
Modern Go compiler emits SIMD-friendly XOR loops on
amd64 / arm64.
gpu_cgo.go Same surface, routed through luxfi/accel/hqc -> the
C kernel in libluxgpu_hqc.a (also byte-equal to PQClean).
gpu_kat_test.go KAT cross-check across both builds — vectors
captured from the cgo path's PQClean kernel,
verified bit-identical from the pure-Go path
(100-iter checksum + single-vector KAT).
gpu_polymul_test.go Algebraic properties (commutativity, identity,
zero, distributivity, associativity fuzz x600).
gpu_bench_test.go Single-shot + batched benchmarks per mode.
Updated gpu.go to route the existing batchEncapsulateGPU /
batchDecapsulateGPU through accel/ops/code's batch surface (replaces
the old "not implemented" stubs). Updated randombytes_shim.c to be
the single canonical PQClean entropy bridge.
Determinism contract: validators reach consensus on HQC encapsulation
ciphertexts, so any byte-level divergence between the two paths is a
consensus fork. The KAT test pins this contract.
Test status (all three configs green):
CGO_ENABLED=0 go test ./hqc/... 18.1s
CGO_ENABLED=1 go test ./hqc/... 44.2s
CGO_ENABLED=1 go test -tags=hqc_pqclean ./hqc/.. 5.5s
NIST KATs (HQC128/192/256 encap/decap byte-vectors): all PASS.
Bench numbers on Apple M1 Max (single-shot GF2 polymul, ns/op):
HQC-128: pure-Go 2.03 ms / cgo 4.10 ms
HQC-192: pure-Go 6.28 ms / cgo (n.m.)
HQC-256: pure-Go 11.20 ms / cgo (n.m.)
The cgo path is currently slower than pure-Go because libluxgpu_hqc.a
ships from luxcpp/gpu/build with CMAKE_BUILD_TYPE empty (no -O3). With
the CMake build switched to Release, cgo will return to its expected
~30us / poly margin.
Wired via:
crypto/hqc -> accel/hqc -> accel/ops/code -> luxcpp/gpu/libluxgpu_hqc
Lifts the floor on luxfi/accel so crypto/slhdsa/gpu.go can resolve
the new accel.LatticeOps.SLHDSAVerifyBatch method via the published
module instead of relying on go.work for local builds.
The file carried //go:build ziren so it only built under -tags ziren.
Nobody in the canonical Lux stack ships with that tag. But go mod
recorded the import as // indirect in every transitive consumer of
crypto, polluting ~100 go.mod files with a ZK-rollup-zkVM noise line.
Drop the file entirely. Ziren remains available to ZK-prover stacks
as a direct import — they don't need crypto's transitive vehicle.
The inline ipa/ directory was a vendored copy of go-ipa that
collided with the separate luxfi/crypto/ipa module's import path —
any downstream that pulled both saw "ambiguous import" errors at
the bandersnatch / banderwagon / common/parallel packages.
Removed the inline tree and added `require github.com/luxfi/crypto/ipa
v1.2.4`. The verkle subpackage switches its banderwagon imports from
`luxfi/crypto/banderwagon` (local) to `luxfi/crypto/ipa/banderwagon`
(external) so its Point alias matches the type ipa.CreateMultiProof
and IPAConfig.Commit return. The local lux/crypto/banderwagon
package stays — its MultiExp/Precomp helpers remain available for
callers that want the Element-method shape; it just no longer
shares a struct identity with the verkle types.
Two pre-existing test failures in verkle (TestParseNodeEoA,
TestParseNodeSingleSlot) reproduce both with this change AND on the
pre-change tree — encoding-tag mismatch and projective-coord
hardcoding in the test's expected values. Not blocking; tracked
separately.
The github.com/ethereum/go-verkle string remained only in vendor-
attribution comments (verkle/ files were vendored, not imported);
keeping the phantom require with version 00010101000000-000000000000
made the module unbuildable standalone. luxfi/crypto/ipa v1.2.4 was
an outdated indirect referencing this module's own ipa/ subdir,
causing ambiguous-import on standalone builds.
Both removed; module now builds with GOWORK=off GOFLAGS=-mod=mod.
Vendor-attribution comments in verkle/*.go retained.
Replace github.com/luxfi/crypto/ipa/banderwagon with the canonical
github.com/luxfi/crypto/banderwagon in ipa/batch.go and its test, plus
the verkle-ipa docs example and the rust spec_vectors comment. The old
ipa/banderwagon subdirectory was deleted in 5a1479b; banderwagon now
has a single canonical home at the top level of luxfi/crypto.
go.mod adds the indirect entries for go-verkle and go-ipa that the
rerouted test_helper graph now exercises. The luxfi-mirror replace
directives remain in place so all upstream go-ipa/go-verkle traffic
still flows through luxfi-maintained sources.
Build: go build ./... clean (one ld linker warning unrelated).
Tests: ./banderwagon/... ./ipa/... pass (7 ok, 2 no-test-files).
Pre-existing failure in ./verkle/ is unrelated: verkle_test.go
references BatchProof/VerifyBatch/ErrBatchLengthMismatch symbols that
were never implemented in the verkle package (separate gap, present on
main before this fix).
luxfi/go-verkle v0.2.3-luxfi declares module github.com/luxfi/go-verkle.
Drop the go.mod replace directive that mapped the old ethereum import
literal onto the luxfi mirror; switch the only consumer (verkle/verkle_test.go)
to the canonical luxfi import path.
Refs luxfi/go-verkle#233.
Replaces the prior `verkle/verkle.go` re-export shim (`type X =
upstream.X`, `var Fn = upstream.Fn` against `github.com/ethereum/go-verkle`)
with a full source vendor of `github.com/ethereum/go-verkle@v0.2.2` into
`/Users/z/work/lux/crypto/verkle/` under luxfi copyright. Every type and
function now has a concrete Go body in luxfi-owned files; no aliases
remain. Resolves the canonical-audit violation flagged in
CANONICAL_AUDIT.md (commit 9affea3).
Provenance:
Upstream: github.com/ethereum/go-verkle@v0.2.2 (public domain / Unlicense)
Per-file header notes vendor source and Lux Ecosystem License rebadge.
Files (13 source + 10 tests, ~7,900 LOC vendored):
verkle/config.go, config_ipa.go, conversion.go, debug.go, doc.go,
verkle/empty.go, encoding.go, hashednode.go, ipa.go, proof_ipa.go,
verkle/proof_json.go, tree.go, unknown.go (+ matching _test.go)
Surface preserved (all symbols consumed by lux/geth/trie, lux/geth/core/types):
Types: VerkleNode, InternalNode, LeafNode, Empty, Point, Fr,
VerkleProof, StateDiff, NodeResolverFn, IPAConfig, Proof
Const: NodeWidth
Funcs: ParseNode, New, ToDot, GetConfig, MakeVerkleMultiProof,
SerializeProof, HashPointToBytes, FromLEBytes, FromBytes,
DeserializeProof, MergeTrees, BatchNewLeafNode,
PreStateTreeFromProof
Methods: *InternalNode.{Insert,Commit,Hash,GetValuesAtStem,
DeleteAtStem}, IPAConfig.CommitToPoly, ...
go.mod: drop direct require on github.com/ethereum/go-verkle; promote
crate-crypto/go-ipa and davecgh/go-spew to direct (still pulled
through banderwagon primitives — to be swapped to luxfi/crypto/ipa
once the parallel banderwagon vendor lands at lux/crypto/banderwagon).
Tests (GOWORK=off go test ./verkle/ -count=1):
82 PASS, 4 SKIP, 2 FAIL.
TestParseNodeEoA and TestParseNodeSingleSlot fail identically against
upstream go-verkle@v0.2.2 (verified) -- these are pre-existing upstream
test fixtures gone stale; not caused by the vendor.
Consumer compatibility verified by external module replace+build
exercising every consumed symbol: ok.
Outstanding (tracked, not blocking):
- Switch IPA primitives import from `crate-crypto/go-ipa/{banderwagon,
common,ipa}` to `luxfi/crypto/ipa/*` once the banderwagon-vanilla
branch lands. Done in this commit would conflict with the parallel
work-in-progress.
- Two upstream-stale test fixtures (TestParseNodeEoA,
TestParseNodeSingleSlot) to be regenerated against current encoding
in a follow-up.
Branch: verkle-vanilla-2026-04-27
Route github.com/crate-crypto/go-ipa and github.com/ethereum/go-verkle
through luxfi-maintained mirrors so all Lux crypto consumers converge on a
single canonical source. Pinned to luxfi/go-verkle v0.2.2 and
luxfi/go-ipa@d31adc04. See LUXFI-FORK.md in each fork for sync policy.
Tests verified: ./verkle/... and ./ipa/... pass.
luxfi/crypto becomes the single Go entry point for ALL Lux-family crypto.
Every public function in this module now dispatches between three
implementations through a runtime-selectable backend:
- vanilla: pure-Go reference (always available)
- cgo: native binding (blst, libsecp256k1, ckzg) where present
- gpu: batch acceleration via github.com/luxfi/accel
The dispatcher reads LUX_CRYPTO_BACKEND (auto|vanilla|cgo|gpu); auto
picks the most capable backend the binary was compiled and linked with.
New canonical packages:
backend/ runtime backend selector (env + programmatic)
internal/gpuhost/ accel session lifecycle, single per-process
keccak/ Keccak-256 with batch GPU dispatch
sha256/ SHA-256 with batch GPU dispatch
sha3/ SHA3 / SHAKE family
ripemd160/ RIPEMD-160 (Bitcoin/Lux address derivation)
ed25519/ Ed25519 with batch GPU verify
bn254/ canonical alias for bn256 (matches FIPS naming)
modexp/ canonical alias for bigmodexp
evm256/ EIP-196/197 precompile ABI wrappers
poseidon/ Poseidon2 hash via gnark-crypto
pedersen/ Pedersen commitments over BN254
ntt/ Number-Theoretic Transform reference
polymul/ negacyclic polynomial multiplication
Extended existing packages with batch GPU paths:
bls/batch.go BatchVerify routes through accel.BLSVerifyBatch
mldsa/batch.go BatchVerify (ML-DSA-65) via accel.DilithiumVerifyBatch
mlkem/batch.go BatchEncapsulate / BatchDecapsulate via Kyber kernels
secp256k1/batch.go BatchVerifySignature via accel.ECDSAVerifyBatch
GPU dispatch is gated on (a) backend.Default(), (b) batch size threshold,
and (c) accel.Available(). When any gate fails the call falls through to
the vanilla CPU path; output is byte-identical.
The legacy gpu/ stub is replaced with a thin probe surface (Available,
Backend, Devices, Version) that delegates to the same gpuhost session.
Tests show vanilla and gpu backends produce identical outputs across all
batch entry points (-race clean).
See AUDIT.md for the per-algorithm state matrix and honest gaps.
Re-export github.com/ethereum/go-verkle and
github.com/ethereum/go-bigmodexpfix under luxfi/crypto namespace
so downstream packages (luxfi/geth) never import ethereum/* directly.
- Add secret/ package: wraps runtime/secret.Do() for secure key erasure
when built with GOEXPERIMENT=runtimesecret, no-op stub otherwise
- Add encryption/hpke.go: HPKE encryption using Go 1.26 stdlib crypto/hpke
with X25519 (classical) and ML-KEM-768+X25519 (post-quantum hybrid)
- Wrap BLS key generation (both CGO and pure Go) in secret.Do()
- Wrap HexToECDSA and LoadECDSA in secret.Do() for key byte cleanup
- Simplify random.go: crypto/rand.Read never errors in Go 1.26
- Update CI workflows to Go 1.26.1, add GOEXPERIMENT=runtimesecret job
- Update dependencies: circl 1.6.3, x/crypto 0.48.0, age 1.3.1
Provides hash primitives needed by luxfi/ids and other packages:
- ComputeHash256/ComputeHash160 for crypto hashes
- PubkeyBytesToAddress for Bitcoin-style address derivation
- Checksum for CB58 encoding
The gpu package requires C++ headers that only exist locally.
Using 'cgo' build tag caused CI failures when CGO was enabled but
headers weren't present.
Now uses 'luxgpu' custom build tag:
- Default: stub implementation (no dependencies)
- With -tags luxgpu: full CGO acceleration
Build locally with GPU: go build -tags luxgpu ./...
The Go proxy still serves v1.16.1 which has wrong module path
(github.com/ethereum/go-ethereum instead of github.com/luxfi/geth).
Add exclude directive to prevent this version from being selected.
- Remove cache/ and utils/ directories (originally copied from luxfi/node)
- Update secp256k1 imports to use github.com/luxfi/cache/lru
- Update sig_fuzz_test.go to use local crypto/hashing instead of node/utils
- Clean up go.mod: remove unused node/geth dependencies
- crypto package is now fully standalone
- Fix bn256/gnark imports to use crypto/bitutil instead of standalone bitutil
- Remove unused secp256k1 import from crypto.go
- Fix mlkem test files to use correct API (GenerateKeyPair returns pub,priv,err)
- Fix mlkem constant names (MLKEM512SharedKeySize not SharedSecretSize)
- Restore crypto/common types as standalone (not aliases to geth/common)
- Update crypto.go and keccak.go to use crypto/common instead of geth/common
- Fix secp256k1 fuzz tests to use correct DecompressPubkey/CompressPubkey API
- Remove stale test files and go.work files
crypto package is now fully independent of geth (geth -> crypto, not reverse)