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