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,83 @@
package ecgfp5
import (
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
// A curve point in affine (x,u) coordinates. This is used internally
// to make "windows" that speed up point multiplications.
type AffinePoint struct {
x, u gFp5.Element
}
var AFFINE_NEUTRAL = AffinePoint{
x: gFp5.FP5_ZERO,
u: gFp5.FP5_ZERO,
}
func (p AffinePoint) ToPoint() ECgFp5Point {
return ECgFp5Point{
x: p.x,
z: gFp5.FP5_ONE,
u: p.u,
t: gFp5.FP5_ONE,
}
}
func (p *AffinePoint) SetNeg() {
p.u = gFp5.Neg(p.u)
}
// Lookup a point in a window. The win[] slice must contain values
// i*P for i = 1 to n (win[0] contains P, win[1] contains 2*P, and
// so on). Index value k is an integer in the -n to n range; returned
// point is k*P.
func (p *AffinePoint) SetLookup(win []AffinePoint, k int32) {
// sign = 0xFFFFFFFF if k < 0, 0x00000000 otherwise
sign := uint32(k >> 31)
// ka = abs(k)
ka := (uint32(k) ^ sign) - sign
// km1 = ka - 1
km1 := ka - 1
x := gFp5.FP5_ZERO
u := gFp5.FP5_ZERO
for i := 0; i < len(win); i++ {
m := km1 - uint32(i)
c_1 := (m | (^m + 1)) >> 31
c := uint64(c_1) - 1
if c != 0 {
x = win[i].x
u = win[i].u
}
}
// If k < 0, then we must negate the point.
c := uint64(sign) | (uint64(sign) << 32)
p.x = x
p.u = u
if c != 0 {
p.u = gFp5.Neg(p.u)
}
}
func Lookup(win []AffinePoint, k int32) AffinePoint {
r := AFFINE_NEUTRAL
r.SetLookup(win, k)
return r
}
// Same as lookup(), except this implementation is variable-time.
func LookupVarTime(win []AffinePoint, k int32) AffinePoint {
if k == 0 {
return AFFINE_NEUTRAL
} else if k > 0 {
return win[k-1]
} else {
res := win[-k-1]
res.SetNeg()
return res
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,441 @@
package ecgfp5
import (
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
// A curve point.
type ECgFp5Point struct {
// Internally, we use the (x,u) fractional coordinates: for curve
// point (x,y), we have (x,u) = (x,x/y) = (X/Z,U/T) (for the neutral
// N, the u coordinate is 0).
x, z, u, t gFp5.Element
}
// Constants for ECgFp5Point
var (
A_ECgFp5Point = gFp5.FromUint64Array([5]uint64{2, 0, 0, 0, 0})
B1 = uint64(263)
B_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, B1, 0, 0, 0})
B_MUL2_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 2 * B1, 0, 0, 0})
B_MUL4_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 4 * B1, 0, 0, 0})
B_MUL16_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 16 * B1, 0, 0, 0})
NEUTRAL_ECgFp5Point = ECgFp5Point{
x: gFp5.FP5_ZERO,
z: gFp5.FP5_ONE,
u: gFp5.FP5_ZERO,
t: gFp5.FP5_ONE,
}
GENERATOR_ECgFp5Point = ECgFp5Point{
x: gFp5.FromUint64Array([5]uint64{
12883135586176881569,
4356519642755055268,
5248930565894896907,
2165973894480315022,
2448410071095648785,
},
),
z: gFp5.FP5_ONE,
u: gFp5.FP5_ONE,
t: gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0}),
}
)
func (p ECgFp5Point) Equals(rhs ECgFp5Point) bool {
return gFp5.Equals(
gFp5.Mul(p.u, rhs.t),
gFp5.Mul(rhs.u, p.t),
)
}
func CanBeDecodedIntoPoint(w gFp5.Element) bool {
// Value w can be decoded if and only if it is zero, or
// (w^2 - a)^2 - 4*b is a quadratic residue.
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
deltaLegendre := gFp5.Legendre(delta)
return gFp5.IsZero(w) || deltaLegendre.IsOne()
}
func (p ECgFp5Point) Encode() gFp5.Element {
return gFp5.Mul(p.t, gFp5.InverseOrZero(p.u))
}
// Attempt to decode a point from an gFp5 element
func Decode(w gFp5.Element) (ECgFp5Point, bool) {
// Curve equation is y^2 = x*(x^2 + a*x + b); encoded value
// is w = y/x. Dividing by x, we get the equation:
// x^2 - (w^2 - a)*x + b = 0
// We solve for x and keep the solution which is not itself a
// square (if there are solutions, exactly one of them will be
// a square, and the other will not be a square).
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
r, c := gFp5.CanonicalSqrt(delta)
if !c {
r = gFp5.FP5_ZERO
}
x1 := gFp5.Div(gFp5.Add(e, r), gFp5.FP5_TWO)
x2 := gFp5.Div(gFp5.Sub(e, r), gFp5.FP5_TWO)
x := x2
x1Legendre := gFp5.Legendre(x1)
one := g.One()
if !one.Equal(&x1Legendre) {
x = x1
}
// If c == true (delta is not a sqrt) then we want to get the neutral here; note that if
// w == 0, then delta = a^2 - 4*b, which is not a square, and
// thus we also get c == 0.
if !c {
x = gFp5.FP5_ZERO
}
z := gFp5.FP5_ONE
u := gFp5.FP5_ONE
if !c {
u = gFp5.FP5_ZERO
}
t := w
if !c {
t = gFp5.FP5_ONE
}
// If w == 0 then this is in fact a success.
if c || gFp5.IsZero(w) {
return ECgFp5Point{x: x, z: z, u: u, t: t}, true
}
return ECgFp5Point{}, false
}
func (p ECgFp5Point) IsNeutral() bool {
return gFp5.IsZero(p.u)
}
// General point addition. formulas are complete (no special case).
func (p ECgFp5Point) Add(rhs ECgFp5Point) ECgFp5Point {
// cost: 10M
x1 := p.x
z1 := p.z
u1 := p.u
_t1 := p.t
x2 := rhs.x
z2 := rhs.z
u2 := rhs.u
_t2 := rhs.t
// let t1 = x1 * x2;
t1 := gFp5.Mul(x1, x2)
// let t2 = z1 * z2;
t2 := gFp5.Mul(z1, z2)
// let t3 = u1 * u2;
t3 := gFp5.Mul(u1, u2)
// let t4 = _t1 * _t2;
t4 := gFp5.Mul(_t1, _t2)
// let t5 = (x1 + z1) * (x2 + z2) - t1 - t2;
t5 := gFp5.Sub(
gFp5.Mul(gFp5.Add(x1, z1), gFp5.Add(x2, z2)),
gFp5.Add(t1, t2),
)
// let t6 = (u1 + _t1) * (u2 + _t2) - t3 - t4;
t6 := gFp5.Sub(
gFp5.Mul(gFp5.Add(u1, _t1), gFp5.Add(u2, _t2)),
gFp5.Add(t3, t4),
)
// let t7 = t1 + t2 * Self::B;
t7 := gFp5.Add(t1, gFp5.Mul(t2, B_ECgFp5Point))
// let t8 = t4 * t7;
t8 := gFp5.Mul(t4, t7)
// let t9 = t3 * (t5 * Self::B_MUL2 + t7.double());
t9 := gFp5.Mul(
t3,
gFp5.Add(gFp5.Mul(t5, B_MUL2_ECgFp5Point), gFp5.Double(t7)),
)
// let t10 = (t4 + t3.double()) * (t5 + t7);
t10 := gFp5.Mul(
gFp5.Add(t4, gFp5.Double(t3)),
gFp5.Add(t5, t7),
)
xNew := gFp5.Mul(gFp5.Sub(t10, t8), B_ECgFp5Point)
zNew := gFp5.Sub(t8, t9)
uNew := gFp5.Mul(t6, gFp5.Sub(gFp5.Mul(t2, B_ECgFp5Point), t1))
tNew := gFp5.Add(t8, t9)
return ECgFp5Point{x: xNew, z: zNew, u: uNew, t: tNew}
}
func (p ECgFp5Point) Double() ECgFp5Point {
newPoint := p
newPoint.SetDouble()
return newPoint
}
func (p *ECgFp5Point) SetDouble() {
// cost: 4M+5S
x := p.x
z := p.z
u := p.u
t := p.t
t1 := gFp5.Mul(z, t)
t2 := gFp5.Mul(t1, t)
x1 := gFp5.Square(t2)
z1 := gFp5.Mul(t1, u)
t3 := gFp5.Square(u)
w1 := gFp5.Sub(
t2,
gFp5.Mul(
t3,
gFp5.Double(gFp5.Add(x, z)),
),
)
t4 := gFp5.Square(z1)
xNew := gFp5.Mul(t4, B_MUL4_ECgFp5Point)
zNew := gFp5.Square(w1)
uNew := gFp5.Sub(
gFp5.Square(gFp5.Add(w1, z1)),
gFp5.Add(t4, zNew),
)
tNew := gFp5.Sub(
gFp5.Double(x1),
gFp5.Add(
gFp5.Mul(t4, gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0})),
zNew,
),
)
p.x = xNew
p.z = zNew
p.u = uNew
p.t = tNew
}
func (p *ECgFp5Point) MDouble(n uint32) ECgFp5Point {
newPoint := ECgFp5Point{x: p.x, z: p.z, u: p.u, t: p.t}
newPoint.SetMDouble(n)
return newPoint
}
func (p *ECgFp5Point) SetMDouble(n uint32) {
if n == 0 {
return
}
if n == 1 {
p.SetDouble()
return
}
// cost: n*(2M+5S) + 2M+1S
x0 := p.x
z0 := p.z
u0 := p.u
t0 := p.t
t1 := gFp5.Mul(z0, t0)
t2 := gFp5.Mul(t1, t0)
x1 := gFp5.Square(t2)
z1 := gFp5.Mul(t1, u0)
t3 := gFp5.Square(u0)
w1 := gFp5.Sub(
t2,
gFp5.Mul(
gFp5.Double(gFp5.Add(x0, z0)),
t3,
),
)
t4 := gFp5.Square(w1)
t5 := gFp5.Square(z1)
x := gFp5.Mul(gFp5.Square(t5), B_MUL16_ECgFp5Point)
w := gFp5.Sub(
gFp5.Double(x1),
gFp5.Add(
gFp5.Mul(t5, gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0})),
t4,
),
)
z := gFp5.Sub(
gFp5.Square(gFp5.Add(w1, z1)),
gFp5.Add(t4, t5),
)
for i := 2; i < int(n); i++ {
t1 = gFp5.Square(z)
t2 = gFp5.Square(t1)
t3 = gFp5.Square(w)
t4 = gFp5.Square(t3)
t5 = gFp5.Sub(
gFp5.Square(gFp5.Add(w, z)),
gFp5.Add(t1, t3),
)
z = gFp5.Mul(
t5,
gFp5.Sub(
gFp5.Double(gFp5.Add(x, t1)),
t3,
),
)
x = gFp5.Mul(gFp5.Mul(t2, t4), B_MUL16_ECgFp5Point)
w = gFp5.Neg(
gFp5.Add(
t4,
gFp5.Mul(
t2,
gFp5.Sub(
B_MUL4_ECgFp5Point,
gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0}),
),
),
),
)
}
t1 = gFp5.Square(w)
t2 = gFp5.Square(z)
t3 = gFp5.Sub(
gFp5.Square(gFp5.Add(w, z)),
gFp5.Add(t1, t2),
)
w1 = gFp5.Sub(
t1,
gFp5.Double(gFp5.Add(x, t2)),
)
p.x = gFp5.Mul(gFp5.Square(t3), B_ECgFp5Point)
p.z = gFp5.Square(w1)
p.u = gFp5.Mul(t3, w1)
p.t = gFp5.Sub(
gFp5.Mul(
gFp5.Double(t1),
gFp5.Sub(t1, gFp5.Double(t2)),
),
p.z,
)
}
// Add a point in affine coordinates to this one.
func (p ECgFp5Point) AddAffine(rhs AffinePoint) ECgFp5Point {
// cost: 8M
x1, z1, u1, _t1 := p.x, p.z, p.u, p.t
x2, u2 := rhs.x, rhs.u
t1 := gFp5.Mul(x1, x2)
t2 := z1
t3 := gFp5.Mul(u1, u2)
t4 := _t1
t5 := gFp5.Add(x1, gFp5.Mul(x2, z1))
t6 := gFp5.Add(u1, gFp5.Mul(u2, _t1))
t7 := gFp5.Add(t1, gFp5.Mul(t2, B_ECgFp5Point))
t8 := gFp5.Mul(t4, t7)
t9 := gFp5.Mul(t3, gFp5.Add(gFp5.Mul(t5, B_MUL2_ECgFp5Point), gFp5.Double(t7)))
t10 := gFp5.Mul(gFp5.Add(t4, gFp5.Double(t3)), gFp5.Add(t5, t7))
return ECgFp5Point{
x: gFp5.Mul(gFp5.Sub(t10, t8), B_ECgFp5Point),
u: gFp5.Mul(t6, gFp5.Sub(gFp5.Mul(t2, B_ECgFp5Point), t1)),
z: gFp5.Sub(t8, t9),
t: gFp5.Add(t8, t9),
}
}
const (
WINDOW = 5
WIN_SIZE = 1 << (WINDOW - 1)
)
// Convert points to affine coordinates.
func BatchToAffine(src []ECgFp5Point) []AffinePoint {
// We use a trick due to Montgomery: to compute the inverse of
// x and of y, a single inversion suffices, with:
// 1/x = y*(1/(x*y))
// 1/y = x*(1/(x*y))
// This extends to the case of inverting n values, with a total
// cost of 1 inversion and 3*(n-1) multiplications.
n := len(src)
if n == 0 {
return []AffinePoint{}
}
if n == 1 {
p := src[0]
m1 := gFp5.InverseOrZero(gFp5.Mul(p.z, p.t))
return []AffinePoint{
{
x: gFp5.Mul(gFp5.Mul(p.x, p.t), m1),
u: gFp5.Mul(gFp5.Mul(p.u, p.z), m1),
},
}
}
res := make([]AffinePoint, n)
// Compute product of all values to invert, and invert it.
// We also use the x and u coordinates of the points in the
// destination slice to keep track of the partial products.
m := gFp5.Mul(src[0].z, src[0].t)
for i := 1; i < n; i++ {
x := m
m = gFp5.Mul(m, src[i].z)
u := m
m = gFp5.Mul(m, src[i].t)
res[i] = AffinePoint{x: x, u: u}
}
m = gFp5.InverseOrZero(m)
// Propagate back inverses.
for i := n - 1; i > 0; i-- {
res[i].u = gFp5.Mul(gFp5.Mul(src[i].u, res[i].u), m)
m = gFp5.Mul(m, src[i].t)
res[i].x = gFp5.Mul(gFp5.Mul(src[i].x, res[i].x), m)
m = gFp5.Mul(m, src[i].z)
}
res[0].u = gFp5.Mul(gFp5.Mul(src[0].u, src[0].z), m)
m = gFp5.Mul(m, src[0].t)
res[0].x = gFp5.Mul(src[0].x, m)
return res
}
func (p ECgFp5Point) MakeWindowAffine() []AffinePoint {
tmp := make([]ECgFp5Point, WIN_SIZE)
tmp[0] = p
for i := 1; i < WIN_SIZE; i++ {
if (i & 1) == 0 {
tmp[i] = tmp[i-1].Add(p)
} else {
tmp[i] = tmp[i>>1].Double()
}
}
return BatchToAffine(tmp)
}
// Multiply this point by a scalar.
func (p *ECgFp5Point) SetMul(s *ECgFp5Scalar) {
// Make a window with affine points.
win := p.MakeWindowAffine()
digits := make([]int32, (319+WINDOW)/WINDOW)
s.RecodeSigned(digits, int32(WINDOW))
*p = LookupVarTime(win, digits[len(digits)-1]).ToPoint()
for i := len(digits) - 2; i >= 0; i-- {
p.SetMDouble(uint32(WINDOW))
lookup := Lookup(win, digits[i])
*p = p.AddAffine(lookup)
}
}
func (p ECgFp5Point) Mul(s *ECgFp5Scalar) ECgFp5Point {
newPoint := p
newPoint.SetMul(s)
return newPoint
}
@@ -0,0 +1,349 @@
package ecgfp5
import (
cryptorand "crypto/rand"
"crypto/sha256"
"encoding/binary"
"math/big"
"math/rand"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
// ECgFp5Scalar represents the scalar field of the ECgFP5 elliptic curve where
// p = 1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241
type ECgFp5Scalar [5]uint64
func (s *ECgFp5Scalar) DeepCopy() ECgFp5Scalar {
return ECgFp5Scalar{s[0], s[1], s[2], s[3], s[4]}
}
func (s ECgFp5Scalar) ToLittleEndianBytes() []byte {
var result [40]byte
for i := 0; i < 5; i++ {
binary.LittleEndian.PutUint64(result[i*8:], s[i])
}
return result[:]
}
func ScalarElementFromLittleEndianBytes(data []byte) ECgFp5Scalar {
if len(data) != 40 {
panic("invalid length")
}
var value ECgFp5Scalar
for i := 0; i < 5; i++ {
value[i] = binary.LittleEndian.Uint64(data[i*8:])
}
return value
}
func (s ECgFp5Scalar) SplitTo4BitLimbs() [80]uint8 {
limbs := s[:]
var result [80]uint8
for i := 0; i < 5; i++ {
for j := 0; j < 16; j++ {
result[i*16+j] = uint8((limbs[i] >> uint(j*4)) & 0xF)
}
}
return result
}
func SampleScalarCrypto() ECgFp5Scalar {
rng, err := cryptorand.Int(cryptorand.Reader, ORDER)
if err != nil {
panic("failed to read random bytes into buffer")
}
return FromNonCanonicalBigInt(rng)
}
func SampleScalar(seed *string) ECgFp5Scalar {
var rng *rand.Rand
if seed == nil {
return SampleScalarCrypto()
}
hash := sha256.Sum256([]byte(*seed))
var intSeed int64
for _, b := range hash[:8] {
intSeed = (intSeed << 8) | int64(b)
}
rng = rand.New(rand.NewSource(intSeed))
return FromNonCanonicalBigInt(new(big.Int).Rand(rng, ORDER))
}
var (
ORDER, _ = new(big.Int).SetString("1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241", 10)
ZERO = ECgFp5Scalar{}
ONE = ECgFp5Scalar{1, 0, 0, 0, 0}
TWO = ECgFp5Scalar{2, 0, 0, 0, 0}
NEG_ONE = ECgFp5Scalar{
0xE80FD996948BFFE0,
0xE8885C39D724A09C,
0x7FFFFFE6CFB80639,
0x7FFFFFF100000016,
0x7FFFFFFD80000007,
}
)
func (s ECgFp5Scalar) Order() *big.Int {
return ORDER
}
var (
// Group order n is slightly below 2^319. We store values over five
// 64-bit limbs. We use Montgomery multiplication to perform
// computations; however, we keep the limbs in normal
// (non-Montgomery) representation, so that operations that do not
// require any multiplication of scalars, just encoding and
// decoding, are fastest.
// The modulus itself, stored in a Scalar structure (which
// contravenes to the rules of a Scalar; this constant MUST NOT leak
// outside the API).
N = ECgFp5Scalar{
0xE80FD996948BFFE1,
0xE8885C39D724A09C,
0x7FFFFFE6CFB80639,
0x7FFFFFF100000016,
0x7FFFFFFD80000007,
}
// -1/N[0] mod 2^64
N0I = uint64(0xD78BEF72057B7BDF)
// 2^640 mod n
R2 = ECgFp5Scalar{
0xA01001DCE33DC739,
0x6C3228D33F62ACCF,
0xD1D796CC91CF8525,
0xAADFFF5D1574C1D8,
0x4ACA13B28CA251F5,
}
// 2^632 mod n
T632 = ECgFp5Scalar{
0x2B0266F317CA91B3,
0xEC1D26528E984773,
0x8651D7865E12DB94,
0xDA2ADFF5941574D0,
0x53CACA12110CA256,
}
)
func (s *ECgFp5Scalar) IsZero() bool {
for i := 0; i < 5; i++ {
if s[i] != 0 {
return false
}
}
return true
}
func (s *ECgFp5Scalar) Equals(rhs *ECgFp5Scalar) bool {
for i := 0; i < 5; i++ {
if s[i] != rhs[i] {
return false
}
}
return true
}
// raw addition (no reduction)
func (s ECgFp5Scalar) AddInner(a ECgFp5Scalar) ECgFp5Scalar {
var r ECgFp5Scalar
var c uint64 = 0
for i := 0; i < 5; i++ {
z := U128From64(s[i]).Add64(a[i]).Add64(c)
r[i] = z.Lo
c = z.Hi
}
return r
}
// raw subtraction (no reduction)
// Final borrow is returned (0xFFFFFFFFFFFFFFFF if borrow, 0 otherwise).
func (s *ECgFp5Scalar) SubInner(a *ECgFp5Scalar) (*ECgFp5Scalar, uint64) {
r := new(ECgFp5Scalar)
c := uint64(0)
for i := 0; i < 5; i++ {
z := U128From64(s[i]).Sub64(a[i]).Sub64(c)
r[i] = z.Lo
c = z.Hi & 1
}
if c != 0 {
return r, 0xFFFFFFFFFFFFFFFF
}
return r, 0
}
// If c == 0, return a0.
// If c == 0xFFFFFFFFFFFFFFFF, return a1.
// c MUST be equal to 0 or 0xFFFFFFFFFFFFFFFF.
func Select(c uint64, a0, a1 *ECgFp5Scalar) *ECgFp5Scalar {
return &ECgFp5Scalar{
a0[0] ^ (c & (a0[0] ^ a1[0])),
a0[1] ^ (c & (a0[1] ^ a1[1])),
a0[2] ^ (c & (a0[2] ^ a1[2])),
a0[3] ^ (c & (a0[3] ^ a1[3])),
a0[4] ^ (c & (a0[4] ^ a1[4])),
}
}
func (s ECgFp5Scalar) Add(rhs ECgFp5Scalar) ECgFp5Scalar {
r0 := s.AddInner(rhs)
r1, c := r0.SubInner(&N)
return *Select(c, r1, &r0)
}
func (s ECgFp5Scalar) Sub(rhs ECgFp5Scalar) ECgFp5Scalar {
r0, c := s.SubInner(&rhs)
r1 := r0.AddInner(N)
return *Select(c, r0, &r1)
}
func (s ECgFp5Scalar) Neg() ECgFp5Scalar {
return ZERO.Sub(s)
}
func (s *ECgFp5Scalar) Mul(rhs *ECgFp5Scalar) *ECgFp5Scalar {
res := s.MontyMul(&R2).MontyMul(rhs)
return res
}
func (s *ECgFp5Scalar) Square() *ECgFp5Scalar {
return s.Mul(s)
}
// Montgomery multiplication.
// Returns (self*rhs)/2^320 mod n.
// 'self' MUST be less than n (the other operand can be up to 2^320-1).
func (s *ECgFp5Scalar) MontyMul(rhs *ECgFp5Scalar) *ECgFp5Scalar {
r := new(ECgFp5Scalar)
for i := 0; i < 5; i++ {
// Iteration i computes r <- (r + self*rhs_i + f*n)/2^64.
// Factor f is at most 2^64-1 and set so that the division
// is exact.
// On input:
// r <= 2^320 - 1
// self <= n - 1
// rhs_i <= 2^64 - 1
// f <= 2^64 - 1
// Therefore:
// r + self*rhs_i + f*n <= 2^320-1 + (2^64 - 1) * (n - 1)
// + (2^64 - 1) * n
// < 2^384
// Thus, the new r fits on 320 bits.
m := rhs[i]
f := (s[0]*m + r[0]) * N0I
cc1, cc2 := uint64(0), uint64(0)
for j := 0; j < 5; j++ {
z := U128From64(s[j]).Mul64(m).Add64(r[j]).Add64(cc1)
cc1 = z.Hi
z = U128From64(f).Mul64(N[j]).Add64(z.Lo).Add64(cc2)
cc2 = z.Hi
if j > 0 {
r[j-1] = z.Lo
}
}
// No overflow here since the new r fits on 320 bits.
r[4] = cc1 + cc2
}
// We computed (self*rhs + ff*n) / 2^320, with:
// self < n
// rhs < 2^320
// ff < 2^320
// Thus, the value we obtained is lower than 2*n. Subtracting n
// once (conditionally) is sufficient to achieve full reduction.
r2, c := r.SubInner(&N)
return Select(c, r2, r)
}
func (s ECgFp5Scalar) expPowerOf2(exp int) ECgFp5Scalar {
result := s
for i := 0; i < exp; i++ {
result = *result.Square()
}
return result
}
func FromGfp5(fp5 gFp5.Element) ECgFp5Scalar {
return FromNonCanonicalBigInt(BigIntFromArray([5]uint64{
fp5[0].Uint64(), fp5[1].Uint64(), fp5[2].Uint64(), fp5[3].Uint64(), fp5[4].Uint64(),
}))
}
func BigIntFromArray(arr [5]uint64) *big.Int {
result := new(big.Int)
for i := 4; i >= 0; i-- {
result.Lsh(result, 64)
result.Or(result, new(big.Int).SetUint64(arr[i]))
}
return result
}
func FromNonCanonicalBigInt(val *big.Int) ECgFp5Scalar {
limbs := new(big.Int).Mod(val, ORDER).Bits()
if len(limbs) < 5 {
limbs = append(limbs, 0)
}
return ECgFp5Scalar{uint64(limbs[0]), uint64(limbs[1]), uint64(limbs[2]), uint64(limbs[3]), uint64(limbs[4])}
}
func (s ECgFp5Scalar) ToCanonicalBigInt() *big.Int {
result := BigIntFromArray(s)
order := ORDER
if result.Cmp(order) >= 0 {
result.Sub(result, order)
}
return result
}
// Recode a scalar into signed integers. For a window width of w
// bits, returned integers are in the -(2^w-1) to +2^w range. The
// provided slice is filled; if w*len(ss) >= 320, then the output
// encodes the complete scalar value, and the top (last) signed
// integer is nonnegative.
// Window width MUST be between 2 and 10.
func (s ECgFp5Scalar) RecodeSigned(ss []int32, w int32) {
RecodeSignedFromLimbs(s[:], ss, w)
}
func RecodeSignedFromLimbs(limbs []uint64, ss []int32, w int32) {
var acc uint64 = 0
var accLen int32 = 0
var j int = 0
mw := (uint32(1) << w) - 1
hw := uint32(1) << (w - 1)
var cc uint32 = 0
for i := 0; i < len(ss); i++ {
// Get next w-bit chunk in bb.
var bb uint32
if accLen < w {
if j < len(limbs) {
nl := limbs[j]
j++
bb = (uint32(acc | (nl << accLen))) & mw
acc = nl >> (w - accLen)
} else {
bb = uint32(acc) & mw
acc = 0
}
accLen += 64 - w
} else {
bb = uint32(acc) & mw
accLen -= w
acc >>= w
}
// If bb is greater than 2^(w-1), subtract 2^w and propagate a carry.
bb += cc
cc = (hw - bb) >> 31
ss[i] = int32(bb) - int32(cc<<w)
}
}
@@ -0,0 +1,729 @@
package ecgfp5
import (
"testing"
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
func TestSerdes(t *testing.T) {
scalar := ECgFp5Scalar{
6950590877883398434,
17178336263794770543,
11012823478139181320,
16445091359523510936,
5882925226143600273,
}
leBytes := scalar.ToLittleEndianBytes()
result := ScalarElementFromLittleEndianBytes(leBytes)
if !scalar.Equals(&result) {
t.Fatalf("Expected %v, but got %v", scalar, result)
}
}
func TestSplitTo4LimbBits(t *testing.T) {
scalar := ECgFp5Scalar{
6950590877883398434,
17178336263794770543,
11012823478139181320,
16445091359523510936,
5882925226143600273,
}
limbs := scalar.SplitTo4BitLimbs()
expectedValues := map[int]uint8{
0: 2, 1: 2, 2: 9, 3: 7, 4: 15, 5: 4, 6: 15, 7: 13,
8: 3, 9: 9, 10: 5, 11: 7, 12: 5, 13: 7, 14: 0, 15: 6,
16: 15, 17: 6, 18: 2, 19: 12, 20: 2, 21: 11, 22: 3, 23: 3,
24: 1, 25: 13, 26: 5, 27: 11, 28: 5, 29: 6, 30: 14, 31: 14,
32: 8, 33: 0, 34: 9, 35: 5, 36: 1, 37: 9, 38: 12, 39: 13,
40: 10, 41: 9, 42: 8, 43: 6, 44: 5, 45: 13, 46: 8, 47: 9,
48: 8, 49: 9, 50: 10, 51: 9, 52: 14, 53: 3, 54: 15, 55: 2,
56: 6, 57: 7, 58: 3, 59: 11, 60: 8, 61: 3, 62: 4, 63: 14,
64: 1, 65: 9, 66: 14, 67: 4, 68: 9, 69: 7, 70: 8, 71: 15,
72: 2, 73: 5, 74: 9, 75: 5, 76: 4, 77: 10, 78: 1, 79: 5,
}
for i, expected := range expectedValues {
if limbs[i] != expected {
t.Fatalf("Expected limbs[%d] to be %d, but got %d", i, expected, limbs[i])
}
}
}
func TestAddInner(t *testing.T) {
scalar1 := ECgFp5Scalar{
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
scalar2 := ECgFp5Scalar{
0xFFFFFFFFFeeFFF,
12312321312,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFacdFFFFF,
0xbcaFFFFFFFFFFFFF,
}
result := scalar1.AddInner(scalar2)
expectedValues := ECgFp5Scalar{
0xfffffffffeeffe,
0x2dddf1d20,
0xffffffffffffffff,
0xffffffacdfffff,
0xbcafffffffffffff,
}
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected %v but got %v at index %d", expectedValues[i], result[i], i)
}
}
}
func TestSubInner(t *testing.T) {
scalar1 := ECgFp5Scalar{0, 0, 0, 0, 0}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
result, carry := scalar1.SubInner(&scalar2)
expectedValues := ECgFp5Scalar{1, 0, 0, 0, 0}
expectedCarry := uint64(18446744073709551615)
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
}
}
if carry != expectedCarry {
t.Fatalf("Expected carry to be %d, but got %d", expectedCarry, carry)
}
}
func TestAddScalar(t *testing.T) {
scalar1 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
result := scalar1.AddInner(scalar2)
expectedValues := ECgFp5Scalar{0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected %v but got %v at index %d", expectedValues[i], result[i], i)
}
}
}
func TestSub(t *testing.T) {
scalar1 := ECgFp5Scalar{1, 2, 0, 0, 0}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
result := scalar1.Sub(scalar2)
expectedValues := ECgFp5Scalar{0xe80fd996948bffe3, 0xe8885c39d724a09e, 0x7fffffe6cfb80639, 0x7ffffff100000016, 0x7ffffffd80000007}
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
}
}
}
func TestSelect(t *testing.T) {
a0 := ECgFp5Scalar{1, 2, 3, 4, 5}
a1 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFB}
result := Select(uint64(0), &a0, &a1)
for i := 0; i < 5; i++ {
if result[i] != a0[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, a0[i], result[i])
}
}
result = Select(uint64(0xFFFFFFFFFFFFFFFF), &a0, &a1)
for i := 0; i < 5; i++ {
if result[i] != a1[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, a1[i], result[i])
}
}
}
func TestMontyMul(t *testing.T) {
scalar1 := ECgFp5Scalar{1, 2, 3, 4, 5}
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
result := scalar1.MontyMul(&scalar2)
expectedValues := ECgFp5Scalar{10974894505036100890, 7458803775930281466, 744239893213209819, 3396127080529349464, 5979369289905897562}
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
}
}
}
func TestMul(t *testing.T) {
scalar := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
result := scalar.Mul(&scalar)
expectedValues := ECgFp5Scalar{471447996674510360, 3520142298321118626, 17240611161823899731, 5610669884293437850, 1193611606749909414}
for i := 0; i < 5; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
}
}
}
func TestRecodeSigned(t *testing.T) {
var ss [50]int32
scalar := ECgFp5Scalar{
g.Modulus() - 1,
g.Modulus() - 2,
g.Modulus() - 3,
0xFFFFFFFFFFFFFFFF,
g.Modulus() - 5,
}
scalar.RecodeSigned(ss[:], 5)
expectedValues := map[int]int32{
6: -4,
19: -2,
25: -8,
32: -1,
}
for i, elem := range ss {
if expected, exists := expectedValues[i]; exists {
if elem != expected {
t.Fatalf("Expected ss[%d] to be %d, but got %d", i, expected, elem)
}
} else if elem != 0 {
t.Fatalf("Expected ss[%d] to be 0, but got %d", i, elem)
}
}
}
func TestFromQuinticExtension(t *testing.T) {
scalar := FromGfp5(gFp5.Element{*g.NegOne(), *g.NegOne(), *g.NegOne(), *g.NegOne(), *g.NegOne()})
expectedValues := ECgFp5Scalar{
3449841778703204414,
3382000508875488967,
212073444237,
124554051540,
17179869170,
}
for i := 0; i < 5; i++ {
if scalar[i] != expectedValues[i] {
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], scalar[i])
}
}
}
func TestAddShiftedSmall161(t *testing.T) {
scalar := Signed161{
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
scalar.AddShiftedSmall([]uint64{1, 0xFFFFFFFFFFFFDDBB, 0xFFFFAACFFFFFDDBB}, 1231233)
expectedValues := [3]uint64{
1,
18446744073709534070,
18446556744415951735,
}
for i, elem := range scalar {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestAdd161(t *testing.T) {
scalar := Signed161{
0x10FFFFabcdFF1213,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
scalar.Add([]uint64{0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFDDBB, 0xFFFFAACFFFFFDDBB})
expectedValues := [3]uint64{
2449957474057200678,
18446744073709542842,
18446650409062751675,
}
for i, elem := range scalar {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestAddShifted161(t *testing.T) {
scalar1 := Signed161{
0x10FFFFabcdFF1213,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
scalar2 := Signed161{
0xabcdabcdabcdabcd,
0xdef0def0def0def0,
0x1234123412341234,
}
scalar1.AddShifted(&scalar2, 21423423)
expectedValues := [3]uint64{
1224978737028600339,
18446744073709551615,
18446744073709551615,
}
for i, elem := range scalar1 {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar1[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestRecodeSigned5_161(t *testing.T) {
scalar1 := Signed161{
0x1234567890abcdef,
0xfedcba0987654321,
0x0fedcba987654321,
}
expectedValues := [33]int32{
15, 15, -13, -8, 11, 8, 2, 15, -10, 3, 13, 4, -15, -15, 13, 8, 5, -5, 2, -13, 1, -3, -13, -4, -1, 16, 8, 6, -12, -13, -2, -15, 0,
}
for i, elem := range scalar1.RecodeSigned5() {
if elem != expectedValues[i] {
t.Fatalf("Expected ss[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestSub161(t *testing.T) {
scalar := Signed161{
0x1010111112121313,
0x10FFFFabcdFF1213,
0xabcdef12345abcde,
}
scalar.Sub([]uint64{0xFFFFFFFFFFFFFFFF, 0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFFFFF})
expectedValues := [3]uint64{
1157443869249508116,
18446744073709551615,
12379813812178173150,
}
for i, elem := range scalar {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestSubShiftedSmall161(t *testing.T) {
scalar := Signed161{
0x1010111112121313,
0x10FFFFabcdFF1213,
0xabcdef12345abcde,
}
scalar.SubShiftedSmall([]uint64{0xFFFFFFFFFFFFFFFF, 0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFFFFF}, 5123142)
expectedValues := [3]uint64{
1157443869249508179,
15060059935745936659,
12379813812178173209,
}
for i, elem := range scalar {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestSubShifted161(t *testing.T) {
scalar1 := Signed161{
0x1010111112121313,
0x10FFFFabcdFF1213,
0xabcdef12345abcde,
}
scalar2 := Signed161{
0xabcdabcdabcdabcd,
0xdef0def0def0def0,
0x1234123412341234,
}
scalar1.SubShifted(&scalar2, 12315523)
expectedValues := [3]uint64{
1157443869249508115,
1224978737028600339,
12379813812178173150,
}
for i, elem := range scalar1 {
if elem != expectedValues[i] {
t.Fatalf("Expected scalar1[%d] to be %d, but got %d", i, expectedValues[i], elem)
}
}
}
func TestFromNsquared(t *testing.T) {
expected := Signed640{
10262430419493848001,
781583365610726095,
1685487855950207164,
0x90465B4214B27B1C,
0xD308FECCB1878B88,
0x3CC55EB2EAC07502,
0x59F038FB784335CE,
0xBFFFFE954FB808EA,
13835057829796380825,
4611686007689969677,
}
for i, limb := range FromNsquared() {
if limb != expected[i] {
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
}
}
}
func TestFromMulScalars(t *testing.T) {
a := ECgFp5Scalar{
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
b := ECgFp5Scalar{
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
result := FromMulScalars(&a, &b)
expectedValues := [10]uint64{
1,
0,
0,
0,
0,
18446744073709551614,
18446744073709551615,
18446744073709551615,
18446744073709551615,
18446744073709551615,
}
for i := 0; i < 10; i++ {
if result[i] != expectedValues[i] {
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
}
}
}
func TestAdd1_640(t *testing.T) {
a := Signed640{
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
}
a.Add1()
expected := Signed640{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
for i, limb := range a {
if limb != expected[i] {
t.Fatalf("Test case 2: Expected limb %d to be %x, but got %x", i, expected[i], limb)
}
}
}
func TestIsNonnegative640(t *testing.T) {
nonnegativeTest := Signed640{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7FFFFFFFFFFFFFFF,
}
if !nonnegativeTest.IsNonnegative() {
t.Fatalf("Expected nonnegativeTest to be nonnegative, but it is not")
}
}
func TestLtUnsigned640(t *testing.T) {
ltTest1 := Signed640{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
ltTest2 := Signed640{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
if !ltTest1.LtUnsigned(&ltTest2) {
t.Fatalf("Expected ltTest1 to be less than ltTest2, but it is not")
}
if ltTest2.LtUnsigned(&ltTest1) {
t.Fatalf("Expected ltTest2 to be greater than ltTest1, but it is not")
}
}
func TestBitlength(t *testing.T) {
bitlengthTest := Signed640{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8000000000000000,
}
bitlength := bitlengthTest.Bitlength()
expectedBitlength := int32(639)
if bitlength != expectedBitlength {
t.Fatalf("Expected bit length to be %d, but got %d", expectedBitlength, bitlength)
}
bitlengthTest = Signed640{
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
0xFFFFFFFFFFFFFaFF,
}
bitlength = bitlengthTest.Bitlength()
expectedBitlength = int32(587)
if bitlength != expectedBitlength {
t.Fatalf("Expected bit length to be %d, but got %d", expectedBitlength, bitlength)
}
}
func TestAdd640(t *testing.T) {
a := Signed640{
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFaaaF,
}
b := Signed640{
0xFFFFFabcdFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0xFFFFFFFFFFFFFFFF,
}
a.AddShifted(&b, 5543242)
expected := Signed640{
18446744073709551615,
0,
18446744073709551615,
0,
18446744073709551615,
0,
18446744073709551615,
0,
18446744073709551615,
18446744073709529775,
}
for i, limb := range a {
if limb != expected[i] {
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
}
}
a.AddShifted(&b, 63)
expected = Signed640{
9223372036854775807,
18446741180780642304,
18446744073709551614,
0,
9223372004938743807,
9223372036854775809,
18446744073709551614,
18446744041793519616,
18446744073709551614,
18446744041793497775,
}
for i, limb := range a {
if limb != expected[i] {
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
}
}
a.AddShifted(&b, 0)
expected = Signed640{
9223366250996957182,
18446741180780642304,
18446744073709551614,
18446744009877487616,
9223372004938743807,
9223372036854775808,
18446744009877487614,
18446744041793519616,
18446744009877487614,
18446744041793497775,
}
for i, limb := range a {
if limb != expected[i] {
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
}
}
}
func TestSubShifted640(t *testing.T) {
// Create two Signed640 instances for the test
a := Signed640{
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFaaaF,
}
b := Signed640{
0xFFFFFabcdFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFF1234FFFFF,
0xFFFFFFFFFFFFFFFF,
}
a.SubShifted(&b, 313)
expectedLargeShift := Signed640{
18446744073709551615,
0,
18446744073709551615,
0,
18446744073709551615,
144115188075855872,
18446744073709551615,
498688000,
18446744073709551615,
498666159,
}
for i, limb := range a {
if limb != expectedLargeShift[i] {
t.Fatalf("sub_shifted (large shift): Expected limb %d to be %x, but got %x", i, expectedLargeShift[i], limb)
}
}
a.SubShifted(&b, 63)
expectedSmallShift := Signed640{
9223372036854775807,
2892928909313,
18446744073709551615,
0,
9223372068770807807,
9367487224930631680,
18446744073709551615,
32414720000,
18446744073709551615,
32414698159,
}
for i, limb := range a {
if limb != expectedSmallShift[i] {
t.Fatalf("sub_shifted (small shift): Expected limb %d to be %x, but got %x", i, expectedSmallShift[i], limb)
}
}
a.SubShifted(&b, 0)
expectedZeroShift := Signed640{
9223377822712594432,
2892928909313,
18446744073709551615,
63832064000,
9223372068770807806,
9367487224930631681,
63832063999,
32414720001,
63832063999,
32414698160,
}
for i, limb := range a {
if limb != expectedZeroShift[i] {
t.Fatalf("sub: Expected limb %d to be %x, but got %x", i, expectedZeroShift[i], limb)
}
}
}
@@ -0,0 +1,96 @@
package ecgfp5
// A custom 161-bit integer type; used for splitting a scalar into a
// fraction. Negative values use two's complement notation; the value
// is truncated to 161 bits (upper bits in the top limb are ignored).
// Elements are mutable containers.
// WARNING: everything in here is vartime; do not use on secret values.
type Signed161 [3]uint64
// Export this value as a 192-bit integer (three 64-bit limbs, in little-endian order).
func (s Signed161) ToU192() [3]uint64 {
x := s[2] & 0x00000001FFFFFFFF
x |= (^(x >> 32) + 1) << 33
return [3]uint64{s[0], s[1], x}
}
// Recode this integer into 33 signed digits for a 5-bit window.
func (s Signed161) RecodeSigned5() [33]int32 {
// We first sign-extend the value to 192 bits, then add
// 2^160 to get a nonnegative value in the 0 to 2^161-1
// range. We then recode that value; and finally we fix
// the result by subtracting 1 from the top digit.
tmp := s.ToU192()
tmp[2] += 0x0000000100000000
var ss [33]int32
RecodeSignedFromLimbs(tmp[:], ss[:], 5)
ss[32] -= 1
return ss
}
// Add v*2^s to this value.
func (s *Signed161) AddShifted(v *Signed161, shift int32) {
if shift == 0 {
s.Add(v[:])
} else if shift < 64 {
s.AddShiftedSmall(v[:], shift)
} else if shift < 161 {
s.AddShiftedSmall(v[(shift>>6):], shift&63)
}
}
func (s *Signed161) AddShiftedSmall(v []uint64, shift int32) {
cc, vbits, j := uint64(0), uint64(0), 3-len(v)
for i := j; i < 3; i++ {
vw := v[i-j]
vws := (vw << (uint32(shift) % 64)) | vbits
vbits = vw >> ((64 - uint32(shift)) % 64)
z := U128From64(s[i]).Add64(vws).Add64(cc)
s[i] = z.Lo
cc = z.Hi
}
}
func (s *Signed161) Add(v []uint64) {
cc, j := uint64(0), 3-len(v)
for i := j; i < 3; i++ {
z := U128From64(s[i]).Add64(v[i-j]).Add64(cc)
s[i] = z.Lo
cc = z.Hi
}
}
// Subtract v*2^s from this value.
func (s *Signed161) SubShifted(v *Signed161, shift int32) {
if shift == 0 {
s.Sub(v[:])
} else if shift < 64 {
s.SubShiftedSmall(v[:], shift)
} else if shift < 161 {
s.SubShiftedSmall(v[(shift>>6):], shift&63)
}
}
func (s *Signed161) SubShiftedSmall(v []uint64, shift int32) {
cc, vbits, j := uint64(0), uint64(0), 3-len(v)
for i := j; i < 3; i++ {
vw := v[i-j]
vws := (vw << (uint32(shift) % 64)) | vbits
vbits = vw >> ((64 - uint32(shift)) % 64)
z := U128From64(s[i]).Sub64(vws).Sub64(cc)
s[i] = z.Lo
cc = z.Hi & 1
}
}
func (s *Signed161) Sub(v []uint64) {
cc, j := uint64(0), 3-len(v)
for i := j; i < 3; i++ {
z := U128From64(s[i]).Sub64(v[i-j]).Sub64(cc)
s[i] = z.Lo
cc = z.Hi & 1
}
}
@@ -0,0 +1,182 @@
package ecgfp5
// A custom 640-bit integer type (signed).
// Elements are mutable containers.
// WARNING: everything in here is vartime; do not use on secret values.
type Signed640 [10]uint64
// Obtain an instance containing n^2.
func FromNsquared() *Signed640 {
return &Signed640{
0x8E6B7A18061803C1,
0x0AD8BDEE1594E2CF,
0x17640E465F2598BC,
0x90465B4214B27B1C,
0xD308FECCB1878B88,
0x3CC55EB2EAC07502,
0x59F038FB784335CE,
0xBFFFFE954FB808EA,
0xBFFFFFCB80000099,
0x3FFFFFFD8000000D,
}
}
// Obtain an instance containing a*b (both a and b are interpreted
// as integers in the 0..n-1 range).
func FromMulScalars(a, b *ECgFp5Scalar) *Signed640 {
var r Signed640
for i := 0; i < 5; i++ {
aw := a[i]
cc := uint64(0)
for j := 0; j < 5; j++ {
z := U128From64(aw).Mul64(b[j]).Add64(r[i+j]).Add64(cc)
r[i+j] = z.Lo
cc = z.Hi
}
r[i+5] = cc
}
return &r
}
// Add 1 to this instance.
func (s *Signed640) Add1() {
for i := 0; i < 10; i++ {
s[i]++
if s[i] != 0 {
return
}
}
}
func (s *Signed640) IsNonnegative() bool {
return (s[9] >> 63) == 0
}
func (s *Signed640) LtUnsigned(rhs *Signed640) bool {
for i := 9; i >= 0; i-- {
aw := s[i]
bw := rhs[i]
if aw < bw {
return true
}
if aw > bw {
return false
}
}
return false
}
// Get the bit length of this value. The bit length is defined as the
// minimal size of the binary representation in two's complement,
// _excluding_ the sign bit (thus, -2^k has bit length k, whereas +2^k
// has bit length k+1).
func (s *Signed640) Bitlength() int32 {
sm := (^(s[9] >> 63) + 1)
for i := 9; i >= 0; i-- {
w := s[i] ^ sm
if w != 0 {
return (int32(i) << 6) + U64Bitlength(w)
}
}
return 0
}
func U64Bitlength(w uint64) int32 {
// We use here a portable algorithm; some architectures have
// dedicated opcodes that could speed up this operation
// greatly (e.g. lzcnt on recent x86).
var x = w
var r int32
if x > 0xFFFFFFFF {
x >>= 32
r += 32
}
if x > 0x0000FFFF {
x >>= 16
r += 16
}
if x > 0x000000FF {
x >>= 8
r += 8
}
if x > 0x0000000F {
x >>= 4
r += 4
}
if x > 0x00000003 {
x >>= 2
r += 2
}
return r + int32(x) - int32((x+1)>>2)
}
// Add v*2^s to this instance.
func (s *Signed640) AddShifted(v *Signed640, shift int32) {
if shift == 0 {
s.Add(v[:])
} else if shift < 64 {
s.AddShiftedSmall(v[:], shift)
} else if shift < 640 {
s.AddShiftedSmall(v[(shift>>6):], shift&63)
}
}
func (s *Signed640) AddShiftedSmall(v []uint64, shift int32) {
cc := uint64(0)
j := 10 - len(v)
vbits := uint64(0)
for i := j; i < 10; i++ {
vw := v[i-j]
vws := (vw << (uint32(shift) % 64)) | vbits
vbits = vw >> ((64 - uint32(shift)) % 64)
z := U128From64(s[i]).Add64(vws).Add64(cc)
s[i] = z.Lo
cc = z.Hi
}
}
func (s *Signed640) Add(v []uint64) {
cc := uint64(0)
j := 10 - len(v)
for i := j; i < 10; i++ {
z := U128From64(s[i]).Add64(v[i-j]).Add64(cc)
s[i] = z.Lo
cc = z.Hi
}
}
// Subtract v*2^s from this instance.
func (s *Signed640) SubShifted(v *Signed640, shift int32) {
if shift == 0 {
s.Sub(v[:])
} else if shift < 64 {
s.SubShiftedSmall(v[:], shift)
} else if shift < 640 {
s.SubShiftedSmall(v[(shift>>6):], shift&63)
}
}
func (s *Signed640) SubShiftedSmall(v []uint64, shift int32) {
cc, vbits, j := uint64(0), uint64(0), 10-len(v)
for i := j; i < 10; i++ {
vw := v[i-j]
vws := (vw << (uint32(shift) % 64)) | vbits
vbits = vw >> ((64 - uint32(shift)) % 64)
z := U128From64(s[i]).Sub64(vws).Sub64(cc)
s[i] = z.Lo
cc = z.Hi & 1
}
}
func (s *Signed640) Sub(v []uint64) {
cc, j := uint64(0), 10-len(v)
for i := j; i < 10; i++ {
z := U128From64(s[i]).Sub64(v[i-j]).Sub64(cc)
s[i] = z.Lo
cc = z.Hi & 1
}
}
@@ -0,0 +1,29 @@
package ecgfp5
import (
"math/bits"
)
type U128 struct{ Hi, Lo uint64 }
func U128From64(v uint64) U128 { return U128{Lo: v} }
func (u U128) Add64(n uint64) (v U128) {
var carry uint64
v.Lo, carry = bits.Add64(u.Lo, n, 0)
v.Hi = u.Hi + carry
return v
}
func (u U128) Sub64(n uint64) (v U128) {
var borrowed uint64
v.Lo, borrowed = bits.Sub64(u.Lo, n, 0)
v.Hi = u.Hi - borrowed
return v
}
func (u U128) Mul64(n uint64) (dest U128) {
dest.Hi, dest.Lo = bits.Mul64(u.Lo, n)
dest.Hi += u.Hi * n
return dest
}
@@ -0,0 +1,180 @@
package ecgfp5
import (
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
// A curve point in short Weirstrass form (x, y). This is used by the in-circuit representation
type WeierstrassPoint struct {
X gFp5.Element
Y gFp5.Element
IsInf bool
}
var (
GENERATOR_WEIERSTRASS = WeierstrassPoint{
X: gFp5.Element{
g.FromUint64(11712523173042564207),
g.FromUint64(14090224426659529053),
g.FromUint64(13197813503519687414),
g.FromUint64(16280770174934269299),
g.FromUint64(15998333998318935536),
},
Y: gFp5.Element{
g.FromUint64(14639054205878357578),
g.FromUint64(17426078571020221072),
g.FromUint64(2548978194165003307),
g.FromUint64(8663895577921260088),
g.FromUint64(9793640284382595140),
},
IsInf: false,
}
A_WEIERSTRASS = gFp5.Element{
g.FromUint64(6148914689804861439),
g.FromUint64(263),
g.FromUint64(0),
g.FromUint64(0),
g.FromUint64(0),
}
NEUTRAL_WEIERSTRASS = WeierstrassPoint{
X: gFp5.FP5_ZERO,
Y: gFp5.FP5_ZERO,
IsInf: true,
}
)
func (p WeierstrassPoint) Equals(q WeierstrassPoint) bool {
if p.IsInf && q.IsInf {
return true
}
return gFp5.Equals(p.X, q.X) && gFp5.Equals(p.Y, q.Y)
}
func (p WeierstrassPoint) Encode() gFp5.Element {
return gFp5.Div(p.Y, gFp5.Sub(gFp5.Div(A_ECgFp5Point, gFp5.FromUint64(3)), p.X))
}
func DecodeFp5AsWeierstrass(w gFp5.Element) (WeierstrassPoint, bool) {
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
r, success := gFp5.CanonicalSqrt(delta)
if !success {
r = gFp5.FP5_ZERO
}
x1 := gFp5.Div(gFp5.Add(e, r), gFp5.FP5_TWO)
x2 := gFp5.Div(gFp5.Sub(e, r), gFp5.FP5_TWO)
x := x1
x1Legendre := gFp5.Legendre(x1)
if !x1Legendre.IsOne() {
x = x2
}
y := gFp5.Neg(gFp5.Mul(w, x))
if success {
x = gFp5.Add(x, gFp5.Div(A_ECgFp5Point, gFp5.FromUint64(3)))
} else {
x = gFp5.FP5_ZERO
}
isInf := !success
// If w == 0 then this is in fact a success.
if success || gFp5.IsZero(w) {
return WeierstrassPoint{X: x, Y: y, IsInf: isInf}, true
}
return WeierstrassPoint{}, false
}
func (p WeierstrassPoint) Add(q WeierstrassPoint) WeierstrassPoint {
if p.IsInf {
return q
}
if q.IsInf {
return p
}
x1, y1 := p.X, p.Y
x2, y2 := q.X, q.Y
// note: paper has a typo. sx == 1 when x1 != x2, not when x1 == x2
xSame := gFp5.Equals(x1, x2)
yDiff := !gFp5.Equals(y1, y2)
var lambda0, lambda1 gFp5.Element
if xSame {
lambda0 = gFp5.Add(gFp5.Triple(gFp5.Square(x1)), A_WEIERSTRASS)
lambda1 = gFp5.Double(y1)
} else {
lambda0 = gFp5.Sub(y2, y1)
lambda1 = gFp5.Sub(x2, x1)
}
lambda := gFp5.Div(lambda0, lambda1)
x3 := gFp5.Sub(gFp5.Sub(gFp5.Square(lambda), x1), x2)
y3 := gFp5.Sub(gFp5.Mul(lambda, gFp5.Sub(x1, x3)), y1)
return WeierstrassPoint{X: x3, Y: y3, IsInf: xSame && yDiff}
}
func (p WeierstrassPoint) Double() WeierstrassPoint {
x := p.X
y := p.Y
is_inf := p.IsInf
if is_inf {
return p
}
lambda0 := gFp5.Square(x)
lambda0 = gFp5.Triple(lambda0)
lambda0 = gFp5.Add(lambda0, A_WEIERSTRASS)
lambda1 := gFp5.Double(y)
lambda := gFp5.Div(lambda0, lambda1)
x2 := gFp5.Square(lambda)
two_x := gFp5.Double(x)
x2 = gFp5.Sub(x2, two_x)
y2 := gFp5.Sub(x, x2)
y2 = gFp5.Mul(lambda, y2)
y2 = gFp5.Sub(y2, y)
return WeierstrassPoint{X: x2, Y: y2, IsInf: is_inf}
}
func (p WeierstrassPoint) PrecomputeWindow(windowBits uint32) []WeierstrassPoint {
if windowBits < 2 {
panic("windowBits in PrecomputeWindow for WeierstrassPoint must be at least 2")
}
multiples := []WeierstrassPoint{NEUTRAL_WEIERSTRASS, p, p.Double()}
for i := 3; i < 1<<windowBits; i++ {
multiples = append(multiples, p.Add(multiples[len(multiples)-1]))
}
return multiples
}
func MulAdd2(a, b WeierstrassPoint, scalarA, scalarB ECgFp5Scalar) WeierstrassPoint {
aWindow := a.PrecomputeWindow(4)
aFourBitLimbs := scalarA.SplitTo4BitLimbs()
bWindow := b.PrecomputeWindow(4)
bFourBitLimbs := scalarB.SplitTo4BitLimbs()
numLimbs := len(aFourBitLimbs)
res := aWindow[aFourBitLimbs[numLimbs-1]].Add(bWindow[bFourBitLimbs[numLimbs-1]])
for i := numLimbs - 2; i >= 0; i-- {
for j := 0; j < 4; j++ {
res = res.Double()
}
res = res.Add(aWindow[aFourBitLimbs[i]].Add(bWindow[bFourBitLimbs[i]]))
}
return res
}