arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/poseidon/poseidon.go
poseidon/poseidon.goRaw
  1package poseidon
  2
  3import (
  4	. "gnark-ed25519/field"
  5
  6	"github.com/consensys/gnark/frontend"
  7)
  8
  9const HALF_N_FULL_ROUNDS = 4
 10const N_FULL_ROUNDS_TOTAL = 2 * HALF_N_FULL_ROUNDS
 11const N_PARTIAL_ROUNDS = 22
 12const N_ROUNDS = N_FULL_ROUNDS_TOTAL + N_PARTIAL_ROUNDS
 13const MAX_WIDTH = 12
 14const WIDTH = 12
 15const SPONGE_WIDTH = 12
 16const SPONGE_RATE = 8
 17
 18type PoseidonState = [WIDTH]F
 19type PoseidonChip struct {
 20	api   frontend.API
 21	field frontend.API
 22}
 23
 24func NewPoseidonChip(api frontend.API, field frontend.API) *PoseidonChip {
 25	return &PoseidonChip{api: api, field: field}
 26}
 27
 28func (c *PoseidonChip) Poseidon(input PoseidonState) PoseidonState {
 29	state := input
 30	roundCounter := 0
 31	state = c.fullRounds(state, &roundCounter)
 32	state = c.partialRounds(state, &roundCounter)
 33	state = c.fullRounds(state, &roundCounter)
 34	return state
 35}
 36
 37func (c *PoseidonChip) HashNToMNoPad(input []F, nbOutputs int) []F {
 38	var state PoseidonState
 39
 40	for i := 0; i < len(input); i += SPONGE_RATE {
 41		for j := 0; j < SPONGE_RATE; j++ {
 42			if i+j < len(input) {
 43				state[j] = input[i+j]
 44			}
 45		}
 46		state = c.Poseidon(state)
 47	}
 48
 49	var outputs []F
 50
 51	for {
 52		for i := 0; i < SPONGE_RATE; i++ {
 53			outputs = append(outputs, state[i])
 54			if len(outputs) == nbOutputs {
 55				return outputs
 56			}
 57		}
 58		state = c.Poseidon(state)
 59	}
 60}
 61
 62func (c *PoseidonChip) HashNoPad(input []F) Hash {
 63	var hash Hash
 64	copy(hash[:], c.HashNToMNoPad(input, 4))
 65	return hash
 66}
 67
 68func (c *PoseidonChip) fullRounds(state PoseidonState, roundCounter *int) PoseidonState {
 69	for i := 0; i < HALF_N_FULL_ROUNDS; i++ {
 70		state = c.constantLayer(state, roundCounter)
 71		state = c.sBoxLayer(state)
 72		state = c.mdsLayer(state)
 73		if *roundCounter >= 26 && i == 3 {
 74			break
 75		}
 76		*roundCounter += 1
 77	}
 78	return state
 79}
 80
 81func (c *PoseidonChip) partialRounds(state PoseidonState, roundCounter *int) PoseidonState {
 82	state = c.partialFirstConstantLayer(state)
 83	state = c.mdsPartialLayerInit(state)
 84
 85	for i := 0; i < N_PARTIAL_ROUNDS; i++ {
 86		state[0] = c.sBoxMonomial(state[0])
 87		state[0] = c.field.Add(state[0], FAST_PARTIAL_ROUND_CONSTANTS[i]).(F)
 88		state = c.mdsPartialLayerFast(state, i)
 89	}
 90
 91	*roundCounter += N_PARTIAL_ROUNDS
 92
 93	return state
 94}
 95
 96func (c *PoseidonChip) constantLayer(state PoseidonState, roundCounter *int) PoseidonState {
 97	for i := 0; i < 12; i++ {
 98		if i < WIDTH {
 99			roundConstant := NewFieldElement(ALL_ROUND_CONSTANTS[i+WIDTH*(*roundCounter)])
100			state[i] = c.field.Add(state[i], roundConstant).(F)
101		}
102	}
103	return state
104}
105
106func (c *PoseidonChip) sBoxLayer(state PoseidonState) PoseidonState {
107	for i := 0; i < 12; i++ {
108		if i < WIDTH {
109			state[i] = c.sBoxMonomial(state[i])
110		}
111	}
112	return state
113}
114
115func (c *PoseidonChip) sBoxMonomial(x F) F {
116	x2 := c.field.Mul(x, x)
117	x4 := c.field.Mul(x2, x2)
118	x3 := c.field.Mul(x2, x)
119	return c.field.Mul(x3, x4).(F)
120}
121
122func (c *PoseidonChip) mdsRowShf(r int, v [WIDTH]frontend.Variable) frontend.Variable {
123	res := frontend.Variable(0)
124
125	for i := 0; i < 12; i++ {
126		if i < WIDTH {
127			res1 := c.api.Mul(v[(i+r)%WIDTH], frontend.Variable(MDS_MATRIX_CIRC[i]))
128			res = c.api.Add(res, res1)
129		}
130	}
131
132	res = c.api.Add(res, c.api.Mul(v[r], MDS_MATRIX_DIAG[r]))
133	return res
134}
135
136func (c *PoseidonChip) mdsLayer(state_ PoseidonState) PoseidonState {
137	var result PoseidonState
138	for i := 0; i < WIDTH; i++ {
139		result[i] = NewFieldElement(0)
140	}
141
142	var state [WIDTH]frontend.Variable
143	for i := 0; i < WIDTH; i++ {
144		state[i] = c.api.FromBinary(c.field.ToBinary(state_[i])...)
145	}
146
147	for r := 0; r < 12; r++ {
148		if r < WIDTH {
149			sum := c.mdsRowShf(r, state)
150			bits := c.api.ToBinary(sum)
151			result[r] = c.field.FromBinary(bits).(F)
152		}
153	}
154
155	return result
156}
157
158func (c *PoseidonChip) partialFirstConstantLayer(state PoseidonState) PoseidonState {
159	for i := 0; i < 12; i++ {
160		if i < WIDTH {
161			state[i] = c.field.Add(state[i], NewFieldElement(FAST_PARTIAL_FIRST_ROUND_CONSTANT[i])).(F)
162		}
163	}
164	return state
165}
166
167func (c *PoseidonChip) mdsPartialLayerInit(state PoseidonState) PoseidonState {
168	var result PoseidonState
169	for i := 0; i < 12; i++ {
170		result[i] = NewFieldElement(0)
171	}
172
173	result[0] = state[0]
174
175	for r := 1; r < 12; r++ {
176		if r < WIDTH {
177			for d := 1; d < 12; d++ {
178				if d < WIDTH {
179					t := NewFieldElement(FAST_PARTIAL_ROUND_INITIAL_MATRIX[r-1][d-1])
180					result[d] = c.field.Add(result[d], c.field.Mul(state[r], t)).(F)
181				}
182			}
183		}
184	}
185
186	return result
187}
188
189func (c *PoseidonChip) mdsPartialLayerFast(state PoseidonState, r int) PoseidonState {
190	dSum := frontend.Variable(0)
191	for i := 1; i < 12; i++ {
192		if i < WIDTH {
193			t := frontend.Variable(FAST_PARTIAL_ROUND_W_HATS[r][i-1])
194			si := c.api.FromBinary(c.field.ToBinary(state[i])...)
195			dSum = c.api.Add(dSum, c.api.Mul(si, t))
196		}
197	}
198
199	s0 := c.api.FromBinary(c.field.ToBinary(state[0])...)
200	mds0to0 := frontend.Variable(MDS_MATRIX_CIRC[0] + MDS_MATRIX_DIAG[0])
201	dSum = c.api.Add(dSum, c.api.Mul(s0, mds0to0))
202	d := c.field.FromBinary(c.api.ToBinary(dSum))
203
204	var result PoseidonState
205	for i := 0; i < WIDTH; i++ {
206		result[i] = NewFieldElement(0)
207	}
208
209	result[0] = d.(F)
210
211	for i := 1; i < 12; i++ {
212		if i < WIDTH {
213			t := NewFieldElement(FAST_PARTIAL_ROUND_VS[r][i-1])
214			result[i] = c.field.Add(state[i], c.field.Mul(state[0], t)).(F)
215		}
216	}
217
218	return result
219}