Compare commits

..

100 Commits

Author SHA1 Message Date
Raul Jordan
4f0662bc49 confs 2022-07-08 09:40:40 -04:00
Raul Jordan
74ee0eded0 Merge branch 'develop' into reconstruct-engine 2022-07-08 03:31:23 +00:00
Raul Jordan
98b34ef942 Merge branch 'reconstruct-engine' of github.com:prysmaticlabs/prysm into reconstruct-engine 2022-07-07 23:31:06 -04:00
Raul Jordan
d2e6fe9c78 add test based on recs 2022-07-07 23:30:56 -04:00
Preston Van Loon
f9b3cde005 Batch build API requests for RegisterValidator (#11002)
* Add UnmarshalJSON for SignedValidatorRequest

* add failing test for batch limits

* Add functionality

* gofmt

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

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

* fixing unit tests

* fixing more unit tests
2022-07-07 22:24:06 +00:00
Raul Jordan
6d6e4fe48a Merge branch 'develop' into reconstruct-engine 2022-07-07 21:56:57 +00:00
terencechain
c2caff4230 Minor UX improvement to validator registration (#11004) 2022-07-07 19:32:02 +00:00
Raul Jordan
539b997c66 build 2022-07-06 21:02:34 -04:00
Raul Jordan
8e11b1be74 prevent nil block 2022-07-06 20:59:17 -04:00
Raul Jordan
e58a83d53e deadcode 2022-07-06 20:57:44 -04:00
Raul Jordan
91a122e70e metrics 2022-07-06 20:55:27 -04:00
Raul Jordan
6d91db594d powchain pass 2022-07-06 20:54:19 -04:00
Raul Jordan
5104bb646d gaz 2022-07-06 20:45:39 -04:00
Raul Jordan
86ac0ed09b engine reconstructor 2022-07-06 20:11:47 -04:00
Raul Jordan
97fa814193 fix confs 2022-07-06 20:06:12 -04:00
Raul Jordan
b67c885995 Major Simplification of JSON Handling for Execution Blocks (#10993)
* no more execution block custom type

* simpler json rpc data unmarshaling

* simplicify

* included hash and txs fix

* all tests

* pass

* build

* mock fix

* attempt build

* builds

* build

* builds

* pass

* pass

* build

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-06 22:06:00 +00:00
james-prysm
60ed488428 changing gaslimit to validator registration (#10992)
* changing gaslimit to validator registration

* adding new flag to enable validator registration for suggested fee recipient

* making sure default gaslimit is set

* Update cmd/validator/flags/flags.go

Co-authored-by: terencechain <terence@prysmaticlabs.com>

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: terencechain <terence@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-06 18:42:21 +00:00
Potuz
2f0e2886b4 Do not error if the LVH is bogus (#10996)
* Do not error if the LVH is bogus

* add tests and mark the regression PR

* dead code

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-06 17:37:15 +00:00
terencechain
2d53ae79df Cleanups to pulltips (#10984)
* Minor cleanups to pulltips

* Feedbacks

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-06 16:55:17 +00:00
Nishant Das
ce277cb388 Add Fuzzing For JSON Marshalling/Unmarshalling Methods (#10995)
* modify it

* add gaz

* revert

* deps

* revert change

* fix it
2022-07-06 15:15:14 +00:00
Raul Jordan
c9a366c36a Revert "Move Slasher E2E To Scenario Test" (#10986)
This reverts commit 65900115fc.

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-06 13:03:30 +00:00
terencechain
3a957c567f Handle invalid_block_hash error from ee (#10991)
* Handle invalid_block_hash error from ee

* Update beacon-chain/blockchain/error.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* Remove invalid block and state

* Revert "Remove invalid block and state"

This reverts commit 9ca011b8ce.

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
2022-07-06 00:22:12 +00:00
Raul Jordan
77a63f871d Included Blinded Beacon Block in V1alpha1 Protobuf (#10989)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-05 21:03:14 +00:00
Raul Jordan
8dd8ccc147 pure funcs (#10988) 2022-07-05 16:24:17 -04:00
Preston Van Loon
3c48bce3a3 Annotate build client requests (#10987)
* Annotate build client requests

* Use named return arguments to annotate errors

* Unhandled error was bad

* Error level is better than warning for this

* Clarifying commentary while i'm here

* delete the pasta
2022-07-05 19:33:33 +00:00
Raul Jordan
e9b4c0110b Merge branch 'develop' into only-save-payload 2022-07-05 13:38:05 -04:00
Raul Jordan
23aeb4df6f Merge branch 'only-save-payload' of github.com:prysmaticlabs/prysm into only-save-payload 2022-06-29 16:50:04 -04:00
Raul Jordan
ca5b368d15 build 2022-06-29 16:49:29 -04:00
Raul Jordan
9cc1076fee conflicts 2022-06-29 15:44:29 -05:00
Raul Jordan
6453e98dc6 fix up confs 2022-06-29 16:40:23 -04:00
james-prysm
747db024ad regenerating beacon_chain proto 2022-06-16 09:04:54 -05:00
Raul Jordan
cdcb7ee389 fixing some rpc tests 2022-06-15 18:40:00 -06:00
Raul Jordan
5754f9d271 blinded bellatrix block in apis 2022-06-15 18:31:57 -06:00
Raul Jordan
f7a6167c1d pass db tests, fix cache issue 2022-06-15 18:23:02 -06:00
Raul Jordan
5f2fd08255 Merge branch 'develop' into only-save-payload 2022-06-08 17:39:10 -04:00
Raul Jordan
dda2064e07 resolve conflicts 2022-06-08 11:21:11 -04:00
Raul Jordan
0299d7a036 Merge branch 'only-save-payload' of github.com:prysmaticlabs/prysm into only-save-payload 2022-06-01 13:18:32 -05:00
Raul Jordan
7bb5bd0fba Merge branch 'develop' into only-save-payload 2022-05-26 21:32:03 +00:00
Raul Jordan
f5b2dd986a Merge branch 'develop' into only-save-payload 2022-05-26 17:28:28 -04:00
Raul Jordan
b9b24afb69 no log 2022-05-26 16:01:36 -04:00
Raul Jordan
8103095cc0 fix up 2022-05-26 15:58:41 -04:00
Raul Jordan
76f201ee8f no log 2022-05-26 15:31:03 -04:00
Raul Jordan
31c39aac96 fix up some failing tests 2022-05-26 15:21:07 -04:00
Raul Jordan
51109f61b4 Merge branch 'develop' into only-save-payload 2022-05-26 19:14:06 +00:00
Raul Jordan
dc94612272 Merge branch 'develop' into only-save-payload 2022-05-25 13:43:49 +00:00
Raul Jordan
eb150622ed Merge branch 'only-save-payload' of github.com:prysmaticlabs/prysm into only-save-payload 2022-05-24 23:29:59 -04:00
Raul Jordan
27e210f6b8 item 2022-05-24 23:29:54 -04:00
Raul Jordan
bbcbb8dc26 Merge branch 'develop' into only-save-payload 2022-05-25 03:17:13 +00:00
Raul Jordan
04a96da75d build 2022-05-24 23:16:21 -04:00
Raul Jordan
1acb3b6346 gaz 2022-05-24 14:07:25 -04:00
Raul Jordan
ff69994b7b reconstructs with payloads 2022-05-24 14:06:27 -04:00
Raul Jordan
7ad27324fd right test 2022-05-24 13:59:22 -04:00
Raul Jordan
1cba6c306e Merge branch 'develop' into only-save-payload 2022-05-24 13:46:56 -04:00
Raul Jordan
247c2da608 work on more rpc tests 2022-05-24 12:58:29 -04:00
Raul Jordan
f749702ed7 begin populating 2022-05-23 20:50:33 -04:00
Raul Jordan
dbd6232e6f Merge branch 'develop' into only-save-payload 2022-05-23 20:42:27 -04:00
Raul Jordan
d8b6b6d17c test with real txs 2022-05-23 20:40:44 -04:00
Raul Jordan
e2a06625cf Merge branch 'develop' into only-save-payload 2022-05-23 19:53:45 -04:00
Raul Jordan
92f9aff295 begin testing happy case of reconstruction func 2022-05-23 19:53:34 -04:00
Raul Jordan
ab734442a3 test fixes 2022-05-23 17:44:55 -04:00
Raul Jordan
36b1efb12f Merge branch 'develop' into only-save-payload 2022-05-23 16:32:54 -04:00
Raul Jordan
5a4a4c2016 engine client correct use 2022-05-20 18:32:21 -04:00
Raul Jordan
ba6c28c48d correct logic 2022-05-20 18:29:48 -04:00
Raul Jordan
01ae8d58d5 wrapper tests 2022-05-20 18:21:58 -04:00
Raul Jordan
574b03d2ed begin some tests 2022-05-20 18:02:35 -04:00
Raul Jordan
0c6feb60b1 builds 2022-05-20 17:51:56 -04:00
Raul Jordan
70143cff56 Merge branch 'payload-utils' into only-save-payload 2022-05-20 17:43:15 -04:00
Raul Jordan
49aedf8459 move to consensus-types folder 2022-05-20 17:43:00 -04:00
Raul Jordan
ea5e8b99b7 clean move 2022-05-20 17:30:40 -04:00
Raul Jordan
3611afb448 gaz 2022-05-20 17:28:04 -04:00
Raul Jordan
d3a1cff406 beacon block is nil wrapper 2022-05-20 17:26:45 -04:00
Raul Jordan
e3c07ac84f gazelle 2022-05-20 17:24:41 -04:00
Raul Jordan
57d52089bc handle err 2022-05-20 17:18:46 -04:00
Raul Jordan
d0b92aa42b future proof the code 2022-05-20 17:17:11 -04:00
Raul Jordan
35a7cc43e3 Merge branch 'develop' into only-save-payload 2022-05-20 17:10:15 -04:00
Raul Jordan
c214525e70 simpler logic 2022-05-20 16:40:43 -04:00
Raul Jordan
fcd9f0830e simplify 2022-05-20 16:27:08 -04:00
Raul Jordan
8c8380f28c better version check 2022-05-20 16:02:53 -04:00
Raul Jordan
5885e44670 Merge branch 'develop' into only-save-payload 2022-05-20 15:49:42 -04:00
Raul Jordan
11e0f4025a metrics for responding to blocks by range 2022-05-17 20:58:30 -04:00
Raul Jordan
05ed96dc25 add empty payload check 2022-05-17 20:35:40 -04:00
Raul Jordan
c57baa00f7 Merge branch 'develop' into only-save-payload 2022-05-17 19:55:41 -04:00
Raul Jordan
76b2e23232 ensure can respond to peer requests 2022-05-13 16:07:15 -04:00
Raul Jordan
68e67c3023 add logging times 2022-05-13 15:53:01 -04:00
Raul Jordan
eaa3d756e7 reconstruct full blocks during blocks by range request 2022-05-13 15:50:07 -04:00
Raul Jordan
cdf4c8d3fe api responses with real txs 2022-05-13 15:35:36 -04:00
Raul Jordan
ada07f5358 block reconstruction working as expected 2022-05-13 13:56:58 -04:00
Raul Jordan
114277d0b0 payload api reconstruct 2022-05-13 13:24:47 -04:00
Raul Jordan
0b6bf2c316 ensure can overcome sync hurdles 2022-05-13 00:05:07 -04:00
Raul Jordan
2299b00cd8 building up payloads 2022-05-12 17:08:42 -04:00
Raul Jordan
4ba8c98acd Merge branch 'develop' into only-save-payload 2022-05-12 15:52:59 -04:00
Raul Jordan
63f858d2da Merge branch 'develop' into only-save-payload 2022-05-12 15:52:49 -04:00
Raul Jordan
e7d9b33904 reconstruct full 2022-05-11 23:58:49 -04:00
Raul Jordan
77657dca93 reconstruct 2022-05-11 23:53:32 -04:00
Raul Jordan
c755751410 Merge branch 'develop' into only-save-payload 2022-05-11 14:24:58 -04:00
Raul Jordan
571edeaf43 wrap signed blinded 2022-05-05 17:10:04 -04:00
Raul Jordan
e2e8528f97 build from blinded 2022-05-05 17:05:23 -04:00
Raul Jordan
2cfbc92c17 experiment with only saving blinded blocks 2022-05-05 17:03:10 -04:00
96 changed files with 3510 additions and 2161 deletions

View File

@@ -12,11 +12,15 @@ 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",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_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",
],
)
@@ -36,6 +40,7 @@ go_test(
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
],
)

View File

@@ -12,11 +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"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
log "github.com/sirupsen/logrus"
)
@@ -30,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)
@@ -128,37 +134,49 @@ func (c *Client) NodeURL() string {
type reqOption func(*http.Request)
// do is a generic, opinionated GET function to reduce boilerplate amongst the getters in this packageapi/client/builder/types.go.
func (c *Client) do(ctx context.Context, method string, path string, body io.Reader, opts ...reqOption) ([]byte, error) {
// do is a generic, opinionated request function to reduce boilerplate amongst the methods in this package api/client/builder/types.go.
func (c *Client) do(ctx context.Context, method string, path string, body io.Reader, opts ...reqOption) (res []byte, err error) {
ctx, span := trace.StartSpan(ctx, "builder.client.do")
defer func() {
tracing.AnnotateError(span, err)
span.End()
}()
u := c.baseURL.ResolveReference(&url.URL{Path: path})
log.Printf("requesting %s", u.String())
span.AddAttributes(trace.StringAttribute("url", u.String()),
trace.StringAttribute("method", method))
req, err := http.NewRequestWithContext(ctx, method, u.String(), body)
if err != nil {
return nil, err
return
}
for _, o := range opts {
o(req)
}
for _, o := range c.obvs {
if err := o.observe(req); err != nil {
return nil, err
if err = o.observe(req); err != nil {
return
}
}
r, err := c.hc.Do(req)
if err != nil {
return nil, err
return
}
defer func() {
err = r.Body.Close()
closeErr := r.Body.Close()
log.WithError(closeErr).Error("Failed to close response body")
}()
if r.StatusCode != http.StatusOK {
return nil, non200Err(r)
err = non200Err(r)
return
}
b, err := io.ReadAll(r.Body)
res, err = io.ReadAll(r.Body)
if err != nil {
return nil, errors.Wrap(err, "error reading http response body from GetBlock")
err = errors.Wrap(err, "error reading http response body from builder server")
return
}
return b, nil
return
}
var execHeaderTemplate = template.Must(template.New("").Parse(getExecHeaderPath))
@@ -201,19 +219,38 @@ func (c *Client) GetHeader(ctx context.Context, slot types.Slot, parentHash [32]
// RegisterValidator encodes the SignedValidatorRegistrationV1 message to json (including hex-encoding the byte
// fields with 0x prefixes) and posts to the builder validator registration endpoint.
func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValidatorRegistrationV1) error {
ctx, span := trace.StartSpan(ctx, "builder.client.RegisterValidator")
defer span.End()
if len(svr) == 0 {
return errors.Wrap(errMalformedRequest, "empty validator registration list")
err := errors.Wrap(errMalformedRequest, "empty validator registration list")
tracing.AnnotateError(span, err)
return err
}
vs := make([]*SignedValidatorRegistration, len(svr))
for i := 0; i < len(svr); i++ {
vs[i] = &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[i]}
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
})
}
body, err := json.Marshal(vs)
if err != nil {
return errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
}
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
return err
return eg.Wait()
}
// SubmitBlindedBlock calls the builder API endpoint that binds the validator to the builder and submits the block.

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,9 @@ import "github.com/pkg/errors"
var (
// ErrInvalidPayload is returned when the payload is invalid
ErrInvalidPayload = errors.New("recevied an INVALID payload from execution engine")
ErrInvalidPayload = 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")
// 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.

View File

@@ -145,14 +145,7 @@ func (s *Service) getPayloadHash(ctx context.Context, root []byte) ([32]byte, er
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 getBlockPayloadHash(blk.Block())
}
// notifyForkchoiceUpdate signals execution engine on a new payload.
@@ -213,6 +206,9 @@ func (s *Service) notifyNewPayload(ctx context.Context, postStateVersion int,
"invalidCount": len(invalidRoots),
}).Warn("Pruned invalid blocks")
return false, invalidBlock{ErrInvalidPayload}
case powchain.ErrInvalidBlockHashPayloadStatus:
newPayloadInvalidNodeCount.Inc()
return false, invalidBlock{ErrInvalidBlockHashPayloadStatus}
default:
return false, errors.WithMessage(ErrUndefinedExecutionEngineError, err.Error())
}

View File

@@ -6,6 +6,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
@@ -673,16 +674,40 @@ func Test_NotifyNewPayload(t *testing.T) {
newPayloadErr: ErrUndefinedExecutionEngineError,
errString: ErrUndefinedExecutionEngineError.Error(),
},
{
name: "invalid block hash error from ee",
postState: bellatrixState,
blk: func() interfaces.SignedBeaconBlock {
blk := &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
},
},
},
}
b, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
return b
}(),
newPayloadErr: ErrInvalidBlockHashPayloadStatus,
errString: ErrInvalidBlockHashPayloadStatus.Error(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &mockPOW.EngineClient{ErrNewPayload: tt.newPayloadErr, BlockByHashMap: map[[32]byte]*v1.ExecutionBlock{}}
e.BlockByHashMap[[32]byte{'a'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x2",
}
e.BlockByHashMap[[32]byte{'b'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("3")),
},
TotalDifficulty: "0x1",
}
service.cfg.ExecutionEngineCaller = e
@@ -735,11 +760,15 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
require.NoError(t, err)
e := &mockPOW.EngineClient{BlockByHashMap: map[[32]byte]*v1.ExecutionBlock{}}
e.BlockByHashMap[[32]byte{'a'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x2",
}
e.BlockByHashMap[[32]byte{'b'}] = &v1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("3")),
},
TotalDifficulty: "0x1",
}
service.cfg.ExecutionEngineCaller = e

View File

@@ -100,7 +100,7 @@ func (s *Service) getBlkParentHashAndTD(ctx context.Context, blkHash []byte) ([]
if overflows {
return nil, nil, errors.New("total difficulty overflows")
}
return blk.ParentHash, blkTDUint256, nil
return blk.ParentHash[:], blkTDUint256, nil
}
// validateTerminalBlockHash validates if the merge block is a valid terminal PoW block.

View File

@@ -6,12 +6,12 @@ import (
"math/big"
"testing"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray"
mocks "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -120,12 +120,19 @@ func Test_validateMergeBlock(t *testing.T) {
engine := &mocks.EngineClient{BlockByHashMap: map[[32]byte]*enginev1.ExecutionBlock{}}
service.cfg.ExecutionEngineCaller = engine
engine.BlockByHashMap[[32]byte{'a'}] = &enginev1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
a := [32]byte{'a'}
b := [32]byte{'b'}
mergeBlockParentHash := [32]byte{'3'}
engine.BlockByHashMap[a] = &enginev1.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: b,
},
TotalDifficulty: "0x2",
}
engine.BlockByHashMap[[32]byte{'b'}] = &enginev1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
engine.BlockByHashMap[b] = &enginev1.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: mergeBlockParentHash,
},
TotalDifficulty: "0x1",
}
blk := &ethpb.SignedBeaconBlockBellatrix{
@@ -133,18 +140,18 @@ func Test_validateMergeBlock(t *testing.T) {
Slot: 1,
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &enginev1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentHash: a[:],
},
},
},
}
b, err := wrapper.WrappedSignedBeaconBlock(blk)
bk, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, service.validateMergeBlock(ctx, b))
require.NoError(t, service.validateMergeBlock(ctx, bk))
cfg.TerminalTotalDifficulty = "1"
params.OverrideBeaconConfig(cfg)
err = service.validateMergeBlock(ctx, b)
err = service.validateMergeBlock(ctx, bk)
require.ErrorContains(t, "invalid TTD, configTTD: 1, currentTTD: 2, parentTTD: 1", err)
require.Equal(t, true, IsInvalidBlock(err))
}
@@ -167,7 +174,9 @@ func Test_getBlkParentHashAndTD(t *testing.T) {
p := [32]byte{'b'}
td := "0x1"
engine.BlockByHashMap[h] = &enginev1.ExecutionBlock{
ParentHash: p[:],
Header: gethtypes.Header{
ParentHash: p,
},
TotalDifficulty: td,
}
parentHash, totalDifficulty, err := service.getBlkParentHashAndTD(ctx, h[:])
@@ -183,14 +192,18 @@ func Test_getBlkParentHashAndTD(t *testing.T) {
require.ErrorContains(t, "pow block is nil", err)
engine.BlockByHashMap[h] = &enginev1.ExecutionBlock{
ParentHash: p[:],
Header: gethtypes.Header{
ParentHash: p,
},
TotalDifficulty: "1",
}
_, _, err = service.getBlkParentHashAndTD(ctx, h[:])
require.ErrorContains(t, "could not decode merge block total difficulty: hex string without 0x prefix", err)
engine.BlockByHashMap[h] = &enginev1.ExecutionBlock{
ParentHash: p[:],
Header: gethtypes.Header{
ParentHash: p,
},
TotalDifficulty: "0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
}
_, _, err = service.getBlkParentHashAndTD(ctx, h[:])

View File

@@ -537,6 +537,27 @@ func (s *Service) InsertSlashingsToForkChoiceStore(ctx context.Context, slashing
}
}
func getBlockPayloadHash(blk interfaces.BeaconBlock) ([32]byte, error) {
var blockHashFromPayload [32]byte
if blocks.IsPreBellatrixVersion(blk.Version()) {
return blockHashFromPayload, nil
}
payload, err := blk.Body().ExecutionPayload()
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
payloadHeader, err := blk.Body().ExecutionPayloadHeader()
if err != nil {
return blockHashFromPayload, err
}
blockHashFromPayload = bytesutil.ToBytes32(payloadHeader.BlockHash)
case err != nil:
return blockHashFromPayload, err
default:
blockHashFromPayload = bytesutil.ToBytes32(payload.BlockHash)
}
return blockHashFromPayload, nil
}
// This saves post state info to DB or cache. This also saves post state info to fork choice store.
// Post state info consists of processed block and state. Do not call this method unless the block and state are verified.
func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interfaces.SignedBeaconBlock, st state.BeaconState) error {

View File

@@ -9,6 +9,8 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
@@ -1527,6 +1529,9 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
service, err := NewService(ctx, opts...)
require.NoError(t, err)
aHash := common.BytesToHash([]byte("a"))
bHash := common.BytesToHash([]byte("b"))
tests := []struct {
name string
stateVersion int
@@ -1557,7 +1562,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state older than Bellatrix, non empty payload",
stateVersion: 1,
payload: &enginev1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentHash: aHash[:],
},
},
{
@@ -1583,7 +1588,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state is Bellatrix, non empty payload, empty header",
stateVersion: 2,
payload: &enginev1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentHash: aHash[:],
},
header: &enginev1.ExecutionPayloadHeader{
ParentHash: make([]byte, fieldparams.RootLength),
@@ -1601,7 +1606,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state is Bellatrix, non empty payload, non empty header",
stateVersion: 2,
payload: &enginev1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentHash: aHash[:],
},
header: &enginev1.ExecutionPayloadHeader{
BlockNumber: 1,
@@ -1611,7 +1616,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state is Bellatrix, non empty payload, nil header",
stateVersion: 2,
payload: &enginev1.ExecutionPayload{
ParentHash: bytesutil.PadTo([]byte{'a'}, fieldparams.RootLength),
ParentHash: aHash[:],
},
errString: "nil header or block body",
},
@@ -1619,12 +1624,16 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &mockPOW.EngineClient{BlockByHashMap: map[[32]byte]*enginev1.ExecutionBlock{}}
e.BlockByHashMap[[32]byte{'a'}] = &enginev1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'b'}, fieldparams.RootLength),
e.BlockByHashMap[aHash] = &enginev1.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: bHash,
},
TotalDifficulty: "0x2",
}
e.BlockByHashMap[[32]byte{'b'}] = &enginev1.ExecutionBlock{
ParentHash: bytesutil.PadTo([]byte{'3'}, fieldparams.RootLength),
e.BlockByHashMap[bHash] = &enginev1.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("3")),
},
TotalDifficulty: "0x1",
}
service.cfg.ExecutionEngineCaller = e

View File

@@ -1,8 +1,6 @@
package precompute
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
@@ -17,7 +15,7 @@ var errNilState = errors.New("nil state")
// UnrealizedCheckpoints returns the justification and finalization checkpoints of the
// given state as if it was progressed with empty slots until the next epoch.
func UnrealizedCheckpoints(ctx context.Context, st state.BeaconState) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
func UnrealizedCheckpoints(st state.BeaconState) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
if st == nil || st.IsNil() {
return nil, nil, errNilState
}

View File

@@ -243,7 +243,7 @@ func TestUnrealizedCheckpoints(t *testing.T) {
_, _, err = altair.InitializePrecomputeValidators(context.Background(), state)
require.NoError(t, err)
jc, fc, err := precompute.UnrealizedCheckpoints(context.Background(), state)
jc, fc, err := precompute.UnrealizedCheckpoints(state)
require.NoError(t, err)
require.DeepEqual(t, test.expectedJustified, jc.Epoch)
require.DeepEqual(t, test.expectedFinalized, fc.Epoch)

View File

@@ -304,6 +304,13 @@ func (s *Store) SaveBlocks(ctx context.Context, blocks []interfaces.SignedBeacon
if err := updateValueForIndices(ctx, indicesForBlocks[i], blockRoots[i], tx); err != nil {
return errors.Wrap(err, "could not update DB indices")
}
if _, err := blk.Block().Body().ExecutionPayload(); err == nil {
blindedBlock, err := wrapper.WrapSignedBlindedBeaconBlock(blk)
if err != nil {
return err
}
blk = blindedBlock
}
s.blockCache.Set(string(blockRoots[i]), blk, int64(len(encodedBlocks[i])))
if err := bkt.Put(blockRoots[i], encodedBlocks[i]); err != nil {
return err
@@ -768,11 +775,6 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
if err := rawBlock.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
return nil, err
}
case hasBellatrixKey(enc):
rawBlock = &ethpb.SignedBeaconBlockBellatrix{}
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
return nil, err
}
case hasBellatrixBlindKey(enc):
rawBlock = &ethpb.SignedBlindedBeaconBlockBellatrix{}
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixBlindKey):]); err != nil {
@@ -790,19 +792,34 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
// marshal versioned beacon block from struct type down to bytes.
func marshalBlock(_ context.Context, blk interfaces.SignedBeaconBlock) ([]byte, error) {
obj, err := blk.MarshalSSZ()
if err != nil {
var encodedBlock []byte
var blindedBlock interfaces.SignedBeaconBlock
var err error
// If the block supports blinding of execution payloads, we wrap as
// a signed, blinded beacon block and then marshal to bytes. Otherwise,
// We just marshal the block as it is.
blindedBlock, err = wrapper.WrapSignedBlindedBeaconBlock(blk)
switch {
case errors.Is(err, wrapper.ErrUnsupportedSignedBeaconBlock):
encodedBlock, err = blk.MarshalSSZ()
if err != nil {
return nil, err
}
case err != nil:
return nil, err
default:
encodedBlock, err = blindedBlock.MarshalSSZ()
if err != nil {
return nil, err
}
}
switch blk.Version() {
case version.BellatrixBlind:
return snappy.Encode(nil, append(bellatrixBlindKey, obj...)), nil
case version.Bellatrix:
return snappy.Encode(nil, append(bellatrixKey, obj...)), nil
case version.Bellatrix, version.BellatrixBlind:
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil
case version.Altair:
return snappy.Encode(nil, append(altairKey, obj...)), nil
return snappy.Encode(nil, append(altairKey, encodedBlock...)), nil
case version.Phase0:
return snappy.Encode(nil, obj), nil
return snappy.Encode(nil, encodedBlock), nil
default:
return nil, errors.New("Unknown block version")
}

View File

@@ -134,11 +134,17 @@ func TestStore_BlocksCRUD(t *testing.T) {
retrievedBlock, err := db.Block(ctx, blockRoot)
require.NoError(t, err)
assert.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
require.NoError(t, db.SaveBlock(ctx, blk))
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
retrievedBlock, err = db.Block(ctx, blockRoot)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(blk.Proto(), retrievedBlock.Proto()), "Wanted: %v, received: %v", blk, retrievedBlock)
wanted := retrievedBlock
if _, err := retrievedBlock.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(retrievedBlock)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), retrievedBlock.Proto()), "Wanted: %v, received: %v", wanted, retrievedBlock)
})
}
}
@@ -314,7 +320,13 @@ func TestStore_BlocksCRUD_NoCache(t *testing.T) {
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
retrievedBlock, err = db.Block(ctx, blockRoot)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(blk.Proto(), retrievedBlock.Proto()), "Wanted: %v, received: %v", blk, retrievedBlock)
wanted := blk
if _, err := blk.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(blk)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), retrievedBlock.Proto()), "Wanted: %v, received: %v", wanted, retrievedBlock)
})
}
}
@@ -524,7 +536,12 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
root := roots[0]
b, err := db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block1.Proto(), b.Proto()), "Wanted: %v, received: %v", block1, b)
wanted := block1
if _, err := block1.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(wanted)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 11)
require.NoError(t, err)
@@ -533,7 +550,12 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block2.Proto(), b.Proto()), "Wanted: %v, received: %v", block2, b)
wanted2 := block2
if _, err := block2.PbBellatrixBlock(); err == nil {
wanted2, err = wrapper.WrapSignedBlindedBeaconBlock(block2)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted2.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted2, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 101)
require.NoError(t, err)
@@ -542,7 +564,12 @@ func TestStore_SaveBlock_CanGetHighestAt(t *testing.T) {
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block3.Proto(), b.Proto()), "Wanted: %v, received: %v", block3, b)
wanted = block3
if _, err := block3.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(wanted)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted, b)
})
}
}
@@ -569,7 +596,12 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
root := roots[0]
b, err := db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(block1.Proto(), b.Proto()), "Wanted: %v, received: %v", block1, b)
wanted := block1
if _, err := block1.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(block1)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 1)
require.NoError(t, err)
@@ -577,7 +609,12 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), b.Proto()), "Wanted: %v, received: %v", genesisBlock, b)
wanted = genesisBlock
if _, err := genesisBlock.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(genesisBlock)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted, b)
_, roots, err = db.HighestRootsBelowSlot(ctx, 0)
require.NoError(t, err)
@@ -585,7 +622,12 @@ func TestStore_GenesisBlock_CanGetHighestAt(t *testing.T) {
root = roots[0]
b, err = db.Block(ctx, root)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(genesisBlock.Proto(), b.Proto()), "Wanted: %v, received: %v", genesisBlock, b)
wanted = genesisBlock
if _, err := genesisBlock.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(genesisBlock)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), b.Proto()), "Wanted: %v, received: %v", wanted, b)
})
}
}
@@ -671,15 +713,31 @@ func TestStore_BlocksBySlot_BlockRootsBySlot(t *testing.T) {
assert.Equal(t, 0, len(retrievedBlocks), "Unexpected number of blocks received, expected none")
retrievedBlocks, err = db.BlocksBySlot(ctx, 20)
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(b1.Proto(), retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b1, retrievedBlocks[0])
wanted := b1
if _, err := b1.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(b1)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(retrievedBlocks[0].Proto(), wanted.Proto()), "Wanted: %v, received: %v", retrievedBlocks[0], wanted)
assert.Equal(t, true, len(retrievedBlocks) > 0, "Expected to have blocks")
retrievedBlocks, err = db.BlocksBySlot(ctx, 100)
require.NoError(t, err)
if len(retrievedBlocks) != 2 {
t.Fatalf("Expected 2 blocks, received %d blocks", len(retrievedBlocks))
}
assert.Equal(t, true, proto.Equal(b2.Proto(), retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", b2, retrievedBlocks[0])
assert.Equal(t, true, proto.Equal(b3.Proto(), retrievedBlocks[1].Proto()), "Wanted: %v, received: %v", b3, retrievedBlocks[1])
wanted = b2
if _, err := b2.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(b2)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(wanted.Proto(), retrievedBlocks[0].Proto()), "Wanted: %v, received: %v", retrievedBlocks[0], wanted)
wanted = b3
if _, err := b3.PbBellatrixBlock(); err == nil {
wanted, err = wrapper.WrapSignedBlindedBeaconBlock(b3)
require.NoError(t, err)
}
assert.Equal(t, true, proto.Equal(retrievedBlocks[1].Proto(), wanted.Proto()), "Wanted: %v, received: %v", retrievedBlocks[1], wanted)
assert.Equal(t, true, len(retrievedBlocks) > 0, "Expected to have blocks")
hasBlockRoots, retrievedBlockRoots, err := db.BlockRootsBySlot(ctx, 1)

View File

@@ -8,7 +8,6 @@ var errInvalidProposerBoostRoot = errors.New("invalid proposer boost root")
var errUnknownFinalizedRoot = errors.New("unknown finalized root")
var errUnknownJustifiedRoot = errors.New("unknown justified root")
var errInvalidOptimisticStatus = errors.New("invalid optimistic status")
var errUnknownPayloadHash = errors.New("unknown payload hash")
var errInvalidNilCheckpoint = errors.New("invalid nil checkpoint")
var errInvalidUnrealizedJustifiedEpoch = errors.New("invalid unrealized justified epoch")
var errInvalidUnrealizedFinalizedEpoch = errors.New("invalid unrealized finalized epoch")

View File

@@ -150,8 +150,8 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro
return err
}
if features.Get().PullTips {
jc, fc = f.store.pullTips(ctx, state, node, jc, fc)
if features.Get().EnablePullTips {
jc, fc = f.store.pullTips(state, node, jc, fc)
}
return f.updateCheckpoints(ctx, jc, fc)
}

View File

@@ -65,8 +65,8 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot types.Slot) error {
f.store.justifiedCheckpoint = bjcp
}
}
if features.Get().PullTips {
f.UpdateUnrealizedCheckpoints()
if features.Get().EnablePullTips {
f.updateUnrealizedCheckpoints()
}
return nil
}

View File

@@ -32,12 +32,6 @@ func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, pa
return invalidRoots, errInvalidParentRoot
}
}
// Check if last valid hash is an ancestor of the passed node.
lastValid, ok := s.nodeByPayload[payloadHash]
if !ok || lastValid == nil {
s.nodesLock.Unlock()
return invalidRoots, errUnknownPayloadHash
}
firstInvalid := node
for ; firstInvalid.parent != nil && firstInvalid.parent.payloadHash != payloadHash; firstInvalid = firstInvalid.parent {
if ctx.Err() != nil {

View File

@@ -30,6 +30,23 @@ func TestPruneInvalid(t *testing.T) {
wantedRoots [][32]byte
wantedErr error
}{
{ // Bogus LVH, root not in forkchoice
[32]byte{'x'},
[32]byte{'i'},
[32]byte{'R'},
13,
[][32]byte{},
nil,
},
{
// Bogus LVH
[32]byte{'i'},
[32]byte{'h'},
[32]byte{'R'},
12,
[][32]byte{{'i'}},
nil,
},
{
[32]byte{'j'},
[32]byte{'b'},

View File

@@ -1,8 +1,6 @@
package doublylinkedtree
import (
"context"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
@@ -44,20 +42,19 @@ func (s *Store) setUnrealizedFinalizedEpoch(root [32]byte, epoch types.Epoch) er
return nil
}
// UpdateUnrealizedCheckpoints "realizes" the unrealized justified and finalized
// epochs stored within nodes. It should be called at the beginning of each
// epoch
func (f *ForkChoice) UpdateUnrealizedCheckpoints() {
// updateUnrealizedCheckpoints "realizes" the unrealized justified and finalized
// epochs stored within nodes. It should be called at the beginning of each epoch.
func (f *ForkChoice) updateUnrealizedCheckpoints() {
f.store.nodesLock.Lock()
defer f.store.nodesLock.Unlock()
for _, node := range f.store.nodeByRoot {
node.justifiedEpoch = node.unrealizedJustifiedEpoch
node.finalizedEpoch = node.unrealizedFinalizedEpoch
if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch {
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
if node.justifiedEpoch > f.store.bestJustifiedCheckpoint.Epoch {
f.store.bestJustifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
}
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
}
if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch {
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
@@ -66,33 +63,37 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() {
}
}
func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
func (s *Store) pullTips(state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
s.nodesLock.Lock()
defer s.nodesLock.Unlock()
if node.parent == nil { // Nothing to do if the parent is nil.
return jc, fc
}
s.checkpointsLock.Lock()
defer s.checkpointsLock.Unlock()
var uj, uf *ethpb.Checkpoint
currentSlot := slots.CurrentSlot(s.genesisTime)
currentEpoch := slots.ToEpoch(currentSlot)
currentEpoch := slots.ToEpoch(slots.CurrentSlot(s.genesisTime))
stateSlot := state.Slot()
stateEpoch := slots.ToEpoch(stateSlot)
if node.parent == nil {
return jc, fc
}
currJustified := node.parent.unrealizedJustifiedEpoch == currentEpoch
prevJustified := node.parent.unrealizedJustifiedEpoch+1 == currentEpoch
tooEarlyForCurr := slots.SinceEpochStarts(stateSlot)*3 < params.BeaconConfig().SlotsPerEpoch*2
// Exit early if it's justified or too early to be justified.
if currJustified || (stateEpoch == currentEpoch && prevJustified && tooEarlyForCurr) {
node.unrealizedJustifiedEpoch = node.parent.unrealizedJustifiedEpoch
node.unrealizedFinalizedEpoch = node.parent.unrealizedFinalizedEpoch
return jc, fc
}
uj, uf, err := precompute.UnrealizedCheckpoints(ctx, state)
uj, uf, err := precompute.UnrealizedCheckpoints(state)
if err != nil {
log.WithError(err).Debug("could not compute unrealized checkpoints")
uj, uf = jc, fc
}
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
// Update store's unrealized checkpoints.
if uj.Epoch > s.unrealizedJustifiedCheckpoint.Epoch {
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
Epoch: uj.Epoch, Root: bytesutil.ToBytes32(uj.Root),
@@ -107,6 +108,8 @@ func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Nod
}
}
// Update node's checkpoints.
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
if stateEpoch < currentEpoch {
jc, fc = uj, uf
node.justifiedEpoch = uj.Epoch

View File

@@ -98,7 +98,7 @@ func TestStore_LongFork(t *testing.T) {
require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'c'}].weight)
// Update unrealized justification, c becomes head
f.UpdateUnrealizedCheckpoints()
f.updateUnrealizedCheckpoints()
headRoot, err = f.Head(ctx, []uint64{100})
require.NoError(t, err)
require.Equal(t, [32]byte{'c'}, headRoot)
@@ -179,7 +179,7 @@ func TestStore_NoDeadLock(t *testing.T) {
require.Equal(t, types.Epoch(0), f.FinalizedCheckpoint().Epoch)
// Realized Justified checkpoints, H becomes head
f.UpdateUnrealizedCheckpoints()
f.updateUnrealizedCheckpoints()
headRoot, err = f.Head(ctx, []uint64{100})
require.NoError(t, err)
require.Equal(t, [32]byte{'h'}, headRoot)
@@ -240,7 +240,7 @@ func TestStore_ForkNextEpoch(t *testing.T) {
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 1))
f.store.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: 1}
f.UpdateUnrealizedCheckpoints()
f.updateUnrealizedCheckpoints()
headRoot, err = f.Head(ctx, []uint64{100})
require.NoError(t, err)
require.Equal(t, [32]byte{'d'}, headRoot)
@@ -251,7 +251,7 @@ func TestStore_ForkNextEpoch(t *testing.T) {
func TestStore_PullTips_Heuristics(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
PullTips: true,
EnablePullTips: true,
})
defer resetCfg()
ctx := context.Background()

View File

@@ -17,3 +17,4 @@ var errInvalidNilCheckpoint = errors.New("invalid nil checkpoint")
var errInvalidUnrealizedJustifiedEpoch = errors.New("invalid unrealized justified epoch")
var errInvalidUnrealizedFinalizedEpoch = errors.New("invalid unrealized finalized epoch")
var errNilBlockHeader = errors.New("invalid nil block header")
var errInvalidParentRoot = errors.New("invalid parent root")

View File

@@ -65,7 +65,7 @@ func (f *ForkChoice) NewSlot(ctx context.Context, slot types.Slot) error {
f.store.justifiedCheckpoint = bjcp
}
}
if features.Get().PullTips {
if features.Get().EnablePullTips {
f.UpdateUnrealizedCheckpoints()
}
return nil

View File

@@ -56,8 +56,8 @@ func (f *ForkChoice) SetOptimisticToInvalid(ctx context.Context, root, parentRoo
defer f.store.nodesLock.Unlock()
invalidRoots := make([][32]byte, 0)
lastValidIndex, ok := f.store.payloadIndices[payloadHash]
if !ok || lastValidIndex == NonExistentNode {
return invalidRoots, errInvalidFinalizedNode
if !ok {
lastValidIndex = uint64(len(f.store.nodes))
}
invalidIndex, ok := f.store.nodesIndices[root]

View File

@@ -385,8 +385,6 @@ func TestSetOptimisticToInvalid_InvalidRoots(t *testing.T) {
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
_, err = f.SetOptimisticToInvalid(ctx, [32]byte{'p'}, [32]byte{'p'}, [32]byte{'B'})
require.ErrorIs(t, ErrUnknownNodeRoot, err)
_, err = f.SetOptimisticToInvalid(ctx, [32]byte{'a'}, [32]byte{}, [32]byte{'p'})
require.ErrorIs(t, errInvalidFinalizedNode, err)
}
// This is a regression test (10445)
@@ -417,3 +415,40 @@ func TestSetOptimisticToInvalid_ProposerBoost(t *testing.T) {
require.DeepEqual(t, params.BeaconConfig().ZeroHash, f.store.previousProposerBoostRoot)
f.store.proposerBoostLock.RUnlock()
}
// This is a regression test (10996)
func TestSetOptimisticToInvalid_BogusLVH(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
state, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, root))
state, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, root))
invalidRoots, err := f.SetOptimisticToInvalid(ctx, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'R'})
require.NoError(t, err)
require.Equal(t, 1, len(invalidRoots))
require.Equal(t, [32]byte{'b'}, invalidRoots[0])
}
// This is a regression test (10996)
func TestSetOptimisticToInvalid_BogusLVH_RotNotImported(t *testing.T) {
ctx := context.Background()
f := setup(1, 1)
state, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, root))
state, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, root))
invalidRoots, err := f.SetOptimisticToInvalid(ctx, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'R'})
require.NoError(t, err)
require.Equal(t, 0, len(invalidRoots))
}

View File

@@ -155,8 +155,8 @@ func (f *ForkChoice) InsertNode(ctx context.Context, state state.BeaconState, ro
return err
}
if features.Get().PullTips {
jc, fc = f.store.pullTips(ctx, state, node, jc, fc)
if features.Get().EnablePullTips {
jc, fc = f.store.pullTips(state, node, jc, fc)
}
return f.updateCheckpoints(ctx, jc, fc)
}

View File

@@ -1,8 +1,6 @@
package protoarray
import (
"context"
"github.com/prysmaticlabs/prysm/beacon-chain/core/epoch/precompute"
forkchoicetypes "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
@@ -77,16 +75,18 @@ func (f *ForkChoice) UpdateUnrealizedCheckpoints() {
}
}
func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
var uj, uf *ethpb.Checkpoint
func (s *Store) pullTips(state state.BeaconState, node *Node, jc, fc *ethpb.Checkpoint) (*ethpb.Checkpoint, *ethpb.Checkpoint) {
s.nodesLock.Lock()
defer s.nodesLock.Unlock()
currentSlot := slots.CurrentSlot(s.genesisTime)
currentEpoch := slots.ToEpoch(currentSlot)
stateSlot := state.Slot()
stateEpoch := slots.ToEpoch(stateSlot)
if node.parent == NonExistentNode {
if node.parent == NonExistentNode { // Nothing to do if the parent is nil.
return jc, fc
}
currentEpoch := slots.ToEpoch(slots.CurrentSlot(s.genesisTime))
stateSlot := state.Slot()
stateEpoch := slots.ToEpoch(stateSlot)
parent := s.nodes[node.parent]
currJustified := parent.unrealizedJustifiedEpoch == currentEpoch
prevJustified := parent.unrealizedJustifiedEpoch+1 == currentEpoch
@@ -97,12 +97,13 @@ func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Nod
return jc, fc
}
uj, uf, err := precompute.UnrealizedCheckpoints(ctx, state)
uj, uf, err := precompute.UnrealizedCheckpoints(state)
if err != nil {
log.WithError(err).Debug("could not compute unrealized checkpoints")
uj, uf = jc, fc
}
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
// Update store's unrealized checkpoints.
s.checkpointsLock.Lock()
if uj.Epoch > s.unrealizedJustifiedCheckpoint.Epoch {
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{
@@ -117,12 +118,15 @@ func (s *Store) pullTips(ctx context.Context, state state.BeaconState, node *Nod
Epoch: uf.Epoch, Root: bytesutil.ToBytes32(uf.Root),
}
}
s.checkpointsLock.Unlock()
// Update node's checkpoints.
node.unrealizedJustifiedEpoch, node.unrealizedFinalizedEpoch = uj.Epoch, uf.Epoch
if stateEpoch < currentEpoch {
jc, fc = uj, uf
node.justifiedEpoch = uj.Epoch
node.finalizedEpoch = uf.Epoch
}
s.checkpointsLock.Unlock()
return jc, fc
}

View File

@@ -252,7 +252,7 @@ func TestStore_ForkNextEpoch(t *testing.T) {
func TestStore_PullTips_Heuristics(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
PullTips: true,
EnablePullTips: true,
})
defer resetCfg()
ctx := context.Background()

View File

@@ -694,6 +694,7 @@ func (b *BeaconNode) registerSyncService() error {
regularsync.WithStateGen(b.stateGen),
regularsync.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
regularsync.WithSlasherBlockHeadersFeed(b.slasherBlockHeadersFeed),
regularsync.WithExecutionPayloadReconstructor(web3Service),
)
return b.services.RegisterService(rs)
}
@@ -799,49 +800,50 @@ func (b *BeaconNode) registerRPCService() error {
p2pService := b.fetchP2P()
rpcService := rpc.NewService(b.ctx, &rpc.Config{
Host: host,
Port: port,
BeaconMonitoringHost: beaconMonitoringHost,
BeaconMonitoringPort: beaconMonitoringPort,
CertFlag: cert,
KeyFlag: key,
BeaconDB: b.db,
Broadcaster: p2pService,
PeersFetcher: p2pService,
PeerManager: p2pService,
MetadataProvider: p2pService,
ChainInfoFetcher: chainService,
HeadUpdater: chainService,
HeadFetcher: chainService,
CanonicalFetcher: chainService,
ForkFetcher: chainService,
FinalizationFetcher: chainService,
BlockReceiver: chainService,
AttestationReceiver: chainService,
GenesisTimeFetcher: chainService,
GenesisFetcher: chainService,
OptimisticModeFetcher: chainService,
AttestationsPool: b.attestationPool,
ExitPool: b.exitPool,
SlashingsPool: b.slashingsPool,
SlashingChecker: slasherService,
SyncCommitteeObjectPool: b.syncCommitteePool,
POWChainService: web3Service,
POWChainInfoFetcher: web3Service,
ChainStartFetcher: chainStartFetcher,
MockEth1Votes: mockEth1DataVotes,
SyncService: syncService,
DepositFetcher: depositFetcher,
PendingDepositFetcher: b.depositCache,
BlockNotifier: b,
StateNotifier: b,
OperationNotifier: b,
StateGen: b.stateGen,
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
MaxMsgSize: maxMsgSize,
ProposerIdsCache: b.proposerIdsCache,
ExecutionEngineCaller: web3Service,
BlockBuilder: b.fetchBuilderService(),
ExecutionEngineCaller: web3Service,
ExecutionPayloadReconstructor: web3Service,
Host: host,
Port: port,
BeaconMonitoringHost: beaconMonitoringHost,
BeaconMonitoringPort: beaconMonitoringPort,
CertFlag: cert,
KeyFlag: key,
BeaconDB: b.db,
Broadcaster: p2pService,
PeersFetcher: p2pService,
PeerManager: p2pService,
MetadataProvider: p2pService,
ChainInfoFetcher: chainService,
HeadUpdater: chainService,
HeadFetcher: chainService,
CanonicalFetcher: chainService,
ForkFetcher: chainService,
FinalizationFetcher: chainService,
BlockReceiver: chainService,
AttestationReceiver: chainService,
GenesisTimeFetcher: chainService,
GenesisFetcher: chainService,
OptimisticModeFetcher: chainService,
AttestationsPool: b.attestationPool,
ExitPool: b.exitPool,
SlashingsPool: b.slashingsPool,
SlashingChecker: slasherService,
SyncCommitteeObjectPool: b.syncCommitteePool,
POWChainService: web3Service,
POWChainInfoFetcher: web3Service,
ChainStartFetcher: chainStartFetcher,
MockEth1Votes: mockEth1DataVotes,
SyncService: syncService,
DepositFetcher: depositFetcher,
PendingDepositFetcher: b.depositCache,
BlockNotifier: b,
StateNotifier: b,
OperationNotifier: b,
StateGen: b.stateGen,
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
MaxMsgSize: maxMsgSize,
ProposerIdsCache: b.proposerIdsCache,
BlockBuilder: b.fetchBuilderService(),
})
return b.services.RegisterService(rpcService)

View File

@@ -39,6 +39,8 @@ go_library(
"//beacon-chain/state/v1:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",
"//contracts/deposit:go_default_library",
"//crypto/hash:go_default_library",
@@ -77,6 +79,7 @@ go_test(
"block_reader_test.go",
"check_transition_config_test.go",
"deposit_test.go",
"engine_client_fuzz_test.go",
"engine_client_test.go",
"init_test.go",
"log_processing_test.go",
@@ -85,6 +88,7 @@ go_test(
"provider_test.go",
"service_test.go",
],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//async/event:go_default_library",
@@ -101,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",
@@ -121,6 +126,7 @@ go_test(
"@com_github_ethereum_go_ethereum//accounts/abi/bind/backends:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//core/beacon:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_ethereum_go_ethereum//trie:go_default_library",

View File

@@ -4,12 +4,14 @@ import (
"context"
"encoding/json"
"errors"
"math/big"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
@@ -161,7 +163,27 @@ func TestService_logTtdStatus(t *testing.T) {
require.NoError(t, r.Body.Close())
}()
resp := &pb.ExecutionBlock{TotalDifficulty: "0x12345678"}
resp := &pb.ExecutionBlock{
Header: gethtypes.Header{
ParentHash: common.Hash{},
UncleHash: common.Hash{},
Coinbase: common.Address{},
Root: common.Hash{},
TxHash: common.Hash{},
ReceiptHash: common.Hash{},
Bloom: gethtypes.Bloom{},
Difficulty: big.NewInt(1),
Number: big.NewInt(2),
GasLimit: 3,
GasUsed: 4,
Time: 5,
Extra: nil,
MixDigest: common.Hash{},
Nonce: gethtypes.BlockNonce{},
BaseFee: big.NewInt(6),
},
TotalDifficulty: "0x12345678",
}
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,

View File

@@ -14,6 +14,8 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/sirupsen/logrus"
@@ -46,6 +48,15 @@ type ForkchoiceUpdatedResponse struct {
PayloadId *pb.PayloadIDBytes `json:"payloadId"`
}
// ExecutionPayloadReconstructor defines a service that can reconstruct a full beacon
// block with an execution payload from a signed beacon block and a connection
// to an execution client's engine API.
type ExecutionPayloadReconstructor interface {
ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error)
}
// EngineCaller defines a client that can interact with an Ethereum
// execution node's engine service via JSON-RPC.
type EngineCaller interface {
@@ -57,7 +68,7 @@ type EngineCaller interface {
ExchangeTransitionConfiguration(
ctx context.Context, cfg *pb.TransitionConfiguration,
) error
ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error)
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error)
}
@@ -80,7 +91,7 @@ func (s *Service) NewPayload(ctx context.Context, payload *pb.ExecutionPayload)
switch result.Status {
case pb.PayloadStatus_INVALID_BLOCK_HASH:
return nil, fmt.Errorf("could not validate block hash: %v", result.ValidationError)
return nil, ErrInvalidBlockHashPayloadStatus
case pb.PayloadStatus_ACCEPTED, pb.PayloadStatus_SYNCING:
return nil, ErrAcceptedSyncingPayloadStatus
case pb.PayloadStatus_INVALID:
@@ -228,11 +239,11 @@ func (s *Service) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error
}
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
parentHash := bytesutil.ToBytes32(blk.ParentHash)
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
parentHash := blk.ParentHash
if parentHash == params.BeaconConfig().ZeroHash {
return nil, false, nil
}
parentBlk, err := s.ExecutionBlockByHash(ctx, parentHash)
parentBlk, err := s.ExecutionBlockByHash(ctx, parentHash, false /* no txs */)
if err != nil {
return nil, false, errors.Wrap(err, "could not get parent execution block")
}
@@ -248,12 +259,12 @@ func (s *Service) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error
if !parentReachedTTD {
log.WithFields(logrus.Fields{
"number": blk.Number,
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash)),
"hash": fmt.Sprintf("%#x", bytesutil.Trunc(blk.Hash[:])),
"td": blk.TotalDifficulty,
"parentTd": parentBlk.TotalDifficulty,
"ttd": terminalTotalDifficulty,
}).Info("Retrieved terminal block hash")
return blk.Hash, true, nil
return blk.Hash[:], true, nil
}
} else {
return nil, false, nil
@@ -281,15 +292,88 @@ func (s *Service) LatestExecutionBlock(ctx context.Context) (*pb.ExecutionBlock,
// ExecutionBlockByHash fetches an execution engine block by hash by calling
// eth_blockByHash via JSON-RPC.
func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*pb.ExecutionBlock, error) {
func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.ExecutionBlockByHash")
defer span.End()
result := &pb.ExecutionBlock{}
err := s.rpcClient.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, false /* no full transaction objects */)
err := s.rpcClient.CallContext(ctx, result, ExecutionBlockByHashMethod, hash, withTxs)
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, true /* with txs */)
if err != nil {
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
}
if executionBlock == nil {
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionBlockHash)
}
payload, err := fullPayloadFromExecutionBlock(header, executionBlock)
if err != nil {
return nil, err
}
fullBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
if err != nil {
return nil, err
}
reconstructedExecutionPayloadCount.Add(1)
return fullBlock, nil
}
func fullPayloadFromExecutionBlock(
header *pb.ExecutionPayloadHeader, block *pb.ExecutionBlock,
) (*pb.ExecutionPayload, error) {
if header == nil || block == nil {
return nil, errors.New("execution block and header cannot be nil")
}
if !bytes.Equal(header.BlockHash, block.Hash[:]) {
return nil, fmt.Errorf(
"block hash field in execution header %#x does not match execution block hash %#x",
header.BlockHash,
block.Hash,
)
}
txs := make([][]byte, len(block.Transactions))
for i, tx := range block.Transactions {
txBin, err := tx.MarshalBinary()
if err != nil {
return nil, err
}
txs[i] = txBin
}
return &pb.ExecutionPayload{
ParentHash: header.ParentHash,
FeeRecipient: header.FeeRecipient,
StateRoot: header.StateRoot,
ReceiptsRoot: header.ReceiptsRoot,
LogsBloom: header.LogsBloom,
PrevRandao: header.PrevRandao,
BlockNumber: header.BlockNumber,
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Timestamp: header.Timestamp,
ExtraData: header.ExtraData,
BaseFeePerGas: header.BaseFeePerGas,
BlockHash: block.Hash[:],
Transactions: txs,
}, nil
}
// Handles errors received from the RPC server according to the specification.
func handleRPCError(err error) error {
if err == nil {

View File

@@ -0,0 +1,114 @@
//go:build go1.18
// +build go1.18
package powchain_test
import (
"encoding/json"
"fmt"
"math"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/beacon"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/assert"
)
func FuzzForkChoiceResponse(f *testing.F) {
valHash := common.Hash([32]byte{0xFF, 0x01})
payloadID := beacon.PayloadID([8]byte{0x01, 0xFF, 0xAA, 0x00, 0xEE, 0xFE, 0x00, 0x00})
valErr := "asjajshjahsaj"
seed := &beacon.ForkChoiceResponse{
PayloadStatus: beacon.PayloadStatusV1{
Status: "INVALID_TERMINAL_BLOCK",
LatestValidHash: &valHash,
ValidationError: &valErr,
},
PayloadID: &payloadID,
}
output, err := json.Marshal(seed)
assert.NoError(f, err)
f.Add(output)
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
gethResp := &beacon.ForkChoiceResponse{}
prysmResp := &powchain.ForkchoiceUpdatedResponse{}
gethErr := json.Unmarshal(jsonBlob, gethResp)
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
assert.Equal(t, gethErr != nil, prysmErr != nil, fmt.Sprintf("geth and prysm unmarshaller return inconsistent errors. %v and %v", gethErr, prysmErr))
// Nothing to marshal if we have an error.
if gethErr != nil {
return
}
gethBlob, gethErr := json.Marshal(gethResp)
prysmBlob, prysmErr := json.Marshal(prysmResp)
assert.Equal(t, gethErr != nil, prysmErr != nil, "geth and prysm unmarshaller return inconsistent errors")
newGethResp := &beacon.ForkChoiceResponse{}
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
assert.NoError(t, newGethErr)
if newGethResp.PayloadStatus.Status == "UNKNOWN" {
return
}
newGethResp2 := &beacon.ForkChoiceResponse{}
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
assert.NoError(t, newGethErr)
assert.DeepEqual(t, newGethResp.PayloadID, newGethResp2.PayloadID)
assert.DeepEqual(t, newGethResp.PayloadStatus.Status, newGethResp2.PayloadStatus.Status)
assert.DeepEqual(t, newGethResp.PayloadStatus.LatestValidHash, newGethResp2.PayloadStatus.LatestValidHash)
isNilOrEmpty := newGethResp.PayloadStatus.ValidationError == nil || (*newGethResp.PayloadStatus.ValidationError == "")
isNilOrEmpty2 := newGethResp2.PayloadStatus.ValidationError == nil || (*newGethResp2.PayloadStatus.ValidationError == "")
assert.DeepEqual(t, isNilOrEmpty, isNilOrEmpty2)
if !isNilOrEmpty {
assert.DeepEqual(t, *newGethResp.PayloadStatus.ValidationError, *newGethResp2.PayloadStatus.ValidationError)
}
})
}
func FuzzExecutionPayload(f *testing.F) {
logsBloom := [256]byte{'j', 'u', 'n', 'k'}
execData := &beacon.ExecutableDataV1{
ParentHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
FeeRecipient: common.Address([20]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}),
StateRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
ReceiptsRoot: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
LogsBloom: logsBloom[:],
Random: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
Number: math.MaxUint64,
GasLimit: math.MaxUint64,
GasUsed: math.MaxUint64,
Timestamp: 100,
ExtraData: nil,
BaseFeePerGas: big.NewInt(math.MaxInt),
BlockHash: common.Hash([32]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}),
Transactions: [][]byte{{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}, {0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01}},
}
output, err := json.Marshal(execData)
assert.NoError(f, err)
f.Add(output)
f.Fuzz(func(t *testing.T, jsonBlob []byte) {
gethResp := &beacon.ExecutableDataV1{}
prysmResp := &pb.ExecutionPayload{}
gethErr := json.Unmarshal(jsonBlob, gethResp)
prysmErr := json.Unmarshal(jsonBlob, prysmResp)
assert.Equal(t, gethErr != nil, prysmErr != nil, fmt.Sprintf("geth and prysm unmarshaller return inconsistent errors. %v and %v", gethErr, prysmErr))
// Nothing to marshal if we have an error.
if gethErr != nil {
return
}
gethBlob, gethErr := json.Marshal(gethResp)
prysmBlob, prysmErr := json.Marshal(prysmResp)
assert.Equal(t, gethErr != nil, prysmErr != nil, "geth and prysm unmarshaller return inconsistent errors")
newGethResp := &beacon.ExecutableDataV1{}
newGethErr := json.Unmarshal(prysmBlob, newGethResp)
assert.NoError(t, newGethErr)
newGethResp2 := &beacon.ExecutableDataV1{}
newGethErr = json.Unmarshal(gethBlob, newGethResp2)
assert.NoError(t, newGethErr)
assert.DeepEqual(t, newGethResp.LogsBloom, newGethResp2.LogsBloom)
})
}

View File

@@ -12,20 +12,27 @@ 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"
"github.com/pkg/errors"
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{})
_ = ExecutionPayloadReconstructor(&Service{})
_ = EngineCaller(&mocks.EngineClient{})
)
@@ -250,7 +257,7 @@ func TestClient_HTTP(t *testing.T) {
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
require.ErrorContains(t, "could not validate block hash", err)
require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
t.Run(NewPayloadMethod+" INVALID status", func(t *testing.T) {
@@ -385,6 +392,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
@@ -416,7 +516,7 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
name: "current execution block invalid TD",
paramsTd: "1",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
Hash: common.BytesToHash([]byte("a")),
TotalDifficulty: "1115792089237316195423570985008687907853269984665640564039457584007913129638912",
},
errString: "could not convert total difficulty to uint256",
@@ -425,8 +525,10 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
name: "current execution block has zero hash parent",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: params.BeaconConfig().ZeroHash[:],
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash(params.BeaconConfig().ZeroHash[:]),
},
TotalDifficulty: "0x3",
},
},
@@ -434,8 +536,10 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
name: "could not get parent block",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x3",
},
errString: "could not get parent execution block",
@@ -444,13 +548,17 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
name: "parent execution block invalid TD",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
Hash: common.BytesToHash([]byte("b")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("c")),
},
TotalDifficulty: "1",
},
errString: "could not convert total difficulty to uint256",
@@ -459,29 +567,37 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
name: "happy case",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
Hash: common.BytesToHash([]byte("b")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("c")),
},
TotalDifficulty: "0x1",
},
wantExists: true,
wantTerminalBlockHash: []byte{'a'},
wantTerminalBlockHash: common.BytesToHash([]byte("a")).Bytes(),
},
{
name: "ttd not reached",
paramsTd: "3",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x2",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
Hash: common.BytesToHash([]byte("b")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("c")),
},
TotalDifficulty: "0x1",
},
},
@@ -494,7 +610,7 @@ func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
var m map[[32]byte]*pb.ExecutionBlock
if tt.parentPowBlock != nil {
m = map[[32]byte]*pb.ExecutionBlock{
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
tt.parentPowBlock.Hash: tt.parentPowBlock,
}
}
client := mocks.EngineClient{
@@ -534,6 +650,87 @@ func Test_tDStringToUint256(t *testing.T) {
require.ErrorContains(t, "hex number > 256 bits", err)
}
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["number"] = encodedNum
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 TestExchangeTransitionConfiguration(t *testing.T) {
fix := fixtures()
ctx := context.Background()
@@ -749,8 +946,6 @@ func fixtures() map[string]interface{} {
BlockHash: foo[:],
Transactions: [][]byte{foo[:]},
}
number := bytesutil.PadTo([]byte("100"), fieldparams.RootLength)
hash := bytesutil.PadTo([]byte("hash"), fieldparams.RootLength)
parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
sha3Uncles := bytesutil.PadTo([]byte("sha3Uncles"), fieldparams.RootLength)
miner := bytesutil.PadTo([]byte("miner"), fieldparams.FeeRecipientLength)
@@ -759,25 +954,24 @@ func fixtures() map[string]interface{} {
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
executionBlock := &pb.ExecutionBlock{
Number: number,
Hash: hash,
ParentHash: parent,
Sha3Uncles: sha3Uncles,
Miner: miner,
StateRoot: stateRoot,
TransactionsRoot: transactionsRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
Difficulty: bytesutil.PadTo([]byte("1"), fieldparams.RootLength),
TotalDifficulty: "2",
GasLimit: 3,
GasUsed: 4,
Timestamp: 5,
Size: bytesutil.PadTo([]byte("6"), fieldparams.RootLength),
ExtraData: bytesutil.PadTo([]byte("extraData"), fieldparams.RootLength),
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
Transactions: [][]byte{foo[:]},
Uncles: [][]byte{foo[:]},
Header: gethtypes.Header{
ParentHash: common.BytesToHash(parent),
UncleHash: common.BytesToHash(sha3Uncles),
Coinbase: common.BytesToAddress(miner),
Root: common.BytesToHash(stateRoot),
TxHash: common.BytesToHash(transactionsRoot),
ReceiptHash: common.BytesToHash(receiptsRoot),
Bloom: gethtypes.BytesToBloom(logsBloom),
Difficulty: big.NewInt(1),
Number: big.NewInt(2),
GasLimit: 3,
GasUsed: 4,
Time: 5,
Extra: []byte("extra"),
MixDigest: common.BytesToHash([]byte("mix")),
Nonce: gethtypes.EncodeNonce(6),
BaseFee: big.NewInt(7),
},
}
status := &pb.PayloadStatus{
Status: pb.PayloadStatus_VALID,
@@ -806,7 +1000,7 @@ func fixtures() map[string]interface{} {
forkChoiceInvalidResp := &ForkchoiceUpdatedResponse{
Status: &pb.PayloadStatus{
Status: pb.PayloadStatus_INVALID,
LatestValidHash: []byte("latestValidHash"),
LatestValidHash: bytesutil.PadTo([]byte("latestValidHash"), 32),
},
PayloadId: &id,
}
@@ -859,6 +1053,66 @@ func fixtures() map[string]interface{} {
}
}
func Test_fullPayloadFromExecutionBlock(t *testing.T) {
type args struct {
header *pb.ExecutionPayloadHeader
block *pb.ExecutionBlock
}
wantedHash := common.BytesToHash([]byte("foo"))
tests := []struct {
name string
args args
want *pb.ExecutionPayload
err string
}{
{
name: "nil header fails",
args: args{header: nil, block: &pb.ExecutionBlock{}},
err: "cannot be nil",
},
{
name: "nil block fails",
args: args{header: &pb.ExecutionPayloadHeader{}, block: nil},
err: "cannot be nil",
},
{
name: "block hash field in header and block hash mismatch",
args: args{
header: &pb.ExecutionPayloadHeader{
BlockHash: []byte("foo"),
},
block: &pb.ExecutionBlock{
Hash: common.BytesToHash([]byte("bar")),
},
},
err: "does not match execution block hash",
},
{
name: "ok",
args: args{
header: &pb.ExecutionPayloadHeader{
BlockHash: wantedHash[:],
},
block: &pb.ExecutionBlock{
Hash: wantedHash,
},
},
want: &pb.ExecutionPayload{
BlockHash: wantedHash[:],
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := fullPayloadFromExecutionBlock(tt.args.header, tt.args.block)
if (err != nil) && !strings.Contains(err.Error(), tt.err) {
t.Fatalf("Wanted err %s got %v", tt.err, err)
}
require.DeepEqual(t, tt.want, got)
})
}
}
type testEngineService struct{}
func (*testEngineService) NoArgsRets() {}

View File

@@ -30,6 +30,8 @@ var (
ErrAcceptedSyncingPayloadStatus = errors.New("payload status is SYNCING or ACCEPTED")
// ErrInvalidPayloadStatus when the status of the payload is invalid.
ErrInvalidPayloadStatus = errors.New("payload status is INVALID")
// ErrInvalidBlockHashPayloadStatus when the status of the payload fails to validate block hash.
ErrInvalidBlockHashPayloadStatus = errors.New("payload status is INVALID_BLOCK_HASH")
// ErrNilResponse when the response is nil.
ErrNilResponse = errors.New("nil response")
)

View File

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

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff01000000fffffff0000000000000000000\",\"logsBloom\":\"0x6a756e6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"blockNumber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"timestamp\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7fffff0000000fff\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"BAseFeePerGAs\":\"0X0\"}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"logsBloom\":\"0x6a756e6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff01ff000000ff0100000000000000000000000000000000\",\"blockNumber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"timestamp\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7f0fffffffffffff\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01Ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff0fff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"logsBloom\":\"0x6a756e6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff011f01ff01ff0100000000000000000000000000000000\",\"blockNumber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"timestamp\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7fffffffffffffff\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"pArentHAsh\":\"\",\"feeReCipient\":\"\",\"stAteRoot\":\"\",\"reCeiptsRoot\":\"\",\"logsBloom\":\"\",\"prevRAndAo\":\"\",\"eXtrADAtA\":\"\",\"BAseFeePerGAs\":\"0X0\",\"BloCkHAsh\":\"\",\"trAnsACtions\":[]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff01000000fffffff0000000000000000000\",\"logsBloom\":\"0x6a756e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"blockNumber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"t\xc1\xc1\xc1\xc1\xc1\xc1\xc1\xc1\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7fffff0000000fff\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff01000000fffffff0000000000000000000\",\"logsBloom\":\"0x6a756e6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"blockNumber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"timestamp\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7fffff0000000fff0000000\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff010ff1ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -0,0 +1,2 @@
go test fuzz v1
[]byte("{\"parentHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"feeRecipient\":\"0xff01ff01ff01ff01ffff01ff01ff01ff01ff0000\",\"stateRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"receiptsRoot\":\"0xff01ff01ff01ff01ff01ff01ff01ff01000000fffffff0000000000000000000\",\"logsBloom\":\"0x6a756e6b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"blockNu0000000mber\":\"0xffffffffffffffff\",\"gasLimit\":\"0xffffffffffffffff\",\"gasUsed\":\"0xffffffffffffffff\",\"timestamp\":\"0x64\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x7fffff0000000fff\",\"blockHash\":\"0xff01ff01ff01ff01ff01ff01ff01ff0100000000000000000000000000000000\",\"transactions\":[\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\",\"0xff01ff01ff01ff01ff01ff01ff01ff01\"]}")

View File

@@ -18,6 +18,8 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/v1:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -9,26 +9,31 @@ 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"
)
// EngineClient --
type EngineClient struct {
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
NewPayloadResp []byte
PayloadIDBytes *pb.PayloadIDBytes
ForkChoiceUpdatedResp []byte
ExecutionPayload *pb.ExecutionPayload
ExecutionBlock *pb.ExecutionBlock
Err error
ErrLatestExecBlock error
ErrExecBlockByHash error
ErrForkchoiceUpdated error
ErrNewPayload error
ExecutionPayloadByBlockHash map[[32]byte]*pb.ExecutionPayload
BlockByHashMap map[[32]byte]*pb.ExecutionBlock
BlockWithTxsByHashMap map[[32]byte]*pb.ExecutionBlockWithTxs
NumReconstructedPayloads uint64
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
}
// NewPayload --
@@ -70,6 +75,33 @@ func (e *EngineClient) ExecutionBlockByHash(_ context.Context, h common.Hash) (*
return b, e.ErrExecBlockByHash
}
func (e *EngineClient) ReconstructFullBellatrixBlock(
_ context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error) {
if !blindedBlock.Block().IsBlinded() {
return nil, errors.New("block must be blinded")
}
header, err := blindedBlock.Block().Body().ExecutionPayloadHeader()
if err != nil {
return nil, err
}
payload, ok := e.ExecutionPayloadByBlockHash[bytesutil.ToBytes32(header.BlockHash)]
if !ok {
return nil, errors.New("block not found")
}
e.NumReconstructedPayloads++
return wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
}
// ExecutionBlockByHashWithTxs --
func (e *EngineClient) ExecutionBlockByHashWithTxs(_ context.Context, h common.Hash) (*pb.ExecutionBlockWithTxs, error) {
b, ok := e.BlockWithTxsByHashMap[h]
if !ok {
return nil, errors.New("block not found")
}
return b, e.ErrExecBlockByHash
}
// GetTerminalBlockHash --
func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool, error) {
ttd := new(big.Int)
@@ -94,8 +126,8 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool,
currentTotalDifficulty, _ := uint256.FromBig(b)
blockReachedTTD := currentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
parentHash := bytesutil.ToBytes32(blk.ParentHash)
if len(blk.ParentHash) == 0 || parentHash == params.BeaconConfig().ZeroHash {
parentHash := blk.ParentHash
if parentHash == params.BeaconConfig().ZeroHash {
return nil, false, nil
}
parentBlk, err := e.ExecutionBlockByHash(ctx, parentHash)
@@ -110,7 +142,7 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context) ([]byte, bool,
parentTotalDifficulty, _ := uint256.FromBig(b)
parentReachedTTD := parentTotalDifficulty.Cmp(terminalTotalDifficulty) >= 0
if !parentReachedTTD {
return blk.Hash, true, nil
return blk.Hash[:], true, nil
}
} else {
return nil, false, nil

View File

@@ -29,6 +29,7 @@ go_library(
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/rpc/eth/helpers:go_default_library",
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
"//beacon-chain/rpc/statefetcher:go_default_library",

View File

@@ -474,8 +474,42 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
ExecutionOptimistic: isOptimistic,
}, nil
}
if _, err := blk.PbBlindedBellatrixBlock(); err == nil {
signedFullBlock, err := bs.ExecutionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blk)
if err != nil {
return nil, status.Errorf(
codes.Internal,
"Could not reconstruct full execution payload to create signed beacon block: %v",
err,
)
}
bellatrixBlk, err = signedFullBlock.PbBellatrixBlock()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
v2Blk, err := migration.V1Alpha1BeaconBlockBellatrixToV2(bellatrixBlk.Block)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err)
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if block is optimistic: %v", err)
}
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Data: &ethpbv2.SignedBeaconBlockContainerV2{
Message: &ethpbv2.SignedBeaconBlockContainerV2_BellatrixBlock{BellatrixBlock: v2Blk},
Signature: blk.Signature(),
},
ExecutionOptimistic: isOptimistic,
}, nil
}
// ErrUnsupportedBellatrixBlock means that we have another block type
if !errors.Is(err, wrapper.ErrUnsupportedBellatrixBlock) {
if !errors.Is(err, wrapper.ErrUnsupportedBlindedBellatrixBlock) {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
@@ -21,22 +22,23 @@ import (
// Server defines a server implementation of the gRPC Beacon Chain service,
// providing RPC endpoints to access data relevant to the Ethereum Beacon Chain.
type Server struct {
BeaconDB db.ReadOnlyDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
BlockReceiver blockchain.BlockReceiver
BlockNotifier blockfeed.Notifier
OperationNotifier operation.Notifier
Broadcaster p2p.Broadcaster
AttestationsPool attestations.Pool
SlashingsPool slashings.PoolManager
VoluntaryExitsPool voluntaryexits.PoolManager
StateGenService stategen.StateManager
StateFetcher statefetcher.Fetcher
HeadFetcher blockchain.HeadFetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
V1Alpha1ValidatorServer *v1alpha1validator.Server
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
HeadUpdater blockchain.HeadUpdater
BeaconDB db.ReadOnlyDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
BlockReceiver blockchain.BlockReceiver
BlockNotifier blockfeed.Notifier
OperationNotifier operation.Notifier
Broadcaster p2p.Broadcaster
AttestationsPool attestations.Pool
SlashingsPool slashings.PoolManager
VoluntaryExitsPool voluntaryexits.PoolManager
StateGenService stategen.StateManager
StateFetcher statefetcher.Fetcher
HeadFetcher blockchain.HeadFetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
V1Alpha1ValidatorServer *v1alpha1validator.Server
SyncChecker sync.Checker
CanonicalHistory *stategen.CanonicalHistory
HeadUpdater blockchain.HeadUpdater
ExecutionPayloadReconstructor powchain.ExecutionPayloadReconstructor
}

View File

@@ -139,6 +139,12 @@ func convertToBlockContainer(blk interfaces.SignedBeaconBlock, root [32]byte, is
return nil, err
}
ctr.Block = &ethpb.BeaconBlockContainer_BellatrixBlock{BellatrixBlock: rBlk}
case version.BellatrixBlind:
rBlk, err := blk.PbBlindedBellatrixBlock()
if err != nil {
return nil, err
}
ctr.Block = &ethpb.BeaconBlockContainer_BlindedBellatrixBlock{BlindedBellatrixBlock: rBlk}
default:
return nil, errors.Errorf("block type is not recognized: %d", blk.Version())
}

View File

@@ -871,8 +871,12 @@ func TestServer_ListBeaconBlocks_Genesis(t *testing.T) {
blk.Block.ParentRoot = parentRoot[:]
wrapped, err := wrapper.WrappedSignedBeaconBlock(blk)
assert.NoError(t, err)
blinded, err := wrapper.WrapSignedBlindedBeaconBlock(wrapped)
assert.NoError(t, err)
blindedProto, err := blinded.PbBlindedBellatrixBlock()
assert.NoError(t, err)
blkContainer := &ethpb.BeaconBlockContainer{
Block: &ethpb.BeaconBlockContainer_BellatrixBlock{BellatrixBlock: blk}}
Block: &ethpb.BeaconBlockContainer_BlindedBellatrixBlock{BlindedBellatrixBlock: blindedProto}}
runListBlocksGenesis(t, wrapped, blkContainer)
})
}

View File

@@ -165,6 +165,7 @@ go_test(
"//time/slots:go_default_library",
"@com_github_d4l3k_messagediff//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
chainMock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
@@ -203,7 +204,7 @@ func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
}{
{
name: "use terminal block hash, doesn't exist",
paramsTerminalHash: []byte{'a'},
paramsTerminalHash: common.BytesToHash([]byte("a")).Bytes(),
errString: "could not fetch height for hash",
},
{
@@ -218,17 +219,21 @@ func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
name: "use terminal total difficulty",
paramsTd: "2",
currentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'a'},
ParentHash: []byte{'b'},
Hash: common.BytesToHash([]byte("a")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("b")),
},
TotalDifficulty: "0x3",
},
parentPowBlock: &pb.ExecutionBlock{
Hash: []byte{'b'},
ParentHash: []byte{'c'},
Hash: common.BytesToHash([]byte("b")),
Header: gethtypes.Header{
ParentHash: common.BytesToHash([]byte("c")),
},
TotalDifficulty: "0x1",
},
wantExists: true,
wantTerminalBlockHash: []byte{'a'},
wantTerminalBlockHash: common.BytesToHash([]byte("a")).Bytes(),
},
}
for _, tt := range tests {
@@ -240,7 +245,7 @@ func TestServer_getTerminalBlockHashIfExists(t *testing.T) {
var m map[[32]byte]*pb.ExecutionBlock
if tt.parentPowBlock != nil {
m = map[[32]byte]*pb.ExecutionBlock{
bytesutil.ToBytes32(tt.parentPowBlock.Hash): tt.parentPowBlock,
tt.parentPowBlock.Hash: tt.parentPowBlock,
}
}
c := powtesting.NewPOWChain()

View File

@@ -71,49 +71,50 @@ type Service struct {
// Config options for the beacon node RPC server.
type Config struct {
Host string
Port string
CertFlag string
KeyFlag string
BeaconMonitoringHost string
BeaconMonitoringPort int
BeaconDB db.HeadAccessDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadUpdater blockchain.HeadUpdater
HeadFetcher blockchain.HeadFetcher
CanonicalFetcher blockchain.CanonicalFetcher
ForkFetcher blockchain.ForkFetcher
FinalizationFetcher blockchain.FinalizationFetcher
AttestationReceiver blockchain.AttestationReceiver
BlockReceiver blockchain.BlockReceiver
POWChainService powchain.Chain
ChainStartFetcher powchain.ChainStartFetcher
POWChainInfoFetcher powchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
EnableDebugRPCEndpoints bool
MockEth1Votes bool
AttestationsPool attestations.Pool
ExitPool voluntaryexits.PoolManager
SlashingsPool slashings.PoolManager
SlashingChecker slasherservice.SlashingChecker
SyncCommitteeObjectPool synccommittee.Pool
SyncService chainSync.Checker
Broadcaster p2p.Broadcaster
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
MetadataProvider p2p.MetadataProvider
DepositFetcher depositcache.DepositFetcher
PendingDepositFetcher depositcache.PendingDepositsFetcher
StateNotifier statefeed.Notifier
BlockNotifier blockfeed.Notifier
OperationNotifier opfeed.Notifier
StateGen *stategen.State
MaxMsgSize int
ExecutionEngineCaller powchain.EngineCaller
ProposerIdsCache *cache.ProposerPayloadIDsCache
OptimisticModeFetcher blockchain.OptimisticModeFetcher
BlockBuilder builder.BlockBuilder
ExecutionPayloadReconstructor powchain.ExecutionPayloadReconstructor
Host string
Port string
CertFlag string
KeyFlag string
BeaconMonitoringHost string
BeaconMonitoringPort int
BeaconDB db.HeadAccessDatabase
ChainInfoFetcher blockchain.ChainInfoFetcher
HeadUpdater blockchain.HeadUpdater
HeadFetcher blockchain.HeadFetcher
CanonicalFetcher blockchain.CanonicalFetcher
ForkFetcher blockchain.ForkFetcher
FinalizationFetcher blockchain.FinalizationFetcher
AttestationReceiver blockchain.AttestationReceiver
BlockReceiver blockchain.BlockReceiver
POWChainService powchain.Chain
ChainStartFetcher powchain.ChainStartFetcher
POWChainInfoFetcher powchain.ChainInfoFetcher
GenesisTimeFetcher blockchain.TimeFetcher
GenesisFetcher blockchain.GenesisFetcher
EnableDebugRPCEndpoints bool
MockEth1Votes bool
AttestationsPool attestations.Pool
ExitPool voluntaryexits.PoolManager
SlashingsPool slashings.PoolManager
SlashingChecker slasherservice.SlashingChecker
SyncCommitteeObjectPool synccommittee.Pool
SyncService chainSync.Checker
Broadcaster p2p.Broadcaster
PeersFetcher p2p.PeersProvider
PeerManager p2p.PeerManager
MetadataProvider p2p.MetadataProvider
DepositFetcher depositcache.DepositFetcher
PendingDepositFetcher depositcache.PendingDepositsFetcher
StateNotifier statefeed.Notifier
BlockNotifier blockfeed.Notifier
OperationNotifier opfeed.Notifier
StateGen *stategen.State
MaxMsgSize int
ExecutionEngineCaller powchain.EngineCaller
ProposerIdsCache *cache.ProposerPayloadIDsCache
OptimisticModeFetcher blockchain.OptimisticModeFetcher
BlockBuilder builder.BlockBuilder
}
// NewService instantiates a new RPC service instance that will
@@ -309,11 +310,12 @@ func (s *Service) Start() {
StateGenService: s.cfg.StateGen,
ReplayerBuilder: ch,
},
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
HeadFetcher: s.cfg.HeadFetcher,
VoluntaryExitsPool: s.cfg.ExitPool,
V1Alpha1ValidatorServer: validatorServer,
SyncChecker: s.cfg.SyncService,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
HeadFetcher: s.cfg.HeadFetcher,
VoluntaryExitsPool: s.cfg.ExitPool,
V1Alpha1ValidatorServer: validatorServer,
SyncChecker: s.cfg.SyncService,
ExecutionPayloadReconstructor: s.cfg.ExecutionPayloadReconstructor,
}
ethpbv1alpha1.RegisterNodeServer(s.grpcServer, nodeServer)
ethpbservice.RegisterBeaconNodeServer(s.grpcServer, nodeServerV1)

View File

@@ -76,6 +76,7 @@ go_library(
"//beacon-chain/p2p/encoder:go_default_library",
"//beacon-chain/p2p/peers:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//cache/lru:go_default_library",
@@ -185,6 +186,7 @@ go_test(
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/powchain:go_default_library",
"//beacon-chain/powchain/testing:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/state/v1:go_default_library",
@@ -193,6 +195,7 @@ go_test(
"//cmd/beacon-chain/flags:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
@@ -201,6 +204,7 @@ go_test(
"//encoding/bytesutil:go_default_library",
"//encoding/ssz/equality:go_default_library",
"//network/forks:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//proto/prysm/v1alpha1/metadata:go_default_library",
@@ -210,6 +214,8 @@ go_test(
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_d4l3k_messagediff//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_kevinms_leakybucket_go//:go_default_library",

View File

@@ -75,7 +75,13 @@ var (
Buckets: []float64{10, 50, 100, 200, 400, 800, 1600, 3200},
},
)
rpcBlocksByRangeResponseLatency = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "rpc_blocks_by_range_response_latency_milliseconds",
Help: "Captures total time to respond to rpc blocks by range requests in a milliseconds distribution",
Buckets: []float64{250, 500, 1000, 1500, 2000, 3000, 4000, 10000},
},
)
arrivalBlockPropagationHistogram = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "block_arrival_latency_milliseconds",

View File

@@ -11,6 +11,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
)
@@ -120,3 +121,10 @@ func WithSlasherBlockHeadersFeed(slasherBlockHeadersFeed *event.Feed) Option {
return nil
}
}
func WithExecutionPayloadReconstructor(r powchain.ExecutionPayloadReconstructor) Option {
return func(s *Service) error {
s.cfg.executionPayloadReconstructor = r
return nil
}
}

View File

@@ -12,6 +12,7 @@ import (
"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"
"github.com/prysmaticlabs/prysm/monitoring/tracing"
pb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -161,18 +162,29 @@ func (s *Service) writeBlockRangeToStream(ctx context.Context, startSlot, endSlo
tracing.AnnotateError(span, err)
return err
}
start := time.Now()
for _, b := range blks {
if b == nil || b.IsNil() || b.Block().IsNil() {
if err := wrapper.BeaconBlockIsNil(b); err != nil {
continue
}
if chunkErr := s.chunkBlockWriter(stream, b); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
blockToWrite := b
if blockToWrite.Block().IsBlinded() {
fullBlock, err := s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blockToWrite)
if err != nil {
log.WithError(err).Error("Could not get reconstruct full bellatrix block from blinded body")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
return err
}
blockToWrite = fullBlock
}
if chunkErr := s.chunkBlockWriter(stream, blockToWrite); chunkErr != nil {
log.WithError(chunkErr).Error("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
}
rpcBlocksByRangeResponseLatency.Observe(float64(time.Since(start).Milliseconds()))
// Return error in the event we have an invalid parent.
return err
}

View File

@@ -3,10 +3,13 @@ package sync
import (
"context"
"io"
"math/big"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/protocol"
@@ -17,13 +20,16 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
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/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"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -157,6 +163,121 @@ func TestRPCBeaconBlocksByRange_ReturnCorrectNumberBack(t *testing.T) {
}
}
func TestRPCBeaconBlocksByRange_CanReconstructFullPayloadBlocks(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
req := &ethpb.BeaconBlocksByRangeRequest{
StartSlot: 200,
Step: 21,
Count: 33,
}
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
stateRoot := bytesutil.PadTo([]byte("stateRoot"), fieldparams.RootLength)
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
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)
blockHash := bytesutil.ToBytes32([]byte("foo"))
payload := &enginev1.ExecutionPayload{
ParentHash: parent,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: stateRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
PrevRandao: blockHash[:],
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BlockHash: blockHash[:],
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
Transactions: encodedBinaryTxs,
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
endSlot := req.StartSlot.Add(req.Step * (req.Count - 1))
expectedRoots := make([][32]byte, req.Count)
// Populate the database with blocks that would match the request.
for i, j := endSlot, req.Count-1; i >= req.StartSlot; i -= types.Slot(req.Step) {
blk := util.NewBlindedBeaconBlockBellatrix()
blk.Block.Body.ExecutionPayloadHeader = header
blk.Block.Slot = i
rt, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
expectedRoots[j] = rt
wsb, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, d.SaveBlock(context.Background(), wsb))
j--
}
mockEngine := &mockPOW.EngineClient{
ExecutionPayloadByBlockHash: map[[32]byte]*enginev1.ExecutionPayload{
blockHash: payload,
},
}
r := &Service{cfg: &config{
p2p: p1,
beaconDB: d,
chain: &chainMock.ChainService{},
executionPayloadReconstructor: mockEngine,
},
rateLimiter: newRateLimiter(p1),
}
pcl := protocol.ID(p2p.RPCBlocksByRangeTopicV1)
topic := string(pcl)
r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(0.000001, int64(req.Count*10), false)
var wg sync.WaitGroup
wg.Add(1)
p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
defer wg.Done()
prevSlot := types.Slot(0)
require.Equal(t, uint64(len(expectedRoots)), req.Count, "Number of roots not expected")
for i, j := req.StartSlot, 0; i < req.StartSlot.Add(req.Count*req.Step); i += types.Slot(req.Step) {
expectSuccess(t, stream)
res := &ethpb.SignedBeaconBlockBellatrix{}
assert.NoError(t, r.cfg.p2p.Encoding().DecodeWithMaxLength(stream, res))
if res.Block.Slot < prevSlot {
t.Errorf("Received block is unsorted with slot %d lower than previous slot %d", res.Block.Slot, prevSlot)
}
rt, err := res.Block.HashTreeRoot()
require.NoError(t, err)
assert.Equal(t, expectedRoots[j], rt, "roots not equal")
prevSlot = res.Block.Slot
j++
}
require.Equal(t, uint64(33), mockEngine.NumReconstructedPayloads, "wrong number of reconstructed payloads")
})
stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
require.NoError(t, err)
require.NoError(t, r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1))
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")
}
}
func TestRPCBeaconBlocksByRange_RPCHandlerReturnsSortedBlocks(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
)
// sendRecentBeaconBlocksRequest sends a recent beacon blocks request to a peer to get
@@ -68,13 +69,24 @@ func (s *Service) beaconBlocksRootRPCHandler(ctx context.Context, msg interface{
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
return err
}
if blk == nil || blk.IsNil() {
if err := wrapper.BeaconBlockIsNil(blk); err != nil {
continue
}
if blk.Block().IsBlinded() {
blk, err = s.cfg.executionPayloadReconstructor.ReconstructFullBellatrixBlock(ctx, blk)
if err != nil {
log.WithError(err).Error("Could not get reconstruct full bellatrix block from blinded body")
s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream)
return err
}
}
if err := s.chunkBlockWriter(stream, blk); err != nil {
return err
}
}
closeStream(stream, log)
return nil
}

View File

@@ -2,10 +2,13 @@ package sync
import (
"context"
"math/big"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/protocol"
@@ -16,8 +19,14 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
p2pTypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
mockPOW "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"
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"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -72,6 +81,103 @@ func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) {
}
}
func TestRecentBeaconBlocksRPCHandler_ReturnsBlocks_ReconstructsExecutionPayloads(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
parent := bytesutil.PadTo([]byte("parentHash"), fieldparams.RootLength)
stateRoot := bytesutil.PadTo([]byte("stateRoot"), fieldparams.RootLength)
receiptsRoot := bytesutil.PadTo([]byte("receiptsRoot"), fieldparams.RootLength)
logsBloom := bytesutil.PadTo([]byte("logs"), fieldparams.LogsBloomLength)
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)
blockHash := bytesutil.ToBytes32([]byte("foo"))
payload := &enginev1.ExecutionPayload{
ParentHash: parent,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: stateRoot,
ReceiptsRoot: receiptsRoot,
LogsBloom: logsBloom,
PrevRandao: blockHash[:],
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BlockHash: blockHash[:],
BaseFeePerGas: bytesutil.PadTo([]byte("baseFeePerGas"), fieldparams.RootLength),
Transactions: encodedBinaryTxs,
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
var blkRoots p2pTypes.BeaconBlockByRootsReq
// Populate the database with blocks that would match the request.
for i := types.Slot(1); i < 11; i++ {
blk := util.NewBlindedBeaconBlockBellatrix()
blk.Block.Body.ExecutionPayloadHeader = header
blk.Block.Slot = i
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
wsb, err := wrapper.WrappedSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, d.SaveBlock(context.Background(), wsb))
blkRoots = append(blkRoots, root)
}
mockEngine := &mockPOW.EngineClient{
ExecutionPayloadByBlockHash: map[[32]byte]*enginev1.ExecutionPayload{
blockHash: payload,
},
}
r := &Service{cfg: &config{
p2p: p1,
beaconDB: d,
executionPayloadReconstructor: mockEngine,
}, rateLimiter: newRateLimiter(p1)}
r.cfg.chain = &mock.ChainService{ValidatorsRoot: [32]byte{}}
pcl := protocol.ID(p2p.RPCBlocksByRootTopicV1)
topic := string(pcl)
r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(10000, 10000, false)
var wg sync.WaitGroup
wg.Add(1)
p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
defer wg.Done()
for i := range blkRoots {
expectSuccess(t, stream)
res := util.NewBeaconBlockBellatrix()
assert.NoError(t, r.cfg.p2p.Encoding().DecodeWithMaxLength(stream, res))
if uint64(res.Block.Slot) != uint64(i+1) {
t.Errorf("Received unexpected block slot %d but wanted %d", res.Block.Slot, i+1)
}
}
require.Equal(t, uint64(10), mockEngine.NumReconstructedPayloads)
})
stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
require.NoError(t, err)
err = r.beaconBlocksRootRPCHandler(context.Background(), &blkRoots, stream1)
assert.NoError(t, err)
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")
}
}
func TestRecentBeaconBlocks_RPCRequestSent(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)

View File

@@ -30,6 +30,7 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
var obtainedCtx []byte
switch blk.Version() {
case version.Phase0:
valRoot := chain.GenesisValidatorsRoot()
@@ -45,7 +46,7 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
obtainedCtx = digest[:]
case version.Bellatrix:
case version.Bellatrix, version.BellatrixBlind:
valRoot := chain.GenesisValidatorsRoot()
digest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().BellatrixForkEpoch, valRoot[:])
if err != nil {

View File

@@ -29,6 +29,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/synccommittee"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
lruwrpr "github.com/prysmaticlabs/prysm/cache/lru"
"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
@@ -68,21 +69,22 @@ type validationFn func(ctx context.Context) (pubsub.ValidationResult, error)
// config to hold dependencies for the sync service.
type config struct {
attestationNotifier operation.Notifier
p2p p2p.P2P
beaconDB db.NoHeadAccessDatabase
attPool attestations.Pool
exitPool voluntaryexits.PoolManager
slashingPool slashings.PoolManager
syncCommsPool synccommittee.Pool
chain blockchainService
initialSync Checker
stateNotifier statefeed.Notifier
blockNotifier blockfeed.Notifier
operationNotifier operation.Notifier
stateGen *stategen.State
slasherAttestationsFeed *event.Feed
slasherBlockHeadersFeed *event.Feed
attestationNotifier operation.Notifier
p2p p2p.P2P
beaconDB db.NoHeadAccessDatabase
attPool attestations.Pool
exitPool voluntaryexits.PoolManager
slashingPool slashings.PoolManager
syncCommsPool synccommittee.Pool
chain blockchainService
initialSync Checker
stateNotifier statefeed.Notifier
blockNotifier blockfeed.Notifier
operationNotifier operation.Notifier
executionPayloadReconstructor powchain.ExecutionPayloadReconstructor
stateGen *stategen.State
slasherAttestationsFeed *event.Feed
slasherBlockHeadersFeed *event.Feed
}
// This defines the interface for interacting with block chain service

View File

@@ -358,6 +358,13 @@ var (
" For additional setting overrides use the --" + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " Flags. ",
Value: params.BeaconConfig().EthBurnAddressHex,
}
// EnableValidatorRegistrationFlag enables the periodic validator registration API calls that will update the custom builder with validator settings.
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: false,
}
)
// DefaultValidatorDir returns OS-specific default validator directory.

View File

@@ -80,6 +80,7 @@ var appFlags = []cli.Flag{
flags.SuggestedFeeRecipientFlag,
flags.ProposerSettingsURLFlag,
flags.ProposerSettingsFlag,
flags.EnableValidatorRegistrationFlag,
////////////////////
cmd.DisableMonitoringFlag,
cmd.MonitoringHostFlag,

View File

@@ -114,6 +114,7 @@ var appHelpFlagGroups = []flagGroup{
flags.ProposerSettingsFlag,
flags.ProposerSettingsURLFlag,
flags.SuggestedFeeRecipientFlag,
flags.EnableValidatorRegistrationFlag,
},
},
{

View File

@@ -61,7 +61,7 @@ type Flags struct {
EnableSlashingProtectionPruning bool
EnableNativeState bool // EnableNativeState defines whether the beacon state will be represented as a pure Go struct or a Go struct that wraps a proto struct.
PullTips bool // Experimental disable of boundary checks
EnablePullTips bool // EnablePullTips enables experimental disabling of boundary checks.
EnableVectorizedHTR bool // EnableVectorizedHTR specifies whether the beacon state will use the optimized sha256 routines.
EnableForkChoiceDoublyLinkedTree bool // EnableForkChoiceDoublyLinkedTree specifies whether fork choice store will use a doubly linked tree.
EnableBatchGossipAggregation bool // EnableBatchGossipAggregation specifies whether to further aggregate our gossip batches before verifying them.
@@ -212,9 +212,9 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logDisabled(disableNativeState)
cfg.EnableNativeState = false
}
if ctx.Bool(pullTips.Name) {
logEnabled(pullTips)
cfg.PullTips = true
if ctx.Bool(enablePullTips.Name) {
logEnabled(enablePullTips)
cfg.EnablePullTips = true
}
if ctx.Bool(enableVecHTR.Name) {
logEnabled(enableVecHTR)

View File

@@ -106,7 +106,7 @@ var (
Usage: "Disables representing the beacon state as a pure Go struct.",
}
pullTips = &cli.BoolFlag{
enablePullTips = &cli.BoolFlag{
Name: "experimental-disable-boundary-checks",
Usage: "Experimental disable of boundary checks, useful for debugging, may cause bad votes.",
}
@@ -169,7 +169,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
enableSlasherFlag,
enableHistoricalSpaceRepresentation,
disableNativeState,
pullTips,
enablePullTips,
enableVecHTR,
enableForkChoiceDoublyLinkedTree,
enableGossipBatchAggregation,

View File

@@ -16,10 +16,16 @@ type ProposerSettingsPayload struct {
// ProposerOptionPayload is the struct representation of the JSON config file set in the validator through the CLI.
// FeeRecipient is set to an eth address in hex string format with 0x prefix.
// GasLimit is a number set to help the network decide on the maximum gas in each block.
type ProposerOptionPayload struct {
FeeRecipient string `json:"fee_recipient" yaml:"fee_recipient"`
GasLimit uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"`
FeeRecipient string `json:"fee_recipient" yaml:"fee_recipient"`
ValidatorRegistration *ValidatorRegistration `json:"validator_registration" yaml:"validator_registration"`
}
// ValidatorRegistration is the struct representation of the JSON config file set in the validator through the CLI.
// GasLimit is a number set to help the network decide on the maximum gas in each block.
type ValidatorRegistration struct {
Enable bool `json:"enable" yaml:"enable"`
GasLimit uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"`
}
// ProposerSettings is a Prysm internal representation of the fee recipient config on the validator client.
@@ -31,14 +37,14 @@ type ProposerSettings struct {
// ProposerOption is a Prysm internal representation of the ProposerOptionPayload on the validator client in bytes format instead of hex.
type ProposerOption struct {
FeeRecipient common.Address
GasLimit uint64
FeeRecipient common.Address
ValidatorRegistration *ValidatorRegistration
}
// DefaultProposerOption returns a Proposer Option with defaults filled
func DefaultProposerOption() ProposerOption {
return ProposerOption{
FeeRecipient: params.BeaconConfig().DefaultFeeRecipient,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
FeeRecipient: params.BeaconConfig().DefaultFeeRecipient,
ValidatorRegistration: nil,
}
}

View File

@@ -14,6 +14,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/consensus-types/wrapper",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
@@ -39,6 +40,8 @@ go_test(
],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
@@ -48,5 +51,6 @@ go_test(
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)

View File

@@ -1,8 +1,12 @@
package wrapper
import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -145,6 +149,115 @@ func BuildSignedBeaconBlock(blk interfaces.BeaconBlock, signature []byte) (inter
}
}
// BuildSignedBeaconBlockFromExecutionPayload takes a signed, blinded beacon block and converts into
// a full, signed beacon block by specifying an execution payload.
func BuildSignedBeaconBlockFromExecutionPayload(
blk interfaces.SignedBeaconBlock, payload *enginev1.ExecutionPayload,
) (interfaces.SignedBeaconBlock, error) {
if err := BeaconBlockIsNil(blk); err != nil {
return nil, err
}
b := blk.Block()
payloadHeader, err := b.Body().ExecutionPayloadHeader()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, errors.Wrap(err, "can only build signed beacon block from blinded format")
case err != nil:
return nil, errors.Wrap(err, "could not get execution payload header")
default:
}
payloadRoot, err := payload.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not hash tree root execution payload")
}
payloadHeaderRoot, err := payloadHeader.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not hash tree root payload header")
}
if payloadRoot != payloadHeaderRoot {
return nil, fmt.Errorf(
"payload %#x and header %#x roots do not match",
payloadRoot,
payloadHeaderRoot,
)
}
syncAgg, err := b.Body().SyncAggregate()
if err != nil {
return nil, errors.Wrap(err, "could not get sync aggregate from block body")
}
bellatrixFullBlock := &eth.SignedBeaconBlockBellatrix{
Block: &eth.BeaconBlockBellatrix{
Slot: b.Slot(),
ProposerIndex: b.ProposerIndex(),
ParentRoot: b.ParentRoot(),
StateRoot: b.StateRoot(),
Body: &eth.BeaconBlockBodyBellatrix{
RandaoReveal: b.Body().RandaoReveal(),
Eth1Data: b.Body().Eth1Data(),
Graffiti: b.Body().Graffiti(),
ProposerSlashings: b.Body().ProposerSlashings(),
AttesterSlashings: b.Body().AttesterSlashings(),
Attestations: b.Body().Attestations(),
Deposits: b.Body().Deposits(),
VoluntaryExits: b.Body().VoluntaryExits(),
SyncAggregate: syncAgg,
ExecutionPayload: payload,
},
},
Signature: blk.Signature(),
}
return wrappedBellatrixSignedBeaconBlock(bellatrixFullBlock)
}
// WrapSignedBlindedBeaconBlock converts a signed beacon block into a blinded format.
func WrapSignedBlindedBeaconBlock(blk interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if err := BeaconBlockIsNil(blk); err != nil {
return nil, err
}
if blk.Block().IsBlinded() {
return blk, nil
}
b := blk.Block()
payload, err := b.Body().ExecutionPayload()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, ErrUnsupportedSignedBeaconBlock
case err != nil:
return nil, errors.Wrap(err, "could not get execution payload")
default:
}
syncAgg, err := b.Body().SyncAggregate()
if err != nil {
return nil, err
}
header, err := bellatrix.PayloadToHeader(payload)
if err != nil {
return nil, err
}
blindedBlock := &eth.SignedBlindedBeaconBlockBellatrix{
Block: &eth.BlindedBeaconBlockBellatrix{
Slot: b.Slot(),
ProposerIndex: b.ProposerIndex(),
ParentRoot: b.ParentRoot(),
StateRoot: b.StateRoot(),
Body: &eth.BlindedBeaconBlockBodyBellatrix{
RandaoReveal: b.Body().RandaoReveal(),
Eth1Data: b.Body().Eth1Data(),
Graffiti: b.Body().Graffiti(),
ProposerSlashings: b.Body().ProposerSlashings(),
AttesterSlashings: b.Body().AttesterSlashings(),
Attestations: b.Body().Attestations(),
Deposits: b.Body().Deposits(),
VoluntaryExits: b.Body().VoluntaryExits(),
SyncAggregate: syncAgg,
ExecutionPayloadHeader: header,
},
},
Signature: blk.Signature(),
}
return wrappedBellatrixSignedBlindedBeaconBlock(blindedBlock)
}
func UnwrapGenericSignedBeaconBlock(gb *eth.GenericSignedBeaconBlock) (interfaces.SignedBeaconBlock, error) {
if gb == nil {
return nil, ErrNilObjectWrapped

View File

@@ -3,11 +3,121 @@ package wrapper_test
import (
"testing"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
)
func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
t.Run("nil block check", func(t *testing.T) {
_, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(nil, nil)
require.ErrorIs(t, wrapper.ErrNilSignedBeaconBlock, err)
})
t.Run("unsupported field payload header", func(t *testing.T) {
altairBlock := util.NewBeaconBlockAltair()
blk, err := wrapper.WrappedSignedBeaconBlock(altairBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, nil)
require.Equal(t, true, errors.Is(err, wrapper.ErrUnsupportedField))
})
t.Run("payload header root and payload root mismatch", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
// Modify the header.
header.GasUsed += 1
blindedBlock.Block.Body.ExecutionPayloadHeader = header
blk, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, payload)
require.ErrorContains(t, "roots do not match", err)
})
t.Run("ok", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
blindedBlock.Block.Body.ExecutionPayloadHeader = header
blk, err := wrapper.WrappedSignedBeaconBlock(blindedBlock)
require.NoError(t, err)
builtBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, payload)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayload()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
})
}
func TestWrapSignedBlindedBeaconBlock(t *testing.T) {
t.Run("nil block check", func(t *testing.T) {
_, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(nil, nil)
require.ErrorIs(t, wrapper.ErrNilSignedBeaconBlock, err)
})
t.Run("unsupported field execution payload", func(t *testing.T) {
altairBlock := util.NewBeaconBlockAltair()
blk, err := wrapper.WrappedSignedBeaconBlock(altairBlock)
require.NoError(t, err)
_, err = wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, nil)
require.Equal(t, true, errors.Is(err, wrapper.ErrUnsupportedField))
})
t.Run("ok", func(t *testing.T) {
payload := &enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, fieldparams.RootLength),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
bellatrixBlk := util.NewBeaconBlockBellatrix()
bellatrixBlk.Block.Body.ExecutionPayload = payload
want, err := bellatrix.PayloadToHeader(payload)
require.NoError(t, err)
blk, err := wrapper.WrappedSignedBeaconBlock(bellatrixBlk)
require.NoError(t, err)
builtBlock, err := wrapper.WrapSignedBlindedBeaconBlock(blk)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayloadHeader()
require.NoError(t, err)
require.DeepEqual(t, want, got)
})
}
func TestWrappedSignedBeaconBlock(t *testing.T) {
tests := []struct {
name string

View File

@@ -46,11 +46,11 @@ go_proto_library(
proto = ":proto",
visibility = ["//visibility:public"],
deps = [
"//proto/eth/ext:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/eth/ext:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@go_googleapis//google/api:annotations_go_proto",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
],
@@ -68,19 +68,22 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/proto/engine/v1",
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/eth/ext:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
"@go_googleapis//google/api:annotations_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@org_golang_google_protobuf//encoding/protojson:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"//encoding/bytesutil:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
], # keep
)
@@ -91,10 +94,12 @@ go_test(
],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//encoding/bytesutil:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
],
)

View File

@@ -80,213 +80,6 @@ func (PayloadStatus_Status) EnumDescriptor() ([]byte, []int) {
return file_proto_engine_v1_execution_engine_proto_rawDescGZIP(), []int{5, 0}
}
type ExecutionBlock struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Number []byte `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
ParentHash []byte `protobuf:"bytes,3,opt,name=parent_hash,json=parentHash,proto3" json:"parent_hash,omitempty"`
Sha3Uncles []byte `protobuf:"bytes,4,opt,name=sha3_uncles,json=sha3Uncles,proto3" json:"sha3_uncles,omitempty"`
Miner []byte `protobuf:"bytes,5,opt,name=miner,proto3" json:"miner,omitempty"`
StateRoot []byte `protobuf:"bytes,6,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"`
TransactionsRoot []byte `protobuf:"bytes,7,opt,name=transactions_root,json=transactionsRoot,proto3" json:"transactions_root,omitempty"`
ReceiptsRoot []byte `protobuf:"bytes,8,opt,name=receipts_root,json=receiptsRoot,proto3" json:"receipts_root,omitempty"`
LogsBloom []byte `protobuf:"bytes,9,opt,name=logs_bloom,json=logsBloom,proto3" json:"logs_bloom,omitempty"`
Difficulty []byte `protobuf:"bytes,10,opt,name=difficulty,proto3" json:"difficulty,omitempty"`
TotalDifficulty string `protobuf:"bytes,11,opt,name=total_difficulty,json=totalDifficulty,proto3" json:"total_difficulty,omitempty"`
GasLimit uint64 `protobuf:"varint,12,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"`
GasUsed uint64 `protobuf:"varint,13,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"`
BaseFeePerGas []byte `protobuf:"bytes,14,opt,name=base_fee_per_gas,json=baseFeePerGas,proto3" json:"base_fee_per_gas,omitempty"`
Size []byte `protobuf:"bytes,15,opt,name=size,proto3" json:"size,omitempty"`
Timestamp uint64 `protobuf:"varint,16,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
ExtraData []byte `protobuf:"bytes,17,opt,name=extra_data,json=extraData,proto3" json:"extra_data,omitempty"`
MixHash []byte `protobuf:"bytes,18,opt,name=mix_hash,json=mixHash,proto3" json:"mix_hash,omitempty"`
Nonce []byte `protobuf:"bytes,19,opt,name=nonce,proto3" json:"nonce,omitempty"`
Transactions [][]byte `protobuf:"bytes,20,rep,name=transactions,proto3" json:"transactions,omitempty"`
Uncles [][]byte `protobuf:"bytes,21,rep,name=uncles,proto3" json:"uncles,omitempty"`
}
func (x *ExecutionBlock) Reset() {
*x = ExecutionBlock{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ExecutionBlock) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExecutionBlock) ProtoMessage() {}
func (x *ExecutionBlock) ProtoReflect() protoreflect.Message {
mi := &file_proto_engine_v1_execution_engine_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExecutionBlock.ProtoReflect.Descriptor instead.
func (*ExecutionBlock) Descriptor() ([]byte, []int) {
return file_proto_engine_v1_execution_engine_proto_rawDescGZIP(), []int{0}
}
func (x *ExecutionBlock) GetNumber() []byte {
if x != nil {
return x.Number
}
return nil
}
func (x *ExecutionBlock) GetHash() []byte {
if x != nil {
return x.Hash
}
return nil
}
func (x *ExecutionBlock) GetParentHash() []byte {
if x != nil {
return x.ParentHash
}
return nil
}
func (x *ExecutionBlock) GetSha3Uncles() []byte {
if x != nil {
return x.Sha3Uncles
}
return nil
}
func (x *ExecutionBlock) GetMiner() []byte {
if x != nil {
return x.Miner
}
return nil
}
func (x *ExecutionBlock) GetStateRoot() []byte {
if x != nil {
return x.StateRoot
}
return nil
}
func (x *ExecutionBlock) GetTransactionsRoot() []byte {
if x != nil {
return x.TransactionsRoot
}
return nil
}
func (x *ExecutionBlock) GetReceiptsRoot() []byte {
if x != nil {
return x.ReceiptsRoot
}
return nil
}
func (x *ExecutionBlock) GetLogsBloom() []byte {
if x != nil {
return x.LogsBloom
}
return nil
}
func (x *ExecutionBlock) GetDifficulty() []byte {
if x != nil {
return x.Difficulty
}
return nil
}
func (x *ExecutionBlock) GetTotalDifficulty() string {
if x != nil {
return x.TotalDifficulty
}
return ""
}
func (x *ExecutionBlock) GetGasLimit() uint64 {
if x != nil {
return x.GasLimit
}
return 0
}
func (x *ExecutionBlock) GetGasUsed() uint64 {
if x != nil {
return x.GasUsed
}
return 0
}
func (x *ExecutionBlock) GetBaseFeePerGas() []byte {
if x != nil {
return x.BaseFeePerGas
}
return nil
}
func (x *ExecutionBlock) GetSize() []byte {
if x != nil {
return x.Size
}
return nil
}
func (x *ExecutionBlock) GetTimestamp() uint64 {
if x != nil {
return x.Timestamp
}
return 0
}
func (x *ExecutionBlock) GetExtraData() []byte {
if x != nil {
return x.ExtraData
}
return nil
}
func (x *ExecutionBlock) GetMixHash() []byte {
if x != nil {
return x.MixHash
}
return nil
}
func (x *ExecutionBlock) GetNonce() []byte {
if x != nil {
return x.Nonce
}
return nil
}
func (x *ExecutionBlock) GetTransactions() [][]byte {
if x != nil {
return x.Transactions
}
return nil
}
func (x *ExecutionBlock) GetUncles() [][]byte {
if x != nil {
return x.Uncles
}
return nil
}
type ExecutionPayload struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1066,18 +859,6 @@ func file_proto_engine_v1_execution_engine_proto_init() {
}
if !protoimpl.UnsafeEnabled {
file_proto_engine_v1_execution_engine_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExecutionBlock); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExecutionPayload); i {
case 0:
return &v.state
@@ -1089,7 +870,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
file_proto_engine_v1_execution_engine_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ExecutionPayloadHeader); i {
case 0:
return &v.state
@@ -1101,7 +882,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
file_proto_engine_v1_execution_engine_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TransitionConfiguration); i {
case 0:
return &v.state
@@ -1113,7 +894,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
file_proto_engine_v1_execution_engine_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PayloadAttributes); i {
case 0:
return &v.state
@@ -1125,7 +906,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
file_proto_engine_v1_execution_engine_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PayloadStatus); i {
case 0:
return &v.state
@@ -1137,7 +918,7 @@ func file_proto_engine_v1_execution_engine_proto_init() {
return nil
}
}
file_proto_engine_v1_execution_engine_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
file_proto_engine_v1_execution_engine_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ForkchoiceState); i {
case 0:
return &v.state

View File

@@ -24,30 +24,6 @@ option java_outer_classname = "ExecutionEngineProto";
option java_package = "org.ethereum.engine.v1";
option php_namespace = "Ethereum\\Engine\\v1";
message ExecutionBlock {
bytes number = 1;
bytes hash = 2;
bytes parent_hash = 3;
bytes sha3_uncles = 4;
bytes miner = 5;
bytes state_root = 6;
bytes transactions_root = 7;
bytes receipts_root = 8;
bytes logs_bloom = 9;
bytes difficulty = 10;
string total_difficulty = 11;
uint64 gas_limit = 12;
uint64 gas_used = 13;
bytes base_fee_per_gas = 14;
bytes size = 15;
uint64 timestamp = 16;
bytes extra_data = 17;
bytes mix_hash = 18;
bytes nonce = 19;
repeated bytes transactions = 20;
repeated bytes uncles = 21;
}
message ExecutionPayload {
bytes parent_hash = 1 [(ethereum.eth.ext.ssz_size) = "32"];
bytes fee_recipient = 2 [(ethereum.eth.ext.ssz_size) = "20"];

View File

@@ -3,8 +3,12 @@ package enginev1
import (
"encoding/json"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
)
@@ -18,6 +22,78 @@ func (b PayloadIDBytes) MarshalJSON() ([]byte, error) {
return json.Marshal(hexutil.Bytes(b[:]))
}
// ExecutionBlock is the response kind received by the eth_getBlockByHash and
// eth_getBlockByNumber endpoints via JSON-RPC.
type ExecutionBlock struct {
gethtypes.Header
Hash common.Hash `json:"hash"`
Transactions []*gethtypes.Transaction `json:"transactions"`
TotalDifficulty string `json:"totalDifficulty"`
}
func (e *ExecutionBlock) MarshalJSON() ([]byte, error) {
decoded := make(map[string]interface{})
encodedHeader, err := e.Header.MarshalJSON()
if err != nil {
return nil, err
}
if err := json.Unmarshal(encodedHeader, &decoded); err != nil {
return nil, err
}
encodedTxs, err := json.Marshal(e.Transactions)
if err != nil {
return nil, err
}
decoded["hash"] = e.Hash.String()
decoded["transactions"] = string(encodedTxs)
decoded["totalDifficulty"] = e.TotalDifficulty
return json.Marshal(decoded)
}
func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
if err := e.Header.UnmarshalJSON(enc); err != nil {
return err
}
decoded := make(map[string]interface{})
if err := json.Unmarshal(enc, &decoded); err != nil {
return err
}
blockHashStr, ok := decoded["hash"].(string)
if !ok {
return errors.New("expected `hash` field in JSON response")
}
e.Hash = common.HexToHash(blockHashStr)
e.TotalDifficulty, ok = decoded["totalDifficulty"].(string)
if !ok {
return errors.New("expected `totalDifficulty` field in JSON response")
}
txsList, ok := decoded["transactions"].([]interface{})
if !ok {
return nil
}
// If the block contains a list of transactions, we JSON unmarshal
// 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 {
return err
}
if err := json.Unmarshal(encodedTx, &t); err != nil {
return err
}
txs[i] = t
}
e.Transactions = txs
return nil
}
// UnmarshalJSON --
func (b *PayloadIDBytes) UnmarshalJSON(enc []byte) error {
hexBytes := hexutil.Bytes(make([]byte, 0))
@@ -30,147 +106,20 @@ func (b *PayloadIDBytes) UnmarshalJSON(enc []byte) error {
return nil
}
type executionBlockJSON struct {
Number string `json:"number"`
Hash hexutil.Bytes `json:"hash"`
ParentHash hexutil.Bytes `json:"parentHash"`
Sha3Uncles hexutil.Bytes `json:"sha3Uncles"`
Miner hexutil.Bytes `json:"miner"`
StateRoot hexutil.Bytes `json:"stateRoot"`
TransactionsRoot hexutil.Bytes `json:"transactionsRoot"`
ReceiptsRoot hexutil.Bytes `json:"receiptsRoot"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
Difficulty string `json:"difficulty"`
TotalDifficulty string `json:"totalDifficulty"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Timestamp hexutil.Uint64 `json:"timestamp"`
BaseFeePerGas string `json:"baseFeePerGas"`
ExtraData hexutil.Bytes `json:"extraData"`
MixHash hexutil.Bytes `json:"mixHash"`
Nonce hexutil.Bytes `json:"nonce"`
Size string `json:"size"`
Transactions []hexutil.Bytes `json:"transactions"`
Uncles []hexutil.Bytes `json:"uncles"`
}
// MarshalJSON defines a custom json.Marshaler interface implementation
// that uses custom json.Marshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlock) MarshalJSON() ([]byte, error) {
transactions := make([]hexutil.Bytes, len(e.Transactions))
for i, tx := range e.Transactions {
transactions[i] = tx
}
uncles := make([]hexutil.Bytes, len(e.Uncles))
for i, ucl := range e.Uncles {
uncles[i] = ucl
}
num := new(big.Int).SetBytes(e.Number)
numHex := hexutil.EncodeBig(num)
diff := new(big.Int).SetBytes(e.Difficulty)
diffHex := hexutil.EncodeBig(diff)
size := new(big.Int).SetBytes(e.Size)
sizeHex := hexutil.EncodeBig(size)
baseFee := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(e.BaseFeePerGas))
baseFeeHex := hexutil.EncodeBig(baseFee)
return json.Marshal(executionBlockJSON{
Number: numHex,
Hash: e.Hash,
ParentHash: e.ParentHash,
Sha3Uncles: e.Sha3Uncles,
Miner: e.Miner,
StateRoot: e.StateRoot,
TransactionsRoot: e.TransactionsRoot,
ReceiptsRoot: e.ReceiptsRoot,
LogsBloom: e.LogsBloom,
Difficulty: diffHex,
TotalDifficulty: e.TotalDifficulty,
GasLimit: hexutil.Uint64(e.GasLimit),
GasUsed: hexutil.Uint64(e.GasUsed),
Timestamp: hexutil.Uint64(e.Timestamp),
ExtraData: e.ExtraData,
MixHash: e.MixHash,
Nonce: e.Nonce,
Size: sizeHex,
BaseFeePerGas: baseFeeHex,
Transactions: transactions,
Uncles: uncles,
})
}
// UnmarshalJSON defines a custom json.Unmarshaler interface implementation
// that uses custom json.Unmarshalers for the hexutil.Bytes and hexutil.Uint64 types.
func (e *ExecutionBlock) UnmarshalJSON(enc []byte) error {
dec := executionBlockJSON{}
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
*e = ExecutionBlock{}
num, err := hexutil.DecodeBig(dec.Number)
if err != nil {
return err
}
e.Number = num.Bytes()
e.Hash = dec.Hash
e.ParentHash = dec.ParentHash
e.Sha3Uncles = dec.Sha3Uncles
e.Miner = dec.Miner
e.StateRoot = dec.StateRoot
e.TransactionsRoot = dec.TransactionsRoot
e.ReceiptsRoot = dec.ReceiptsRoot
e.LogsBloom = dec.LogsBloom
diff, err := hexutil.DecodeBig(dec.Difficulty)
if err != nil {
return err
}
e.Difficulty = diff.Bytes()
e.TotalDifficulty = dec.TotalDifficulty
e.GasLimit = uint64(dec.GasLimit)
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ExtraData = dec.ExtraData
e.MixHash = dec.MixHash
e.Nonce = dec.Nonce
size, err := hexutil.DecodeBig(dec.Size)
if err != nil {
return err
}
e.Size = size.Bytes()
baseFee, err := hexutil.DecodeBig(dec.BaseFeePerGas)
if err != nil {
return err
}
e.BaseFeePerGas = bytesutil.PadTo(bytesutil.ReverseByteOrder(baseFee.Bytes()), fieldparams.RootLength)
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
transactions[i] = tx
}
e.Transactions = transactions
uncles := make([][]byte, len(dec.Uncles))
for i, ucl := range dec.Uncles {
uncles[i] = ucl
}
e.Uncles = uncles
return nil
}
type executionPayloadJSON struct {
ParentHash hexutil.Bytes `json:"parentHash"`
FeeRecipient hexutil.Bytes `json:"feeRecipient"`
StateRoot hexutil.Bytes `json:"stateRoot"`
ReceiptsRoot hexutil.Bytes `json:"receiptsRoot"`
LogsBloom hexutil.Bytes `json:"logsBloom"`
PrevRandao hexutil.Bytes `json:"prevRandao"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ParentHash *common.Hash `json:"parentHash"`
FeeRecipient *common.Address `json:"feeRecipient"`
StateRoot *common.Hash `json:"stateRoot"`
ReceiptsRoot *common.Hash `json:"receiptsRoot"`
LogsBloom *hexutil.Bytes `json:"logsBloom"`
PrevRandao *common.Hash `json:"prevRandao"`
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
GasLimit *hexutil.Uint64 `json:"gasLimit"`
GasUsed *hexutil.Uint64 `json:"gasUsed"`
Timestamp *hexutil.Uint64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"`
BaseFeePerGas string `json:"baseFeePerGas"`
BlockHash hexutil.Bytes `json:"blockHash"`
BlockHash *common.Hash `json:"blockHash"`
Transactions []hexutil.Bytes `json:"transactions"`
}
@@ -182,20 +131,31 @@ func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
}
baseFee := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(e.BaseFeePerGas))
baseFeeHex := hexutil.EncodeBig(baseFee)
pHash := common.BytesToHash(e.ParentHash)
sRoot := common.BytesToHash(e.StateRoot)
recRoot := common.BytesToHash(e.ReceiptsRoot)
prevRan := common.BytesToHash(e.PrevRandao)
bHash := common.BytesToHash(e.BlockHash)
blockNum := hexutil.Uint64(e.BlockNumber)
gasLimit := hexutil.Uint64(e.GasLimit)
gasUsed := hexutil.Uint64(e.GasUsed)
timeStamp := hexutil.Uint64(e.Timestamp)
recipient := common.BytesToAddress(e.FeeRecipient)
logsBloom := hexutil.Bytes(e.LogsBloom)
return json.Marshal(executionPayloadJSON{
ParentHash: e.ParentHash,
FeeRecipient: e.FeeRecipient,
StateRoot: e.StateRoot,
ReceiptsRoot: e.ReceiptsRoot,
LogsBloom: e.LogsBloom,
PrevRandao: e.PrevRandao,
BlockNumber: hexutil.Uint64(e.BlockNumber),
GasLimit: hexutil.Uint64(e.GasLimit),
GasUsed: hexutil.Uint64(e.GasUsed),
Timestamp: hexutil.Uint64(e.Timestamp),
ParentHash: &pHash,
FeeRecipient: &recipient,
StateRoot: &sRoot,
ReceiptsRoot: &recRoot,
LogsBloom: &logsBloom,
PrevRandao: &prevRan,
BlockNumber: &blockNum,
GasLimit: &gasLimit,
GasUsed: &gasUsed,
Timestamp: &timeStamp,
ExtraData: e.ExtraData,
BaseFeePerGas: baseFeeHex,
BlockHash: e.BlockHash,
BlockHash: &bHash,
Transactions: transactions,
})
}
@@ -206,24 +166,65 @@ func (e *ExecutionPayload) UnmarshalJSON(enc []byte) error {
if err := json.Unmarshal(enc, &dec); err != nil {
return err
}
if dec.ParentHash == nil {
return errors.New("missing required field 'parentHash' for ExecutionPayload")
}
if dec.FeeRecipient == nil {
return errors.New("missing required field 'feeRecipient' for ExecutionPayload")
}
if dec.StateRoot == nil {
return errors.New("missing required field 'stateRoot' for ExecutionPayload")
}
if dec.ReceiptsRoot == nil {
return errors.New("missing required field 'receiptsRoot' for ExecutableDataV1")
}
if dec.LogsBloom == nil {
return errors.New("missing required field 'logsBloom' for ExecutionPayload")
}
if dec.PrevRandao == nil {
return errors.New("missing required field 'prevRandao' for ExecutionPayload")
}
if dec.ExtraData == nil {
return errors.New("missing required field 'extraData' for ExecutionPayload")
}
if dec.BlockHash == nil {
return errors.New("missing required field 'blockHash' for ExecutionPayload")
}
if dec.Transactions == nil {
return errors.New("missing required field 'transactions' for ExecutionPayload")
}
if dec.BlockNumber == nil {
return errors.New("missing required field 'blockNumber' for ExecutionPayload")
}
if dec.Timestamp == nil {
return errors.New("missing required field 'timestamp' for ExecutionPayload")
}
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for ExecutionPayload")
}
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for ExecutionPayload")
}
*e = ExecutionPayload{}
e.ParentHash = bytesutil.PadTo(dec.ParentHash, fieldparams.RootLength)
e.FeeRecipient = bytesutil.PadTo(dec.FeeRecipient, fieldparams.FeeRecipientLength)
e.StateRoot = bytesutil.PadTo(dec.StateRoot, fieldparams.RootLength)
e.ReceiptsRoot = bytesutil.PadTo(dec.ReceiptsRoot, fieldparams.RootLength)
e.LogsBloom = bytesutil.PadTo(dec.LogsBloom, fieldparams.LogsBloomLength)
e.PrevRandao = bytesutil.PadTo(dec.PrevRandao, fieldparams.RootLength)
e.BlockNumber = uint64(dec.BlockNumber)
e.GasLimit = uint64(dec.GasLimit)
e.GasUsed = uint64(dec.GasUsed)
e.Timestamp = uint64(dec.Timestamp)
e.ParentHash = dec.ParentHash.Bytes()
e.FeeRecipient = dec.FeeRecipient.Bytes()
e.StateRoot = dec.StateRoot.Bytes()
e.ReceiptsRoot = dec.ReceiptsRoot.Bytes()
e.LogsBloom = *dec.LogsBloom
e.PrevRandao = dec.PrevRandao.Bytes()
e.BlockNumber = uint64(*dec.BlockNumber)
e.GasLimit = uint64(*dec.GasLimit)
e.GasUsed = uint64(*dec.GasUsed)
e.Timestamp = uint64(*dec.Timestamp)
e.ExtraData = dec.ExtraData
baseFee, err := hexutil.DecodeBig(dec.BaseFeePerGas)
if err != nil {
return err
}
e.BaseFeePerGas = bytesutil.PadTo(bytesutil.ReverseByteOrder(baseFee.Bytes()), fieldparams.RootLength)
e.BlockHash = bytesutil.PadTo(dec.BlockHash, fieldparams.RootLength)
e.BlockHash = dec.BlockHash.Bytes()
transactions := make([][]byte, len(dec.Transactions))
for i, tx := range dec.Transactions {
transactions[i] = tx
@@ -261,16 +262,20 @@ func (p *PayloadAttributes) UnmarshalJSON(enc []byte) error {
}
type payloadStatusJSON struct {
LatestValidHash *hexutil.Bytes `json:"latestValidHash"`
Status string `json:"status"`
ValidationError *string `json:"validationError"`
LatestValidHash *common.Hash `json:"latestValidHash"`
Status string `json:"status"`
ValidationError *string `json:"validationError"`
}
// MarshalJSON --
func (p *PayloadStatus) MarshalJSON() ([]byte, error) {
hash := p.LatestValidHash
var latestHash *common.Hash
if p.LatestValidHash != nil {
hash := common.Hash(bytesutil.ToBytes32(p.LatestValidHash))
latestHash = (*common.Hash)(&hash)
}
return json.Marshal(payloadStatusJSON{
LatestValidHash: (*hexutil.Bytes)(&hash),
LatestValidHash: latestHash,
Status: p.Status.String(),
ValidationError: &p.ValidationError,
})
@@ -284,7 +289,7 @@ func (p *PayloadStatus) UnmarshalJSON(enc []byte) error {
}
*p = PayloadStatus{}
if dec.LatestValidHash != nil {
p.LatestValidHash = *dec.LatestValidHash
p.LatestValidHash = dec.LatestValidHash[:]
}
p.Status = PayloadStatus_Status(PayloadStatus_Status_value[dec.Status])
if dec.ValidationError != nil {

View File

@@ -5,7 +5,8 @@ import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -125,61 +126,185 @@ func TestJsonMarshalUnmarshal(t *testing.T) {
})
t.Run("execution block", func(t *testing.T) {
baseFeePerGas := big.NewInt(1770307273)
jsonPayload := &enginev1.ExecutionBlock{
Number: []byte("100"),
Hash: []byte("hash"),
ParentHash: []byte("parent"),
Sha3Uncles: []byte("sha3Uncles"),
Miner: []byte("miner"),
StateRoot: []byte("stateRoot"),
TransactionsRoot: []byte("txRoot"),
ReceiptsRoot: []byte("receiptsRoot"),
LogsBloom: []byte("logsBloom"),
Difficulty: []byte("1"),
TotalDifficulty: "2",
GasLimit: 3,
GasUsed: 4,
Timestamp: 5,
BaseFeePerGas: baseFeePerGas.Bytes(),
Size: []byte("7"),
ExtraData: []byte("extraData"),
MixHash: []byte("mixHash"),
Nonce: []byte("nonce"),
Transactions: [][]byte{[]byte("hi")},
Uncles: [][]byte{[]byte("bye")},
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(jsonPayload)
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"
encodedPayloadItems, err := json.Marshal(payloadItems)
require.NoError(t, err)
payloadPb := &enginev1.ExecutionBlock{}
require.NoError(t, json.Unmarshal(enc, payloadPb))
require.DeepEqual(t, []byte("100"), payloadPb.Number)
require.DeepEqual(t, []byte("hash"), payloadPb.Hash)
require.DeepEqual(t, []byte("parent"), payloadPb.ParentHash)
require.DeepEqual(t, []byte("sha3Uncles"), payloadPb.Sha3Uncles)
require.DeepEqual(t, []byte("miner"), payloadPb.Miner)
require.DeepEqual(t, []byte("stateRoot"), payloadPb.StateRoot)
require.DeepEqual(t, []byte("txRoot"), payloadPb.TransactionsRoot)
require.DeepEqual(t, []byte("receiptsRoot"), payloadPb.ReceiptsRoot)
require.DeepEqual(t, []byte("logsBloom"), payloadPb.LogsBloom)
require.DeepEqual(t, []byte("1"), payloadPb.Difficulty)
require.DeepEqual(t, "2", payloadPb.TotalDifficulty)
require.DeepEqual(t, uint64(3), payloadPb.GasLimit)
require.DeepEqual(t, uint64(4), payloadPb.GasUsed)
require.DeepEqual(t, uint64(5), payloadPb.Timestamp)
require.DeepEqual(t, bytesutil.PadTo(baseFeePerGas.Bytes(), fieldparams.RootLength), payloadPb.BaseFeePerGas)
require.DeepEqual(t, []byte("7"), payloadPb.Size)
require.DeepEqual(t, []byte("extraData"), payloadPb.ExtraData)
require.DeepEqual(t, []byte("mixHash"), payloadPb.MixHash)
require.DeepEqual(t, []byte("nonce"), payloadPb.Nonce)
require.DeepEqual(t, [][]byte{[]byte("hi")}, payloadPb.Transactions)
require.DeepEqual(t, [][]byte{[]byte("bye")}, payloadPb.Uncles)
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)
})
t.Run("nil execution block", func(t *testing.T) {
jsonPayload := (*enginev1.ExecutionBlock)(nil)
enc, err := json.Marshal(jsonPayload)
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.ErrorIs(t, hexutil.ErrEmptyString, json.Unmarshal(enc, payloadPb))
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())
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -434,6 +434,9 @@ message BeaconBlockContainer {
// Representing an bellatrix block.
SignedBeaconBlockBellatrix bellatrix_block = 5;
// Representing a blinded bellatrix block.
SignedBlindedBeaconBlockBellatrix blinded_bellatrix_block = 6;
}
}

View File

@@ -52,6 +52,7 @@ go_test(
"endtoend_setup_test.go",
"endtoend_test.go",
"minimal_e2e_test.go",
"minimal_slashing_e2e_test.go",
"slasher_simulator_e2e_test.go",
],
args = ["-test.v"],

View File

@@ -3,7 +3,6 @@ package endtoend
import (
"testing"
ev "github.com/prysmaticlabs/prysm/testing/endtoend/evaluators"
"github.com/prysmaticlabs/prysm/testing/endtoend/types"
)
@@ -19,21 +18,6 @@ func TestEndToEnd_MinimalConfig_Web3Signer(t *testing.T) {
e2eMinimal(t, types.WithRemoteSigner()).run()
}
func TestEndToEnd_MinimalConfig_Slasher(t *testing.T) {
t.Skip("Skip test until it is fixed.")
e2eMinimal(
t,
types.WithSlasher(
ev.PeersConnect,
ev.HealthzCheck,
ev.ValidatorsSlashedAfterEpoch(4),
ev.SlashedValidatorsLoseBalanceAfterEpoch(4),
ev.InjectDoubleVoteOnEpoch(2),
ev.InjectDoubleBlockOnEpoch(2),
),
).run()
}
func TestEndToEnd_ScenarioRun_EEOffline(t *testing.T) {
t.Skip("TODO(#10242) Prysm is current unable to handle an offline e2e")
runner := e2eMinimal(t)

View File

@@ -0,0 +1,44 @@
package endtoend
import (
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/config/params"
ev "github.com/prysmaticlabs/prysm/testing/endtoend/evaluators"
e2eParams "github.com/prysmaticlabs/prysm/testing/endtoend/params"
"github.com/prysmaticlabs/prysm/testing/endtoend/types"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestEndToEnd_Slasher_MinimalConfig(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.OverrideBeaconConfig(params.E2ETestConfig().Copy())
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
tracingPort := e2eParams.TestParams.Ports.JaegerTracingPort
tracingEndpoint := fmt.Sprintf("127.0.0.1:%d", tracingPort)
testConfig := &types.E2EConfig{
BeaconFlags: []string{
"--slasher",
},
ValidatorFlags: []string{},
EpochsToRun: 4,
TestSync: false,
TestFeature: false,
TestDeposits: false,
Evaluators: []types.Evaluator{
ev.PeersConnect,
ev.HealthzCheck,
ev.ValidatorsSlashedAfterEpoch(4),
ev.SlashedValidatorsLoseBalanceAfterEpoch(4),
ev.InjectDoubleVoteOnEpoch(2),
ev.InjectDoubleBlockOnEpoch(2),
},
EvalInterceptor: defaultInterceptor,
TracingSinkEndpoint: tracingEndpoint,
}
newTestRunner(t, testConfig).run()
}

View File

@@ -23,14 +23,6 @@ func WithRemoteSigner() E2EConfigOpt {
}
}
func WithSlasher(evs ...Evaluator) E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.UseSlasher = true
cfg.Evaluators = evs
cfg.BeaconFlags = append(cfg.BeaconFlags, "--slasher")
}
}
func WithCheckpointSync() E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.TestCheckpointSync = true
@@ -45,7 +37,6 @@ type E2EConfig struct {
UsePrysmShValidator bool
UsePprof bool
UseWeb3RemoteSigner bool
UseSlasher bool
TestDeposits bool
UseFixedPeerIDs bool
UseValidatorCrossClient bool

View File

@@ -40,6 +40,7 @@ go_library(
"//testing/util:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_golang_snappy//:go_default_library",
],
)

View File

@@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
@@ -79,7 +80,11 @@ func (m *engineMock) NewPayload(context.Context, *pb.ExecutionPayload) ([]byte,
return nil, nil
}
func (m *engineMock) LatestExecutionBlock() (*pb.ExecutionBlock, error) {
func (s *engineMock) ExecutionBlockByHashWithTxs(_ context.Context, _ common.Hash) (*pb.ExecutionBlockWithTxs, error) {
return nil, nil
}
func (m *engineMock) LatestExecutionBlock(context.Context) (*pb.ExecutionBlock, error) {
return nil, nil
}
@@ -96,9 +101,11 @@ func (m *engineMock) ExecutionBlockByHash(_ context.Context, hash common.Hash) (
td := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(b.TotalDifficulty))
tdHex := hexutil.EncodeBig(td)
return &pb.ExecutionBlock{
ParentHash: b.ParentHash,
Header: gethtypes.Header{
ParentHash: common.BytesToHash(b.ParentHash),
},
TotalDifficulty: tdHex,
Hash: b.BlockHash,
Hash: common.BytesToHash(b.BlockHash),
}, nil
}

View File

@@ -980,12 +980,14 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
}); 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 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
}
@@ -994,6 +996,7 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
var registerValidatorRequests []*ethpb.ValidatorRegistrationV1
// need to check for pubkey to validator index mappings
for i, key := range pubkeys {
var enableValidatorRegistration bool
skipAppendToFeeRecipientArray := false
feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex)
gasLimit := params.BeaconConfig().DefaultBuilderGasLimit
@@ -1012,14 +1015,25 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
}
if v.ProposerSettings.DefaultConfig != nil {
feeRecipient = v.ProposerSettings.DefaultConfig.FeeRecipient
gasLimit = v.ProposerSettings.DefaultConfig.GasLimit
vr := v.ProposerSettings.DefaultConfig.ValidatorRegistration
if vr != nil && vr.Enable {
gasLimit = vr.GasLimit
enableValidatorRegistration = true
}
}
if v.ProposerSettings.ProposeConfig != nil {
option, ok := v.ProposerSettings.ProposeConfig[key]
if ok && option != nil {
// override the default if a proposeconfig is set
feeRecipient = option.FeeRecipient
gasLimit = option.GasLimit
vr := option.ValidatorRegistration
if vr != nil && vr.Enable {
gasLimit = vr.GasLimit
enableValidatorRegistration = true
} else {
enableValidatorRegistration = false
}
}
}
if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex {
@@ -1031,13 +1045,14 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
FeeRecipient: feeRecipient[:],
})
}
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,
Timestamp: uint64(time.Now().UTC().Unix()),
Pubkey: pubkeys[i][:],
})
if enableValidatorRegistration {
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,
Timestamp: uint64(time.Now().UTC().Unix()),
Pubkey: pubkeys[i][:],
})
}
}
return validatorToFeeRecipients, registerValidatorRequests, nil
}

View File

@@ -358,7 +358,6 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
client := mock2.NewMockBeaconNodeValidatorClient(ctrl)
nodeClient := mock2.NewMockNodeClient(ctrl)
privKey, err := bls.RandKey()
require.NoError(t, err)
pubKey := [fieldparams.BLSPubkeyLength]byte{}
@@ -369,17 +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"),
},
},
validatorClient: client,
keyManager: km,
}
resp := generateMockStatusResponse([][]byte{pubKey[:]})
@@ -395,18 +385,6 @@ func TestWaitMultipleActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
require.NoError(t, v.WaitForActivation(ctx, nil), "Could not wait for activation")
require.LogsContain(t, hook, "Validator activated")
}
@@ -415,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{}
@@ -426,17 +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"),
},
},
validatorClient: client,
keyManager: km,
}
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
@@ -453,16 +421,6 @@ func TestWaitActivation_NotAllValidatorsActivatedOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil), "Could not wait for activation")
}
@@ -1032,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)
@@ -1546,19 +1503,25 @@ func TestValidator_PushProposerSettings(t *testing.T) {
}).Return(nil, nil)
config[keys[0]] = &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
GasLimit: uint64(40000000),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
}
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
GasLimit: uint64(35000000),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(35000000),
},
},
}
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(2).Return(&empty.Empty{}, nil)
).Return(&empty.Empty{}, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
@@ -1577,6 +1540,81 @@ func TestValidator_PushProposerSettings(t *testing.T) {
},
},
},
{
name: " Happy Path default doesn't send 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,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
config := make(map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
ctx, // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().ValidatorIndex(
ctx, // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[1][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 2,
}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 2},
},
}).Return(nil, nil)
config[keys[0]] = &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
}
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: false,
GasLimit: uint64(35000000),
},
},
}
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: "0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9",
2: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(),
GasLimit: uint64(40000000),
},
},
},
{
name: " Happy Path",
validatorSetter: func(t *testing.T) *validator {
@@ -1605,7 +1643,10 @@ func TestValidator_PushProposerSettings(t *testing.T) {
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
client.EXPECT().ValidatorIndex(
@@ -1636,6 +1677,64 @@ func TestValidator_PushProposerSettings(t *testing.T) {
},
},
},
{
name: " Happy Path validator index not found in cache",
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: 1,
Offset: 1,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
},
}
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
ctx, // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
},
{
name: " Skip if no config",
validatorSetter: func(t *testing.T) *validator {
@@ -1655,61 +1754,6 @@ func TestValidator_PushProposerSettings(t *testing.T) {
return &v
},
},
{
name: " Happy Path validator index not found in cache",
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: 1,
Offset: 1,
},
}
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
},
}
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
client.EXPECT().ValidatorIndex(
ctx, // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
},
{
name: " proposer config not nil but fee recipient empty",
validatorSetter: func(t *testing.T) *validator {
@@ -1814,7 +1858,6 @@ func TestValidator_PushProposerSettings(t *testing.T) {
{
name: "register validator batch failed",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
@@ -1839,21 +1882,29 @@ func TestValidator_PushProposerSettings(t *testing.T) {
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
config[keys[0]] = &validatorserviceconfig.ProposerOption{
FeeRecipient: common.Address{},
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
}
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: config,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
},
}
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x0").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),

View File

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

View File

@@ -6,12 +6,9 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/golang/mock/gomock"
"github.com/golang/protobuf/ptypes/empty"
"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"
@@ -68,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{}
@@ -79,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(),
@@ -98,19 +85,9 @@ func TestWaitActivation_StreamSetupFails_AttemptsToReconnect(t *testing.T) {
PublicKeys: [][]byte{pubKey[:]},
},
).Return(clientStream, errors.New("failed stream")).Return(clientStream, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
clientStream.EXPECT().Recv().Return(resp, nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -118,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{}
@@ -129,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(),
@@ -148,11 +115,6 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
PublicKeys: [][]byte{pubKey[:]},
},
).Return(clientStream, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
// A stream fails the first time, but succeeds the second time.
resp := generateMockStatusResponse([][]byte{pubKey[:]})
resp.Statuses[0].Status.Status = ethpb.ValidatorStatus_ACTIVE
@@ -160,11 +122,6 @@ func TestWaitForActivation_ReceiveErrorFromStream_AttemptsReconnection(t *testin
nil,
errors.New("fails"),
).Return(resp, nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -173,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{}
@@ -184,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)
@@ -210,16 +157,6 @@ func TestWaitActivation_LogsActivationEpochOK(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("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")
}
@@ -228,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{}
@@ -239,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)
@@ -265,16 +191,6 @@ func TestWaitForActivation_Exiting(t *testing.T) {
resp,
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
assert.NoError(t, v.WaitForActivation(context.Background(), nil))
}
@@ -289,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{}
@@ -301,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)
@@ -326,16 +231,6 @@ func TestWaitForActivation_RefetchKeys(t *testing.T) {
clientStream.EXPECT().Recv().Return(
resp,
nil)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("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")
@@ -362,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)
@@ -390,12 +274,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
inactiveResp,
nil,
).AnyTimes()
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
activeResp := generateMockStatusResponse([][]byte{inactivePubKey[:], activePubKey[:]})
activeResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
@@ -412,16 +290,10 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(1).Return(&empty.Empty{}, nil)
go func() {
// 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))
}()
@@ -456,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
@@ -485,12 +347,6 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
inactiveResp,
nil,
).AnyTimes()
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
activeResp := generateMockStatusResponse([][]byte{inactivePubKey[:], activePubKey[:]})
activeResp.Statuses[0].Status.Status = ethpb.ValidatorStatus_UNKNOWN_STATUS
@@ -507,22 +363,12 @@ func TestWaitForActivation_AccountsChanged(t *testing.T) {
nil,
)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(1).Return(&empty.Empty{}, nil)
channel := make(chan [][fieldparams.BLSPubkeyLength]byte)
go func() {
// We add the active key into the keymanager and simulate a key refresh.
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{}
}()
@@ -552,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.
@@ -588,17 +423,6 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
PublicKeys: [][]byte{inactiveKey[:], activeKey[:]},
},
).Return(resp, nil /* err */)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(1).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
err := v.waitForActivation(ctx, nil /* accountsChangedChan */)
require.NoError(t, err)
assert.LogsContain(t, hook, "Waiting for deposit to be observed by beacon node")
@@ -630,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)
@@ -678,17 +491,6 @@ func TestWaitForActivation_RemoteKeymanager(t *testing.T) {
},
).Return(resp2, nil /* err */)
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Times(1).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 2},
{FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9").Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
err := v.waitForActivation(ctx, remoteKm.ReloadPublicKeysChan /* accountsChangedChan */)
require.NoError(t, err)
assert.LogsContain(t, hook, "Waiting for deposit to be observed by beacon node")

View File

@@ -486,13 +486,22 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
}
// is overridden by file and URL flags
if cliCtx.IsSet(flags.SuggestedFeeRecipientFlag.Name) {
if cliCtx.IsSet(flags.SuggestedFeeRecipientFlag.Name) &&
!cliCtx.IsSet(flags.ProposerSettingsFlag.Name) &&
!cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
suggestedFee := cliCtx.String(flags.SuggestedFeeRecipientFlag.Name)
var vr *validatorServiceConfig.ValidatorRegistration
if cliCtx.Bool(flags.EnableValidatorRegistrationFlag.Name) {
vr = &validatorServiceConfig.ValidatorRegistration{
Enable: true,
GasLimit: reviewGasLimit(params.BeaconConfig().DefaultBuilderGasLimit),
}
}
fileConfig = &validatorServiceConfig.ProposerSettingsPayload{
ProposerConfig: nil,
DefaultConfig: &validatorServiceConfig.ProposerOptionPayload{
FeeRecipient: suggestedFee,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
FeeRecipient: suggestedFee,
ValidatorRegistration: vr,
},
}
}
@@ -525,7 +534,7 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
// default fileConfig is mandatory
if fileConfig.DefaultConfig == nil {
return nil, errors.New("default fileConfig is required")
return nil, errors.New("default fileConfig is required, proposer settings file is either empty or an incorrect format")
}
if !common.IsHexAddress(fileConfig.DefaultConfig.FeeRecipient) {
return nil, errors.New("default fileConfig fee recipient is not a valid eth1 address")
@@ -534,8 +543,11 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
return nil, err
}
vpSettings.DefaultConfig = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.HexToAddress(fileConfig.DefaultConfig.FeeRecipient),
GasLimit: reviewGasLimit(fileConfig.DefaultConfig.GasLimit),
FeeRecipient: common.HexToAddress(fileConfig.DefaultConfig.FeeRecipient),
ValidatorRegistration: fileConfig.DefaultConfig.ValidatorRegistration,
}
if vpSettings.DefaultConfig.ValidatorRegistration != nil {
vpSettings.DefaultConfig.ValidatorRegistration.GasLimit = reviewGasLimit(vpSettings.DefaultConfig.ValidatorRegistration.GasLimit)
}
if fileConfig.ProposerConfig != nil {
@@ -557,10 +569,14 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
if err := warnNonChecksummedAddress(option.FeeRecipient); err != nil {
return nil, err
}
vpSettings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.HexToAddress(option.FeeRecipient),
GasLimit: reviewGasLimit(option.GasLimit),
if option.ValidatorRegistration != nil {
option.ValidatorRegistration.GasLimit = reviewGasLimit(option.ValidatorRegistration.GasLimit)
}
vpSettings.ProposeConfig[bytesutil.ToBytes48(decodedKey)] = &validatorServiceConfig.ProposerOption{
FeeRecipient: common.HexToAddress(option.FeeRecipient),
ValidatorRegistration: option.ValidatorRegistration,
}
}
}

View File

@@ -205,12 +205,13 @@ func TestProposerSettings(t *testing.T) {
proposerSettingsFlagValues *proposerSettingsFlag
}
tests := []struct {
name string
args args
want func() *validatorserviceconfig.ProposerSettings
urlResponse string
wantErr string
wantLog string
name string
args args
want func() *validatorserviceconfig.ProposerSettings
urlResponse string
wantErr string
wantLog string
validatorRegistrationEnabled bool
}{
{
name: "Happy Path Config file File, bad checksum",
@@ -228,12 +229,10 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -258,16 +257,25 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
bytesutil.ToBytes48(key2): {
FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
},
@@ -289,12 +297,10 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
@@ -316,19 +322,25 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: uint64(40000000),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: uint64(40000000),
},
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: uint64(45000000),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: false,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
},
wantErr: "",
},
{
name: "Happy Path Suggested Fee File",
name: "Happy Path Suggested Fee ",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "",
@@ -341,12 +353,35 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
wantErr: "",
},
{
name: "Happy Path Suggested Fee , validator registration enabled",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "",
url: "",
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
},
},
want: func() *validatorserviceconfig.ProposerSettings {
return &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
},
wantErr: "",
validatorRegistrationEnabled: true,
},
{
name: "Suggested Fee does not Override Config",
args: args{
@@ -363,17 +398,41 @@ func TestProposerSettings(t *testing.T) {
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
}
},
wantErr: "",
},
{
name: "Suggested Fee with validator registration does not Override Config",
args: args{
proposerSettingsFlagValues: &proposerSettingsFlag{
dir: "./testdata/good-prepare-beacon-proposer-config.json",
url: "",
defaultfee: "0x6e35733c5af9B61374A128e6F85f553aF09ff89B",
},
},
want: func() *validatorserviceconfig.ProposerSettings {
key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a")
require.NoError(t, err)
return &validatorserviceconfig.ProposerSettings{
ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*validatorserviceconfig.ProposerOption{
bytesutil.ToBytes48(key1): {
FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"),
},
},
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"),
},
}
},
wantErr: "",
validatorRegistrationEnabled: true,
},
{
name: "No flags set means empty config",
args: args{
@@ -443,6 +502,9 @@ func TestProposerSettings(t *testing.T) {
set.String(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee, "")
require.NoError(t, set.Set(flags.SuggestedFeeRecipientFlag.Name, tt.args.proposerSettingsFlagValues.defaultfee))
}
if tt.validatorRegistrationEnabled {
set.Bool(flags.EnableValidatorRegistrationFlag.Name, true, "")
}
cliCtx := cli.NewContext(&app, set, nil)
got, err := proposerSettings(cliCtx)
if tt.wantErr != "" {

View File

@@ -1,13 +1,25 @@
{
"proposer_config": {
"0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a": {
"fee_recipient": "0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"
"fee_recipient": "0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3",
"validator_registration": {
"enable": true,
"gas_limit": 30000000
}
},
"0xb057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7b": {
"fee_recipient": "0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"
"fee_recipient": "0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4",
"validator_registration": {
"enable": true,
"gas_limit": 30000000
}
}
},
"default_config": {
"fee_recipient": "0x6e35733c5af9B61374A128e6F85f553aF09ff89A"
"fee_recipient": "0x6e35733c5af9B61374A128e6F85f553aF09ff89A",
"validator_registration": {
"enable": true,
"gas_limit": 30000000
}
}
}

View File

@@ -2,7 +2,11 @@
proposer_config:
'0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a':
fee_recipient: '0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3'
gas_limit: 40000000
validator_registration:
enable: true
gas_limit: 40000000
default_config:
fee_recipient: '0x6e35733c5af9B61374A128e6F85f553aF09ff89A'
gas_limit: 45000000
validator_registration:
enable: false
gas_limit: 30000000

View File

@@ -455,6 +455,7 @@ func (s *Server) SetFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.
DefaultConfig: &defaultOption,
}
case s.validatorService.ProposerSettings.ProposeConfig == nil:
pOption.ValidatorRegistration = s.validatorService.ProposerSettings.DefaultConfig.ValidatorRegistration
s.validatorService.ProposerSettings.ProposeConfig = map[[fieldparams.BLSPubkeyLength]byte]*validatorServiceConfig.ProposerOption{
bytesutil.ToBytes48(validatorKey): &pOption,
}
@@ -463,6 +464,7 @@ func (s *Server) SetFeeRecipientByPubkey(ctx context.Context, req *ethpbservice.
if found {
proposerOption.FeeRecipient = common.BytesToAddress(req.Ethaddress)
} else {
pOption.ValidatorRegistration = s.validatorService.ProposerSettings.DefaultConfig.ValidatorRegistration
s.validatorService.ProposerSettings.ProposeConfig[bytesutil.ToBytes48(validatorKey)] = &pOption
}
}