Implement Merkle proof spectests (#13146)

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Potuz 2023-11-02 20:08:16 -03:00 committed by GitHub
parent 8a743a6430
commit daa6d2e741
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 178 additions and 10 deletions

View File

@ -11,7 +11,6 @@ go_library(
deps = [ deps = [
"//crypto/hash:go_default_library", "//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1:go_default_library",
"@com_github_pkg_errors//:go_default_library", "@com_github_pkg_errors//:go_default_library",
], ],

View File

@ -9,7 +9,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/crypto/hash" "github.com/prysmaticlabs/prysm/v4/crypto/hash"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v4/math"
protodb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" protodb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
) )
@ -214,18 +213,15 @@ func VerifyMerkleProofWithDepth(root, item []byte, merkleIndex uint64, proof [][
if uint64(len(proof)) != depth+1 { if uint64(len(proof)) != depth+1 {
return false return false
} }
if depth >= 64 {
return false // PowerOf2 would overflow.
}
node := bytesutil.ToBytes32(item) node := bytesutil.ToBytes32(item)
for i := uint64(0); i <= depth; i++ { 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[:]...)) node = hash.Hash(append(proof[i], node[:]...))
} else { } else {
node = hash.Hash(append(node[:], proof[i]...)) node = hash.Hash(append(node[:], proof[i]...))
} }
merkleIndex /= 2
} }
return bytes.Equal(root, node[:]) return bytes.Equal(root, node[:])
} }

View File

@ -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"],
)

View File

@ -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")
}

View File

@ -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"],
)

View File

@ -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")
}

View File

@ -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",
],
)

View File

@ -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))
})
}
}
}

View File

@ -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",
],
)

View File

@ -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)
}

View File

@ -15,7 +15,7 @@ import (
// RunSSZStaticTests executes "ssz_static" tests. // RunSSZStaticTests executes "ssz_static" tests.
func RunSSZStaticTests(t *testing.T, config string) { 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 { func customHtr(t *testing.T, htrs []common.HTR, object interface{}) []common.HTR {
@ -32,8 +32,8 @@ func customHtr(t *testing.T, htrs []common.HTR, object interface{}) []common.HTR
return htrs return htrs
} }
// unmarshalledSSZ unmarshalls serialized input. // UnmarshalledSSZ unmarshalls serialized input.
func unmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) { func UnmarshalledSSZ(t *testing.T, serializedBytes []byte, folderName string) (interface{}, error) {
var obj interface{} var obj interface{}
switch folderName { switch folderName {
case "ExecutionPayload": case "ExecutionPayload":