chore(kzg): Additional tests for KZG commitments (#13758)

* add a test explaining kzgRootIndex

* minor

* minor
This commit is contained in:
Chanh Le 2024-03-19 05:08:02 -04:00 committed by GitHub
parent 357211b7d9
commit d779e65d4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 85 additions and 14 deletions

View File

@ -15,7 +15,8 @@ const (
bodyLength = 12 // The number of elements in the BeaconBlockBody Container bodyLength = 12 // The number of elements in the BeaconBlockBody Container
logBodyLength = 4 // The log 2 of bodyLength logBodyLength = 4 // The log 2 of bodyLength
kzgPosition = 11 // The index of the KZG commitment list in the Body kzgPosition = 11 // The index of the KZG commitment list in the Body
KZGOffset = 54 * field_params.MaxBlobCommitmentsPerBlock kzgRootIndex = 54 // The Merkle index of the KZG commitment list's root in the Body's Merkle tree
KZGOffset = kzgRootIndex * field_params.MaxBlobCommitmentsPerBlock
) )
var ( var (
@ -37,9 +38,7 @@ func VerifyKZGInclusionProof(blob ROBlob) error {
if len(root) != field_params.RootLength { if len(root) != field_params.RootLength {
return errInvalidBodyRoot return errInvalidBodyRoot
} }
chunks := make([][32]byte, 2) chunks := makeChunk(blob.KzgCommitment)
copy(chunks[0][:], blob.KzgCommitment)
copy(chunks[1][:], blob.KzgCommitment[field_params.RootLength:])
gohashtree.HashChunks(chunks, chunks) gohashtree.HashChunks(chunks, chunks)
verified := trie.VerifyMerkleProof(root, chunks[0][:], blob.Index+KZGOffset, blob.CommitmentInclusionProof) verified := trie.VerifyMerkleProof(root, chunks[0][:], blob.Index+KZGOffset, blob.CommitmentInclusionProof)
if !verified { if !verified {
@ -85,15 +84,21 @@ func MerkleProofKZGCommitment(body interfaces.ReadOnlyBeaconBlockBody, index int
func leavesFromCommitments(commitments [][]byte) [][]byte { func leavesFromCommitments(commitments [][]byte) [][]byte {
leaves := make([][]byte, len(commitments)) leaves := make([][]byte, len(commitments))
for i, kzg := range commitments { for i, kzg := range commitments {
chunk := make([][32]byte, 2) chunk := makeChunk(kzg)
copy(chunk[0][:], kzg)
copy(chunk[1][:], kzg[field_params.RootLength:])
gohashtree.HashChunks(chunk, chunk) gohashtree.HashChunks(chunk, chunk)
leaves[i] = chunk[0][:] leaves[i] = chunk[0][:]
} }
return leaves return leaves
} }
// makeChunk constructs a chunk from a KZG commitment.
func makeChunk(commitment []byte) [][32]byte {
chunk := make([][32]byte, 2)
copy(chunk[0][:], commitment)
copy(chunk[1][:], commitment[field_params.RootLength:])
return chunk
}
// bodyProof returns the Merkle proof of the subtree up to the root of the KZG // bodyProof returns the Merkle proof of the subtree up to the root of the KZG
// commitment list. // commitment list.
func bodyProof(commitments [][]byte, index int) ([][]byte, error) { func bodyProof(commitments [][]byte, index int) ([][]byte, error) {

View File

@ -1,7 +1,8 @@
package blocks package blocks
import ( import (
"math/rand" "crypto/rand"
"errors"
"testing" "testing"
"github.com/prysmaticlabs/gohashtree" "github.com/prysmaticlabs/gohashtree"
@ -74,14 +75,79 @@ func Test_MerkleProofKZGCommitment(t *testing.T) {
proof, err := MerkleProofKZGCommitment(body, index) proof, err := MerkleProofKZGCommitment(body, index)
require.NoError(t, err) require.NoError(t, err)
chunk := make([][32]byte, 2) // Test the logic of topProof in MerkleProofKZGCommitment.
copy(chunk[0][:], kzgs[index]) commitmentsRoot, err := getBlobKzgCommitmentsRoot(kzgs)
copy(chunk[1][:], kzgs[index][32:]) require.NoError(t, err)
gohashtree.HashChunks(chunk, chunk) bodyMembersRoots, err := topLevelRoots(body)
require.NoError(t, err, "Failed to get top level roots")
bodySparse, err := trie.GenerateTrieFromItems(
bodyMembersRoots,
logBodyLength,
)
require.NoError(t, err, "Failed to generate trie from member roots")
require.Equal(t, bodyLength, bodySparse.NumOfItems())
topProof, err := bodySparse.MerkleProof(kzgPosition)
require.NoError(t, err, "Failed to generate Merkle proof")
require.DeepEqual(t,
topProof[:len(topProof)-1],
proof[fieldparams.LogMaxBlobCommitments+1:],
)
root, err := body.HashTreeRoot() root, err := body.HashTreeRoot()
require.NoError(t, err) require.NoError(t, err)
kzgOffset := 54 * fieldparams.MaxBlobCommitmentsPerBlock // Partially verify if the commitments root is in the body root.
require.Equal(t, true, trie.VerifyMerkleProof(root[:], chunk[0][:], uint64(index+kzgOffset), proof)) // Proof of the commitment length is not needed.
require.Equal(t, true, trie.VerifyMerkleProof(root[:], commitmentsRoot[:], kzgPosition, topProof[:len(topProof)-1]))
chunk := makeChunk(kzgs[index])
gohashtree.HashChunks(chunk, chunk)
require.Equal(t, true, trie.VerifyMerkleProof(root[:], chunk[0][:], uint64(index+KZGOffset), proof))
}
// This test explains the calculation of the KZG commitment root's Merkle index
// in the Body's Merkle tree based on the index of the KZG commitment list in the Body.
func Test_KZGRootIndex(t *testing.T) {
// Level of the KZG commitment root's parent.
kzgParentRootLevel, err := ceilLog2(kzgPosition)
require.NoError(t, err)
// Merkle index of the KZG commitment root's parent.
// The parent's left child is the KZG commitment root,
// and its right child is the KZG commitment size.
kzgParentRootIndex := kzgPosition + (1 << kzgParentRootLevel)
// The KZG commitment root is the left child of its parent.
// Its Merkle index is the double of its parent's Merkle index.
require.Equal(t, 2*kzgParentRootIndex, kzgRootIndex)
}
// ceilLog2 returns the smallest integer greater than or equal to
// the base-2 logarithm of x.
func ceilLog2(x uint32) (uint32, error) {
if x == 0 {
return 0, errors.New("log2(0) is undefined")
}
var y uint32
if (x & (x - 1)) == 0 {
y = 0
} else {
y = 1
}
for x > 1 {
x >>= 1
y += 1
}
return y, nil
}
func getBlobKzgCommitmentsRoot(commitments [][]byte) ([32]byte, error) {
commitmentsLeaves := leavesFromCommitments(commitments)
commitmentsSparse, err := trie.GenerateTrieFromItems(
commitmentsLeaves,
fieldparams.LogMaxBlobCommitments,
)
if err != nil {
return [32]byte{}, err
}
return commitmentsSparse.HashTreeRoot()
} }
func Benchmark_MerkleProofKZGCommitment(b *testing.B) { func Benchmark_MerkleProofKZGCommitment(b *testing.B) {