Compare commits

...

16 Commits

Author SHA1 Message Date
Raul Jordan
029b31c21e rem deadcode 2022-07-11 14:05:59 -04:00
Raul Jordan
ef7ec31b89 build 2022-07-11 13:58:29 -04:00
Raul Jordan
308f6adff9 gaz 2022-07-11 13:31:36 -04:00
Raul Jordan
d6f6bc18b8 refactor payload funcs 2022-07-11 13:22:32 -04:00
Raul Jordan
11ebb45b61 work with both blinded and full blocks to get payload block hash 2022-07-11 12:20:43 -04:00
terencechain
7fd2c08be3 Propagate FCU invalid error (#10997)
* Wrap fcu error

* Wrap fcu error

* Wrap error better

* More test

* Add else

* Potuz feedback

* Propagate the correct root for fcu

* Always return true for invalid

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-09 20:51:03 +00:00
james-prysm
6695e7c756 Push Proposer Settings: improve warn logs + bug fix (#11013)
* initial commit

* adding unit test to fix bug and test for log inclusion
2022-07-08 16:15:49 -04:00
james-prysm
aeede2165d WEB v.2 (#11007)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-08 19:08:11 +00:00
terencechain
67a15183ca Wrap NewPayload error (#10994)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-08 18:24:48 +00:00
terencechain
208dc80702 Add Capella config (#11003)
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-08 17:40:01 +00:00
terencechain
e80806d455 Check nil before logging "Failed to close response body..." (#11011) 2022-07-08 16:55:12 +00:00
Raul Jordan
80d0a82f9b Engine Client Method to Reconstruct Full Bellatrix Beacon Block (#10998)
* engine reconstructor

* gaz

* powchain pass

* metrics

* deadcode

* prevent nil block

* build

* add test based on recs
2022-07-08 14:10:33 +00:00
Preston Van Loon
f9b3cde005 Batch build API requests for RegisterValidator (#11002)
* Add UnmarshalJSON for SignedValidatorRequest

* add failing test for batch limits

* Add functionality

* gofmt

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-07 23:50:48 +00:00
Raul Jordan
d8f9750387 Only Unmarshal Full Tx Bodies in ExecutionBlock JSON Unmarshaler (#11006)
* full tx unmarshaling fixed

* prefix check
2022-07-07 23:08:44 +00:00
james-prysm
5e8b9dde5b Simplify Push Proposer settings (#11005)
* initial commit

* fixing unit tests

* fixing more unit tests
2022-07-07 22:24:06 +00:00
terencechain
c2caff4230 Minor UX improvement to validator registration (#11004) 2022-07-07 19:32:02 +00:00
39 changed files with 1012 additions and 484 deletions

View File

@@ -342,9 +342,9 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
sha256 = "4797a7e594a5b1f4c1c8080701613f3ee451b01ec0861499ea7d9b60877a6b23",
sha256 = "98013b40922e54a64996da49b939e0a88fe2456f68eedc5aee4ceba0f8623f71",
urls = [
"https://github.com/prysmaticlabs/prysm-web-ui/releases/download/v1.0.3/prysm-web-ui.tar.gz",
"https://github.com/prysmaticlabs/prysm-web-ui/releases/download/v2.0.0/prysm-web-ui.tar.gz",
],
)

View File

@@ -12,6 +12,7 @@ go_library(
deps = [
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//monitoring/tracing:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
@@ -19,6 +20,7 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
)
@@ -38,6 +40,7 @@ go_test(
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -12,13 +12,15 @@ import (
"text/template"
"time"
"github.com/pkg/errors"
mathprysm "github.com/prysmaticlabs/prysm/math"
"go.opencensus.io/trace"
"golang.org/x/sync/errgroup"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/monitoring/tracing"
v1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"go.opencensus.io/trace"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
log "github.com/sirupsen/logrus"
)
@@ -32,6 +34,8 @@ const (
var errMalformedHostname = errors.New("hostname must include port, separated by one colon, like example.com:3500")
var errMalformedRequest = errors.New("required request data are missing")
const registerValidatorBatchLimit = 100
// ClientOpt is a functional option for the Client type (http.Client wrapper)
type ClientOpt func(*Client)
@@ -161,7 +165,9 @@ func (c *Client) do(ctx context.Context, method string, path string, body io.Rea
}
defer func() {
closeErr := r.Body.Close()
log.WithError(closeErr).Error("Failed to close response body")
if closeErr != nil {
log.WithError(closeErr).Error("Failed to close response body")
}
}()
if r.StatusCode != http.StatusOK {
err = non200Err(r)
@@ -223,18 +229,30 @@ func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValid
tracing.AnnotateError(span, err)
return err
}
vs := make([]*SignedValidatorRegistration, len(svr))
for i := 0; i < len(svr); i++ {
vs[i] = &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[i]}
}
body, err := json.Marshal(vs)
if err != nil {
err := errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
eg, ctx := errgroup.WithContext(ctx)
for i := 0; i < len(svr); i += registerValidatorBatchLimit {
end := int(mathprysm.Min(uint64(len(svr)), uint64(i+registerValidatorBatchLimit))) // lint:ignore uintcast -- Request will never exceed int.
vs := make([]*SignedValidatorRegistration, 0, registerValidatorBatchLimit)
for j := i; j < end; j++ {
vs = append(vs, &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[j]})
}
body, err := json.Marshal(vs)
if err != nil {
err := errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
}
eg.Go(func() error {
ctx, span := trace.StartSpan(ctx, "builder.client.RegisterValidator.Go")
defer span.End()
span.AddAttributes(trace.Int64Attribute("reqs", int64(len(vs))))
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
return err
})
}
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
return err
return eg.Wait()
}
// SubmitBlindedBlock calls the builder API endpoint that binds the validator to the builder and submits the block.

View File

@@ -3,6 +3,7 @@ package builder
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
@@ -108,6 +109,52 @@ func TestClient_RegisterValidator(t *testing.T) {
require.NoError(t, c.RegisterValidator(ctx, []*eth.SignedValidatorRegistrationV1{reg}))
}
func TestClient_RegisterValidator_Over100Requests(t *testing.T) {
reqs := make([]*eth.SignedValidatorRegistrationV1, 301)
for i := 0; i < len(reqs); i++ {
reqs[i] = &eth.SignedValidatorRegistrationV1{
Message: &eth.ValidatorRegistrationV1{
FeeRecipient: ezDecode(t, params.BeaconConfig().EthBurnAddressHex),
GasLimit: 23,
Timestamp: 42,
Pubkey: []byte(fmt.Sprint(i)),
},
}
}
var total int
ctx := context.Background()
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
body, err := io.ReadAll(r.Body)
require.NoError(t, r.Body.Close())
require.NoError(t, err)
recvd := make([]*SignedValidatorRegistration, 0)
require.NoError(t, json.Unmarshal(body, &recvd))
if len(recvd) > registerValidatorBatchLimit {
t.Errorf("Number of requests (%d) exceeds limit (%d)", len(recvd), registerValidatorBatchLimit)
}
total += len(recvd)
require.Equal(t, http.MethodPost, r.Method)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBuffer(nil)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
require.NoError(t, c.RegisterValidator(ctx, reqs))
require.Equal(t, len(reqs), total)
}
func TestClient_GetHeader(t *testing.T) {
ctx := context.Background()
expectedPath := "/eth/v1/builder/header/23/0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2/0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"

View File

@@ -31,6 +31,22 @@ func (r *SignedValidatorRegistration) MarshalJSON() ([]byte, error) {
})
}
func (r *SignedValidatorRegistration) UnmarshalJSON(b []byte) error {
if r.SignedValidatorRegistrationV1 == nil {
r.SignedValidatorRegistrationV1 = &eth.SignedValidatorRegistrationV1{}
}
o := struct {
Message *ValidatorRegistration `json:"message,omitempty"`
Signature hexutil.Bytes `json:"signature,omitempty"`
}{}
if err := json.Unmarshal(b, &o); err != nil {
return err
}
r.Message = o.Message.ValidatorRegistrationV1
r.Signature = o.Signature
return nil
}
func (r *ValidatorRegistration) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
FeeRecipient hexutil.Bytes `json:"fee_recipient,omitempty"`
@@ -45,6 +61,33 @@ func (r *ValidatorRegistration) MarshalJSON() ([]byte, error) {
})
}
func (r *ValidatorRegistration) UnmarshalJSON(b []byte) error {
if r.ValidatorRegistrationV1 == nil {
r.ValidatorRegistrationV1 = &eth.ValidatorRegistrationV1{}
}
o := struct {
FeeRecipient hexutil.Bytes `json:"fee_recipient,omitempty"`
GasLimit string `json:"gas_limit,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
Pubkey hexutil.Bytes `json:"pubkey,omitempty"`
}{}
if err := json.Unmarshal(b, &o); err != nil {
return err
}
r.FeeRecipient = o.FeeRecipient
r.Pubkey = o.Pubkey
var err error
if r.GasLimit, err = strconv.ParseUint(o.GasLimit, 10, 64); err != nil {
return errors.Wrap(err, "failed to parse gas limit")
}
if r.Timestamp, err = strconv.ParseUint(o.Timestamp, 10, 64); err != nil {
return errors.Wrap(err, "failed to parse timestamp")
}
return nil
}
type Uint256 struct {
*big.Int
}

View File

@@ -9,6 +9,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto"
"github.com/prysmaticlabs/go-bitfield"
v1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -31,7 +32,8 @@ func TestSignedValidatorRegistration_MarshalJSON(t *testing.T) {
},
Signature: make([]byte, 96),
}
je, err := json.Marshal(&SignedValidatorRegistration{SignedValidatorRegistrationV1: svr})
a := &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr}
je, err := json.Marshal(a)
require.NoError(t, err)
// decode with a struct w/ plain strings so we can check the string encoding of the hex fields
un := struct {
@@ -45,6 +47,14 @@ func TestSignedValidatorRegistration_MarshalJSON(t *testing.T) {
require.Equal(t, "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", un.Signature)
require.Equal(t, "0x0000000000000000000000000000000000000000", un.Message.FeeRecipient)
require.Equal(t, "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", un.Message.Pubkey)
t.Run("roundtrip", func(t *testing.T) {
b := &SignedValidatorRegistration{}
if err := json.Unmarshal(je, b); err != nil {
require.NoError(t, err)
}
require.Equal(t, proto.Equal(a.SignedValidatorRegistrationV1, b.SignedValidatorRegistrationV1), true)
})
}
var testExampleHeaderResponse = `{

View File

@@ -4,9 +4,9 @@ import "github.com/pkg/errors"
var (
// ErrInvalidPayload is returned when the payload is invalid
ErrInvalidPayload = errors.New("received an INVALID payload from execution engine")
ErrInvalidPayload = invalidBlock{error: errors.New("received an INVALID payload from execution engine")}
// ErrInvalidBlockHashPayloadStatus is returned when the payload has invalid block hash.
ErrInvalidBlockHashPayloadStatus = errors.New("received an INVALID_BLOCK_HASH payload from execution engine")
ErrInvalidBlockHashPayloadStatus = invalidBlock{error: errors.New("received an INVALID_BLOCK_HASH payload from execution engine")}
// ErrUndefinedExecutionEngineError is returned when the execution engine returns an error that is not defined
ErrUndefinedExecutionEngineError = errors.New("received an undefined ee error")
// errNilFinalizedInStore is returned when a nil finalized checkpt is returned from store.
@@ -30,7 +30,7 @@ var (
// errWSBlockNotFoundInEpoch is returned when a block is not found in the WS cache or DB within epoch.
errWSBlockNotFoundInEpoch = errors.New("weak subjectivity root not found in db within epoch")
// errNotDescendantOfFinalized is returned when a block is not a descendant of the finalized checkpoint
errNotDescendantOfFinalized = invalidBlock{errors.New("not descendant of finalized checkpoint")}
errNotDescendantOfFinalized = invalidBlock{error: errors.New("not descendant of finalized checkpoint")}
)
// An invalid block is the block that fails state transition based on the core protocol rules.
@@ -41,16 +41,17 @@ var (
// The block violates certain fork choice rules (before finalized slot, not finalized ancestor)
type invalidBlock struct {
error
root [32]byte
}
type invalidBlockError interface {
Error() string
InvalidBlock() bool
BlockRoot() [32]byte
}
// InvalidBlock returns true for `invalidBlock`.
func (e invalidBlock) InvalidBlock() bool {
return true
// BlockRoot returns the invalid block root.
func (e invalidBlock) BlockRoot() [32]byte {
return e.root
}
// IsInvalidBlock returns true if the error has `invalidBlock`.
@@ -58,9 +59,22 @@ func IsInvalidBlock(e error) bool {
if e == nil {
return false
}
d, ok := e.(invalidBlockError)
_, ok := e.(invalidBlockError)
if !ok {
return IsInvalidBlock(errors.Unwrap(e))
}
return d.InvalidBlock()
return true
}
// InvalidBlockRoot returns the invalid block root. If the error
// doesn't have an invalid blockroot. [32]byte{} is returned.
func InvalidBlockRoot(e error) [32]byte {
if e == nil {
return [32]byte{}
}
d, ok := e.(invalidBlockError)
if !ok {
return [32]byte{}
}
return d.BlockRoot()
}

View File

@@ -8,10 +8,18 @@ import (
)
func TestIsInvalidBlock(t *testing.T) {
require.Equal(t, false, IsInvalidBlock(ErrInvalidPayload))
err := invalidBlock{ErrInvalidPayload}
require.Equal(t, true, IsInvalidBlock(ErrInvalidPayload)) // Already wrapped.
err := invalidBlock{error: ErrInvalidPayload}
require.Equal(t, true, IsInvalidBlock(err))
newErr := errors.Wrap(err, "wrap me")
require.Equal(t, true, IsInvalidBlock(newErr))
}
func TestInvalidBlockRoot(t *testing.T) {
require.Equal(t, [32]byte{}, InvalidBlockRoot(ErrUndefinedExecutionEngineError))
require.Equal(t, [32]byte{}, InvalidBlockRoot(ErrInvalidPayload))
err := invalidBlock{error: ErrInvalidPayload, root: [32]byte{'a'}}
require.Equal(t, [32]byte{'a'}, InvalidBlockRoot(err))
}

View File

@@ -39,24 +39,27 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
headBlk := arg.headBlock
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
return nil, errors.New("nil head block")
log.Error("Head block is nil")
return nil, nil
}
// Must not call fork choice updated until the transition conditions are met on the Pow network.
isExecutionBlk, err := blocks.IsExecutionBlock(headBlk.Body())
if err != nil {
return nil, errors.Wrap(err, "could not determine if block is execution block")
log.WithError(err).Error("Could not determine if head block is execution block")
return nil, nil
}
if !isExecutionBlk {
return nil, nil
}
headPayload, err := headBlk.Body().ExecutionPayload()
blockHashFromPayload, err := blocks.BlockHashFromExecutionPayload(headBlk)
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload")
log.WithError(err).Error("Could not get block hash for block from payload")
return nil, nil
}
finalizedHash := s.ForkChoicer().FinalizedPayloadBlockHash()
justifiedHash := s.ForkChoicer().JustifiedPayloadBlockHash()
fcs := &enginev1.ForkchoiceState{
HeadBlockHash: headPayload.BlockHash,
HeadBlockHash: blockHashFromPayload[:],
SafeBlockHash: justifiedHash[:],
FinalizedBlockHash: finalizedHash[:],
}
@@ -64,7 +67,8 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
nextSlot := s.CurrentSlot() + 1 // Cache payload ID for next slot proposer.
hasAttr, attr, proposerId, err := s.getPayloadAttribute(ctx, arg.headState, nextSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get payload attribute")
log.WithError(err).Error("Could not get head payload attribute")
return nil, nil
}
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attr)
@@ -74,32 +78,41 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
forkchoiceUpdatedOptimisticNodeCount.Inc()
log.WithFields(logrus.Fields{
"headSlot": headBlk.Slot(),
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(headPayload.BlockHash)),
"headPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(blockHashFromPayload[:])),
"finalizedPayloadBlockHash": fmt.Sprintf("%#x", bytesutil.Trunc(finalizedHash[:])),
}).Info("Called fork choice updated with optimistic block")
return payloadID, s.optimisticCandidateBlock(ctx, headBlk)
err := s.optimisticCandidateBlock(ctx, headBlk)
if err != nil {
log.WithError(err).Error("Optimistic block failed to be candidate")
}
return payloadID, nil
case powchain.ErrInvalidPayloadStatus:
newPayloadInvalidNodeCount.Inc()
headRoot := arg.headRoot
invalidRoots, err := s.ForkChoicer().SetOptimisticToInvalid(ctx, headRoot, bytesutil.ToBytes32(headBlk.ParentRoot()), bytesutil.ToBytes32(lastValidHash))
if err != nil {
return nil, err
log.WithError(err).Error("Could not set head root to invalid")
return nil, nil
}
if err := s.removeInvalidBlockAndState(ctx, invalidRoots); err != nil {
return nil, err
log.WithError(err).Error("Could not remove invalid block and state")
return nil, nil
}
r, err := s.cfg.ForkChoiceStore.Head(ctx, s.justifiedBalances.balances)
if err != nil {
return nil, err
log.WithError(err).Error("Could not get head root")
return nil, nil
}
b, err := s.getBlock(ctx, r)
if err != nil {
return nil, err
log.WithError(err).Error("Could not get head block")
return nil, nil
}
st, err := s.cfg.StateGen.StateByRoot(ctx, r)
if err != nil {
return nil, err
log.WithError(err).Error("Could not get head state")
return nil, nil
}
pid, err := s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
headState: st,
@@ -107,7 +120,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
headBlock: b.Block(),
})
if err != nil {
return nil, err
return nil, err // Returning err because it's recursive here.
}
if err := s.saveHead(ctx, r, b, st); err != nil {
@@ -120,15 +133,17 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
"invalidCount": len(invalidRoots),
"newHeadRoot": fmt.Sprintf("%#x", bytesutil.Trunc(r[:])),
}).Warn("Pruned invalid blocks")
return pid, ErrInvalidPayload
return pid, invalidBlock{error: ErrInvalidPayload, root: arg.headRoot}
default:
return nil, errors.WithMessage(ErrUndefinedExecutionEngineError, err.Error())
log.WithError(err).Error(ErrUndefinedExecutionEngineError)
return nil, nil
}
}
forkchoiceUpdatedValidNodeCount.Inc()
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, arg.headRoot); err != nil {
return nil, errors.Wrap(err, "could not set block to valid")
log.WithError(err).Error("Could not set head root to valid")
return nil, nil
}
if hasAttr { // If the forkchoice update call has an attribute, update the proposer payload ID cache.
var pId [8]byte
@@ -138,21 +153,14 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
return payloadID, nil
}
// getPayloadHash returns the payload hash given the block root.
// payloadBlockHashByBeaconBlockRoot returns the payload hash given the block root.
// if the block is before bellatrix fork epoch, it returns the zero hash.
func (s *Service) getPayloadHash(ctx context.Context, root []byte) ([32]byte, error) {
func (s *Service) payloadBlockHashByBeaconBlockRoot(ctx context.Context, root []byte) ([32]byte, error) {
blk, err := s.getBlock(ctx, s.ensureRootNotZeros(bytesutil.ToBytes32(root)))
if err != nil {
return [32]byte{}, err
}
if blocks.IsPreBellatrixVersion(blk.Block().Version()) {
return params.BeaconConfig().ZeroHash, nil
}
payload, err := blk.Block().Body().ExecutionPayload()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not get execution payload")
}
return bytesutil.ToBytes32(payload.BlockHash), nil
return blocks.BlockHashFromExecutionPayload(blk.Block())
}
// notifyForkchoiceUpdate signals execution engine on a new payload.
@@ -173,14 +181,14 @@ func (s *Service) notifyNewPayload(ctx context.Context, postStateVersion int,
body := blk.Block().Body()
enabled, err := blocks.IsExecutionEnabledUsingHeader(postStateHeader, body)
if err != nil {
return false, errors.Wrap(invalidBlock{err}, "could not determine if execution is enabled")
return false, errors.Wrap(invalidBlock{error: err}, "could not determine if execution is enabled")
}
if !enabled {
return true, nil
}
payload, err := body.ExecutionPayload()
if err != nil {
return false, errors.Wrap(invalidBlock{err}, "could not get execution payload")
return false, errors.Wrap(invalidBlock{error: err}, "could not get execution payload")
}
lastValidHash, err := s.cfg.ExecutionEngineCaller.NewPayload(ctx, payload)
switch err {
@@ -212,10 +220,10 @@ func (s *Service) notifyNewPayload(ctx context.Context, postStateVersion int,
"blockRoot": fmt.Sprintf("%#x", root),
"invalidCount": len(invalidRoots),
}).Warn("Pruned invalid blocks")
return false, invalidBlock{ErrInvalidPayload}
return false, ErrInvalidPayload
case powchain.ErrInvalidBlockHashPayloadStatus:
newPayloadInvalidNodeCount.Inc()
return false, invalidBlock{ErrInvalidBlockHashPayloadStatus}
return false, ErrInvalidBlockHashPayloadStatus
default:
return false, errors.WithMessage(ErrUndefinedExecutionEngineError, err.Error())
}
@@ -243,6 +251,7 @@ func (s *Service) optimisticCandidateBlock(ctx context.Context, blk interfaces.B
}
parentIsExecutionBlock, err := blocks.IsExecutionBlock(parent.Block().Body())
if err != nil {
log.Errorf("NOT AN EXECUTION BLOCK: %v", err)
return err
}
if parentIsExecutionBlock {

View File

@@ -75,10 +75,6 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
newForkchoiceErr error
errString string
}{
{
name: "nil block",
errString: "nil head block",
},
{
name: "phase0 block",
blk: func() interfaces.BeaconBlock {
@@ -192,6 +188,10 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
_, err = service.notifyForkchoiceUpdate(ctx, arg)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
if tt.errString == ErrInvalidPayload.Error() {
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, tt.headRoot, InvalidBlockRoot(err)) // Head root should be invalid. Not block root!
}
} else {
require.NoError(t, err)
}
@@ -330,7 +330,9 @@ func Test_NotifyForkchoiceUpdateRecursive_Protoarray(t *testing.T) {
headRoot: brg,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
require.ErrorIs(t, ErrInvalidPayload, err)
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, brf, InvalidBlockRoot(err))
// Ensure Head is D
headRoot, err = fcs.Head(ctx, service.justifiedBalances.balances)
require.NoError(t, err)
@@ -473,7 +475,9 @@ func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
headRoot: brg,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
require.ErrorIs(t, ErrInvalidPayload, err)
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, brf, InvalidBlockRoot(err))
// Ensure Head is D
headRoot, err = fcs.Head(ctx, service.justifiedBalances.balances)
require.NoError(t, err)
@@ -1137,7 +1141,7 @@ func TestService_getPayloadHash(t *testing.T) {
service, err := NewService(ctx, opts...)
require.NoError(t, err)
_, err = service.getPayloadHash(ctx, []byte{})
_, err = service.payloadBlockHashByBeaconBlockRoot(ctx, []byte{})
require.ErrorIs(t, errBlockNotFoundInCacheOrDB, err)
b := util.NewBeaconBlock()
@@ -1147,7 +1151,7 @@ func TestService_getPayloadHash(t *testing.T) {
require.NoError(t, err)
require.NoError(t, service.saveInitSyncBlock(ctx, r, wsb))
h, err := service.getPayloadHash(ctx, r[:])
h, err := service.payloadBlockHashByBeaconBlockRoot(ctx, r[:])
require.NoError(t, err)
require.DeepEqual(t, params.BeaconConfig().ZeroHash, h)
@@ -1160,7 +1164,7 @@ func TestService_getPayloadHash(t *testing.T) {
require.NoError(t, err)
require.NoError(t, service.saveInitSyncBlock(ctx, r, wsb))
h, err = service.getPayloadHash(ctx, r[:])
h, err = service.payloadBlockHashByBeaconBlockRoot(ctx, r[:])
require.NoError(t, err)
require.DeepEqual(t, [32]byte{'a'}, h)
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -52,6 +53,14 @@ func logStateTransitionData(b interfaces.BeaconBlock) error {
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)))
log = log.WithField("txCount", len(p.Transactions))
}
if b.Version() == version.BellatrixBlind {
p, err := b.Body().ExecutionPayloadHeader()
if err != nil {
return err
}
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)))
log = log.WithField("txsRoot", fmt.Sprintf("%#x", bytesutil.Trunc(p.TransactionsRoot)))
}
log.Info("Finished applying state transition")
return nil
}
@@ -98,19 +107,47 @@ func logPayload(block interfaces.BeaconBlock) error {
if !isExecutionBlk {
return nil
}
var gasLimit, gasUsed float64
var blockNum uint64
var blockHash, parentHash []byte
payload, err := block.Body().ExecutionPayload()
if err != nil {
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
payloadHeader, err := block.Body().ExecutionPayloadHeader()
if err != nil {
return err
}
if payloadHeader == nil {
log.Warn("Nil execution payload header in block")
return nil
}
gasLimit = float64(payloadHeader.GasLimit)
gasUsed = float64(payloadHeader.GasUsed)
blockNum = payloadHeader.BlockNumber
blockHash = payloadHeader.BlockHash
parentHash = payloadHeader.ParentHash
case err != nil:
return err
default:
if payload == nil {
log.Warn("Nil execution payload in block")
return nil
}
gasLimit = float64(payload.GasLimit)
gasUsed = float64(payload.GasUsed)
blockNum = payload.BlockNumber
blockHash = payload.BlockHash
parentHash = payload.ParentHash
}
if payload.GasLimit == 0 {
if gasLimit == 0 {
return errors.New("gas limit should not be 0")
}
gasUtilized := float64(payload.GasUsed) / float64(payload.GasLimit)
gasUtilized := gasUsed / gasLimit
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.ParentHash)),
"blockNumber": payload.BlockNumber,
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(blockHash)),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(parentHash)),
"blockNumber": blockNum,
"gasUtilized": fmt.Sprintf("%.2f", gasUtilized),
}).Debug("Synced new payload")
return nil

View File

@@ -10,12 +10,12 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/time/slots"
"github.com/sirupsen/logrus"
)
@@ -41,17 +41,14 @@ func (s *Service) validateMergeBlock(ctx context.Context, b interfaces.SignedBea
if err := wrapper.BeaconBlockIsNil(b); err != nil {
return err
}
payload, err := b.Block().Body().ExecutionPayload()
parentHashForPayload, err := blocks.ParentBlockHashFromExecutionPayload(b.Block())
if err != nil {
return err
}
if payload == nil {
return errors.New("nil execution payload")
}
if err := validateTerminalBlockHash(b.Block().Slot(), payload); err != nil {
if err := validateTerminalBlockHash(b.Block().Slot(), parentHashForPayload[:]); err != nil {
return errors.Wrap(err, "could not validate terminal block hash")
}
mergeBlockParentHash, mergeBlockTD, err := s.getBlkParentHashAndTD(ctx, payload.ParentHash)
mergeBlockParentHash, mergeBlockTD, err := s.getBlkParentHashAndTD(ctx, parentHashForPayload[:])
if err != nil {
return errors.Wrap(err, "could not get merge block parent hash and total difficulty")
}
@@ -66,12 +63,12 @@ func (s *Service) validateMergeBlock(ctx context.Context, b interfaces.SignedBea
if !valid {
err := fmt.Errorf("invalid TTD, configTTD: %s, currentTTD: %s, parentTTD: %s",
params.BeaconConfig().TerminalTotalDifficulty, mergeBlockTD, mergeBlockParentTD)
return invalidBlock{err}
return invalidBlock{error: err}
}
log.WithFields(logrus.Fields{
"slot": b.Block().Slot(),
"mergeBlockHash": common.BytesToHash(payload.ParentHash).String(),
"mergeBlockHash": common.BytesToHash(parentHashForPayload[:]).String(),
"mergeBlockParentHash": common.BytesToHash(mergeBlockParentHash).String(),
"terminalTotalDifficulty": params.BeaconConfig().TerminalTotalDifficulty,
"mergeBlockTotalDifficulty": mergeBlockTD,
@@ -110,14 +107,14 @@ func (s *Service) getBlkParentHashAndTD(ctx context.Context, blkHash []byte) ([]
// assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
// assert block.body.execution_payload.parent_hash == TERMINAL_BLOCK_HASH
// return
func validateTerminalBlockHash(blkSlot types.Slot, payload *enginev1.ExecutionPayload) error {
func validateTerminalBlockHash(blkSlot types.Slot, payloadParentHash []byte) error {
if bytesutil.ToBytes32(params.BeaconConfig().TerminalBlockHash.Bytes()) == [32]byte{} {
return nil
}
if params.BeaconConfig().TerminalBlockHashActivationEpoch > slots.ToEpoch(blkSlot) {
return errors.New("terminal block hash activation epoch not reached")
}
if !bytes.Equal(payload.ParentHash, params.BeaconConfig().TerminalBlockHash.Bytes()) {
if !bytes.Equal(payloadParentHash, params.BeaconConfig().TerminalBlockHash.Bytes()) {
return errors.New("parent hash does not match terminal block hash")
}
return nil

View File

@@ -211,16 +211,16 @@ func Test_getBlkParentHashAndTD(t *testing.T) {
}
func Test_validateTerminalBlockHash(t *testing.T) {
require.NoError(t, validateTerminalBlockHash(1, &enginev1.ExecutionPayload{}))
require.NoError(t, validateTerminalBlockHash(1, make([]byte, 32)))
cfg := params.BeaconConfig()
cfg.TerminalBlockHash = [32]byte{0x01}
params.OverrideBeaconConfig(cfg)
require.ErrorContains(t, "terminal block hash activation epoch not reached", validateTerminalBlockHash(1, &enginev1.ExecutionPayload{}))
require.ErrorContains(t, "terminal block hash activation epoch not reached", validateTerminalBlockHash(1, make([]byte, 32)))
cfg.TerminalBlockHashActivationEpoch = 0
params.OverrideBeaconConfig(cfg)
require.ErrorContains(t, "parent hash does not match terminal block hash", validateTerminalBlockHash(1, &enginev1.ExecutionPayload{}))
require.ErrorContains(t, "parent hash does not match terminal block hash", validateTerminalBlockHash(1, make([]byte, 32)))
require.NoError(t, validateTerminalBlockHash(1, &enginev1.ExecutionPayload{ParentHash: cfg.TerminalBlockHash.Bytes()}))
require.NoError(t, validateTerminalBlockHash(1, cfg.TerminalBlockHash.Bytes()))
}

View File

@@ -97,7 +97,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
defer span.End()
if err := wrapper.BeaconBlockIsNil(signed); err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
b := signed.Block()
@@ -118,7 +118,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
postState, err := transition.ExecuteStateTransition(ctx, preState, signed)
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
postStateVersion, postStateHeader, err := getStateVersionAndPayload(postState)
if err != nil {
@@ -126,7 +126,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
isValidPayload, err := s.notifyNewPayload(ctx, postStateVersion, postStateHeader, signed)
if err != nil {
return fmt.Errorf("could not verify new payload: %v", err)
return errors.Wrap(err, "could not validate new payload")
}
if isValidPayload {
if err := s.validateMergeTransitionBlock(ctx, preStateVersion, preStateHeader, signed); err != nil {
@@ -184,7 +184,9 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
if err != nil {
log.WithError(err).Warn("Could not update head")
}
s.notifyEngineIfChangedHead(ctx, headRoot)
if err := s.notifyEngineIfChangedHead(ctx, headRoot); err != nil {
return err
}
if err := s.pruneCanonicalAttsFromPool(ctx, blockRoot, signed); err != nil {
return err
@@ -293,7 +295,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
}
if err := wrapper.BeaconBlockIsNil(blks[0]); err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
b := blks[0].Block()
@@ -341,7 +343,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
set, preState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, preState, b)
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
// Save potential boundary states.
if slots.IsEpochStart(preState.Slot()) {
@@ -362,7 +364,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
}
verify, err := sigSet.Verify()
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
if !verify {
return errors.New("batch block signature verification failed")
@@ -587,7 +589,7 @@ func (s *Service) validateMergeTransitionBlock(ctx context.Context, stateVersion
// Skip validation if block has an empty payload.
payload, err := blk.Block().Body().ExecutionPayload()
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
if bellatrix.IsEmptyPayload(payload) {
return nil

View File

@@ -114,7 +114,7 @@ func (s *Service) VerifyFinalizedBlkDescendant(ctx context.Context, root [32]byt
bytesutil.Trunc(root[:]), fSlot, bytesutil.Trunc(bFinalizedRoot),
bytesutil.Trunc(fRoot[:]))
tracing.AnnotateError(span, err)
return invalidBlock{err}
return invalidBlock{error: err}
}
return nil
}
@@ -129,7 +129,7 @@ func (s *Service) verifyBlkFinalizedSlot(b interfaces.BeaconBlock) error {
}
if finalizedSlot >= b.Slot() {
err = fmt.Errorf("block is equal or earlier than finalized block, slot %d < slot %d", b.Slot(), finalizedSlot)
return invalidBlock{err}
return invalidBlock{error: err}
}
return nil
}

View File

@@ -166,33 +166,35 @@ func (s *Service) UpdateHead(ctx context.Context) error {
}).Debug("Head changed due to attestations")
}
s.headLock.RUnlock()
s.notifyEngineIfChangedHead(ctx, newHeadRoot)
if err := s.notifyEngineIfChangedHead(ctx, newHeadRoot); err != nil {
return err
}
return nil
}
// This calls notify Forkchoice Update in the event that the head has changed
func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32]byte) {
func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32]byte) error {
s.headLock.RLock()
if newHeadRoot == [32]byte{} || s.headRoot() == newHeadRoot {
s.headLock.RUnlock()
return
return nil
}
s.headLock.RUnlock()
if !s.hasBlockInInitSyncOrDB(ctx, newHeadRoot) {
log.Debug("New head does not exist in DB. Do nothing")
return // We don't have the block, don't notify the engine and update head.
return nil // We don't have the block, don't notify the engine and update head.
}
newHeadBlock, err := s.getBlock(ctx, newHeadRoot)
if err != nil {
log.WithError(err).Error("Could not get new head block")
return
return nil
}
headState, err := s.cfg.StateGen.StateByRoot(ctx, newHeadRoot)
if err != nil {
log.WithError(err).Error("Could not get state from db")
return
return nil
}
arg := &notifyForkchoiceUpdateArg{
headState: headState,
@@ -201,11 +203,12 @@ func (s *Service) notifyEngineIfChangedHead(ctx context.Context, newHeadRoot [32
}
_, err = s.notifyForkchoiceUpdate(s.ctx, arg)
if err != nil {
log.WithError(err).Error("could not notify forkchoice update")
return err
}
if err := s.saveHead(ctx, newHeadRoot, newHeadBlock, headState); err != nil {
log.WithError(err).Error("could not save head")
}
return nil
}
// This processes fork choice attestations from the pool to account for validator votes and fork choice.

View File

@@ -131,7 +131,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
service, err := NewService(ctx, opts...)
require.NoError(t, err)
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
service.notifyEngineIfChangedHead(ctx, service.headRoot())
require.NoError(t, service.notifyEngineIfChangedHead(ctx, service.headRoot()))
hookErr := "could not notify forkchoice update"
invalidStateErr := "Could not get state from db"
require.LogsDoNotContain(t, hook, invalidStateErr)
@@ -139,7 +139,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
gb, err := wrapper.WrappedSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
require.NoError(t, service.saveInitSyncBlock(ctx, [32]byte{'a'}, gb))
service.notifyEngineIfChangedHead(ctx, [32]byte{'a'})
require.NoError(t, service.notifyEngineIfChangedHead(ctx, [32]byte{'a'}))
require.LogsContain(t, hook, invalidStateErr)
hook.Reset()
@@ -164,7 +164,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
state: st,
}
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1})
service.notifyEngineIfChangedHead(ctx, r1)
require.NoError(t, service.notifyEngineIfChangedHead(ctx, r1))
require.LogsDoNotContain(t, hook, invalidStateErr)
require.LogsDoNotContain(t, hook, hookErr)
@@ -182,7 +182,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
state: st,
}
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1})
service.notifyEngineIfChangedHead(ctx, r1)
require.NoError(t, service.notifyEngineIfChangedHead(ctx, r1))
require.LogsDoNotContain(t, hook, invalidStateErr)
require.LogsDoNotContain(t, hook, hookErr)
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2)
@@ -192,7 +192,7 @@ func TestNotifyEngineIfChangedHead(t *testing.T) {
// Test zero headRoot returns immediately.
headRoot := service.headRoot()
service.notifyEngineIfChangedHead(ctx, [32]byte{})
require.NoError(t, service.notifyEngineIfChangedHead(ctx, [32]byte{}))
require.Equal(t, service.headRoot(), headRoot)
}

View File

@@ -17,6 +17,7 @@ import (
)
var (
ErrNilPayload = errors.New("nil execution payload")
ErrInvalidPayloadBlockHash = errors.New("invalid payload block hash")
ErrInvalidPayloadTimeStamp = errors.New("invalid payload timestamp")
ErrInvalidPayloadPrevRandao = errors.New("invalid payload previous randao")
@@ -67,7 +68,11 @@ func IsExecutionBlock(body interfaces.BeaconBlockBody) (bool, error) {
payload, err := body.ExecutionPayload()
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
return false, nil
payloadHeader, err := body.ExecutionPayloadHeader()
if err != nil {
return false, err
}
return !bellatrix.IsEmptyHeader(payloadHeader), nil
case err != nil:
return false, err
default:
@@ -272,15 +277,60 @@ func ProcessPayloadHeader(st state.BeaconState, header *enginev1.ExecutionPayloa
return st, nil
}
// GetBlockPayloadHash returns the hash of the execution payload of the block
func GetBlockPayloadHash(blk interfaces.BeaconBlock) ([32]byte, error) {
payloadHash := [32]byte{}
// BlockHashFromExecutionPayload reads the block hash field from an execution payload or
// execution payload header contained within a specified beacon block's body.
func BlockHashFromExecutionPayload(blk interfaces.BeaconBlock) ([32]byte, error) {
var blockHashFromPayload [32]byte
if IsPreBellatrixVersion(blk.Version()) {
return payloadHash, nil
return blockHashFromPayload, nil
}
payload, err := blk.Body().ExecutionPayload()
if err != nil {
return payloadHash, err
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
payloadHeader, err := blk.Body().ExecutionPayloadHeader()
if err != nil {
return blockHashFromPayload, err
}
if payloadHeader == nil {
return blockHashFromPayload, ErrNilPayload
}
blockHashFromPayload = bytesutil.ToBytes32(payloadHeader.BlockHash)
case err != nil:
return blockHashFromPayload, err
default:
if payload == nil {
return blockHashFromPayload, ErrNilPayload
}
blockHashFromPayload = bytesutil.ToBytes32(payload.BlockHash)
}
return bytesutil.ToBytes32(payload.BlockHash), nil
return blockHashFromPayload, nil
}
// ParentBlockHashFromExecutionPayload reads the parent hash field from an execution
// payload or execution payload header contained within a specified beacon block's body.
func ParentBlockHashFromExecutionPayload(blk interfaces.BeaconBlock) ([32]byte, error) {
var parentHashFromPayload [32]byte
if IsPreBellatrixVersion(blk.Version()) {
return parentHashFromPayload, nil
}
payload, err := blk.Body().ExecutionPayload()
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
payloadHeader, err := blk.Body().ExecutionPayloadHeader()
if err != nil {
return parentHashFromPayload, err
}
if payloadHeader == nil {
return parentHashFromPayload, ErrNilPayload
}
parentHashFromPayload = bytesutil.ToBytes32(payloadHeader.ParentHash)
case err != nil:
return parentHashFromPayload, err
default:
if payload == nil {
return parentHashFromPayload, ErrNilPayload
}
parentHashFromPayload = bytesutil.ToBytes32(payload.ParentHash)
}
return parentHashFromPayload, nil
}

View File

@@ -549,7 +549,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho
b := chain[i].Block
r := bytesutil.ToBytes32(chain[i-1].Block.ParentRoot())
parentRoot := bytesutil.ToBytes32(b.ParentRoot())
payloadHash, err := blocks.GetBlockPayloadHash(b)
payloadHash, err := blocks.BlockHashFromExecutionPayload(b)
if err != nil {
return err
}

View File

@@ -994,7 +994,7 @@ func (f *ForkChoice) InsertOptimisticChain(ctx context.Context, chain []*forkcho
b := chain[i].Block
r := bytesutil.ToBytes32(chain[i-1].Block.ParentRoot())
parentRoot := bytesutil.ToBytes32(b.ParentRoot())
payloadHash, err := blocks.GetBlockPayloadHash(b)
payloadHash, err := blocks.BlockHashFromExecutionPayload(b)
if err != nil {
return err
}

View File

@@ -39,6 +39,8 @@ go_library(
"//beacon-chain/state/v1:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",
"//contracts/deposit:go_default_library",
"//crypto/hash:go_default_library",
@@ -103,6 +105,7 @@ go_test(
"//beacon-chain/state/stategen:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",

View File

@@ -14,6 +14,8 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/sirupsen/logrus"
@@ -46,6 +48,15 @@ type ForkchoiceUpdatedResponse struct {
PayloadId *pb.PayloadIDBytes `json:"payloadId"`
}
// ExecutionPayloadReconstructor defines a service that can reconstruct a full beacon
// block with an execution payload from a signed beacon block and a connection
// to an execution client's engine API.
type ExecutionPayloadReconstructor interface {
ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error)
}
// EngineCaller defines a client that can interact with an Ethereum
// execution node's engine service via JSON-RPC.
type EngineCaller interface {
@@ -290,6 +301,80 @@ func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*
return result, handleRPCError(err)
}
// ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs
// a beacon block with a full execution payload via the engine API.
func (s *Service) ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error) {
if err := wrapper.BeaconBlockIsNil(blindedBlock); err != nil {
return nil, errors.Wrap(err, "cannot reconstruct bellatrix block from nil data")
}
if !blindedBlock.Block().IsBlinded() {
return nil, errors.New("can only reconstruct block from blinded block format")
}
header, err := blindedBlock.Block().Body().ExecutionPayloadHeader()
if err != nil {
return nil, err
}
executionBlockHash := common.BytesToHash(header.BlockHash)
executionBlock, err := s.ExecutionBlockByHash(ctx, executionBlockHash)
if err != nil {
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
}
if executionBlock == nil {
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionBlockHash)
}
payload, err := fullPayloadFromExecutionBlock(header, executionBlock)
if err != nil {
return nil, err
}
fullBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
if err != nil {
return nil, err
}
reconstructedExecutionPayloadCount.Add(1)
return fullBlock, nil
}
func fullPayloadFromExecutionBlock(
header *pb.ExecutionPayloadHeader, block *pb.ExecutionBlock,
) (*pb.ExecutionPayload, error) {
if header == nil || block == nil {
return nil, errors.New("execution block and header cannot be nil")
}
if !bytes.Equal(header.BlockHash, block.Hash[:]) {
return nil, fmt.Errorf(
"block hash field in execution header %#x does not match execution block hash %#x",
header.BlockHash,
block.Hash,
)
}
txs := make([][]byte, len(block.Transactions))
for i, tx := range block.Transactions {
txBin, err := tx.MarshalBinary()
if err != nil {
return nil, err
}
txs[i] = txBin
}
return &pb.ExecutionPayload{
ParentHash: header.ParentHash,
FeeRecipient: header.FeeRecipient,
StateRoot: header.StateRoot,
ReceiptsRoot: header.ReceiptsRoot,
LogsBloom: header.LogsBloom,
PrevRandao: header.PrevRandao,
BlockNumber: header.BlockNumber,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Timestamp: header.Timestamp,
ExtraData: header.ExtraData,
BaseFeePerGas: header.BaseFeePerGas,
BlockHash: block.Hash[:],
Transactions: txs,
}, nil
}
// Handles errors received from the RPC server according to the specification.
func handleRPCError(err error) error {
if err == nil {

View File

@@ -12,6 +12,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
@@ -19,13 +20,17 @@ import (
mocks "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"google.golang.org/protobuf/proto"
)
var (
_ = ExecutionPayloadReconstructor(&Service{})
_ = EngineCaller(&Service{})
_ = EngineCaller(&mocks.EngineClient{})
)
@@ -386,6 +391,99 @@ func TestClient_HTTP(t *testing.T) {
})
}
func TestReconstructFullBellatrixBlock(t *testing.T) {
ctx := context.Background()
t.Run("nil block", func(t *testing.T) {
service := &Service{}
_, err := service.ReconstructFullBellatrixBlock(ctx, nil)
require.ErrorContains(t, "nil data", err)
})
t.Run("only blinded block", func(t *testing.T) {
want := "can only reconstruct block from blinded block format"
service := &Service{}
bellatrixBlock := util.NewBeaconBlockBellatrix()
wrapped, err := wrapper.WrappedSignedBeaconBlock(bellatrixBlock)
require.NoError(t, err)
_, err = service.ReconstructFullBellatrixBlock(ctx, wrapped)
require.ErrorContains(t, want, err)
})
t.Run("properly reconstructs block with correct payload", func(t *testing.T) {
fix := fixtures()
payload, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
jsonPayload := make(map[string]interface{})
tx := gethtypes.NewTransaction(
0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), 0, big.NewInt(0),
nil,
)
txs := []*gethtypes.Transaction{tx}
encodedBinaryTxs := make([][]byte, 1)
var err error
encodedBinaryTxs[0], err = txs[0].MarshalBinary()
require.NoError(t, err)
payload.Transactions = encodedBinaryTxs
jsonPayload["transactions"] = txs
num := big.NewInt(1)
encodedNum := hexutil.EncodeBig(num)
jsonPayload["hash"] = hexutil.Encode(payload.BlockHash)
jsonPayload["parentHash"] = common.BytesToHash([]byte("parent"))
jsonPayload["sha3Uncles"] = common.BytesToHash([]byte("uncles"))
jsonPayload["miner"] = common.BytesToAddress([]byte("miner"))
jsonPayload["stateRoot"] = common.BytesToHash([]byte("state"))
jsonPayload["transactionsRoot"] = common.BytesToHash([]byte("txs"))
jsonPayload["receiptsRoot"] = common.BytesToHash([]byte("receipts"))
jsonPayload["logsBloom"] = gethtypes.BytesToBloom([]byte("bloom"))
jsonPayload["gasLimit"] = hexutil.EncodeUint64(1)
jsonPayload["gasUsed"] = hexutil.EncodeUint64(2)
jsonPayload["timestamp"] = hexutil.EncodeUint64(3)
jsonPayload["number"] = encodedNum
jsonPayload["extraData"] = common.BytesToHash([]byte("extra"))
jsonPayload["totalDifficulty"] = "0x123456"
jsonPayload["difficulty"] = encodedNum
jsonPayload["size"] = encodedNum
jsonPayload["baseFeePerGas"] = encodedNum
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
defer func() {
require.NoError(t, r.Body.Close())
}()
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": jsonPayload,
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
rpcClient, err := rpc.DialHTTP(srv.URL)
require.NoError(t, err)
defer rpcClient.Close()
service := &Service{}
service.rpcClient = rpcClient
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
blindedBlock.Block.Body.ExecutionPayloadHeader = header
wrapped, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
reconstructed, err := service.ReconstructFullBellatrixBlock(ctx, wrapped)
require.NoError(t, err)
got, err := reconstructed.Block().Body().ExecutionPayload()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
})
}
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
tests := []struct {
name string
@@ -873,6 +971,66 @@ func fixtures() map[string]interface{} {
}
}
func Test_fullPayloadFromExecutionBlock(t *testing.T) {
type args struct {
header *pb.ExecutionPayloadHeader
block *pb.ExecutionBlock
}
wantedHash := common.BytesToHash([]byte("foo"))
tests := []struct {
name string
args args
want *pb.ExecutionPayload
err string
}{
{
name: "nil header fails",
args: args{header: nil, block: &pb.ExecutionBlock{}},
err: "cannot be nil",
},
{
name: "nil block fails",
args: args{header: &pb.ExecutionPayloadHeader{}, block: nil},
err: "cannot be nil",
},
{
name: "block hash field in header and block hash mismatch",
args: args{
header: &pb.ExecutionPayloadHeader{
BlockHash: []byte("foo"),
},
block: &pb.ExecutionBlock{
Hash: common.BytesToHash([]byte("bar")),
},
},
err: "does not match execution block hash",
},
{
name: "ok",
args: args{
header: &pb.ExecutionPayloadHeader{
BlockHash: wantedHash[:],
},
block: &pb.ExecutionBlock{
Hash: wantedHash,
},
},
want: &pb.ExecutionPayload{
BlockHash: wantedHash[:],
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := fullPayloadFromExecutionBlock(tt.args.header, tt.args.block)
if (err != nil) && !strings.Contains(err.Error(), tt.err) {
t.Fatalf("Wanted err %s got %v", tt.err, err)
}
require.DeepEqual(t, tt.want, got)
})
}
}
type testEngineService struct{}
func (*testEngineService) NoArgsRets() {}

View File

@@ -31,4 +31,8 @@ var (
Buckets: []float64{25, 50, 100, 200, 500, 1000, 2000, 4000},
},
)
reconstructedExecutionPayloadCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "reconstructed_execution_payload_count",
Help: "Count the number of execution payloads that are reconstructed using JSON-RPC from payload headers",
})
)

View File

@@ -52,6 +52,8 @@ func TestGetSpec(t *testing.T) {
config.BellatrixForkEpoch = 101
config.ShardingForkVersion = []byte("ShardingForkVersion")
config.ShardingForkEpoch = 102
config.CapellaForkVersion = []byte("CapellaForkVersion")
config.CapellaForkEpoch = 103
config.BLSWithdrawalPrefixByte = byte('b')
config.GenesisDelay = 24
config.SecondsPerSlot = 25
@@ -133,7 +135,7 @@ func TestGetSpec(t *testing.T) {
resp, err := server.GetSpec(context.Background(), &emptypb.Empty{})
require.NoError(t, err)
assert.Equal(t, 99, len(resp.Data))
assert.Equal(t, 101, len(resp.Data))
for k, v := range resp.Data {
switch k {
case "CONFIG_NAME":
@@ -202,6 +204,10 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "0x"+hex.EncodeToString([]byte("ShardingForkVersion")), v)
case "SHARDING_FORK_EPOCH":
assert.Equal(t, "102", v)
case "CAPELLA_FORK_VERSION":
assert.Equal(t, "0x"+hex.EncodeToString([]byte("CapellaForkVersion")), v)
case "CAPELLA_FORK_EPOCH":
assert.Equal(t, "103", v)
case "MIN_ANCHOR_POW_BLOCK_DIFFICULTY":
assert.Equal(t, "1000", v)
case "BLS_WITHDRAWAL_PREFIX":

View File

@@ -170,8 +170,12 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
if err := s.cfg.chain.ReceiveBlock(ctx, b, blkRoot); err != nil {
if blockchain.IsInvalidBlock(err) {
tracing.AnnotateError(span, err)
s.setBadBlock(ctx, blkRoot)
r := blockchain.InvalidBlockRoot(err)
if r != [32]byte{} {
s.setBadBlock(ctx, r) // Setting head block as bad.
} else {
s.setBadBlock(ctx, blkRoot)
}
}
log.Debugf("Could not process block from slot %d: %v", b.Block().Slot(), err)

View File

@@ -31,8 +31,13 @@ func (s *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message)
if err := s.cfg.chain.ReceiveBlock(ctx, signed, root); err != nil {
if blockchain.IsInvalidBlock(err) {
interop.WriteBlockToDisk(signed, true /*failed*/)
s.setBadBlock(ctx, root)
r := blockchain.InvalidBlockRoot(err)
if r != [32]byte{} {
s.setBadBlock(ctx, r) // Setting head block as bad.
} else {
interop.WriteBlockToDisk(signed, true /*failed*/)
s.setBadBlock(ctx, root)
}
}
return err
}

View File

@@ -360,10 +360,10 @@ var (
}
// EnableValidatorRegistrationFlag enables the periodic validator registration API calls that will update the custom builder with validator settings.
EnableValidatorRegistrationFlag = &cli.StringFlag{
EnableValidatorRegistrationFlag = &cli.BoolFlag{
Name: "enable-validator-registration",
Usage: "Enables validator registration APIs (MEV Builder APIs) for the validator client to update settings such as fee recipient and gas limit",
Value: "",
Value: false,
}
)

View File

@@ -138,15 +138,18 @@ type BeaconChainConfig struct {
SlashingProtectionPruningEpochs types.Epoch // SlashingProtectionPruningEpochs defines a period after which all prior epochs are pruned in the validator database.
// Fork-related values.
GenesisForkVersion []byte `yaml:"GENESIS_FORK_VERSION" spec:"true"` // GenesisForkVersion is used to track fork version between state transitions.
AltairForkVersion []byte `yaml:"ALTAIR_FORK_VERSION" spec:"true"` // AltairForkVersion is used to represent the fork version for altair.
AltairForkEpoch types.Epoch `yaml:"ALTAIR_FORK_EPOCH" spec:"true"` // AltairForkEpoch is used to represent the assigned fork epoch for altair.
BellatrixForkVersion []byte `yaml:"BELLATRIX_FORK_VERSION" spec:"true"` // BellatrixForkVersion is used to represent the fork version for bellatrix.
BellatrixForkEpoch types.Epoch `yaml:"BELLATRIX_FORK_EPOCH" spec:"true"` // BellatrixForkEpoch is used to represent the assigned fork epoch for bellatrix.
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
ForkVersionSchedule map[[fieldparams.VersionLength]byte]types.Epoch // Schedule of fork epochs by version.
ForkVersionNames map[[fieldparams.VersionLength]byte]string // Human-readable names of fork versions.
GenesisForkVersion []byte `yaml:"GENESIS_FORK_VERSION" spec:"true"` // GenesisForkVersion is used to track fork version between state transitions.
AltairForkVersion []byte `yaml:"ALTAIR_FORK_VERSION" spec:"true"` // AltairForkVersion is used to represent the fork version for altair.
AltairForkEpoch types.Epoch `yaml:"ALTAIR_FORK_EPOCH" spec:"true"` // AltairForkEpoch is used to represent the assigned fork epoch for altair.
BellatrixForkVersion []byte `yaml:"BELLATRIX_FORK_VERSION" spec:"true"` // BellatrixForkVersion is used to represent the fork version for bellatrix.
BellatrixForkEpoch types.Epoch `yaml:"BELLATRIX_FORK_EPOCH" spec:"true"` // BellatrixForkEpoch is used to represent the assigned fork epoch for bellatrix.
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
CapellaForkVersion []byte `yaml:"CAPELLA_FORK_VERSION" spec:"true"` // CapellaForkVersion is used to represent the fork version for capella.
CapellaForkEpoch types.Epoch `yaml:"CAPELLA_FORK_EPOCH" spec:"true"` // CapellaForkEpoch is used to represent the assigned fork epoch for capella.
ForkVersionSchedule map[[fieldparams.VersionLength]byte]types.Epoch // Schedule of fork epochs by version.
ForkVersionNames map[[fieldparams.VersionLength]byte]string // Human-readable names of fork versions.
// Weak subjectivity values.
SafetyDecay uint64 // SafetyDecay is defined as the loss in the 1/3 consensus safety margin of the casper FFG mechanism.

View File

@@ -18,7 +18,7 @@ import (
"gopkg.in/yaml.v2"
)
var placeholderFields = []string{"UPDATE_TIMEOUT", "INTERVALS_PER_SLOT", "CAPELLA_FORK_VERSION", "CAPELLA_FORK_EPOCH"}
var placeholderFields = []string{"UPDATE_TIMEOUT", "INTERVALS_PER_SLOT"}
func TestLoadConfigFile(t *testing.T) {
// See https://media.githubusercontent.com/media/ethereum/consensus-spec-tests/master/tests/minimal/config/phase0.yaml

View File

@@ -203,6 +203,8 @@ var mainnetBeaconConfig = &BeaconChainConfig{
AltairForkEpoch: mainnetAltairForkEpoch,
BellatrixForkVersion: []byte{2, 0, 0, 0},
BellatrixForkEpoch: mainnetBellatrixForkEpoch,
CapellaForkVersion: []byte{3, 0, 0, 0},
CapellaForkEpoch: math.MaxUint64,
ShardingForkVersion: []byte{4, 0, 0, 0},
ShardingForkEpoch: math.MaxUint64,

View File

@@ -84,6 +84,8 @@ func MinimalSpecConfig() *BeaconChainConfig {
minimalConfig.AltairForkEpoch = math.MaxUint64
minimalConfig.BellatrixForkVersion = []byte{2, 0, 0, 1}
minimalConfig.BellatrixForkEpoch = math.MaxUint64
minimalConfig.CapellaForkVersion = []byte{3, 0, 0, 1}
minimalConfig.CapellaForkEpoch = math.MaxUint64
minimalConfig.ShardingForkVersion = []byte{4, 0, 0, 1}
minimalConfig.ShardingForkEpoch = math.MaxUint64

View File

@@ -3,6 +3,7 @@ package enginev1
import (
"encoding/json"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -74,6 +75,11 @@ func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
// them into a list of geth transaction objects.
txs := make([]*gethtypes.Transaction, len(txsList))
for i, tx := range txsList {
// If the transaction is just a hex string, do not attempt to
// unmarshal into a full transaction object.
if txItem, ok := tx.(string); ok && strings.HasPrefix(txItem, "0x") {
return nil
}
t := &gethtypes.Transaction{}
encodedTx, err := json.Marshal(tx)
if err != nil {

View File

@@ -179,6 +179,133 @@ func TestJsonMarshalUnmarshal(t *testing.T) {
require.DeepEqual(t, want.MixDigest, payloadPb.MixDigest)
require.DeepEqual(t, want.Nonce, payloadPb.Nonce)
})
t.Run("execution block with txs as hashes", func(t *testing.T) {
baseFeePerGas := big.NewInt(1770307273)
want := &gethtypes.Header{
Number: big.NewInt(1),
ParentHash: common.BytesToHash([]byte("parent")),
UncleHash: common.BytesToHash([]byte("uncle")),
Coinbase: common.BytesToAddress([]byte("coinbase")),
Root: common.BytesToHash([]byte("uncle")),
TxHash: common.BytesToHash([]byte("txHash")),
ReceiptHash: common.BytesToHash([]byte("receiptHash")),
Bloom: gethtypes.BytesToBloom([]byte("bloom")),
Difficulty: big.NewInt(2),
GasLimit: 3,
GasUsed: 4,
Time: 5,
BaseFee: baseFeePerGas,
Extra: []byte("extraData"),
MixDigest: common.BytesToHash([]byte("mix")),
Nonce: gethtypes.EncodeNonce(6),
}
enc, err := json.Marshal(want)
require.NoError(t, err)
payloadItems := make(map[string]interface{})
require.NoError(t, json.Unmarshal(enc, &payloadItems))
blockHash := want.Hash()
payloadItems["hash"] = blockHash.String()
payloadItems["totalDifficulty"] = "0x393a2e53de197c"
payloadItems["transactions"] = []string{"0xd57870623ea84ac3e2ffafbee9417fd1263b825b1107b8d606c25460dabeb693"}
encodedPayloadItems, err := json.Marshal(payloadItems)
require.NoError(t, err)
payloadPb := &enginev1.ExecutionBlock{}
require.NoError(t, json.Unmarshal(encodedPayloadItems, payloadPb))
require.DeepEqual(t, blockHash, payloadPb.Hash)
require.DeepEqual(t, want.Number, payloadPb.Number)
require.DeepEqual(t, want.ParentHash, payloadPb.ParentHash)
require.DeepEqual(t, want.UncleHash, payloadPb.UncleHash)
require.DeepEqual(t, want.Coinbase, payloadPb.Coinbase)
require.DeepEqual(t, want.Root, payloadPb.Root)
require.DeepEqual(t, want.TxHash, payloadPb.TxHash)
require.DeepEqual(t, want.ReceiptHash, payloadPb.ReceiptHash)
require.DeepEqual(t, want.Bloom, payloadPb.Bloom)
require.DeepEqual(t, want.Difficulty, payloadPb.Difficulty)
require.DeepEqual(t, payloadItems["totalDifficulty"], payloadPb.TotalDifficulty)
require.DeepEqual(t, want.GasUsed, payloadPb.GasUsed)
require.DeepEqual(t, want.GasLimit, payloadPb.GasLimit)
require.DeepEqual(t, want.Time, payloadPb.Time)
require.DeepEqual(t, want.BaseFee, payloadPb.BaseFee)
require.DeepEqual(t, want.Extra, payloadPb.Extra)
require.DeepEqual(t, want.MixDigest, payloadPb.MixDigest)
require.DeepEqual(t, want.Nonce, payloadPb.Nonce)
// Expect no transaction objects in the unmarshaled data.
require.Equal(t, 0, len(payloadPb.Transactions))
})
t.Run("execution block with full transaction data", func(t *testing.T) {
baseFeePerGas := big.NewInt(1770307273)
want := &gethtypes.Header{
Number: big.NewInt(1),
ParentHash: common.BytesToHash([]byte("parent")),
UncleHash: common.BytesToHash([]byte("uncle")),
Coinbase: common.BytesToAddress([]byte("coinbase")),
Root: common.BytesToHash([]byte("uncle")),
TxHash: common.BytesToHash([]byte("txHash")),
ReceiptHash: common.BytesToHash([]byte("receiptHash")),
Bloom: gethtypes.BytesToBloom([]byte("bloom")),
Difficulty: big.NewInt(2),
GasLimit: 3,
GasUsed: 4,
Time: 5,
BaseFee: baseFeePerGas,
Extra: []byte("extraData"),
MixDigest: common.BytesToHash([]byte("mix")),
Nonce: gethtypes.EncodeNonce(6),
}
enc, err := json.Marshal(want)
require.NoError(t, err)
payloadItems := make(map[string]interface{})
require.NoError(t, json.Unmarshal(enc, &payloadItems))
tx := gethtypes.NewTransaction(
1,
common.BytesToAddress([]byte("hi")),
big.NewInt(0),
21000,
big.NewInt(1e6),
[]byte{},
)
txs := []*gethtypes.Transaction{tx}
blockHash := want.Hash()
payloadItems["hash"] = blockHash.String()
payloadItems["totalDifficulty"] = "0x393a2e53de197c"
payloadItems["transactions"] = txs
encodedPayloadItems, err := json.Marshal(payloadItems)
require.NoError(t, err)
payloadPb := &enginev1.ExecutionBlock{}
require.NoError(t, json.Unmarshal(encodedPayloadItems, payloadPb))
require.DeepEqual(t, blockHash, payloadPb.Hash)
require.DeepEqual(t, want.Number, payloadPb.Number)
require.DeepEqual(t, want.ParentHash, payloadPb.ParentHash)
require.DeepEqual(t, want.UncleHash, payloadPb.UncleHash)
require.DeepEqual(t, want.Coinbase, payloadPb.Coinbase)
require.DeepEqual(t, want.Root, payloadPb.Root)
require.DeepEqual(t, want.TxHash, payloadPb.TxHash)
require.DeepEqual(t, want.ReceiptHash, payloadPb.ReceiptHash)
require.DeepEqual(t, want.Bloom, payloadPb.Bloom)
require.DeepEqual(t, want.Difficulty, payloadPb.Difficulty)
require.DeepEqual(t, payloadItems["totalDifficulty"], payloadPb.TotalDifficulty)
require.DeepEqual(t, want.GasUsed, payloadPb.GasUsed)
require.DeepEqual(t, want.GasLimit, payloadPb.GasLimit)
require.DeepEqual(t, want.Time, payloadPb.Time)
require.DeepEqual(t, want.BaseFee, payloadPb.BaseFee)
require.DeepEqual(t, want.Extra, payloadPb.Extra)
require.DeepEqual(t, want.MixDigest, payloadPb.MixDigest)
require.DeepEqual(t, want.Nonce, payloadPb.Nonce)
require.Equal(t, 1, len(payloadPb.Transactions))
require.DeepEqual(t, txs[0].Hash(), payloadPb.Transactions[0].Hash())
})
}
func TestPayloadIDBytes_MarshalUnmarshalJSON(t *testing.T) {

View File

@@ -972,20 +972,28 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
return err
}
if len(feeRecipients) == 0 {
log.Warnf("no valid validator indices were found, prepare beacon proposer request fee recipients array is empty")
log.Warnf("No valid validator indices were found, prepare beacon proposer request fee recipients array is empty")
return nil
}
if len(feeRecipients) != len(pubkeys) {
log.Warnf("%d public key(s) will not prepare beacon proposer and update fee recipient until a validator index is assigned", len(pubkeys)-len(feeRecipients))
}
if _, err := v.validatorClient.PrepareBeaconProposer(ctx, &ethpb.PrepareBeaconProposerRequest{
Recipients: feeRecipients,
}); err != nil {
return err
}
log.Infoln("Successfully prepared beacon proposer with fee recipient to validator index mapping.")
log.Infoln("Prepared beacon proposer with fee recipient to validator index mapping")
if err := SubmitValidatorRegistration(ctx, v.validatorClient, km.Sign, registerValidatorRequests); err != nil {
return err
if len(registerValidatorRequests) > 0 {
if len(registerValidatorRequests) != len(pubkeys) {
log.Warnf("%d public key(s) will not be included in validator registration until a validator index is assigned", len(pubkeys)-len(registerValidatorRequests))
}
if err := SubmitValidatorRegistration(ctx, v.validatorClient, km.Sign, registerValidatorRequests); err != nil {
return err
}
log.Infoln("Submitted builder validator registration settings for custom builders")
}
log.Infoln("Successfully submitted builder validator registration settings for custom builders.")
return nil
}
@@ -1007,9 +1015,10 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
}
if !foundIndex {
skipAppendToFeeRecipientArray = true
} else {
validatorIndex = ind
v.pubkeyToValidatorIndex[key] = validatorIndex
}
validatorIndex = ind
v.pubkeyToValidatorIndex[key] = validatorIndex
}
if v.ProposerSettings.DefaultConfig != nil {
feeRecipient = v.ProposerSettings.DefaultConfig.FeeRecipient
@@ -1037,13 +1046,15 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex {
log.Warnln("Fee recipient is set to the burn address. You will not be rewarded transaction fees on this setting. Please set a different fee recipient.")
}
// Only include requests with assigned validator index
if !skipAppendToFeeRecipientArray {
validatorToFeeRecipients = append(validatorToFeeRecipients, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
})
}
if enableValidatorRegistration {
if !skipAppendToFeeRecipientArray && enableValidatorRegistration {
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,

View File

@@ -358,7 +358,6 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -369,21 +368,8 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
},
}
v := validator{
validatorClient: client,
keyManager: km,
node: nodeClient,
genesisTime: 1,
pubkeyToValidatorIndex: map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex{pubKey: 1},
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
},
},
validatorClient: client,
keyManager: km,
}
resp := generateMockStatusResponse([][]byte{pubKey[:]})
@@ -399,18 +385,6 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
require.NoError(t, v.WaitForActivation(ctx, nil), "Could not wait for activation")
require.LogsContain(t, hook, "Validator activated")
}
@@ -419,7 +393,6 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -430,21 +403,8 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
},
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex{pubKey: 1},
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
},
},
validatorClient: client,
keyManager: km,
}
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
@@ -461,16 +421,6 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil), "Could not wait for activation")
}
@@ -1040,9 +990,8 @@ func TestAllValidatorsAreExited_CorrectRequest(t *testing.T) {
// If AllValidatorsAreExited does not create the expected request, this test will fail
v := validator{
keyManager: &mockKeymanager{keysMap: keysMap},
validatorClient: client,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
keyManager: &mockKeymanager{keysMap: keysMap},
validatorClient: client,
}
exited, err := v.AllValidatorsAreExited(context.Background())
require.NoError(t, err)
@@ -1511,6 +1460,7 @@ func TestValidator_PushProposerSettings(t *testing.T) {
feeRecipientMap map[types.ValidatorIndex]string
mockExpectedRequests []ExpectedValidatorRegistration
err string
logMessages []string
}{
{
name: " Happy Path proposer config not nil",
@@ -1964,9 +1914,81 @@ func TestValidator_PushProposerSettings(t *testing.T) {
},
err: "could not submit signed registrations to beacon node",
},
{
name: "Validator Index Not found with validator registration",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 2,
Offset: 1,
},
genesisTime: 0,
}
// set bellatrix as current epoch
params.BeaconConfig().BellatrixForkEpoch = 0
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
client.EXPECT().ValidatorIndex(
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().ValidatorIndex(
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[1][:]},
).Times(2).Return(nil, errors.New("Could not find validator index"))
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
logMessages: []string{
"prepare beacon proposer and update fee recipient until a validator index is assigned",
"not be included in validator registration until a validator index is assigned",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hook := logTest.NewGlobal()
v := tt.validatorSetter(t)
km, err := v.Keymanager()
require.NoError(t, err)
@@ -1994,6 +2016,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
if err := v.PushProposerSettings(ctx, km); tt.err != "" {
assert.ErrorContains(t, tt.err, err)
}
if len(tt.logMessages) > 0 {
for _, message := range tt.logMessages {
assert.LogsContain(t, hook, message)
}
}
})
}
}

View File

@@ -145,10 +145,6 @@ func (v *validator) handleWithRemoteKeyManager(ctx context.Context, accountsChan
valActivated := v.checkAndLogValidatorStatus(statuses)
if valActivated {
logActiveValidatorStatus(statuses)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.PushProposerSettings(ctx, *remoteKm); err != nil {
return err
}
} else {
continue
}
@@ -196,11 +192,6 @@ func (v *validator) handleWithoutRemoteKeyManager(ctx context.Context, accountsC
valActivated := v.checkAndLogValidatorStatus(statuses)
if valActivated {
logActiveValidatorStatus(statuses)
// Set properties on the beacon node like the fee recipient for validators that are being used & active.
if err := v.PushProposerSettings(ctx, v.keyManager); err != nil {
return err
}
} else {
continue
}

View File

@@ -6,11 +6,9 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
validatorserviceconfig "github.com/prysmaticlabs/prysm/config/validator/service"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -67,7 +65,6 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -78,18 +75,9 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
},
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
}
v.pubkeyToValidatorIndex[pubKey] = 1
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
client.EXPECT().WaitForActivation(
gomock.Any(),
@@ -97,11 +85,6 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
PublicKeys: [][]byte{pubKey[:]},
},
).Return(clientStream, errors.New("failed stream")).Return(clientStream, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
clientStream.EXPECT().Recv().Return(resp, nil)
@@ -112,7 +95,6 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -123,18 +105,9 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
},
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
}
v.pubkeyToValidatorIndex[pubKey] = 1
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
client.EXPECT().WaitForActivation(
gomock.Any(),
@@ -142,11 +115,6 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
PublicKeys: [][]byte{pubKey[:]},
},
).Return(clientStream, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
// A stream fails the first time, but succeeds the second time.
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
@@ -162,7 +130,6 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -173,19 +140,10 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
},
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
genesisTime: 1,
}
v.pubkeyToValidatorIndex[pubKey] = 1
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
@@ -199,11 +157,6 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil), "Could not wait for activation")
assert.LogsContain(t, hook, "Validator activated")
}
@@ -212,7 +165,6 @@ func TestWaitForActivation_Exiting(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -223,19 +175,9 @@ func TestWaitForActivation_Exiting(t *testing.T) {
},
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
}
v.pubkeyToValidatorIndex[pubKey] = 1
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_EXITING
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
@@ -249,11 +191,6 @@ func TestWaitForActivation_Exiting(t *testing.T) {
resp,
nil,
)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -268,7 +205,6 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -280,19 +216,9 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
fetchNoKeys: true,
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
}
v.pubkeyToValidatorIndex[pubKey] = 1
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
clientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
@@ -305,12 +231,6 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
clientStream.EXPECT().Recv().Return(
resp,
nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.waitForActivation(context.Background(), make(chan [][fieldparams.BLSPubkeyLength]byte)), "Could not wait for activation")
assert.LogsContain(t, hook, msgNoKeysFetched)
assert.LogsContain(t, hook, "Validator activated")
@@ -337,21 +257,10 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
},
}
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
}
v.pubkeyToValidatorIndex[inactivePubKey] = 1
inactiveResp := generateMockStatusResponse([][]byte{inactivePubKey[:]})
inactiveResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
inactiveClientStream := mock.NewMockBeaconNodeValidator_WaitForActivationClient(ctrl)
@@ -365,12 +274,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
inactiveResp,
nil,
).AnyTimes()
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
activeResp := generateMockStatusResponse([][]byte{inactivePubKey[:], activePubKey[:]})
activeResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
@@ -391,7 +294,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
// We add the active key into the keymanager and simulate a key refresh.
time.Sleep(time.Second * 1)
km.keysMap[activePubKey] = activePrivKey
v.pubkeyToValidatorIndex[activePubKey] = 1
km.SimulateAccountChanges(make([][fieldparams.BLSPubkeyLength]byte, 0))
}()
@@ -426,21 +328,11 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
err = km.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", 1)
require.NoError(t, err)
client := mock.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock.NewMockNodeClient(ctrl)
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: km,
genesisTime: 1,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: km,
genesisTime: 1,
}
v.pubkeyToValidatorIndex[inactivePubKey] = 1
inactiveResp := generateMockStatusResponse([][]byte{inactivePubKey[:]})
inactiveResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
@@ -455,12 +347,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
inactiveResp,
nil,
).AnyTimes()
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
activeResp := generateMockStatusResponse([][]byte{inactivePubKey[:], activePubKey[:]})
activeResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
@@ -483,11 +369,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
time.Sleep(time.Second * 1)
err = km.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", 2)
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
for _, key := range keys {
v.pubkeyToValidatorIndex[key] = 1
}
channel <- [][fieldparams.BLSPubkeyLength]byte{}
}()
@@ -517,26 +398,15 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
t.Run("activated", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
hook := logTest.NewGlobal()
nodeClient := mock.NewMockNodeClient(ctrl)
tickerChan := make(chan types.Slot)
ticker := &slotutilmock.MockTicker{
Channel: tickerChan,
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: &km,
ticker: ticker,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: &km,
ticker: ticker,
}
v.pubkeyToValidatorIndex[activeKey] = 1
v.pubkeyToValidatorIndex[inactiveKey] = 2
go func() {
tickerChan <- slot
// Cancel after timeout to avoid waiting on channel forever in case test goes wrong.
@@ -553,13 +423,6 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
PublicKeys: [][]byte{inactiveKey[:], activeKey[:]},
},
).Return(resp, nil /* err */)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
err := v.waitForActivation(ctx, nil /* accountsChangedChan */)
require.NoError(t, err)
assert.LogsContain(t, hook, "Waiting for deposit to be observed by beacon node")
@@ -591,26 +454,15 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
hook := logTest.NewGlobal()
remoteKm := remotekeymanagermock.NewMock()
remoteKm.PublicKeys = [][fieldparams.BLSPubkeyLength]byte{inactiveKey}
nodeClient := mock.NewMockNodeClient(ctrl)
tickerChan := make(chan types.Slot)
ticker := &slotutilmock.MockTicker{
Channel: tickerChan,
}
v := validator{
validatorClient: client,
node: nodeClient,
keyManager: &remoteKm,
ticker: ticker,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
ProposerSettings: &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
},
},
validatorClient: client,
keyManager: &remoteKm,
ticker: ticker,
}
v.pubkeyToValidatorIndex[activeKey] = 1
v.pubkeyToValidatorIndex[inactiveKey] = 2
go func() {
tickerChan <- slot
time.Sleep(time.Second)
@@ -638,12 +490,6 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
PublicKeys: [][]byte{inactiveKey[:], activeKey[:]},
},
).Return(resp2, nil /* err */)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
err := v.waitForActivation(ctx, remoteKm.ReloadPublicKeysChan /* accountsChangedChan */)
require.NoError(t, err)

File diff suppressed because one or more lines are too long