diff --git a/plonky2_verifier/fri.go b/plonky2_verifier/fri.go index 8b2c269..ba6a77d 100644 --- a/plonky2_verifier/fri.go +++ b/plonky2_verifier/fri.go @@ -98,10 +98,9 @@ func (f *FriChip) hashOrNoop(data []F) Hash { } } -func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits []frontend.Variable, capIndex F, merkleCap MerkleCap, proof *MerkleProof) { +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} - field.PrintHash(f.field, currentDigest) for i, sibling := range proof.Siblings { bit := leafIndexBits[i] @@ -130,11 +129,30 @@ func (f *FriChip) verifyMerkleProofToCapWithCapIndex(leafData []F, leafIndexBits rightHashCompress[3] = rightHash[3] currentDigest = SelectHash(f.field, bit, leftHashCompress, rightHashCompress) - field.PrintHash(f.field, currentDigest) } + + // We assume that the cap_height is 4. Create two levels of the Lookup2 circuit + if len(capIndexBits) != 4 || len(merkleCap) != 16 { + panic("capIndexBits length should be 4 and the merkleCap length should be 16") + } + + const NUM_LEAF_LOOKUPS = 4 + var leafLookups [NUM_LEAF_LOOKUPS]Hash + // First create the "leaf" lookup2 circuits + // 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], + 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) } -func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap, capIndex F) { +func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriInitialTreeProof, initialMerkleCaps []MerkleCap, capIndexBits []frontend.Variable) { if len(proof.EvalsProofs) != len(initialMerkleCaps) { panic("length of eval proofs in fri proof should equal length of initial merkle caps") } @@ -143,7 +161,7 @@ func (f *FriChip) verifyInitialProof(xIndexBits []frontend.Variable, proof *FriI evals := proof.EvalsProofs[i].Elements merkleProof := proof.EvalsProofs[i].MerkleProof cap := initialMerkleCaps[i] - f.verifyMerkleProofToCapWithCapIndex(evals, xIndexBits, capIndex, cap, &merkleProof) + f.verifyMerkleProofToCapWithCapIndex(evals, xIndexBits, capIndexBits, cap, &merkleProof) } } @@ -182,9 +200,9 @@ func (f *FriChip) verifyQueryRound( ) { f.assertNoncanonicalIndicesOK() xIndexBits := f.qe.field.ToBinary(xIndex, int(nLog)) - capIndex := f.qe.field.FromBinary(xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):]...).(F) + capIndexBits := xIndexBits[len(xIndexBits)-int(f.friParams.Config.CapHeight):] - f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndex) + f.verifyInitialProof(xIndexBits, &roundProof.InitialTreesProof, initialMerkleCaps, capIndexBits) } func (f *FriChip) VerifyFriProof( diff --git a/plonky2_verifier/utils.go b/plonky2_verifier/utils.go index 606b8ac..0efbebe 100644 --- a/plonky2_verifier/utils.go +++ b/plonky2_verifier/utils.go @@ -41,3 +41,19 @@ func SelectHash(fieldAPI frontend.API, bit frontend.Variable, leftHash, rightHas return returnHash } + +func Lookup2Hash(fieldAPI frontend.API, b0 frontend.Variable, b1 frontend.Variable, h0, h1, h2, h3 Hash) Hash { + var returnHash Hash + + for i := 0; i < 4; i++ { + returnHash[i] = fieldAPI.Lookup2(b0, b1, h0[i], h1[i], h2[i], h3[i]).(F) + } + + return returnHash +} + +func AssertIsEqualHash(fieldAPI frontend.API, h1, h2 Hash) { + for i := 0; i < 4; i++ { + fieldAPI.AssertIsEqual(h1[0], h2[0]) + } +}