E2E: beacon APIs Part 1 (#11306)

* adding compare beacon block test

* fixing bazel

* fixing evaluator import

* fixing imports

* changing package name

* fixing bazel

* adding logic to check for checking epoch

* fixing linting

* adding check for attester duties

* handle both blockv1 and blockv2

* making middleware objects public instead

* adding test for block attestations

* fixing typo

* adding blockroot test

* adding test for attestations

* fixing type value

* fixing test

* adding in node endpoints

* fixing bazel

* updating web3signer

* printing beacon blocks on request

* fixing struct

* temp log

* forgot string cast

* adding comparison function

* fixing bazel and evaulators, WIP

* fixing bazel

* changing how to minify json

* trying multiclient

* fixing port problem

* reverting evaluator and making test only for mainnet scenario testing

* removing test data

* fixing linting unused functions
git push

* changed to reflect

* adding in ssz comparison

* fixing tests

* fixing conflict

* fixing tests

* making v2 the standard

* adding better error logging

* fixing type

* adding lighthouse settings and fixing some deepsource items

* testing adding delay to evaluator

* testing without peers check

* changing target peers to try to fix lighthouse peer connections

* temp removing other tests

* fix lint issue

* adding peers connect back in

* adding in state version

* fixing bazel

* fixing path error

* testing changes to state

* fix unmarshal

* simplifying beacon api e2e execution

* fixing missed assertian checks

* improve logging and debugging issue

* trying to fix unmarshal

* still breaking more test edits

* removing fork to test unmarshal

* fixing pathing

* resolving error

* fixing beacon_api

* merging in debug api to beacon_api test

* fixing lint and temp commenting out endpoint

* adding in custom comparison function

* fixing custom evaluator

* adding test for block header data

* fixing header evaluation

* add node apis

* fixing linting,adding tests

* fixing bazel and temp removing unused functions

* fixing deepsource and linting issues

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* addressing review

* resolving more review comments

* fixing linting

* removing ssz return value as it's large and possibly not needed

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* fixing more review comments

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* fixing linting and review iteems

* fixing cognit complexity issue

* fixing linting

* fix log printout

* test build kite only with crossclient

* switching out evaluator to depositedvalidatorsareactive

* removed wrong evaluator switching correct one

* removing skip based on review comments

* fixing pathing issue

* test without participation at epoch

* testing without special lighthouse logic in evaluator

* reducing expected participation when multiclient

* fixing imports

* reducing epochs to see if less flaky

* testing with other tests added back in

* reducing epochs ran further

* testing only cross client again

* testing multi run again

* test reverted scenario for tests

* testing with cross client

* removing commented out function

* testing without peers connect

* adding optimization based on suggestions

* removed the wrong peers connect

* accidently commited something I shouldn't have

* fixing lighthouse flag

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update testing/endtoend/evaluators/beaconapi_evaluators/beacon_api.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
james-prysm
2022-12-06 10:01:17 -06:00
committed by GitHub
parent faf16f9e56
commit 19af1d2bb0
14 changed files with 612 additions and 12 deletions

3
.gitignore vendored
View File

@@ -38,3 +38,6 @@ metaData
# execution API authentication # execution API authentication
jwt.hex jwt.hex
# manual testing
tmp

View File

@@ -62,6 +62,7 @@ common_deps = [
"//testing/endtoend/components:go_default_library", "//testing/endtoend/components:go_default_library",
"//testing/endtoend/components/eth1:go_default_library", "//testing/endtoend/components/eth1:go_default_library",
"//testing/endtoend/evaluators:go_default_library", "//testing/endtoend/evaluators:go_default_library",
"//testing/endtoend/evaluators/beaconapi_evaluators:go_default_library",
"//testing/endtoend/helpers:go_default_library", "//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library", "//testing/endtoend/params:go_default_library",
"//testing/endtoend/types:go_default_library", "//testing/endtoend/types:go_default_library",

View File

@@ -182,9 +182,9 @@ func (node *LighthouseBeaconNode) Start(ctx context.Context) error {
fmt.Sprintf("--enr-tcp-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeP2PPort+index), fmt.Sprintf("--enr-tcp-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeP2PPort+index),
fmt.Sprintf("--port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeP2PPort+index), fmt.Sprintf("--port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeP2PPort+index),
fmt.Sprintf("--http-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeHTTPPort+index), fmt.Sprintf("--http-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeHTTPPort+index),
fmt.Sprintf("--target-peers=%d", 10), fmt.Sprintf("--target-peers=%d", e2e.TestParams.LighthouseBeaconNodeCount),
fmt.Sprintf("--eth1-endpoints=http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1RPCPort+prysmNodeCount+index), fmt.Sprintf("--eth1-endpoints=http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1RPCPort+prysmNodeCount+index),
fmt.Sprintf("--execution-endpoints=http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1ProxyPort+prysmNodeCount+index), fmt.Sprintf("--execution-endpoint=http://127.0.0.1:%d", e2e.TestParams.Ports.Eth1ProxyPort+prysmNodeCount+index),
fmt.Sprintf("--jwt-secrets=%s", jwtPath), fmt.Sprintf("--jwt-secrets=%s", jwtPath),
fmt.Sprintf("--boot-nodes=%s", node.enr), fmt.Sprintf("--boot-nodes=%s", node.enr),
fmt.Sprintf("--metrics-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeMetricsPort+index), fmt.Sprintf("--metrics-port=%d", e2e.TestParams.Ports.LighthouseBeaconNodeMetricsPort+index),
@@ -194,6 +194,7 @@ func (node *LighthouseBeaconNode) Start(ctx context.Context) error {
"--enable-private-discovery", "--enable-private-discovery",
"--debug-level=debug", "--debug-level=debug",
"--merge", "--merge",
"--suggested-fee-recipient=0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766",
} }
if node.config.UseFixedPeerIDs { if node.config.UseFixedPeerIDs {
flagVal := strings.Join(node.config.PeerIDs, ",") flagVal := strings.Join(node.config.PeerIDs, ",")

View File

@@ -191,6 +191,7 @@ func (v *LighthouseValidatorNode) Start(ctx context.Context) error {
fmt.Sprintf("--datadir=%s", kPath), fmt.Sprintf("--datadir=%s", kPath),
fmt.Sprintf("--testnet-dir=%s", testNetDir), fmt.Sprintf("--testnet-dir=%s", testNetDir),
fmt.Sprintf("--beacon-nodes=http://localhost:%d", httpPort+index), fmt.Sprintf("--beacon-nodes=http://localhost:%d", httpPort+index),
"--suggested-fee-recipient=0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766",
} }
cmd := exec.CommandContext(ctx, binaryPath, args...) // #nosec G204 -- Safe cmd := exec.CommandContext(ctx, binaryPath, args...) // #nosec G204 -- Safe

View File

@@ -195,7 +195,7 @@ func (v *ValidatorNode) Start(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("validator_%d is starting with offset keys %d", index, offset)
_, pubs, err := interop.DeterministicallyGenerateKeys(uint64(offset), uint64(validatorNum)) _, pubs, err := interop.DeterministicallyGenerateKeys(uint64(offset), uint64(validatorNum))
if err != nil { if err != nil {
return err return err

View File

@@ -1,6 +1,6 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # gazelle:keep load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # gazelle:keep
lighthouse_version = "v3.0.0" lighthouse_version = "v3.1.2"
lighthouse_archive_name = "lighthouse-%s-x86_64-unknown-linux-gnu-portable.tar.gz" % lighthouse_version lighthouse_archive_name = "lighthouse-%s-x86_64-unknown-linux-gnu-portable.tar.gz" % lighthouse_version
def e2e_deps(): def e2e_deps():
@@ -14,7 +14,7 @@ def e2e_deps():
http_archive( http_archive(
name = "lighthouse", name = "lighthouse",
sha256 = "6e0164d8f5074e083b55a161f3e6ecf1038e505334033ceaca37d6c491436d5d", sha256 = "172bb132d5fdc5bd257d5a66e98d0799498f08cb60502f93a5f4437a70d9c5e0",
build_file = "@prysm//testing/endtoend:lighthouse.BUILD", build_file = "@prysm//testing/endtoend:lighthouse.BUILD",
url = ("https://github.com/sigp/lighthouse/releases/download/%s/" + lighthouse_archive_name) % lighthouse_version, url = ("https://github.com/sigp/lighthouse/releases/download/%s/" + lighthouse_archive_name) % lighthouse_version,
) )

View File

@@ -2,6 +2,7 @@ package endtoend
import ( import (
"fmt" "fmt"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/evaluators/beaconapi_evaluators"
"os" "os"
"strconv" "strconv"
"testing" "testing"
@@ -115,16 +116,17 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool, cfgo ...types.E2E
ev.PeersConnect, ev.PeersConnect,
ev.HealthzCheck, ev.HealthzCheck,
ev.MetricsCheck, ev.MetricsCheck,
ev.ValidatorsAreActive,
ev.ValidatorsParticipatingAtEpoch(2), ev.ValidatorsParticipatingAtEpoch(2),
ev.FinalizationOccurs(3), ev.FinalizationOccurs(3),
ev.ProposeVoluntaryExit, ev.ProposeVoluntaryExit,
ev.ValidatorsHaveExited, ev.ValidatorsHaveExited,
ev.DepositedValidatorsAreActive,
ev.ColdStateCheckpoint, ev.ColdStateCheckpoint,
ev.AltairForkTransition, ev.AltairForkTransition,
ev.BellatrixForkTransition, ev.BellatrixForkTransition,
ev.APIMiddlewareVerifyIntegrity, ev.APIMiddlewareVerifyIntegrity,
ev.APIGatewayV1Alpha1VerifyIntegrity, ev.APIGatewayV1Alpha1VerifyIntegrity,
beaconapi_evaluators.BeaconAPIMultiClientVerifyIntegrity,
ev.FinishedSyncing, ev.FinishedSyncing,
ev.AllNodesHaveSameHead, ev.AllNodesHaveSameHead,
ev.FeeRecipientIsPresent, ev.FeeRecipientIsPresent,

View File

@@ -0,0 +1,31 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
testonly = True,
srcs = [
"beacon_api.go",
"beacon_api_verify.go",
"util.go",
],
importpath = "github.com/prysmaticlabs/prysm/v3/testing/endtoend/evaluators/beaconapi_evaluators",
visibility = ["//testing/endtoend:__subpackages__"],
deps = [
"//beacon-chain/rpc/apimiddleware:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/eth/service:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/endtoend/helpers:go_default_library",
"//testing/endtoend/params:go_default_library",
"//testing/endtoend/policies:go_default_library",
"//testing/endtoend/types:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@org_golang_google_grpc//:go_default_library",
],
)

View File

@@ -0,0 +1,430 @@
package beaconapi_evaluators
import (
"bytes"
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
v1 "github.com/prysmaticlabs/prysm/v3/proto/eth/v1"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/helpers"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/golang/protobuf/ptypes/empty"
"github.com/prysmaticlabs/prysm/v3/proto/eth/service"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"google.golang.org/grpc"
)
type metadata struct {
basepath string
params func(encoding string, currentEpoch types.Epoch) []string
prysmResps map[string]interface{}
lighthouseResps map[string]interface{}
customEvaluation func(interface{}, interface{}) error
}
var beaconPathsAndObjects = map[string]metadata{
"/beacon/genesis": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.GenesisResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.GenesisResponseJson{},
},
},
"/beacon/states/{param1}/root": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{"head"}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.StateRootResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.StateRootResponseJson{},
},
},
"/beacon/states/{param1}/finality_checkpoints": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{"head"}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.StateFinalityCheckpointResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.StateFinalityCheckpointResponseJson{},
},
},
"/beacon/blocks/{param1}": {
basepath: v2MiddlewarePathTemplate,
params: func(t string, e types.Epoch) []string {
if t == "ssz" {
if e < 4 {
return []string{"genesis"}
}
return []string{"finalized"}
}
return []string{"head"}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.BlockResponseJson{},
"ssz": []byte{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.BlockResponseJson{},
"ssz": []byte{},
},
},
"/beacon/states/{param1}/fork": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{"finalized"}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.StateForkResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.StateForkResponseJson{},
},
},
"/debug/beacon/states/{param1}": {
basepath: v2MiddlewarePathTemplate,
params: func(_ string, e types.Epoch) []string {
return []string{"head"}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.BeaconStateV2ResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.BeaconStateV2ResponseJson{},
},
},
"/validator/duties/proposer/{param1}": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, e types.Epoch) []string {
return []string{fmt.Sprintf("%v", e)}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.ProposerDutiesResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.ProposerDutiesResponseJson{},
},
customEvaluation: func(prysmResp interface{}, lhouseResp interface{}) error {
castedl, ok := lhouseResp.(*apimiddleware.ProposerDutiesResponseJson)
if !ok {
return errors.New("failed to cast type")
}
if castedl.Data[0].Slot == "0" {
// remove the first item from lighthouse data since lighthouse is returning a value despite no proposer
// there is no proposer on slot 0 so prysm don't return anything for slot 0
castedl.Data = castedl.Data[1:]
}
return compareJSONResponseObjects(prysmResp, castedl)
},
},
"/beacon/headers/{param1}": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, e types.Epoch) []string {
slot := uint64(0)
if e > 0 {
slot = (uint64(e) * uint64(params.BeaconConfig().SlotsPerEpoch)) - 1
}
return []string{fmt.Sprintf("%v", slot)}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.BlockHeaderResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.BlockHeaderResponseJson{},
},
},
"/node/identity": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.IdentityResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.IdentityResponseJson{},
},
customEvaluation: func(prysmResp interface{}, lhouseResp interface{}) error {
castedp, ok := prysmResp.(*apimiddleware.IdentityResponseJson)
if !ok {
return errors.New("failed to cast type")
}
castedl, ok := lhouseResp.(*apimiddleware.IdentityResponseJson)
if !ok {
return errors.New("failed to cast type")
}
if castedp.Data == nil {
return errors.New("prysm node identity was empty")
}
if castedl.Data == nil {
return errors.New("lighthouse node identity was empty")
}
return nil
},
},
"/node/peers": {
basepath: v1MiddlewarePathTemplate,
params: func(_ string, _ types.Epoch) []string {
return []string{}
},
prysmResps: map[string]interface{}{
"json": &apimiddleware.PeersResponseJson{},
},
lighthouseResps: map[string]interface{}{
"json": &apimiddleware.PeersResponseJson{},
},
customEvaluation: func(prysmResp interface{}, lhouseResp interface{}) error {
castedp, ok := prysmResp.(*apimiddleware.PeersResponseJson)
if !ok {
return errors.New("failed to cast type")
}
castedl, ok := lhouseResp.(*apimiddleware.PeersResponseJson)
if !ok {
return errors.New("failed to cast type")
}
if castedp.Data == nil {
return errors.New("prysm node identity was empty")
}
if castedl.Data == nil {
return errors.New("lighthouse node identity was empty")
}
return nil
},
},
}
func withCompareBeaconAPIs(beaconNodeIdx int, conn *grpc.ClientConn) error {
ctx := context.Background()
beaconClient := service.NewBeaconChainClient(conn)
genesisData, err := beaconClient.GetGenesis(ctx, &empty.Empty{})
if err != nil {
return errors.Wrap(err, "error getting genesis data")
}
currentEpoch := slots.EpochsSinceGenesis(genesisData.Data.GenesisTime.AsTime())
for path, meta := range beaconPathsAndObjects {
for key := range meta.prysmResps {
switch key {
case "json":
jsonparams := meta.params("json", currentEpoch)
apipath := pathFromParams(path, jsonparams)
fmt.Printf("executing json api path: %s\n", apipath)
if err := compareJSONMulticlient(beaconNodeIdx,
meta.basepath,
apipath,
beaconPathsAndObjects[path].prysmResps[key],
beaconPathsAndObjects[path].lighthouseResps[key],
meta.customEvaluation,
); err != nil {
return err
}
case "ssz":
sszparams := meta.params("ssz", currentEpoch)
if len(sszparams) == 0 {
continue
}
apipath := pathFromParams(path, sszparams)
fmt.Printf("executing ssz api path: %s\n", apipath)
prysmr, lighthouser, err := compareSSZMulticlient(beaconNodeIdx, meta.basepath, apipath)
if err != nil {
return err
}
beaconPathsAndObjects[path].prysmResps[key] = prysmr
beaconPathsAndObjects[path].lighthouseResps[key] = lighthouser
default:
return fmt.Errorf("unknown encoding type %s", key)
}
}
}
return orderedEvaluationOnResponses(beaconPathsAndObjects, genesisData)
}
func orderedEvaluationOnResponses(beaconPathsAndObjects map[string]metadata, genesisData *v1.GenesisResponse) error {
forkPathData := beaconPathsAndObjects["/beacon/states/{param1}/fork"]
prysmForkData, ok := forkPathData.prysmResps["json"].(*apimiddleware.StateForkResponseJson)
if !ok {
return errors.New("failed to cast type")
}
lighthouseForkData, ok := forkPathData.lighthouseResps["json"].(*apimiddleware.StateForkResponseJson)
if !ok {
return errors.New("failed to cast type")
}
if prysmForkData.Data.Epoch != lighthouseForkData.Data.Epoch {
return fmt.Errorf("prysm epoch %v does not match lighthouse epoch %v",
prysmForkData.Data.Epoch,
lighthouseForkData.Data.Epoch)
}
finalizedEpoch, err := strconv.ParseUint(prysmForkData.Data.Epoch, 10, 64)
if err != nil {
return err
}
blockPathData := beaconPathsAndObjects["/beacon/blocks/{param1}"]
sszrspL, ok := blockPathData.prysmResps["ssz"].([]byte)
if !ok {
return errors.New("failed to cast type")
}
sszrspP, ok := blockPathData.lighthouseResps["ssz"].([]byte)
if !ok {
return errors.New("failed to cast type")
}
if finalizedEpoch < helpers.AltairE2EForkEpoch+2 {
blockP := &ethpb.SignedBeaconBlock{}
blockL := &ethpb.SignedBeaconBlock{}
if err := blockL.UnmarshalSSZ(sszrspL); err != nil {
return errors.Wrap(err, "failed to unmarshal lighthouse ssz")
}
if err := blockP.UnmarshalSSZ(sszrspP); err != nil {
return errors.Wrap(err, "failed to unmarshal rysm ssz")
}
if len(blockP.Signature) == 0 || len(blockL.Signature) == 0 || hexutil.Encode(blockP.Signature) != hexutil.Encode(blockL.Signature) {
return errors.New("prysm signature does not match lighthouse signature")
}
} else if finalizedEpoch >= helpers.AltairE2EForkEpoch+2 && finalizedEpoch < helpers.BellatrixE2EForkEpoch {
blockP := &ethpb.SignedBeaconBlockAltair{}
blockL := &ethpb.SignedBeaconBlockAltair{}
if err := blockL.UnmarshalSSZ(sszrspL); err != nil {
return errors.Wrap(err, "lighthouse ssz error")
}
if err := blockP.UnmarshalSSZ(sszrspP); err != nil {
return errors.Wrap(err, "prysm ssz error")
}
if len(blockP.Signature) == 0 || len(blockL.Signature) == 0 || hexutil.Encode(blockP.Signature) != hexutil.Encode(blockL.Signature) {
return fmt.Errorf("prysm response %v does not match lighthouse response %v",
blockP,
blockL)
}
} else {
blockP := &ethpb.SignedBeaconBlockBellatrix{}
blockL := &ethpb.SignedBeaconBlockBellatrix{}
if err := blockL.UnmarshalSSZ(sszrspL); err != nil {
return errors.Wrap(err, "lighthouse ssz error")
}
if err := blockP.UnmarshalSSZ(sszrspP); err != nil {
return errors.Wrap(err, "prysm ssz error")
}
if len(blockP.Signature) == 0 || len(blockL.Signature) == 0 || hexutil.Encode(blockP.Signature) != hexutil.Encode(blockL.Signature) {
return fmt.Errorf("prysm response %v does not match lighthouse response %v",
blockP,
blockL)
}
}
blockheaderData := beaconPathsAndObjects["/beacon/headers/{param1}"]
prysmHeader, ok := blockheaderData.prysmResps["json"].(*apimiddleware.BlockHeaderResponseJson)
if !ok {
return errors.New("failed to cast type")
}
proposerdutiesData := beaconPathsAndObjects["/validator/duties/proposer/{param1}"]
prysmDuties, ok := proposerdutiesData.prysmResps["json"].(*apimiddleware.ProposerDutiesResponseJson)
if !ok {
return errors.New("failed to cast type")
}
if prysmHeader.Data.Root != prysmDuties.DependentRoot {
fmt.Printf("current slot: %v\n", slots.CurrentSlot(uint64(genesisData.Data.GenesisTime.AsTime().Unix())))
return fmt.Errorf("header root %s does not match duties root %s ", prysmHeader.Data.Root, prysmDuties.DependentRoot)
}
return nil
}
func compareJSONMulticlient(beaconNodeIdx int, base string, path string, respJSONPrysm interface{}, respJSONLighthouse interface{}, customEvaluator func(interface{}, interface{}) error) error {
if err := doMiddlewareJSONGetRequest(
base,
path,
beaconNodeIdx,
respJSONPrysm,
); err != nil {
return errors.Wrap(err, "could not perform GET request for Prysm JSON")
}
if err := doMiddlewareJSONGetRequest(
base,
path,
beaconNodeIdx,
respJSONLighthouse,
"lighthouse",
); err != nil {
return errors.Wrap(err, "could not perform GET request for Lighthouse JSON")
}
if customEvaluator != nil {
return customEvaluator(respJSONPrysm, respJSONLighthouse)
} else {
return compareJSONResponseObjects(respJSONPrysm, respJSONLighthouse)
}
}
func compareSSZMulticlient(beaconNodeIdx int, base string, path string) ([]byte, []byte, error) {
sszrspL, err := doMiddlewareSSZGetRequest(
base,
path,
beaconNodeIdx,
"lighthouse",
)
if err != nil {
return nil, nil, errors.Wrap(err, "could not perform GET request for Lighthouse SSZ")
}
sszrspP, err := doMiddlewareSSZGetRequest(
base,
path,
beaconNodeIdx,
)
if err != nil {
return nil, nil, errors.Wrap(err, "could not perform GET request for Prysm SSZ")
}
if !bytes.Equal(sszrspL, sszrspP) {
return nil, nil, errors.New("prysm ssz response does not match lighthouse ssz response")
}
return sszrspP, sszrspL, nil
}
func compareJSONResponseObjects(prysmResp interface{}, lighthouseResp interface{}) error {
if !reflect.DeepEqual(prysmResp, lighthouseResp) {
p, err := json.Marshal(prysmResp)
if err != nil {
return errors.Wrap(err, "failed to marshal Prysm response to JSON")
}
l, err := json.Marshal(lighthouseResp)
if err != nil {
return errors.Wrap(err, "failed to marshal Lighthouse response to JSON")
}
return fmt.Errorf("prysm response %s does not match lighthouse response %s",
string(p),
string(l))
}
return nil
}
func pathFromParams(path string, params []string) string {
apiPath := path
for index := range params {
apiPath = strings.Replace(path, fmt.Sprintf("{param%d}", index+1), params[index], 1)
}
return apiPath
}

View File

@@ -0,0 +1,46 @@
package beaconapi_evaluators
import (
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/policies"
e2etypes "github.com/prysmaticlabs/prysm/v3/testing/endtoend/types"
"google.golang.org/grpc"
)
// BeaconAPIMultiClientVerifyIntegrity tests our API Middleware responses to other beacon nodes such as lighthouse.
var BeaconAPIMultiClientVerifyIntegrity = e2etypes.Evaluator{
Name: "beacon_api_multi-client_verify_integrity_epoch_%d",
Policy: policies.AllEpochs,
Evaluation: beaconAPIVerify,
}
const (
v1MiddlewarePathTemplate = "http://localhost:%d/eth/v1"
v2MiddlewarePathTemplate = "http://localhost:%d/eth/v2"
)
type apiComparisonFunc func(beaconNodeIdx int, conn *grpc.ClientConn) error
func beaconAPIVerify(_ e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
beacon := []apiComparisonFunc{
withCompareBeaconAPIs,
}
for beaconNodeIdx, conn := range conns {
if err := runAPIComparisonFunctions(
beaconNodeIdx,
conn,
beacon...,
); err != nil {
return err
}
}
return nil
}
func runAPIComparisonFunctions(beaconNodeIdx int, conn *grpc.ClientConn, fs ...apiComparisonFunc) error {
for _, f := range fs {
if err := f(beaconNodeIdx, conn); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,78 @@
package beaconapi_evaluators
import (
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/params"
log "github.com/sirupsen/logrus"
)
func doMiddlewareJSONGetRequest(template string, requestPath string, beaconNodeIdx int, dst interface{}, bnType ...string) error {
var port int
if len(bnType) > 0 {
switch bnType[0] {
case "lighthouse":
port = params.TestParams.Ports.LighthouseBeaconNodeHTTPPort
default:
port = params.TestParams.Ports.PrysmBeaconNodeGatewayPort
}
} else {
port = params.TestParams.Ports.PrysmBeaconNodeGatewayPort
}
basePath := fmt.Sprintf(template, port+beaconNodeIdx)
httpResp, err := http.Get(
basePath + requestPath,
)
if err != nil {
return err
}
return json.NewDecoder(httpResp.Body).Decode(&dst)
}
func doMiddlewareSSZGetRequest(template string, requestPath string, beaconNodeIdx int, bnType ...string) ([]byte, error) {
client := &http.Client{}
var port int
if len(bnType) > 0 {
switch bnType[0] {
case "lighthouse":
port = params.TestParams.Ports.LighthouseBeaconNodeHTTPPort
default:
port = params.TestParams.Ports.PrysmBeaconNodeGatewayPort
}
} else {
port = params.TestParams.Ports.PrysmBeaconNodeGatewayPort
}
basePath := fmt.Sprintf(template, port+beaconNodeIdx)
req, err := http.NewRequest("GET", basePath+requestPath, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/octet-stream")
rsp, err := client.Do(req)
if err != nil {
return nil, err
}
if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("request failed with response code: %d", rsp.StatusCode)
}
defer closeBody(rsp.Body)
body, err := io.ReadAll(rsp.Body)
if err != nil {
return nil, err
}
return body, nil
}
func closeBody(body io.Closer) {
if err := body.Close(); err != nil {
log.WithError(err).Error("could not close response body")
}
}

View File

@@ -4,14 +4,13 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
ethtypes "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" ethtypes "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
ethpbservice "github.com/prysmaticlabs/prysm/v3/proto/eth/service" ethpbservice "github.com/prysmaticlabs/prysm/v3/proto/eth/service"
"github.com/prysmaticlabs/prysm/v3/proto/eth/v2" "github.com/prysmaticlabs/prysm/v3/proto/eth/v2"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
@@ -19,7 +18,6 @@ import (
e2eparams "github.com/prysmaticlabs/prysm/v3/testing/endtoend/params" e2eparams "github.com/prysmaticlabs/prysm/v3/testing/endtoend/params"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/policies" "github.com/prysmaticlabs/prysm/v3/testing/endtoend/policies"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/types" "github.com/prysmaticlabs/prysm/v3/testing/endtoend/types"
"github.com/prysmaticlabs/prysm/v3/time/slots" "github.com/prysmaticlabs/prysm/v3/time/slots"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
@@ -27,7 +25,7 @@ import (
var expectedParticipation = 0.99 var expectedParticipation = 0.99
var expectedMulticlientParticipation = 0.98 var expectedMulticlientParticipation = 0.95
var expectedSyncParticipation = 0.99 var expectedSyncParticipation = 0.99

View File

@@ -1,15 +1,18 @@
package endtoend package endtoend
import ( import (
"testing"
"github.com/prysmaticlabs/prysm/v3/testing/endtoend/types" "github.com/prysmaticlabs/prysm/v3/testing/endtoend/types"
"testing"
) )
func TestEndToEnd_MainnetConfig_MultiClient(t *testing.T) { func TestEndToEnd_MainnetConfig_MultiClient(t *testing.T) {
e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/).run() e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/).run()
} }
func TestEndToEnd_MainnetConfig_Multiclient_CrossClient(t *testing.T) {
e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/, types.WithValidatorCrossClient()).run()
}
func TestEndToEnd_MultiScenarioRun_Multiclient(t *testing.T) { func TestEndToEnd_MultiScenarioRun_Multiclient(t *testing.T) {
runner := e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/, types.WithEpochs(22)) runner := e2eMainnet(t, false /*usePrysmSh*/, true /*useMultiClient*/, types.WithEpochs(22))
runner.config.Evaluators = scenarioEvalsMulti() runner.config.Evaluators = scenarioEvalsMulti()

View File

@@ -29,6 +29,12 @@ func WithCheckpointSync() E2EConfigOpt {
} }
} }
func WithValidatorCrossClient() E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.UseValidatorCrossClient = true
}
}
// E2EConfig defines the struct for all configurations needed for E2E testing. // E2EConfig defines the struct for all configurations needed for E2E testing.
type E2EConfig struct { type E2EConfig struct {
TestCheckpointSync bool TestCheckpointSync bool