diff --git a/field/field.go b/field/field.go index 89250df..5db5859 100644 --- a/field/field.go +++ b/field/field.go @@ -1,10 +1,8 @@ package field import ( - "fmt" - "math/big" - "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/field/goldilocks" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/emulated" ) @@ -32,15 +30,22 @@ func NewFieldAPI(api frontend.API) frontend.API { return field } -var r EmulatedField +var ONE_F = NewFieldElement(1) +var ZERO_F = NewFieldElement(0) -func EmulatedFieldModulus() *big.Int { - return r.Modulus() -} +var GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR = goldilocks.NewElement(7) +var GOLDILOCKS_TWO_ADICITY = uint64(32) +var GOLDILOCKS_POWER_OF_TWO_GENERATOR = goldilocks.NewElement(1753635133440165772) -func PrintHash(f frontend.API, h Hash) { - for i := 0; i < 4; i++ { - fmt.Println("Hash Limb", i) - f.Println(h[i]) +func GoldilocksPrimitiveRootOfUnity(nLog uint64) goldilocks.Element { + if nLog > GOLDILOCKS_TWO_ADICITY { + panic("nLog is greater than GOLDILOCKS_TWO_ADICITY") } + + res := goldilocks.NewElement(GOLDILOCKS_POWER_OF_TWO_GENERATOR.Uint64()) + for i := 0; i < int(GOLDILOCKS_TWO_ADICITY-nLog); i++ { + res.Square(&res) + } + + return res } diff --git a/plonky2_verifier/fri.go b/plonky2_verifier/fri.go index ba6a77d..7a9b3b2 100644 --- a/plonky2_verifier/fri.go +++ b/plonky2_verifier/fri.go @@ -6,7 +6,9 @@ import ( . "gnark-ed25519/field" "gnark-ed25519/poseidon" "math" + "math/big" + "github.com/consensys/gnark-crypto/field/goldilocks" "github.com/consensys/gnark/frontend" ) @@ -31,9 +33,9 @@ func (c *OpeningSet) ToFriOpenings() FriOpenings { } type FriChip struct { - api frontend.API - field frontend.API - qe *QuadraticExtensionAPI + api frontend.API + fieldAPI frontend.API + qeAPI *QuadraticExtensionAPI poseidonChip *poseidon.PoseidonChip @@ -41,11 +43,11 @@ type FriChip struct { verifierOnlyCircuitData *VerifierOnlyCircuitData } -func NewFriChip(api frontend.API, field frontend.API, qe *QuadraticExtensionAPI, poseidonChip *poseidon.PoseidonChip, friParams *FriParams) *FriChip { +func NewFriChip(api frontend.API, fieldAPI frontend.API, qeAPI *QuadraticExtensionAPI, poseidonChip *poseidon.PoseidonChip, friParams *FriParams) *FriChip { return &FriChip{ api: api, - field: field, - qe: qe, + fieldAPI: fieldAPI, + qeAPI: qeAPI, poseidonChip: poseidonChip, friParams: friParams, } @@ -56,7 +58,7 @@ func (f *FriChip) assertLeadingZeros(powWitness F, friConfig FriConfig) { // Note that this is assuming that the Goldilocks field is being used. Specfically that the // field is 64 bits long maxPowWitness := uint64(math.Pow(2, float64(64-friConfig.ProofOfWorkBits))) - 1 - f.field.AssertIsLessOrEqual(powWitness, field.NewFieldElement(maxPowWitness)) + f.fieldAPI.AssertIsLessOrEqual(powWitness, field.NewFieldElement(maxPowWitness)) } func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExtension) []QuadraticExtension { @@ -65,7 +67,7 @@ func (f *FriChip) fromOpeningsAndAlpha(openings *FriOpenings, alpha QuadraticExt reducedOpenings := make([]QuadraticExtension, 0, 2) for _, batch := range openings.Batches { - reducedOpenings = append(reducedOpenings, reduceWithPowers(f.qe, batch.values, alpha)) + reducedOpenings = append(reducedOpenings, reduceWithPowers(f.qeAPI, batch.values, alpha)) } return reducedOpenings @@ -79,7 +81,7 @@ func (f *FriChip) hashOrNoop(data []F) Hash { elements[i] = inputElement } for i := len(data); i < 4; i++ { - elements[i] = f.qe.ZERO_F + elements[i] = field.ZERO_F } return elements @@ -100,7 +102,7 @@ func (f *FriChip) hashOrNoop(data []F) Hash { func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits []frontend.Variable, capIndexBits []frontend.Variable, merkleCap MerkleCap, proof *MerkleProof) { currentDigest := f.hashOrNoop(leafData) - fourZeros := [4]F{f.qe.ZERO_F, f.qe.ZERO_F, f.qe.ZERO_F, f.qe.ZERO_F} + fourZeros := [4]F{field.ZERO_F, field.ZERO_F, field.ZERO_F, field.ZERO_F} for i, sibling := range proof.Siblings { bit := leafIndexBits[i] @@ -128,7 +130,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits rightHashCompress[2] = rightHash[2] rightHashCompress[3] = rightHash[3] - currentDigest = SelectHash(f.field, bit, leftHashCompress, rightHashCompress) + currentDigest = SelectHash(f.fieldAPI, bit, leftHashCompress, rightHashCompress) } // We assume that the cap_height is 4. Create two levels of the Lookup2 circuit @@ -142,14 +144,14 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits // The will use the least significant bits of the capIndexBits array for i := 0; i < NUM_LEAF_LOOKUPS; i++ { leafLookups[i] = Lookup2Hash( - f.field, capIndexBits[0], capIndexBits[1], + f.fieldAPI, capIndexBits[0], capIndexBits[1], merkleCap[i*NUM_LEAF_LOOKUPS], merkleCap[i*NUM_LEAF_LOOKUPS+1], merkleCap[i*NUM_LEAF_LOOKUPS+2], merkleCap[i*NUM_LEAF_LOOKUPS+3], ) } // Use the most 2 significant bits of the capIndexBits array for the "root" lookup - merkleCapEntry := Lookup2Hash(f.field, capIndexBits[2], capIndexBits[3], leafLookups[0], leafLookups[1], leafLookups[2], leafLookups[3]) - AssertIsEqualHash(f.field, currentDigest, merkleCapEntry) + merkleCapEntry := Lookup2Hash(f.fieldAPI, capIndexBits[2], capIndexBits[3], leafLookups[0], leafLookups[1], leafLookups[2], leafLookups[3]) + AssertIsEqualHash(f.fieldAPI, currentDigest, merkleCapEntry) } func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap, capIndexBits []frontend.Variable) { @@ -178,9 +180,9 @@ func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriI // / // / Here we compare the probabilities as a sanity check, to verify the claim above. func (f *FriChip) assertNoncanonicalIndicesOK() { - numAmbiguousElems := uint64(math.MaxUint64) - EmulatedFieldModulus().Uint64() + 1 + numAmbiguousElems := uint64(math.MaxUint64) - goldilocks.Modulus().Uint64() + 1 queryError := f.friParams.Config.rate() - pAmbiguous := float64(numAmbiguousElems) / float64(EmulatedFieldModulus().Uint64()) + pAmbiguous := float64(numAmbiguousElems) / float64(goldilocks.Modulus().Uint64()) // TODO: Check that pAmbiguous value is the same as the one in plonky2 verifier if pAmbiguous >= queryError*1e-5 { @@ -199,10 +201,46 @@ func (f *FriChip) verifyQueryRound( roundProof *FriQueryRound, ) { f.assertNoncanonicalIndicesOK() - xIndexBits := f.qe.field.ToBinary(xIndex, int(nLog)) + xIndexBits := f.fieldAPI.ToBinary(xIndex, int(nLog)) capIndexBits := xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):] f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndexBits) + + // Compute x from its index + // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. + // TODO - Make these as global values + g := field.NewFieldElement(field.GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR.Uint64()) + base := field.GoldilocksPrimitiveRootOfUnity(nLog) + + product := ONE_F + // Create a reverse list of xIndexBits + xIndexBitsRev := make([]frontend.Variable, 0) + for i := len(xIndexBits) - 1; i >= 0; i-- { + xIndexBitsRev = append(xIndexBitsRev, xIndexBits[i]) + } + + for i, bit := range xIndexBitsRev { + pow := int64(1 << i) + // If the bit is on, we multiply product by base^pow. + // We can arithmetize this as: + // product *= 1 + bit (base^pow - 1) + // product = (base^pow - 1) product bit + product + basePow := goldilocks.NewElement(0) + basePow.Exp(base, big.NewInt(pow)) + + basePowElement := NewFieldElement(basePow.Uint64() - 1) + + product = f.fieldAPI.Add( + f.fieldAPI.Mul( + basePowElement, + product, + bit, + ), + product, + ).(F) + } + + subgroupX := f.fieldAPI.Mul(g, product).(F) } func (f *FriChip) VerifyFriProof( diff --git a/plonky2_verifier/quadratic_extension.go b/plonky2_verifier/quadratic_extension.go index d7f1a40..1435b9a 100644 --- a/plonky2_verifier/quadratic_extension.go +++ b/plonky2_verifier/quadratic_extension.go @@ -8,7 +8,7 @@ import ( ) type QuadraticExtensionAPI struct { - field frontend.API + fieldAPI frontend.API W F DTH_ROOT F @@ -18,18 +18,17 @@ type QuadraticExtensionAPI struct { ZERO_QE QuadraticExtension } -func NewQuadraticExtensionAPI(field frontend.API, degreeBits uint64) *QuadraticExtensionAPI { +func NewQuadraticExtensionAPI(fieldAPI frontend.API, degreeBits uint64) *QuadraticExtensionAPI { // TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64? return &QuadraticExtensionAPI{ - field: field, + fieldAPI: fieldAPI, W: NewFieldElement(7), DTH_ROOT: NewFieldElement(18446744069414584320), - ZERO_F: NewFieldElement(0), - ONE: QuadraticExtension{NewFieldElement(1), NewFieldElement(0)}, - ZERO_QE: QuadraticExtension{NewFieldElement(0), NewFieldElement(0)}, + ONE: QuadraticExtension{ONE_F, ZERO_F}, + ZERO_QE: QuadraticExtension{ZERO_F, ZERO_F}, } } @@ -38,20 +37,20 @@ func (c *QuadraticExtensionAPI) SquareExtension(a QuadraticExtension) QuadraticE } func (c *QuadraticExtensionAPI) MulExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { - c_0 := c.field.Add(c.field.Mul(a[0], b[0]).(F), c.field.Mul(c.W, a[1], b[1])).(F) - c_1 := c.field.Add(c.field.Mul(a[0], b[1]).(F), c.field.Mul(a[1], b[0])).(F) + c_0 := c.fieldAPI.Add(c.fieldAPI.Mul(a[0], b[0]).(F), c.fieldAPI.Mul(c.W, a[1], b[1])).(F) + c_1 := c.fieldAPI.Add(c.fieldAPI.Mul(a[0], b[1]).(F), c.fieldAPI.Mul(a[1], b[0])).(F) return QuadraticExtension{c_0, c_1} } func (c *QuadraticExtensionAPI) AddExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { - c_0 := c.field.Add(a[0], b[0]).(F) - c_1 := c.field.Add(a[1], b[1]).(F) + c_0 := c.fieldAPI.Add(a[0], b[0]).(F) + c_1 := c.fieldAPI.Add(a[1], b[1]).(F) return QuadraticExtension{c_0, c_1} } func (c *QuadraticExtensionAPI) SubExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { - c_0 := c.field.Sub(a[0], b[0]).(F) - c_1 := c.field.Sub(a[1], b[1]).(F) + c_0 := c.fieldAPI.Sub(a[0], b[0]).(F) + c_1 := c.fieldAPI.Sub(a[1], b[1]).(F) return QuadraticExtension{c_0, c_1} } @@ -63,19 +62,19 @@ func (c *QuadraticExtensionAPI) DivExtension(a QuadraticExtension, b QuadraticEx // inverse and assert that a_inverse * a = 1. Should reduce # of constraints. func (c *QuadraticExtensionAPI) InverseExtension(a QuadraticExtension) QuadraticExtension { // First assert that a doesn't have 0 value coefficients - a0_is_zero := c.field.IsZero(a[0]) - a1_is_zero := c.field.IsZero(a[1]) + a0_is_zero := c.fieldAPI.IsZero(a[0]) + a1_is_zero := c.fieldAPI.IsZero(a[1]) // assert that a0_is_zero OR a1_is_zero == false - c.field.AssertIsEqual(c.field.Mul(a0_is_zero, a1_is_zero).(F), c.ZERO_F) + c.fieldAPI.AssertIsEqual(c.fieldAPI.Mul(a0_is_zero, a1_is_zero).(F), c.ZERO_F) - a_pow_r_minus_1 := QuadraticExtension{a[0], c.field.Mul(a[1], c.DTH_ROOT).(F)} + a_pow_r_minus_1 := QuadraticExtension{a[0], c.fieldAPI.Mul(a[1], c.DTH_ROOT).(F)} a_pow_r := c.MulExtension(a_pow_r_minus_1, a) - return c.ScalarMulExtension(a_pow_r_minus_1, c.field.Inverse(a_pow_r[0]).(F)) + return c.ScalarMulExtension(a_pow_r_minus_1, c.fieldAPI.Inverse(a_pow_r[0]).(F)) } func (c *QuadraticExtensionAPI) ScalarMulExtension(a QuadraticExtension, scalar F) QuadraticExtension { - return QuadraticExtension{c.field.Mul(a[0], scalar).(F), c.field.Mul(a[1], scalar).(F)} + return QuadraticExtension{c.fieldAPI.Mul(a[0], scalar).(F), c.fieldAPI.Mul(a[1], scalar).(F)} } func (c *QuadraticExtensionAPI) FieldToQE(a F) QuadraticExtension { @@ -84,8 +83,8 @@ func (c *QuadraticExtensionAPI) FieldToQE(a F) QuadraticExtension { func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) { fmt.Print("Degree 0 coefficient") - c.field.Println(a[0]) + c.fieldAPI.Println(a[0]) fmt.Print("Degree 1 coefficient") - c.field.Println(a[1]) + c.fieldAPI.Println(a[1]) } diff --git a/plonky2_verifier/utils.go b/plonky2_verifier/utils.go index 0efbebe..226a97e 100644 --- a/plonky2_verifier/utils.go +++ b/plonky2_verifier/utils.go @@ -57,3 +57,10 @@ func AssertIsEqualHash(fieldAPI frontend.API, h1, h2 Hash) { fieldAPI.AssertIsEqual(h1[0], h2[0]) } } + +func PrintHash(f frontend.API, h Hash) { + for i := 0; i < 4; i++ { + fmt.Println("Hash Limb", i) + f.Println(h[i]) + } +}