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,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
}