arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/plonky2_verifier/poseidon_gate.go
plonky2_verifier/poseidon_gate.goRaw
  1package plonky2_verifier
  2
  3import (
  4	. "gnark-plonky2-verifier/field"
  5	"gnark-plonky2-verifier/poseidon"
  6)
  7
  8func (g *PoseidonGate) WireInput(i uint64) uint64 {
  9	return i
 10}
 11
 12func (g *PoseidonGate) WireOutput(i uint64) uint64 {
 13	return poseidon.SPONGE_WIDTH + i
 14}
 15
 16func (g *PoseidonGate) WireSwap() uint64 {
 17	return 2 * poseidon.SPONGE_WIDTH
 18}
 19
 20const START_DELTA = 2*poseidon.SPONGE_WIDTH + 1
 21
 22func (g *PoseidonGate) WireDelta(i uint64) uint64 {
 23	if i >= 4 {
 24		panic("Delta index out of range")
 25	}
 26	return START_DELTA + i
 27}
 28
 29const START_FULL_0 = START_DELTA + 4
 30
 31func (g *PoseidonGate) WireFullSBox0(round uint64, i uint64) uint64 {
 32	if round == 0 {
 33		panic("First-round S-box inputs are not stored as wires")
 34	}
 35	if round >= poseidon.HALF_N_FULL_ROUNDS {
 36		panic("S-box input round out of range")
 37	}
 38	if i >= poseidon.SPONGE_WIDTH {
 39		panic("S-box input index out of range")
 40	}
 41
 42	return START_FULL_0 + (round-1)*poseidon.SPONGE_WIDTH + i
 43}
 44
 45const START_PARTIAL = START_FULL_0 + (poseidon.HALF_N_FULL_ROUNDS-1)*poseidon.SPONGE_WIDTH
 46
 47func (g *PoseidonGate) WirePartialSBox(round uint64) uint64 {
 48	if round >= poseidon.N_PARTIAL_ROUNDS {
 49		panic("S-box input round out of range")
 50	}
 51	return START_PARTIAL + round
 52}
 53
 54const START_FULL_1 = START_PARTIAL + poseidon.N_PARTIAL_ROUNDS
 55
 56func (g *PoseidonGate) WireFullSBox1(round uint64, i uint64) uint64 {
 57	if round >= poseidon.HALF_N_FULL_ROUNDS {
 58		panic("S-box input round out of range")
 59	}
 60	if i >= poseidon.SPONGE_WIDTH {
 61		panic("S-box input index out of range")
 62	}
 63
 64	return START_FULL_1 + round*poseidon.SPONGE_WIDTH + i
 65}
 66
 67func (g *PoseidonGate) WiresEnd() uint64 {
 68	return START_FULL_1 + poseidon.HALF_N_FULL_ROUNDS*poseidon.SPONGE_WIDTH
 69}
 70
 71type PoseidonGate struct {
 72}
 73
 74func (p *PoseidonGate) EvalUnfiltered(pc *PlonkChip, vars EvaluationVars) []QuadraticExtension {
 75	constraints := []QuadraticExtension{}
 76
 77	poseidonChip := poseidon.NewPoseidonChip(pc.api, NewFieldAPI(pc.api), pc.qeAPI)
 78
 79	// Assert that `swap` is binary.
 80	swap := vars.localWires[p.WireSwap()]
 81	notSwap := pc.qeAPI.SubExtension(pc.qeAPI.FieldToQE(ONE_F), swap)
 82	constraints = append(constraints, pc.qeAPI.MulExtension(swap, notSwap))
 83
 84	// Assert that each delta wire is set properly: `delta_i = swap * (rhs - lhs)`.
 85	for i := uint64(0); i < 4; i++ {
 86		inputLhs := vars.localWires[p.WireInput(i)]
 87		inputRhs := vars.localWires[p.WireInput(i+4)]
 88		deltaI := vars.localWires[p.WireDelta(i)]
 89		diff := pc.qeAPI.SubExtension(inputRhs, inputLhs)
 90		expectedDeltaI := pc.qeAPI.MulExtension(swap, diff)
 91		constraints = append(constraints, pc.qeAPI.SubExtension(expectedDeltaI, deltaI))
 92	}
 93
 94	// Compute the possibly-swapped input layer.
 95	var state [poseidon.SPONGE_WIDTH]QuadraticExtension
 96	for i := uint64(0); i < 4; i++ {
 97		deltaI := vars.localWires[p.WireDelta(i)]
 98		inputLhs := vars.localWires[p.WireInput(i)]
 99		inputRhs := vars.localWires[p.WireInput(i+4)]
100		state[i] = pc.qeAPI.AddExtension(inputLhs, deltaI)
101		state[i+4] = pc.qeAPI.SubExtension(inputRhs, deltaI)
102	}
103	for i := uint64(8); i < poseidon.SPONGE_WIDTH; i++ {
104		state[i] = vars.localWires[p.WireInput(i)]
105	}
106
107	round_ctr := 0
108
109	// First set of full rounds.
110	for r := uint64(0); r < poseidon.HALF_N_FULL_ROUNDS; r++ {
111		state = poseidonChip.ConstantLayerExtension(state, &round_ctr)
112		if r != 0 {
113			for i := uint64(0); i < poseidon.SPONGE_WIDTH; i++ {
114				sBoxIn := vars.localWires[p.WireFullSBox0(r, i)]
115				constraints = append(constraints, pc.qeAPI.SubExtension(state[i], sBoxIn))
116				state[i] = sBoxIn
117			}
118		}
119		state = poseidonChip.SBoxLayerExtension(state)
120		state = poseidonChip.MdsLayerExtension(state)
121		round_ctr++
122	}
123
124	// Partial rounds.
125	state = poseidonChip.PartialFirstConstantLayerExtension(state)
126	state = poseidonChip.MdsPartialLayerInitExtension(state)
127	for r := uint64(0); r < poseidon.N_PARTIAL_ROUNDS-1; r++ {
128		sBoxIn := vars.localWires[p.WirePartialSBox(r)]
129		constraints = append(constraints, pc.qeAPI.SubExtension(state[0], sBoxIn))
130		state[0] = poseidonChip.SBoxMonomialExtension(sBoxIn)
131		state[0] = pc.qeAPI.AddExtension(state[0], pc.qeAPI.FieldToQE(NewFieldElement(poseidon.FAST_PARTIAL_ROUND_CONSTANTS[r])))
132		state = poseidonChip.MdsPartialLayerFastExtension(state, int(r))
133	}
134	sBoxIn := vars.localWires[p.WirePartialSBox(poseidon.N_PARTIAL_ROUNDS-1)]
135	constraints = append(constraints, pc.qeAPI.SubExtension(state[0], sBoxIn))
136	state[0] = poseidonChip.SBoxMonomialExtension(sBoxIn)
137	state = poseidonChip.MdsPartialLayerFastExtension(state, poseidon.N_PARTIAL_ROUNDS-1)
138	round_ctr += poseidon.N_PARTIAL_ROUNDS
139
140	// Second set of full rounds.
141	for r := uint64(0); r < poseidon.HALF_N_FULL_ROUNDS; r++ {
142		poseidonChip.ConstantLayerExtension(state, &round_ctr)
143		for i := uint64(0); i < poseidon.SPONGE_WIDTH; i++ {
144			sBoxIn := vars.localWires[p.WireFullSBox1(r, i)]
145			constraints = append(constraints, pc.qeAPI.SubExtension(state[i], sBoxIn))
146			state[i] = sBoxIn
147		}
148		state = poseidonChip.MdsLayerExtension(state)
149		state = poseidonChip.SBoxLayerExtension(state)
150		round_ctr++
151	}
152
153	for i := uint64(0); i < poseidon.SPONGE_WIDTH; i++ {
154		constraints = append(constraints, pc.qeAPI.SubExtension(state[i], vars.localWires[p.WireOutput(i)]))
155	}
156
157	return constraints
158}