Migrates the canonical Lattigo-derived Montgomery NTT body from
luxfi/lattice/v7/ring into luxfi/math/ntt/canonical. This is the
ownership inversion LP-107 Phase 3 calls for: the math substrate
now OWNS the canonical body, and luxfi/lattice/ring becomes a thin
shim over it.
ntt/canonical/ — new public package with the canonical body:
* subring.go — SubRing struct + constructors (269 LoC)
* ntt.go — NTT/INTT body, loop-unrolled-by-16 (1320 LoC)
* ntt_simd.go — SIMD AVX2 dispatcher (405 LoC, build-tagged)
* ntt_simd_stub.go — purego dispatcher stub for non-SIMD builds
* modular_reduction.go — Barrett + Montgomery scalars
* factorization.go — prime factorization helpers
* utils.go — primitive root finders, parameter helpers
* vec_ops.go — vectorized polynomial helpers
* weierstrass.go — companion algebraic helpers
ntt/purego.go — REWORK: the pureGoBackend now holds *canonical.SubRing
instead of *lattice/v7/ring.SubRing; the lattice/v7/ring import is
GONE. Cycle-free: GOWORK=off go list -deps github.com/luxfi/math/ntt
shows zero github.com/luxfi/lattice imports.
Total: ~3.7K LoC of canonical body migrated. Public API unchanged
for downstream callers — luxfi/lattice/ring re-exports the same
SubRing.NTT/INTT surface from the new owner location.
Tests: GOWORK=off go test ./... in luxfi/math/.claude/worktrees/lp-107-phase3
ok github.com/luxfi/math/ntt (cached)
ok github.com/luxfi/math/ntt/canonical 0.276s
+ every other package green.
Replicates the codec cross-runtime KAT pattern across the rest of the
math substrate. Each package now ships:
* kat.go — KATEntry/KATBundle aliases pointing at codec.KATEntry/
codec.KATBundle (one schema for the whole substrate).
* cmd/emit_*_kat/main.go — Go-side emitter that produces the
canonical bundle.
* kat_test.go — Go replay test (TestKATBundle_RoundTrip*).
* testdata/*_kat.json — emitted bundle, byte-stable across runs.
modarith/cmd/emit_modarith_kat: ~300 entries
- 100 MontMulMod cross-checked against MulMod
- 100 AddMod across deterministic random pairs
- 100 MontgomeryRoundTrip (ToMontgomery -> FromMontgomery)
- Multiple moduli (PulsarQ, NTT998).
ntt/cmd/emit_ntt_kat: ~50 entries
- input: 256 random uint64 mod PulsarQ
- output: NTT(input) byte-stream + SHA-256 commitment
- Pulsar N=256 ring.
sample/cmd/emit_sample_kat: ~30 entries
- Uniform / Ternary / CenteredBinomial across param sweeps
- Each entry records the seed + first 64 sampled values.
Tests: GOWORK=off go test ./modarith/ ./ntt/ ./sample/ -> all PASS.
C++ replay tests follow in luxcpp/crypto commit on this same branch.
codec/kat.go: KATEntry + KATBundle types with canonical JSON
serialization. Header field embeds params.KATHeader so every entry
carries (parameter_set, modulus_id, backend_id, hash_suite_id,
implementation, version) — required by LP-107 §"Parameter registry"
for cross-runtime equality.
codec/cmd/emit_codec_kat/main.go: emits 5 canonical entries:
1. ReadUint64Slice/happy-path/3-elements
2. ReadUint64Slice/empty
3. ReadUint64Slice/reject/lattice-issue-4-70T (regression: huge
length must be rejected by both runtimes)
4. ReadUint16Slice/happy-path/5-elements
5. ReadUint32Slice/happy-path/4-elements
Each entry records:
- InputHex: the wire-format bytes the Reader consumes
- OutputHex: the canonical output byte stream (or "REJECTED")
- OutputSHA256: commitment for fast cross-runtime equality
codec/kat_test.go: TestKATBundle_RoundTrip replays every bundle
entry through the Go Reader and asserts byte-equality + SHA-256
commitment match. PASS in 0.29s.
codec/testdata/codec_kat.json: the Go-emitted bundle. The C++ side
at luxcpp/crypto/math/test/codec_cross_runtime_test.cpp loads the
same file and replays every entry; in this commit, all 5 entries
PASS byte-equal between runtimes.
This closes LP-107 Phase 7 for codec/. Subsequent commits extend the
gate to modarith/, ntt/, poly/, sample/ KATs.
ntt, poly, rns, sample
Eight pure-Go reference packages that own the canonical semantics of
every cryptographic-math primitive Lux protocols share. Backends
(AVX2 / NEON / cgo+C++ / CUDA / Metal / WGSL) plug in behind the
substrate; KATs gate byte-equality across them.
Packages added:
* params/ — ModulusID, NTTParamID, FHEParamID, PulsarParamID,
HashSuiteID, BackendID; KATHeader required-fields
schema. Stable string IDs; renaming is a breaking
change.
* backend/ — Policy enum (PureGo / NativeCPU / GPUPreferred /
GPURequired); Resolve(policy, registered) returns the
BackendID per LP-107's fallback chain. GPURequired
returns ErrUnavailable when no GPU backend is
registered.
* codec/ — Bounded readers (closes lattice issues #2 + #4 DoS
class permanently). Limits + LimitError + Reader.
ReadUint{16,32,64}Slice all reject the 70T-element
attack input before allocating; depth + frame-bytes
caps; iterative (no recursion). Regression test
TestReadUint64Slice_RejectsHugeLength encodes the
original lattice issue #4 input.
* modarith/ — Modulus type with QInv, R2, Barrett constants
(computed via math/big once per modulus). AddMod /
SubMod / MulMod (Div64 reference path). MontMulMod /
ToMontgomery / FromMontgomery cross-checked against
MulMod across 100 random Pulsar-Q pairs.
ReductionMode enum byte-equal to lattice/types.
* ntt/ — Service + Backend interface; pure-Go backend
delegates to lattice/v7/ring.SubRing.NTT/INTT (the
canonical Lattigo-derived Montgomery NTT body — no
re-implementation, just dispatch). Round-trip +
batch + determinism tests on Pulsar N=256.
* poly/ — Add / Sub / ScalarMul / PointwiseMul (NTT-domain) /
negacyclic Mul (NTT round-trip). Composes modarith +
ntt; no lower-level reach.
* rns/ — Basis(Moduli, Name) for FHE RNS chains. Single +
two-prime construction validated; rejects even moduli.
Phase 3 will add basis-extension and modulus-switching.
* sample/ — Uniform (rejection sampling, mask-and-retry); Ternary
(density + sign byte); CenteredBinomial (popcount
difference); DiscreteGaussianRejection (6-sigma cutoff).
All take an io.Reader so callers can KAT-replay.
Architecture invariants enforced in package docs:
- Go is the canonical semantic reference.
- Backend selection MUST NOT alter transcript bytes (consensus
paths default to PolicyPureGo / PolicyNativeCPU).
- No unbounded codec readers anywhere near wire formats.
- No re-implementation: where lattice/v7/ring already owns the
canonical body, we delegate, not fork.
- One ID space; renaming is a breaking change.
Test posture: 8/8 packages green via `GOWORK=off go test ./...`.
Phases 3-7 (queued):
3. lattice consumes math
4. pulsar consumes lattice + math
5. fhe consumes math
6. luxcpp/crypto/math native backend mirror
7. cross-runtime KAT release gate
See SUBSTRATE.md for the full posture and migration plan; full LP at
~/work/lux/lps/LP-107-lux-math-substrate.md.
Added functions from node/vms/dexvm/math:
- Div64: safe division with error on zero
- MulBig: multiply uint64s returning big.Int
- MulDiv64: (a * b) / c without overflow
- MulDivRoundUp64: ceiling division variant
- BigMulDiv: big.Int multiplication+division
- BigMulDivRoundUp: big.Int ceiling variant
- Clamp: generic ordered type clamping
Also added common big.Int values (bigZero, bigOne, maxUint64Big)
for performance in division operations.
- Add Zero[T any]() generic function for zero values
- Add IsSortedBytes for byte slice sorting checks
- Add heap/queue.go with priority Queue[T] implementation
- Add heap/map.go with indexed Map[K,V] for heap operations
- Add linked/list.go with doubly-linked List[T]
- Add linked/hashmap.go with ordered HashMap[K,V]
- Update sampleable_set.go to use local luxmath.Zero[T]()
- Update averager_heap.go to use github.com/luxfi/math/heap
- Update safe_math.go to use luxmath.Zero[T]()
- Remove github.com/luxfi/utils dependency entirely
- Replace github.com/luxfi/node/utils with github.com/luxfi/utils
- Replace github.com/luxfi/node/utils/sampler with github.com/luxfi/sampler
- Replace github.com/luxfi/node/utils/wrappers with github.com/luxfi/utils/wrappers
- Replace github.com/luxfi/node/utils/json with github.com/luxfi/utils/json
- Remove local replace directive for luxfi/utils
- Use published github.com/luxfi/utils v1.0.0
- Update github.com/luxfi/geth: v1.16.34 -> v1.16.43
- Update github.com/luxfi/ids: v1.0.2 -> v1.1.4
- Update github.com/luxfi/crypto: v1.2.9 -> v1.17.15
- Update Go version to 1.25.5
- All tests pass
- Make Add() variadic to accept multiple elements
- Add Overlaps() method to check for common elements
- Add Pop() method to remove and return an element
- Add NewSet() constructor with optional initial capacity
- Implement MarshalJSON/UnmarshalJSON for JSON serialization
- Fix test to use return values from Union/Difference
All tests now passing.