arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/poseidon/poseidon.go
poseidon/poseidon.goRaw
  1package poseidon
  2
  3import (
  4	"github.com/consensys/gnark/frontend"
  5	"github.com/succinctlabs/gnark-plonky2-verifier/field"
  6)
  7
  8const HALF_N_FULL_ROUNDS = 4
  9const N_PARTIAL_ROUNDS = 22
 10const MAX_WIDTH = 12
 11const SPONGE_WIDTH = 12
 12const SPONGE_RATE = 8
 13
 14type PoseidonState = [SPONGE_WIDTH]frontend.Variable
 15type PoseidonStateExtension = [SPONGE_WIDTH]field.QuadraticExtension
 16type PoseidonHashOut = [4]field.F
 17
 18type PoseidonChip struct {
 19	api      frontend.API                 `gnark:"-"`
 20	fieldAPI field.FieldAPI               `gnark:"-"`
 21	qeAPI    *field.QuadraticExtensionAPI `gnark:"-"`
 22}
 23
 24func NewPoseidonChip(api frontend.API, fieldAPI field.FieldAPI, qeAPI *field.QuadraticExtensionAPI) *PoseidonChip {
 25	return &PoseidonChip{api: api, fieldAPI: fieldAPI, qeAPI: qeAPI}
 26}
 27
 28// The permutation function.
 29// The input state MUST have all it's elements be within Goldilocks field (e.g. this function will not reduce the input elements).
 30// The returned state's elements will all be within Goldilocks field.
 31func (c *PoseidonChip) Poseidon(input PoseidonState) PoseidonState {
 32	state := input
 33	roundCounter := 0
 34	state = c.fullRounds(state, &roundCounter)
 35	state = c.partialRounds(state, &roundCounter)
 36	state = c.fullRounds(state, &roundCounter)
 37	return state
 38}
 39
 40// The input elements MUST have all it's elements be within Goldilocks field.
 41// The returned slice's elements will all be within Goldilocks field.
 42func (c *PoseidonChip) HashNToMNoPad(input []frontend.Variable, nbOutputs int) []frontend.Variable {
 43	var state PoseidonState
 44
 45	for i := 0; i < SPONGE_WIDTH; i++ {
 46		state[i] = frontend.Variable(0)
 47	}
 48
 49	for i := 0; i < len(input); i += SPONGE_RATE {
 50		for j := 0; j < SPONGE_RATE; j++ {
 51			if i+j < len(input) {
 52				state[j] = input[i+j]
 53			}
 54		}
 55		state = c.Poseidon(state)
 56	}
 57
 58	var outputs []frontend.Variable
 59
 60	for {
 61		for i := 0; i < SPONGE_RATE; i++ {
 62			outputs = append(outputs, state[i])
 63			if len(outputs) == nbOutputs {
 64				return outputs
 65			}
 66		}
 67		state = c.Poseidon(state)
 68	}
 69}
 70
 71// The input elements can be outside of the Goldilocks field.
 72// The returned slice's elements will all be within Goldilocks field.
 73func (c *PoseidonChip) HashNoPad(input []field.F) PoseidonHashOut {
 74	var hash PoseidonHashOut
 75	inputVars := []frontend.Variable{}
 76
 77	for i := 0; i < len(input); i++ {
 78		inputVars = append(inputVars, c.fieldAPI.Reduce(input[i]).Limbs[0])
 79	}
 80
 81	outputVars := c.HashNToMNoPad(inputVars, 4)
 82	for i := 0; i < 4; i++ {
 83		hash[i] = c.fieldAPI.NewElement(outputVars[i])
 84	}
 85
 86	return hash
 87}
 88
 89func (c *PoseidonChip) ToVec(hash PoseidonHashOut) []field.F {
 90	return hash[:]
 91}
 92
 93func (c *PoseidonChip) fullRounds(state PoseidonState, roundCounter *int) PoseidonState {
 94	for i := 0; i < HALF_N_FULL_ROUNDS; i++ {
 95		state = c.constantLayer(state, roundCounter)
 96		state = c.sBoxLayer(state)
 97		state = c.mdsLayer(state)
 98		*roundCounter += 1
 99	}
100	return state
101}
102
103func (c *PoseidonChip) partialRounds(state PoseidonState, roundCounter *int) PoseidonState {
104	state = c.partialFirstConstantLayer(state)
105	state = c.mdsPartialLayerInit(state)
106
107	for i := 0; i < N_PARTIAL_ROUNDS; i++ {
108		state[0] = c.sBoxMonomial(state[0])
109		state[0] = field.GoldilocksMulAdd(c.api, frontend.Variable(1), state[0], FAST_PARTIAL_ROUND_CONSTANTS[i])
110		state = c.mdsPartialLayerFast(state, i)
111	}
112
113	*roundCounter += N_PARTIAL_ROUNDS
114
115	return state
116}
117
118func (c *PoseidonChip) constantLayer(state PoseidonState, roundCounter *int) PoseidonState {
119	for i := 0; i < 12; i++ {
120		if i < SPONGE_WIDTH {
121			roundConstant := ALL_ROUND_CONSTANTS[i+SPONGE_WIDTH*(*roundCounter)]
122			state[i] = field.GoldilocksMulAdd(c.api, frontend.Variable(1), state[i], roundConstant)
123		}
124	}
125	return state
126}
127
128func (c *PoseidonChip) ConstantLayerExtension(state PoseidonStateExtension, roundCounter *int) PoseidonStateExtension {
129	for i := 0; i < 12; i++ {
130		if i < SPONGE_WIDTH {
131			roundConstant := c.qeAPI.VarToQE(ALL_ROUND_CONSTANTS[i+SPONGE_WIDTH*(*roundCounter)])
132			state[i] = c.qeAPI.AddExtension(state[i], roundConstant)
133		}
134	}
135	return state
136}
137
138func (c *PoseidonChip) sBoxMonomial(x frontend.Variable) frontend.Variable {
139	x2 := field.GoldilocksMulAdd(c.api, x, x, frontend.Variable(0))
140	x4 := field.GoldilocksMulAdd(c.api, x2, x2, frontend.Variable(0))
141	x6 := field.GoldilocksMulAdd(c.api, x4, x2, frontend.Variable(0))
142	return field.GoldilocksMulAdd(c.api, x6, x, frontend.Variable(0))
143}
144
145func (c *PoseidonChip) SBoxMonomialExtension(x field.QuadraticExtension) field.QuadraticExtension {
146	x2 := c.qeAPI.SquareExtension(x)
147	x4 := c.qeAPI.SquareExtension(x2)
148	x3 := c.qeAPI.MulExtension(x, x2)
149	return c.qeAPI.MulExtension(x3, x4)
150}
151
152func (c *PoseidonChip) sBoxLayer(state PoseidonState) PoseidonState {
153	for i := 0; i < 12; i++ {
154		if i < SPONGE_WIDTH {
155			state[i] = c.sBoxMonomial(state[i])
156		}
157	}
158	return state
159}
160
161func (c *PoseidonChip) SBoxLayerExtension(state PoseidonStateExtension) PoseidonStateExtension {
162	for i := 0; i < 12; i++ {
163		if i < SPONGE_WIDTH {
164			state[i] = c.SBoxMonomialExtension(state[i])
165		}
166	}
167	return state
168}
169
170func (c *PoseidonChip) mdsRowShf(r int, v [SPONGE_WIDTH]frontend.Variable) frontend.Variable {
171	res := ZERO_VAR
172
173	for i := 0; i < 12; i++ {
174		if i < SPONGE_WIDTH {
175			res = field.GoldilocksMulAdd(c.api, v[(i+r)%SPONGE_WIDTH], MDS_MATRIX_CIRC_VARS[i], res)
176		}
177	}
178
179	res = field.GoldilocksMulAdd(c.api, v[r], MDS_MATRIX_DIAG_VARS[r], res)
180	return res
181}
182
183func (c *PoseidonChip) MdsRowShfExtension(r int, v [SPONGE_WIDTH]field.QuadraticExtension) field.QuadraticExtension {
184	res := c.qeAPI.FieldToQE(field.ZERO_F)
185
186	for i := 0; i < 12; i++ {
187		if i < SPONGE_WIDTH {
188			matrixVal := c.qeAPI.VarToQE(MDS_MATRIX_CIRC[i])
189			res1 := c.qeAPI.MulExtension(v[(i+r)%SPONGE_WIDTH], matrixVal)
190			res = c.qeAPI.AddExtension(res, res1)
191		}
192	}
193
194	matrixVal := c.qeAPI.VarToQE(MDS_MATRIX_DIAG[r])
195	res = c.qeAPI.AddExtension(res, c.qeAPI.MulExtension(v[r], matrixVal))
196	return res
197}
198
199func (c *PoseidonChip) mdsLayer(state_ PoseidonState) PoseidonState {
200	var result PoseidonState
201	for i := 0; i < SPONGE_WIDTH; i++ {
202		result[i] = frontend.Variable(0)
203	}
204
205	for r := 0; r < 12; r++ {
206		if r < SPONGE_WIDTH {
207			result[r] = c.mdsRowShf(r, state_)
208		}
209	}
210
211	return result
212}
213
214func (c *PoseidonChip) MdsLayerExtension(state_ PoseidonStateExtension) PoseidonStateExtension {
215	var result PoseidonStateExtension
216
217	for r := 0; r < 12; r++ {
218		if r < SPONGE_WIDTH {
219			sum := c.MdsRowShfExtension(r, state_)
220			result[r] = sum
221		}
222	}
223
224	return result
225}
226
227func (c *PoseidonChip) partialFirstConstantLayer(state PoseidonState) PoseidonState {
228	for i := 0; i < 12; i++ {
229		if i < SPONGE_WIDTH {
230			state[i] = field.GoldilocksMulAdd(c.api, frontend.Variable(1), state[i], FAST_PARTIAL_FIRST_ROUND_CONSTANT[i])
231		}
232	}
233	return state
234}
235
236func (c *PoseidonChip) PartialFirstConstantLayerExtension(state PoseidonStateExtension) PoseidonStateExtension {
237	for i := 0; i < 12; i++ {
238		if i < SPONGE_WIDTH {
239			state[i] = c.qeAPI.AddExtension(state[i], c.qeAPI.VarToQE((FAST_PARTIAL_FIRST_ROUND_CONSTANT[i])))
240		}
241	}
242	return state
243}
244
245func (c *PoseidonChip) mdsPartialLayerInit(state PoseidonState) PoseidonState {
246	var result PoseidonState
247	for i := 0; i < 12; i++ {
248		result[i] = frontend.Variable(0)
249	}
250
251	result[0] = state[0]
252
253	for r := 1; r < 12; r++ {
254		if r < SPONGE_WIDTH {
255			for d := 1; d < 12; d++ {
256				if d < SPONGE_WIDTH {
257					t := FAST_PARTIAL_ROUND_INITIAL_MATRIX[r-1][d-1]
258					result[d] = field.GoldilocksMulAdd(c.api, state[r], t, result[d])
259				}
260			}
261		}
262	}
263
264	return result
265}
266
267func (c *PoseidonChip) MdsPartialLayerInitExtension(state PoseidonStateExtension) PoseidonStateExtension {
268	var result PoseidonStateExtension
269	for i := 0; i < 12; i++ {
270		result[i] = c.qeAPI.FieldToQE(field.ZERO_F)
271	}
272
273	result[0] = state[0]
274
275	for r := 1; r < 12; r++ {
276		if r < SPONGE_WIDTH {
277			for d := 1; d < 12; d++ {
278				if d < SPONGE_WIDTH {
279					t := c.qeAPI.VarToQE(FAST_PARTIAL_ROUND_INITIAL_MATRIX[r-1][d-1])
280					result[d] = c.qeAPI.AddExtension(result[d], c.qeAPI.MulExtension(state[r], t))
281				}
282			}
283		}
284	}
285
286	return result
287}
288
289func (c *PoseidonChip) mdsPartialLayerFast(state PoseidonState, r int) PoseidonState {
290	dSum := ZERO_VAR
291	for i := 1; i < 12; i++ {
292		if i < SPONGE_WIDTH {
293			t := FAST_PARTIAL_ROUND_W_HATS_VARS[r][i-1]
294			dSum = field.GoldilocksMulAdd(c.api, state[i], t, dSum)
295		}
296	}
297
298	d := field.GoldilocksMulAdd(c.api, state[0], MDS0TO0_VAR, dSum)
299
300	var result PoseidonState
301	for i := 0; i < SPONGE_WIDTH; i++ {
302		result[i] = frontend.Variable(0)
303	}
304
305	result[0] = d
306
307	for i := 1; i < 12; i++ {
308		if i < SPONGE_WIDTH {
309			t := FAST_PARTIAL_ROUND_VS[r][i-1]
310			result[i] = field.GoldilocksMulAdd(c.api, state[0], t, state[i])
311		}
312	}
313
314	return result
315}
316
317func (c *PoseidonChip) MdsPartialLayerFastExtension(state PoseidonStateExtension, r int) PoseidonStateExtension {
318	s0 := state[0]
319	mds0to0 := c.qeAPI.VarToQE(MDS0TO0)
320	d := c.qeAPI.MulExtension(s0, mds0to0)
321	for i := 1; i < 12; i++ {
322		if i < SPONGE_WIDTH {
323			t := c.qeAPI.VarToQE(FAST_PARTIAL_ROUND_W_HATS[r][i-1])
324			d = c.qeAPI.AddExtension(d, c.qeAPI.MulExtension(state[i], t))
325		}
326	}
327
328	var result PoseidonStateExtension
329	result[0] = d
330	for i := 1; i < 12; i++ {
331		if i < SPONGE_WIDTH {
332			t := c.qeAPI.VarToQE(FAST_PARTIAL_ROUND_VS[r][i-1])
333			result[i] = c.qeAPI.AddExtension(c.qeAPI.MulExtension(state[0], t), state[i])
334		}
335	}
336
337	return result
338}