Compare commits

...

55 Commits

Author SHA1 Message Date
prylabs-bulldozer[bot]
a688811847 Merge refs/heads/develop into execution-payload-iface 2022-07-12 22:40:15 +00:00
terencechain
7c30533870 Log error string instead of data (#11038)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-12 22:40:10 +00:00
prylabs-bulldozer[bot]
203a5b4a00 Merge refs/heads/develop into execution-payload-iface 2022-07-12 22:31:43 +00:00
Preston Van Loon
7654ffdcfc e2e: fix scenario test suite (#11039) 2022-07-12 22:31:37 +00:00
prylabs-bulldozer[bot]
9ecf9c8b7e Merge refs/heads/develop into execution-payload-iface 2022-07-12 21:42:51 +00:00
Preston Van Loon
d6031ac386 Add test_suites for better CI runs (#11037) 2022-07-12 16:42:44 -05:00
Raul Jordan
bdb15c885d exec data error 2022-07-12 16:33:02 -04:00
Raul Jordan
64c64f6ea7 Merge branch 'execution-payload-iface' of github.com:prysmaticlabs/prysm into execution-payload-iface 2022-07-12 15:54:16 -04:00
Raul Jordan
5b003198a4 potuz suggestion 2022-07-12 15:54:11 -04:00
Raul Jordan
10170f1c30 Merge branch 'develop' into execution-payload-iface 2022-07-12 19:52:50 +00:00
Raul Jordan
d53f4295c9 builds 2022-07-12 15:52:38 -04:00
Raul Jordan
2d99d58953 Terence feedback 2022-07-12 15:37:51 -04:00
int88
f7d3b5c510 fix some comments (#11017)
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
Co-authored-by: terencechain <terence@prysmaticlabs.com>
2022-07-12 19:06:38 +00:00
Raul Jordan
337e5874b7 Merge branch 'develop' into execution-payload-iface 2022-07-12 18:21:40 +00:00
Raul Jordan
1eb10a076e exec wrap 2022-07-12 14:21:22 -04:00
james-prysm
c0f3946f58 Full Deprecation of Fee Recipient File Flags (#11033) 2022-07-12 18:10:44 +00:00
Raul Jordan
901c11d7bb patch up 2022-07-12 11:49:01 -04:00
Raul Jordan
8f7a847944 no mask 2022-07-12 11:38:41 -04:00
Raul Jordan
8871525b66 build 2022-07-12 11:24:48 -04:00
Raul Jordan
e6f98c88fa el test 2022-07-12 11:24:21 -04:00
Raul Jordan
8a060a78a3 Merge branch 'execution-payload-iface' of github.com:prysmaticlabs/prysm into execution-payload-iface 2022-07-12 10:55:24 -04:00
Raul Jordan
2aebb0ce68 comments 2022-07-12 10:55:20 -04:00
Raul Jordan
e7718be56f Merge branch 'develop' into execution-payload-iface 2022-07-12 14:48:40 +00:00
Raul Jordan
d79d4f7e69 gaz 2022-07-12 10:48:15 -04:00
Raul Jordan
73cbcf9599 fix up builder case 2022-07-12 10:47:05 -04:00
Luca G.F
c33acde64e Fix counters data races in async tests (#11030)
* Fix counters data races in async/debounce tests

Signed-off-by: Luca Georges Francois <luca.georges-francois@epitech.eu>

* Fix counters data races in async/every tests

Signed-off-by: Luca Georges Francois <luca.georges-francois@epitech.eu>
2022-07-12 14:39:18 +00:00
james-prysm
8310d22a05 Validator Registration: use cached signatures if certain properties don't change. (#11014)
* initial commit

* fixing variable rename

* fixing unit test

* adding based on review comments

* renaming cache

* simplifying logic on signed validator registrationRequest

* adding unit tests

* fixing linting

* using wrong dependency
2022-07-12 04:19:49 +00:00
Raul Jordan
1bbaf0dd6b refactor 2022-07-11 23:12:18 -04:00
AH
3060096233 Trivial fix to the warning message about fee recipient config (#11027)
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2022-07-12 03:11:26 +00:00
Raul Jordan
6ec975c56d test 2022-07-11 22:47:33 -04:00
Raul Jordan
6df8a3648b pass 2022-07-11 22:34:17 -04:00
terencechain
5d06c14cec Check validator has registration before getting header (#11023)
* Check validator has registration before calling header

* Check validator has registration before calling header

* Update proposer_bellatrix_test.go
2022-07-11 20:57:58 -05:00
Raul Jordan
adc28dc30a Merge branch 'execution-payload-iface' of github.com:prysmaticlabs/prysm into execution-payload-iface 2022-07-11 20:32:37 -04:00
Raul Jordan
3f230b8c9e merge test 2022-07-11 20:32:32 -04:00
Raul Jordan
c52462c6e9 Merge branch 'develop' into execution-payload-iface 2022-07-11 22:22:44 +00:00
Preston Van Loon
57abf02e34 Enforce a 1s timeout for block builder reply (#11021)
* Enforce a 1s timeout for block builder reply

* Specify BUILDER_PROPOSAL_DELAY_TOLERANCE

* clarify error message

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-11 22:18:42 +00:00
Raul Jordan
214b3bd46f fix 2022-07-11 18:16:35 -04:00
Raul Jordan
e63de86fcd Merge branch 'execution-payload-iface' of github.com:prysmaticlabs/prysm into execution-payload-iface 2022-07-11 18:13:58 -04:00
Raul Jordan
181c7932be txs field 2022-07-11 18:13:54 -04:00
Raul Jordan
c3ef0aad6e Merge branch 'develop' into execution-payload-iface 2022-07-11 22:06:54 +00:00
Raul Jordan
d723646e41 builds 2022-07-11 18:06:35 -04:00
Raul Jordan
38747afd9b build beacon 2022-07-11 17:59:42 -04:00
Raul Jordan
04a54a0435 use iface more 2022-07-11 17:53:25 -04:00
Potuz
44218a9c5b add nilcheck for payload ID (#11024)
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
2022-07-11 21:36:52 +00:00
Raul Jordan
98f055ac75 begin using iface 2022-07-11 17:29:34 -04:00
Raul Jordan
30159fd4b7 exec payload iface 2022-07-11 17:18:21 -04:00
Preston Van Loon
d53f2c7661 builder client: Revert key batching PR #11002 (#11022)
* builder client: Revert key batching PR #11002

* forgot

* gaz
2022-07-11 20:55:08 +00:00
terencechain
5e53d6976e Fix proposer duty RPC to allow next epoch query (#11015)
Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2022-07-11 18:46:42 +00:00
terencechain
7fd2c08be3 Propagate FCU invalid error (#10997)
* Wrap fcu error

* Wrap fcu error

* Wrap error better

* More test

* Add else

* Potuz feedback

* Propagate the correct root for fcu

* Always return true for invalid

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

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

* gaz

* powchain pass

* metrics

* deadcode

* prevent nil block

* build

* add test based on recs
2022-07-08 14:10:33 +00:00
97 changed files with 1860 additions and 756 deletions

View File

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

View File

@@ -12,7 +12,6 @@ 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",
@@ -20,7 +19,6 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
)

View File

@@ -13,15 +13,12 @@ import (
"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"
log "github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
const (
@@ -34,8 +31,6 @@ 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)
@@ -165,7 +160,9 @@ func (c *Client) do(ctx context.Context, method string, path string, body io.Rea
}
defer func() {
closeErr := r.Body.Close()
log.WithError(closeErr).Error("Failed to close response body")
if closeErr != nil {
log.WithError(closeErr).Error("Failed to close response body")
}
}()
if r.StatusCode != http.StatusOK {
err = non200Err(r)
@@ -221,36 +218,26 @@ func (c *Client) GetHeader(ctx context.Context, slot types.Slot, parentHash [32]
func (c *Client) RegisterValidator(ctx context.Context, svr []*ethpb.SignedValidatorRegistrationV1) error {
ctx, span := trace.StartSpan(ctx, "builder.client.RegisterValidator")
defer span.End()
span.AddAttributes(trace.Int64Attribute("num_reqs", int64(len(svr))))
if len(svr) == 0 {
err := errors.Wrap(errMalformedRequest, "empty validator registration list")
tracing.AnnotateError(span, err)
return err
}
eg, ctx := errgroup.WithContext(ctx)
for i := 0; i < len(svr); i += registerValidatorBatchLimit {
end := int(mathprysm.Min(uint64(len(svr)), uint64(i+registerValidatorBatchLimit))) // lint:ignore uintcast -- Request will never exceed int.
vs := make([]*SignedValidatorRegistration, 0, registerValidatorBatchLimit)
for j := i; j < end; j++ {
vs = append(vs, &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[j]})
}
body, err := json.Marshal(vs)
if err != nil {
err := errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
}
eg.Go(func() error {
ctx, span := trace.StartSpan(ctx, "builder.client.RegisterValidator.Go")
defer span.End()
span.AddAttributes(trace.Int64Attribute("reqs", int64(len(vs))))
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
return err
})
vs := make([]*SignedValidatorRegistration, len(svr))
for i := 0; i < len(svr); i++ {
vs[i] = &SignedValidatorRegistration{SignedValidatorRegistrationV1: svr[i]}
}
body, err := json.Marshal(vs)
if err != nil {
err := errors.Wrap(err, "error encoding the SignedValidatorRegistration value body in RegisterValidator")
tracing.AnnotateError(span, err)
return err
}
return eg.Wait()
_, err = c.do(ctx, http.MethodPost, postRegisterValidatorPath, bytes.NewBuffer(body))
return err
}
// SubmitBlindedBlock calls the builder API endpoint that binds the validator to the builder and submits the block.

View File

@@ -3,7 +3,6 @@ package builder
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
@@ -109,52 +108,6 @@ 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

@@ -3,6 +3,7 @@ package async_test
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
@@ -16,7 +17,7 @@ func TestDebounce_NoEvents(t *testing.T) {
eventsChan := make(chan interface{}, 100)
ctx, cancel := context.WithCancel(context.Background())
interval := time.Second
timesHandled := 0
timesHandled := int32(0)
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
@@ -26,21 +27,21 @@ func TestDebounce_NoEvents(t *testing.T) {
}()
go func() {
async.Debounce(ctx, interval, eventsChan, func(event interface{}) {
timesHandled++
atomic.AddInt32(&timesHandled, 1)
})
wg.Done()
}()
if util.WaitTimeout(wg, interval*2) {
t.Fatalf("Test should have exited by now, timed out")
}
assert.Equal(t, 0, timesHandled, "Wrong number of handled calls")
assert.Equal(t, int32(0), atomic.LoadInt32(&timesHandled), "Wrong number of handled calls")
}
func TestDebounce_CtxClosing(t *testing.T) {
eventsChan := make(chan interface{}, 100)
ctx, cancel := context.WithCancel(context.Background())
interval := time.Second
timesHandled := 0
timesHandled := int32(0)
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
@@ -62,23 +63,23 @@ func TestDebounce_CtxClosing(t *testing.T) {
}()
go func() {
async.Debounce(ctx, interval, eventsChan, func(event interface{}) {
timesHandled++
atomic.AddInt32(&timesHandled, 1)
})
wg.Done()
}()
if util.WaitTimeout(wg, interval*2) {
t.Fatalf("Test should have exited by now, timed out")
}
assert.Equal(t, 0, timesHandled, "Wrong number of handled calls")
assert.Equal(t, int32(0), atomic.LoadInt32(&timesHandled), "Wrong number of handled calls")
}
func TestDebounce_SingleHandlerInvocation(t *testing.T) {
eventsChan := make(chan interface{}, 100)
ctx, cancel := context.WithCancel(context.Background())
interval := time.Second
timesHandled := 0
timesHandled := int32(0)
go async.Debounce(ctx, interval, eventsChan, func(event interface{}) {
timesHandled++
atomic.AddInt32(&timesHandled, 1)
})
for i := 0; i < 100; i++ {
eventsChan <- struct{}{}
@@ -86,7 +87,7 @@ func TestDebounce_SingleHandlerInvocation(t *testing.T) {
// We should expect 100 rapid fire changes to only have caused
// 1 handler to trigger after the debouncing period.
time.Sleep(interval * 2)
assert.Equal(t, 1, timesHandled, "Wrong number of handled calls")
assert.Equal(t, int32(1), atomic.LoadInt32(&timesHandled), "Wrong number of handled calls")
cancel()
}
@@ -94,23 +95,23 @@ func TestDebounce_MultipleHandlerInvocation(t *testing.T) {
eventsChan := make(chan interface{}, 100)
ctx, cancel := context.WithCancel(context.Background())
interval := time.Second
timesHandled := 0
timesHandled := int32(0)
go async.Debounce(ctx, interval, eventsChan, func(event interface{}) {
timesHandled++
atomic.AddInt32(&timesHandled, 1)
})
for i := 0; i < 100; i++ {
eventsChan <- struct{}{}
}
require.Equal(t, 0, timesHandled, "Events must prevent from handler execution")
require.Equal(t, int32(0), atomic.LoadInt32(&timesHandled), "Events must prevent from handler execution")
// By this time the first event should be triggered.
time.Sleep(2 * time.Second)
assert.Equal(t, 1, timesHandled, "Wrong number of handled calls")
assert.Equal(t, int32(1), atomic.LoadInt32(&timesHandled), "Wrong number of handled calls")
// Second event.
eventsChan <- struct{}{}
time.Sleep(2 * time.Second)
assert.Equal(t, 2, timesHandled, "Wrong number of handled calls")
assert.Equal(t, int32(2), atomic.LoadInt32(&timesHandled), "Wrong number of handled calls")
cancel()
}

View File

@@ -2,6 +2,7 @@ package async_test
import (
"context"
"sync/atomic"
"testing"
"time"
@@ -11,15 +12,15 @@ import (
func TestEveryRuns(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
i := 0
i := int32(0)
async.RunEvery(ctx, 100*time.Millisecond, func() {
i++
atomic.AddInt32(&i, 1)
})
// Sleep for a bit and ensure the value has increased.
time.Sleep(200 * time.Millisecond)
if i == 0 {
if atomic.LoadInt32(&i) == 0 {
t.Error("Counter failed to increment with ticker")
}
@@ -28,12 +29,12 @@ func TestEveryRuns(t *testing.T) {
// Sleep for a bit to let the cancel take place.
time.Sleep(100 * time.Millisecond)
last := i
last := atomic.LoadInt32(&i)
// Sleep for a bit and ensure the value has not increased.
time.Sleep(200 * time.Millisecond)
if i != last {
if atomic.LoadInt32(&i) != last {
t.Error("Counter incremented after stop")
}
}

View File

@@ -63,7 +63,6 @@ go_library(
"//config/features: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",

View File

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

View File

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

View File

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

View File

@@ -75,10 +75,6 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
newForkchoiceErr error
errString string
}{
{
name: "nil block",
errString: "nil head block",
},
{
name: "phase0 block",
blk: func() interfaces.BeaconBlock {
@@ -192,6 +188,10 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
_, err = service.notifyForkchoiceUpdate(ctx, arg)
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
if tt.errString == ErrInvalidPayload.Error() {
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, tt.headRoot, InvalidBlockRoot(err)) // Head root should be invalid. Not block root!
}
} else {
require.NoError(t, err)
}
@@ -330,7 +330,9 @@ func Test_NotifyForkchoiceUpdateRecursive_Protoarray(t *testing.T) {
headRoot: brg,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
require.ErrorIs(t, ErrInvalidPayload, err)
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, brf, InvalidBlockRoot(err))
// Ensure Head is D
headRoot, err = fcs.Head(ctx, service.justifiedBalances.balances)
require.NoError(t, err)
@@ -473,7 +475,9 @@ func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
headRoot: brg,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
require.ErrorIs(t, ErrInvalidPayload, err)
require.Equal(t, true, IsInvalidBlock(err))
require.Equal(t, brf, InvalidBlockRoot(err))
// Ensure Head is D
headRoot, err = fcs.Head(ctx, service.justifiedBalances.balances)
require.NoError(t, err)

View File

@@ -45,12 +45,16 @@ func logStateTransitionData(b interfaces.BeaconBlock) error {
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
}
if b.Version() == version.Bellatrix {
p, err := b.Body().ExecutionPayload()
p, err := b.Body().Execution()
if err != nil {
return err
}
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)))
log = log.WithField("txCount", len(p.Transactions))
log = log.WithField("payloadHash", fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash())))
txs, err := p.Transactions()
if err != nil {
return err
}
log = log.WithField("txCount", len(txs))
}
log.Info("Finished applying state transition")
return nil
@@ -98,18 +102,18 @@ func logPayload(block interfaces.BeaconBlock) error {
if !isExecutionBlk {
return nil
}
payload, err := block.Body().ExecutionPayload()
payload, err := block.Body().Execution()
if err != nil {
return err
}
if payload.GasLimit == 0 {
if payload.GasLimit() == 0 {
return errors.New("gas limit should not be 0")
}
gasUtilized := float64(payload.GasUsed) / float64(payload.GasLimit)
gasUtilized := float64(payload.GasUsed()) / float64(payload.GasLimit())
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash)),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.ParentHash)),
"blockHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.BlockHash())),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(payload.ParentHash())),
"blockNumber": payload.BlockNumber,
"gasUtilized": fmt.Sprintf("%.2f", gasUtilized),
}).Debug("Synced new payload")

View File

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

View File

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

View File

@@ -17,7 +17,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/config/features"
"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"
@@ -97,7 +96,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
defer span.End()
if err := wrapper.BeaconBlockIsNil(signed); err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
b := signed.Block()
@@ -118,7 +117,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
postState, err := transition.ExecuteStateTransition(ctx, preState, signed)
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
postStateVersion, postStateHeader, err := getStateVersionAndPayload(postState)
if err != nil {
@@ -126,7 +125,7 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
}
isValidPayload, err := s.notifyNewPayload(ctx, postStateVersion, postStateHeader, signed)
if err != nil {
return fmt.Errorf("could not verify new payload: %v", err)
return errors.Wrap(err, "could not validate new payload")
}
if isValidPayload {
if err := s.validateMergeTransitionBlock(ctx, preStateVersion, preStateHeader, signed); err != nil {
@@ -184,7 +183,9 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
if err != nil {
log.WithError(err).Warn("Could not update head")
}
s.notifyEngineIfChangedHead(ctx, headRoot)
if err := s.notifyEngineIfChangedHead(ctx, headRoot); err != nil {
return err
}
if err := s.pruneCanonicalAttsFromPool(ctx, blockRoot, signed); err != nil {
return err
@@ -293,7 +294,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
}
if err := wrapper.BeaconBlockIsNil(blks[0]); err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
b := blks[0].Block()
@@ -341,7 +342,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
set, preState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, preState, b)
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
// Save potential boundary states.
if slots.IsEpochStart(preState.Slot()) {
@@ -362,7 +363,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
}
verify, err := sigSet.Verify()
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
if !verify {
return errors.New("batch block signature verification failed")
@@ -585,11 +586,15 @@ func (s *Service) validateMergeTransitionBlock(ctx context.Context, stateVersion
}
// Skip validation if block has an empty payload.
payload, err := blk.Block().Body().ExecutionPayload()
payload, err := blk.Block().Body().Execution()
if err != nil {
return invalidBlock{err}
return invalidBlock{error: err}
}
if bellatrix.IsEmptyPayload(payload) {
isEmpty, err := wrapper.IsEmptyExecutionData(payload)
if err != nil {
return err
}
if isEmpty {
return nil
}

View File

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

View File

@@ -1489,9 +1489,11 @@ func Test_getStateVersionAndPayload(t *testing.T) {
name: "bellatrix state",
st: func() state.BeaconState {
s, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, s.SetLatestExecutionPayloadHeader(&enginev1.ExecutionPayloadHeader{
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(&enginev1.ExecutionPayloadHeader{
BlockNumber: 1,
}))
})
require.NoError(t, err)
require.NoError(t, s.SetLatestExecutionPayloadHeader(wrappedHeader))
return s
}(),
version: version.Bellatrix,
@@ -1543,6 +1545,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state older than Bellatrix, nil payload",
stateVersion: 1,
payload: nil,
errString: "attempted to wrap nil",
},
{
name: "state older than Bellatrix, empty payload",
@@ -1569,6 +1572,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
name: "state is Bellatrix, nil payload",
stateVersion: 2,
payload: nil,
errString: "attempted to wrap nil",
},
{
name: "state is Bellatrix, empty payload",

View File

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

View File

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

View File

@@ -31,7 +31,6 @@ go_library(
"//beacon-chain/state: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",
@@ -89,7 +88,6 @@ go_test(
"//beacon-chain/state/v1:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",

View File

@@ -7,7 +7,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
@@ -39,7 +38,15 @@ func IsMergeTransitionComplete(st state.BeaconState) (bool, error) {
if err != nil {
return false, err
}
return !bellatrix.IsEmptyHeader(h), nil
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(h)
if err != nil {
return false, err
}
isEmpty, err := wrapper.IsEmptyExecutionData(wrappedHeader)
if err != nil {
return false, err
}
return !isEmpty, nil
}
// IsMergeTransitionBlockUsingPreStatePayloadHeader returns true if the input block is the terminal merge block.
@@ -49,7 +56,15 @@ func IsMergeTransitionBlockUsingPreStatePayloadHeader(h *enginev1.ExecutionPaylo
if h == nil || body == nil {
return false, errors.New("nil header or block body")
}
if !bellatrix.IsEmptyHeader(h) {
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(h)
if err != nil {
return false, err
}
isEmpty, err := wrapper.IsEmptyExecutionData(wrappedHeader)
if err != nil {
return false, err
}
if !isEmpty {
return false, nil
}
return IsExecutionBlock(body)
@@ -64,7 +79,7 @@ func IsExecutionBlock(body interfaces.BeaconBlockBody) (bool, error) {
if body == nil {
return false, errors.New("nil block body")
}
payload, err := body.ExecutionPayload()
payload, err := body.Execution()
switch {
case errors.Is(err, wrapper.ErrUnsupportedField):
return false, nil
@@ -72,7 +87,11 @@ func IsExecutionBlock(body interfaces.BeaconBlockBody) (bool, error) {
return false, err
default:
}
return !bellatrix.IsEmptyPayload(payload), nil
isEmpty, err := wrapper.IsEmptyExecutionData(payload)
if err != nil {
return false, err
}
return !isEmpty, nil
}
// IsExecutionEnabled returns true if the beacon chain can begin executing.
@@ -98,7 +117,15 @@ func IsExecutionEnabled(st state.BeaconState, body interfaces.BeaconBlockBody) (
// IsExecutionEnabledUsingHeader returns true if the execution is enabled using post processed payload header and block body.
// This is an optimized version of IsExecutionEnabled where beacon state is not required as an argument.
func IsExecutionEnabledUsingHeader(header *enginev1.ExecutionPayloadHeader, body interfaces.BeaconBlockBody) (bool, error) {
if !bellatrix.IsEmptyHeader(header) {
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(header)
if err != nil {
return false, err
}
isEmpty, err := wrapper.IsEmptyExecutionData(wrappedHeader)
if err != nil {
return false, err
}
if !isEmpty {
return true, nil
}
return IsExecutionBlock(body)
@@ -116,7 +143,7 @@ func IsPreBellatrixVersion(v int) bool {
// # Verify consistency of the parent hash with respect to the previous execution payload header
// if is_merge_complete(state):
// assert payload.parent_hash == state.latest_execution_payload_header.block_hash
func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload *enginev1.ExecutionPayload) error {
func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload interfaces.ExecutionData) error {
complete, err := IsMergeTransitionComplete(st)
if err != nil {
return err
@@ -129,7 +156,7 @@ func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload *enginev1.E
if err != nil {
return err
}
if !bytes.Equal(payload.ParentHash, header.BlockHash) {
if !bytes.Equal(payload.ParentHash(), header.BlockHash) {
return errors.New("incorrect block hash")
}
return nil
@@ -143,20 +170,20 @@ func ValidatePayloadWhenMergeCompletes(st state.BeaconState, payload *enginev1.E
// assert payload.random == get_randao_mix(state, get_current_epoch(state))
// # Verify timestamp
// assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
func ValidatePayload(st state.BeaconState, payload *enginev1.ExecutionPayload) error {
func ValidatePayload(st state.BeaconState, payload interfaces.ExecutionData) error {
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
return err
}
if !bytes.Equal(payload.PrevRandao, random) {
if !bytes.Equal(payload.PrevRandao(), random) {
return ErrInvalidPayloadPrevRandao
}
t, err := slots.ToTime(st.GenesisTime(), st.Slot())
if err != nil {
return err
}
if payload.Timestamp != uint64(t.Unix()) {
if payload.Timestamp() != uint64(t.Unix()) {
return ErrInvalidPayloadTimeStamp
}
return nil
@@ -194,27 +221,29 @@ func ValidatePayload(st state.BeaconState, payload *enginev1.ExecutionPayload) e
// block_hash=payload.block_hash,
// transactions_root=hash_tree_root(payload.transactions),
// )
func ProcessPayload(st state.BeaconState, payload *enginev1.ExecutionPayload) (state.BeaconState, error) {
func ProcessPayload(st state.BeaconState, payload interfaces.ExecutionData) (state.BeaconState, error) {
if err := ValidatePayloadWhenMergeCompletes(st, payload); err != nil {
return nil, err
}
if err := ValidatePayload(st, payload); err != nil {
return nil, err
}
header, err := bellatrix.PayloadToHeader(payload)
header, err := wrapper.PayloadToHeader(payload)
if err != nil {
return nil, err
}
if err := st.SetLatestExecutionPayloadHeader(header); err != nil {
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(header)
if err != nil {
return nil, err
}
if err := st.SetLatestExecutionPayloadHeader(wrappedHeader); err != nil {
return nil, err
}
return st, nil
}
// ValidatePayloadHeaderWhenMergeCompletes validates the payload header when the merge completes.
func ValidatePayloadHeaderWhenMergeCompletes(st state.BeaconState, header *enginev1.ExecutionPayloadHeader) error {
func ValidatePayloadHeaderWhenMergeCompletes(st state.BeaconState, header interfaces.ExecutionData) error {
// Skip validation if the state is not merge compatible.
complete, err := IsMergeTransitionComplete(st)
if err != nil {
@@ -228,20 +257,20 @@ func ValidatePayloadHeaderWhenMergeCompletes(st state.BeaconState, header *engin
if err != nil {
return err
}
if !bytes.Equal(header.ParentHash, h.BlockHash) {
if !bytes.Equal(header.ParentHash(), h.BlockHash) {
return ErrInvalidPayloadBlockHash
}
return nil
}
// ValidatePayloadHeader validates the payload header.
func ValidatePayloadHeader(st state.BeaconState, header *enginev1.ExecutionPayloadHeader) error {
func ValidatePayloadHeader(st state.BeaconState, header interfaces.ExecutionData) error {
// Validate header's random mix matches with state in current epoch
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
return err
}
if !bytes.Equal(header.PrevRandao, random) {
if !bytes.Equal(header.PrevRandao(), random) {
return ErrInvalidPayloadPrevRandao
}
@@ -250,22 +279,20 @@ func ValidatePayloadHeader(st state.BeaconState, header *enginev1.ExecutionPaylo
if err != nil {
return err
}
if header.Timestamp != uint64(t.Unix()) {
if header.Timestamp() != uint64(t.Unix()) {
return ErrInvalidPayloadTimeStamp
}
return nil
}
// ProcessPayloadHeader processes the payload header.
func ProcessPayloadHeader(st state.BeaconState, header *enginev1.ExecutionPayloadHeader) (state.BeaconState, error) {
func ProcessPayloadHeader(st state.BeaconState, header interfaces.ExecutionData) (state.BeaconState, error) {
if err := ValidatePayloadHeaderWhenMergeCompletes(st, header); err != nil {
return nil, err
}
if err := ValidatePayloadHeader(st, header); err != nil {
return nil, err
}
if err := st.SetLatestExecutionPayloadHeader(header); err != nil {
return nil, err
}
@@ -278,9 +305,9 @@ func GetBlockPayloadHash(blk interfaces.BeaconBlock) ([32]byte, error) {
if IsPreBellatrixVersion(blk.Version()) {
return payloadHash, nil
}
payload, err := blk.Body().ExecutionPayload()
payload, err := blk.Body().Execution()
if err != nil {
return payloadHash, err
}
return bytesutil.ToBytes32(payload.BlockHash), nil
return bytesutil.ToBytes32(payload.BlockHash()), nil
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
@@ -160,7 +159,9 @@ func Test_IsMergeComplete(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, st.SetLatestExecutionPayloadHeader(tt.payload))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.payload)
require.NoError(t, err)
require.NoError(t, st.SetLatestExecutionPayloadHeader(wrappedHeader))
got, err := blocks.IsMergeTransitionComplete(st)
require.NoError(t, err)
if got != tt.want {
@@ -442,7 +443,9 @@ func Test_IsExecutionEnabled(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, st.SetLatestExecutionPayloadHeader(tt.header))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.header)
require.NoError(t, err)
require.NoError(t, st.SetLatestExecutionPayloadHeader(wrappedHeader))
blk := util.NewBeaconBlockBellatrix()
blk.Block.Body.ExecutionPayload = tt.payload
body, err := wrapper.WrappedBeaconBlockBody(blk.Block.Body)
@@ -567,8 +570,12 @@ func Test_ValidatePayloadWhenMergeCompletes(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, st.SetLatestExecutionPayloadHeader(tt.header))
err := blocks.ValidatePayloadWhenMergeCompletes(st, tt.payload)
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.header)
require.NoError(t, err)
require.NoError(t, st.SetLatestExecutionPayloadHeader(wrappedHeader))
wrappedPayload, err := wrapper.WrappedExecutionPayload(tt.payload)
require.NoError(t, err)
err = blocks.ValidatePayloadWhenMergeCompletes(st, wrappedPayload)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
@@ -616,7 +623,9 @@ func Test_ValidatePayload(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := blocks.ValidatePayload(st, tt.payload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(tt.payload)
require.NoError(t, err)
err = blocks.ValidatePayload(st, wrappedPayload)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
@@ -664,12 +673,14 @@ func Test_ProcessPayload(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, err := blocks.ProcessPayload(st, tt.payload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(tt.payload)
require.NoError(t, err)
st, err := blocks.ProcessPayload(st, wrappedPayload)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
require.Equal(t, tt.err, err)
want, err := bellatrix.PayloadToHeader(tt.payload)
want, err := wrapper.PayloadToHeader(wrappedPayload)
require.Equal(t, tt.err, err)
got, err := st.LatestExecutionPayloadHeader()
require.NoError(t, err)
@@ -717,7 +728,9 @@ func Test_ProcessPayloadHeader(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st, err := blocks.ProcessPayloadHeader(st, tt.header)
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.header)
require.NoError(t, err)
st, err := blocks.ProcessPayloadHeader(st, wrappedHeader)
if err != nil {
require.Equal(t, tt.err.Error(), err.Error())
} else {
@@ -768,7 +781,9 @@ func Test_ValidatePayloadHeader(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := blocks.ValidatePayloadHeader(st, tt.header)
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.header)
require.NoError(t, err)
err = blocks.ValidatePayloadHeader(st, wrappedHeader)
require.Equal(t, tt.err, err)
})
}
@@ -777,7 +792,9 @@ func Test_ValidatePayloadHeader(t *testing.T) {
func Test_ValidatePayloadHeaderWhenMergeCompletes(t *testing.T) {
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
emptySt := st.Copy()
require.NoError(t, st.SetLatestExecutionPayloadHeader(&enginev1.ExecutionPayloadHeader{BlockHash: []byte{'a'}}))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(&enginev1.ExecutionPayloadHeader{BlockHash: []byte{'a'}})
require.NoError(t, err)
require.NoError(t, st.SetLatestExecutionPayloadHeader(wrappedHeader))
tests := []struct {
name string
state state.BeaconState
@@ -816,7 +833,9 @@ func Test_ValidatePayloadHeaderWhenMergeCompletes(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := blocks.ValidatePayloadHeaderWhenMergeCompletes(tt.state, tt.header)
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(tt.header)
require.NoError(t, err)
err = blocks.ValidatePayloadHeaderWhenMergeCompletes(tt.state, wrappedHeader)
require.Equal(t, tt.err, err)
})
}
@@ -824,7 +843,9 @@ func Test_ValidatePayloadHeaderWhenMergeCompletes(t *testing.T) {
func Test_PayloadToHeader(t *testing.T) {
p := emptyPayload()
h, err := bellatrix.PayloadToHeader(p)
wrappedPayload, err := wrapper.WrappedExecutionPayload(p)
require.NoError(t, err)
h, err := wrapper.PayloadToHeader(wrappedPayload)
require.NoError(t, err)
txRoot, err := ssz.TransactionsRoot(p.Transactions)
require.NoError(t, err)
@@ -863,7 +884,9 @@ func Test_PayloadToHeader(t *testing.T) {
func BenchmarkBellatrixComplete(b *testing.B) {
st, _ := util.DeterministicGenesisStateBellatrix(b, 1)
require.NoError(b, st.SetLatestExecutionPayloadHeader(emptyPayloadHeader()))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(emptyPayloadHeader())
require.NoError(b, err)
require.NoError(b, st.SetLatestExecutionPayloadHeader(wrappedHeader))
b.ResetTimer()
for i := 0; i < b.N; i++ {

View File

@@ -290,24 +290,17 @@ func ProcessBlockForStateRoot(
return nil, errors.Wrap(err, "could not check if execution is enabled")
}
if enabled {
executionData, err := blk.Body().Execution()
if err != nil {
return nil, err
}
if blk.IsBlinded() {
header, err := blk.Body().ExecutionPayloadHeader()
if err != nil {
return nil, err
}
state, err = b.ProcessPayloadHeader(state, header)
if err != nil {
return nil, errors.Wrap(err, "could not process execution payload header")
}
state, err = b.ProcessPayloadHeader(state, executionData)
} else {
payload, err := blk.Body().ExecutionPayload()
if err != nil {
return nil, err
}
state, err = b.ProcessPayload(state, payload)
if err != nil {
return nil, errors.Wrap(err, "could not process execution payload")
}
state, err = b.ProcessPayload(state, executionData)
}
if err != nil {
return nil, errors.Wrap(err, "could not process execution data")
}
}

View File

@@ -53,6 +53,7 @@ type ReadOnlyDatabase interface {
PowchainData(ctx context.Context) (*ethpb.ETH1ChainData, error)
// Fee reicipients operations.
FeeRecipientByValidatorID(ctx context.Context, id types.ValidatorIndex) (common.Address, error)
RegistrationByValidatorID(ctx context.Context, id types.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error)
// origin checkpoint sync support
OriginCheckpointBlockRoot(ctx context.Context) ([32]byte, error)
BackfillBlockRoot(ctx context.Context) ([32]byte, error)

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",

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,10 +48,19 @@ 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 {
NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error)
NewPayload(ctx context.Context, payload interfaces.ExecutionData) ([]byte, error)
ForkchoiceUpdated(
ctx context.Context, state *pb.ForkchoiceState, attrs *pb.PayloadAttributes,
) (*pb.PayloadIDBytes, []byte, error)
@@ -62,7 +73,7 @@ type EngineCaller interface {
}
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
func (s *Service) NewPayload(ctx context.Context, payload *pb.ExecutionPayload) ([]byte, error) {
func (s *Service) NewPayload(ctx context.Context, payload interfaces.ExecutionData) ([]byte, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.NewPayload")
defer span.End()
start := time.Now()
@@ -73,7 +84,11 @@ func (s *Service) NewPayload(ctx context.Context, payload *pb.ExecutionPayload)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
result := &pb.PayloadStatus{}
err := s.rpcClient.CallContext(ctx, result, NewPayloadMethod, payload)
payloadPb, ok := payload.Proto().(*pb.ExecutionPayload)
if !ok {
return nil, errors.New("execution data must be an execution payload")
}
err := s.rpcClient.CallContext(ctx, result, NewPayloadMethod, payloadPb)
if err != nil {
return nil, handleRPCError(err)
}
@@ -290,6 +305,80 @@ func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash) (*
return result, handleRPCError(err)
}
// ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs
// a beacon block with a full execution payload via the engine API.
func (s *Service) ReconstructFullBellatrixBlock(
ctx context.Context, blindedBlock interfaces.SignedBeaconBlock,
) (interfaces.SignedBeaconBlock, error) {
if err := wrapper.BeaconBlockIsNil(blindedBlock); err != nil {
return nil, errors.Wrap(err, "cannot reconstruct bellatrix block from nil data")
}
if !blindedBlock.Block().IsBlinded() {
return nil, errors.New("can only reconstruct block from blinded block format")
}
header, err := blindedBlock.Block().Body().Execution()
if err != nil {
return nil, err
}
executionBlockHash := common.BytesToHash(header.BlockHash())
executionBlock, err := s.ExecutionBlockByHash(ctx, executionBlockHash)
if err != nil {
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
}
if executionBlock == nil {
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionBlockHash)
}
payload, err := fullPayloadFromExecutionBlock(header, executionBlock)
if err != nil {
return nil, err
}
fullBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blindedBlock, payload)
if err != nil {
return nil, err
}
reconstructedExecutionPayloadCount.Add(1)
return fullBlock, nil
}
func fullPayloadFromExecutionBlock(
header interfaces.ExecutionData, block *pb.ExecutionBlock,
) (*pb.ExecutionPayload, error) {
if header.IsNil() || 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 {
@@ -332,7 +421,7 @@ func handleRPCError(err error) error {
if !ok {
return errors.Wrapf(err, "got an unexpected error in JSON-RPC response")
}
return errors.Wrapf(ErrServer, "%v", errWithData.ErrorData())
return errors.Wrapf(ErrServer, "%v", errWithData.Error())
default:
return err
}

View File

@@ -12,6 +12,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
@@ -19,13 +20,16 @@ import (
mocks "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/prysmaticlabs/prysm/testing/util"
"google.golang.org/protobuf/proto"
)
var (
_ = ExecutionPayloadReconstructor(&Service{})
_ = EngineCaller(&Service{})
_ = EngineCaller(&mocks.EngineClient{})
)
@@ -61,7 +65,9 @@ func TestClient_IPC(t *testing.T) {
require.Equal(t, true, ok)
req, ok := fix["ExecutionPayload"].(*pb.ExecutionPayload)
require.Equal(t, true, ok)
latestValidHash, err := srv.NewPayload(ctx, req)
wrappedPayload, err := wrapper.WrappedExecutionPayload(req)
require.NoError(t, err)
latestValidHash, err := srv.NewPayload(ctx, wrappedPayload)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.ToBytes32(want.LatestValidHash), bytesutil.ToBytes32(latestValidHash))
})
@@ -226,7 +232,9 @@ func TestClient_HTTP(t *testing.T) {
client := newPayloadSetup(t, want, execPayload)
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload)
require.NoError(t, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
@@ -238,7 +246,9 @@ func TestClient_HTTP(t *testing.T) {
client := newPayloadSetup(t, want, execPayload)
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload)
require.ErrorIs(t, ErrAcceptedSyncingPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
@@ -250,7 +260,9 @@ func TestClient_HTTP(t *testing.T) {
client := newPayloadSetup(t, want, execPayload)
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload)
require.ErrorIs(t, ErrInvalidBlockHashPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
@@ -262,7 +274,9 @@ func TestClient_HTTP(t *testing.T) {
client := newPayloadSetup(t, want, execPayload)
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload)
require.ErrorIs(t, ErrInvalidPayloadStatus, err)
require.DeepEqual(t, want.LatestValidHash, resp)
})
@@ -274,7 +288,9 @@ func TestClient_HTTP(t *testing.T) {
client := newPayloadSetup(t, want, execPayload)
// We call the RPC method via HTTP and expect a proper result.
resp, err := client.NewPayload(ctx, execPayload)
wrappedPayload, err := wrapper.WrappedExecutionPayload(execPayload)
require.NoError(t, err)
resp, err := client.NewPayload(ctx, wrappedPayload)
require.ErrorIs(t, ErrUnknownPayloadStatus, err)
require.DeepEqual(t, []uint8(nil), resp)
})
@@ -386,6 +402,101 @@ 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
wrappedPayload, err := wrapper.WrappedExecutionPayload(payload)
require.NoError(t, err)
header, err := wrapper.PayloadToHeader(wrappedPayload)
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().Execution()
require.NoError(t, err)
require.DeepEqual(t, payload, got.Proto())
})
}
func TestServer_getPowBlockHashAtTerminalTotalDifficulty(t *testing.T) {
tests := []struct {
name string
@@ -873,6 +984,57 @@ 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: "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) {
wrapped, err := wrapper.WrappedExecutionPayloadHeader(tt.args.header)
got, err := fullPayloadFromExecutionBlock(wrapped, tt.args.block)
if (err != nil) && !strings.Contains(err.Error(), tt.err) {
t.Fatalf("Wanted err %s got %v", tt.err, err)
}
require.DeepEqual(t, tt.want, got)
})
}
}
type testEngineService struct{}
func (*testEngineService) NoArgsRets() {}

View File

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

View File

@@ -18,6 +18,7 @@ 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",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -9,6 +9,7 @@ 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/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
)
@@ -32,7 +33,7 @@ type EngineClient struct {
}
// NewPayload --
func (e *EngineClient) NewPayload(_ context.Context, _ *pb.ExecutionPayload) ([]byte, error) {
func (e *EngineClient) NewPayload(_ context.Context, _ interfaces.ExecutionData) ([]byte, error) {
return e.NewPayloadResp, e.ErrNewPayload
}

View File

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

View File

@@ -133,8 +133,8 @@ func (vs *Server) GetProposerDuties(ctx context.Context, req *ethpbv1.ProposerDu
cs := vs.TimeFetcher.CurrentSlot()
currentEpoch := slots.ToEpoch(cs)
if req.Epoch > currentEpoch {
return nil, status.Errorf(codes.InvalidArgument, "Request epoch %d can not be greater than current epoch %d", req.Epoch, currentEpoch)
if req.Epoch > currentEpoch+1 {
return nil, status.Errorf(codes.InvalidArgument, "Request epoch %d can not be greater than next epoch %d", req.Epoch, currentEpoch+1)
}
s, err := vs.HeadFetcher.HeadState(ctx)
@@ -177,7 +177,7 @@ func (vs *Server) GetProposerDuties(ctx context.Context, req *ethpbv1.ProposerDu
return duties[i].Slot < duties[j].Slot
})
root, err := proposalDependentRoot(s, req.Epoch)
root, err := vs.proposalDependentRoot(ctx, s, req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err)
}
@@ -912,7 +912,7 @@ func attestationDependentRoot(s state.BeaconState, epoch types.Epoch) ([]byte, e
// proposalDependentRoot is get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch) - 1)
// or the genesis block root in the case of underflow.
func proposalDependentRoot(s state.BeaconState, epoch types.Epoch) ([]byte, error) {
func (vs *Server) proposalDependentRoot(ctx context.Context, s state.BeaconState, epoch types.Epoch) ([]byte, error) {
var dependentRootSlot types.Slot
if epoch == 0 {
dependentRootSlot = 0
@@ -923,10 +923,21 @@ func proposalDependentRoot(s state.BeaconState, epoch types.Epoch) ([]byte, erro
}
dependentRootSlot = epochStartSlot.Sub(1)
}
root, err := helpers.BlockRootAtSlot(s, dependentRootSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get block root")
var root []byte
var err error
// Per spec, if the dependent root epoch is greater than current epoch, use the head root.
if dependentRootSlot >= s.Slot() {
root, err = vs.HeadFetcher.HeadRoot(ctx)
if err != nil {
return nil, err
}
} else {
root, err = helpers.BlockRootAtSlot(s, dependentRootSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get block root")
}
}
return root, nil
}

View File

@@ -366,11 +366,11 @@ func TestGetProposerDuties(t *testing.T) {
t.Run("Epoch out of bound", func(t *testing.T) {
currentEpoch := slots.ToEpoch(bs.Slot())
req := &ethpbv1.ProposerDutiesRequest{
Epoch: currentEpoch + 1,
Epoch: currentEpoch + 2,
}
_, err := vs.GetProposerDuties(ctx, req)
require.NotNil(t, err)
assert.ErrorContains(t, fmt.Sprintf("Request epoch %d can not be greater than current epoch %d", currentEpoch+1, currentEpoch), err)
assert.ErrorContains(t, fmt.Sprintf("Request epoch %d can not be greater than next epoch %d", currentEpoch+2, currentEpoch+1), err)
})
t.Run("execution optimistic", func(t *testing.T) {

View File

@@ -3,10 +3,12 @@ package validator
import (
"context"
"fmt"
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition/interop"
"github.com/prysmaticlabs/prysm/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/config/params"
coreBlock "github.com/prysmaticlabs/prysm/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
@@ -19,20 +21,32 @@ import (
"github.com/sirupsen/logrus"
)
// blockBuilderTimeout is the maximum amount of time allowed for a block builder to respond to a
// block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec.
const blockBuilderTimeout = 1 * time.Second
func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.GenericBeaconBlock, error) {
altairBlk, err := vs.buildAltairBeaconBlock(ctx, req)
if err != nil {
return nil, err
}
builderReady, b, err := vs.getAndBuildHeaderBlock(ctx, altairBlk)
if err != nil {
// In the event of an error, the node should fall back to default execution engine for building block.
log.WithError(err).Error("Default back to local execution client")
} else if builderReady {
return b, nil
registered, err := vs.validatorRegistered(ctx, altairBlk.ProposerIndex)
if registered && err == nil {
builderReady, b, err := vs.getAndBuildHeaderBlock(ctx, altairBlk)
if err != nil {
// In the event of an error, the node should fall back to default execution engine for building block.
log.WithError(err).Error("Failed to build a block from external builder, falling " +
"back to local execution client")
} else if builderReady {
return b, nil
}
} else if err != nil {
log.WithFields(logrus.Fields{
"slot": req.Slot,
"validatorIndex": altairBlk.ProposerIndex,
}).Errorf("Could not determine validator has registered. Default to local execution client: %v", err)
}
payload, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex)
if err != nil {
return nil, err
@@ -85,7 +99,7 @@ func (vs *Server) getPayloadHeader(ctx context.Context, slot types.Slot, idx typ
if blocks.IsPreBellatrixVersion(b.Version()) {
return nil, nil
}
h, err := b.Block().Body().ExecutionPayload()
h, err := b.Block().Body().Execution()
if err != nil {
return nil, err
}
@@ -93,7 +107,7 @@ func (vs *Server) getPayloadHeader(ctx context.Context, slot types.Slot, idx typ
if err != nil {
return nil, err
}
bid, err := vs.BlockBuilder.GetHeader(ctx, slot, bytesutil.ToBytes32(h.BlockHash), pk)
bid, err := vs.BlockBuilder.GetHeader(ctx, slot, bytesutil.ToBytes32(h.BlockHash()), pk)
if err != nil {
return nil, err
}
@@ -164,10 +178,14 @@ func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.SignedBe
if err != nil {
return nil, err
}
h, err := b.Block().Body().ExecutionPayloadHeader()
h, err := b.Block().Body().Execution()
if err != nil {
return nil, err
}
header, ok := h.Proto().(*enginev1.ExecutionPayloadHeader)
if !ok {
return nil, errors.New("execution data must be execution payload header")
}
sb := &ethpb.SignedBlindedBeaconBlockBellatrix{
Block: &ethpb.BlindedBeaconBlockBellatrix{
Slot: b.Block().Slot(),
@@ -184,7 +202,7 @@ func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.SignedBe
Deposits: b.Block().Body().Deposits(),
VoluntaryExits: b.Block().Body().VoluntaryExits(),
SyncAggregate: agg,
ExecutionPayloadHeader: h,
ExecutionPayloadHeader: header,
},
},
Signature: b.Signature(),
@@ -221,8 +239,8 @@ func (vs *Server) unblindBuilderBlock(ctx context.Context, b interfaces.SignedBe
}
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", h.BlockHash),
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient),
"blockHash": fmt.Sprintf("%#x", h.BlockHash()),
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient()),
"gasUsed": h.GasUsed,
"slot": b.Block().Slot(),
"txs": len(payload.Transactions),
@@ -251,11 +269,14 @@ func (vs *Server) readyForBuilder(ctx context.Context) (bool, error) {
// Get and builder header block. Returns a boolean status, built block and error.
// If the status is false that means builder the header block is disallowed.
// This routine is time limited by `blockBuilderTimeout`.
func (vs *Server) getAndBuildHeaderBlock(ctx context.Context, b *ethpb.BeaconBlockAltair) (bool, *ethpb.GenericBeaconBlock, error) {
// No op. Builder is not defined. User did not specify a user URL. We should use local EE.
if vs.BlockBuilder == nil || !vs.BlockBuilder.Configured() {
return false, nil, nil
}
ctx, cancel := context.WithTimeout(ctx, blockBuilderTimeout)
defer cancel()
// Does the protocol allow for builder at this current moment. Builder is only allowed post merge after finalization.
ready, err := vs.readyForBuilder(ctx)
if err != nil {
@@ -280,3 +301,18 @@ func (vs *Server) getAndBuildHeaderBlock(ctx context.Context, b *ethpb.BeaconBlo
}
return true, gb, nil
}
// validatorRegistered returns true if validator with index `id` was previously registered in the database.
func (vs *Server) validatorRegistered(ctx context.Context, id types.ValidatorIndex) (bool, error) {
if vs.BeaconDB == nil {
return false, errors.New("nil beacon db")
}
_, err := vs.BeaconDB.RegistrationByValidatorID(ctx, id)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
return false, nil
case err != nil:
return false, err
}
return true, nil
}

View File

@@ -13,6 +13,8 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
prysmtime "github.com/prysmaticlabs/prysm/beacon-chain/core/time"
dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
@@ -24,6 +26,7 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"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"
v1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
@@ -561,6 +564,12 @@ func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
wbr1, err := wb1.Block().HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, wb1))
random, err := helpers.RandaoMix(beaconState, prysmtime.CurrentEpoch(beaconState))
require.NoError(t, err)
tstamp, err := slots.ToTime(beaconState.GenesisTime(), bellatrixSlot+1)
require.NoError(t, err)
h := &v1.ExecutionPayloadHeader{
BlockNumber: 123,
GasLimit: 456,
@@ -570,17 +579,18 @@ func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
PrevRandao: random,
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: make([]byte, fieldparams.RootLength),
ExtraData: make([]byte, 0),
Timestamp: uint64(tstamp.Unix()),
}
proposerServer := &Server{
FinalizationFetcher: &blockchainTest.ChainService{FinalizedCheckPoint: &ethpb.Checkpoint{Root: wbr1[:]}},
HeadFetcher: &blockchainTest.ChainService{State: beaconState, Root: parentRoot[:], Optimistic: false, Block: wb1},
TimeFetcher: &blockchainTest.ChainService{Genesis: time.Now()},
TimeFetcher: &blockchainTest.ChainService{Genesis: time.Unix(int64(beaconState.GenesisTime()), 0)},
SyncChecker: &mockSync.Sync{IsSyncing: false},
BlockReceiver: &blockchainTest.ChainService{},
HeadUpdater: &blockchainTest.ChainService{},
@@ -603,6 +613,8 @@ func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
require.NoError(t, err)
require.NoError(t, proposerServer.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []types.ValidatorIndex{40},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength), Pubkey: bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)}}))
block, err := proposerServer.getBellatrixBeaconBlock(ctx, &ethpb.BlockRequest{
Slot: bellatrixSlot + 1,
RandaoReveal: randaoReveal,
@@ -613,3 +625,29 @@ func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
require.LogsContain(t, hook, "Computed state root")
require.DeepEqual(t, h, bellatrixBlk.BlindedBellatrix.Body.ExecutionPayloadHeader) // Payload header should equal.
}
func TestServer_validatorRegistered(t *testing.T) {
proposerServer := &Server{}
ctx := context.Background()
reg, err := proposerServer.validatorRegistered(ctx, 0)
require.ErrorContains(t, "nil beacon db", err)
require.Equal(t, false, reg)
proposerServer.BeaconDB = dbTest.SetupDB(t)
reg, err = proposerServer.validatorRegistered(ctx, 0)
require.NoError(t, err)
require.Equal(t, false, reg)
f := bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength)
p := bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)
require.NoError(t, proposerServer.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []types.ValidatorIndex{0, 1},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: f, Pubkey: p}, {FeeRecipient: f, Pubkey: p}}))
reg, err = proposerServer.validatorRegistered(ctx, 0)
require.NoError(t, err)
require.Equal(t, true, reg)
reg, err = proposerServer.validatorRegistered(ctx, 1)
require.NoError(t, err)
require.Equal(t, true, reg)
}

View File

@@ -105,11 +105,11 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
switch finalizedBlock.Version() {
case version.Phase0, version.Altair: // Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
default:
finalizedPayload, err := finalizedBlock.Block().Body().ExecutionPayload()
finalizedPayload, err := finalizedBlock.Block().Body().Execution()
if err != nil {
return nil, err
}
finalizedBlockHash = finalizedPayload.BlockHash
finalizedBlockHash = finalizedPayload.BlockHash()
}
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -47,7 +48,9 @@ func TestServer_getExecutionPayload(t *testing.T) {
}))
transitionSt, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, transitionSt.SetLatestExecutionPayloadHeader(&pb.ExecutionPayloadHeader{BlockNumber: 1}))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(&pb.ExecutionPayloadHeader{BlockNumber: 1})
require.NoError(t, err)
require.NoError(t, transitionSt.SetLatestExecutionPayloadHeader(wrappedHeader))
b2pb := util.NewBeaconBlockBellatrix()
b2r, err := b2pb.Block.HashTreeRoot()
require.NoError(t, err)
@@ -144,7 +147,9 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
}))
transitionSt, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, transitionSt.SetLatestExecutionPayloadHeader(&pb.ExecutionPayloadHeader{BlockNumber: 1}))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(&pb.ExecutionPayloadHeader{BlockNumber: 1})
require.NoError(t, err)
require.NoError(t, transitionSt.SetLatestExecutionPayloadHeader(wrappedHeader))
b2pb := util.NewBeaconBlockBellatrix()
b2r, err := b2pb.Block.HashTreeRoot()
require.NoError(t, err)

View File

@@ -11,6 +11,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -8,6 +8,7 @@ import (
"github.com/prysmaticlabs/go-bitfield"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
@@ -83,7 +84,7 @@ type WriteOnlyBeaconState interface {
SetSlashings(val []uint64) error
UpdateSlashingsAtIndex(idx, val uint64) error
AppendHistoricalRoots(root [32]byte) error
SetLatestExecutionPayloadHeader(payload *enginev1.ExecutionPayloadHeader) error
SetLatestExecutionPayloadHeader(payload interfaces.ExecutionData) error
}
// ReadOnlyValidator defines a struct which only has read access to validator methods.

View File

@@ -64,6 +64,7 @@ go_library(
"//beacon-chain/state/types:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",
@@ -113,6 +114,7 @@ go_test(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//container/trie:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",

View File

@@ -10,6 +10,7 @@ import (
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
v3 "github.com/prysmaticlabs/prysm/beacon-chain/state/v3"
"github.com/prysmaticlabs/prysm/config/params"
"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"
@@ -180,7 +181,9 @@ func TestComputeFieldRootsWithHasher_Bellatrix(t *testing.T) {
require.NoError(t, beaconState.SetInactivityScores([]uint64{1, 2, 3}))
require.NoError(t, beaconState.SetCurrentSyncCommittee(syncCommittee("current")))
require.NoError(t, beaconState.SetNextSyncCommittee(syncCommittee("next")))
require.NoError(t, beaconState.SetLatestExecutionPayloadHeader(executionPayloadHeader()))
wrappedHeader, err := wrapper.WrappedExecutionPayloadHeader(executionPayloadHeader())
require.NoError(t, err)
require.NoError(t, beaconState.SetLatestExecutionPayloadHeader(wrappedHeader))
v1State, ok := beaconState.(*v3.BeaconState)
require.Equal(t, true, ok)

View File

@@ -1,22 +1,27 @@
package state_native
import (
"github.com/pkg/errors"
nativetypes "github.com/prysmaticlabs/prysm/beacon-chain/state/state-native/types"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
_ "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/runtime/version"
)
// SetLatestExecutionPayloadHeader for the beacon state.
func (b *BeaconState) SetLatestExecutionPayloadHeader(val *enginev1.ExecutionPayloadHeader) error {
func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionData) error {
b.lock.Lock()
defer b.lock.Unlock()
if b.version == version.Phase0 || b.version == version.Altair {
return errNotSupported("SetLatestExecutionPayloadHeader", b.version)
}
b.latestExecutionPayloadHeader = val
header, ok := val.Proto().(*enginev1.ExecutionPayloadHeader)
if !ok {
return errors.New("value must be an execution payload header")
}
b.latestExecutionPayloadHeader = header
b.markFieldAsDirty(nativetypes.LatestExecutionPayloadHeader)
return nil
}

View File

@@ -40,6 +40,7 @@ go_library(
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",

View File

@@ -2,7 +2,7 @@ package v1
import (
"github.com/pkg/errors"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -57,6 +57,6 @@ func (*BeaconState) SetInactivityScores(_ []uint64) error {
}
// SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(_ *enginev1.ExecutionPayloadHeader) error {
func (*BeaconState) SetLatestExecutionPayloadHeader(_ interfaces.ExecutionData) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for phase 0 beacon state")
}

View File

@@ -43,6 +43,7 @@ go_library(
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",

View File

@@ -2,7 +2,7 @@ package v2
import (
"github.com/pkg/errors"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -22,6 +22,6 @@ func (*BeaconState) RotateAttestations() error {
}
// SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state.
func (*BeaconState) SetLatestExecutionPayloadHeader(_ *enginev1.ExecutionPayloadHeader) error {
func (*BeaconState) SetLatestExecutionPayloadHeader(_ interfaces.ExecutionData) error {
return errors.New("SetLatestExecutionPayloadHeader is not supported for hard fork 1 beacon state")
}

View File

@@ -45,6 +45,7 @@ go_library(
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/slice:go_default_library",
"//crypto/hash:go_default_library",

View File

@@ -1,16 +1,24 @@
package v3
import enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
)
// SetLatestExecutionPayloadHeader for the beacon state.
func (b *BeaconState) SetLatestExecutionPayloadHeader(val *enginev1.ExecutionPayloadHeader) error {
func (b *BeaconState) SetLatestExecutionPayloadHeader(val interfaces.ExecutionData) error {
if !b.hasInnerState() {
return ErrNilInnerState
}
b.lock.Lock()
defer b.lock.Unlock()
b.state.LatestExecutionPayloadHeader = val
header, ok := val.Proto().(*enginev1.ExecutionPayloadHeader)
if !ok {
return errors.New("value must be an execution payload header")
}
b.state.LatestExecutionPayloadHeader = header
b.markFieldAsDirty(latestExecutionPayloadHeader)
return nil
}

View File

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

View File

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

View File

@@ -283,14 +283,14 @@ func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState
if err != nil {
return err
}
payload, err := body.ExecutionPayload()
payload, err := body.Execution()
if err != nil {
return err
}
if payload == nil {
if payload.IsNil() {
return errors.New("execution payload is nil")
}
if payload.Timestamp != uint64(t.Unix()) {
if payload.Timestamp() != uint64(t.Unix()) {
return errors.New("incorrect timestamp")
}

View File

@@ -324,19 +324,6 @@ var (
Value: false,
}
// FeeRecipientConfigFileFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigFileFlag = &cli.StringFlag{
Name: "fee-recipient-config-file",
Usage: "DEPRECATED, please use proposer-settings-file",
Value: "",
}
// FeeRecipientConfigURLFlag defines the path or URL to a file with proposer config.
FeeRecipientConfigURLFlag = &cli.StringFlag{
Name: "fee-recipient-config-url",
Usage: "DEPRECATED, please use proposer-settings-url",
Value: "",
}
// ProposerSettingsFlag defines the path or URL to a file with proposer config.
ProposerSettingsFlag = &cli.StringFlag{
Name: "proposer-settings-file",

View File

@@ -75,8 +75,6 @@ var appFlags = []cli.Flag{
// Consensys' Web3Signer flags
flags.Web3SignerURLFlag,
flags.Web3SignerPublicValidatorKeysFlag,
flags.FeeRecipientConfigFileFlag,
flags.FeeRecipientConfigURLFlag,
flags.SuggestedFeeRecipientFlag,
flags.ProposerSettingsURLFlag,
flags.ProposerSettingsFlag,

View File

@@ -109,8 +109,6 @@ var appHelpFlagGroups = []flagGroup{
flags.EnableDutyCountDown,
flags.Web3SignerURLFlag,
flags.Web3SignerPublicValidatorKeysFlag,
flags.FeeRecipientConfigFileFlag,
flags.FeeRecipientConfigURLFlag,
flags.ProposerSettingsFlag,
flags.ProposerSettingsURLFlag,
flags.SuggestedFeeRecipientFlag,

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["execution_payload.go"],
importpath = "github.com/prysmaticlabs/prysm/consensus-types/forks/bellatrix",
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
],
)

View File

@@ -1,130 +0,0 @@
package bellatrix
import (
"bytes"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
)
// PayloadToHeader converts `payload` into execution payload header format.
func PayloadToHeader(payload *enginev1.ExecutionPayload) (*enginev1.ExecutionPayloadHeader, error) {
txRoot, err := ssz.TransactionsRoot(payload.Transactions)
if err != nil {
return nil, err
}
return &enginev1.ExecutionPayloadHeader{
ParentHash: bytesutil.SafeCopyBytes(payload.ParentHash),
FeeRecipient: bytesutil.SafeCopyBytes(payload.FeeRecipient),
StateRoot: bytesutil.SafeCopyBytes(payload.StateRoot),
ReceiptsRoot: bytesutil.SafeCopyBytes(payload.ReceiptsRoot),
LogsBloom: bytesutil.SafeCopyBytes(payload.LogsBloom),
PrevRandao: bytesutil.SafeCopyBytes(payload.PrevRandao),
BlockNumber: payload.BlockNumber,
GasLimit: payload.GasLimit,
GasUsed: payload.GasUsed,
Timestamp: payload.Timestamp,
ExtraData: bytesutil.SafeCopyBytes(payload.ExtraData),
BaseFeePerGas: bytesutil.SafeCopyBytes(payload.BaseFeePerGas),
BlockHash: bytesutil.SafeCopyBytes(payload.BlockHash),
TransactionsRoot: txRoot[:],
}, nil
}
func IsEmptyPayload(p *enginev1.ExecutionPayload) bool {
if p == nil {
return true
}
if !bytes.Equal(p.ParentHash, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(p.FeeRecipient, make([]byte, fieldparams.FeeRecipientLength)) {
return false
}
if !bytes.Equal(p.StateRoot, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(p.ReceiptsRoot, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(p.LogsBloom, make([]byte, fieldparams.LogsBloomLength)) {
return false
}
if !bytes.Equal(p.PrevRandao, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(p.BaseFeePerGas, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(p.BlockHash, make([]byte, fieldparams.RootLength)) {
return false
}
if len(p.Transactions) != 0 {
return false
}
if len(p.ExtraData) != 0 {
return false
}
if p.BlockNumber != 0 {
return false
}
if p.GasLimit != 0 {
return false
}
if p.GasUsed != 0 {
return false
}
if p.Timestamp != 0 {
return false
}
return true
}
func IsEmptyHeader(h *enginev1.ExecutionPayloadHeader) bool {
if !bytes.Equal(h.ParentHash, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.FeeRecipient, make([]byte, fieldparams.FeeRecipientLength)) {
return false
}
if !bytes.Equal(h.StateRoot, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.ReceiptsRoot, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.LogsBloom, make([]byte, fieldparams.LogsBloomLength)) {
return false
}
if !bytes.Equal(h.PrevRandao, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.BaseFeePerGas, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.BlockHash, make([]byte, fieldparams.RootLength)) {
return false
}
if !bytes.Equal(h.TransactionsRoot, make([]byte, fieldparams.RootLength)) {
return false
}
if len(h.ExtraData) != 0 {
return false
}
if h.BlockNumber != 0 {
return false
}
if h.GasLimit != 0 {
return false
}
if h.GasUsed != 0 {
return false
}
if h.Timestamp != 0 {
return false
}
return true
}

View File

@@ -10,7 +10,6 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@@ -3,7 +3,6 @@ package interfaces
import (
ssz "github.com/prysmaticlabs/fastssz"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"google.golang.org/protobuf/proto"
@@ -62,6 +61,30 @@ type BeaconBlockBody interface {
IsNil() bool
HashTreeRoot() ([32]byte, error)
Proto() proto.Message
ExecutionPayload() (*enginev1.ExecutionPayload, error)
ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error)
Execution() (ExecutionData, error)
}
// ExecutionData represents execution layer information that is contained
// within post-Bellatrix beacon block bodies.
type ExecutionData interface {
ssz.Marshaler
ssz.Unmarshaler
ssz.HashRoot
IsNil() bool
Proto() proto.Message
ParentHash() []byte
FeeRecipient() []byte
StateRoot() []byte
ReceiptsRoot() []byte
LogsBloom() []byte
PrevRandao() []byte
BlockNumber() uint64
GasLimit() uint64
GasUsed() uint64
Timestamp() uint64
ExtraData() []byte
BaseFeePerGas() []byte
BlockHash() []byte
Transactions() ([][]byte, error)
TransactionsRoot() ([]byte, error)
}

View File

@@ -8,7 +8,6 @@ go_library(
deps = [
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",

View File

@@ -4,7 +4,6 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"google.golang.org/protobuf/proto"
@@ -199,11 +198,7 @@ func (BeaconBlockBody) Proto() proto.Message {
panic("implement me")
}
func (BeaconBlockBody) ExecutionPayload() (*enginev1.ExecutionPayload, error) {
panic("implement me")
}
func (BeaconBlockBody) ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error) {
func (BeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
panic("implement me")
}

View File

@@ -8,15 +8,18 @@ go_library(
"beacon_block_bellatrix.go",
"beacon_block_phase0.go",
"blinded_beacon_block_bellatrix.go",
"execution.go",
"metadata.go",
"mutator.go",
],
importpath = "github.com/prysmaticlabs/prysm/consensus-types/wrapper",
visibility = ["//visibility:public"],
deps = [
"//consensus-types/forks/bellatrix:go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/metadata:go_default_library",
@@ -37,11 +40,12 @@ go_test(
"beacon_block_phase0_test.go",
"beacon_block_test.go",
"blinded_beacon_block_bellatrix_test.go",
"execution_test.go",
],
deps = [
":go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/forks/bellatrix:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",

View File

@@ -4,7 +4,6 @@ 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"
@@ -158,7 +157,7 @@ func BuildSignedBeaconBlockFromExecutionPayload(
return nil, err
}
b := blk.Block()
payloadHeader, err := b.Body().ExecutionPayloadHeader()
payloadHeader, err := b.Body().Execution()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, errors.Wrap(err, "can only build signed beacon block from blinded format")
@@ -218,7 +217,7 @@ func WrapSignedBlindedBeaconBlock(blk interfaces.SignedBeaconBlock) (interfaces.
return blk, nil
}
b := blk.Block()
payload, err := b.Body().ExecutionPayload()
payload, err := b.Body().Execution()
switch {
case errors.Is(err, ErrUnsupportedField):
return nil, ErrUnsupportedSignedBeaconBlock
@@ -230,7 +229,7 @@ func WrapSignedBlindedBeaconBlock(blk interfaces.SignedBeaconBlock) (interfaces.
if err != nil {
return nil, err
}
header, err := bellatrix.PayloadToHeader(payload)
header, err := PayloadToHeader(payload)
if err != nil {
return nil, err
}

View File

@@ -5,7 +5,6 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -313,12 +312,7 @@ func (w altairBeaconBlockBody) Proto() proto.Message {
return w.b
}
// ExecutionPayload is a stub.
func (w altairBeaconBlockBody) ExecutionPayload() (*enginev1.ExecutionPayload, error) {
// Execution is a stub.
func (w altairBeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayload for %T", w)
}
// ExecutionPayloadHeader is a stub.
func (w altairBeaconBlockBody) ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayloadHeader for %T", w)
}

View File

@@ -351,6 +351,6 @@ func TestAltairBeaconBlock_ExecutionPayloadHeader(t *testing.T) {
}
wsb, err := wrapper.WrappedSignedBeaconBlock(sb)
require.NoError(t, err)
_, err = wsb.Block().Body().ExecutionPayloadHeader()
_, err = wsb.Block().Body().Execution()
require.ErrorContains(t, "unsupported field for block type", err)
}

View File

@@ -5,7 +5,6 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -308,12 +307,7 @@ func (w bellatrixBeaconBlockBody) Proto() proto.Message {
return w.b
}
// ExecutionPayload returns the Execution payload of the block body.
func (w bellatrixBeaconBlockBody) ExecutionPayload() (*enginev1.ExecutionPayload, error) {
return w.b.ExecutionPayload, nil
}
// ExecutionPayloadHeader is a stub.
func (w bellatrixBeaconBlockBody) ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayloadHeader for %T", w)
// Execution returns the Execution payload of the block body.
func (w bellatrixBeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
return WrappedExecutionPayload(w.b.ExecutionPayload)
}

View File

@@ -352,9 +352,9 @@ func TestBellatrixBeaconBlockBody_ExecutionPayload(t *testing.T) {
wbb, err := wrapper.WrappedBeaconBlockBody(body)
require.NoError(t, err)
got, err := wbb.ExecutionPayload()
got, err := wbb.Execution()
require.NoError(t, err)
assert.DeepEqual(t, payloads, got)
assert.DeepEqual(t, payloads, got.Proto())
}
func TestBellatrixBeaconBlock_PbGenericBlock(t *testing.T) {
@@ -392,10 +392,14 @@ func TestBellatrixBeaconBlock_PbBlindedBellatrixBlock(t *testing.T) {
func TestBellatrixBeaconBlock_ExecutionPayloadHeader(t *testing.T) {
sb := &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{Slot: 66},
Block: &ethpb.BeaconBlockBellatrix{Slot: 66, Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &enginev1.ExecutionPayload{},
}},
}
wsb, err := wrapper.WrappedSignedBeaconBlock(sb)
require.NoError(t, err)
_, err = wsb.Block().Body().ExecutionPayloadHeader()
exec, err := wsb.Block().Body().Execution()
require.NoError(t, err)
_, err = exec.TransactionsRoot()
require.ErrorContains(t, "unsupported field for block type", err)
}

View File

@@ -5,7 +5,6 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -301,12 +300,7 @@ func (w Phase0BeaconBlockBody) Proto() proto.Message {
return w.b
}
// ExecutionPayload is a stub.
func (w Phase0BeaconBlockBody) ExecutionPayload() (*enginev1.ExecutionPayload, error) {
// Execution is a stub.
func (w Phase0BeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayload for %T", w)
}
// ExecutionPayloadHeader is a stub.
func (w Phase0BeaconBlockBody) ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayloadHeader for %T", w)
}

View File

@@ -82,6 +82,6 @@ func TestPhase0BeaconBlock_ExecutionPayloadHeader(t *testing.T) {
}
wsb, err := wrapper.WrappedSignedBeaconBlock(sb)
require.NoError(t, err)
_, err = wsb.Block().Body().ExecutionPayloadHeader()
_, err = wsb.Block().Body().Execution()
require.ErrorContains(t, "unsupported field for block type", err)
}

View File

@@ -5,7 +5,6 @@ import (
"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"
@@ -36,7 +35,9 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
wrapped, err := wrapper.WrappedExecutionPayload(payload)
require.NoError(t, err)
header, err := wrapper.PayloadToHeader(wrapped)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
@@ -61,7 +62,9 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
}
header, err := bellatrix.PayloadToHeader(payload)
wrapped, err := wrapper.WrappedExecutionPayload(payload)
require.NoError(t, err)
header, err := wrapper.PayloadToHeader(wrapped)
require.NoError(t, err)
blindedBlock := util.NewBlindedBeaconBlockBellatrix()
blindedBlock.Block.Body.ExecutionPayloadHeader = header
@@ -71,9 +74,9 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
builtBlock, err := wrapper.BuildSignedBeaconBlockFromExecutionPayload(blk, payload)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayload()
got, err := builtBlock.Block().Body().Execution()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
require.DeepEqual(t, payload, got.Proto())
})
}
@@ -104,7 +107,9 @@ func TestWrapSignedBlindedBeaconBlock(t *testing.T) {
bellatrixBlk := util.NewBeaconBlockBellatrix()
bellatrixBlk.Block.Body.ExecutionPayload = payload
want, err := bellatrix.PayloadToHeader(payload)
wrapped, err := wrapper.WrappedExecutionPayload(payload)
require.NoError(t, err)
want, err := wrapper.PayloadToHeader(wrapped)
require.NoError(t, err)
blk, err := wrapper.WrappedSignedBeaconBlock(bellatrixBlk)
@@ -112,9 +117,9 @@ func TestWrapSignedBlindedBeaconBlock(t *testing.T) {
builtBlock, err := wrapper.WrapSignedBlindedBeaconBlock(blk)
require.NoError(t, err)
got, err := builtBlock.Block().Body().ExecutionPayloadHeader()
got, err := builtBlock.Block().Body().Execution()
require.NoError(t, err)
require.DeepEqual(t, want, got)
require.DeepEqual(t, want, got.Proto())
})
}

View File

@@ -5,7 +5,6 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/runtime/version"
@@ -309,12 +308,6 @@ func (w blindedBeaconBlockBodyBellatrix) Proto() proto.Message {
return w.b
}
// ExecutionPayload returns the execution payload of the block body.
func (w blindedBeaconBlockBodyBellatrix) ExecutionPayload() (*enginev1.ExecutionPayload, error) {
return nil, errors.Wrapf(ErrUnsupportedField, "ExecutionPayload for %T", w)
}
// ExecutionPayloadHeader returns the execution payload header of the block body.
func (w blindedBeaconBlockBodyBellatrix) ExecutionPayloadHeader() (*enginev1.ExecutionPayloadHeader, error) {
return w.b.ExecutionPayloadHeader, nil
func (w blindedBeaconBlockBodyBellatrix) Execution() (interfaces.ExecutionData, error) {
return WrappedExecutionPayloadHeader(w.b.ExecutionPayloadHeader)
}

View File

@@ -365,7 +365,9 @@ func TestBellatrixBlindedBeaconBlockBody_ExecutionPayloadHeader(t *testing.T) {
wbb, err := wrapper.WrappedBeaconBlockBody(body)
require.NoError(t, err)
_, err = wbb.ExecutionPayload()
exec, err := wbb.Execution()
require.NoError(t, err)
_, err = exec.Transactions()
require.ErrorContains(t, wrapper.ErrUnsupportedField.Error(), err)
}

View File

@@ -0,0 +1,371 @@
package wrapper
import (
"bytes"
"github.com/pkg/errors"
fastssz "github.com/prysmaticlabs/fastssz"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"google.golang.org/protobuf/proto"
)
// executionPayload is a convenience wrapper around a beacon block body's execution payload data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
type executionPayload struct {
p *enginev1.ExecutionPayload
}
// WrappedExecutionPayload is a constructor which wraps a protobuf execution payload into an interface.
func WrappedExecutionPayload(p *enginev1.ExecutionPayload) (interfaces.ExecutionData, error) {
w := executionPayload{p: p}
if w.IsNil() {
return nil, ErrNilObjectWrapped
}
return w, nil
}
// IsNil checks if the underlying data is nil.
func (e executionPayload) IsNil() bool {
return e.p == nil
}
// MarshalSSZ --
func (e executionPayload) MarshalSSZ() ([]byte, error) {
return e.p.MarshalSSZ()
}
// MarshalSSZTo --
func (e executionPayload) MarshalSSZTo(dst []byte) ([]byte, error) {
return e.p.MarshalSSZTo(dst)
}
// SizeSSZ --
func (e executionPayload) SizeSSZ() int {
return e.p.SizeSSZ()
}
// UnmarshalSSZ --
func (e executionPayload) UnmarshalSSZ(buf []byte) error {
return e.p.UnmarshalSSZ(buf)
}
// HashTreeRoot --
func (e executionPayload) HashTreeRoot() ([32]byte, error) {
return e.p.HashTreeRoot()
}
// HashTreeRootWith --
func (e executionPayload) HashTreeRootWith(hh *fastssz.Hasher) error {
return e.p.HashTreeRootWith(hh)
}
// Proto --
func (e executionPayload) Proto() proto.Message {
return e.p
}
// ParentHash --
func (e executionPayload) ParentHash() []byte {
return e.p.ParentHash
}
// FeeRecipient --
func (e executionPayload) FeeRecipient() []byte {
return e.p.FeeRecipient
}
// StateRoot --
func (e executionPayload) StateRoot() []byte {
return e.p.StateRoot
}
// ReceiptsRoot --
func (e executionPayload) ReceiptsRoot() []byte {
return e.p.ReceiptsRoot
}
// LogsBloom --
func (e executionPayload) LogsBloom() []byte {
return e.p.LogsBloom
}
// PrevRandao --
func (e executionPayload) PrevRandao() []byte {
return e.p.PrevRandao
}
// BlockNumber --
func (e executionPayload) BlockNumber() uint64 {
return e.p.BlockNumber
}
// GasLimit --
func (e executionPayload) GasLimit() uint64 {
return e.p.GasLimit
}
// GasUsed --
func (e executionPayload) GasUsed() uint64 {
return e.p.GasUsed
}
// Timestamp --
func (e executionPayload) Timestamp() uint64 {
return e.p.Timestamp
}
// ExtraData --
func (e executionPayload) ExtraData() []byte {
return e.p.ExtraData
}
// BaseFeePerGas --
func (e executionPayload) BaseFeePerGas() []byte {
return e.p.BaseFeePerGas
}
// BlockHash --
func (e executionPayload) BlockHash() []byte {
return e.p.BlockHash
}
// Transactions --
func (e executionPayload) Transactions() ([][]byte, error) {
return e.p.Transactions, nil
}
// TransactionsRoot --
func (executionPayload) TransactionsRoot() ([]byte, error) {
return nil, ErrUnsupportedField
}
// executionPayloadHeader is a convenience wrapper around a blinded beacon block body's execution header data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
type executionPayloadHeader struct {
p *enginev1.ExecutionPayloadHeader
}
// WrappedExecutionPayloadHeader is a constructor which wraps a protobuf execution header into an interface.
func WrappedExecutionPayloadHeader(p *enginev1.ExecutionPayloadHeader) (interfaces.ExecutionData, error) {
w := executionPayloadHeader{p: p}
if w.IsNil() {
return nil, ErrNilObjectWrapped
}
return w, nil
}
// IsNil checks if the underlying data is nil.
func (e executionPayloadHeader) IsNil() bool {
return e.p == nil
}
// MarshalSSZ --
func (e executionPayloadHeader) MarshalSSZ() ([]byte, error) {
return e.p.MarshalSSZ()
}
// MarshalSSZTo --
func (e executionPayloadHeader) MarshalSSZTo(dst []byte) ([]byte, error) {
return e.p.MarshalSSZTo(dst)
}
// SizeSSZ --
func (e executionPayloadHeader) SizeSSZ() int {
return e.p.SizeSSZ()
}
// UnmarshalSSZ --
func (e executionPayloadHeader) UnmarshalSSZ(buf []byte) error {
return e.p.UnmarshalSSZ(buf)
}
// HashTreeRoot --
func (e executionPayloadHeader) HashTreeRoot() ([32]byte, error) {
return e.p.HashTreeRoot()
}
// HashTreeRootWith --
func (e executionPayloadHeader) HashTreeRootWith(hh *fastssz.Hasher) error {
return e.p.HashTreeRootWith(hh)
}
// Proto --
func (e executionPayloadHeader) Proto() proto.Message {
return e.p
}
// ParentHash --
func (e executionPayloadHeader) ParentHash() []byte {
return e.p.ParentHash
}
// FeeRecipient --
func (e executionPayloadHeader) FeeRecipient() []byte {
return e.p.FeeRecipient
}
// StateRoot --
func (e executionPayloadHeader) StateRoot() []byte {
return e.p.StateRoot
}
// ReceiptsRoot --
func (e executionPayloadHeader) ReceiptsRoot() []byte {
return e.p.ReceiptsRoot
}
// LogsBloom --
func (e executionPayloadHeader) LogsBloom() []byte {
return e.p.LogsBloom
}
// PrevRandao --
func (e executionPayloadHeader) PrevRandao() []byte {
return e.p.PrevRandao
}
// BlockNumber --
func (e executionPayloadHeader) BlockNumber() uint64 {
return e.p.BlockNumber
}
// GasLimit --
func (e executionPayloadHeader) GasLimit() uint64 {
return e.p.GasLimit
}
// GasUsed --
func (e executionPayloadHeader) GasUsed() uint64 {
return e.p.GasUsed
}
// Timestamp --
func (e executionPayloadHeader) Timestamp() uint64 {
return e.p.Timestamp
}
// ExtraData --
func (e executionPayloadHeader) ExtraData() []byte {
return e.p.ExtraData
}
// BaseFeePerGas --
func (e executionPayloadHeader) BaseFeePerGas() []byte {
return e.p.BaseFeePerGas
}
// BlockHash --
func (e executionPayloadHeader) BlockHash() []byte {
return e.p.BlockHash
}
// Transactions --
func (executionPayloadHeader) Transactions() ([][]byte, error) {
return nil, ErrUnsupportedField
}
// TransactionsRoot --
func (e executionPayloadHeader) TransactionsRoot() ([]byte, error) {
return e.p.TransactionsRoot, nil
}
// PayloadToHeader converts `payload` into execution payload header format.
func PayloadToHeader(payload interfaces.ExecutionData) (*enginev1.ExecutionPayloadHeader, error) {
txs, err := payload.Transactions()
if err != nil {
return nil, err
}
txRoot, err := ssz.TransactionsRoot(txs)
if err != nil {
return nil, err
}
return &enginev1.ExecutionPayloadHeader{
ParentHash: bytesutil.SafeCopyBytes(payload.ParentHash()),
FeeRecipient: bytesutil.SafeCopyBytes(payload.FeeRecipient()),
StateRoot: bytesutil.SafeCopyBytes(payload.StateRoot()),
ReceiptsRoot: bytesutil.SafeCopyBytes(payload.ReceiptsRoot()),
LogsBloom: bytesutil.SafeCopyBytes(payload.LogsBloom()),
PrevRandao: bytesutil.SafeCopyBytes(payload.PrevRandao()),
BlockNumber: payload.BlockNumber(),
GasLimit: payload.GasLimit(),
GasUsed: payload.GasUsed(),
Timestamp: payload.Timestamp(),
ExtraData: bytesutil.SafeCopyBytes(payload.ExtraData()),
BaseFeePerGas: bytesutil.SafeCopyBytes(payload.BaseFeePerGas()),
BlockHash: bytesutil.SafeCopyBytes(payload.BlockHash()),
TransactionsRoot: txRoot[:],
}, nil
}
// IsEmptyExecutionData checks if an execution data is empty underneath. If a single field has
// a non-zero value, this function will return false.
func IsEmptyExecutionData(data interfaces.ExecutionData) (bool, error) {
if !bytes.Equal(data.ParentHash(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
if !bytes.Equal(data.FeeRecipient(), make([]byte, fieldparams.FeeRecipientLength)) {
return false, nil
}
if !bytes.Equal(data.StateRoot(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
if !bytes.Equal(data.ReceiptsRoot(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
if !bytes.Equal(data.LogsBloom(), make([]byte, fieldparams.LogsBloomLength)) {
return false, nil
}
if !bytes.Equal(data.PrevRandao(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
if !bytes.Equal(data.BaseFeePerGas(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
if !bytes.Equal(data.BlockHash(), make([]byte, fieldparams.RootLength)) {
return false, nil
}
txs, err := data.Transactions()
switch {
case errors.Is(err, ErrUnsupportedField):
case err != nil:
return false, err
default:
if len(txs) != 0 {
return false, nil
}
}
txsRoot, err := data.TransactionsRoot()
switch {
case errors.Is(err, ErrUnsupportedField):
case err != nil:
return false, err
default:
if !bytes.Equal(txsRoot, make([]byte, fieldparams.RootLength)) {
return false, nil
}
}
if len(data.ExtraData()) != 0 {
return false, nil
}
if data.BlockNumber() != 0 {
return false, nil
}
if data.GasLimit() != 0 {
return false, nil
}
if data.GasUsed() != 0 {
return false, nil
}
if data.Timestamp() != 0 {
return false, nil
}
return true, nil
}

View File

@@ -0,0 +1,124 @@
package wrapper_test
import (
"testing"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestWrapExecutionPayload(t *testing.T) {
data := &enginev1.ExecutionPayload{GasUsed: 54}
wsb, err := wrapper.WrappedExecutionPayload(data)
require.NoError(t, err)
assert.DeepEqual(t, data, wsb.Proto())
}
func TestWrapExecutionPayloadHeader(t *testing.T) {
data := &enginev1.ExecutionPayloadHeader{GasUsed: 54}
wsb, err := wrapper.WrappedExecutionPayloadHeader(data)
require.NoError(t, err)
assert.DeepEqual(t, data, wsb.Proto())
}
func TestWrapExecutionPayload_IsNil(t *testing.T) {
_, err := wrapper.WrappedExecutionPayload(nil)
require.Equal(t, wrapper.ErrNilObjectWrapped, err)
data := &enginev1.ExecutionPayload{GasUsed: 54}
wsb, err := wrapper.WrappedExecutionPayload(data)
require.NoError(t, err)
assert.Equal(t, false, wsb.IsNil())
}
func TestWrapExecutionPayloadHeader_IsNil(t *testing.T) {
_, err := wrapper.WrappedExecutionPayloadHeader(nil)
require.Equal(t, wrapper.ErrNilObjectWrapped, err)
data := &enginev1.ExecutionPayloadHeader{GasUsed: 54}
wsb, err := wrapper.WrappedExecutionPayloadHeader(data)
require.NoError(t, err)
assert.Equal(t, false, wsb.IsNil())
}
func TestWrapExecutionPayload_SSZ(t *testing.T) {
wsb := createWrappedPayload(t)
rt, err := wsb.HashTreeRoot()
assert.NoError(t, err)
assert.NotEmpty(t, rt)
var b []byte
b, err = wsb.MarshalSSZTo(b)
assert.NoError(t, err)
assert.NotEqual(t, 0, len(b))
encoded, err := wsb.MarshalSSZ()
require.NoError(t, err)
assert.NotEqual(t, 0, wsb.SizeSSZ())
assert.NoError(t, wsb.UnmarshalSSZ(encoded))
}
func TestWrapExecutionPayloadHeader_SSZ(t *testing.T) {
wsb := createWrappedPayloadHeader(t)
rt, err := wsb.HashTreeRoot()
assert.NoError(t, err)
assert.NotEmpty(t, rt)
var b []byte
b, err = wsb.MarshalSSZTo(b)
assert.NoError(t, err)
assert.NotEqual(t, 0, len(b))
encoded, err := wsb.MarshalSSZ()
require.NoError(t, err)
assert.NotEqual(t, 0, wsb.SizeSSZ())
assert.NoError(t, wsb.UnmarshalSSZ(encoded))
}
func createWrappedPayload(t testing.TB) interfaces.ExecutionData {
wsb, err := wrapper.WrappedExecutionPayload(&enginev1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
})
require.NoError(t, err)
return wsb
}
func createWrappedPayloadHeader(t testing.TB) interfaces.ExecutionData {
wsb, err := wrapper.WrappedExecutionPayloadHeader(&enginev1.ExecutionPayloadHeader{
ParentHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
StateRoot: make([]byte, fieldparams.RootLength),
ReceiptsRoot: make([]byte, fieldparams.RootLength),
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
PrevRandao: make([]byte, fieldparams.RootLength),
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: make([]byte, fieldparams.RootLength),
})
require.NoError(t, err)
return wsb
}

View File

@@ -5,6 +5,42 @@ load("@prysm//tools/go:def.bzl", "go_test")
# gazelle:exclude mainnet_scenario_e2e_test.go
# gazelle:exclude minimal_scenario_e2e_test.go
# Presubmit tests represent the group of endtoend tests that are run on pull
# requests and must be passing before a pull request can merge.
test_suite(
name = "presubmit",
tags = [
"manual",
],
tests = [
":go_default_test",
],
)
# Postsubmit tests represent the group of endtoend tests that are run after a
# pull request has merged.
test_suite(
name = "postsubmit",
tags = [
"manual",
],
tests = [
":go_mainnet_test",
],
)
# Scenario tests are run infrequently in CI to test specific scenarios.
test_suite(
name = "scenario_tests",
tags = [
"manual",
],
tests = [
":go_mainnet_scenario_test",
":go_minimal_scenario_test",
],
)
common_deps = [
"//api/client/beacon:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",

View File

@@ -73,7 +73,7 @@ func (s *BeaconNodeSet) Start(ctx context.Context) error {
}
s.config.PeerIDs = s.ids
}
// All nodes stated, close channel, so that all services waiting on a set, can proceed.
// All nodes started, close channel, so that all services waiting on a set, can proceed.
close(s.started)
})
}

View File

@@ -34,11 +34,9 @@ func NewProxySet() *ProxySet {
// Start starts all the proxies in set.
func (s *ProxySet) Start(ctx context.Context) error {
totalNodeCount := e2e.TestParams.BeaconNodeCount + e2e.TestParams.LighthouseBeaconNodeCount
nodes := make([]e2etypes.ComponentRunner, totalNodeCount)
for i := 0; i < totalNodeCount; i++ {
// We start indexing nodes from 1 because the miner has an implicit 0 index.
nodes[i] = NewProxy(i)
}
s.proxies = nodes

View File

@@ -63,7 +63,7 @@ func (s *LighthouseBeaconNodeSet) Start(ctx context.Context) error {
// Wait for all nodes to finish their job (blocking).
// Once nodes are ready passed in handler function will be called.
return helpers.WaitOnNodes(ctx, nodes, func() {
// All nodes stated, close channel, so that all services waiting on a set, can proceed.
// All nodes started, close channel, so that all services waiting on a set, can proceed.
close(s.started)
})
}

View File

@@ -67,7 +67,7 @@ func (s *LighthouseValidatorNodeSet) Start(ctx context.Context) error {
// Wait for all nodes to finish their job (blocking).
// Once nodes are ready passed in handler function will be called.
return helpers.WaitOnNodes(ctx, nodes, func() {
// All nodes stated, close channel, so that all services waiting on a set, can proceed.
// All nodes started, close channel, so that all services waiting on a set, can proceed.
close(s.started)
})
}

View File

@@ -81,7 +81,7 @@ func (s *ValidatorNodeSet) Start(ctx context.Context) error {
// Wait for all nodes to finish their job (blocking).
// Once nodes are ready passed in handler function will be called.
return helpers.WaitOnNodes(ctx, nodes, func() {
// All nodes stated, close channel, so that all services waiting on a set, can proceed.
// All nodes started, close channel, so that all services waiting on a set, can proceed.
close(s.started)
})
}

View File

@@ -76,7 +76,7 @@ func (m *engineMock) GetPayload(context.Context, [8]byte) (*pb.ExecutionPayload,
func (m *engineMock) ForkchoiceUpdated(context.Context, *pb.ForkchoiceState, *pb.PayloadAttributes) (*pb.PayloadIDBytes, []byte, error) {
return nil, nil, nil
}
func (m *engineMock) NewPayload(context.Context, *pb.ExecutionPayload) ([]byte, error) {
func (m *engineMock) NewPayload(context.Context, interfaces.ExecutionData) ([]byte, error) {
return nil, nil
}

View File

@@ -11,6 +11,7 @@ go_library(
],
deps = [
"//consensus-types/primitives:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//validator/accounts/iface:go_default_library",
"//validator/client/iface:go_default_library",
"//validator/keymanager:go_default_library",

View File

@@ -8,6 +8,7 @@ import (
"time"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/validator/accounts/iface"
iface2 "github.com/prysmaticlabs/prysm/validator/client/iface"
"github.com/prysmaticlabs/prysm/validator/keymanager"
@@ -190,3 +191,8 @@ func (_ MockValidator) PushProposerSettings(_ context.Context, _ keymanager.IKey
func (_ MockValidator) SetPubKeyToValidatorIndexMap(_ context.Context, _ keymanager.IKeymanager) error {
panic("implement me")
}
// SignValidatorRegistrationRequest for mocking
func (_ MockValidator) SignValidatorRegistrationRequest(_ context.Context, _ iface2.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
panic("implement me")
}

View File

@@ -8,6 +8,9 @@ go_library(
deps = [
"//config/fieldparams:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//validator/keymanager:go_default_library",
],
)

View File

@@ -7,6 +7,9 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/crypto/bls"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/validator/keymanager"
)
@@ -59,4 +62,8 @@ type Validator interface {
HandleKeyReload(ctx context.Context, newKeys [][fieldparams.BLSPubkeyLength]byte) (bool, error)
CheckDoppelGanger(ctx context.Context) error
PushProposerSettings(ctx context.Context, km keymanager.IKeymanager) error
SignValidatorRegistrationRequest(ctx context.Context, signer SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error)
}
// SigningFunc interface defines a type for the a function that signs a message
type SigningFunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error)

View File

@@ -27,8 +27,6 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)
type signingFunc func(context.Context, *validatorpb.SignRequest) (bls.Signature, error)
const domainDataErr = "could not get domain data"
const signingRootErr = "could not get signing root"
const signExitErr = "could not sign voluntary exit proposal"
@@ -147,19 +145,24 @@ func (v *validator) ProposeBlock(ctx context.Context, slot types.Slot, pubKey [f
)
if blk.Version() == version.Bellatrix {
p, err := blk.Block().Body().ExecutionPayload()
p, err := blk.Block().Body().Execution()
if err != nil {
log.WithError(err).Error("Failed to get execution payload")
return
}
txs, err := p.Transactions()
if err != nil {
log.WithError(err).Error("Failed to get execution payload transactions")
return
}
log = log.WithFields(logrus.Fields{
"payloadHash": fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash)),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(p.ParentHash)),
"payloadHash": fmt.Sprintf("%#x", bytesutil.Trunc(p.BlockHash())),
"parentHash": fmt.Sprintf("%#x", bytesutil.Trunc(p.ParentHash())),
"blockNumber": p.BlockNumber,
"txCount": len(p.Transactions),
"txCount": len(txs),
})
if p.GasLimit != 0 {
log = log.WithField("gasUtilized", float64(p.GasUsed)/float64(p.GasLimit))
if p.GasLimit() != 0 {
log = log.WithField("gasUtilized", float64(p.GasUsed())/float64(p.GasLimit()))
}
}
@@ -184,7 +187,7 @@ func ProposeExit(
ctx context.Context,
validatorClient ethpb.BeaconNodeValidatorClient,
nodeClient ethpb.NodeClient,
signer signingFunc,
signer iface.SigningFunc,
pubKey []byte,
) error {
ctx, span := trace.StartSpan(ctx, "validator.ProposeExit")
@@ -281,7 +284,7 @@ func (v *validator) signBlock(ctx context.Context, pubKey [fieldparams.BLSPubkey
func signVoluntaryExit(
ctx context.Context,
validatorClient ethpb.BeaconNodeValidatorClient,
signer signingFunc,
signer iface.SigningFunc,
pubKey []byte,
exit *ethpb.VoluntaryExit,
) ([]byte, error) {

View File

@@ -3,11 +3,14 @@ package client
import (
"context"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/validator/client/iface"
"go.opencensus.io/trace"
)
@@ -15,40 +18,26 @@ import (
func SubmitValidatorRegistration(
ctx context.Context,
validatorClient ethpb.BeaconNodeValidatorClient,
signer signingFunc,
regs []*ethpb.ValidatorRegistrationV1,
signedRegs []*ethpb.SignedValidatorRegistrationV1,
) error {
ctx, span := trace.StartSpan(ctx, "validator.SubmitBuilderValidatorRegistration")
defer span.End()
if len(regs) == 0 {
if len(signedRegs) == 0 {
return nil
}
signedRegs := make([]*ethpb.SignedValidatorRegistrationV1, len(regs))
for i, reg := range regs {
sig, err := signValidatorRegistration(ctx, signer, reg)
if err != nil {
log.WithError(err).Error("failed to sign builder validator registration obj")
continue
}
signedRegs[i] = &ethpb.SignedValidatorRegistrationV1{
Message: reg,
Signature: sig,
}
}
if _, err := validatorClient.SubmitValidatorRegistration(ctx, &ethpb.SignedValidatorRegistrationsV1{
Messages: signedRegs,
}); err != nil {
return errors.Wrap(err, "could not submit signed registrations to beacon node")
}
log.Infoln("Submitted builder validator registration settings for custom builders")
return nil
}
// Sings validator registration obj with the proposer domain and private key.
func signValidatorRegistration(ctx context.Context, signer signingFunc, reg *ethpb.ValidatorRegistrationV1) ([]byte, error) {
func signValidatorRegistration(ctx context.Context, signer iface.SigningFunc, reg *ethpb.ValidatorRegistrationV1) ([]byte, error) {
// Per spec, we want the fork version and genesis validator to be nil.
// Which is genesis value and zero by default.
@@ -72,7 +61,38 @@ func signValidatorRegistration(ctx context.Context, signer signingFunc, reg *eth
Object: &validatorpb.SignRequest_Registration{Registration: reg},
})
if err != nil {
return nil, errors.Wrap(err, signExitErr)
return nil, errors.Wrap(err, "could not sign validator registration")
}
return sig.Marshal(), nil
}
// SignValidatorRegistrationRequest compares and returns either the cached validator registration request or signs a new one.
func (v *validator) SignValidatorRegistrationRequest(ctx context.Context, signer iface.SigningFunc, newValidatorRegistration *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
signedReg, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(newValidatorRegistration.Pubkey)]
if ok && isValidatorRegistrationSame(signedReg.Message, newValidatorRegistration) {
return signedReg, nil
} else {
sig, err := signValidatorRegistration(ctx, signer, newValidatorRegistration)
if err != nil {
log.WithError(err).Error("failed to sign builder validator registration obj")
return nil, err
}
newRequest := &ethpb.SignedValidatorRegistrationV1{
Message: newValidatorRegistration,
Signature: sig,
}
v.signedValidatorRegistrations[bytesutil.ToBytes48(newValidatorRegistration.Pubkey)] = newRequest
return newRequest, nil
}
}
func isValidatorRegistrationSame(cachedVR *ethpb.ValidatorRegistrationV1, newVR *ethpb.ValidatorRegistrationV1) bool {
isSame := true
if cachedVR.GasLimit != newVR.GasLimit {
isSame = false
}
if hexutil.Encode(cachedVR.FeeRecipient) != hexutil.Encode(newVR.FeeRecipient) {
isSame = false
}
return isSame
}

View File

@@ -5,9 +5,12 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/mock/gomock"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
@@ -18,7 +21,7 @@ func TestSubmitValidatorRegistration(t *testing.T) {
defer finish()
ctx := context.Background()
require.NoError(t, nil, SubmitValidatorRegistration(ctx, m.validatorClient, m.signfunc, []*ethpb.ValidatorRegistrationV1{}))
require.NoError(t, nil, SubmitValidatorRegistration(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{}))
reg := &ethpb.ValidatorRegistrationV1{
FeeRecipient: bytesutil.PadTo([]byte("fee"), 20),
@@ -26,7 +29,6 @@ func TestSubmitValidatorRegistration(t *testing.T) {
Timestamp: uint64(time.Now().Unix()),
Pubkey: validatorKey.PublicKey().Marshal(),
}
regs := []*ethpb.ValidatorRegistrationV1{reg}
m.validatorClient.EXPECT().
SubmitValidatorRegistration(gomock.Any(), &ethpb.SignedValidatorRegistrationsV1{
@@ -36,7 +38,10 @@ func TestSubmitValidatorRegistration(t *testing.T) {
},
}).
Return(nil, nil)
require.NoError(t, nil, SubmitValidatorRegistration(ctx, m.validatorClient, m.signfunc, regs))
require.NoError(t, nil, SubmitValidatorRegistration(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{
{Message: reg,
Signature: params.BeaconConfig().ZeroHash[:]},
}))
}
func TestSubmitValidatorRegistration_CantSign(t *testing.T) {
@@ -50,7 +55,6 @@ func TestSubmitValidatorRegistration_CantSign(t *testing.T) {
Timestamp: uint64(time.Now().Unix()),
Pubkey: validatorKey.PublicKey().Marshal(),
}
regs := []*ethpb.ValidatorRegistrationV1{reg}
m.validatorClient.EXPECT().
SubmitValidatorRegistration(gomock.Any(), &ethpb.SignedValidatorRegistrationsV1{
@@ -60,7 +64,10 @@ func TestSubmitValidatorRegistration_CantSign(t *testing.T) {
},
}).
Return(nil, errors.New("could not sign"))
require.ErrorContains(t, "could not sign", SubmitValidatorRegistration(ctx, m.validatorClient, m.signfunc, regs))
require.ErrorContains(t, "could not sign", SubmitValidatorRegistration(ctx, m.validatorClient, []*ethpb.SignedValidatorRegistrationV1{
{Message: reg,
Signature: params.BeaconConfig().ZeroHash[:]},
}))
}
func Test_signValidatorRegistration(t *testing.T) {
@@ -78,3 +85,143 @@ func Test_signValidatorRegistration(t *testing.T) {
require.NoError(t, err)
}
func TestValidator_SignValidatorRegistrationRequest(t *testing.T) {
_, m, validatorKey, finish := setup(t)
defer finish()
ctx := context.Background()
byteval, err := hexutil.Decode("0x878705ba3f8bc32fcf7f4caa1a35e72af65cf766")
require.NoError(t, err)
tests := []struct {
name string
arg *ethpb.ValidatorRegistrationV1
validatorSetter func(t *testing.T) *validator
isCached bool
err string
}{
{
name: " Happy Path cached",
arg: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
GasLimit: 30000000,
Timestamp: uint64(time.Now().Unix()),
},
validatorSetter: func(t *testing.T) *validator {
v := validator{
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
genesisTime: 0,
}
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = &ethpb.SignedValidatorRegistrationV1{
Message: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
GasLimit: 30000000,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
Timestamp: uint64(time.Now().Unix()),
},
Signature: make([]byte, 0),
}
return &v
},
isCached: true,
},
{
name: " Happy Path not cached gas updated",
arg: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
GasLimit: 30000000,
Timestamp: uint64(time.Now().Unix()),
},
validatorSetter: func(t *testing.T) *validator {
v := validator{
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
genesisTime: 0,
}
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = &ethpb.SignedValidatorRegistrationV1{
Message: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
GasLimit: 35000000,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
Timestamp: uint64(time.Now().Unix() - 1),
},
Signature: make([]byte, 0),
}
return &v
},
isCached: false,
},
{
name: " Happy Path not cached feerecipient updated",
arg: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
FeeRecipient: byteval,
GasLimit: 30000000,
Timestamp: uint64(time.Now().Unix()),
},
validatorSetter: func(t *testing.T) *validator {
v := validator{
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
genesisTime: 0,
}
v.signedValidatorRegistrations[bytesutil.ToBytes48(validatorKey.PublicKey().Marshal())] = &ethpb.SignedValidatorRegistrationV1{
Message: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
GasLimit: 30000000,
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
Timestamp: uint64(time.Now().Unix() - 1),
},
Signature: make([]byte, 0),
}
return &v
},
isCached: false,
},
{
name: " Happy Path not cached first Entry",
arg: &ethpb.ValidatorRegistrationV1{
Pubkey: validatorKey.PublicKey().Marshal(),
FeeRecipient: byteval,
GasLimit: 30000000,
Timestamp: uint64(time.Now().Unix()),
},
validatorSetter: func(t *testing.T) *validator {
v := validator{
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
genesisTime: 0,
}
return &v
},
isCached: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := tt.validatorSetter(t)
startingReq, ok := v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)]
got, err := v.SignValidatorRegistrationRequest(ctx, m.signfunc, tt.arg)
require.NoError(t, err)
if tt.isCached {
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
} else {
if ok {
require.NotEqual(t, got.Message.Timestamp, startingReq.Message.Timestamp)
}
require.Equal(t, got.Message.Timestamp, tt.arg.Timestamp)
require.Equal(t, got.Message.GasLimit, tt.arg.GasLimit)
require.Equal(t, hexutil.Encode(got.Message.FeeRecipient), hexutil.Encode(tt.arg.FeeRecipient))
require.DeepEqual(t, got, v.signedValidatorRegistrations[bytesutil.ToBytes48(tt.arg.Pubkey)])
}
})
}
}

View File

@@ -192,6 +192,7 @@ func (v *ValidatorService) Start() {
startBalances: make(map[[fieldparams.BLSPubkeyLength]byte]uint64),
prevBalance: make(map[[fieldparams.BLSPubkeyLength]byte]uint64),
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
attLogs: make(map[[32]byte]*attSubmitted),
domainDataCache: cache,
aggregatedSlotCommitteeIDCache: aggregatedSlotCommitteeIDCache,

View File

@@ -261,3 +261,8 @@ func (_ *FakeValidator) PushProposerSettings(_ context.Context, _ keymanager.IKe
func (_ *FakeValidator) SetPubKeyToValidatorIndexMap(_ context.Context, _ keymanager.IKeymanager) error {
return nil
}
// SignValidatorRegistrationRequest for mocking
func (_ *FakeValidator) SignValidatorRegistrationRequest(_ context.Context, _ iface.SigningFunc, _ *ethpb.ValidatorRegistrationV1) (*ethpb.SignedValidatorRegistrationV1, error) {
return nil, nil
}

View File

@@ -77,6 +77,7 @@ type validator struct {
duties *ethpb.DutiesResponse
prevBalance map[[fieldparams.BLSPubkeyLength]byte]uint64
pubkeyToValidatorIndex map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex
signedValidatorRegistrations map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1
graffitiOrderedIndex uint64
aggregatedSlotCommitteeIDCache *lru.Cache
domainDataCache *ristretto.Cache
@@ -953,7 +954,7 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
} else {
log.Warn("In order to receive transaction fees from proposing blocks, " +
"you must now specify a configuration known as a fee recipient config. " +
"If it not provided, transaction fees will be burnt. Please see our documentation for more information on this requirement (https://docs.prylabs.network/docs/execution-node/fee-recipient).")
"If it is not provided, transaction fees will be burnt. Please see our documentation for more information on this requirement (https://docs.prylabs.network/docs/execution-node/fee-recipient).")
}
return nil
}
@@ -967,33 +968,44 @@ func (v *validator) PushProposerSettings(ctx context.Context, km keymanager.IKey
if err != nil {
return err
}
feeRecipients, registerValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys)
if len(pubkeys) == 0 {
log.Info("No public keys have been imported. Skipping Push Proposer Settings")
return nil
}
feeRecipients, signedRegisterValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys, km.Sign)
if err != nil {
return err
}
if len(feeRecipients) == 0 {
log.Warnf("no valid validator indices were found, prepare beacon proposer request fee recipients array is empty")
log.Warnf("No valid validator indices were found, prepare beacon proposer request fee recipients array is empty")
return nil
}
if len(feeRecipients) != len(pubkeys) {
log.WithFields(logrus.Fields{
"activePubkeys": len(pubkeys) - len(feeRecipients),
}).Warnln("will not prepare beacon proposer and update fee recipient until a validator index is assigned")
}
if _, err := v.validatorClient.PrepareBeaconProposer(ctx, &ethpb.PrepareBeaconProposerRequest{
Recipients: feeRecipients,
}); err != nil {
return err
}
log.Infoln("Prepared beacon proposer with fee recipient to validator index mapping")
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")
if len(signedRegisterValidatorRequests) != len(pubkeys) {
log.WithFields(logrus.Fields{
"activePubkeys": len(pubkeys) - len(signedRegisterValidatorRequests),
}).Warnln("will not be included in validator registration until a validator index is assigned")
}
if err := SubmitValidatorRegistration(ctx, v.validatorClient, signedRegisterValidatorRequests); err != nil {
return err
}
return nil
}
func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte) ([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, []*ethpb.ValidatorRegistrationV1, error) {
func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [][fieldparams.BLSPubkeyLength]byte, signer iface.SigningFunc) ([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, []*ethpb.SignedValidatorRegistrationV1, error) {
var validatorToFeeRecipients []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer
var registerValidatorRequests []*ethpb.ValidatorRegistrationV1
var signedRegisterValidatorRequests []*ethpb.SignedValidatorRegistrationV1
// need to check for pubkey to validator index mappings
for i, key := range pubkeys {
var enableValidatorRegistration bool
@@ -1009,9 +1021,10 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
}
if !foundIndex {
skipAppendToFeeRecipientArray = true
} else {
validatorIndex = ind
v.pubkeyToValidatorIndex[key] = validatorIndex
}
validatorIndex = ind
v.pubkeyToValidatorIndex[key] = validatorIndex
}
if v.ProposerSettings.DefaultConfig != nil {
feeRecipient = v.ProposerSettings.DefaultConfig.FeeRecipient
@@ -1039,22 +1052,30 @@ func (v *validator) buildProposerSettingsRequests(ctx context.Context, pubkeys [
if hexutil.Encode(feeRecipient.Bytes()) == params.BeaconConfig().EthBurnAddressHex {
log.Warnln("Fee recipient is set to the burn address. You will not be rewarded transaction fees on this setting. Please set a different fee recipient.")
}
// Only include requests with assigned validator index
if !skipAppendToFeeRecipientArray {
validatorToFeeRecipients = append(validatorToFeeRecipients, &ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
ValidatorIndex: validatorIndex,
FeeRecipient: feeRecipient[:],
})
}
if enableValidatorRegistration {
registerValidatorRequests = append(registerValidatorRequests, &ethpb.ValidatorRegistrationV1{
if !skipAppendToFeeRecipientArray && enableValidatorRegistration {
unsignedRequest := &ethpb.ValidatorRegistrationV1{
FeeRecipient: feeRecipient[:],
GasLimit: gasLimit,
Timestamp: uint64(time.Now().UTC().Unix()),
Pubkey: pubkeys[i][:],
})
}
request, err := v.SignValidatorRegistrationRequest(ctx, signer, unsignedRequest)
if err != nil {
//error is logged and skips appending
continue
}
signedRegisterValidatorRequests = append(signedRegisterValidatorRequests, request)
}
}
return validatorToFeeRecipients, registerValidatorRequests, nil
return validatorToFeeRecipients, signedRegisterValidatorRequests, nil
}
func (v *validator) cacheValidatorPubkeyHexToValidatorIndex(ctx context.Context, pubkey [fieldparams.BLSPubkeyLength]byte) (types.ValidatorIndex, bool, error) {

View File

@@ -1460,17 +1460,19 @@ func TestValidator_PushProposerSettings(t *testing.T) {
feeRecipientMap map[types.ValidatorIndex]string
mockExpectedRequests []ExpectedValidatorRegistration
err string
logMessages []string
}{
{
name: " Happy Path proposer config not nil",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 2,
Offset: 1,
@@ -1545,11 +1547,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 2,
Offset: 1,
@@ -1620,11 +1623,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1682,11 +1686,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1740,10 +1745,11 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1759,11 +1765,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1804,10 +1811,11 @@ func TestValidator_PushProposerSettings(t *testing.T) {
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1840,10 +1848,11 @@ func TestValidator_PushProposerSettings(t *testing.T) {
name: "Before Bellatrix returns nil",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1859,11 +1868,12 @@ func TestValidator_PushProposerSettings(t *testing.T) {
name: "register validator batch failed",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
useWeb: false,
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 1,
Offset: 1,
@@ -1913,16 +1923,89 @@ func TestValidator_PushProposerSettings(t *testing.T) {
},
err: "could not submit signed registrations to beacon node",
},
{
name: "Validator Index Not found with validator registration",
validatorSetter: func(t *testing.T) *validator {
v := validator{
validatorClient: client,
node: nodeClient,
db: db,
pubkeyToValidatorIndex: make(map[[fieldparams.BLSPubkeyLength]byte]types.ValidatorIndex),
signedValidatorRegistrations: make(map[[fieldparams.BLSPubkeyLength]byte]*ethpb.SignedValidatorRegistrationV1),
useWeb: false,
interopKeysConfig: &local.InteropKeymanagerConfig{
NumValidatorKeys: 2,
Offset: 1,
},
genesisTime: 0,
}
// set bellatrix as current epoch
params.BeaconConfig().BellatrixForkEpoch = 0
err := v.WaitForKeymanagerInitialization(ctx)
require.NoError(t, err)
km, err := v.Keymanager()
require.NoError(t, err)
keys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
v.ProposerSettings = &validatorserviceconfig.ProposerSettings{
ProposeConfig: nil,
DefaultConfig: &validatorserviceconfig.ProposerOption{
FeeRecipient: common.HexToAddress(defaultFeeHex),
ValidatorRegistration: &validatorserviceconfig.ValidatorRegistration{
Enable: true,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
}
client.EXPECT().ValidatorIndex(
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[0][:]},
).Return(&ethpb.ValidatorIndexResponse{
Index: 1,
}, nil)
client.EXPECT().ValidatorIndex(
gomock.Any(), // ctx
&ethpb.ValidatorIndexRequest{PublicKey: keys[1][:]},
).Times(2).Return(nil, errors.New("Could not find validator index"))
client.EXPECT().SubmitValidatorRegistration(
gomock.Any(),
gomock.Any(),
).Return(&empty.Empty{}, nil)
client.EXPECT().PrepareBeaconProposer(gomock.Any(), &ethpb.PrepareBeaconProposerRequest{
Recipients: []*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer{
{FeeRecipient: common.HexToAddress(defaultFeeHex).Bytes(), ValidatorIndex: 1},
},
}).Return(nil, nil)
return &v
},
feeRecipientMap: map[types.ValidatorIndex]string{
1: defaultFeeHex,
},
mockExpectedRequests: []ExpectedValidatorRegistration{
{
FeeRecipient: byteValueAddress,
GasLimit: params.BeaconConfig().DefaultBuilderGasLimit,
},
},
logMessages: []string{
"prepare beacon proposer and update fee recipient until a validator index is assigned",
"not be included in validator registration until a validator index is assigned",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hook := logTest.NewGlobal()
v := tt.validatorSetter(t)
km, err := v.Keymanager()
require.NoError(t, err)
pubkeys, err := km.FetchValidatingPublicKeys(ctx)
require.NoError(t, err)
if tt.feeRecipientMap != nil {
feeRecipients, registerValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys)
feeRecipients, signedRegisterValidatorRequests, err := v.buildProposerSettingsRequests(ctx, pubkeys, km.Sign)
require.NoError(t, err)
for _, recipient := range feeRecipients {
require.Equal(t, strings.ToLower(tt.feeRecipientMap[recipient.ValidatorIndex]), strings.ToLower(hexutil.Encode(recipient.FeeRecipient)))
@@ -1934,15 +2017,22 @@ func TestValidator_PushProposerSettings(t *testing.T) {
}
// check if Pubkeys are always unique
var unique = make(map[string]bool)
for _, request := range registerValidatorRequests {
require.Equal(t, unique[common.BytesToAddress(request.Pubkey).Hex()], false)
unique[common.BytesToAddress(request.Pubkey).Hex()] = true
for _, request := range signedRegisterValidatorRequests {
require.Equal(t, unique[common.BytesToAddress(request.Message.Pubkey).Hex()], false)
unique[common.BytesToAddress(request.Message.Pubkey).Hex()] = true
}
require.Equal(t, len(tt.mockExpectedRequests), len(registerValidatorRequests))
require.Equal(t, len(tt.mockExpectedRequests), len(signedRegisterValidatorRequests))
require.Equal(t, len(signedRegisterValidatorRequests), len(v.signedValidatorRegistrations))
}
if err := v.PushProposerSettings(ctx, km); tt.err != "" {
assert.ErrorContains(t, tt.err, err)
}
if len(tt.logMessages) > 0 {
for _, message := range tt.logMessages {
assert.LogsContain(t, hook, message)
}
}
})
}
}

View File

@@ -476,10 +476,6 @@ func web3SignerConfig(cliCtx *cli.Context) (*remoteweb3signer.SetupConfig, error
func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSettings, error) {
var fileConfig *validatorServiceConfig.ProposerSettingsPayload
//TODO(10809): remove when fully deprecated
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) && cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
return nil, fmt.Errorf("cannot specify both --%s and --%s", flags.FeeRecipientConfigFileFlag.Name, flags.FeeRecipientConfigURLFlag.Name)
}
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) && cliCtx.IsSet(flags.ProposerSettingsURLFlag.Name) {
return nil, errors.New("cannot specify both " + flags.ProposerSettingsFlag.Name + " and " + flags.ProposerSettingsURLFlag.Name)
@@ -506,14 +502,6 @@ func proposerSettings(cliCtx *cli.Context) (*validatorServiceConfig.ProposerSett
}
}
if cliCtx.IsSet(flags.FeeRecipientConfigFileFlag.Name) {
return nil, errors.New(flags.FeeRecipientConfigFileFlag.Usage)
}
if cliCtx.IsSet(flags.FeeRecipientConfigURLFlag.Name) {
return nil, errors.New(flags.FeeRecipientConfigURLFlag.Usage)
}
if cliCtx.IsSet(flags.ProposerSettingsFlag.Name) {
if err := unmarshalFromFile(cliCtx.Context, cliCtx.String(flags.ProposerSettingsFlag.Name), &fileConfig); err != nil {
return nil, err

File diff suppressed because one or more lines are too long