Skip to content

Commit

Permalink
Implement Merkle proof spectests (#13146)
Browse files Browse the repository at this point in the history
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
  • Loading branch information
potuz and prylabs-bulldozer[bot] authored Nov 2, 2023
1 parent 8a743a6 commit daa6d2e
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 10 deletions.
1 change: 0 additions & 1 deletion container/trie/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ go_library(
deps = [
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
Expand Down
8 changes: 2 additions & 6 deletions container/trie/sparse_merkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/math"
protodb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
)

Expand Down Expand Up @@ -214,18 +213,15 @@ func VerifyMerkleProofWithDepth(root, item []byte, merkleIndex uint64, proof [][
if uint64(len(proof)) != depth+1 {
return false
}
if depth >= 64 {
return false // PowerOf2 would overflow.
}
node := bytesutil.ToBytes32(item)
for i := uint64(0); i <= depth; i++ {
if (merkleIndex / math.PowerOf2(i) % 2) != 0 {
if (merkleIndex & 1) == 1 {
node = hash.Hash(append(proof[i], node[:]...))
} else {
node = hash.Hash(append(node[:], proof[i]...))
}
merkleIndex /= 2
}

return bytes.Equal(root, node[:])
}

Expand Down
11 changes: 11 additions & 0 deletions testing/spectest/mainnet/deneb/merkle_proof/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("@prysm//tools/go:def.bzl", "go_test")

go_test(
name = "go_default_test",
srcs = ["merkle_proof_test.go"],
data = glob(["*.yaml"]) + [
"@consensus_spec_tests_mainnet//:test_data",
],
tags = ["spectest"],
deps = ["//testing/spectest/shared/deneb/merkle_proof:go_default_library"],
)
11 changes: 11 additions & 0 deletions testing/spectest/mainnet/deneb/merkle_proof/merkle_proof_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package merkle_proof

import (
"testing"

"github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/merkle_proof"
)

func TestMainnet_Deneb_MerkleProof(t *testing.T) {
merkle_proof.RunMerkleProofTests(t, "mainnet")
}
16 changes: 16 additions & 0 deletions testing/spectest/minimal/deneb/merkle_proof/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@prysm//tools/go:def.bzl", "go_test")

go_test(
name = "go_default_test",
size = "small",
srcs = ["merkle_proof_test.go"],
data = glob(["*.yaml"]) + [
"@consensus_spec_tests_minimal//:test_data",
],
eth_network = "minimal",
tags = [
"minimal",
"spectest",
],
deps = ["//testing/spectest/shared/deneb/merkle_proof:go_default_library"],
)
11 changes: 11 additions & 0 deletions testing/spectest/minimal/deneb/merkle_proof/merkle_proof_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package merkle_proof

import (
"testing"

"github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/merkle_proof"
)

func TestMainnet_Deneb_MerkleProof(t *testing.T) {
merkle_proof.RunMerkleProofTests(t, "minimal")
}
19 changes: 19 additions & 0 deletions testing/spectest/shared/common/merkle_proof/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@prysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
testonly = True,
srcs = ["single_merkle_proof.go"],
importpath = "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/common/merkle_proof",
visibility = ["//visibility:public"],
deps = [
"//container/trie:go_default_library",
"//testing/require:go_default_library",
"//testing/spectest/shared/common/ssz_static:go_default_library",
"//testing/spectest/utils:go_default_library",
"//testing/util:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
],
)
80 changes: 80 additions & 0 deletions testing/spectest/shared/common/merkle_proof/single_merkle_proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package merkle_proof

import (
"encoding/hex"
"os"
"path"
"testing"

"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/golang/snappy"
fssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/v4/container/trie"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/common/ssz_static"
"github.com/prysmaticlabs/prysm/v4/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)

// SingleMerkleProof is the format used to read spectest Merkle Proof test data.
type SingleMerkleProof struct {
Leaf string `json:"leaf"`
LeafIndex uint64 `json:"leaf_index"`
Branch []string `json:"branch"`
}

func RunMerkleProofTests(t *testing.T, config, forkOrPhase string, unmarshaller ssz_static.Unmarshaller) {
t.Skip("testvectors are not available yet")
runSingleMerkleProofTests(t, config, forkOrPhase, unmarshaller)
}

func runSingleMerkleProofTests(t *testing.T, config, forkOrPhase string, unmarshaller ssz_static.Unmarshaller) {
require.NoError(t, utils.SetConfig(t, config))

testFolders, basePath := utils.TestFolders(t, config, forkOrPhase, "merkle_proof/single_merkle_proof")

if len(testFolders) == 0 {
t.Fatalf("No test folders found for %s/%s/merkle_proof", config, forkOrPhase)
}

for _, folder := range testFolders {
typeFolderBase := path.Join(basePath, folder.Name())
typeFolder, err := bazel.Runfile(typeFolderBase)
require.NoError(t, err)
modeFolders, err := os.ReadDir(typeFolder)
require.NoError(t, err)

if len(modeFolders) == 0 {
t.Fatalf("No test folders found for %s", typeFolder)
}

for _, modeFolder := range modeFolders {
t.Run(path.Join(folder.Name(), modeFolder.Name()), func(t *testing.T) {
serializedBytes, err := util.BazelFileBytes(typeFolder, modeFolder.Name(), "object.ssz_snappy")
require.NoError(t, err)
serializedSSZ, err := snappy.Decode(nil /* dst */, serializedBytes)
require.NoError(t, err, "Failed to decompress")
object, err := unmarshaller(t, serializedSSZ, folder.Name())
require.NoError(t, err, "Could not unmarshall serialized SSZ")
sszObj, ok := object.(fssz.HashRoot)
require.Equal(t, true, ok)
root, err := sszObj.HashTreeRoot()
require.NoError(t, err)

proofYamlFile, err := util.BazelFileBytes(typeFolder, modeFolder.Name(), "proof.yaml")
require.NoError(t, err)
proof := &SingleMerkleProof{}
require.NoError(t, utils.UnmarshalYaml(proofYamlFile, proof), "Failed to Unmarshal single Merkle proof")
branch := make([][]byte, len(proof.Branch))
for i, proofRoot := range proof.Branch {
branch[i], err = hex.DecodeString(proofRoot[2:])
require.NoError(t, err)
}
leaf, err := hex.DecodeString(proof.Leaf[2:])
require.NoError(t, err)

require.Equal(t, true, trie.VerifyMerkleProof(root[:], leaf, proof.LeafIndex, branch))
})
}
}
}
13 changes: 13 additions & 0 deletions testing/spectest/shared/deneb/merkle_proof/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("@prysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
testonly = True,
srcs = ["merkle_proof.go"],
importpath = "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/merkle_proof",
visibility = ["//visibility:public"],
deps = [
"//testing/spectest/shared/common/merkle_proof:go_default_library",
"//testing/spectest/shared/deneb/ssz_static:go_default_library",
],
)
12 changes: 12 additions & 0 deletions testing/spectest/shared/deneb/merkle_proof/merkle_proof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package merkle_proof

import (
"testing"

common "github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/common/merkle_proof"
"github.com/prysmaticlabs/prysm/v4/testing/spectest/shared/deneb/ssz_static"
)

func RunMerkleProofTests(t *testing.T, config string) {
common.RunMerkleProofTests(t, config, "deneb", ssz_static.UnmarshalledSSZ)
}
6 changes: 3 additions & 3 deletions testing/spectest/shared/deneb/ssz_static/ssz_static.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

// RunSSZStaticTests executes "ssz_static" tests.
func RunSSZStaticTests(t *testing.T, config string) {
common.RunSSZStaticTests(t, config, "deneb", unmarshalledSSZ, customHtr)
common.RunSSZStaticTests(t, config, "deneb", UnmarshalledSSZ, customHtr)
}

func customHtr(t *testing.T, htrs []common.HTR, object interface{}) []common.HTR {
Expand All @@ -32,8 +32,8 @@ func customHtr(t *testing.T, htrs []common.HTR, object interface{}) []common.HTR
return htrs
}

// unmarshalledSSZ unmarshalls serialized input.
func unmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) {
// UnmarshalledSSZ unmarshalls serialized input.
func UnmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) {
var obj interface{}
switch folderName {
case "ExecutionPayload":
Expand Down

0 comments on commit daa6d2e

Please sign in to comment.