Tests for builder service (#11214)

* Tests for builder service

* fix glob

* minor changes to mock

* add comments to mock

* Revert "Auxiliary commit to revert individual files from bc62a140347e3cbd8bd82e99cf5e9deb98b74df0"

This reverts commit c4c1016cb597b9340d1c81ab3816e037a6b97f9e.

* correct comment

* Do not init builder for empty endpoint

* revert  merging issues

* better nil checks

* test fix

Co-authored-by: terencechain <terence@prysmaticlabs.com>
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Radosław Kapka
2022-09-06 20:29:44 +02:00
committed by GitHub
parent 0cee01ad55
commit b4d2395a38
8 changed files with 166 additions and 34 deletions

View File

@@ -13,6 +13,8 @@ go_library(
"//consensus-types/primitives:go_default_library", "//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"//monitoring/tracing:go_default_library", "//monitoring/tracing:go_default_library",
"//network:go_default_library",
"//network/authorization:go_default_library",
"//proto/engine/v1:go_default_library", "//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",

View File

@@ -15,6 +15,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing" "github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v3/network"
"github.com/prysmaticlabs/prysm/v3/network/authorization"
v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -83,6 +85,15 @@ func (*requestLogger) observe(r *http.Request) (e error) {
var _ observer = &requestLogger{} var _ observer = &requestLogger{}
// BuilderClient provides a collection of helper methods for calling Builder API endpoints.
type BuilderClient interface {
NodeURL() string
GetHeader(ctx context.Context, slot types.Slot, parentHash [32]byte, pubkey [48]byte) (*ethpb.SignedBuilderBid, error)
RegisterValidator(ctx context.Context, svr []*ethpb.SignedValidatorRegistrationV1) error
SubmitBlindedBlock(ctx context.Context, sb *ethpb.SignedBlindedBeaconBlockBellatrix) (*v1.ExecutionPayload, error)
Status(ctx context.Context) error
}
// Client provides a collection of helper methods for calling Builder API endpoints. // Client provides a collection of helper methods for calling Builder API endpoints.
type Client struct { type Client struct {
hc *http.Client hc *http.Client
@@ -94,7 +105,8 @@ type Client struct {
// `host` is the base host + port used to construct request urls. This value can be // `host` is the base host + port used to construct request urls. This value can be
// a URL string, or NewClient will assume an http endpoint if just `host:port` is used. // a URL string, or NewClient will assume an http endpoint if just `host:port` is used.
func NewClient(host string, opts ...ClientOpt) (*Client, error) { func NewClient(host string, opts ...ClientOpt) (*Client, error) {
u, err := urlForHost(host) endpoint := covertEndPoint(host)
u, err := urlForHost(endpoint.Url)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -304,3 +316,12 @@ func non200Err(response *http.Response) error {
return errors.Wrap(ErrNotOK, fmt.Sprintf("unsupported error code: %d", response.StatusCode)) return errors.Wrap(ErrNotOK, fmt.Sprintf("unsupported error code: %d", response.StatusCode))
} }
} }
func covertEndPoint(ep string) network.Endpoint {
return network.Endpoint{
Url: ep,
Auth: network.AuthorizationData{ // Auth is not used for builder.
Method: authorization.None,
Value: "",
}}
}

View File

@@ -0,0 +1,14 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["mock.go"],
importpath = "github.com/prysmaticlabs/prysm/v3/api/client/builder/testing",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
],
)

View File

@@ -0,0 +1,49 @@
package testing
import (
"context"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
)
// MockClient is a mock implementation of BuilderClient.
type MockClient struct {
RegisteredVals map[[48]byte]bool
}
// NewClient creates a new, correctly initialized mock.
func NewClient() MockClient {
return MockClient{RegisteredVals: map[[48]byte]bool{}}
}
// NodeURL --
func (MockClient) NodeURL() string {
return ""
}
// GetHeader --
func (MockClient) GetHeader(_ context.Context, _ types.Slot, _ [32]byte, _ [48]byte) (*ethpb.SignedBuilderBid, error) {
return nil, nil
}
// RegisterValidator --
func (m MockClient) RegisterValidator(_ context.Context, svr []*ethpb.SignedValidatorRegistrationV1) error {
for _, r := range svr {
b := bytesutil.ToBytes48(r.Message.Pubkey)
m.RegisteredVals[b] = true
}
return nil
}
// SubmitBlindedBlock --
func (MockClient) SubmitBlindedBlock(_ context.Context, _ *ethpb.SignedBlindedBeaconBlockBellatrix) (*v1.ExecutionPayload, error) {
return nil, nil
}
// Status --
func (MockClient) Status(_ context.Context) error {
return nil
}

View File

@@ -1,4 +1,4 @@
load("@prysm//tools/go:def.bzl", "go_library") load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
@@ -16,8 +16,6 @@ go_library(
"//cmd/beacon-chain/flags:go_default_library", "//cmd/beacon-chain/flags:go_default_library",
"//consensus-types/primitives:go_default_library", "//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"//network:go_default_library",
"//network/authorization:go_default_library",
"//proto/engine/v1:go_default_library", "//proto/engine/v1: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",
@@ -28,3 +26,18 @@ go_library(
"@io_opencensus_go//trace:go_default_library", "@io_opencensus_go//trace:go_default_library",
], ],
) )
go_test(
name = "go_default_test",
srcs = ["service_test.go"],
embed = [":go_default_library"],
deps = [
"//api/client/builder/testing:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -1,11 +1,10 @@
package builder package builder
import ( import (
"github.com/prysmaticlabs/prysm/v3/api/client/builder"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db" "github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags" "github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v3/network"
"github.com/prysmaticlabs/prysm/v3/network/authorization"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@@ -14,22 +13,30 @@ type Option func(s *Service) error
// FlagOptions for builder service flag configurations. // FlagOptions for builder service flag configurations.
func FlagOptions(c *cli.Context) ([]Option, error) { func FlagOptions(c *cli.Context) ([]Option, error) {
endpoint := c.String(flags.MevRelayEndpoint.Name) endpoint := c.String(flags.MevRelayEndpoint.Name)
var client *builder.Client
if endpoint != "" {
var err error
client, err = builder.NewClient(endpoint)
if err != nil {
return nil, err
}
}
opts := []Option{ opts := []Option{
WithBuilderEndpoints(endpoint), WithBuilderClient(client),
} }
return opts, nil return opts, nil
} }
// WithBuilderEndpoints sets the endpoint for the beacon chain builder service. // WithBuilderClient sets the builder client for the beacon chain builder service.
func WithBuilderEndpoints(endpoint string) Option { func WithBuilderClient(client builder.BuilderClient) Option {
return func(s *Service) error { return func(s *Service) error {
s.cfg.builderEndpoint = covertEndPoint(endpoint) s.cfg.builderClient = client
return nil return nil
} }
} }
// WithHeadFetcher gets the head info from chain service. // WithHeadFetcher gets the head info from chain service.
func WithHeadFetcher(svc *blockchain.Service) Option { func WithHeadFetcher(svc blockchain.HeadFetcher) Option {
return func(s *Service) error { return func(s *Service) error {
s.cfg.headFetcher = svc s.cfg.headFetcher = svc
return nil return nil
@@ -43,12 +50,3 @@ func WithDatabase(beaconDB db.HeadAccessDatabase) Option {
return nil return nil
} }
} }
func covertEndPoint(ep string) network.Endpoint {
return network.Endpoint{
Url: ep,
Auth: network.AuthorizationData{ // Auth is not used for builder.
Method: authorization.None,
Value: "",
}}
}

View File

@@ -2,6 +2,7 @@ package builder
import ( import (
"context" "context"
"reflect"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -10,7 +11,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db" "github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/network"
v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -30,15 +30,15 @@ type BlockBuilder interface {
// config defines a config struct for dependencies into the service. // config defines a config struct for dependencies into the service.
type config struct { type config struct {
builderEndpoint network.Endpoint builderClient builder.BuilderClient
beaconDB db.HeadAccessDatabase beaconDB db.HeadAccessDatabase
headFetcher blockchain.HeadFetcher headFetcher blockchain.HeadFetcher
} }
// Service defines a service that provides a client for interacting with the beacon chain and MEV relay network. // Service defines a service that provides a client for interacting with the beacon chain and MEV relay network.
type Service struct { type Service struct {
cfg *config cfg *config
c *builder.Client c builder.BuilderClient
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
} }
@@ -56,18 +56,14 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
return nil, err return nil, err
} }
} }
if s.cfg.builderEndpoint.Url != "" { if s.cfg.builderClient != nil && !reflect.ValueOf(s.cfg.builderClient).IsNil() {
c, err := builder.NewClient(s.cfg.builderEndpoint.Url) s.c = s.cfg.builderClient
if err != nil {
return nil, err
}
s.c = c
// Is the builder up? // Is the builder up?
if err := s.c.Status(ctx); err != nil { if err := s.c.Status(ctx); err != nil {
log.WithError(err).Error("Failed to check builder status") log.WithError(err).Error("Failed to check builder status")
} else { } else {
log.WithField("endpoint", c.NodeURL()).Info("Builder has been configured") log.WithField("endpoint", s.c.NodeURL()).Info("Builder has been configured")
log.Warn("Outsourcing block construction to external builders adds non-trivial delay to block propagation time. " + log.Warn("Outsourcing block construction to external builders adds non-trivial delay to block propagation time. " +
"Builder-constructed blocks or fallback blocks may get orphaned. Use at your own risk!") "Builder-constructed blocks or fallback blocks may get orphaned. Use at your own risk!")
} }
@@ -157,7 +153,7 @@ func (s *Service) RegisterValidator(ctx context.Context, reg []*ethpb.SignedVali
return s.cfg.beaconDB.SaveRegistrationsByValidatorIDs(ctx, idxs, msgs) return s.cfg.beaconDB.SaveRegistrationsByValidatorIDs(ctx, idxs, msgs)
} }
// Configured returns true if the user has input a builder URL. // Configured returns true if the user has configured a builder client.
func (s *Service) Configured() bool { func (s *Service) Configured() bool {
return s.cfg.builderEndpoint.Url != "" return s.c != nil && !reflect.ValueOf(s.c).IsNil()
} }

View File

@@ -0,0 +1,39 @@
package builder
import (
"context"
"testing"
buildertesting "github.com/prysmaticlabs/prysm/v3/api/client/builder/testing"
blockchainTesting "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
dbtesting "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
)
func Test_NewServiceWithBuilder(t *testing.T) {
s, err := NewService(context.Background(), WithBuilderClient(&buildertesting.MockClient{}))
require.NoError(t, err)
assert.Equal(t, true, s.Configured())
}
func Test_NewServiceWithoutBuilder(t *testing.T) {
s, err := NewService(context.Background())
require.NoError(t, err)
assert.Equal(t, false, s.Configured())
}
func Test_RegisterValidator(t *testing.T) {
ctx := context.Background()
db := dbtesting.SetupDB(t)
headFetcher := &blockchainTesting.ChainService{}
builder := buildertesting.NewClient()
s, err := NewService(ctx, WithDatabase(db), WithHeadFetcher(headFetcher), WithBuilderClient(&builder))
require.NoError(t, err)
pubkey := bytesutil.ToBytes48([]byte("pubkey"))
var feeRecipient [20]byte
require.NoError(t, s.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{{Message: &eth.ValidatorRegistrationV1{Pubkey: pubkey[:], FeeRecipient: feeRecipient[:]}}}))
assert.Equal(t, true, builder.RegisteredVals[pubkey])
}