Files
ritmex-bot/docs/lighter/poseidon_crypto-main/field/field_test.go
T

500 lines
14 KiB
Go

package field
import (
"encoding/binary"
"math"
"testing"
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
"math/big"
"math/rand/v2"
)
func TestBytes(t *testing.T) {
e1 := g.Sample()
leBytes := g.ToLittleEndianBytes(e1)
beBytes := e1.Bytes()
for i := 0; i < g.Bytes; i++ {
if beBytes[i] != leBytes[g.Bytes-i-1] {
t.Fatalf("Big endian and little endian bytes are not reversed")
}
}
e1ReconstructedLE, _ := g.FromCanonicalLittleEndianBytes(leBytes)
if !g.Equals(&e1, e1ReconstructedLE) {
t.Fatalf("bytes do not match")
}
r := rand.Uint64N(g.ORDER)
leBytesUint64 := make([]byte, 8)
binary.LittleEndian.PutUint64(leBytesUint64, r)
leBytesElem := g.ToLittleEndianBytes(g.FromUint64(r))
for i := 0; i < 8; i++ {
if leBytesUint64[i] != leBytesElem[i] {
t.Fatalf("Little-endian bytes do not match at index %d: expected %x, got %x", i, leBytesUint64[i], leBytesElem[i])
}
}
}
func TestBytesF(t *testing.T) {
r := rand.Uint64N(g.ORDER)
f := g.GoldilocksField(r)
rBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(rBytes, r)
fBytes := g.ToLittleEndianBytesF(f)
for i := 0; i < 8; i++ {
if rBytes[i] != fBytes[i] {
t.Fatalf("Little-endian bytes do not match at index %d: expected %x, got %x", i, rBytes[i], fBytes[i])
}
}
ff := g.FromCanonicalLittleEndianBytesF(fBytes)
if ff != f {
t.Fatalf("bytes do not match")
}
}
// Goldilocks field tests
// Inputs that covers several input ranges
var inputs = []uint64{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 2147483638, 2147483639, 2147483640, 2147483641, 2147483642, 2147483643, 2147483644, 2147483645, 2147483646, 2147483647, 2147483648, 2147483649,
2147483650, 2147483651, 2147483652, 2147483653, 2147483654, 2147483655, 2147483656, 2147483657, 4294967286, 4294967287, 4294967288, 4294967289, 4294967290, 4294967291,
4294967292, 4294967293, 4294967294, 4294967295, 4294967296, 4294967297, 4294967298, 4294967299, 4294967300, 4294967301, 4294967302, 4294967303, 4294967304, 4294967305,
9223372036854775798, 9223372036854775799, 9223372036854775800, 9223372036854775801, 9223372036854775802, 9223372036854775803, 9223372036854775804, 9223372036854775805,
9223372036854775806, 9223372036854775807, 9223372036854775808, 9223372036854775809, 9223372036854775810, 9223372036854775811, 9223372036854775812, 9223372036854775813,
9223372036854775814, 9223372036854775815, 9223372036854775816, 9223372036854775817, 18446744069414584311, 18446744069414584312, 18446744069414584313, 18446744069414584314,
18446744069414584315, 18446744069414584316, 18446744069414584317, 18446744069414584318, 18446744069414584319, 18446744069414584320,
}
func NewBigInt(x uint64) *big.Int {
return big.NewInt(0).SetUint64(x)
}
func SumMod(x, y uint64) uint64 {
sum := NewBigInt(x).Add(NewBigInt(x), NewBigInt(y))
res := sum.Mod(sum, NewBigInt(g.ORDER))
if !res.IsUint64() {
panic("sum is not uint64")
}
return res.Uint64()
}
func SubMod(x, y uint64) uint64 {
sub := NewBigInt(x).Sub(NewBigInt(x), NewBigInt(y))
res := sub.Mod(sub, NewBigInt(g.ORDER))
if !res.IsUint64() {
panic("difference is not uint64")
}
return res.Uint64()
}
func MulMod(x, y uint64) uint64 {
mul := NewBigInt(x).Mul(NewBigInt(x), NewBigInt(y))
res := mul.Mod(mul, NewBigInt(g.ORDER))
if !res.IsUint64() {
panic("product is not uint64")
}
return res.Uint64()
}
func NegMod(x uint64) uint64 {
neg := NewBigInt(x).Neg(NewBigInt(x))
res := neg.Mod(neg, NewBigInt(g.ORDER))
if !res.IsUint64() {
panic("negative number is not uint64")
}
return res.Uint64()
}
func SquareMod(x uint64) uint64 {
square := NewBigInt(x).Mul(NewBigInt(x), NewBigInt(x))
res := square.Mod(square, NewBigInt(g.ORDER))
if !res.IsUint64() {
panic("square is not uint64")
}
return res.Uint64()
}
func TestAddF(t *testing.T) {
for _, lhs := range inputs {
for _, rhs := range inputs {
fLhs := g.GoldilocksField(lhs)
fRhs := g.GoldilocksField(rhs)
sum := g.AddF(fLhs, fRhs).ToCanonicalUint64()
expected := SumMod(lhs, rhs)
if sum != expected {
t.Fatalf("Expected %d + %d = %d, but got %d", lhs, rhs, expected, sum)
}
}
}
}
func TestSubF(t *testing.T) {
for _, lhs := range inputs {
for _, rhs := range inputs {
fLhs := g.GoldilocksField(lhs)
fRhs := g.GoldilocksField(rhs)
diff := g.SubF(fLhs, fRhs).ToCanonicalUint64()
expected := SubMod(lhs, rhs)
if diff != expected {
t.Fatalf("Expected %d - %d = %d, but got %d", lhs, rhs, expected, diff)
}
}
}
}
func TestMulF(t *testing.T) {
for _, lhs := range inputs {
for _, rhs := range inputs {
fLhs := g.GoldilocksField(lhs)
fRhs := g.GoldilocksField(rhs)
mul := g.MulF(fLhs, fRhs).ToCanonicalUint64()
expected := MulMod(lhs, rhs)
if mul != expected {
t.Fatalf("Expected %d * %d = %d, but got %d", lhs, rhs, expected, mul)
}
}
}
}
func TestNegF(t *testing.T) {
for _, lhs := range inputs {
fLhs := g.GoldilocksField(lhs)
neg := g.NegF(fLhs).ToCanonicalUint64()
expected := NegMod(lhs)
if neg != expected {
t.Fatalf("Expected Neg(%d) = %d, but got %d", lhs, expected, neg)
}
}
}
func TestSquareF(t *testing.T) {
for _, lhs := range inputs {
fLhs := g.GoldilocksField(lhs)
sqr := g.SquareF(fLhs).ToCanonicalUint64()
expected := SquareMod(lhs)
if sqr != expected {
t.Fatalf("Expected (%d)^2 = %d, but got %d", lhs, expected, sqr)
}
}
}
func TestSubFDoubleWraparound(t *testing.T) {
/*
let (a, b) = (F::from_canonical_u64((F::ORDER + 1u64) / 2u64), F::TWO);
let x = a * b;
assert_eq!(x, F::ONE);
assert_eq!(F::ZERO - x, F::NEG_ONE);
*/
a := g.GoldilocksField((g.ORDER + 1) / 2)
b := g.GoldilocksField(2)
x := g.MulF(a, b)
if x.ToCanonicalUint64() != g.OneF().ToCanonicalUint64() {
t.Fatalf("Expected a*b to be 1, but got %v", x)
}
if g.SubF(g.ZeroF(), x).ToCanonicalUint64() != g.NegOneF().ToCanonicalUint64() {
t.Fatalf("Expected 0 - x to be -1, but got %v", g.SubF(g.ZeroF(), x))
}
}
func TestAddFDoubleWraparound(t *testing.T) {
/*
let a = F::from_canonical_u64(u64::MAX - F::ORDER);
let b = F::NEG_ONE;
let c = (a + a) + (b + b);
let d = (a + b) + (a + b);
assert_eq!(c, d);
*/
a := g.GoldilocksField(math.MaxUint64 - g.ORDER)
b := g.NegOneF()
c := g.AddF(g.AddF(a, a), g.AddF(b, b))
d := g.AddF(g.AddF(a, b), g.AddF(a, b))
if c.ToCanonicalUint64() != d.ToCanonicalUint64() {
t.Fatalf("Expected c to be equal to d, but got %v and %v", c, d)
}
}
// Quintic extension tests
func TestQuinticExtensionAddSubMulSquare(t *testing.T) {
val1 := gFp5.Element{
g.FromUint64(0x1234567890ABCDEF),
g.FromUint64(0x0FEDCBA987654321),
g.FromUint64(0x1122334455667788),
g.FromUint64(0x8877665544332211),
g.FromUint64(0xAABBCCDDEEFF0011),
}
val2 := gFp5.Element{
g.FromUint64(0xFFFFFFFFFFFFFFFF),
g.FromUint64(0xFFFFFFFFFFFFFFFF),
g.FromUint64(0xFFFFFFFFFFFFFFFF),
g.FromUint64(0xFFFFFFFFFFFFFFFF),
g.FromUint64(0xFFFFFFFFFFFFFFFF),
}
add := gFp5.Add(val1, val2)
expectedAdd := [5]uint64{1311768471589866989, 1147797413325783839, 1234605620731475846, 9833440832084189711, 12302652064957136911}
for i := 0; i < 5; i++ {
if add[i].Uint64() != expectedAdd[i] {
t.Fatalf("Addition: Expected limb %d to be %x, but got %x", i, expectedAdd[i], add[i])
}
}
sub := gFp5.Sub(val1, val2)
expectedSub := [5]uint64{1311768462999932401, 1147797404735849251, 1234605612141541258, 9833440823494255123, 12302652056367202323}
for i := 0; i < 5; i++ {
if sub[i].Uint64() != expectedSub[i] {
t.Fatalf("Subtraction: Expected limb %d to be %x, but got %x", i, expectedSub[i], sub[i])
}
}
mul := gFp5.Mul(val1, val2)
expectedMul := [5]uint64{12801331769143413385, 14031114708135177824, 4192851210753422088, 14031114723597060086, 4193451712464626164}
for i := 0; i < 5; i++ {
if mul[i].Uint64() != expectedMul[i] {
t.Fatalf("Multiplication: Expected limb %d to be %x, but got %x", i, expectedMul[i], mul[i])
}
}
square := gFp5.Square(val1)
expectedSquare := [5]uint64{
2711468769317614959,
15562737284369360677,
48874032493986270,
11211402278708723253,
2864528669572451733,
}
for i := 0; i < 5; i++ {
if square[i].Uint64() != expectedSquare[i] {
t.Fatalf("Square: Expected limb %d to be %x, but got %x", i, expectedSquare[i], square[i])
}
}
}
func TestQuinticExtensionAddSubMulSquareF(t *testing.T) {
val1 := gFp5.FromPlonky2GoldilocksField([]g.GoldilocksField{
g.GoldilocksField(0x1234567890ABCDEF),
g.GoldilocksField(0x0FEDCBA987654321),
g.GoldilocksField(0x1122334455667788),
g.GoldilocksField(0x8877665544332211),
g.GoldilocksField(0xAABBCCDDEEFF0011),
})
val2 := gFp5.FromPlonky2GoldilocksField([]g.GoldilocksField{
g.GoldilocksField(0xFFFFFFFFFFFFFFFF),
g.GoldilocksField(0xFFFFFFFFFFFFFFFF),
g.GoldilocksField(0xFFFFFFFFFFFFFFFF),
g.GoldilocksField(0xFFFFFFFFFFFFFFFF),
g.GoldilocksField(0xFFFFFFFFFFFFFFFF),
})
add := gFp5.Add(val1, val2)
expectedAdd := [5]uint64{1311768471589866989, 1147797413325783839, 1234605620731475846, 9833440832084189711, 12302652064957136911}
for i := 0; i < 5; i++ {
if add[i].Uint64() != expectedAdd[i] {
t.Fatalf("Addition: Expected limb %d to be %x, but got %x", i, expectedAdd[i], add[i])
}
}
sub := gFp5.Sub(val1, val2)
expectedSub := [5]uint64{1311768462999932401, 1147797404735849251, 1234605612141541258, 9833440823494255123, 12302652056367202323}
for i := 0; i < 5; i++ {
if sub[i].Uint64() != expectedSub[i] {
t.Fatalf("Subtraction: Expected limb %d to be %x, but got %x", i, expectedSub[i], sub[i])
}
}
mul := gFp5.Mul(val1, val2)
expectedMul := [5]uint64{12801331769143413385, 14031114708135177824, 4192851210753422088, 14031114723597060086, 4193451712464626164}
for i := 0; i < 5; i++ {
if mul[i].Uint64() != expectedMul[i] {
t.Fatalf("Multiplication: Expected limb %d to be %x, but got %x", i, expectedMul[i], mul[i])
}
}
square := gFp5.Square(val1)
expectedSquare := [5]uint64{
2711468769317614959,
15562737284369360677,
48874032493986270,
11211402278708723253,
2864528669572451733,
}
for i := 0; i < 5; i++ {
if square[i].Uint64() != expectedSquare[i] {
t.Fatalf("Square: Expected limb %d to be %x, but got %x", i, expectedSquare[i], square[i])
}
}
}
func TestRepeatedFrobeniusgFp5(t *testing.T) {
val := gFp5.Element{
g.FromUint64(0x1234567890ABCDEF),
g.FromUint64(0x0FEDCBA987654321),
g.FromUint64(0x1122334455667788),
g.FromUint64(0x8877665544332211),
g.FromUint64(0xAABBCCDDEEFF0011),
}
res := gFp5.RepeatedFrobenius(val, 1)
expected := [5]uint64{
1311768467294899695,
5234265561494296110,
6204816484784411482,
8858034429214283719,
17855579289599571296,
}
for i := 0; i < 5; i++ {
if res[i] != g.FromUint64(expected[i]) {
t.Fatalf("Assertion failed at index %d: expected %d, got %d", i, expected[i], res[i])
}
}
}
func TestTryInverse(t *testing.T) {
val := gFp5.Element{
g.FromUint64(0x1234567890ABCDEF),
g.FromUint64(0x0FEDCBA987654321),
g.FromUint64(0x1122334455667788),
g.FromUint64(0x8877665544332211),
g.FromUint64(0xAABBCCDDEEFF0011),
}
result := gFp5.InverseOrZero(val)
// Expected values
expected := [5]uint64{
10760985268447604442,
1770001646280707407,
826117924202660585,
45414427571889187,
8256636258983026155,
}
for i, elem := range result.ToBasefieldArray() {
if elem.Uint64() != expected[i] {
t.Fatalf("Assertion failed at index %d: expected %d, got %d", i, expected[i], elem)
}
}
}
func TestQuinticExtSgn0(t *testing.T) {
if !gFp5.Sgn0(gFp5.Element{
g.FromUint64(7146494650688613286),
g.FromUint64(2524706331227574337),
g.FromUint64(2805008444831673606),
g.FromUint64(10342159727506097401),
g.FromUint64(5582307593199735986),
}) {
t.Fatalf("Expected sign to be true, but got false")
}
}
func TestSqrtFunctions(t *testing.T) {
x := gFp5.Element{
g.FromUint64(17397692312497920520),
g.FromUint64(4597259071399531684),
g.FromUint64(15835726694542307225),
g.FromUint64(16979717054676631815),
g.FromUint64(12876043227925845432),
}
expected := gFp5.Element{
g.FromUint64(16260118390353633405),
g.FromUint64(2204473665618140400),
g.FromUint64(10421517006653550782),
g.FromUint64(4618467884536173852),
g.FromUint64(15556190572415033139),
}
result, exists := gFp5.CanonicalSqrt(x)
if !exists {
t.Fatalf("Expected canonical sqrt to exist, but it does not")
}
if !gFp5.Equals(result, expected) {
t.Fatalf("Expected canonical sqrt to be %v, but got %v", expected, result)
}
result2, exists2 := gFp5.Sqrt(x)
if !exists2 {
t.Fatalf("Expected sqrt to exist, but it does not")
}
if !gFp5.Equals(result2, expected) {
t.Fatalf("Expected sqrt to be %v, but got %v", expected, result2)
}
}
func TestSqrtNonExistent(t *testing.T) {
_, exists := gFp5.Sqrt(gFp5.Element{
g.FromUint64(3558249639744866495),
g.FromUint64(2615658757916804776),
g.FromUint64(14375546700029059319),
g.FromUint64(16160052538060569780),
g.FromUint64(8366525948816396307),
})
if exists {
t.Fatalf("Expected sqrt not to exist, but it does")
}
}
func TestLegendre(t *testing.T) {
// Test zero
zeroLegendre := gFp5.Legendre(gFp5.FP5_ZERO)
if !zeroLegendre.IsZero() {
t.Fatalf("Expected Legendre symbol of zero to be zero")
}
// Test non-squares
for i := 0; i < 32; i++ {
var x gFp5.Element
for {
attempt := gFp5.Sample()
if _, exists := gFp5.Sqrt(attempt); !exists {
x = attempt
break
}
}
legendreSym := gFp5.Legendre(x)
negOne := g.NegOne()
if !negOne.Equal(&legendreSym) {
t.Fatalf("Expected Legendre symbol of non-square to be -1, but got %v", legendreSym)
}
}
// Test squares
for i := 0; i < 32; i++ {
x := gFp5.Sample()
square := gFp5.Square(x)
legendreSym := gFp5.Legendre(square)
if !legendreSym.IsOne() {
t.Fatalf("Expected Legendre symbol of square to be 1, but got %v", legendreSym)
}
}
// Test zero again
x := gFp5.FP5_ZERO
square := gFp5.Mul(x, x)
legendreSym := gFp5.Legendre(square)
if !legendreSym.IsZero() {
t.Fatalf("Expected Legendre symbol of zero to be zero")
}
}