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