Skip to content

Commit

Permalink
Load graffiti from file (#8041)
Browse files Browse the repository at this point in the history
* Pass graffati file and   use it

* Visibility

* Parse test

* More proposal tests

* Gazelle

* Add sequential functionality

* fix length check

* Update priorities. Specified -> random -> default

* Log warn instead return err

* Comment

* E2e test

* Comment

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
  • Loading branch information
terencechain and prestonvanloon committed Dec 4, 2020
1 parent 8ad328d commit b4437e6
Show file tree
Hide file tree
Showing 18 changed files with 391 additions and 1 deletion.
5 changes: 5 additions & 0 deletions endtoend/components/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,14 @@ func StartNewValidatorClient(t *testing.T, config *types.E2EConfig, validatorNum
if err != nil {
t.Fatal(err)
}
gFile, err := helpers.GraffitiYamlFile(e2e.TestParams.TestPath)
if err != nil {
t.Fatal(err)
}
args := []string{
fmt.Sprintf("--datadir=%s/eth2-val-%d", e2e.TestParams.TestPath, index),
fmt.Sprintf("--log-file=%s", file.Name()),
fmt.Sprintf("--graffiti-file=%s", gFile),
fmt.Sprintf("--interop-num-validators=%d", validatorNum),
fmt.Sprintf("--interop-start-index=%d", offset),
fmt.Sprintf("--monitoring-port=%d", e2e.TestParams.ValidatorMetricsPort+index),
Expand Down
1 change: 1 addition & 0 deletions endtoend/evaluators/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
deps = [
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//endtoend/helpers:go_default_library",
"//endtoend/params:go_default_library",
"//endtoend/policies:go_default_library",
"//endtoend/types:go_default_library",
Expand Down
39 changes: 39 additions & 0 deletions endtoend/evaluators/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
"github.com/pkg/errors"
eth "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
corehelpers "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/endtoend/helpers"
e2e "github.com/prysmaticlabs/prysm/endtoend/params"
"github.com/prysmaticlabs/prysm/endtoend/policies"
"github.com/prysmaticlabs/prysm/endtoend/types"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
"golang.org/x/exp/rand"
Expand Down Expand Up @@ -44,6 +46,13 @@ var ProcessesDepositsInBlocks = types.Evaluator{
Evaluation: processesDepositsInBlocks,
}

// VerifyBlockGraffiti ensures the block graffiti is one of the random list.
var VerifyBlockGraffiti = types.Evaluator{
Name: "verify_graffiti_in_blocks_epoch_%d",
Policy: policies.AfterNthEpoch(0),
Evaluation: verifyGraffitiInBlocks,
}

// ActivatesDepositedValidators ensures the expected amount of validator deposits are activated into the state.
var ActivatesDepositedValidators = types.Evaluator{
Name: "processes_deposit_validators_epoch_%d",
Expand Down Expand Up @@ -109,6 +118,36 @@ func processesDepositsInBlocks(conns ...*grpc.ClientConn) error {
return nil
}

func verifyGraffitiInBlocks(conns ...*grpc.ClientConn) error {
conn := conns[0]
client := eth.NewBeaconChainClient(conn)

chainHead, err := client.GetChainHead(context.Background(), &ptypes.Empty{})
if err != nil {
return errors.Wrap(err, "failed to get chain head")
}

req := &eth.ListBlocksRequest{QueryFilter: &eth.ListBlocksRequest_Epoch{Epoch: chainHead.HeadEpoch - 1}}
blks, err := client.ListBlocks(context.Background(), req)
if err != nil {
return errors.Wrap(err, "failed to get blocks from beacon-chain")
}
for _, blk := range blks.BlockContainers {
var e bool
for _, graffiti := range helpers.Graffiti {
if bytes.Equal(bytesutil.PadTo([]byte(graffiti), 32), blk.Block.Block.Body.Graffiti) {
e = true
break
}
}
if !e && blk.Block.Block.Slot != 0 {
return errors.New("could not get graffiti from the list")
}
}

return nil
}

func activatesDepositedValidators(conns ...*grpc.ClientConn) error {
conn := conns[0]
client := eth.NewBeaconChainClient(conn)
Expand Down
16 changes: 16 additions & 0 deletions endtoend/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
maxFileBufferSize = 1024 * 1024
)

var Graffiti = []string{"Sushi", "Ramen", "Takoyaki"}

// DeleteAndCreateFile checks if the file path given exists, if it does, it deletes it and creates a new file.
// If not, it just creates the requested file.
func DeleteAndCreateFile(tmpPath, fileName string) (*os.File, error) {
Expand Down Expand Up @@ -83,6 +85,20 @@ func WaitForTextInFile(file *os.File, text string) error {
}
}

func GraffitiYamlFile(testDir string) (string, error) {
b := []byte(`default: "Rice"
random:
- "Sushi"
- "Ramen"
- "Takoyaki"
`)
f := filepath.Join(testDir, "graffiti.yaml")
if err := ioutil.WriteFile(f, b, os.ModePerm); err != nil {
return "", err
}
return f, nil
}

// LogOutput logs the output of all log files made.
func LogOutput(t *testing.T, config *types.E2EConfig) {
// Log out errors from beacon chain nodes.
Expand Down
1 change: 1 addition & 0 deletions endtoend/minimal_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func TestEndToEnd_MinimalConfig(t *testing.T) {
ev.ValidatorsParticipating,
ev.FinalizationOccurs,
ev.ProcessesDepositsInBlocks,
ev.VerifyBlockGraffiti,
ev.ActivatesDepositedValidators,
ev.DepositedValidatorsAreActive,
ev.ProposeVoluntaryExit,
Expand Down
3 changes: 3 additions & 0 deletions validator/client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ go_library(
"//shared/grpcutils:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/rand:go_default_library",
"//shared/slotutil:go_default_library",
"//shared/timeutils:go_default_library",
"//validator/accounts/wallet:go_default_library",
"//validator/db:go_default_library",
"//validator/db/kv:go_default_library",
"//validator/graffiti:go_default_library",
"//validator/keymanager:go_default_library",
"//validator/keymanager/imported:go_default_library",
"//validator/slashing-protection:go_default_library",
Expand Down Expand Up @@ -96,6 +98,7 @@ go_test(
"//shared/timeutils:go_default_library",
"//validator/db/kv:go_default_library",
"//validator/db/testing:go_default_library",
"//validator/graffiti:go_default_library",
"//validator/testing:go_default_library",
"@com_github_gogo_protobuf//types:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
Expand Down
49 changes: 48 additions & 1 deletion validator/client/propose.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package client
import (
"context"
"fmt"
"time"

"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/rand"
"github.com/prysmaticlabs/prysm/shared/timeutils"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
Expand Down Expand Up @@ -52,11 +54,19 @@ func (v *validator) ProposeBlock(ctx context.Context, slot uint64, pubKey [48]by
return
}

g, err := v.getGraffiti(ctx, pubKey)
if err != nil {
// Graffiti is not a critical enough to fail block production and cause
// validator to miss block reward. When failed, validator should continue
// to produce the block.
log.WithError(err).Warn("Could not get graffiti")
}

// Request block from beacon node
b, err := v.validatorClient.GetBlock(ctx, &ethpb.BlockRequest{
Slot: slot,
RandaoReveal: randaoReveal,
Graffiti: v.graffiti,
Graffiti: g,
})
if err != nil {
log.WithField("blockSlot", slot).WithError(err).Error("Failed to request block from beacon node")
Expand Down Expand Up @@ -257,3 +267,40 @@ func signVoluntaryExit(
}
return sig.Marshal(), nil
}

// Gets the graffiti from cli or file for the validator public key.
func (v *validator) getGraffiti(ctx context.Context, pubKey [48]byte) ([]byte, error) {
// When specified, default graffiti from the command line takes the first priority.
if len(v.graffiti) != 0 {
return v.graffiti, nil
}

if v.graffitiStruct == nil {
return nil, errors.New("graffitiStruct can't be nil")
}

// When specified, individual validator specified graffiti takes the second priority.
idx, err := v.validatorClient.ValidatorIndex(ctx, &ethpb.ValidatorIndexRequest{PublicKey: pubKey[:]})
if err != nil {
return []byte{}, err
}
g, ok := v.graffitiStruct.Specific[idx.Index]
if ok {
return []byte(g), nil
}

// When specified, a graffiti from the random list in the file take third priority.
if len(v.graffitiStruct.Random) != 0 {
r := rand.NewGenerator()
r.Seed(time.Now().Unix())
i := r.Uint64() % uint64(len(v.graffitiStruct.Random))
return []byte(v.graffitiStruct.Random[i]), nil
}

// Finally, default graffiti if specified in the file will be used.
if v.graffitiStruct.Default != "" {
return []byte(v.graffitiStruct.Default), nil
}

return []byte{}, nil
}
83 changes: 83 additions & 0 deletions validator/client/propose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"errors"
"strings"
"testing"
"time"

Expand All @@ -21,6 +22,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/testutil/require"
"github.com/prysmaticlabs/prysm/validator/db/kv"
testing2 "github.com/prysmaticlabs/prysm/validator/db/testing"
"github.com/prysmaticlabs/prysm/validator/graffiti"
logTest "github.com/sirupsen/logrus/hooks/test"
)

Expand Down Expand Up @@ -622,3 +624,84 @@ func TestSignBlock(t *testing.T) {
// proposer domain
require.DeepEqual(t, proposerDomain, domain.SignatureDomain)
}

func TestGetGraffiti_Ok(t *testing.T) {
ctrl := gomock.NewController(t)
m := &mocks{
validatorClient: mock.NewMockBeaconNodeValidatorClient(ctrl),
}
pubKey := [48]byte{'a'}
tests := []struct {
name string
v *validator
want []byte
}{
{name: "use default cli graffiti",
v: &validator{
graffiti: []byte{'b'},
graffitiStruct: &graffiti.Graffiti{
Default: "c",
Random: []string{"d", "e"},
Specific: map[uint64]string{
1: "f",
2: "g",
},
},
},
want: []byte{'b'},
},
{name: "use default file graffiti",
v: &validator{
validatorClient: m.validatorClient,
graffitiStruct: &graffiti.Graffiti{
Default: "c",
},
},
want: []byte{'c'},
},
{name: "use random file graffiti",
v: &validator{
validatorClient: m.validatorClient,
graffitiStruct: &graffiti.Graffiti{
Random: []string{"d"},
Default: "c",
},
},
want: []byte{'d'},
},
{name: "use validator file graffiti, has validator",
v: &validator{
validatorClient: m.validatorClient,
graffitiStruct: &graffiti.Graffiti{
Random: []string{"d"},
Default: "c",
Specific: map[uint64]string{
1: "f",
2: "g",
},
},
},
want: []byte{'g'},
},
{name: "use validator file graffiti, none specified",
v: &validator{
validatorClient: m.validatorClient,
graffitiStruct: &graffiti.Graffiti{},
},
want: []byte{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !strings.Contains(tt.name, "use default cli graffiti") {
m.validatorClient.EXPECT().
ValidatorIndex(gomock.Any(), &ethpb.ValidatorIndexRequest{PublicKey: pubKey[:]}).
Return(&ethpb.ValidatorIndexResponse{Index: 2}, nil)
}
got, err := tt.v.getGraffiti(context.Background(), pubKey)
require.NoError(t, err)
require.DeepEqual(t, tt.want, got)
})
}
}
5 changes: 5 additions & 0 deletions validator/client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
"github.com/prysmaticlabs/prysm/validator/db"
"github.com/prysmaticlabs/prysm/validator/graffiti"
"github.com/prysmaticlabs/prysm/validator/keymanager"
"github.com/prysmaticlabs/prysm/validator/keymanager/imported"
slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection"
Expand Down Expand Up @@ -74,6 +75,7 @@ type ValidatorService struct {
keyManager keymanager.IKeymanager
grpcHeaders []string
graffiti []byte
graffitiStruct *graffiti.Graffiti
}

// Config for the validator service.
Expand All @@ -94,6 +96,7 @@ type Config struct {
CertFlag string
DataDir string
GrpcHeadersFlag string
GraffitiStruct *graffiti.Graffiti
}

// NewValidatorService creates a new validator service for the service
Expand All @@ -119,6 +122,7 @@ func NewValidatorService(ctx context.Context, cfg *Config) (*ValidatorService, e
db: cfg.ValDB,
walletInitializedFeed: cfg.WalletInitializedFeed,
useWeb: cfg.UseWeb,
graffitiStruct: cfg.GraffitiStruct,
}, nil
}

Expand Down Expand Up @@ -195,6 +199,7 @@ func (v *ValidatorService) Start() {
voteStats: voteStats{startEpoch: ^uint64(0)},
useWeb: v.useWeb,
walletInitializedFeed: v.walletInitializedFeed,
graffitiStruct: v.graffitiStruct,
}
go run(v.ctx, v.validator)
go v.recheckKeys(v.ctx)
Expand Down
2 changes: 2 additions & 0 deletions validator/client/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/prysmaticlabs/prysm/validator/accounts/wallet"
vdb "github.com/prysmaticlabs/prysm/validator/db"
"github.com/prysmaticlabs/prysm/validator/db/kv"
"github.com/prysmaticlabs/prysm/validator/graffiti"
"github.com/prysmaticlabs/prysm/validator/keymanager"
slashingprotection "github.com/prysmaticlabs/prysm/validator/slashing-protection"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -77,6 +78,7 @@ type validator struct {
db vdb.Database
graffiti []byte
voteStats voteStats
graffitiStruct *graffiti.Graffiti
}

// Done cleans up the validator.
Expand Down
5 changes: 5 additions & 0 deletions validator/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ var (
Usage: "Enables the web portal for the validator client (work in progress)",
Value: false,
}
// GraffitiFileFlag specifies the file path to load graffiti values.
GraffitiFileFlag = &cli.StringFlag{
Name: "graffiti-file",
Usage: "The path to a YAML file with graffiti values",
}
)

// DefaultValidatorDir returns OS-specific default validator directory.
Expand Down

0 comments on commit b4437e6

Please sign in to comment.