mirror of
https://github.com/arnaucube/gnark-plonky2-verifier.git
synced 2026-01-12 09:01:32 +01:00
calculated subgroupX
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
package field
|
package field
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc"
|
"github.com/consensys/gnark-crypto/ecc"
|
||||||
|
"github.com/consensys/gnark-crypto/field/goldilocks"
|
||||||
"github.com/consensys/gnark/frontend"
|
"github.com/consensys/gnark/frontend"
|
||||||
"github.com/consensys/gnark/std/math/emulated"
|
"github.com/consensys/gnark/std/math/emulated"
|
||||||
)
|
)
|
||||||
@@ -32,15 +30,22 @@ func NewFieldAPI(api frontend.API) frontend.API {
|
|||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
var r EmulatedField
|
var ONE_F = NewFieldElement(1)
|
||||||
|
var ZERO_F = NewFieldElement(0)
|
||||||
|
|
||||||
func EmulatedFieldModulus() *big.Int {
|
var GOLDILOCKS_MULTIPLICATIVE_GROUP_GENERATOR = goldilocks.NewElement(7)
|
||||||
return r.Modulus()
|
var GOLDILOCKS_TWO_ADICITY = uint64(32)
|
||||||
}
|
var GOLDILOCKS_POWER_OF_TWO_GENERATOR = goldilocks.NewElement(1753635133440165772)
|
||||||
|
|
||||||
func PrintHash(f frontend.API, h Hash) {
|
func GoldilocksPrimitiveRootOfUnity(nLog uint64) goldilocks.Element {
|
||||||
for i := 0; i < 4; i++ {
|
if nLog > GOLDILOCKS_TWO_ADICITY {
|
||||||
fmt.Println("Hash Limb", i)
|
panic("nLog is greater than GOLDILOCKS_TWO_ADICITY")
|
||||||
f.Println(h[i])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := goldilocks.NewElement(GOLDILOCKS_POWER_OF_TWO_GENERATOR.Uint64())
|
||||||
|
for i := 0; i < int(GOLDILOCKS_TWO_ADICITY-nLog); i++ {
|
||||||
|
res.Square(&res)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
. "gnark-ed25519/field"
|
. "gnark-ed25519/field"
|
||||||
"gnark-ed25519/poseidon"
|
"gnark-ed25519/poseidon"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/consensys/gnark-crypto/field/goldilocks"
|
||||||
"github.com/consensys/gnark/frontend"
|
"github.com/consensys/gnark/frontend"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,9 +33,9 @@ func (c *OpeningSet) ToFriOpenings() FriOpenings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FriChip struct {
|
type FriChip struct {
|
||||||
api frontend.API
|
api frontend.API
|
||||||
field frontend.API
|
fieldAPI frontend.API
|
||||||
qe *QuadraticExtensionAPI
|
qeAPI *QuadraticExtensionAPI
|
||||||
|
|
||||||
poseidonChip *poseidon.PoseidonChip
|
poseidonChip *poseidon.PoseidonChip
|
||||||
|
|
||||||
@@ -41,11 +43,11 @@ type FriChip struct {
|
|||||||
verifierOnlyCircuitData *VerifierOnlyCircuitData
|
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{
|
return &FriChip{
|
||||||
api: api,
|
api: api,
|
||||||
field: field,
|
fieldAPI: fieldAPI,
|
||||||
qe: qe,
|
qeAPI: qeAPI,
|
||||||
poseidonChip: poseidonChip,
|
poseidonChip: poseidonChip,
|
||||||
friParams: friParams,
|
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
|
// Note that this is assuming that the Goldilocks field is being used. Specfically that the
|
||||||
// field is 64 bits long
|
// field is 64 bits long
|
||||||
maxPowWitness := uint64(math.Pow(2, float64(64-friConfig.ProofOfWorkBits))) - 1
|
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 {
|
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)
|
reducedOpenings := make([]QuadraticExtension, 0, 2)
|
||||||
for _, batch := range openings.Batches {
|
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
|
return reducedOpenings
|
||||||
@@ -79,7 +81,7 @@ func (f *FriChip) hashOrNoop(data []F) Hash {
|
|||||||
elements[i] = inputElement
|
elements[i] = inputElement
|
||||||
}
|
}
|
||||||
for i := len(data); i < 4; i++ {
|
for i := len(data); i < 4; i++ {
|
||||||
elements[i] = f.qe.ZERO_F
|
elements[i] = field.ZERO_F
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements
|
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) {
|
func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits []frontend.Variable, capIndexBits []frontend.Variable, merkleCap MerkleCap, proof *MerkleProof) {
|
||||||
currentDigest := f.hashOrNoop(leafData)
|
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 {
|
for i, sibling := range proof.Siblings {
|
||||||
bit := leafIndexBits[i]
|
bit := leafIndexBits[i]
|
||||||
|
|
||||||
@@ -128,7 +130,7 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits
|
|||||||
rightHashCompress[2] = rightHash[2]
|
rightHashCompress[2] = rightHash[2]
|
||||||
rightHashCompress[3] = rightHash[3]
|
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
|
// 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
|
// The will use the least significant bits of the capIndexBits array
|
||||||
for i := 0; i < NUM_LEAF_LOOKUPS; i++ {
|
for i := 0; i < NUM_LEAF_LOOKUPS; i++ {
|
||||||
leafLookups[i] = Lookup2Hash(
|
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],
|
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
|
// 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])
|
merkleCapEntry := Lookup2Hash(f.fieldAPI, capIndexBits[2], capIndexBits[3], leafLookups[0], leafLookups[1], leafLookups[2], leafLookups[3])
|
||||||
AssertIsEqualHash(f.field, currentDigest, merkleCapEntry)
|
AssertIsEqualHash(f.fieldAPI, currentDigest, merkleCapEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap, capIndexBits []frontend.Variable) {
|
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.
|
// / Here we compare the probabilities as a sanity check, to verify the claim above.
|
||||||
func (f *FriChip) assertNoncanonicalIndicesOK() {
|
func (f *FriChip) assertNoncanonicalIndicesOK() {
|
||||||
numAmbiguousElems := uint64(math.MaxUint64) - EmulatedFieldModulus().Uint64() + 1
|
numAmbiguousElems := uint64(math.MaxUint64) - goldilocks.Modulus().Uint64() + 1
|
||||||
queryError := f.friParams.Config.rate()
|
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
|
// TODO: Check that pAmbiguous value is the same as the one in plonky2 verifier
|
||||||
if pAmbiguous >= queryError*1e-5 {
|
if pAmbiguous >= queryError*1e-5 {
|
||||||
@@ -199,10 +201,46 @@ func (f *FriChip) verifyQueryRound(
|
|||||||
roundProof *FriQueryRound,
|
roundProof *FriQueryRound,
|
||||||
) {
|
) {
|
||||||
f.assertNoncanonicalIndicesOK()
|
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):]
|
capIndexBits := xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):]
|
||||||
|
|
||||||
f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndexBits)
|
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(
|
func (f *FriChip) VerifyFriProof(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type QuadraticExtensionAPI struct {
|
type QuadraticExtensionAPI struct {
|
||||||
field frontend.API
|
fieldAPI frontend.API
|
||||||
|
|
||||||
W F
|
W F
|
||||||
DTH_ROOT F
|
DTH_ROOT F
|
||||||
@@ -18,18 +18,17 @@ type QuadraticExtensionAPI struct {
|
|||||||
ZERO_QE QuadraticExtension
|
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?
|
// TODO: Should degreeBits be verified that it fits within the field and that degree is within uint64?
|
||||||
|
|
||||||
return &QuadraticExtensionAPI{
|
return &QuadraticExtensionAPI{
|
||||||
field: field,
|
fieldAPI: fieldAPI,
|
||||||
|
|
||||||
W: NewFieldElement(7),
|
W: NewFieldElement(7),
|
||||||
DTH_ROOT: NewFieldElement(18446744069414584320),
|
DTH_ROOT: NewFieldElement(18446744069414584320),
|
||||||
ZERO_F: NewFieldElement(0),
|
|
||||||
|
|
||||||
ONE: QuadraticExtension{NewFieldElement(1), NewFieldElement(0)},
|
ONE: QuadraticExtension{ONE_F, ZERO_F},
|
||||||
ZERO_QE: QuadraticExtension{NewFieldElement(0), NewFieldElement(0)},
|
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 {
|
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_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.field.Add(c.field.Mul(a[0], b[1]).(F), c.field.Mul(a[1], b[0])).(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}
|
return QuadraticExtension{c_0, c_1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QuadraticExtensionAPI) AddExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension {
|
func (c *QuadraticExtensionAPI) AddExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension {
|
||||||
c_0 := c.field.Add(a[0], b[0]).(F)
|
c_0 := c.fieldAPI.Add(a[0], b[0]).(F)
|
||||||
c_1 := c.field.Add(a[1], b[1]).(F)
|
c_1 := c.fieldAPI.Add(a[1], b[1]).(F)
|
||||||
return QuadraticExtension{c_0, c_1}
|
return QuadraticExtension{c_0, c_1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *QuadraticExtensionAPI) SubExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension {
|
func (c *QuadraticExtensionAPI) SubExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension {
|
||||||
c_0 := c.field.Sub(a[0], b[0]).(F)
|
c_0 := c.fieldAPI.Sub(a[0], b[0]).(F)
|
||||||
c_1 := c.field.Sub(a[1], b[1]).(F)
|
c_1 := c.fieldAPI.Sub(a[1], b[1]).(F)
|
||||||
return QuadraticExtension{c_0, c_1}
|
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.
|
// inverse and assert that a_inverse * a = 1. Should reduce # of constraints.
|
||||||
func (c *QuadraticExtensionAPI) InverseExtension(a QuadraticExtension) QuadraticExtension {
|
func (c *QuadraticExtensionAPI) InverseExtension(a QuadraticExtension) QuadraticExtension {
|
||||||
// First assert that a doesn't have 0 value coefficients
|
// First assert that a doesn't have 0 value coefficients
|
||||||
a0_is_zero := c.field.IsZero(a[0])
|
a0_is_zero := c.fieldAPI.IsZero(a[0])
|
||||||
a1_is_zero := c.field.IsZero(a[1])
|
a1_is_zero := c.fieldAPI.IsZero(a[1])
|
||||||
|
|
||||||
// assert that a0_is_zero OR a1_is_zero == false
|
// 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)
|
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 {
|
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 {
|
func (c *QuadraticExtensionAPI) FieldToQE(a F) QuadraticExtension {
|
||||||
@@ -84,8 +83,8 @@ func (c *QuadraticExtensionAPI) FieldToQE(a F) QuadraticExtension {
|
|||||||
|
|
||||||
func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) {
|
func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) {
|
||||||
fmt.Print("Degree 0 coefficient")
|
fmt.Print("Degree 0 coefficient")
|
||||||
c.field.Println(a[0])
|
c.fieldAPI.Println(a[0])
|
||||||
|
|
||||||
fmt.Print("Degree 1 coefficient")
|
fmt.Print("Degree 1 coefficient")
|
||||||
c.field.Println(a[1])
|
c.fieldAPI.Println(a[1])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,3 +57,10 @@ func AssertIsEqualHash(fieldAPI frontend.API, h1, h2 Hash) {
|
|||||||
fieldAPI.AssertIsEqual(h1[0], h2[0])
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user