mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 09:18:08 +00:00
442 lines
9.7 KiB
Go
442 lines
9.7 KiB
Go
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
|
|
}
|