arnaucube
arnaucube/gnark-plonky2-verifierPublic Self-hosted repository
gnark-plonky2-verifier/field/goldilocks.go
field/goldilocks.goRaw
  1package field
  2
  3import (
  4	"fmt"
  5	"math/big"
  6
  7	"github.com/consensys/gnark-crypto/ecc"
  8	"github.com/consensys/gnark-crypto/field/goldilocks"
  9	"github.com/consensys/gnark/constraint/solver"
 10	"github.com/consensys/gnark/frontend"
 11	"github.com/consensys/gnark/std/math/bits"
 12	"github.com/consensys/gnark/std/math/emulated"
 13)
 14
 15type EmulatedField = emulated.Goldilocks
 16type F = *emulated.Element[EmulatedField]
 17type FieldAPI = *emulated.Field[emulated.Goldilocks]
 18
 19var TEST_CURVE = ecc.BN254
 20
 21func NewFieldAPI(api frontend.API) FieldAPI {
 22	fieldAPI, err := emulated.NewField[EmulatedField](api)
 23	if err != nil {
 24		panic(err)
 25	}
 26	return fieldAPI
 27}
 28
 29func NewFieldConst(x uint64) F {
 30	val := emulated.ValueOf[EmulatedField](x)
 31	return &val
 32}
 33
 34func NewFieldConstFromString(x string) F {
 35	val := emulated.ValueOf[EmulatedField](x)
 36	return &val
 37}
 38
 39var ONE_F = NewFieldConst(1)
 40var ZERO_F = NewFieldConst(0)
 41var NEG_ONE_F = NewFieldConst(EmulatedField{}.Modulus().Uint64() - 1)
 42
 43var GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR = goldilocks.NewElement(7)
 44var GOLDILOCKS_TWO_ADICITY = uint64(32)
 45var GOLDILOCKS_POWER_OF_TWO_GENERATOR = goldilocks.NewElement(1753635133440165772)
 46var GOLDILOCKS_MODULUS = EmulatedField{}.Modulus()
 47
 48func GoldilocksPrimitiveRootOfUnity(nLog uint64) goldilocks.Element {
 49	if nLog > GOLDILOCKS_TWO_ADICITY {
 50		panic("nLog is greater than GOLDILOCKS_TWO_ADICITY")
 51	}
 52
 53	res := goldilocks.NewElement(GOLDILOCKS_POWER_OF_TWO_GENERATOR.Uint64())
 54	for i := 0; i < int(GOLDILOCKS_TWO_ADICITY-nLog); i++ {
 55		res.Square(&res)
 56	}
 57
 58	return res
 59}
 60
 61func TwoAdicSubgroup(nLog uint64) []goldilocks.Element {
 62	if nLog > GOLDILOCKS_TWO_ADICITY {
 63		panic("nLog is greater than GOLDILOCKS_TWO_ADICITY")
 64	}
 65
 66	var res []goldilocks.Element
 67	rootOfUnity := GoldilocksPrimitiveRootOfUnity(nLog)
 68	res = append(res, goldilocks.NewElement(1))
 69
 70	for i := 0; i < (1 << nLog); i++ {
 71		lastElement := res[len(res)-1]
 72		res = append(res, *lastElement.Mul(&lastElement, &rootOfUnity))
 73	}
 74
 75	return res
 76}
 77
 78func IsZero(api frontend.API, fieldAPI *emulated.Field[emulated.Goldilocks], x F) frontend.Variable {
 79	reduced := fieldAPI.Reduce(x)
 80	limbs := reduced.Limbs
 81
 82	isZero := api.IsZero(limbs[0])
 83	for i := 1; i < len(limbs); i++ {
 84		isZero = api.Mul(isZero, api.IsZero(limbs[i]))
 85	}
 86
 87	return isZero
 88
 89}
 90
 91func init() {
 92	// register hints
 93	solver.RegisterHint(GoldilocksMulAddHint)
 94}
 95
 96func GoldilocksRangeCheck(api frontend.API, x frontend.Variable) {
 97	// Goldilocks' modulus is 2^64 - 2^32 + 1,
 98	// which is "1111111111111111111111111111111100000000000000000000000000000001' in big endian binary
 99	// This function will first verify that x is at most 64 bits wide.
100	// Then it checks that if the bits[0:31] (in big-endian) are all 1, then bits[32:64] are all zero
101
102	// First decompose x into 64 bits.  The bits will be in little-endian order.
103	bits := bits.ToBinary(api, x, bits.WithNbDigits(64))
104
105	// Those bits should compose back to x
106	reconstructedX := frontend.Variable(0)
107	c := uint64(1)
108	for i := 0; i < 64; i++ {
109		reconstructedX = api.Add(reconstructedX, api.Mul(bits[i], c))
110		c = c << 1
111		api.AssertIsBoolean(bits[i])
112	}
113	api.AssertIsEqual(x, reconstructedX)
114
115	mostSigBits32Sum := frontend.Variable(0)
116	for i := 32; i < 64; i++ {
117		mostSigBits32Sum = api.Add(mostSigBits32Sum, bits[i])
118	}
119
120	leastSigBits32Sum := frontend.Variable(0)
121	for i := 0; i < 32; i++ {
122		leastSigBits32Sum = api.Add(leastSigBits32Sum, bits[i])
123	}
124
125	// If mostSigBits32Sum < 32, then we know that x < (2^63 + ... + 2^32 + 0 * 2^31 + ... + 0 * 2^0), which equals to 2^64 - 2^32
126	// So in that case, we don't need to do any more checks.
127	// If mostSigBits32Sum == 32, then we need to check that x == 2^64 - 2^32 (max GL value)
128	shouldCheck := api.IsZero(api.Sub(mostSigBits32Sum, 32))
129	api.AssertIsEqual(
130		api.Select(
131			shouldCheck,
132			leastSigBits32Sum,
133			frontend.Variable(0),
134		),
135		frontend.Variable(0),
136	)
137}
138
139// Calculates operands[0] * operands[1] + operands[2]
140// This function assumes that all operands are within goldilocks, and will panic otherwise
141// It will ensure that the result is within goldilocks
142func GoldilocksMulAdd(api frontend.API, operand1, operand2, operand3 frontend.Variable) frontend.Variable {
143	result, err := api.Compiler().NewHint(GoldilocksMulAddHint, 2, operand1, operand2, operand3)
144	if err != nil {
145		panic(err)
146	}
147
148	quotient := result[0]
149	remainder := result[1]
150
151	// Verify the calculated value
152	lhs := api.Mul(operand1, operand2)
153	lhs = api.Add(lhs, operand3)
154	rhs := api.Add(api.Mul(quotient, GOLDILOCKS_MODULUS), remainder)
155	api.AssertIsEqual(lhs, rhs)
156
157	GoldilocksRangeCheck(api, quotient)
158	GoldilocksRangeCheck(api, remainder)
159
160	return remainder
161}
162
163func GoldilocksMulAddHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
164	if len(inputs) != 3 {
165		panic("GoldilocksMulAddHint expects 3 input operands")
166	}
167
168	for _, operand := range inputs {
169		if operand.Cmp(GOLDILOCKS_MODULUS) >= 0 {
170			panic(fmt.Sprintf("%s is not in the field", operand.String()))
171		}
172	}
173
174	product := new(big.Int).Mul(inputs[0], inputs[1])
175	sum := new(big.Int).Add(product, inputs[2])
176	quotient := new(big.Int).Div(sum, GOLDILOCKS_MODULUS)
177	remainder := new(big.Int).Rem(sum, GOLDILOCKS_MODULUS)
178
179	results[0] = quotient
180	results[1] = remainder
181
182	return nil
183}