arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/poseidon/bn254.go
poseidon/bn254.goRaw
  1package poseidon
  2
  3// This is a customized implementation of the Poseidon hash function inside the BN254 field.
  4// This implementation is based on the following implementation:
  5//
  6// 		https://github.com/iden3/go-iden3-crypto/blob/master/poseidon/poseidon.go
  7//
  8// The input and output are modified to ingest Goldilocks field elements.
  9
 10import (
 11	"math/big"
 12
 13	"github.com/consensys/gnark-crypto/ecc/bn254"
 14	"github.com/consensys/gnark/frontend"
 15	gl "github.com/succinctlabs/gnark-plonky2-verifier/goldilocks"
 16)
 17
 18const BN254_FULL_ROUNDS int = 8
 19const BN254_PARTIAL_ROUNDS int = 56
 20const BN254_SPONGE_WIDTH int = 4
 21const BN254_SPONGE_RATE int = 3
 22
 23type BN254Chip struct {
 24	api frontend.API `gnark:"-"`
 25	gl  *gl.Chip     `gnark:"-"`
 26}
 27
 28type BN254State = [BN254_SPONGE_WIDTH]frontend.Variable
 29type BN254HashOut = frontend.Variable
 30
 31func NewBN254Chip(api frontend.API) *BN254Chip {
 32	if api.Compiler().Field().Cmp(bn254.ID.ScalarField()) != 0 {
 33		panic("Gnark compiler not set to BN254 scalar field")
 34	}
 35
 36	return &BN254Chip{api: api, gl: gl.New(api)}
 37}
 38
 39func (c *BN254Chip) Poseidon(state BN254State) BN254State {
 40	state = c.ark(state, 0)
 41	state = c.fullRounds(state, true)
 42	state = c.partialRounds(state)
 43	state = c.fullRounds(state, false)
 44	return state
 45}
 46
 47func (c *BN254Chip) HashNoPad(input []gl.Variable) BN254HashOut {
 48	state := BN254State{
 49		frontend.Variable(0),
 50		frontend.Variable(0),
 51		frontend.Variable(0),
 52		frontend.Variable(0),
 53	}
 54
 55	two_to_32 := new(big.Int).SetInt64(1 << 32)
 56	two_to_64 := new(big.Int).Mul(two_to_32, two_to_32)
 57
 58	for i := 0; i < len(input); i += BN254_SPONGE_RATE * 3 {
 59		endI := c.min(len(input), i+BN254_SPONGE_RATE*3)
 60		rateChunk := input[i:endI]
 61		for j, stateIdx := 0, 0; j < len(rateChunk); j, stateIdx = j+3, stateIdx+1 {
 62			endJ := c.min(len(rateChunk), j+3)
 63			bn254Chunk := rateChunk[j:endJ]
 64
 65			inter := frontend.Variable(0)
 66			for k := 0; k < len(bn254Chunk); k++ {
 67				inter = c.api.MulAcc(inter, bn254Chunk[k].Limb, new(big.Int).Exp(two_to_64, big.NewInt(int64(k)), nil))
 68			}
 69
 70			state[stateIdx+1] = inter
 71		}
 72
 73		state = c.Poseidon(state)
 74	}
 75
 76	return BN254HashOut(state[0])
 77}
 78
 79func (c *BN254Chip) HashOrNoop(input []gl.Variable) BN254HashOut {
 80	if len(input) <= 3 {
 81		returnVal := frontend.Variable(0)
 82
 83		alpha := new(big.Int).SetInt64(1 << 32)
 84		alpha = new(big.Int).Mul(alpha, alpha)
 85		for i, inputElement := range input {
 86			mulFactor := new(big.Int).Exp(alpha, big.NewInt(int64(i)), nil)
 87			returnVal = c.api.MulAcc(returnVal, inputElement.Limb, mulFactor)
 88		}
 89
 90		return BN254HashOut(returnVal)
 91	} else {
 92		return c.HashNoPad(input)
 93	}
 94}
 95
 96func (c *BN254Chip) TwoToOne(left BN254HashOut, right BN254HashOut) BN254HashOut {
 97	var inputs BN254State
 98	inputs[0] = frontend.Variable(0)
 99	inputs[1] = frontend.Variable(0)
100	inputs[2] = left
101	inputs[3] = right
102	state := c.Poseidon(inputs)
103	return state[0]
104}
105
106func (c *BN254Chip) ToVec(hash BN254HashOut) []gl.Variable {
107	bits := c.api.ToBinary(hash)
108
109	returnElements := []gl.Variable{}
110
111	// Split into 7 byte chunks, since 8 byte chunks can result in collisions
112	chunkSize := 56
113	for i := 0; i < len(bits); i += chunkSize {
114		maxIdx := c.min(len(bits), i+chunkSize)
115		bitChunk := bits[i:maxIdx]
116		returnElements = append(returnElements, gl.NewVariable(c.api.FromBinary(bitChunk...)))
117	}
118
119	return returnElements
120}
121
122func (c *BN254Chip) min(x, y int) int {
123	if x < y {
124		return x
125	}
126
127	return y
128}
129
130func (c *BN254Chip) fullRounds(state BN254State, isFirst bool) BN254State {
131	for i := 0; i < BN254_FULL_ROUNDS/2-1; i++ {
132		state = c.exp5state(state)
133		if isFirst {
134			state = c.ark(state, (i+1)*BN254_SPONGE_WIDTH)
135		} else {
136			state = c.ark(state, (BN254_FULL_ROUNDS/2+1)*BN254_SPONGE_WIDTH+BN254_PARTIAL_ROUNDS+i*BN254_SPONGE_WIDTH)
137		}
138		state = c.mix(state, mMatrix)
139	}
140
141	state = c.exp5state(state)
142	if isFirst {
143		state = c.ark(state, (BN254_FULL_ROUNDS/2)*BN254_SPONGE_WIDTH)
144		state = c.mix(state, pMatrix)
145	} else {
146		state = c.mix(state, mMatrix)
147	}
148
149	return state
150}
151
152func (c *BN254Chip) partialRounds(state BN254State) BN254State {
153	for i := 0; i < BN254_PARTIAL_ROUNDS; i++ {
154		state[0] = c.exp5(state[0])
155		state[0] = c.api.Add(state[0], cConstants[(BN254_FULL_ROUNDS/2+1)*BN254_SPONGE_WIDTH+i])
156
157		newState0 := frontend.Variable(0)
158		for j := 0; j < BN254_SPONGE_WIDTH; j++ {
159			newState0 = c.api.MulAcc(newState0, sConstants[(BN254_SPONGE_WIDTH*2-1)*i+j], state[j])
160		}
161
162		for k := 1; k < BN254_SPONGE_WIDTH; k++ {
163			state[k] = c.api.MulAcc(state[k], state[0], sConstants[(BN254_SPONGE_WIDTH*2-1)*i+BN254_SPONGE_WIDTH+k-1])
164		}
165		state[0] = newState0
166	}
167
168	return state
169}
170
171func (c *BN254Chip) ark(state BN254State, it int) BN254State {
172	var result BN254State
173
174	for i := 0; i < len(state); i++ {
175		result[i] = c.api.Add(state[i], cConstants[it+i])
176	}
177
178	return result
179}
180
181func (c *BN254Chip) exp5(x frontend.Variable) frontend.Variable {
182	x2 := c.api.Mul(x, x)
183	x4 := c.api.Mul(x2, x2)
184	return c.api.Mul(x4, x)
185}
186
187func (c *BN254Chip) exp5state(state BN254State) BN254State {
188	for i := 0; i < BN254_SPONGE_WIDTH; i++ {
189		state[i] = c.exp5(state[i])
190	}
191	return state
192}
193
194func (c *BN254Chip) mix(state_ BN254State, constantMatrix [][]*big.Int) BN254State {
195	var result BN254State
196
197	for i := 0; i < BN254_SPONGE_WIDTH; i++ {
198		result[i] = frontend.Variable(0)
199	}
200
201	for i := 0; i < BN254_SPONGE_WIDTH; i++ {
202		for j := 0; j < BN254_SPONGE_WIDTH; j++ {
203			result[i] = c.api.MulAcc(result[i], constantMatrix[j][i], state_[j])
204		}
205	}
206
207	return result
208}