arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/goldilocks/base.go
goldilocks/base.goRaw
  1// This package implements efficient Golidlocks arithmetic operations within Gnark. We do not use
  2// the emulated field arithmetic API, because it is too slow for our purposes. Instead, we use
  3// an efficient reduction method that leverages the fact that the modulus is a simple
  4// linear combination of powers of two.
  5package goldilocks
  6
  7// In general, methods whose name do not contain `NoReduce` can be used without any extra mental
  8// overhead. These methods act exactly as you would expect a normal field would operate.
  9//
 10// However, if you want to aggressively optimize the number of constraints in your circuit, it can
 11// be very beneficial to use the no reduction methods and keep track of the maximum number of bits
 12// your computation uses.
 13
 14// This implementation is based on the following plonky2 implementation of Goldilocks
 15// Available here: https://github.com/0xPolygonZero/plonky2/blob/main/field/src/goldilocks_field.rs#L70
 16
 17import (
 18	"fmt"
 19	"math"
 20	"math/big"
 21	"os"
 22
 23	"github.com/consensys/gnark-crypto/field/goldilocks"
 24	"github.com/consensys/gnark/constraint/solver"
 25	"github.com/consensys/gnark/frontend"
 26	"github.com/consensys/gnark/std/math/emulated"
 27	"github.com/consensys/gnark/std/rangecheck"
 28)
 29
 30// The multiplicative group generator of the field.
 31var MULTIPLICATIVE_GROUP_GENERATOR goldilocks.Element = goldilocks.NewElement(7)
 32
 33// The two adicity of the field.
 34var TWO_ADICITY uint64 = 32
 35
 36// The power of two generator of the field.
 37var POWER_OF_TWO_GENERATOR goldilocks.Element = goldilocks.NewElement(1753635133440165772)
 38
 39// The modulus of the field.
 40var MODULUS *big.Int = emulated.Goldilocks{}.Modulus()
 41
 42// The number of bits to use for range checks on inner products of field elements.
 43var RANGE_CHECK_NB_BITS int = 140
 44
 45// Registers the hint functions with the solver.
 46func init() {
 47	solver.RegisterHint(MulAddHint)
 48	solver.RegisterHint(ReduceHint)
 49	solver.RegisterHint(InverseHint)
 50	solver.RegisterHint(SplitLimbsHint)
 51}
 52
 53// A type alias used to represent Goldilocks field elements.
 54type Variable struct {
 55	Limb frontend.Variable
 56}
 57
 58// Creates a new Goldilocks field element from an existing variable. Assumes that the element is
 59// already reduced.
 60func NewVariable(x frontend.Variable) Variable {
 61	return Variable{Limb: x}
 62}
 63
 64// The zero element in the Golidlocks field.
 65func Zero() Variable {
 66	return NewVariable(0)
 67}
 68
 69// The one element in the Goldilocks field.
 70func One() Variable {
 71	return NewVariable(1)
 72}
 73
 74// The negative one element in the Goldilocks field.
 75func NegOne() Variable {
 76	return NewVariable(MODULUS.Uint64() - 1)
 77}
 78
 79// The chip used for Goldilocks field operations.
 80type Chip struct {
 81	api          frontend.API
 82	rangeChecker frontend.Rangechecker
 83}
 84
 85// Creates a new Goldilocks Chip.
 86func New(api frontend.API) *Chip {
 87	use_bit_decomp := os.Getenv("USE_BIT_DECOMPOSITION_RANGE_CHECK")
 88
 89	var rangeChecker frontend.Rangechecker
 90
 91	// If USE_BIT_DECOMPOSITION_RANGE_CHECK is not set, then use the std.rangecheck New function
 92	if use_bit_decomp == "" {
 93		rangeChecker = rangecheck.New(api)
 94	} else {
 95		rangeChecker = bitDecompChecker{api: api}
 96	}
 97	return &Chip{api: api, rangeChecker: rangeChecker}
 98}
 99
100// Adds two goldilocks field elements and returns a value within the goldilocks field.
101func (p *Chip) Add(a Variable, b Variable) Variable {
102	return p.MulAdd(a, NewVariable(1), b)
103}
104
105// Adds two goldilocks field elements and returns a value that may not be within the goldilocks field
106// (e.g. the sum is not reduced).
107func (p *Chip) AddNoReduce(a Variable, b Variable) Variable {
108	return NewVariable(p.api.Add(a.Limb, b.Limb))
109}
110
111// Subracts two goldilocks field elements and returns a value within the goldilocks field.
112func (p *Chip) Sub(a Variable, b Variable) Variable {
113	return p.MulAdd(b, NegOne(), a)
114}
115
116// Subracts two goldilocks field elements and returns a value that may not be within the goldilocks field
117// (e.g. the difference is not reduced).
118func (p *Chip) SubNoReduce(a Variable, b Variable) Variable {
119	return NewVariable(p.api.Add(a.Limb, p.api.Mul(b.Limb, NegOne().Limb)))
120}
121
122// Multiplies two goldilocks field elements and returns a value within the goldilocks field.
123func (p *Chip) Mul(a Variable, b Variable) Variable {
124	return p.MulAdd(a, b, Zero())
125}
126
127// Multiplies two goldilocks field elements and returns a value that may not be within the goldilocks field
128// (e.g. the product is not reduced).
129func (p *Chip) MulNoReduce(a Variable, b Variable) Variable {
130	return NewVariable(p.api.Mul(a.Limb, b.Limb))
131}
132
133// Multiplies two field elements and adds a field element (e.g. computes a * b + c).  The returned value
134// will be within the goldilocks field.
135func (p *Chip) MulAdd(a Variable, b Variable, c Variable) Variable {
136	result, err := p.api.Compiler().NewHint(MulAddHint, 2, a.Limb, b.Limb, c.Limb)
137	if err != nil {
138		panic(err)
139	}
140
141	quotient := NewVariable(result[0])
142	remainder := NewVariable(result[1])
143
144	cLimbCopy := p.api.Mul(c.Limb, 1)
145	lhs := p.api.MulAcc(cLimbCopy, a.Limb, b.Limb)
146	rhs := p.api.MulAcc(remainder.Limb, MODULUS, quotient.Limb)
147	p.api.AssertIsEqual(lhs, rhs)
148
149	p.RangeCheck(quotient)
150	p.RangeCheck(remainder)
151	return remainder
152}
153
154// Multiplies two field elements and adds a field element (e.g. computes a * b + c).  The returned value
155// may no be within the goldilocks field (e.g. the result is not reduced).
156func (p *Chip) MulAddNoReduce(a Variable, b Variable, c Variable) Variable {
157	cLimbCopy := p.api.Mul(c.Limb, 1)
158	return NewVariable(p.api.MulAcc(cLimbCopy, a.Limb, b.Limb))
159}
160
161// The hint used to compute MulAdd.
162func MulAddHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
163	if len(inputs) != 3 {
164		panic("MulAddHint expects 3 input operands")
165	}
166
167	for _, operand := range inputs {
168		if operand.Cmp(MODULUS) >= 0 {
169			panic(fmt.Sprintf("%s is not in the field", operand.String()))
170		}
171	}
172
173	product := new(big.Int).Mul(inputs[0], inputs[1])
174	sum := new(big.Int).Add(product, inputs[2])
175	quotient := new(big.Int).Div(sum, MODULUS)
176	remainder := new(big.Int).Rem(sum, MODULUS)
177
178	results[0] = quotient
179	results[1] = remainder
180
181	return nil
182}
183
184// Reduces a field element x such that x % MODULUS = y.
185func (p *Chip) Reduce(x Variable) Variable {
186	// Witness a `quotient` and `remainder` such that:
187	//
188	// 		MODULUS * quotient + remainder = x
189	//
190	// Must check that offset \in [0, MODULUS) and carry \in [0, 2^RANGE_CHECK_NB_BITS) to ensure
191	// that this computation does not overflow. We use 2^RANGE_CHECK_NB_BITS to reduce the cost of the range check
192	//
193	// In other words, we assume that we at most compute a a dot product with dimension at most RANGE_CHECK_NB_BITS - 128.
194	return p.ReduceWithMaxBits(x, uint64(RANGE_CHECK_NB_BITS))
195}
196
197// Reduces a field element x such that x % MODULUS = y.
198func (p *Chip) ReduceWithMaxBits(x Variable, maxNbBits uint64) Variable {
199	// Witness a `quotient` and `remainder` such that:
200	//
201	// 		MODULUS * quotient + remainder = x
202	//
203	// Must check that remainder \in [0, MODULUS) and quotient \in [0, 2^maxNbBits) to ensure that this
204	// computation does not overflow.
205
206	result, err := p.api.Compiler().NewHint(ReduceHint, 2, x.Limb)
207	if err != nil {
208		panic(err)
209	}
210
211	quotient := result[0]
212	p.rangeChecker.Check(quotient, int(maxNbBits))
213
214	remainder := NewVariable(result[1])
215	p.RangeCheck(remainder)
216
217	p.api.AssertIsEqual(x.Limb, p.api.Add(p.api.Mul(quotient, MODULUS), remainder.Limb))
218
219	return remainder
220}
221
222// The hint used to compute Reduce.
223func ReduceHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
224	if len(inputs) != 1 {
225		panic("ReduceHint expects 1 input operand")
226	}
227	input := inputs[0]
228	quotient := new(big.Int).Div(input, MODULUS)
229	remainder := new(big.Int).Rem(input, MODULUS)
230	results[0] = quotient
231	results[1] = remainder
232	return nil
233}
234
235// Computes the inverse of a field element x such that x * x^-1 = 1.
236func (p *Chip) Inverse(x Variable) (Variable, frontend.Variable) {
237	result, err := p.api.Compiler().NewHint(InverseHint, 1, x.Limb)
238	if err != nil {
239		panic(err)
240	}
241
242	inverse := NewVariable(result[0])
243	isZero := p.api.IsZero(x.Limb)
244	hasInv := p.api.Sub(1, isZero)
245	p.RangeCheck(inverse)
246
247	product := p.Mul(inverse, x)
248	productToCheck := p.api.Select(hasInv, product.Limb, frontend.Variable(1))
249	p.api.AssertIsEqual(productToCheck, frontend.Variable(1))
250
251	return inverse, hasInv
252}
253
254// The hint used to compute Inverse.
255func InverseHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
256	if len(inputs) != 1 {
257		panic("InverseHint expects 1 input operand")
258	}
259
260	input := inputs[0]
261	if input.Cmp(MODULUS) == 0 || input.Cmp(MODULUS) == 1 {
262		panic("Input is not in the field")
263	}
264
265	inputGl := goldilocks.NewElement(input.Uint64())
266	resultGl := goldilocks.NewElement(0)
267
268	// Will set resultGL if inputGL == 0
269	resultGl.Inverse(&inputGl)
270
271	result := big.NewInt(0)
272	results[0] = resultGl.BigInt(result)
273
274	return nil
275}
276
277// The hint used to split a GoldilocksVariable into 2 32 bit limbs.
278func SplitLimbsHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
279	if len(inputs) != 1 {
280		panic("SplitLimbsHint expects 1 input operand")
281	}
282
283	// The Goldilocks field element
284	input := inputs[0]
285
286	if input.Cmp(MODULUS) == 0 || input.Cmp(MODULUS) == 1 {
287		return fmt.Errorf("input is not in the field")
288	}
289
290	two_32 := big.NewInt(int64(math.Pow(2, 32)))
291
292	// The most significant bits
293	results[0] = new(big.Int).Quo(input, two_32)
294	// The least significant bits
295	results[1] = new(big.Int).Rem(input, two_32)
296
297	return nil
298}
299
300// Range checks a field element x to be less than the Golidlocks modulus 2 ^ 64 - 2 ^ 32 + 1.
301func (p *Chip) RangeCheck(x Variable) {
302	// The Goldilocks' modulus is 2^64 - 2^32 + 1, which is:
303	//
304	// 		1111111111111111111111111111111100000000000000000000000000000001
305	//
306	// in big endian binary. This function will first verify that x is at most 64 bits wide. Then it
307	// checks that if the bits[0:31] (in big-endian) are all 1, then bits[32:64] are all zero.
308
309	result, err := p.api.Compiler().NewHint(SplitLimbsHint, 2, x.Limb)
310	if err != nil {
311		panic(err)
312	}
313
314	// We check that this is a valid decomposition of the Goldilock's element and range-check each limb.
315	mostSigLimb := result[0]
316	leastSigLimb := result[1]
317	p.api.AssertIsEqual(
318		p.api.Add(
319			p.api.Mul(mostSigLimb, uint64(math.Pow(2, 32))),
320			leastSigLimb,
321		),
322		x.Limb,
323	)
324	p.rangeChecker.Check(mostSigLimb, 32)
325	p.rangeChecker.Check(leastSigLimb, 32)
326
327	// If the most significant bits are all 1, then we need to check that the least significant bits are all zero
328	// in order for element to be less than the Goldilock's modulus.
329	// Otherwise, we don't need to do any checks, since we already know that the element is less than the Goldilocks modulus.
330	shouldCheck := p.api.IsZero(p.api.Sub(mostSigLimb, uint64(math.Pow(2, 32))-1))
331	p.api.AssertIsEqual(
332		p.api.Select(
333			shouldCheck,
334			leastSigLimb,
335			frontend.Variable(0),
336		),
337		frontend.Variable(0),
338	)
339}
340
341// This function will assert that the field element x is less than 2^maxNbBits.
342func (p *Chip) RangeCheckWithMaxBits(x Variable, maxNbBits uint64) {
343	p.rangeChecker.Check(x.Limb, int(maxNbBits))
344}
345
346func (p *Chip) AssertIsEqual(x, y Variable) {
347	p.api.AssertIsEqual(x.Limb, y.Limb)
348}
349
350// Computes the n'th primitive root of unity for the Goldilocks field.
351func PrimitiveRootOfUnity(nLog uint64) goldilocks.Element {
352	if nLog > TWO_ADICITY {
353		panic("nLog is greater than TWO_ADICITY")
354	}
355	res := goldilocks.NewElement(POWER_OF_TWO_GENERATOR.Uint64())
356	for i := 0; i < int(TWO_ADICITY-nLog); i++ {
357		res.Square(&res)
358	}
359	return res
360}
361
362func TwoAdicSubgroup(nLog uint64) []goldilocks.Element {
363	if nLog > TWO_ADICITY {
364		panic("nLog is greater than GOLDILOCKS_TWO_ADICITY")
365	}
366
367	var res []goldilocks.Element
368	rootOfUnity := PrimitiveRootOfUnity(nLog)
369	res = append(res, goldilocks.NewElement(1))
370
371	for i := 0; i < (1<<nLog)-1; i++ {
372		lastElement := res[len(res)-1]
373		res = append(res, *lastElement.Mul(&lastElement, &rootOfUnity))
374	}
375
376	return res
377}