Skip to content

Commit

Permalink
chore(kzg): Additional tests for KZG commitments (#13758)
Browse files Browse the repository at this point in the history
* add a test explaining kzgRootIndex

* minor

* minor
  • Loading branch information
letonchanh authored Mar 19, 2024
1 parent 357211b commit d779e65
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 14 deletions.
19 changes: 12 additions & 7 deletions consensus-types/blocks/kzg.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ const (
bodyLength = 12 // The number of elements in the BeaconBlockBody Container
logBodyLength = 4 // The log 2 of bodyLength
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 (
Expand All @@ -37,9 +38,7 @@ func VerifyKZGInclusionProof(blob ROBlob) error {
if len(root) != field_params.RootLength {
return errInvalidBodyRoot
}
chunks := make([][32]byte, 2)
copy(chunks[0][:], blob.KzgCommitment)
copy(chunks[1][:], blob.KzgCommitment[field_params.RootLength:])
chunks := makeChunk(blob.KzgCommitment)
gohashtree.HashChunks(chunks, chunks)
verified := trie.VerifyMerkleProof(root, chunks[0][:], blob.Index+KZGOffset, blob.CommitmentInclusionProof)
if !verified {
Expand Down Expand Up @@ -85,15 +84,21 @@ func MerkleProofKZGCommitment(body interfaces.ReadOnlyBeaconBlockBody, index int
func leavesFromCommitments(commitments [][]byte) [][]byte {
leaves := make([][]byte, len(commitments))
for i, kzg := range commitments {
chunk := make([][32]byte, 2)
copy(chunk[0][:], kzg)
copy(chunk[1][:], kzg[field_params.RootLength:])
chunk := makeChunk(kzg)
gohashtree.HashChunks(chunk, chunk)
leaves[i] = chunk[0][:]
}
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
// commitment list.
func bodyProof(commitments [][]byte, index int) ([][]byte, error) {
Expand Down
80 changes: 73 additions & 7 deletions consensus-types/blocks/kzg_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package blocks

import (
"math/rand"
"crypto/rand"
"errors"
"testing"

"github.com/prysmaticlabs/gohashtree"
Expand Down Expand Up @@ -74,14 +75,79 @@ func Test_MerkleProofKZGCommitment(t *testing.T) {
proof, err := MerkleProofKZGCommitment(body, index)
require.NoError(t, err)

chunk := make([][32]byte, 2)
copy(chunk[0][:], kzgs[index])
copy(chunk[1][:], kzgs[index][32:])
gohashtree.HashChunks(chunk, chunk)
// Test the logic of topProof in MerkleProofKZGCommitment.
commitmentsRoot, err := getBlobKzgCommitmentsRoot(kzgs)
require.NoError(t, err)
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()
require.NoError(t, err)
kzgOffset := 54 * fieldparams.MaxBlobCommitmentsPerBlock
require.Equal(t, true, trie.VerifyMerkleProof(root[:], chunk[0][:], uint64(index+kzgOffset), proof))
// Partially verify if the commitments root is in the body root.
// 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) {
Expand Down

0 comments on commit d779e65

Please sign in to comment.