feat: 添加 Lighter 适配器及相关功能,支持 trailing stops 和新的交易逻辑

This commit is contained in:
discountry
2025-09-30 22:08:05 +08:00
parent e83bdfccf9
commit c8b0ab1c8e
101 changed files with 90621 additions and 3 deletions
@@ -0,0 +1,499 @@
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")
}
}
@@ -0,0 +1,4 @@
package goldilocks
//go:noescape
func branchHint()
@@ -0,0 +1,3 @@
TEXT ·branchHint(SB),$0
NOP
RET
@@ -0,0 +1,198 @@
package goldilocks
// Partially wraps and extends the functionality of the goldilocks field package.
import (
"fmt"
g "github.com/consensys/gnark-crypto/field/goldilocks"
)
type Element = g.Element
const Bytes = 8
func NewElement(value uint64) Element {
return g.NewElement(value)
}
func reverseBytes(b []byte) []byte {
res := make([]byte, len(b))
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
res[i], res[j] = b[j], b[i]
}
return res
}
func ArrayFromCanonicalLittleEndianBytes(in []byte) ([]Element, error) {
missing := 8 - len(in)%8
if missing == 8 {
missing = 0
}
ret := make([]Element, 0)
for i := 0; i < len(in); {
nextStart := i + 8
if nextStart > len(in) {
nextStart = len(in)
}
slice := make([]byte, 8)
copy(slice[:], in[i:nextStart])
if len(slice) < 8 {
slice = append(slice, make([]byte, missing)...)
}
elem, err := FromCanonicalLittleEndianBytes(slice)
if err != nil {
return nil, fmt.Errorf("failed to convert bytes to field element. bytes: %v, error: %w", slice, err)
}
ret = append(ret, *elem)
i = nextStart
}
return ret, nil
}
func ToLittleEndianBytes(e ...Element) []byte {
res := make([]byte, 0)
for _, elem := range e {
bytes := elem.Bytes()
res = append(res, reverseBytes(bytes[:])...)
}
return res
}
func FromCanonicalLittleEndianBytes(in []byte) (*Element, error) {
elem := g.NewElement(0)
err := elem.SetBytesCanonical(reverseBytes(in))
if err != nil {
return nil, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
return &elem, nil
}
func ArrayToLittleEndianBytes(e []Element) []byte {
res := make([]byte, 0)
for _, elem := range e {
res = append(res, ToLittleEndianBytes(elem)...)
}
return res
}
func ToString(e ...Element) string {
res := ""
for _, elem := range e {
res += elem.String() + " "
}
return res
}
func FromBool(value bool) Element {
if value {
return One()
}
return Zero()
}
func FromInt64Abs(value int64) Element {
return FromUint64(uint64(value & 0x7FFFFFFFFFFFFFFF))
}
func FromInt64(value int64) Element {
elem := g.NewElement(0)
elem.SetInt64(value)
return elem
}
func FromUint64(value uint64) Element {
elem := g.NewElement(0)
elem.SetUint64(value)
return elem
}
func FromUint32(value uint32) Element {
return FromUint64(uint64(value))
}
func Equals(a, b *Element) bool {
return a.Equal(b)
}
func Modulus() uint64 {
return g.Modulus().Uint64()
}
func Zero() Element {
return g.NewElement(0)
}
func One() Element {
return g.NewElement(1)
}
func Neg(e Element) Element {
res := g.NewElement(0)
res.Neg(&e)
return res
}
func NegOne() *Element {
res := Neg(One())
return &res
}
func Sample() Element {
elem := g.NewElement(0)
elem.SetRandom()
return elem
}
func RandArray(count int) []Element {
ret := make([]Element, count)
for i := 0; i < count; i++ {
ret[i] = Sample()
}
return ret
}
func Add(elems ...Element) Element {
res := g.NewElement(0)
for _, elem := range elems {
res.Add(&res, &elem)
}
return res
}
func Sub(a, b *Element) Element {
res := g.NewElement(0)
res.Sub(a, b)
return res
}
func Mul(elems ...*Element) Element {
res := g.NewElement(1)
for _, elem := range elems {
res.Mul(&res, elem)
}
return res
}
func Sqrt(elem *Element) *Element {
elemCopy := DeepCopy(elem)
return elemCopy.Sqrt(&elemCopy)
}
// Powers starting from 1
func Powers(e *Element, count int) []Element {
ret := make([]Element, count)
ret[0] = g.One()
for i := 1; i < int(count); i++ {
ret[i].Mul(&ret[i-1], e)
}
return ret
}
func DeepCopy(source *Element) Element {
return Element{source[0]}
}
@@ -0,0 +1,154 @@
package goldilocks
import (
"crypto/rand"
"encoding/binary"
"math/big"
"math/bits"
)
type GoldilocksField uint64
const EPSILON = uint64((1 << 32) - 1)
const ORDER = uint64(0xffffffff00000001)
var ORDER_BIG, _ = new(big.Int).SetString("0xffffffff00000001", 16)
func NonCannonicalGoldilocksField(x int64) GoldilocksField {
if x < 0 {
return NegF(GoldilocksField(-x))
}
return GoldilocksField(x)
}
func ZeroF() GoldilocksField {
return 0
}
func OneF() GoldilocksField {
return 1
}
func NegOneF() GoldilocksField {
return GoldilocksField(ORDER - 1)
}
func (z GoldilocksField) IsZero() bool {
return z.ToCanonicalUint64() == 0
}
func (z GoldilocksField) ToCanonicalUint64() uint64 {
x := uint64(z)
if x >= ORDER {
x -= ORDER
}
return x
}
func AddF(lhs, rhs GoldilocksField) GoldilocksField {
sum, over := bits.Add64(uint64(lhs), uint64(rhs), 0)
sum, over = bits.Add64(sum, over*EPSILON, 0)
if over == 1 {
branchHint()
sum += EPSILON // this can't overflow
}
return GoldilocksField(sum)
}
func DoubleF(lhs GoldilocksField) GoldilocksField {
return AddF(lhs, lhs)
}
func SubF(lhs, rhs GoldilocksField) GoldilocksField {
diff, borrow := bits.Sub64(uint64(lhs), uint64(rhs), 0)
diff, borrow = bits.Sub64(diff, borrow*EPSILON, 0)
if borrow == 1 {
branchHint()
diff -= EPSILON // this can't underflow
}
return GoldilocksField(diff)
}
func MulF(lhs, rhs GoldilocksField) GoldilocksField {
x_hi, x_lo := bits.Mul64(uint64(lhs), uint64(rhs))
x_hi_hi := x_hi >> 32
x_hi_lo := x_hi & EPSILON
t0, borrow := bits.Sub64(x_lo, x_hi_hi, 0)
if borrow == 1 {
branchHint()
t0 -= EPSILON
}
t1 := x_hi_lo * EPSILON
sum, over := bits.Add64(t0, t1, 0)
t2 := sum + EPSILON*over
return GoldilocksField(t2)
}
func SquareF(x GoldilocksField) GoldilocksField {
return MulF(x, x)
}
func ExpPowerOf2(x GoldilocksField, n uint) GoldilocksField {
z := x
for i := uint(0); i < n; i++ {
z = SquareF(z)
}
return z
}
func NegF(x GoldilocksField) GoldilocksField {
z := GoldilocksField(0)
if !x.IsZero() {
z = GoldilocksField(ORDER - x.ToCanonicalUint64())
}
return z
}
func SampleF() GoldilocksField {
rng, err := rand.Int(rand.Reader, ORDER_BIG)
if err != nil {
panic("failed to read random bytes into buffer")
}
return GoldilocksField(rng.Uint64())
}
func ToLittleEndianBytesF(z GoldilocksField) []byte {
res := make([]byte, Bytes)
binary.LittleEndian.PutUint64(res, z.ToCanonicalUint64())
return res
}
func FromCanonicalLittleEndianBytesF(b []byte) GoldilocksField {
return GoldilocksField(binary.LittleEndian.Uint64(b))
}
// func (z *GoldilocksField) Inverse(x *GoldilocksField) *GoldilocksField {
// if x.IsZero() {
// z.SetZero()
// return z
// }
// var tmp *GoldilocksField
// t2 := *tmp.Square(x).Mul(tmp, x)
// t3 := *tmp.Square(&t2).Mul(tmp, x)
// t6 := *tmp.ExpPowerOf2(&t3, 3).Mul(tmp, &t3)
// t12 := *tmp.ExpPowerOf2(&t6, 6).Mul(tmp, &t6)
// t24 := *tmp.ExpPowerOf2(&t12, 12).Mul(tmp, &t12)
// t30 := *tmp.ExpPowerOf2(&t24, 6).Mul(tmp, &t6)
// t31 := *tmp.Square(&t30).Mul(tmp, x)
// t63 := *tmp.ExpPowerOf2(&t31, 32).Mul(tmp, &t31)
// z.Square(&t63).Mul(z, x)
// return z
// }
@@ -0,0 +1,393 @@
package goldilocks_quintic_extension
import (
"fmt"
"math/big"
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
)
type Element [5]g.Element
type NumericalElement [5]uint64
const Bytes = g.Bytes * 5
var (
FP5_D = 5
FP5_ZERO = Element{g.Zero(), g.Zero(), g.Zero(), g.Zero(), g.Zero()}
FP5_ONE = Element{g.One(), g.Zero(), g.Zero(), g.Zero(), g.Zero()}
FP5_TWO = FromF(g.FromUint64(2))
FP5_W = g.FromUint64(3)
FP5_DTH_ROOT = g.FromUint64(1041288259238279555)
)
func (e *Element) ToString() string {
return fmt.Sprintf("%d,%d,%d,%d,%d", e[0].Uint64(), e[1].Uint64(), e[2].Uint64(), e[3].Uint64(), e[4].Uint64())
}
func (e Element) ToUint64Array() [5]uint64 {
return [5]uint64{e[0].Uint64(), e[1].Uint64(), e[2].Uint64(), e[3].Uint64(), e[4].Uint64()}
}
func gFp5FromUint64Array(arr [5]uint64) Element {
return Element{g.FromUint64(arr[0]), g.FromUint64(arr[1]), g.FromUint64(arr[2]), g.FromUint64(arr[3]), g.FromUint64(arr[4])}
}
func (e Element) ToBasefieldArray() [5]g.Element {
return [5]g.Element{e[0], e[1], e[2], e[3], e[4]}
}
func gFp5FromBasefieldArray(arr [5]g.Element) Element {
return Element{arr[0], arr[1], arr[2], arr[3], arr[4]}
}
func (e Element) ToLittleEndianBytes() []byte {
elemBytes := [Bytes]byte{}
for i, limb := range e {
copy(elemBytes[i*g.Bytes:], g.ToLittleEndianBytes(limb))
}
return elemBytes[:]
}
func FromCanonicalLittleEndianBytes(in []byte) (Element, error) {
if len(in) != Bytes {
return Element{}, fmt.Errorf("input bytes len should be 40 but is %d", len(in))
}
elemBytesLittleEndian := [5][]byte{
{in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]},
{in[8], in[9], in[10], in[11], in[12], in[13], in[14], in[15]},
{in[16], in[17], in[18], in[19], in[20], in[21], in[22], in[23]},
{in[24], in[25], in[26], in[27], in[28], in[29], in[30], in[31]},
{in[32], in[33], in[34], in[35], in[36], in[37], in[38], in[39]},
}
e1, err := g.FromCanonicalLittleEndianBytes(elemBytesLittleEndian[0])
if err != nil {
return Element{}, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
e2, err := g.FromCanonicalLittleEndianBytes(elemBytesLittleEndian[1])
if err != nil {
return Element{}, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
e3, err := g.FromCanonicalLittleEndianBytes(elemBytesLittleEndian[2])
if err != nil {
return Element{}, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
e4, err := g.FromCanonicalLittleEndianBytes(elemBytesLittleEndian[3])
if err != nil {
return Element{}, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
e5, err := g.FromCanonicalLittleEndianBytes(elemBytesLittleEndian[4])
if err != nil {
return Element{}, fmt.Errorf("failed to convert bytes to field element: %w", err)
}
return Element{*e1, *e2, *e3, *e4, *e5}, nil
}
func Sample() Element {
arr := g.RandArray(5)
return Element{arr[0], arr[1], arr[2], arr[3], arr[4]}
}
func Equals(a, b Element) bool {
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] && a[4] == b[4]
}
func IsZero(e Element) bool {
return e[0].IsZero() && e[1].IsZero() && e[2].IsZero() && e[3].IsZero() && e[4].IsZero()
}
func FromF(elem g.Element) Element {
return Element{elem, g.Zero(), g.Zero(), g.Zero(), g.Zero()}
}
func FromUint64(a uint64) Element {
return Element{g.FromUint64(a), g.Zero(), g.Zero(), g.Zero(), g.Zero()}
}
func FromUint64Array(elems [5]uint64) Element {
return Element{
g.FromUint64(elems[0]),
g.FromUint64(elems[1]),
g.FromUint64(elems[2]),
g.FromUint64(elems[3]),
g.FromUint64(elems[4]),
}
}
func Neg(e Element) Element {
return Element{g.Neg(e[0]), g.Neg(e[1]), g.Neg(e[2]), g.Neg(e[3]), g.Neg(e[4])}
}
func Add(a, b Element) Element {
return Element{
g.Add(a[0], b[0]),
g.Add(a[1], b[1]),
g.Add(a[2], b[2]),
g.Add(a[3], b[3]),
g.Add(a[4], b[4]),
}
}
func Sub(a, b Element) Element {
return Element{
g.Sub(&a[0], &b[0]),
g.Sub(&a[1], &b[1]),
g.Sub(&a[2], &b[2]),
g.Sub(&a[3], &b[3]),
g.Sub(&a[4], &b[4]),
}
}
func Mul(a, b Element) Element {
w := FP5_W
a0b0 := g.Mul(&a[0], &b[0])
a1b4 := g.Mul(&a[1], &b[4])
a2b3 := g.Mul(&a[2], &b[3])
a3b2 := g.Mul(&a[3], &b[2])
a4b1 := g.Mul(&a[4], &b[1])
added := g.Add(a1b4, a2b3, a3b2, a4b1)
muld := g.Mul(&w, &added)
c0 := g.Add(a0b0, muld)
a0b1 := g.Mul(&a[0], &b[1])
a1b0 := g.Mul(&a[1], &b[0])
a2b4 := g.Mul(&a[2], &b[4])
a3b3 := g.Mul(&a[3], &b[3])
a4b2 := g.Mul(&a[4], &b[2])
added = g.Add(a2b4, a3b3, a4b2)
muld = g.Mul(&w, &added)
c1 := g.Add(a0b1, a1b0, muld)
a0b2 := g.Mul(&a[0], &b[2])
a1b1 := g.Mul(&a[1], &b[1])
a2b0 := g.Mul(&a[2], &b[0])
a3b4 := g.Mul(&a[3], &b[4])
a4b3 := g.Mul(&a[4], &b[3])
added = g.Add(a3b4, a4b3)
muld = g.Mul(&w, &added)
c2 := g.Add(a0b2, a1b1, a2b0, muld)
a0b3 := g.Mul(&a[0], &b[3])
a1b2 := g.Mul(&a[1], &b[2])
a2b1 := g.Mul(&a[2], &b[1])
a3b0 := g.Mul(&a[3], &b[0])
a4b4 := g.Mul(&a[4], &b[4])
muld = g.Mul(&w, &a4b4)
c3 := g.Add(a0b3, a1b2, a2b1, a3b0, muld)
a0b4 := g.Mul(&a[0], &b[4])
a1b3 := g.Mul(&a[1], &b[3])
a2b2 := g.Mul(&a[2], &b[2])
a3b1 := g.Mul(&a[3], &b[1])
a4b0 := g.Mul(&a[4], &b[0])
c4 := g.Add(a0b4, a1b3, a2b2, a3b1, a4b0)
return Element{c0, c1, c2, c3, c4}
}
func Div(a, b Element) Element {
bInv := InverseOrZero(b)
if IsZero(bInv) {
panic("division by zero")
}
return Mul(a, bInv)
}
func ExpPowerOf2(x Element, power int) Element {
res := Element{x[0], x[1], x[2], x[3], x[4]}
for i := 0; i < power; i++ {
res = Square(res)
}
return res
}
func Square(a Element) Element {
w := FP5_W
double_w := g.Add(w, w)
a0s := g.Mul(&a[0], &a[0])
a1a4 := g.Mul(&a[1], &a[4])
a2a3 := g.Mul(&a[2], &a[3])
added := g.Add(a1a4, a2a3)
muld := g.Mul(&double_w, &added)
c0 := g.Add(a0s, muld)
a0Double := g.Add(a[0], a[0])
a0Doublea1 := g.Mul(&a0Double, &a[1])
a2a4DoubleW := g.Mul(&a[2], &a[4], &double_w)
a3a3w := g.Mul(&a[3], &a[3], &w)
c1 := g.Add(a0Doublea1, a2a4DoubleW, a3a3w)
a0Doublea2 := g.Mul(&a0Double, &a[2])
a1Square := g.Mul(&a[1], &a[1])
a4a3DoubleW := g.Mul(&a[4], &a[3], &double_w)
c2 := g.Add(a0Doublea2, a1Square, a4a3DoubleW)
a1Double := g.Add(a[1], a[1])
a0Doublea3 := g.Mul(&a0Double, &a[3])
a1Doublea2 := g.Mul(&a1Double, &a[2])
a4SquareW := g.Mul(&a[4], &a[4], &w)
c3 := g.Add(a0Doublea3, a1Doublea2, a4SquareW)
a0Doublea4 := g.Mul(&a0Double, &a[4])
a1Doublea3 := g.Mul(&a1Double, &a[3])
a2Square := g.Mul(&a[2], &a[2])
c4 := g.Add(a0Doublea4, a1Doublea3, a2Square)
return Element{c0, c1, c2, c3, c4}
}
func Triple(a Element) Element {
three := g.FromUint64(3)
return Element{
g.Mul(&a[0], &three),
g.Mul(&a[1], &three),
g.Mul(&a[2], &three),
g.Mul(&a[3], &three),
g.Mul(&a[4], &three),
}
}
func Sqrt(x Element) (Element, bool) {
v := ExpPowerOf2(x, 31)
d := Mul(Mul(x, ExpPowerOf2(v, 32)), InverseOrZero(v))
e := Frobenius(Mul(d, RepeatedFrobenius(d, 2)))
_f := Square(e)
x1f4 := g.Mul(&x[1], &_f[4])
x2f3 := g.Mul(&x[2], &_f[3])
x3f2 := g.Mul(&x[3], &_f[2])
x4f1 := g.Mul(&x[4], &_f[1])
added := g.Add(x1f4, x2f3, x3f2, x4f1)
three := g.FromUint64(3)
muld := g.Mul(&three, &added)
x0f0 := g.Mul(&x[0], &_f[0])
_g := g.Add(x0f0, muld)
s := g.Sqrt(&_g)
if s == nil {
return Element{}, false
}
eInv := InverseOrZero(e)
sFp5 := FromF(*s)
return Mul(sFp5, eInv), true
}
func Sgn0(x Element) bool {
sign := false
zero := true
for _, limb := range x {
sign_i := (limb.Uint64() & 1) == 0
zero_i := limb.IsZero()
sign = sign || (zero && sign_i)
zero = zero && zero_i
}
return sign
}
func CanonicalSqrt(x Element) (Element, bool) {
sqrtX, exists := Sqrt(x)
if !exists {
return Element{}, false
}
if Sgn0(sqrtX) {
return Neg(sqrtX), true
}
return sqrtX, true
}
func ScalarMul(a Element, scalar g.Element) Element {
return Element{
g.Mul(&a[0], &scalar),
g.Mul(&a[1], &scalar),
g.Mul(&a[2], &scalar),
g.Mul(&a[3], &scalar),
g.Mul(&a[4], &scalar),
}
}
func Double(a Element) Element {
return Add(a, a)
}
func InverseOrZero(a Element) Element {
if IsZero(a) {
return FP5_ZERO
}
d := Frobenius(a)
e := Mul(d, Frobenius(d))
f := Mul(e, RepeatedFrobenius(e, 2))
a0b0 := g.Mul(&a[0], &f[0])
a1b4 := g.Mul(&a[1], &f[4])
a2b3 := g.Mul(&a[2], &f[3])
a3b2 := g.Mul(&a[3], &f[2])
a4b1 := g.Mul(&a[4], &f[1])
added := g.Add(a1b4, a2b3, a3b2, a4b1)
muld := g.Mul(&FP5_W, &added)
g := g.Add(a0b0, muld)
return ScalarMul(f, *g.Inverse(&g))
}
func Frobenius(x Element) Element {
return RepeatedFrobenius(x, 1)
}
func RepeatedFrobenius(x Element, count int) Element {
if count == 0 {
return x
} else if count >= FP5_D {
return RepeatedFrobenius(x, count%FP5_D)
}
z0 := FP5_DTH_ROOT
for i := 1; i < count; i++ {
z0 = g.Mul(&FP5_DTH_ROOT, &z0)
}
res := Element{}
for i, z := range g.Powers(&z0, FP5_D) {
res[i] = g.Mul(&x[i], &z)
}
return res
}
func Legendre(x Element) g.Element {
frob1 := Frobenius(x)
frob2 := Frobenius(frob1)
frob1TimesFrob2 := Mul(frob1, frob2)
frob2Frob1TimesFrob2 := RepeatedFrobenius(frob1TimesFrob2, 2)
xrExt := Mul(Mul(x, frob1TimesFrob2), frob2Frob1TimesFrob2)
xr := g.FromUint64(xrExt[0].Uint64())
xr31 := xr.Exp(xr, new(big.Int).SetUint64(1<<31))
xr31InvOrZero := g.FromUint64(0)
xr31InvOrZero = *xr31InvOrZero.Inverse(xr31)
xr63 := xr31.Exp(*xr31, new(big.Int).SetUint64(1<<32))
return g.Mul(xr63, &xr31InvOrZero)
}
func FromPlonky2GoldilocksField(f []g.GoldilocksField) Element {
return Element{
g.NewElement(uint64(f[0])),
g.NewElement(uint64(f[1])),
g.NewElement(uint64(f[2])),
g.NewElement(uint64(f[3])),
g.NewElement(uint64(f[4])),
}
}