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