mirror of
https://github.com/luxfi/accel.git
synced 2026-10-30 17:03:45 +00:00
306 lines
6.8 KiB
Go
306 lines
6.8 KiB
Go
package zk
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// Use smaller modulus for simpler tests
|
|
// 97 is prime and 97-1=96, 8|96, so 8th roots of unity exist
|
|
// A generator of Z/97* is 5
|
|
// The primitive 8th root of unity is 5^(96/8) = 5^12 mod 97 = 64
|
|
var smallNTTParams = NTTParams{
|
|
N: 8,
|
|
Modulus: 97, // Prime with 8 | (97-1)
|
|
Root: 64, // 5^12 mod 97 = 64 is a primitive 8th root of unity
|
|
}
|
|
|
|
var smallFieldParams = FieldParams{
|
|
Modulus: 97,
|
|
}
|
|
|
|
// Goldilocks prime for more realistic tests (fits in uint64)
|
|
// 2^64 - 2^32 + 1 has order 2^32, so 8th roots exist
|
|
var goldilocksNTTParams = NTTParams{
|
|
N: 8,
|
|
Modulus: 0xFFFFFFFF00000001, // 2^64 - 2^32 + 1 (Goldilocks)
|
|
Root: 7, // Generator
|
|
}
|
|
|
|
var goldilocksFieldParams = FieldParams{
|
|
Modulus: 0xFFFFFFFF00000001,
|
|
}
|
|
|
|
func TestNTTRoundtrip(t *testing.T) {
|
|
coeffs := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
|
|
|
|
// Forward NTT
|
|
evals, err := NTT(smallNTTParams, coeffs)
|
|
if err != nil {
|
|
t.Fatalf("NTT failed: %v", err)
|
|
}
|
|
|
|
// Inverse NTT
|
|
recovered, err := INTT(smallNTTParams, evals)
|
|
if err != nil {
|
|
t.Fatalf("INTT failed: %v", err)
|
|
}
|
|
|
|
// Check roundtrip
|
|
for i := range coeffs {
|
|
if recovered[i] != coeffs[i] {
|
|
t.Errorf("NTT roundtrip failed at index %d: got %d, want %d", i, recovered[i], coeffs[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBatchNTT(t *testing.T) {
|
|
polys := [][]uint64{
|
|
{1, 2, 3, 4, 5, 6, 7, 8},
|
|
{8, 7, 6, 5, 4, 3, 2, 1},
|
|
}
|
|
|
|
// Forward batch NTT
|
|
evals, err := BatchNTT(smallNTTParams, polys)
|
|
if err != nil {
|
|
t.Fatalf("BatchNTT failed: %v", err)
|
|
}
|
|
|
|
// Inverse batch NTT
|
|
recovered, err := BatchINTT(smallNTTParams, evals)
|
|
if err != nil {
|
|
t.Fatalf("BatchINTT failed: %v", err)
|
|
}
|
|
|
|
// Check roundtrip
|
|
for i := range polys {
|
|
for j := range polys[i] {
|
|
if recovered[i][j] != polys[i][j] {
|
|
t.Errorf("BatchNTT roundtrip failed at [%d][%d]: got %d, want %d",
|
|
i, j, recovered[i][j], polys[i][j])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPolyAdd(t *testing.T) {
|
|
a := []uint64{1, 2, 3, 4}
|
|
b := []uint64{5, 6, 7, 8}
|
|
|
|
result, err := PolyAdd(smallFieldParams, a, b)
|
|
if err != nil {
|
|
t.Fatalf("PolyAdd failed: %v", err)
|
|
}
|
|
|
|
expected := []uint64{6, 8, 10, 12}
|
|
for i := range expected {
|
|
if result[i] != expected[i] {
|
|
t.Errorf("PolyAdd[%d]: got %d, want %d", i, result[i], expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPolySub(t *testing.T) {
|
|
a := []uint64{10, 12, 14, 16}
|
|
b := []uint64{5, 6, 7, 8}
|
|
|
|
result, err := PolySub(smallFieldParams, a, b)
|
|
if err != nil {
|
|
t.Fatalf("PolySub failed: %v", err)
|
|
}
|
|
|
|
expected := []uint64{5, 6, 7, 8}
|
|
for i := range expected {
|
|
if result[i] != expected[i] {
|
|
t.Errorf("PolySub[%d]: got %d, want %d", i, result[i], expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPolyEval(t *testing.T) {
|
|
// p(x) = 1 + 2x + 3x^2
|
|
coeffs := []uint64{1, 2, 3}
|
|
points := []uint64{0, 1, 2}
|
|
|
|
result, err := PolyEval(smallFieldParams, coeffs, points)
|
|
if err != nil {
|
|
t.Fatalf("PolyEval failed: %v", err)
|
|
}
|
|
|
|
// p(0) = 1
|
|
// p(1) = 1 + 2 + 3 = 6
|
|
// p(2) = 1 + 4 + 12 = 17 mod 97 = 17
|
|
expected := []uint64{1, 6, 17}
|
|
for i := range expected {
|
|
if result[i] != expected[i] {
|
|
t.Errorf("PolyEval at x=%d: got %d, want %d", points[i], result[i], expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFieldOps(t *testing.T) {
|
|
a := []uint64{5, 10, 15}
|
|
b := []uint64{3, 4, 5}
|
|
|
|
// Test add
|
|
sum, err := FieldAdd(smallFieldParams, a, b)
|
|
if err != nil {
|
|
t.Fatalf("FieldAdd failed: %v", err)
|
|
}
|
|
expectedSum := []uint64{8, 14, 20} // All < 97
|
|
for i := range expectedSum {
|
|
if sum[i] != expectedSum[i] {
|
|
t.Errorf("FieldAdd[%d]: got %d, want %d", i, sum[i], expectedSum[i])
|
|
}
|
|
}
|
|
|
|
// Test mul
|
|
prod, err := FieldMul(smallFieldParams, a, b)
|
|
if err != nil {
|
|
t.Fatalf("FieldMul failed: %v", err)
|
|
}
|
|
// 5*3=15, 10*4=40, 15*5=75 (all < 97)
|
|
expectedProd := []uint64{15, 40, 75}
|
|
for i := range expectedProd {
|
|
if prod[i] != expectedProd[i] {
|
|
t.Errorf("FieldMul[%d]: got %d, want %d", i, prod[i], expectedProd[i])
|
|
}
|
|
}
|
|
|
|
// Test inv
|
|
c := []uint64{2, 3, 5}
|
|
inv, err := FieldInv(smallFieldParams, c)
|
|
if err != nil {
|
|
t.Fatalf("FieldInv failed: %v", err)
|
|
}
|
|
// Check a * inv(a) = 1
|
|
for i := range c {
|
|
product := (c[i] * inv[i]) % 97
|
|
if product != 1 {
|
|
t.Errorf("FieldInv[%d]: %d * %d = %d, want 1", i, c[i], inv[i], product)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFieldExp(t *testing.T) {
|
|
a := []uint64{2, 3, 5}
|
|
|
|
result, err := FieldExp(smallFieldParams, a, 4)
|
|
if err != nil {
|
|
t.Fatalf("FieldExp failed: %v", err)
|
|
}
|
|
|
|
// 2^4=16, 3^4=81, 5^4=625=43 (mod 97)
|
|
expected := []uint64{16, 81, 43}
|
|
for i := range expected {
|
|
if result[i] != expected[i] {
|
|
t.Errorf("FieldExp[%d]: got %d, want %d", i, result[i], expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFRIFold(t *testing.T) {
|
|
params := FRIParams{
|
|
Modulus: 97,
|
|
FoldFactor: 2,
|
|
BlowupFactor: 2,
|
|
}
|
|
|
|
// Simple test: even length evaluations
|
|
evals := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
|
|
alpha := uint64(3)
|
|
|
|
folded, err := FRIFold(params, evals, alpha)
|
|
if err != nil {
|
|
t.Fatalf("FRIFold failed: %v", err)
|
|
}
|
|
|
|
if len(folded) != len(evals)/2 {
|
|
t.Errorf("FRIFold: expected length %d, got %d", len(evals)/2, len(folded))
|
|
}
|
|
}
|
|
|
|
func TestMSM(t *testing.T) {
|
|
// Simple test - just verify it doesn't error
|
|
scalars := [][]byte{{1, 0, 0, 0}, {2, 0, 0, 0}}
|
|
points := [][]byte{make([]byte, 64), make([]byte, 64)}
|
|
|
|
result, err := MSM(CurveBN254, scalars, points)
|
|
if err != nil {
|
|
t.Fatalf("MSM failed: %v", err)
|
|
}
|
|
|
|
if len(result) != 64 {
|
|
t.Errorf("MSM: expected 64 byte result, got %d", len(result))
|
|
}
|
|
}
|
|
|
|
func TestPoseidon2(t *testing.T) {
|
|
// Simple test with minimal params
|
|
params := Poseidon2Params{
|
|
T: 3,
|
|
D: 5,
|
|
RoundsF: 4,
|
|
RoundsP: 4,
|
|
Modulus: 97,
|
|
}
|
|
|
|
inputs := []uint64{1, 2}
|
|
|
|
_, err := Poseidon2(params, inputs)
|
|
if err != nil {
|
|
t.Fatalf("Poseidon2 failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestErrors(t *testing.T) {
|
|
// Test invalid degree
|
|
_, err := NTT(smallNTTParams, []uint64{1, 2, 3}) // Wrong length
|
|
if err != ErrInvalidDegree {
|
|
t.Errorf("Expected ErrInvalidDegree, got %v", err)
|
|
}
|
|
|
|
// Test dimension mismatch
|
|
_, err = PolyAdd(smallFieldParams, []uint64{1, 2}, []uint64{1, 2, 3})
|
|
if err != ErrDimensionMismatch {
|
|
t.Errorf("Expected ErrDimensionMismatch, got %v", err)
|
|
}
|
|
|
|
// Test empty batch
|
|
_, err = BatchNTT(smallNTTParams, [][]uint64{})
|
|
if err != ErrEmptyBatch {
|
|
t.Errorf("Expected ErrEmptyBatch, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Benchmarks
|
|
|
|
func BenchmarkNTT_N8(b *testing.B) {
|
|
coeffs := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = NTT(smallNTTParams, coeffs)
|
|
}
|
|
}
|
|
|
|
func BenchmarkPolyMul_N8(b *testing.B) {
|
|
a := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
|
|
bc := []uint64{8, 7, 6, 5, 4, 3, 2, 1}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = PolyMul(smallNTTParams, a, bc)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFieldMul(b *testing.B) {
|
|
a := make([]uint64, 1024)
|
|
bc := make([]uint64, 1024)
|
|
for i := range a {
|
|
a[i] = uint64(i + 1)
|
|
bc[i] = uint64(i + 2)
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, _ = FieldMul(smallFieldParams, a, bc)
|
|
}
|
|
}
|