mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 01:08:07 +00:00
feat: 添加 Lighter 适配器及相关功能,支持 trailing stops 和新的交易逻辑
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+285
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
poseidon2_gnark "github.com/elliottech/poseidon_crypto/hash/poseidon2_goldilocks"
|
||||
poseidon2_plonky2 "github.com/elliottech/poseidon_crypto/hash/poseidon2_goldilocks_plonky2"
|
||||
)
|
||||
|
||||
func TestLongRunningCompare(t *testing.T) {
|
||||
run := os.Getenv("LONG_RUNNING_TESTS")
|
||||
if run != "true" {
|
||||
t.Skip("Skipping long running test")
|
||||
}
|
||||
|
||||
file := os.Getenv("LONG_RUNNING_TESTS_FILE")
|
||||
|
||||
// Generate random 12 inputs
|
||||
for j := 0; j < 1_000_000_000; j++ {
|
||||
inputs := make([]uint64, 12)
|
||||
for i := 0; i < 12; i++ {
|
||||
inputs[i] = rand.Uint64N(g.ORDER)
|
||||
}
|
||||
|
||||
// Convert to GoldilocksField
|
||||
gInputs := make([]g.GoldilocksField, 0, 12)
|
||||
for _, input := range inputs {
|
||||
gInputs = append(gInputs, g.GoldilocksField(input))
|
||||
}
|
||||
gOutputs := poseidon2_plonky2.HashNToMNoPad(gInputs, 12)
|
||||
|
||||
// Convert to Element
|
||||
eInputs := make([]g.Element, 0, 12)
|
||||
for _, input := range inputs {
|
||||
eInputs = append(eInputs, g.NewElement(input))
|
||||
}
|
||||
eOutputs := poseidon2_gnark.HashNToMNoPad(eInputs, 12)
|
||||
|
||||
// Compare
|
||||
for i := 0; i < 12; i++ {
|
||||
if gOutputs[i].ToCanonicalUint64() != eOutputs[i].Uint64() {
|
||||
if file != "" {
|
||||
f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString("<--- Mismatch --->\n")
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
_, err = f.WriteString(fmt.Sprintf("Inputs: %v; %v; [%v]\n", inputs, gInputs, g.ToString(eInputs...)))
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
_, err = f.WriteString(fmt.Sprintf("Outputs: %v; [%v]\n\n", gOutputs, g.ToString(eOutputs...)))
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
f.Close()
|
||||
} else {
|
||||
t.Log("<--- Mismatch --->")
|
||||
t.Logf("Inputs: %v; %v; [%v]", inputs, gInputs, g.ToString(eInputs...))
|
||||
t.Logf("Outputs: %v; [%v]", gOutputs, g.ToString(eOutputs...))
|
||||
t.FailNow()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.Log("Completed long-running comparison.")
|
||||
}
|
||||
|
||||
func TestPoseidon2Bench(t *testing.T) {
|
||||
inputs, err := readBenchInputs("bench_vector")
|
||||
totalInputs := len(inputs)
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
PrintMemUsage()
|
||||
|
||||
results := make([]g.GoldilocksField, 0, 4*len(inputs))
|
||||
start := time.Now()
|
||||
for _, input := range inputs {
|
||||
res := poseidon2_plonky2.HashNToHashNoPad(input)
|
||||
results = append(results, res[:]...)
|
||||
}
|
||||
duration := time.Since(start)
|
||||
t.Logf("HashNToHashNoPad plonky2 took %s for %d inputs", duration, totalInputs)
|
||||
|
||||
sha2 := sha256.New()
|
||||
for _, res := range results {
|
||||
sha2.Write(g.ToLittleEndianBytesF(res))
|
||||
}
|
||||
t.Logf("Hash: %x\n", sha2.Sum(nil))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoseidon2HasherBench(t *testing.T) {
|
||||
inputs, err := readBenchInputsBytes("bench_vector")
|
||||
totalInputs := len(inputs)
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
hasher := poseidon2_plonky2.NewPoseidon2()
|
||||
start1 := time.Now()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
PrintMemUsage()
|
||||
|
||||
results := make([]byte, 0, 4*8*len(inputs))
|
||||
start := time.Now()
|
||||
for _, input := range inputs {
|
||||
for _, b := range input {
|
||||
hasher.Write(b)
|
||||
}
|
||||
res := hasher.Sum(nil)
|
||||
hasher.Reset()
|
||||
results = append(results, res...)
|
||||
}
|
||||
duration := time.Since(start)
|
||||
t.Logf("Hasher plonky2 took %s for %d inputs", duration, totalInputs)
|
||||
|
||||
sha2 := sha256.New()
|
||||
sha2.Write(results)
|
||||
t.Logf("Hash: %x\n", sha2.Sum(nil))
|
||||
}
|
||||
|
||||
duration := time.Since(start1)
|
||||
t.Logf("===> Hasher plonky2 took %s", duration)
|
||||
}
|
||||
|
||||
func TestPoseidon2BenchOld(t *testing.T) {
|
||||
inputs, err := readBenchInputsOld("bench_vector")
|
||||
totalInputs := len(inputs)
|
||||
if err != nil {
|
||||
t.Logf("Error: %v\n", err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
results := make([]g.Element, 0, 4*len(inputs))
|
||||
start := time.Now()
|
||||
for _, input := range inputs {
|
||||
res := poseidon2_gnark.HashNToHashNoPad(input)
|
||||
results = append(results, res[:]...)
|
||||
}
|
||||
duration := time.Since(start)
|
||||
t.Logf("HashNToHashNoPadPure gnark took %s for %d inputs", duration, totalInputs)
|
||||
|
||||
sha2 := sha256.New()
|
||||
for _, res := range results {
|
||||
sha2.Write(g.ToLittleEndianBytes(res))
|
||||
}
|
||||
hash := sha2.Sum(nil)
|
||||
t.Logf("Hash: %x\n", hash)
|
||||
}
|
||||
|
||||
func readBenchInputs(filename string) ([][]g.GoldilocksField, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
var inputs [][]g.GoldilocksField
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
strVals := strings.Split(line, ",")
|
||||
var input []g.GoldilocksField
|
||||
for _, strVal := range strVals {
|
||||
val, err := strconv.ParseUint(strVal, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse uint64: %v", err)
|
||||
}
|
||||
input = append(input, g.GoldilocksField(val))
|
||||
}
|
||||
inputs = append(inputs, input)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %v", err)
|
||||
}
|
||||
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func readBenchInputsBytes(filename string) ([][][]byte, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
var inputs [][][]byte
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
strVals := strings.Split(line, ",")
|
||||
var input [][]byte
|
||||
for _, strVal := range strVals {
|
||||
val, err := strconv.ParseUint(strVal, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse uint64: %v", err)
|
||||
}
|
||||
input = append(input, g.ToLittleEndianBytesF(g.GoldilocksField(val)))
|
||||
}
|
||||
inputs = append(inputs, input)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %v", err)
|
||||
}
|
||||
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func readBenchInputsOld(filename string) ([][]g.Element, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
var inputs [][]g.Element
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
strVals := strings.Split(line, ",")
|
||||
var input []g.Element
|
||||
for _, strVal := range strVals {
|
||||
val, err := strconv.ParseUint(strVal, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse uint64: %v", err)
|
||||
}
|
||||
input = append(input, g.NewElement(val))
|
||||
}
|
||||
inputs = append(inputs, input)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %v", err)
|
||||
}
|
||||
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func PrintMemUsage() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
|
||||
fmt.Printf("Alloc = %v Bytes", m.Alloc)
|
||||
fmt.Printf("\tTotalAlloc = %v Bytes", m.TotalAlloc)
|
||||
fmt.Printf("\tSys = %v Bytes", m.Sys)
|
||||
fmt.Printf("\tNumGC = %v\n", m.NumGC)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user