Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading