mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-11 06:18:05 -05:00
Compare commits
16 Commits
item
...
future-pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
029b31c21e | ||
|
|
ef7ec31b89 | ||
|
|
308f6adff9 | ||
|
|
d6f6bc18b8 | ||
|
|
11ebb45b61 | ||
|
|
7fd2c08be3 | ||
|
|
6695e7c756 | ||
|
|
aeede2165d | ||
|
|
67a15183ca | ||
|
|
208dc80702 | ||
|
|
e80806d455 | ||
|
|
80d0a82f9b | ||
|
|
f9b3cde005 | ||
|
|
d8f9750387 | ||
|
|
5e8b9dde5b | ||
|
|
c2caff4230 |
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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] = ð.SignedValidatorRegistrationV1{
|
||||
Message: ð.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"
|
||||
|
||||
@@ -31,6 +31,22 @@ func (r *SignedValidatorRegistration) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *SignedValidatorRegistration) UnmarshalJSON(b []byte) error {
|
||||
if r.SignedValidatorRegistrationV1 == nil {
|
||||
r.SignedValidatorRegistrationV1 = ð.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 = ð.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
|
||||
}
|
||||
|
||||
@@ -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 = `{
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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, ¬ifyForkchoiceUpdateArg{
|
||||
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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 := ¬ifyForkchoiceUpdateArg{
|
||||
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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
)
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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, ðpb.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, ðpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
|
||||
ValidatorIndex: validatorIndex,
|
||||
FeeRecipient: feeRecipient[:],
|
||||
})
|
||||
}
|
||||
if enableValidatorRegistration {
|
||||
if !skipAppendToFeeRecipientArray && enableValidatorRegistration {
|
||||
registerValidatorRequests = append(registerValidatorRequests, ðpb.ValidatorRegistrationV1{
|
||||
FeeRecipient: feeRecipient[:],
|
||||
GasLimit: gasLimit,
|
||||
|
||||
@@ -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(), ðpb.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(), ðpb.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
|
||||
ðpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
|
||||
).Return(ðpb.ValidatorIndexResponse{
|
||||
Index: 1,
|
||||
}, nil)
|
||||
|
||||
client.EXPECT().ValidatorIndex(
|
||||
gomock.Any(), // ctx
|
||||
ðpb.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(), ðpb.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)
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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(), ðpb.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
Reference in New Issue
Block a user