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