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

Tests for builder service #11214

Merged
merged 17 commits into from
Sep 6, 2022
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
2 changes: 2 additions & 0 deletions api/client/builder/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//monitoring/tracing:go_default_library",
"//network:go_default_library",
"//network/authorization:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
Expand Down
23 changes: 22 additions & 1 deletion api/client/builder/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/pkg/errors"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"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"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -83,6 +85,15 @@ func (*requestLogger) observe(r *http.Request) (e error) {

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.
type Client struct {
hc *http.Client
Expand All @@ -94,7 +105,8 @@ type Client struct {
// `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.
func NewClient(host string, opts ...ClientOpt) (*Client, error) {
u, err := urlForHost(host)
endpoint := covertEndPoint(host)
u, err := urlForHost(endpoint.Url)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -304,3 +316,12 @@ func non200Err(response *http.Response) error {
return errors.Wrap(ErrNotOK, fmt.Sprintf("unsupported error code: %d", response.StatusCode))
}
}

func covertEndPoint(ep string) network.Endpoint {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be useful as a general helper in a common location, we do something like this in execution service as well. Another PR will be great for this

return network.Endpoint{
Url: ep,
Auth: network.AuthorizationData{ // Auth is not used for builder.
Method: authorization.None,
Value: "",
}}
}
14 changes: 14 additions & 0 deletions api/client/builder/testing/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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",
],
)
49 changes: 49 additions & 0 deletions api/client/builder/testing/mock.go
Original file line number Diff line number Diff line change
@@ -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
}
19 changes: 16 additions & 3 deletions beacon-chain/builder/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("@prysm//tools/go:def.bzl", "go_library")
load("@prysm//tools/go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
Expand All @@ -16,8 +16,6 @@ go_library(
"//cmd/beacon-chain/flags:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//network:go_default_library",
"//network/authorization:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_pkg_errors//:go_default_library",
Expand All @@ -28,3 +26,18 @@ go_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",
],
)
30 changes: 14 additions & 16 deletions beacon-chain/builder/option.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package builder

import (
"github.com/prysmaticlabs/prysm/v3/api/client/builder"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
"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"
)

Expand All @@ -14,22 +13,30 @@ type Option func(s *Service) error
// FlagOptions for builder service flag configurations.
func FlagOptions(c *cli.Context) ([]Option, error) {
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{
WithBuilderEndpoints(endpoint),
WithBuilderClient(client),
}
return opts, nil
}

// WithBuilderEndpoints sets the endpoint for the beacon chain builder service.
func WithBuilderEndpoints(endpoint string) Option {
// WithBuilderClient sets the builder client for the beacon chain builder service.
func WithBuilderClient(client builder.BuilderClient) Option {
return func(s *Service) error {
s.cfg.builderEndpoint = covertEndPoint(endpoint)
s.cfg.builderClient = client
return nil
}
}

// 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 {
s.cfg.headFetcher = svc
return nil
Expand All @@ -43,12 +50,3 @@ func WithDatabase(beaconDB db.HeadAccessDatabase) Option {
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: "",
}}
}
24 changes: 10 additions & 14 deletions beacon-chain/builder/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package builder

import (
"context"
"reflect"
"time"

"github.com/pkg/errors"
Expand All @@ -10,7 +11,6 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/network"
v1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
log "github.com/sirupsen/logrus"
Expand All @@ -30,15 +30,15 @@ type BlockBuilder interface {

// config defines a config struct for dependencies into the service.
type config struct {
builderEndpoint network.Endpoint
beaconDB db.HeadAccessDatabase
headFetcher blockchain.HeadFetcher
builderClient builder.BuilderClient
beaconDB db.HeadAccessDatabase
headFetcher blockchain.HeadFetcher
}

// Service defines a service that provides a client for interacting with the beacon chain and MEV relay network.
type Service struct {
cfg *config
c *builder.Client
c builder.BuilderClient
ctx context.Context
cancel context.CancelFunc
}
Expand All @@ -56,18 +56,14 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
return nil, err
}
}
if s.cfg.builderEndpoint.Url != "" {
c, err := builder.NewClient(s.cfg.builderEndpoint.Url)
if err != nil {
return nil, err
}
s.c = c
if s.cfg.builderClient != nil && !reflect.ValueOf(s.cfg.builderClient).IsNil() {
s.c = s.cfg.builderClient

// Is the builder up?
if err := s.c.Status(ctx); err != nil {
log.WithError(err).Error("Failed to check builder status")
} 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. " +
"Builder-constructed blocks or fallback blocks may get orphaned. Use at your own risk!")
}
Expand Down Expand Up @@ -157,7 +153,7 @@ func (s *Service) RegisterValidator(ctx context.Context, reg []*ethpb.SignedVali
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 {
return s.cfg.builderEndpoint.Url != ""
return s.c != nil && !reflect.ValueOf(s.c).IsNil()
}
39 changes: 39 additions & 0 deletions beacon-chain/builder/service_test.go
Original file line number Diff line number Diff line change
@@ -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])
}