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/backend/hint"
 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	hint.Register(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, err := api.Compiler().NewHint(bits.NBits, 64, x)
104	if err != nil {
105		panic(err)
106	}
107
108	// Those bits should compose back to x
109	reconstructedX := frontend.Variable(0)
110	c := uint64(1)
111	for i := 0; i < 64; i++ {
112		reconstructedX = api.Add(reconstructedX, api.Mul(bits[i], c))
113		c = c << 1
114		api.AssertIsBoolean(bits[i])
115	}
116	api.AssertIsEqual(x, reconstructedX)
117
118	mostSigBits32Sum := frontend.Variable(0)
119	for i := 32; i < 64; i++ {
120		mostSigBits32Sum = api.Add(mostSigBits32Sum, bits[i])
121	}
122
123	leastSigBits32Sum := frontend.Variable(0)
124	for i := 0; i < 32; i++ {
125		leastSigBits32Sum = api.Add(leastSigBits32Sum, bits[i])
126	}
127
128	// 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
129	// So in that case, we don't need to do any more checks.
130	// If mostSigBits32Sum == 32, then we need to check that x == 2^64 - 2^32 (max GL value)
131	shouldCheck := api.IsZero(api.Sub(mostSigBits32Sum, 32))
132	api.AssertIsEqual(
133		api.Select(
134			shouldCheck,
135			leastSigBits32Sum,
136			frontend.Variable(0),
137		),
138		frontend.Variable(0),
139	)
140}
141
142// Calculates operands[0] * operands[1] + operands[2]
143// This function assumes that all operands are within goldilocks, and will panic otherwise
144// It will ensure that the result is within goldilocks
145func GoldilocksMulAdd(api frontend.API, operand1, operand2, operand3 frontend.Variable) frontend.Variable {
146	result, err := api.Compiler().NewHint(GoldilocksMulAddHint, 2, operand1, operand2, operand3)
147	if err != nil {
148		panic(err)
149	}
150
151	quotient := result[0]
152	remainder := result[1]
153
154	// Verify the calculated value
155	lhs := api.Mul(operand1, operand2)
156	lhs = api.Add(lhs, operand3)
157	rhs := api.Add(api.Mul(quotient, GOLDILOCKS_MODULUS), remainder)
158	api.AssertIsEqual(lhs, rhs)
159
160	GoldilocksRangeCheck(api, quotient)
161	GoldilocksRangeCheck(api, remainder)
162
163	return remainder
164}
165
166func GoldilocksMulAddHint(_ *big.Int, inputs []*big.Int, results []*big.Int) error {
167	if len(inputs) != 3 {
168		panic("GoldilocksMulAddHint expects 3 input operands")
169	}
170
171	for _, operand := range inputs {
172		if operand.Cmp(GOLDILOCKS_MODULUS) >= 0 {
173			panic(fmt.Sprintf("%s is not in the field", operand.String()))
174		}
175	}
176
177	product := new(big.Int).Mul(inputs[0], inputs[1])
178	sum := new(big.Int).Add(product, inputs[2])
179	quotient := new(big.Int).Div(sum, GOLDILOCKS_MODULUS)
180	remainder := new(big.Int).Rem(sum, GOLDILOCKS_MODULUS)
181
182	results[0] = quotient
183	results[1] = remainder
184
185	return nil
186}