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,174 @@
package poseidon2
import g "github.com/elliottech/poseidon_crypto/field/goldilocks"
const (
WIDTH = 12
RATE = 8
OUT = 4
D = 7
// Generated by `poseidon2_round_numbers_128`
ROUNDS_F = 8
ROUNDS_F_HALF = 4
ROUNDS_P = 22
)
var (
// Generated randomly for ROUNDS_F
EXTERNAL_CONSTANTS = [ROUNDS_F][WIDTH]g.Element{
{
g.NewElement(15492826721047263190),
g.NewElement(11728330187201910315),
g.NewElement(8836021247773420868),
g.NewElement(16777404051263952451),
g.NewElement(5510875212538051896),
g.NewElement(6173089941271892285),
g.NewElement(2927757366422211339),
g.NewElement(10340958981325008808),
g.NewElement(8541987352684552425),
g.NewElement(9739599543776434497),
g.NewElement(15073950188101532019),
g.NewElement(12084856431752384512),
},
{
g.NewElement(4584713381960671270),
g.NewElement(8807052963476652830),
g.NewElement(54136601502601741),
g.NewElement(4872702333905478703),
g.NewElement(5551030319979516287),
g.NewElement(12889366755535460989),
g.NewElement(16329242193178844328),
g.NewElement(412018088475211848),
g.NewElement(10505784623379650541),
g.NewElement(9758812378619434837),
g.NewElement(7421979329386275117),
g.NewElement(375240370024755551),
},
{
g.NewElement(3331431125640721931),
g.NewElement(15684937309956309981),
g.NewElement(578521833432107983),
g.NewElement(14379242000670861838),
g.NewElement(17922409828154900976),
g.NewElement(8153494278429192257),
g.NewElement(15904673920630731971),
g.NewElement(11217863998460634216),
g.NewElement(3301540195510742136),
g.NewElement(9937973023749922003),
g.NewElement(3059102938155026419),
g.NewElement(1895288289490976132),
},
{
g.NewElement(5580912693628927540),
g.NewElement(10064804080494788323),
g.NewElement(9582481583369602410),
g.NewElement(10186259561546797986),
g.NewElement(247426333829703916),
g.NewElement(13193193905461376067),
g.NewElement(6386232593701758044),
g.NewElement(17954717245501896472),
g.NewElement(1531720443376282699),
g.NewElement(2455761864255501970),
g.NewElement(11234429217864304495),
g.NewElement(4746959618548874102),
},
{
g.NewElement(13571697342473846203),
g.NewElement(17477857865056504753),
g.NewElement(15963032953523553760),
g.NewElement(16033593225279635898),
g.NewElement(14252634232868282405),
g.NewElement(8219748254835277737),
g.NewElement(7459165569491914711),
g.NewElement(15855939513193752003),
g.NewElement(16788866461340278896),
g.NewElement(7102224659693946577),
g.NewElement(3024718005636976471),
g.NewElement(13695468978618890430),
},
{
g.NewElement(8214202050877825436),
g.NewElement(2670727992739346204),
g.NewElement(16259532062589659211),
g.NewElement(11869922396257088411),
g.NewElement(3179482916972760137),
g.NewElement(13525476046633427808),
g.NewElement(3217337278042947412),
g.NewElement(14494689598654046340),
g.NewElement(15837379330312175383),
g.NewElement(8029037639801151344),
g.NewElement(2153456285263517937),
g.NewElement(8301106462311849241),
},
{
g.NewElement(13294194396455217955),
g.NewElement(17394768489610594315),
g.NewElement(12847609130464867455),
g.NewElement(14015739446356528640),
g.NewElement(5879251655839607853),
g.NewElement(9747000124977436185),
g.NewElement(8950393546890284269),
g.NewElement(10765765936405694368),
g.NewElement(14695323910334139959),
g.NewElement(16366254691123000864),
g.NewElement(15292774414889043182),
g.NewElement(10910394433429313384),
},
{
g.NewElement(17253424460214596184),
g.NewElement(3442854447664030446),
g.NewElement(3005570425335613727),
g.NewElement(10859158614900201063),
g.NewElement(9763230642109343539),
g.NewElement(6647722546511515039),
g.NewElement(909012944955815706),
g.NewElement(18101204076790399111),
g.NewElement(11588128829349125809),
g.NewElement(15863878496612806566),
g.NewElement(5201119062417750399),
g.NewElement(176665553780565743),
},
}
// Generated randomly for ROUNDS_P
INTERNAL_CONSTANTS = [ROUNDS_P]g.Element{
g.NewElement(11921381764981422944),
g.NewElement(10318423381711320787),
g.NewElement(8291411502347000766),
g.NewElement(229948027109387563),
g.NewElement(9152521390190983261),
g.NewElement(7129306032690285515),
g.NewElement(15395989607365232011),
g.NewElement(8641397269074305925),
g.NewElement(17256848792241043600),
g.NewElement(6046475228902245682),
g.NewElement(12041608676381094092),
g.NewElement(12785542378683951657),
g.NewElement(14546032085337914034),
g.NewElement(3304199118235116851),
g.NewElement(16499627707072547655),
g.NewElement(10386478025625759321),
g.NewElement(13475579315436919170),
g.NewElement(16042710511297532028),
g.NewElement(1411266850385657080),
g.NewElement(9024840976168649958),
g.NewElement(14047056970978379368),
g.NewElement(838728605080212101),
}
// Taken from Plonk3 Poseidon2 implementation. https://github.com/Plonky3/Plonky3/blob/eeb4e37b20127c4daa871b2bad0df30a7c7380db/goldilocks/src/poseidon2.rs#L28
MATRIX_DIAG_12_U64 = [WIDTH]g.Element{
g.NewElement(0xc3b6c08e23ba9300),
g.NewElement(0xd84b5de94a324fb6),
g.NewElement(0x0d0c371c5b35b84f),
g.NewElement(0x7964f570e7188037),
g.NewElement(0x5daf18bbd996604b),
g.NewElement(0x6743bc47b9595257),
g.NewElement(0x5528b9362c59bb70),
g.NewElement(0xac45e25b7127b68b),
g.NewElement(0xa2077d7dfbb606b5),
g.NewElement(0xf3faac6faee378ae),
g.NewElement(0x0c6388b51545e883),
g.NewElement(0xd27dbb6944917b60),
}
)
@@ -0,0 +1,237 @@
package poseidon2
import (
"fmt"
"hash"
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
)
type HashOut [4]g.Element
type NumericalHashOut [4]uint64
func (h HashOut) ToLittleEndianBytes() []byte {
return g.ArrayToLittleEndianBytes([]g.Element{h[0], h[1], h[2], h[3]})
}
func (h HashOut) ToUint64Array() [4]uint64 {
return [4]uint64{h[0].Uint64(), h[1].Uint64(), h[2].Uint64(), h[3].Uint64()}
}
func HashToQuinticExtension(m []g.Element) gFp5.Element {
res := HashNToMNoPad(m, 5)
return gFp5.Element(res[:])
}
func HashOutFromUint64Array(arr [4]uint64) HashOut {
return HashOut{g.FromUint64(arr[0]), g.FromUint64(arr[1]), g.FromUint64(arr[2]), g.FromUint64(arr[3])}
}
func HashOutFromLittleEndianBytes(b []byte) (HashOut, error) {
if len(b) != 4*g.Bytes {
return HashOut{}, fmt.Errorf("input bytes len should be 32 but is %d", len(b))
}
gArr, err := g.ArrayFromCanonicalLittleEndianBytes(b)
if err != nil {
return HashOut{}, fmt.Errorf("failed to convert bytes to field element. bytes: %v, error: %w", b, err)
}
return HashOut{gArr[0], gArr[1], gArr[2], gArr[3]}, nil
}
func EmptyHashOut() HashOut {
return HashOut{g.Zero(), g.Zero(), g.Zero(), g.Zero()}
}
type Poseidon2 struct{}
func HashNoPad(input []g.Element) HashOut {
return HashNToHashNoPad(input)
}
func HashNToOne(input []HashOut) HashOut {
if len(input) == 1 {
return input[0]
}
res := HashTwoToOne(input[0], input[1])
for i := 2; i < len(input); i++ {
res = HashTwoToOne(res, input[i])
}
return res
}
func HashTwoToOne(input1, input2 HashOut) HashOut {
return HashNToHashNoPad([]g.Element{input1[0], input1[1], input1[2], input1[3], input2[0], input2[1], input2[2], input2[3]})
}
func HashNToHashNoPad(input []g.Element) HashOut {
res := HashNToMNoPad(input, 4)
return HashOut{res[0], res[1], res[2], res[3]}
}
func HashNToMNoPad(input []g.Element, numOutputs int) []g.Element {
var perm [WIDTH]g.Element
for i := 0; i < len(input); i += RATE {
for j := 0; j < RATE && i+j < len(input); j++ {
perm[j].Set(&input[i+j])
}
Permute(&perm)
}
outputs := make([]g.Element, 0, numOutputs)
for {
for i := 0; i < RATE; i++ {
outputs = append(outputs, perm[i])
if len(outputs) == numOutputs {
return outputs
}
}
Permute(&perm)
}
}
func Permute(input *[WIDTH]g.Element) {
externalLinearLayer(input)
fullRounds(input, 0)
partialRounds(input)
fullRounds(input, ROUNDS_F_HALF)
}
func fullRounds(state *[WIDTH]g.Element, start int) {
for r := start; r < start+ROUNDS_F_HALF; r++ {
addRC(state, r)
sbox(state)
externalLinearLayer(state)
}
}
func partialRounds(state *[WIDTH]g.Element) {
for r := 0; r < ROUNDS_P; r++ {
addRCI(state, r)
sboxP(0, state)
internalLinearLayer(state)
}
}
func externalLinearLayer(s *[WIDTH]g.Element) {
for i := 0; i < 3; i++ { // 4 size window
var t0, t1, t2, t3, t4, t5, t6 g.Element
t0.Add(&s[4*i], &s[4*i+1]) // s0+s1
t1.Add(&s[4*i+2], &s[4*i+3]) // s2+s3
t2.Add(&t0, &t1) // t0+t1 = s0+s1+s2+s3
t3.Add(&t2, &s[4*i+1]) // t2+s1 = s0+2s1+s2+s3
t4.Add(&t2, &s[4*i+3]) // t2+s3 = s0+s1+s2+2s3
t5.Double(&s[4*i]) // 2s0
t6.Double(&s[4*i+2]) // 2s2
s[4*i].Add(&t3, &t0)
s[4*i+1].Add(&t6, &t3)
s[4*i+2].Add(&t1, &t4)
s[4*i+3].Add(&t5, &t4)
}
sums := [4]g.Element{}
for k := 0; k < 4; k++ {
for j := 0; j < WIDTH; j += 4 {
sums[k].Add(&sums[k], &s[j+k])
}
}
for i := 0; i < WIDTH; i++ {
s[i].Add(&s[i], &sums[i%4])
}
}
func internalLinearLayer(state *[WIDTH]g.Element) {
var sum g.Element
sum.Set(&state[0])
for i := 1; i < WIDTH; i++ {
sum.Add(&sum, &state[i])
}
for i := 0; i < WIDTH; i++ {
state[i].Mul(&state[i], &MATRIX_DIAG_12_U64[i]).
Add(&state[i], &sum)
}
}
func addRC(state *[WIDTH]g.Element, externalRound int) {
for i := 0; i < WIDTH; i++ {
state[i].Add(&state[i], &EXTERNAL_CONSTANTS[externalRound][i])
}
}
func addRCI(state *[WIDTH]g.Element, round int) {
state[0].Add(&state[0], &INTERNAL_CONSTANTS[round])
}
func sbox(state *[WIDTH]g.Element) {
for i := range state {
sboxP(i, state)
}
}
func sboxP(index int, state *[WIDTH]g.Element) {
var tmp g.Element
tmp.Set(&state[index])
var tmpSquare g.Element
tmpSquare.Square(&tmp)
var tmpSixth g.Element
tmpSixth.Mul(&tmpSquare, &tmp)
tmpSixth.Square(&tmpSixth)
state[index].Mul(&tmpSixth, &tmp)
}
const BlockSize = g.Bytes // BlockSize size that poseidon consumes
type digest struct {
data []g.Element
}
func NewPoseidon2() hash.Hash {
d := new(digest)
d.Reset()
return d
}
// Reset resets the Hash to its initial state.
func (d *digest) Reset() {
d.data = nil
}
// Get element by element.
func (d *digest) Write(p []byte) (n int, err error) {
if len(p)%g.Bytes != 0 {
return 0, fmt.Errorf("input bytes len should be multiple of 8 but is %d", len(p))
}
gArr, err := g.ArrayFromCanonicalLittleEndianBytes(p)
if err != nil {
return 0, fmt.Errorf("failed to convert bytes to field element. bytes: %v, error: %w", p, err)
}
d.data = append(d.data, gArr...)
return len(p), nil
}
func (d *digest) Size() int {
return BlockSize
}
// BlockSize returns the number of bytes Sum will return.
func (d *digest) BlockSize() int {
return BlockSize
}
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (d *digest) Sum(b []byte) []byte {
b = append(b, HashNToHashNoPad(d.data).ToLittleEndianBytes()...)
d.data = nil
return b
}
@@ -0,0 +1,279 @@
package poseidon2
import (
"math"
"testing"
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
)
func TestPermute(t *testing.T) {
inp := [WIDTH]g.Element{
g.FromUint64(5417613058500526590),
g.FromUint64(2481548824842427254),
g.FromUint64(6473243198879784792),
g.FromUint64(1720313757066167274),
g.FromUint64(2806320291675974571),
g.FromUint64(7407976414706455446),
g.FromUint64(1105257841424046885),
g.FromUint64(7613435757403328049),
g.FromUint64(3376066686066811538),
g.FromUint64(5888575799323675710),
g.FromUint64(6689309723188675948),
g.FromUint64(2468250420241012720),
}
Permute(&inp)
expected := [WIDTH]g.Element{
g.FromUint64(5364184781011389007),
g.FromUint64(15309475861242939136),
g.FromUint64(5983386513087443499),
g.FromUint64(886942118604446276),
g.FromUint64(14903657885227062600),
g.FromUint64(7742650891575941298),
g.FromUint64(1962182278500985790),
g.FromUint64(10213480816595178755),
g.FromUint64(3510799061817443836),
g.FromUint64(4610029967627506430),
g.FromUint64(7566382334276534836),
g.FromUint64(2288460879362380348),
}
for i := 0; i < WIDTH; i++ {
if inp[i] != expected[i] {
t.Logf("Expected: %d, got: %d\n", expected[i], inp[i])
t.Fail()
}
}
}
func TestHashNToMNoPad(t *testing.T) {
inp := [WIDTH]g.Element{
g.FromUint64(2963773914414780088),
g.FromUint64(8389525300242074234),
g.FromUint64(3700959901615818008),
g.FromUint64(6116199383751757212),
g.FromUint64(3418607418699599889),
g.FromUint64(8793277256263635044),
g.FromUint64(448623437464918480),
g.FromUint64(1857310021116627925),
g.FromUint64(6145634616307237342),
g.FromUint64(1548353948794474539),
g.FromUint64(2318110128254703527),
g.FromUint64(8347759953730634762),
}
res := HashNToMNoPad(inp[:], 12)
expected := [WIDTH]g.Element{
g.FromUint64(3627923032009111551),
g.FromUint64(1460752551327577353),
g.FromUint64(1084214837491058067),
g.FromUint64(1841622875286057462),
g.FromUint64(3996252440506437984),
g.FromUint64(1276718204392552803),
g.FromUint64(8564515621134952155),
g.FromUint64(9252927025993202701),
g.FromUint64(1147435538714642916),
g.FromUint64(16407277821156164797),
g.FromUint64(11997661877740155273),
g.FromUint64(12485021000320141292),
}
for i := 0; i < 12; i++ {
if res[i] != expected[i] {
t.Fail()
}
}
}
func TestDigest(t *testing.T) {
hFunc := NewPoseidon2()
inputs := make([][]byte, 2)
inputs[0] = make([]byte, 8)
inputs[0][0] = 1
inputs[0][1] = 2
inputs[0][2] = 3
inputs[0][3] = 4
inputs[0][4] = 5
inputs[0][5] = 6
inputs[0][6] = 7
inputs[0][7] = 0
inputs[1] = make([]byte, 8)
inputs[1][0] = 7
inputs[1][1] = 6
inputs[1][2] = 5
inputs[1][3] = 4
inputs[1][4] = 3
inputs[1][5] = 2
inputs[1][6] = 1
inputs[1][7] = 0
g1, _ := g.FromCanonicalLittleEndianBytes(inputs[0]) // 289077004332300282
g2, _ := g.FromCanonicalLittleEndianBytes(inputs[1]) // 289644378102298614
hFunc.Write(inputs[0])
hFunc.Write(inputs[1])
hash := hFunc.Sum(nil)
hash2Elems := HashNoPad([]g.Element{*g1, *g2})
hash2 := hash2Elems.ToLittleEndianBytes()
for i := 0; i < len(hash); i++ {
if hash[i] != hash2[i] {
t.Fail()
}
}
reconstructed, _ := HashOutFromLittleEndianBytes(hash)
for i := 0; i < 4; i++ {
if hash2Elems[i] != reconstructed[i] {
t.Fail()
}
}
}
func TestHashNToHashNoPad(t *testing.T) {
res := HashNToHashNoPad([]g.Element{
g.FromUint64(11295517158488612626),
g.FromUint64(10669470463693797151),
g.FromUint64(17232114065640264171),
g.FromUint64(4175927072186299193),
g.FromUint64(13985285184240204531),
g.FromUint64(7901017084268693144),
g.FromUint64(4326299618263946178),
g.FromUint64(14787024750292535041),
g.FromUint64(894520636503353046),
g.FromUint64(12556655399058578835),
g.FromUint64(3097737892474696200),
g.FromUint64(7515335668060050861),
})
expected := HashOut{
g.FromUint64(15396602476382546759),
g.FromUint64(12422280135166335470),
g.FromUint64(8165681190607828974),
g.FromUint64(3475588160239961712),
}
for i := 0; i < 4; i++ {
if res[i] != expected[i] {
t.Fail()
}
}
}
func TestHashNToHashNoPadLarge(t *testing.T) {
res := HashNToHashNoPad([]g.Element{
g.FromUint64(g.ORDER + 1),
g.FromUint64(g.ORDER + 2),
g.FromUint64(g.ORDER + 3),
g.FromUint64(math.MaxUint64),
g.FromUint64(math.MaxUint64 - 1),
})
expected := HashOut{
g.FromUint64(14216040864787980138),
g.FromUint64(17275303675000904868),
g.FromUint64(11831395338463193314),
g.FromUint64(281267649235863375),
}
for i := 0; i < 4; i++ {
if res[i] != expected[i] {
t.Logf("Expected: [%v], got: [%v]\n", g.ToString(expected[:]...), g.ToString(res[:]...))
t.FailNow()
}
}
}
func TestHashTwoToOne(t *testing.T) {
input1 := HashOut{
g.FromUint64(3777312593917610528),
g.FromUint64(6858608920877200812),
g.FromUint64(5269611035257552853),
g.FromUint64(10607733449481270434),
}
input2 := HashOut{
g.FromUint64(10355703322562521155),
g.FromUint64(1039917189921776884),
g.FromUint64(10844249567941924238),
g.FromUint64(14291130953945924124),
}
expected := HashOut{
g.FromUint64(1453933811752520343),
g.FromUint64(16186418140372484281),
g.FromUint64(9207215809524681813),
g.FromUint64(10182182911172027974),
}
res := HashTwoToOne(input1, input2)
for i := 0; i < 4; i++ {
if res[i] != expected[i] {
t.Fail()
}
}
}
func TestHashNToOne(t *testing.T) {
hashIns := []HashOut{HashNToHashNoPad([]g.Element{
g.FromUint64(18231458557829081414),
g.FromUint64(16449039301999856654),
g.FromUint64(14758090268883299362),
g.FromUint64(10271725147130672875),
g.FromUint64(6253304685402495037),
g.FromUint64(16079709420464120062),
g.FromUint64(10838593640248082543),
g.FromUint64(2974225335734585509),
g.FromUint64(6365466669981419503),
g.FromUint64(12964544245312854826),
g.FromUint64(3161534615047618958),
g.FromUint64(15109271288782125222),
})}
for i := 1; i < 12; i++ {
hashIns = append(hashIns, HashTwoToOne(hashIns[i-1], hashIns[i-1]))
}
res := HashNToOne(hashIns)
expected := HashOut{
g.FromUint64(3346041518891302234),
g.FromUint64(10181430332820953144),
g.FromUint64(14852547783810217847),
g.FromUint64(17043509806476508794),
}
for i := 0; i < 4; i++ {
if res[i] != expected[i] {
t.Fail()
}
}
}
func TestHashToQuinticExtension(t *testing.T) {
result := HashToQuinticExtension([]g.Element{
g.FromUint64(3451004116618606032),
g.FromUint64(11263134342958518251),
g.FromUint64(10957204882857370932),
g.FromUint64(5369763041201481933),
g.FromUint64(7695734348563036858),
g.FromUint64(1393419330378128434),
g.FromUint64(7387917082382606332),
})
expected := [5]uint64{
17992684813643984528,
5243896189906434327,
7705560276311184368,
2785244775876017560,
14449776097783372302,
}
for i := 0; i < 5; i++ {
if result[i] != g.FromUint64(expected[i]) {
t.Logf("Expected limb %d to be %x, but got %x", i, expected[i], result[i])
t.Fail()
}
}
}