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,67 @@
package poseidon_bn254
import (
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"github.com/elliottech/poseidon_crypto/hash/poseidon_bn254/constants"
)
// Number of full rounds
const rf = 8
var alpha = big.NewInt(5)
// Number of partial rounds rounded up to nearest integer that divides by t in [2, 13]
var rp = []int{56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68}
// Round constants and matrices
var (
c, s [][]*fr.Element
m, p [][][]*fr.Element
)
func toElement(value string) *fr.Element {
n, success := new(big.Int).SetString(value, 16)
if !success {
panic("Error parsing hex number")
}
e := fr.Element{0, 0, 0, 0}
e.SetBigInt(n)
return &e
}
func init() {
var size = len(rp)
c = make([][]*fr.Element, size)
s = make([][]*fr.Element, size)
m = make([][][]*fr.Element, size)
p = make([][][]*fr.Element, size)
for i := 0; i < size; i++ {
// initialize round constants and matrices
c[i] = make([]*fr.Element, len(constants.CStr[i]))
s[i] = make([]*fr.Element, len(constants.SStr[i]))
m[i] = make([][]*fr.Element, len(constants.MStr[i]))
p[i] = make([][]*fr.Element, len(constants.PStr[i]))
for j := 0; j < len(c[i]); j++ {
c[i][j] = toElement(constants.CStr[i][j])
}
for j := 0; j < len(s[i]); j++ {
s[i][j] = toElement(constants.SStr[i][j])
}
for j := 0; j < len(m[i]); j++ {
m[i][j] = make([]*fr.Element, len(constants.MStr[i][j]))
for k := 0; k < len(m[i][j]); k++ {
m[i][j][k] = toElement(constants.MStr[i][j][k])
}
}
for j := 0; j < len(p[i]); j++ {
p[i][j] = make([]*fr.Element, len(constants.PStr[i][j]))
for k := 0; k < len(p[i][j]); k++ {
p[i][j][k] = toElement(constants.PStr[i][j][k])
}
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,208 @@
package poseidon_bn254
import (
"errors"
"hash"
"math/big"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
)
const (
BlockSize = fr.Bytes // BlockSize size that poseidon consumes
)
func zeroElement() *fr.Element {
return &fr.Element{0, 0, 0, 0}
}
func deepCopy(dst, src []*fr.Element) {
if len(src) > len(dst) {
panic("Cannot copy to a smaller destination")
}
for i := 0; i < len(src); i++ {
v := *src[i]
dst[i] = &v
}
}
// Add round constants
func arc(state []*fr.Element, C []*fr.Element, t, offset int) {
for i := 0; i < t; i++ {
state[i].Add(state[i], C[offset+i])
}
}
// power 5 as s-box for full state
func sbox(state []*fr.Element, t int) {
for i := 0; i < t; i++ {
state[i].Exp(*state[i], alpha)
}
}
// Matrix vector multiplication
func mix(state []*fr.Element, M [][]*fr.Element, t int) []*fr.Element {
newState := make([]*fr.Element, t)
for i := 0; i < t; i++ {
newState[i] = zeroElement()
for j := 0; j < t; j++ {
newState[i].Add(newState[i], zeroElement().Mul(M[j][i], state[j]))
}
}
return newState
}
func permutation(state []*fr.Element) []*fr.Element {
// Minimum length of state = nInput + nOutput = 2
t := len(state)
index := t - 2
RP := rp[index]
C := c[index]
M := m[index]
S := s[index]
P := p[index]
// 1. Pre-step to the first-half of full rounds: add round constant for round=0
arc(state, C, t, 0)
// 2. First-half of full rounds starting at roundNumber = 1 except last round
for i := 0; i < rf/2-1; i++ {
sbox(state, t)
arc(state, C, t, (i+1)*t)
state = mix(state, M, t)
}
// 3. Last round of first-half of full rounds
sbox(state, t)
arc(state, C, t, (rf/2)*t)
state = mix(state, P, t)
// 4. Partial rounds
for i := 0; i < RP; i++ {
state[0].Exp(*state[0], alpha)
state[0].Add(state[0], C[(rf/2+1)*t+i])
// S[i] is a vector of [t*2-1] elements where first t elements are used to compute state[0]
// and the remaining elements starting at [t] are used to compute state[1,..,t-1]
offset := (t*2 - 1) * i
newState0 := zeroElement()
for j := 0; j < len(state); j++ {
newState0.Add(newState0, zeroElement().Mul(state[j], S[offset+j]))
}
offset += t - 1
for k := 1; k < t; k++ {
state[k].Add(state[k], zeroElement().Mul(state[0], S[offset+k]))
}
state[0] = newState0
}
// 5. Second-half of full rounds except last round
for i := 0; i < rf/2-1; i++ {
sbox(state, t)
arc(state, C, t, (rf/2+1)*t+RP+i*t)
state = mix(state, M, t)
}
// 6. Last round of the second-half of full rounds
sbox(state, t)
state = mix(state, M, t)
return state
}
func Poseidon(input ...*fr.Element) *fr.Element {
inputLength := len(input)
if inputLength == 0 {
panic("No support for dummy input")
}
const maxLength = 16
state := make([]*fr.Element, maxLength+1)
state[0] = zeroElement()
startIndex := 0
lastIndex := 0
// Make a hash chain of the input if its length > maxLength
if inputLength > maxLength {
count := inputLength / maxLength
for i := 0; i < count; i++ {
lastIndex = (i + 1) * maxLength
deepCopy(state[1:], input[startIndex:lastIndex])
state = permutation(state)
startIndex = lastIndex
}
}
// For the remaining part of the input OR if 1 <= inputLength <= 16
if lastIndex < inputLength {
lastIndex = inputLength
remainigLength := lastIndex - startIndex
deepCopy(state[1:], input[startIndex:lastIndex])
state = permutation(state[:remainigLength+1])
}
// Return capacity element 1
return state[1]
}
func PoseidonBytes(input ...[]byte) []byte {
inputElements := make([]*fr.Element, len(input))
for i, ele := range input {
num := new(big.Int).SetBytes(ele)
if num.Cmp(fr.Modulus()) >= 0 {
panic("not support bytes bigger than modulus")
}
e := fr.Element{0, 0, 0, 0}
e.SetBigInt(num)
inputElements[i] = &e
}
res := Poseidon(inputElements...).Bytes()
return res[:]
}
type digest struct {
h fr.Element
data [][]byte // data to hash
}
func NewPoseidon() hash.Hash {
d := new(digest)
d.Reset()
return d
}
// Reset resets the Hash to its initial state.
func (d *digest) Reset() {
d.data = nil
d.h = fr.Element{0, 0, 0, 0}
}
// Only receive byte slice less than fr.Modulus()
func (d *digest) Write(p []byte) (n int, err error) {
n = len(p)
num := new(big.Int).SetBytes(p)
if num.Cmp(fr.Modulus()) >= 0 {
return 0, errors.New("not support bytes bigger than modulus")
}
d.data = append(d.data, p)
return n, 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 {
e := fr.Element{0, 0, 0, 0}
e.SetBigInt(new(big.Int).SetBytes(PoseidonBytes(d.data...)))
d.h = e
d.data = nil // flush the data already hashed
hash := d.h.Bytes()
b = append(b, hash[:]...)
return b
}
@@ -0,0 +1,184 @@
package poseidon_bn254_test
import (
"math/big"
"testing"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
"github.com/elliottech/poseidon_crypto/hash/poseidon_bn254"
"github.com/stretchr/testify/assert"
)
func elementFromString(v string) *fr.Element {
n, success := new(big.Int).SetString(v, 10)
if !success {
panic("Error parsing hex number")
}
var e fr.Element
e.SetBigInt(n)
return &e
}
func elementFromStringHex(v string) *fr.Element {
n, success := new(big.Int).SetString(v, 16)
if !success {
panic("Error parsing hex number")
}
var e fr.Element
e.SetBigInt(n)
return &e
}
func TestPoseidon1(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("7764075183688725171230668857402392634761334547267776368103645048439717572548")
length := 1
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon2(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("7142104613055408817911962100316808866448378443474503659992478482890339429929")
length := 2
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon4(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("7817711165059374331357136443537800893307845083525445872661165200086166013245")
length := 4
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon13(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("1709610050961943784828399921362905178787999827108026634048665681910636069934")
length := 13
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon16(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("8319791455060392555425392842391403897548969645190976863995973180967774875286")
length := 16
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon24(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("14281896993318141900551144554156181598834585543901557749703302979893059224887")
length := 24
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon30(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("3706864405066113783363062549980271879113588784557216652303342540436728346372")
length := 30
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestPoseidon256(t *testing.T) {
// WARNING: No test vector to compare with
expectedHash := elementFromString("3889232958018785041730045800798978544000060048890444628344970190264245196615")
length := 256
inputs := make([]*fr.Element, length)
for i := 0; i < length; i++ {
e := fr.NewElement((uint64)(i + 1))
inputs[i] = &e
}
actualHash := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash.Equal(expectedHash), "%s != %s", actualHash, expectedHash)
}
func TestConsistency(t *testing.T) {
// Check whether Poseidon returns the same value for the same input
// Test vector https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
inputsStr := []string{"1", "2", "3", "4"}
inputs := make([]*fr.Element, len(inputsStr))
for i := 0; i < len(inputsStr); i++ {
inputs[i] = elementFromString(inputsStr[i])
}
actualHash1 := poseidon_bn254.Poseidon(inputs...)
actualHash2 := poseidon_bn254.Poseidon(inputs...)
assert.True(t, actualHash1.Equal(actualHash2), "%s != %s", actualHash1, actualHash2)
}
func TestPoseidonBytes(t *testing.T) {
// Test vector https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/test_vectors.txt
expectedHash := elementFromStringHex("FCA49B798923AB0239DE1C9E7A4A9A2210312B6A2F616D18B5A87F9B628AE29")
inputs := make([][]byte, 2)
inputs[0] = make([]byte, 1)
inputs[0][0] = 1
inputs[1] = make([]byte, 1)
inputs[1][0] = 2
actualHash := poseidon_bn254.PoseidonBytes(inputs...)
actualHashEle := fr.Element{0, 0, 0, 0}
actualHashEle.SetBytes(actualHash)
assert.True(t, actualHashEle.Equal(expectedHash), "%s != %s", actualHashEle, expectedHash)
}
func TestDigest(t *testing.T) {
expectedHash := elementFromStringHex("FCA49B798923AB0239DE1C9E7A4A9A2210312B6A2F616D18B5A87F9B628AE29")
hFunc := poseidon_bn254.NewPoseidon()
inputs := make([][]byte, 2)
inputs[0] = make([]byte, 1)
inputs[0][0] = 1
inputs[1] = make([]byte, 1)
inputs[1][0] = 2
hFunc.Write(inputs[0])
hFunc.Write(inputs[1])
actualHash := hFunc.Sum(nil)
actualHashEle := fr.Element{0, 0, 0, 0}
actualHashEle.SetBytes(actualHash)
assert.True(t, actualHashEle.Equal(expectedHash), "%s != %s", actualHashEle, expectedHash)
hFunc.Reset()
bigNumber, _ := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
inputs[0] = bigNumber.Bytes()
n, err := hFunc.Write(inputs[0])
assert.EqualError(t, err, "not support bytes bigger than modulus")
assert.Equal(t, n, 0)
}