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