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