Compare commits

...

49 Commits

Author SHA1 Message Date
terence tsao
3c6efd785f Unlock if attribute is empty 2024-01-05 18:17:08 -08:00
Potuz
b4ecb90a67 lint 2024-01-05 19:34:26 -03:00
Potuz
d66d918fa6 Pass fcuArgs as arguments 2024-01-05 19:34:26 -03:00
Potuz
449438b414 return early if the incoming block does not change head 2024-01-05 19:34:26 -03:00
Potuz
08dc279ea9 move log for non-canonical block 2024-01-05 19:34:26 -03:00
Potuz
d43540a00f add unit tests 2024-01-05 19:34:26 -03:00
Potuz
2b2fde4732 check for empty payload instead of only nil 2024-01-05 19:34:26 -03:00
Potuz
fad88dce57 call with attributes only when incoming block is canonical 2024-01-05 19:34:26 -03:00
Potuz
03614bd42a Remove calls for tracked proposer
getPayloadAttribute already takes care of this
Also compute correctly the time into voting window
2024-01-05 19:34:26 -03:00
Potuz
a39f9cce1a Break out several helpers from postBlockProcess
In addition fix a bug found by @terencechain where we should use a slot
context instead of the parent context in the second FCU call.
2024-01-05 19:34:26 -03:00
james-prysm
d3d25e3ae5 proposer and attester slashing sse (#13414)
* wip

* adding in event notifiers for slashing events

* fixing tests
2024-01-05 15:27:50 +00:00
Nishant Das
929e9ddf4c enable it (#13421) 2024-01-05 05:23:22 +00:00
Nishant Das
7c0e79d432 Make New Engine Methods The Permanent Default (#13406)
* make them the default

* gaz

* fix tests
2024-01-05 04:38:04 +00:00
terence
3c1c0b3c00 Update blob pruning log (#13417) 2024-01-04 18:02:19 +00:00
james-prysm
d439e6da74 adding builder boost factor to get block v3 (#13409)
* adding builder boost factor to functions

* gaz

* fixing linting

* fixing unit tests

* gaz

* addressing review comments

* fixing tests

* addressing review feedback

* gaz

* changing log based on review
2024-01-04 17:25:18 +00:00
Radosław Kapka
e68b2821c1 REST VC: Subscribe to Beacon API events (#13354)
* Initial code for head event streaming

* handle events and error

* keepalive event

* tests

* generate new mock

* remove single case select

* cleanup

* explain eventByteLimit

* use 2 channels in test

* review

* more review

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2024-01-04 17:14:45 +00:00
Potuz
cfef8f4676 Don't hardcode 4 seconds in forkchoice (#13416) 2024-01-04 16:49:16 +00:00
terence
9709412511 Use Afero Walk for Pruning Blob (#13410)
* Use Afero walk

* Return err

* Use wrap

* More err to the end

* Fix loop
2024-01-04 16:41:00 +00:00
james-prysm
7781eb60f4 Add rpc trigger for blob sidecar event (#13411)
* adding missed rpc trigger for blob sidecar event

* fixing unit tests

* moving event feed to after receive blob call to prioritize db
2024-01-04 14:22:24 +00:00
Potuz
396b8bf970 Simplify fcu 4 (#13403)
* send two FCU when proposing

* compute voting window at runtime
2024-01-04 13:43:57 +00:00
Nishant Das
d5107942a1 update it (#13415) 2024-01-04 11:22:23 +00:00
terence
bd4a520013 Initialize blob storage without pruning (#13412) 2024-01-04 05:56:38 +00:00
Sammy Rosso
a0ff1351a0 Fix batch pruning errors (#13355)
* Add compareAndSwap

* Update lastPrunedEpoch before prune

* Fix and test

* Remove debug log

* Kasey' review

* Fix tests

* Address Kasey's comments

* Fix prune before slot

* Rename

* Fix bad test

---------

Co-authored-by: terence tsao <terence@prysmaticlabs.com>
2024-01-03 20:52:07 +00:00
Nishant Das
7e6fd5fd8b Make Reorging Of Late Blocks The Permanent Default (#13405)
* make it the permanent default

* gaz

* fix merge conflicts
2024-01-03 14:46:58 +00:00
Nishant Das
d984210baa fix it (#13404) 2024-01-03 13:54:18 +00:00
Potuz
31c72672d7 Remove the getPayloadAttribute call from updateForkchoiceWithExecution (#13402)
* Remove the getPayloadAttribute call from updateForkchoiceWithExecution

* Move log
2024-01-03 12:43:40 +00:00
Potuz
8c1e180dd1 Simplify fcu 2 (#13400)
* change getPayloadAttribute signature

* Unify different FCU arguments
2024-01-02 22:45:55 +00:00
Manu NALEPA
886d76fe7c Refactor validator client help. (#13401)
* Define `cli.App` without mutation.

No functional change.

* `usage.go`:  Clean `appHelpTemplate`.

No functional change is added.
Modifications consist in adding prefix/suffix `-` to improve readability of
the template without adding new lines in template inference.

We now see some inconsistencies of the template:
- `if .App.Version` is around the `AUTHOR` section.
- `if .App.Copyright` is around both `COPYRIGHT` and `VERSION` sections.
- `if len .App.Authors` is around nothing.

* `usage.go`: Surround version and author correctly.

* `usage.go`: `AUTHOR` ==> `AUTHORS`

* `usage.go`: `GLOBAL` --> `global`.

* `--grpc-max-msg-size`: Remove double default.

* VC: Standardize help message.

- Flags help begin with a capital letter and end with a period.
- If a flag help begins with a verb, it is conjugated.
- Expermitemtal, danger etc... mentions are between parenthesis.

* VC help message: Wrap too long lines.
2024-01-02 18:02:28 +00:00
Potuz
a602acf492 Remove getPayloadAttributes from FCU call (#13399) 2024-01-02 17:37:18 +00:00
terence
1b6547de6a Add Goerli Deneb Fork Epoch (#13390)
* Add deneb fork epoch

* Fix test
2024-01-02 15:31:57 +00:00
Nishant Das
88685bb3bd Fix Up Builder Evaluator (#13395)
* fix it up

* fix evaluator

* fix evaluator again

* fix it

* gaz
2024-01-02 10:40:26 +00:00
Nishant Das
2319b7d4bd increase params (#13398) 2024-01-02 10:19:59 +00:00
Manu NALEPA
82b2840d68 --validatorS-registration-batch-size (add s) (#13396) 2024-01-02 09:52:14 +00:00
Manu NALEPA
cf221d0f4c Validator client: Always use the --datadir value. (#13392)
Fix https://github.com/prysmaticlabs/prysm/issues/13391
2024-01-02 09:24:24 +00:00
Preston Van Loon
0956e3a657 Update libp2p/go-libp2p-asn-util to v0.4.1 (#13370)
* Update go-libp2p-asn-util to v0.4.1

* fix go mod

---------

Co-authored-by: nisdas <nishdas93@gmail.com>
2024-01-02 07:30:50 +00:00
terence
351ed1c511 Check kzg commitment count from builder (#13394) 2024-01-02 06:50:23 +00:00
Potuz
9809f5ac77 Simplify fcu 1 (#13387)
* Remove unsafe proposer indices cache

* Simplify FCU #1

This PR starts the process of gradually simplifying FCU
It removes the responsibility of getting the state and block from this
function and informing if head has changed. It is only called when the
imported block has actually become head.

* Add a call to FCU in edge cases
2023-12-30 12:20:20 +00:00
Potuz
cff5e2b5fe Remove unsafe proposer indices cache (#13385) 2023-12-30 12:20:02 +00:00
terence
dd15f9e0cc Rewrite ProposeBlock endpoint (#13380)
* Init

* Tests

* Init

* Tests

* Radek's feedback

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* More Radek's feedback

* Potuz feedback

* Use inline copy

* Fix conflict

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2023-12-29 23:32:58 +00:00
terence
1c9ded4684 Remove blind field from block type (#13389)
* Init

* Init

* Fix tests
2023-12-29 21:28:19 +00:00
Potuz
d4cc6fcf4a update shuffling caches before calling FCU on epoch boundaries (#13383)
* update shuffling caches before calling FCU on epoch boundaries

* Terence's review
2023-12-28 15:19:09 +00:00
Radosław Kapka
49c16f1a71 Return SignedBeaconBlock from ReadOnlySignedBeaconBlock.Copy (#13386) 2023-12-28 08:55:45 +00:00
terence
e70b606e78 Replace validator count with validator indices in update fee recipient log (#13384)
* Add validator count to updated fee recipient address log

* Add validator count to updated fee recipient address log

* Replace
2023-12-27 16:46:15 +00:00
Potuz
0e8b37c317 Log value of local payload when proposing (#13381) 2023-12-27 14:43:32 +00:00
Potuz
e80db9554d Use advanced epoch cache when preparing proposals (#13377) 2023-12-27 12:42:51 +00:00
Radosław Kapka
d0bf03e863 Simplify error handling for JsonRestHandler (#13369)
* Simplify error handling for `JsonRestHandler`

* POST

* reduce complexity

* review feedback

* uncomment route

* fix rest of tests
2023-12-22 22:39:20 +00:00
Potuz
b7e0819f00 refactor Payload Id caches (#12987)
* init

- getLocalPayload does not use the proposer ID from the cache but takes
  it from the block

- Fixed tests in blockchain package
- Fixed tests in the RPC package
- Fixed spectests

EpochProposers takes 256 bytes that can be avoided to be copied, but
this optimization is not clear to be worth it.

assginmentStatus can be optimized to use the cached version from the
TrackedValidatorsCache

We shouldn't cache the proposer duties when calling getDuties but when
we update the epoch boundary instead

* track validators on prepare proposers

* more rpc tests

* more rpc tests

* initialize grpc caches

* Add back fcu log

Also fix two existing bugs wrong parent hash on pre Capella and wrong
blockhashes on altair

* use beacon default fee recipient if there is none in the vc

* fix validator test

* radek's review

* push always proposer settings even if no flag is specified in the VC

* Only register with the builder if the VC flag is set

Great find by @terencechain

* add regression test

* Radek's review

* change signature of registration builder
2023-12-22 18:47:51 +00:00
Radosław Kapka
7d64104003 block publishing (#13376) 2023-12-22 18:15:00 +00:00
Nishant Das
b1e8a9ea3d fix it with regression (#13375) 2023-12-22 12:33:23 +00:00
211 changed files with 4191 additions and 3341 deletions

View File

@@ -322,6 +322,22 @@ filegroup(
url = "https://github.com/eth-clients/eth2-networks/archive/7b4897888cebef23801540236f73123e21774954.tar.gz",
)
http_archive(
name = "goerli_testnet",
build_file_content = """
filegroup(
name = "configs",
srcs = [
"prater/config.yaml",
],
visibility = ["//visibility:public"],
)
""",
sha256 = "43fc0f55ddff7b511713e2de07aa22846a67432df997296fb4fc09cd8ed1dcdb",
strip_prefix = "goerli-6522ac6684693740cd4ddcc2a0662e03702aa4a1",
url = "https://github.com/eth-clients/goerli/archive/6522ac6684693740cd4ddcc2a0662e03702aa4a1.tar.gz",
)
http_archive(
name = "holesky_testnet",
build_file_content = """

View File

@@ -275,7 +275,24 @@ func TestClient_GetHeader(t *testing.T) {
require.Equal(t, len(kcgCommitments[i]) == 48, true)
}
})
t.Run("deneb, too many kzg commitments", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
require.Equal(t, expectedPath, r.URL.Path)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBufferString(testExampleHeaderResponseDenebTooManyBlobs)),
Request: r.Clone(ctx),
}, nil
}),
}
c := &Client{
hc: hc,
baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"},
}
_, err := c.GetHeader(ctx, slot, bytesutil.ToBytes32(parentHash), bytesutil.ToBytes48(pubkey))
require.ErrorContains(t, "could not extract proto message from header: too many blob commitments: 7", err)
})
t.Run("unsupported version", func(t *testing.T) {
hc := &http.Client{
Transport: roundtrip(func(r *http.Request) (*http.Response, error) {
@@ -412,7 +429,7 @@ func TestSubmitBlindedBlock(t *testing.T) {
require.ErrorContains(t, "not a bellatrix payload", err)
})
t.Run("not blinded", func(t *testing.T) {
sbb, err := blocks.NewSignedBeaconBlock(&eth.SignedBeaconBlockBellatrix{Block: &eth.BeaconBlockBellatrix{Body: &eth.BeaconBlockBodyBellatrix{}}})
sbb, err := blocks.NewSignedBeaconBlock(&eth.SignedBeaconBlockBellatrix{Block: &eth.BeaconBlockBellatrix{Body: &eth.BeaconBlockBodyBellatrix{ExecutionPayload: &v1.ExecutionPayload{}}}})
require.NoError(t, err)
_, _, err = (&Client{}).SubmitBlindedBlock(ctx, sbb)
require.ErrorIs(t, err, errNotBlinded)

View File

@@ -908,6 +908,9 @@ func (bb *BuilderBidDeneb) ToProto() (*eth.BuilderBidDeneb, error) {
if err != nil {
return nil, err
}
if len(bb.BlobKzgCommitments) > fieldparams.MaxBlobsPerBlock {
return nil, fmt.Errorf("too many blob commitments: %d", len(bb.BlobKzgCommitments))
}
kzgCommitments := make([][]byte, len(bb.BlobKzgCommitments))
for i, commit := range bb.BlobKzgCommitments {
if len(commit) != fieldparams.BLSPubkeyLength {

View File

@@ -209,6 +209,39 @@ var testExampleHeaderResponseUnknownVersion = `{
}
}`
var testExampleHeaderResponseDenebTooManyBlobs = `{
"version": "deneb",
"data": {
"message": {
"header": {
"parent_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"fee_recipient": "0xabcf8e0d4e9587369b2301d0790347320302cc09",
"state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"receipts_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"prev_randao": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"block_number": "1",
"gas_limit": "1",
"gas_used": "1",
"timestamp": "1",
"extra_data": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"base_fee_per_gas": "452312848583266388373324160190187140051835877600158453279131187530910662656",
"block_hash": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"transactions_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"withdrawals_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
"blob_gas_used": "1",
"excess_blob_gas": "2"
},
"blob_kzg_commitments": [
"","","","","","",""
],
"value": "652312848583266388373324160190187140051835877600158453279131187530910662656",
"pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a"
},
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}
}`
func TestExecutionHeaderResponseUnmarshal(t *testing.T) {
hr := &ExecHeaderResponse{}
require.NoError(t, json.Unmarshal([]byte(testExampleHeaderResponse), hr))

View File

@@ -7,4 +7,5 @@ const (
ConsensusBlockValueHeader = "Eth-Consensus-Block-Value"
JsonMediaType = "application/json"
OctetStreamMediaType = "application/octet-stream"
EventStreamMediaType = "text/event-stream"
)

View File

@@ -26,6 +26,7 @@ go_library(
"receive_blob.go",
"receive_block.go",
"service.go",
"tracked_proposer.go",
"weak_subjectivity_checks.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain",
@@ -52,7 +53,6 @@ go_library(
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/filesystem:go_default_library",
"//beacon-chain/db/filters:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/execution:go_default_library",
"//beacon-chain/forkchoice:go_default_library",
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",

View File

@@ -7,11 +7,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/features"
@@ -32,21 +32,18 @@ const blobCommitmentVersionKZG uint8 = 0x01
var defaultLatestValidHash = bytesutil.PadTo([]byte{0xff}, 32)
// notifyForkchoiceUpdateArg is the argument for the forkchoice update notification `notifyForkchoiceUpdate`.
type notifyForkchoiceUpdateArg struct {
headState state.BeaconState
headRoot [32]byte
headBlock interfaces.ReadOnlyBeaconBlock
}
// notifyForkchoiceUpdate signals execution engine the fork choice updates. Execution engine should:
// 1. Re-organizes the execution payload chain and corresponding state to make head_block_hash the head.
// 2. Applies finality to the execution state: it irreversibly persists the chain of all execution payloads and corresponding state, up to and including finalized_block_hash.
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkchoiceUpdateArg) (*enginev1.PayloadIDBytes, error) {
func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*enginev1.PayloadIDBytes, error) {
ctx, span := trace.StartSpan(ctx, "blockChain.notifyForkchoiceUpdate")
defer span.End()
headBlk := arg.headBlock
if arg.headBlock.IsNil() {
log.Error("Head block is nil")
return nil, nil
}
headBlk := arg.headBlock.Block()
if headBlk == nil || headBlk.IsNil() || headBlk.Body().IsNil() {
log.Error("Head block is nil")
return nil, nil
@@ -72,11 +69,10 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
SafeBlockHash: justifiedHash[:],
FinalizedBlockHash: finalizedHash[:],
}
nextSlot := s.CurrentSlot() + 1 // Cache payload ID for next slot proposer.
hasAttr, attr, proposerId := s.getPayloadAttribute(ctx, arg.headState, nextSlot, arg.headRoot[:])
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, attr)
if arg.attributes == nil {
arg.attributes = payloadattribute.EmptyWithVersion(headBlk.Version())
}
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, arg.attributes)
if err != nil {
switch err {
case execution.ErrAcceptedSyncingPayloadStatus:
@@ -122,10 +118,11 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
log.WithError(err).Error("Could not get head state")
return nil, nil
}
pid, err := s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
headState: st,
headRoot: r,
headBlock: b.Block(),
pid, err := s.notifyForkchoiceUpdate(ctx, &fcuConfig{
headState: st,
headRoot: r,
headBlock: b,
attributes: arg.attributes,
})
if err != nil {
return nil, err // Returning err because it's recursive here.
@@ -153,7 +150,9 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
log.WithError(err).Error("Could not set head root to valid")
return nil, nil
}
// If the forkchoice update call has an attribute, update the proposer payload ID cache.
// If the forkchoice update call has an attribute, update the payload ID cache.
hasAttr := arg.attributes != nil && !arg.attributes.IsEmpty()
nextSlot := s.CurrentSlot() + 1
if hasAttr && payloadID != nil {
var pId [8]byte
copy(pId[:], payloadID[:])
@@ -162,7 +161,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
"headSlot": headBlk.Slot(),
"payloadID": fmt.Sprintf("%#x", bytesutil.Trunc(payloadID[:])),
}).Info("Forkchoice updated with payload attributes for proposal")
s.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nextSlot, proposerId, pId, arg.headRoot)
s.cfg.PayloadIDCache.Set(nextSlot, arg.headRoot, pId)
} else if hasAttr && payloadID == nil && !features.Get().PrepareAllPayloads {
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
@@ -277,56 +276,50 @@ func (s *Service) pruneInvalidBlock(ctx context.Context, root, parentRoot, lvh [
// getPayloadAttributes returns the payload attributes for the given state and slot.
// The attribute is required to initiate a payload build process in the context of an `engine_forkchoiceUpdated` call.
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot primitives.Slot, headRoot []byte) (bool, payloadattribute.Attributer, primitives.ValidatorIndex) {
func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState, slot primitives.Slot, headRoot []byte) payloadattribute.Attributer {
emptyAttri := payloadattribute.EmptyWithVersion(st.Version())
// Root is `[32]byte{}` since we are retrieving proposer ID of a given slot. During insertion at assignment the root was not known.
proposerID, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
if !ok && !features.Get().PrepareAllPayloads { // There's no need to build attribute if there is no proposer for slot.
return false, emptyAttri, 0
}
// Get previous randao.
// If it is an epoch boundary then process slots to get the right
// shuffling before checking if the proposer is tracked. Otherwise
// perform this check before. This is cheap as the NSC has already been updated.
var val cache.TrackedValidator
var ok bool
e := slots.ToEpoch(slot)
stateEpoch := slots.ToEpoch(st.Slot())
if e == stateEpoch {
val, ok = s.trackedProposer(st, slot)
if !ok {
return emptyAttri
}
}
st = st.Copy()
if slot > st.Slot() {
var err error
st, err = transition.ProcessSlotsUsingNextSlotCache(ctx, st, headRoot, slot)
if err != nil {
log.WithError(err).Error("Could not process slots to get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
}
if e > stateEpoch {
emptyAttri := payloadattribute.EmptyWithVersion(st.Version())
val, ok = s.trackedProposer(st, slot)
if !ok {
return emptyAttri
}
}
// Get previous randao.
prevRando, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
log.WithError(err).Error("Could not get randao mix to get payload attribute")
return false, emptyAttri, 0
}
// Get fee recipient.
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := s.cfg.BeaconDB.FeeRecipientByValidatorID(ctx, proposerID)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": proposerID,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +
"Please refer to our documentation for instructions")
}
case err != nil:
log.WithError(err).Error("Could not get fee recipient to get payload attribute")
return false, emptyAttri, 0
default:
feeRecipient = recipient
return emptyAttri
}
// Get timestamp.
t, err := slots.ToTime(uint64(s.genesisTime.Unix()), slot)
if err != nil {
log.WithError(err).Error("Could not get timestamp to get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
var attr payloadattribute.Attributer
@@ -335,51 +328,51 @@ func (s *Service) getPayloadAttribute(ctx context.Context, st state.BeaconState,
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
Timestamp: uint64(t.Unix()),
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
Withdrawals: withdrawals,
ParentBeaconBlockRoot: headRoot,
})
if err != nil {
log.WithError(err).Error("Could not get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
case version.Capella:
withdrawals, err := st.ExpectedWithdrawals()
if err != nil {
log.WithError(err).Error("Could not get expected withdrawals to get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
Timestamp: uint64(t.Unix()),
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
Withdrawals: withdrawals,
})
if err != nil {
log.WithError(err).Error("Could not get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
case version.Bellatrix:
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
Timestamp: uint64(t.Unix()),
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
})
if err != nil {
log.WithError(err).Error("Could not get payload attribute")
return false, emptyAttri, 0
return emptyAttri
}
default:
log.WithField("version", st.Version()).Error("Could not get payload attribute due to unknown state version")
return false, emptyAttri, 0
return emptyAttri
}
return true, attr, proposerID
return attr
}
// removeInvalidBlockAndState removes the invalid block, blob and its corresponding state from the cache and DB.

View File

@@ -26,11 +26,10 @@ import (
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
@@ -57,11 +56,14 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
require.NoError(t, err)
require.NoError(t, fcs.InsertNode(ctx, state, blkRoot))
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
sb := &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
},
},
})
}
b, err := consensusblocks.NewSignedBeaconBlock(sb)
require.NoError(t, err)
pid := &v1.PayloadIDBytes{1}
@@ -73,20 +75,20 @@ func Test_NotifyForkchoiceUpdate_GetPayloadAttrErrorCanContinue(t *testing.T) {
// Intentionally generate a bad state such that `hash_tree_root` fails during `process_slot`
s, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{})
require.NoError(t, err)
arg := &notifyForkchoiceUpdateArg{
arg := &fcuConfig{
headState: s,
headRoot: [32]byte{},
headBlock: b,
}
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(1, 0, [8]byte{}, [32]byte{})
service.cfg.PayloadIDCache.Set(1, [32]byte{}, [8]byte{})
got, err := service.notifyForkchoiceUpdate(ctx, arg)
require.NoError(t, err)
require.DeepEqual(t, got, pid) // We still get a payload ID even though the state is bad. This means it returns until the end.
}
func Test_NotifyForkchoiceUpdate(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
@@ -114,7 +116,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
tests := []struct {
name string
blk interfaces.ReadOnlyBeaconBlock
blk interfaces.ReadOnlySignedBeaconBlock
headRoot [32]byte
finalizedRoot [32]byte
justifiedRoot [32]byte
@@ -123,24 +125,24 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
}{
{
name: "phase0 block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlock{Body: &ethpb.BeaconBlockBody{}})
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{Body: &ethpb.BeaconBlockBody{}}})
require.NoError(t, err)
return b
}(),
},
{
name: "altair block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockAltair{Body: &ethpb.BeaconBlockBodyAltair{}})
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockAltair{Block: &ethpb.BeaconBlockAltair{Body: &ethpb.BeaconBlockBodyAltair{}}})
require.NoError(t, err)
return b
}(),
},
{
name: "not execution block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{
ParentHash: make([]byte, fieldparams.RootLength),
@@ -153,19 +155,19 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
BlockHash: make([]byte, fieldparams.RootLength),
},
},
})
}})
require.NoError(t, err)
return b
}(),
},
{
name: "happy case: finalized root is altair block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
},
})
}})
require.NoError(t, err)
return b
}(),
@@ -174,12 +176,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
},
{
name: "happy case: finalized root is bellatrix block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
},
})
}})
require.NoError(t, err)
return b
}(),
@@ -188,12 +190,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
},
{
name: "forkchoice updated with optimistic block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
},
})
}})
require.NoError(t, err)
return b
}(),
@@ -203,12 +205,12 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
},
{
name: "forkchoice updated with invalid block",
blk: func() interfaces.ReadOnlyBeaconBlock {
b, err := consensusblocks.NewBeaconBlock(&ethpb.BeaconBlockBellatrix{
blk: func() interfaces.ReadOnlySignedBeaconBlock {
b, err := consensusblocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{
ExecutionPayload: &v1.ExecutionPayload{},
},
})
}})
require.NoError(t, err)
return b
}(),
@@ -226,7 +228,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
st, _ := util.DeterministicGenesisState(t, 1)
require.NoError(t, beaconDB.SaveState(ctx, st, tt.finalizedRoot))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, tt.finalizedRoot))
arg := &notifyForkchoiceUpdateArg{
arg := &fcuConfig{
headState: st,
headRoot: tt.headRoot,
headBlock: tt.blk,
@@ -246,7 +248,7 @@ func Test_NotifyForkchoiceUpdate(t *testing.T) {
}
func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
// Prepare blocks
@@ -306,9 +308,9 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
require.NoError(t, beaconDB.SaveState(ctx, st, bra))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bra))
a := &notifyForkchoiceUpdateArg{
a := &fcuConfig{
headState: st,
headBlock: wbd.Block(),
headBlock: wbd,
headRoot: brd,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
@@ -334,7 +336,7 @@ func Test_NotifyForkchoiceUpdate_NIlLVH(t *testing.T) {
// 3. the blockchain package calls fcu to obtain heads G -> F -> D.
func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
// Prepare blocks
@@ -443,9 +445,9 @@ func Test_NotifyForkchoiceUpdateRecursive_DoublyLinkedTree(t *testing.T) {
require.NoError(t, beaconDB.SaveState(ctx, st, bra))
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, bra))
a := &notifyForkchoiceUpdateArg{
a := &fcuConfig{
headState: st,
headBlock: wbg.Block(),
headBlock: wbg,
headRoot: brg,
}
_, err = service.notifyForkchoiceUpdate(ctx, a)
@@ -467,7 +469,7 @@ func Test_NotifyNewPayload(t *testing.T) {
cfg := params.BeaconConfig()
cfg.TerminalTotalDifficulty = "2"
params.OverrideBeaconConfig(cfg)
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, fcs := tr.ctx, tr.fcs
phase0State, _ := util.DeterministicGenesisState(t, 1)
@@ -709,7 +711,7 @@ func Test_NotifyNewPayload_SetOptimisticToValid(t *testing.T) {
cfg.TerminalTotalDifficulty = "2"
params.OverrideBeaconConfig(cfg)
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
bellatrixState, _ := util.DeterministicGenesisStateBellatrix(t, 2)
@@ -777,83 +779,70 @@ func Test_reportInvalidBlock(t *testing.T) {
}
func Test_GetPayloadAttribute(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, false, hasPayload)
require.Equal(t, primitives.ValidatorIndex(0), vId)
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, true, attr.IsEmpty())
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
// Cache hit, advance state, no fee recipient
suggestedVid := primitives.ValidatorIndex(1)
slot := primitives.Slot(1)
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hook := logTest.NewGlobal()
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
}
func Test_GetPayloadAttribute_PrepareAllPayloads(t *testing.T) {
hook := logTest.NewGlobal()
resetCfg := features.InitWithReset(&features.Flags{
PrepareAllPayloads: true,
})
defer resetCfg()
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, true, hasPayload)
require.Equal(t, primitives.ValidatorIndex(0), vId)
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
}
func Test_GetPayloadAttributeV2(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
st, _ := util.DeterministicGenesisStateCapella(t, 1)
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, false, hasPayload)
require.Equal(t, primitives.ValidatorIndex(0), vId)
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, true, attr.IsEmpty())
// Cache hit, advance state, no fee recipient
suggestedVid := primitives.ValidatorIndex(1)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
slot := primitives.Slot(1)
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hook := logTest.NewGlobal()
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
a, err := attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
a, err = attr.Withdrawals()
require.NoError(t, err)
@@ -861,35 +850,30 @@ func Test_GetPayloadAttributeV2(t *testing.T) {
}
func Test_GetPayloadAttributeDeneb(t *testing.T) {
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
st, _ := util.DeterministicGenesisStateDeneb(t, 1)
hasPayload, _, vId := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, false, hasPayload)
require.Equal(t, primitives.ValidatorIndex(0), vId)
attr := service.getPayloadAttribute(ctx, st, 0, []byte{})
require.Equal(t, true, attr.IsEmpty())
// Cache hit, advance state, no fee recipient
suggestedVid := primitives.ValidatorIndex(1)
slot := primitives.Slot(1)
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hook := logTest.NewGlobal()
hasPayload, attr, vId := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(attr.SuggestedFeeRecipient()).String())
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
a, err := attr.Withdrawals()
require.NoError(t, err)
require.Equal(t, 0, len(a))
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
require.NoError(t, service.cfg.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, []primitives.ValidatorIndex{suggestedVid}, []common.Address{suggestedAddr}))
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, suggestedVid, [8]byte{}, [32]byte{})
hasPayload, attr, vId = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, true, hasPayload)
require.Equal(t, suggestedVid, vId)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
attr = service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:])
require.Equal(t, false, attr.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(attr.SuggestedFeeRecipient()))
a, err = attr.Withdrawals()
require.NoError(t, err)
@@ -1112,3 +1096,35 @@ func TestKZGCommitmentToVersionedHashes(t *testing.T) {
require.Equal(t, vhs[0].String(), vh0)
require.Equal(t, vhs[1].String(), vh1)
}
func TestComputePayloadAttribute(t *testing.T) {
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: 0})
// Cache hit, advance state, no fee recipient
slot := primitives.Slot(1)
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
cfg := &postBlockProcessConfig{
ctx: ctx,
blockRoot: [32]byte{'a'},
}
fcu := &fcuConfig{
headState: st,
proposingSlot: slot,
headRoot: [32]byte{},
}
require.NoError(t, service.computePayloadAttributes(cfg, fcu))
require.Equal(t, false, fcu.attributes.IsEmpty())
require.Equal(t, params.BeaconConfig().EthBurnAddressHex, common.BytesToAddress(fcu.attributes.SuggestedFeeRecipient()).String())
// Cache hit, advance state, has fee recipient
suggestedAddr := common.HexToAddress("123")
service.cfg.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, FeeRecipient: primitives.ExecutionAddress(suggestedAddr), Index: 0})
service.cfg.PayloadIDCache.Set(slot, [32]byte{}, [8]byte{})
require.NoError(t, service.computePayloadAttributes(cfg, fcu))
require.Equal(t, false, fcu.attributes.IsEmpty())
require.Equal(t, suggestedAddr, common.BytesToAddress(fcu.attributes.SuggestedFeeRecipient()))
}

View File

@@ -8,20 +8,15 @@ import (
"github.com/pkg/errors"
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/features"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
payloadattribute "github.com/prysmaticlabs/prysm/v4/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
func (s *Service) isNewProposer(slot primitives.Slot) bool {
_, _, ok := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, [32]byte{} /* root */)
return ok || features.Get().PrepareAllPayloads
}
func (s *Service) isNewHead(r [32]byte) bool {
s.headLock.RLock()
defer s.headLock.RUnlock()
@@ -49,48 +44,69 @@ func (s *Service) getStateAndBlock(ctx context.Context, r [32]byte) (state.Beaco
return headState, newHeadBlock, nil
}
type fcuConfig struct {
headState state.BeaconState
headBlock interfaces.ReadOnlySignedBeaconBlock
headRoot [32]byte
proposingSlot primitives.Slot
attributes payloadattribute.Attributer
}
// sendFCU handles the logic to notify the engine of a forckhoice update
// for the first time when processing an incoming block during regular sync. It
// always updates the shuffling caches and handles epoch transitions when the
// incoming block is late, preparing payload attributes in this case while it
// only sends a message with empty attributes for early blocks.
func (s *Service) sendFCU(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
if !s.isNewHead(cfg.headRoot) {
return nil
}
if fcuArgs.attributes != nil && !fcuArgs.attributes.IsEmpty() && s.shouldOverrideFCU(cfg.headRoot, s.CurrentSlot()+1) {
return nil
}
return s.forkchoiceUpdateWithExecution(cfg.ctx, fcuArgs)
}
// sendFCUWithAttributes computes the payload attributes and sends an FCU message
// to the engine if needed
func (s *Service) sendFCUWithAttributes(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) {
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
defer cancel()
cfg.ctx = slotCtx
if err := s.computePayloadAttributes(cfg, fcuArgs); err != nil {
log.WithError(err).Error("could not compute payload attributes")
return
}
if fcuArgs.attributes.IsEmpty() {
return
}
s.cfg.ForkChoiceStore.RLock()
defer s.cfg.ForkChoiceStore.RUnlock()
if _, err := s.notifyForkchoiceUpdate(cfg.ctx, fcuArgs); err != nil {
log.WithError(err).Error("could not update forkchoice with payload attributes for proposal")
}
}
// fockchoiceUpdateWithExecution is a wrapper around notifyForkchoiceUpdate. It decides whether a new call to FCU should be made.
// it returns true if the new head is updated
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, newHeadRoot [32]byte, proposingSlot primitives.Slot) (bool, error) {
func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, args *fcuConfig) error {
_, span := trace.StartSpan(ctx, "beacon-chain.blockchain.forkchoiceUpdateWithExecution")
defer span.End()
// Note: Use the service context here to avoid the parent context being ended during a forkchoice update.
ctx = trace.NewContext(s.ctx, span)
isNewHead := s.isNewHead(newHeadRoot)
if !isNewHead {
return false, nil
}
isNewProposer := s.isNewProposer(proposingSlot)
if isNewProposer && !features.Get().DisableReorgLateBlocks {
if s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
return false, nil
}
}
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
_, err := s.notifyForkchoiceUpdate(ctx, args)
if err != nil {
log.WithError(err).Error("Could not get forkchoice update argument")
return false, nil
return errors.Wrap(err, "could not notify forkchoice update")
}
_, err = s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
headState: headState,
headRoot: newHeadRoot,
headBlock: headBlock.Block(),
})
if err != nil {
return false, errors.Wrap(err, "could not notify forkchoice update")
}
if err := s.saveHead(ctx, newHeadRoot, headBlock, headState); err != nil {
if err := s.saveHead(ctx, args.headRoot, args.headBlock, args.headState); err != nil {
log.WithError(err).Error("could not save head")
}
// Only need to prune attestations from pool if the head has changed.
if err := s.pruneAttsFromPool(headBlock); err != nil {
if err := s.pruneAttsFromPool(args.headBlock); err != nil {
log.WithError(err).Error("could not prune attestations from pool")
}
return true, nil
return nil
}
// shouldOverrideFCU checks whether the incoming block is still subject to being

View File

@@ -17,15 +17,6 @@ import (
logTest "github.com/sirupsen/logrus/hooks/test"
)
func TestService_isNewProposer(t *testing.T) {
beaconDB := testDB.SetupDB(t)
service := setupBeaconChain(t, beaconDB)
require.Equal(t, false, service.isNewProposer(service.CurrentSlot()+1))
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
require.Equal(t, true, service.isNewProposer(service.CurrentSlot()+1))
}
func TestService_isNewHead(t *testing.T) {
beaconDB := testDB.SetupDB(t)
service := setupBeaconChain(t, beaconDB)
@@ -67,33 +58,14 @@ func TestService_getHeadStateAndBlock(t *testing.T) {
}
func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
hook := logTest.NewGlobal()
ctx := context.Background()
opts := testServiceOptsWithDB(t)
service, err := NewService(ctx, opts...)
require.NoError(t, err)
service.cfg.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
_, err = service.forkchoiceUpdateWithExecution(ctx, service.headRoot(), service.CurrentSlot()+1)
require.NoError(t, err)
hookErr := "could not notify forkchoice update"
invalidStateErr := "could not get state summary: could not find block in DB"
require.LogsDoNotContain(t, hook, invalidStateErr)
require.LogsDoNotContain(t, hook, hookErr)
gb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
require.NoError(t, service.saveInitSyncBlock(ctx, [32]byte{'a'}, gb))
_, err = service.forkchoiceUpdateWithExecution(ctx, [32]byte{'a'}, service.CurrentSlot()+1)
require.NoError(t, err)
require.LogsContain(t, hook, invalidStateErr)
service.cfg.PayloadIDCache = cache.NewPayloadIDCache()
service.cfg.TrackedValidatorsCache = cache.NewTrackedValidatorsCache()
hook.Reset()
service.head = &head{
root: [32]byte{'a'},
block: nil, /* should not panic if notify head uses correct head */
}
// Block in Cache
b := util.NewBeaconBlock()
b.Block.Slot = 2
wsb, err := blocks.NewSignedBeaconBlock(b)
@@ -107,13 +79,7 @@ func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
block: wsb,
state: st,
}
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
_, err = service.forkchoiceUpdateWithExecution(ctx, r1, service.CurrentSlot())
require.NoError(t, err)
require.LogsDoNotContain(t, hook, invalidStateErr)
require.LogsDoNotContain(t, hook, hookErr)
// Block in DB
service.cfg.PayloadIDCache.Set(2, [32]byte{2}, [8]byte{1})
b = util.NewBeaconBlock()
b.Block.Slot = 3
util.SaveBlock(t, ctx, service.cfg.BeaconDB, b)
@@ -125,25 +91,22 @@ func TestService_forkchoiceUpdateWithExecution_exceptionalCases(t *testing.T) {
block: wsb,
state: st,
}
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(2, 1, [8]byte{1}, [32]byte{2})
_, err = service.forkchoiceUpdateWithExecution(ctx, r1, service.CurrentSlot()+1)
require.NoError(t, err)
require.LogsDoNotContain(t, hook, invalidStateErr)
require.LogsDoNotContain(t, hook, hookErr)
vId, payloadID, has := service.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{2})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(1), vId)
require.Equal(t, [8]byte{1}, payloadID)
service.cfg.PayloadIDCache.Set(2, [32]byte{2}, [8]byte{1})
args := &fcuConfig{
headState: st,
headRoot: r1,
headBlock: wsb,
proposingSlot: service.CurrentSlot() + 1,
}
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, args))
// Test zero headRoot returns immediately.
headRoot := service.headRoot()
_, err = service.forkchoiceUpdateWithExecution(ctx, [32]byte{}, service.CurrentSlot()+1)
require.NoError(t, err)
require.Equal(t, service.headRoot(), headRoot)
payloadID, has := service.cfg.PayloadIDCache.PayloadID(2, [32]byte{2})
require.Equal(t, true, has)
require.Equal(t, primitives.PayloadID{1}, payloadID)
}
func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testing.T) {
service, tr := minimalTestService(t)
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx, beaconDB, fcs := tr.ctx, tr.db, tr.fcs
altairBlk := util.SaveBlock(t, ctx, beaconDB, util.NewBeaconBlockAltair())
@@ -182,10 +145,14 @@ func TestService_forkchoiceUpdateWithExecution_SameHeadRootNewProposer(t *testin
service.head.root = r
service.head.block = sb
service.head.state = st
service.cfg.ProposerSlotIndexCache.SetProposerAndPayloadIDs(service.CurrentSlot()+1, 0, [8]byte{}, [32]byte{} /* root */)
_, err = service.forkchoiceUpdateWithExecution(ctx, r, service.CurrentSlot()+1)
require.NoError(t, err)
service.cfg.PayloadIDCache.Set(service.CurrentSlot()+1, [32]byte{} /* root */, [8]byte{})
args := &fcuConfig{
headState: st,
headBlock: sb,
headRoot: r,
proposingSlot: service.CurrentSlot() + 1,
}
require.NoError(t, service.forkchoiceUpdateWithExecution(ctx, args))
}
func TestShouldOverrideFCU(t *testing.T) {

View File

@@ -69,10 +69,18 @@ func WithDepositCache(c cache.DepositCache) Option {
}
}
// WithProposerIdsCache for proposer id cache.
func WithProposerIdsCache(c *cache.ProposerPayloadIDsCache) Option {
// WithPayloadIDCache for payload ID cache.
func WithPayloadIDCache(c *cache.PayloadIDCache) Option {
return func(s *Service) error {
s.cfg.ProposerSlotIndexCache = c
s.cfg.PayloadIDCache = c
return nil
}
}
// WithTrackedValidatorsCache for tracked validators cache.
func WithTrackedValidatorsCache(c *cache.TrackedValidatorsCache) Option {
return func(s *Service) error {
s.cfg.TrackedValidatorsCache = c
return nil
}
}

View File

@@ -28,7 +28,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/attestation"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -41,106 +40,67 @@ const depositDeadline = 20 * time.Second
// This defines size of the upper bound for initial sync block cache.
var initialSyncBlockCacheSize = uint64(2 * params.BeaconConfig().SlotsPerEpoch)
// postBlockProcessConfig is a structure that contains the data needed to
// process the beacon block after validating the state transition function
type postBlockProcessConfig struct {
ctx context.Context
signed interfaces.ReadOnlySignedBeaconBlock
blockRoot [32]byte
headRoot [32]byte
postState state.BeaconState
isValidPayload bool
}
// postBlockProcess is called when a gossip block is received. This function performs
// several duties most importantly informing the engine if head was updated,
// saving the new head information to the blockchain package and
// handling attestations, slashings and similar included in the block.
func (s *Service) postBlockProcess(ctx context.Context, signed interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, postState state.BeaconState, isValidPayload bool) error {
ctx, span := trace.StartSpan(ctx, "blockChain.onBlock")
func (s *Service) postBlockProcess(cfg *postBlockProcessConfig) error {
ctx, span := trace.StartSpan(cfg.ctx, "blockChain.onBlock")
defer span.End()
if err := consensusblocks.BeaconBlockIsNil(signed); err != nil {
cfg.ctx = ctx
if err := consensusblocks.BeaconBlockIsNil(cfg.signed); err != nil {
return invalidBlock{error: err}
}
startTime := time.Now()
b := signed.Block()
fcuArgs := &fcuConfig{}
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, postState, blockRoot); err != nil {
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
defer s.handleSecondFCUCall(cfg, fcuArgs)
defer s.sendStateFeedOnBlock(cfg)
defer reportProcessingTime(startTime)
defer reportAttestationInclusion(cfg.signed.Block())
err := s.cfg.ForkChoiceStore.InsertNode(ctx, cfg.postState, cfg.blockRoot)
if err != nil {
return errors.Wrapf(err, "could not insert block %d to fork choice store", cfg.signed.Block().Slot())
}
if err := s.handleBlockAttestations(ctx, signed.Block(), postState); err != nil {
if err := s.handleBlockAttestations(ctx, cfg.signed.Block(), cfg.postState); err != nil {
return errors.Wrap(err, "could not handle block's attestations")
}
s.InsertSlashingsToForkChoiceStore(ctx, signed.Block().Body().AttesterSlashings())
if isValidPayload {
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, blockRoot); err != nil {
s.InsertSlashingsToForkChoiceStore(ctx, cfg.signed.Block().Body().AttesterSlashings())
if cfg.isValidPayload {
if err := s.cfg.ForkChoiceStore.SetOptimisticToValid(ctx, cfg.blockRoot); err != nil {
return errors.Wrap(err, "could not set optimistic block to valid")
}
}
start := time.Now()
headRoot, err := s.cfg.ForkChoiceStore.Head(ctx)
cfg.headRoot, err = s.cfg.ForkChoiceStore.Head(ctx)
if err != nil {
log.WithError(err).Warn("Could not update head")
}
if blockRoot != headRoot {
receivedWeight, err := s.cfg.ForkChoiceStore.Weight(blockRoot)
if err != nil {
log.WithField("root", fmt.Sprintf("%#x", blockRoot)).Warn("could not determine node weight")
}
headWeight, err := s.cfg.ForkChoiceStore.Weight(headRoot)
if err != nil {
log.WithField("root", fmt.Sprintf("%#x", headRoot)).Warn("could not determine node weight")
}
log.WithFields(logrus.Fields{
"receivedRoot": fmt.Sprintf("%#x", blockRoot),
"receivedWeight": receivedWeight,
"headRoot": fmt.Sprintf("%#x", headRoot),
"headWeight": headWeight,
}).Debug("Head block is not the received block")
}
newBlockHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
// verify conditions for FCU, notifies FCU, and saves the new head.
// This function also prunes attestations, other similar operations happen in prunePostBlockOperationPools.
if _, err := s.forkchoiceUpdateWithExecution(ctx, headRoot, s.CurrentSlot()+1); err != nil {
return err
if cfg.headRoot != cfg.blockRoot {
s.logNonCanonicalBlockReceived(cfg.blockRoot, cfg.headRoot)
return nil
}
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(blockRoot)
if err != nil {
log.WithError(err).Debug("Could not check if block is optimistic")
optimistic = true
if err := s.getFCUArgs(cfg, fcuArgs); err != nil {
log.WithError(err).Error("Could not get forkchoice update argument")
return nil
}
// Send notification of the processed block to the state feed.
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
Slot: signed.Block().Slot(),
BlockRoot: blockRoot,
SignedBlock: signed,
Verified: true,
Optimistic: optimistic,
},
})
defer reportAttestationInclusion(b)
if headRoot == blockRoot {
// Updating next slot state cache can happen in the background
// except in the epoch boundary in which case we lock to handle
// the shuffling and proposer caches updates.
// We handle these caches only on canonical
// blocks, otherwise this will be handled by lateBlockTasks
slot := postState.Slot()
if slots.IsEpochEnd(slot) {
if err := transition.UpdateNextSlotCache(ctx, blockRoot[:], postState); err != nil {
return errors.Wrap(err, "could not update next slot state cache")
}
if err := s.handleEpochBoundary(ctx, slot, postState, blockRoot[:]); err != nil {
return errors.Wrap(err, "could not handle epoch boundary")
}
} else {
go func() {
slotCtx, cancel := context.WithTimeout(context.Background(), slotDeadline)
defer cancel()
if err := transition.UpdateNextSlotCache(slotCtx, blockRoot[:], postState); err != nil {
log.WithError(err).Error("could not update next slot state cache")
}
}()
}
if err := s.sendFCU(cfg, fcuArgs); err != nil {
return errors.Wrap(err, "could not send FCU to engine")
}
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
return nil
}
@@ -322,10 +282,10 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
return errors.Wrap(err, "could not set optimistic block to valid")
}
}
arg := &notifyForkchoiceUpdateArg{
arg := &fcuConfig{
headState: preState,
headRoot: lastBR,
headBlock: lastB.Block(),
headBlock: lastB,
}
if _, err := s.notifyForkchoiceUpdate(ctx, arg); err != nil {
return err
@@ -381,9 +341,6 @@ func (s *Service) updateEpochBoundaryCaches(ctx context.Context, st state.Beacon
if err := helpers.UpdateCommitteeCache(slotCtx, st, e+1); err != nil {
log.WithError(err).Warn("Could not update committee cache")
}
if err := helpers.UpdateUnsafeProposerIndicesInCache(slotCtx, st, e+1); err != nil {
log.WithError(err).Warn("Failed to cache next epoch proposers")
}
}()
// The latest block header is from the previous epoch
r, err := st.LatestBlockHeader().HashTreeRoot()
@@ -674,13 +631,12 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
log.WithError(err).Error("lateBlockTasks: could not update epoch boundary caches")
}
s.cfg.ForkChoiceStore.RUnlock()
// Head root should be empty when retrieving proposer index for the next slot.
_, id, has := s.cfg.ProposerSlotIndexCache.GetProposerPayloadIDs(s.CurrentSlot()+1, [32]byte{} /* head root */)
// There exists proposer for next slot, but we haven't called fcu w/ payload attribute yet.
if (!has && !features.Get().PrepareAllPayloads) || id != [8]byte{} {
// return early if we already started building a block for the current
// head root
_, has := s.cfg.PayloadIDCache.PayloadID(s.CurrentSlot()+1, headRoot)
if has {
return
}
s.headLock.RLock()
headBlock, err := s.headBlock()
if err != nil {
@@ -690,11 +646,18 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
}
s.headLock.RUnlock()
s.cfg.ForkChoiceStore.RLock()
_, err = s.notifyForkchoiceUpdate(ctx, &notifyForkchoiceUpdateArg{
fcuArgs := &fcuConfig{
headState: headState,
headRoot: headRoot,
headBlock: headBlock.Block(),
})
headBlock: headBlock,
}
fcuArgs.attributes = s.getPayloadAttribute(ctx, headState, s.CurrentSlot()+1, headRoot[:])
// return early if we are not proposing next slot
if fcuArgs.attributes.IsEmpty() {
s.cfg.ForkChoiceStore.RUnlock()
return
}
_, err = s.notifyForkchoiceUpdate(ctx, fcuArgs)
s.cfg.ForkChoiceStore.RUnlock()
if err != nil {
log.WithError(err).Debug("could not perform late block tasks: failed to update forkchoice with engine")

View File

@@ -3,9 +3,13 @@ package blockchain
import (
"context"
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
forkchoicetypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
@@ -15,8 +19,8 @@ import (
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
mathutil "github.com/prysmaticlabs/prysm/v4/math"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/time"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
@@ -25,6 +29,127 @@ func (s *Service) CurrentSlot() primitives.Slot {
return slots.CurrentSlot(uint64(s.genesisTime.Unix()))
}
// getFCUArgs returns the arguments to call forkchoice update
func (s *Service) getFCUArgs(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
if err := s.getFCUArgsEarlyBlock(cfg, fcuArgs); err != nil {
return err
}
slot := cfg.signed.Block().Slot()
if slots.WithinVotingWindow(uint64(s.genesisTime.Unix()), slot) {
return nil
}
return s.computePayloadAttributes(cfg, fcuArgs)
}
func (s *Service) getFCUArgsEarlyBlock(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
if cfg.blockRoot == cfg.headRoot {
fcuArgs.headState = cfg.postState
fcuArgs.headBlock = cfg.signed
fcuArgs.headRoot = cfg.headRoot
fcuArgs.proposingSlot = s.CurrentSlot() + 1
return nil
}
return s.fcuArgsNonCanonicalBlock(cfg, fcuArgs)
}
// logNonCanonicalBlockReceived prints a message informing that the received
// block is not the head of the chain. It requires the caller holds a lock on
// Foprkchoice.
func (s *Service) logNonCanonicalBlockReceived(blockRoot [32]byte, headRoot [32]byte) {
receivedWeight, err := s.cfg.ForkChoiceStore.Weight(blockRoot)
if err != nil {
log.WithField("root", fmt.Sprintf("%#x", blockRoot)).Warn("could not determine node weight")
}
headWeight, err := s.cfg.ForkChoiceStore.Weight(headRoot)
if err != nil {
log.WithField("root", fmt.Sprintf("%#x", headRoot)).Warn("could not determine node weight")
}
log.WithFields(logrus.Fields{
"receivedRoot": fmt.Sprintf("%#x", blockRoot),
"receivedWeight": receivedWeight,
"headRoot": fmt.Sprintf("%#x", headRoot),
"headWeight": headWeight,
}).Debug("Head block is not the received block")
}
// fcuArgsNonCanonicalBlock returns the arguments to the FCU call when the
// incoming block is non-canonical, that is, based on the head root.
func (s *Service) fcuArgsNonCanonicalBlock(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
headState, headBlock, err := s.getStateAndBlock(cfg.ctx, cfg.headRoot)
if err != nil {
return err
}
fcuArgs.headState = headState
fcuArgs.headBlock = headBlock
fcuArgs.headRoot = cfg.headRoot
fcuArgs.proposingSlot = s.CurrentSlot() + 1
return nil
}
// sendStateFeedOnBlock sends an event that a new block has been synced
func (s *Service) sendStateFeedOnBlock(cfg *postBlockProcessConfig) {
optimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(cfg.blockRoot)
if err != nil {
log.WithError(err).Debug("Could not check if block is optimistic")
optimistic = true
}
// Send notification of the processed block to the state feed.
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{
Type: statefeed.BlockProcessed,
Data: &statefeed.BlockProcessedData{
Slot: cfg.signed.Block().Slot(),
BlockRoot: cfg.blockRoot,
SignedBlock: cfg.signed,
Verified: true,
Optimistic: optimistic,
},
})
}
// updateCachesPostBlockProcessing updates the next slot cache and handles the epoch
// boundary in order to compute the right proposer indices after processing
// state transition. This function is called on late blocks while still locked,
// before sending FCU to the engine.
func (s *Service) updateCachesPostBlockProcessing(cfg *postBlockProcessConfig) error {
slot := cfg.postState.Slot()
if err := transition.UpdateNextSlotCache(cfg.ctx, cfg.blockRoot[:], cfg.postState); err != nil {
return errors.Wrap(err, "could not update next slot state cache")
}
if !slots.IsEpochEnd(slot) {
return nil
}
return s.handleEpochBoundary(cfg.ctx, slot, cfg.postState, cfg.blockRoot[:])
}
// handleSecondFCUCall handles a second call to FCU when syncing a new block.
// This is useful when proposing in the next block and we want to defer the
// computation of the next slot shuffling.
func (s *Service) handleSecondFCUCall(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) {
if (fcuArgs.attributes == nil || fcuArgs.attributes.IsEmpty()) && cfg.headRoot == cfg.blockRoot {
go s.sendFCUWithAttributes(cfg, fcuArgs)
}
}
// reportProcessingTime reports the metric of how long it took to process the
// current block
func reportProcessingTime(startTime time.Time) {
onBlockProcessingTime.Observe(float64(time.Since(startTime).Milliseconds()))
}
// computePayloadAttributes modifies the passed FCU arguments to
// contain the right payload attributes with the tracked proposer. It gets
// called on blocks that arrive after the attestation voting window, or in a
// background routine after syncing early blocks.
func (s *Service) computePayloadAttributes(cfg *postBlockProcessConfig, fcuArgs *fcuConfig) error {
if cfg.blockRoot == cfg.headRoot {
if err := s.updateCachesPostBlockProcessing(cfg); err != nil {
return err
}
}
fcuArgs.attributes = s.getPayloadAttribute(cfg.ctx, fcuArgs.headState, fcuArgs.proposingSlot, cfg.headRoot[:])
return nil
}
// getBlockPreState returns the pre state of an incoming block. It uses the parent root of the block
// to retrieve the state in DB. It verifies the pre state's validity and the incoming block
// is in the correct time window.

View File

@@ -567,7 +567,7 @@ func TestOnBlock_CanFinalize_WithOnTick(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true}))
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
require.NoError(t, err)
@@ -615,7 +615,7 @@ func TestOnBlock_CanFinalize(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, true}))
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
require.NoError(t, err)
@@ -641,7 +641,7 @@ func TestOnBlock_CanFinalize(t *testing.T) {
func TestOnBlock_NilBlock(t *testing.T) {
service, tr := minimalTestService(t)
err := service.postBlockProcess(tr.ctx, nil, [32]byte{}, nil, true)
err := service.postBlockProcess(&postBlockProcessConfig{tr.ctx, nil, [32]byte{}, [32]byte{}, nil, true})
require.Equal(t, true, IsInvalidBlock(err))
}
@@ -689,7 +689,7 @@ func TestOnBlock_CallNewPayloadAndForkchoiceUpdated(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, r, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, r, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, r, [32]byte{}, postState, false}))
testState, err = service.cfg.StateGen.StateByRoot(ctx, r)
require.NoError(t, err)
}
@@ -895,7 +895,7 @@ func Test_validateMergeTransitionBlock(t *testing.T) {
cfg.TerminalBlockHash = params.BeaconConfig().ZeroHash
params.OverrideBeaconConfig(cfg)
service, tr := minimalTestService(t, WithProposerIdsCache(cache.NewProposerPayloadIDsCache()))
service, tr := minimalTestService(t, WithPayloadIDCache(cache.NewPayloadIDCache()))
ctx := tr.ctx
aHash := common.BytesToHash([]byte("a"))
@@ -1111,7 +1111,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb1)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb1, r1, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb1, r1, [32]byte{}, postState, true}))
lock.Unlock()
wg.Done()
}()
@@ -1121,7 +1121,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb2)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb2, r2, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb2, r2, [32]byte{}, postState, true}))
lock.Unlock()
wg.Done()
}()
@@ -1131,7 +1131,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb3)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb3, r3, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb3, r3, [32]byte{}, postState, true}))
lock.Unlock()
wg.Done()
}()
@@ -1141,7 +1141,7 @@ func TestOnBlock_ProcessBlocksParallel(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb4)
require.NoError(t, err)
lock.Lock()
require.NoError(t, service.postBlockProcess(ctx, wsb4, r4, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb4, r4, [32]byte{}, postState, true}))
lock.Unlock()
wg.Done()
}()
@@ -1216,7 +1216,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
}
for i := 6; i < 12; i++ {
@@ -1234,7 +1234,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
@@ -1253,7 +1253,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
// Check that we haven't justified the second epoch yet
@@ -1275,7 +1275,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState))
err = service.postBlockProcess(ctx, wsb, firstInvalidRoot, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false})
require.NoError(t, err)
jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint()
require.Equal(t, primitives.Epoch(2), jc.Epoch)
@@ -1303,7 +1303,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err = service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.ErrorContains(t, "received an INVALID payload from execution engine", err)
// Check that forkchoice's head is the last invalid block imported. The
// store's headroot is the previous head (since the invalid block did
@@ -1332,7 +1332,7 @@ func TestStore_NoViableHead_FCU(t *testing.T) {
postState, err = service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, true)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
require.NoError(t, err)
// Check the newly imported block is head, it justified the right
// checkpoint and the node is no longer optimistic
@@ -1394,7 +1394,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
}
for i := 6; i < 12; i++ {
@@ -1412,7 +1412,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
@@ -1432,7 +1432,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
// Check that we haven't justified the second epoch yet
@@ -1454,7 +1454,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, firstInvalidRoot, wsb, postState))
err = service.postBlockProcess(ctx, wsb, firstInvalidRoot, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, firstInvalidRoot, [32]byte{}, postState, false})
require.NoError(t, err)
jc = service.cfg.ForkChoiceStore.JustifiedCheckpoint()
require.Equal(t, primitives.Epoch(2), jc.Epoch)
@@ -1510,7 +1510,7 @@ func TestStore_NoViableHead_NewPayload(t *testing.T) {
postState, err = service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, true)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
require.NoError(t, err)
// Check the newly imported block is head, it justified the right
// checkpoint and the node is no longer optimistic
@@ -1574,7 +1574,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
}
for i := 6; i < 12; i++ {
@@ -1593,7 +1593,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
@@ -1612,7 +1612,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState))
err = service.postBlockProcess(ctx, wsb, lastValidRoot, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false})
require.NoError(t, err)
// save the post state and the payload Hash of this block since it will
// be the LVH
@@ -1639,7 +1639,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, invalidRoots[i-13], wsb, postState))
err = service.postBlockProcess(ctx, wsb, invalidRoots[i-13], postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, invalidRoots[i-13], [32]byte{}, postState, false})
require.NoError(t, err)
}
// Check that we have justified the second epoch
@@ -1704,7 +1704,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err = service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, true))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true}))
// Check that the head is still INVALID and the node is still optimistic
require.Equal(t, invalidHeadRoot, service.cfg.ForkChoiceStore.CachedHeadRoot())
optimistic, err = service.IsOptimistic(ctx)
@@ -1727,7 +1727,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, true)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
require.NoError(t, err)
st, err = service.cfg.StateGen.StateByRoot(ctx, root)
require.NoError(t, err)
@@ -1753,7 +1753,7 @@ func TestStore_NoViableHead_Liveness(t *testing.T) {
postState, err = service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, true)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, true})
require.NoError(t, err)
require.Equal(t, root, service.cfg.ForkChoiceStore.CachedHeadRoot())
sjc = service.CurrentJustifiedCheckpt()
@@ -1809,7 +1809,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
}
for i := 6; i < 12; i++ {
@@ -1827,7 +1827,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
err = service.postBlockProcess(ctx, wsb, root, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false})
require.NoError(t, err)
}
@@ -1846,7 +1846,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, lastValidRoot, wsb, postState))
err = service.postBlockProcess(ctx, wsb, lastValidRoot, postState, false)
err = service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, lastValidRoot, [32]byte{}, postState, false})
require.NoError(t, err)
// save the post state and the payload Hash of this block since it will
// be the LVH
@@ -1875,7 +1875,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
require.NoError(t, service.updateJustificationOnBlock(ctx, preState, postState, currStoreJustifiedEpoch))
_, err = service.updateFinalizationOnBlock(ctx, preState, postState, currStoreFinalizedEpoch)
require.NoError(t, err)
@@ -1990,7 +1990,7 @@ func TestOnBlock_HandleBlockAttestations(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, root, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, root, [32]byte{}, postState, false}))
st, err = service.HeadState(ctx)
require.NoError(t, err)
@@ -2045,7 +2045,7 @@ func TestFillMissingBlockPayloadId_PrepareAllPayloads(t *testing.T) {
// Helper function to simulate the block being on time or delayed for proposer
// boost. It alters the genesisTime tracked by the store.
func driftGenesisTime(s *Service, slot, delay int64) {
offset := slot*int64(params.BeaconConfig().SecondsPerSlot) - delay
offset := slot*int64(params.BeaconConfig().SecondsPerSlot) + delay
s.SetGenesisTime(time.Unix(time.Now().Unix()-offset, 0))
}
@@ -2197,6 +2197,35 @@ func TestMissingIndices(t *testing.T) {
}
}
func Test_getFCUArgs(t *testing.T) {
s, tr := minimalTestService(t)
ctx := tr.ctx
st, keys := util.DeterministicGenesisState(t, 64)
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), 1)
require.NoError(t, err)
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
cfg := &postBlockProcessConfig{
ctx: ctx,
signed: wsb,
blockRoot: [32]byte{'a'},
postState: st,
isValidPayload: true,
}
// error branch
fcuArgs := &fcuConfig{}
err = s.getFCUArgs(cfg, fcuArgs)
require.ErrorContains(t, "block does not exist", err)
// canonical branch
cfg.headRoot = cfg.blockRoot
fcuArgs = &fcuConfig{}
err = s.getFCUArgs(cfg, fcuArgs)
require.NoError(t, err)
require.Equal(t, cfg.blockRoot, fcuArgs.headRoot)
}
func fakeCommitments(n int) [][]byte {
f := make([][]byte, n)
for i := range f {

View File

@@ -9,7 +9,6 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/features"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
@@ -122,35 +121,41 @@ func (s *Service) UpdateHead(ctx context.Context, proposingSlot primitives.Slot)
defer s.cfg.ForkChoiceStore.Unlock()
// This function is only called at 10 seconds or 0 seconds into the slot
disparity := params.BeaconConfig().MaximumGossipClockDisparityDuration()
if !features.Get().DisableReorgLateBlocks {
disparity += reorgLateBlockCountAttestations
}
disparity += reorgLateBlockCountAttestations
s.processAttestations(ctx, disparity)
processAttsElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
start = time.Now()
// return early if we haven't changed head
newHeadRoot, err := s.cfg.ForkChoiceStore.Head(ctx)
if err != nil {
log.WithError(err).Error("Could not compute head from new attestations")
// Fallback to our current head root in the event of a failure.
s.headLock.RLock()
newHeadRoot = s.headRoot()
s.headLock.RUnlock()
return
}
if !s.isNewHead(newHeadRoot) {
return
}
log.WithField("newHeadRoot", fmt.Sprintf("%#x", newHeadRoot)).Debug("Head changed due to attestations")
headState, headBlock, err := s.getStateAndBlock(ctx, newHeadRoot)
if err != nil {
log.WithError(err).Error("could not get head block")
return
}
newAttHeadElapsedTime.Observe(float64(time.Since(start).Milliseconds()))
changed, err := s.forkchoiceUpdateWithExecution(s.ctx, newHeadRoot, proposingSlot)
if err != nil {
log.WithError(err).Error("could not update forkchoice")
fcuArgs := &fcuConfig{
headState: headState,
headRoot: newHeadRoot,
headBlock: headBlock,
proposingSlot: proposingSlot,
}
if changed {
s.headLock.RLock()
log.WithFields(logrus.Fields{
"oldHeadRoot": fmt.Sprintf("%#x", s.headRoot()),
"newHeadRoot": fmt.Sprintf("%#x", newHeadRoot),
}).Debug("Head changed due to attestations")
s.headLock.RUnlock()
fcuArgs.attributes = s.getPayloadAttribute(ctx, headState, proposingSlot, newHeadRoot[:])
if fcuArgs.attributes != nil && s.shouldOverrideFCU(newHeadRoot, proposingSlot) {
return
}
if err := s.forkchoiceUpdateWithExecution(s.ctx, fcuArgs); err != nil {
log.WithError(err).Error("could not update forkchoice")
}
}

View File

@@ -112,7 +112,7 @@ func TestService_ProcessAttestationsAndUpdateHead(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, tRoot, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false}))
copied, err = service.cfg.StateGen.StateByRoot(ctx, tRoot)
require.NoError(t, err)
require.Equal(t, 2, fcs.NodeCount())
@@ -168,7 +168,7 @@ func TestService_UpdateHead_NoAtts(t *testing.T) {
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, tRoot, wsb, postState))
require.NoError(t, service.postBlockProcess(ctx, wsb, tRoot, postState, false))
require.NoError(t, service.postBlockProcess(&postBlockProcessConfig{ctx, wsb, tRoot, [32]byte{}, postState, false}))
require.Equal(t, 2, fcs.NodeCount())
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb))
require.Equal(t, tRoot, service.head.root)

View File

@@ -119,7 +119,14 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
if err := s.savePostStateInfo(ctx, blockRoot, blockCopy, postState); err != nil {
return errors.Wrap(err, "could not save post state info")
}
if err := s.postBlockProcess(ctx, blockCopy, blockRoot, postState, isValidPayload); err != nil {
args := &postBlockProcessConfig{
ctx: ctx,
signed: blockCopy,
blockRoot: blockRoot,
postState: postState,
isValidPayload: isValidPayload,
}
if err := s.postBlockProcess(args); err != nil {
err := errors.Wrap(err, "could not process block")
tracing.AnnotateError(span, err)
return err

View File

@@ -7,6 +7,7 @@ import (
"time"
blockchainTesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
@@ -130,7 +131,9 @@ func TestService_ReceiveBlock(t *testing.T) {
s, tr := minimalTestService(t,
WithFinalizedStateAtStartUp(genesis),
WithExitPool(voluntaryexits.NewPool()),
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}))
WithStateNotifier(&blockchainTesting.MockStateNotifier{RecordEvents: true}),
WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
)
beaconDB := tr.db
genesisBlockRoot := bytesutil.ToBytes32(nil)

View File

@@ -73,7 +73,8 @@ type config struct {
ChainStartFetcher execution.ChainStartFetcher
BeaconDB db.HeadAccessDatabase
DepositCache cache.DepositCache
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
PayloadIDCache *cache.PayloadIDCache
TrackedValidatorsCache *cache.TrackedValidatorsCache
AttPool attestations.Pool
ExitPool voluntaryexits.PoolManager
SlashingPool slashings.PoolManager
@@ -167,7 +168,7 @@ func NewService(ctx context.Context, opts ...Option) (*Service, error) {
checkpointStateCache: cache.NewCheckpointStateCache(),
initSyncBlocks: make(map[[32]byte]interfaces.ReadOnlySignedBeaconBlock),
blobNotifiers: bn,
cfg: &config{ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache()},
cfg: &config{},
blockBeingSynced: &currentlySyncingBlock{roots: make(map[[32]byte]struct{})},
}
for _, opt := range opts {

View File

@@ -100,7 +100,7 @@ func setupBeaconChain(t *testing.T, beaconDB db.Database) *Service {
WithForkChoiceStore(fc),
WithAttestationService(attService),
WithStateGen(stateGen),
WithProposerIdsCache(cache.NewProposerPayloadIDsCache()),
WithPayloadIDCache(cache.NewPayloadIDCache()),
WithClockSynchronizer(startup.NewClockSynchronizer()),
}

View File

@@ -6,6 +6,7 @@ import (
"testing"
"github.com/prysmaticlabs/prysm/v4/async/event"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache/depositcache"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
@@ -114,6 +115,7 @@ func minimalTestService(t *testing.T, opts ...Option) (*Service, *testServiceReq
WithAttestationService(req.attSrv),
WithBLSToExecPool(req.blsPool),
WithDepositCache(dc),
WithTrackedValidatorsCache(cache.NewTrackedValidatorsCache()),
}
// append the variadic opts so they override the defaults by being processed afterwards
opts = append(defOpts, opts...)

View File

@@ -0,0 +1,27 @@
package blockchain
import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v4/config/features"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
)
// trackedProposer returns whether the beacon node was informed, via the
// validators/prepare_proposer endpoint, of the proposer at the given slot.
// It only returns true if the tracked proposer is present and active.
func (s *Service) trackedProposer(st state.ReadOnlyBeaconState, slot primitives.Slot) (cache.TrackedValidator, bool) {
if features.Get().PrepareAllPayloads {
return cache.TrackedValidator{Active: true}, true
}
id, err := helpers.BeaconProposerIndexAtSlot(s.ctx, st, slot)
if err != nil {
return cache.TrackedValidator{}, false
}
val, ok := s.cfg.TrackedValidatorsCache.Validator(id)
if !ok {
return cache.TrackedValidator{}, false
}
return val, val.Active
}

View File

@@ -25,6 +25,7 @@ go_library(
"sync_committee_disabled.go", # keep
"sync_committee_head_state.go",
"sync_subnet_ids.go",
"tracked_validators.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/cache",
visibility = [

View File

@@ -1,94 +1,63 @@
package cache
import (
"bytes"
"sync"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
)
const keyLength = 40
const vIdLength = 8
const pIdLength = 8
const vpIdsLength = vIdLength + pIdLength
// RootToPayloadIDMap is a map with keys the head root and values the
// corresponding PayloadID
type RootToPayloadIDMap map[[32]byte]primitives.PayloadID
// ProposerPayloadIDsCache is a cache of proposer payload IDs.
// The key is the concatenation of the slot and the block root.
// The value is the concatenation of the proposer and payload IDs, 8 bytes each.
type ProposerPayloadIDsCache struct {
slotToProposerAndPayloadIDs map[[keyLength]byte][vpIdsLength]byte
sync.RWMutex
// PayloadIDCache is a cache that keeps track of the prepared payload ID for the
// given slot and with the given head root.
type PayloadIDCache struct {
slotToPayloadID map[primitives.Slot]RootToPayloadIDMap
sync.Mutex
}
// NewProposerPayloadIDsCache creates a new proposer payload IDs cache.
func NewProposerPayloadIDsCache() *ProposerPayloadIDsCache {
return &ProposerPayloadIDsCache{
slotToProposerAndPayloadIDs: make(map[[keyLength]byte][vpIdsLength]byte),
}
// NewPayloadIDCache returns a new payload ID cache
func NewPayloadIDCache() *PayloadIDCache {
return &PayloadIDCache{slotToPayloadID: make(map[primitives.Slot]RootToPayloadIDMap)}
}
// GetProposerPayloadIDs returns the proposer and payload IDs for the given slot and head root to build the block.
func (f *ProposerPayloadIDsCache) GetProposerPayloadIDs(
slot primitives.Slot,
r [fieldparams.RootLength]byte,
) (primitives.ValidatorIndex, [pIdLength]byte, bool) {
f.RLock()
defer f.RUnlock()
ids, ok := f.slotToProposerAndPayloadIDs[idKey(slot, r)]
// PayloadID returns the payload ID for the given slot and parent block root
func (p *PayloadIDCache) PayloadID(slot primitives.Slot, root [32]byte) (primitives.PayloadID, bool) {
p.Lock()
defer p.Unlock()
inner, ok := p.slotToPayloadID[slot]
if !ok {
return 0, [pIdLength]byte{}, false
return primitives.PayloadID{}, false
}
vId := ids[:vIdLength]
b := ids[vIdLength:]
var pId [pIdLength]byte
copy(pId[:], b)
return primitives.ValidatorIndex(bytesutil.BytesToUint64BigEndian(vId)), pId, true
pid, ok := inner[root]
if !ok {
return primitives.PayloadID{}, false
}
return pid, true
}
// SetProposerAndPayloadIDs sets the proposer and payload IDs for the given slot and head root to build block.
func (f *ProposerPayloadIDsCache) SetProposerAndPayloadIDs(
slot primitives.Slot,
vId primitives.ValidatorIndex,
pId [pIdLength]byte,
r [fieldparams.RootLength]byte,
) {
f.Lock()
defer f.Unlock()
var vIdBytes [vIdLength]byte
copy(vIdBytes[:], bytesutil.Uint64ToBytesBigEndian(uint64(vId)))
var bs [vpIdsLength]byte
copy(bs[:], append(vIdBytes[:], pId[:]...))
k := idKey(slot, r)
ids, ok := f.slotToProposerAndPayloadIDs[k]
// Ok to overwrite if the slot is already set but the cached payload ID is not set.
// This combats the re-org case where payload assignment could change at the start of the epoch.
var byte8 [vIdLength]byte
if !ok || (ok && bytes.Equal(ids[vIdLength:], byte8[:])) {
f.slotToProposerAndPayloadIDs[k] = bs
// SetPayloadID updates the payload ID for the given slot and head root
// it also prunes older entries in the cache
func (p *PayloadIDCache) Set(slot primitives.Slot, root [32]byte, pid primitives.PayloadID) {
p.Lock()
defer p.Unlock()
if slot > 1 {
p.prune(slot - 2)
}
inner, ok := p.slotToPayloadID[slot]
if !ok {
inner = make(RootToPayloadIDMap)
p.slotToPayloadID[slot] = inner
}
inner[root] = pid
}
// PrunePayloadIDs removes the payload ID entries older than input slot.
func (f *ProposerPayloadIDsCache) PrunePayloadIDs(slot primitives.Slot) {
f.Lock()
defer f.Unlock()
for k := range f.slotToProposerAndPayloadIDs {
s := primitives.Slot(bytesutil.BytesToUint64BigEndian(k[:8]))
if slot > s {
delete(f.slotToProposerAndPayloadIDs, k)
// Prune prunes old payload IDs. Requires a Lock in the cache
func (p *PayloadIDCache) prune(slot primitives.Slot) {
for key := range p.slotToPayloadID {
if key < slot {
delete(p.slotToPayloadID, key)
}
}
}
func idKey(slot primitives.Slot, r [fieldparams.RootLength]byte) [keyLength]byte {
var k [keyLength]byte
copy(k[:], append(bytesutil.Uint64ToBytesBigEndian(uint64(slot)), r[:]...))
return k
}

View File

@@ -8,65 +8,54 @@ import (
)
func TestValidatorPayloadIDsCache_GetAndSaveValidatorPayloadIDs(t *testing.T) {
cache := NewProposerPayloadIDsCache()
cache := NewPayloadIDCache()
var r [32]byte
i, p, ok := cache.GetProposerPayloadIDs(0, r)
p, ok := cache.PayloadID(0, r)
require.Equal(t, false, ok)
require.Equal(t, primitives.ValidatorIndex(0), i)
require.Equal(t, [pIdLength]byte{}, p)
require.Equal(t, primitives.PayloadID{}, p)
slot := primitives.Slot(1234)
vid := primitives.ValidatorIndex(34234324)
pid := [8]byte{1, 2, 3, 3, 7, 8, 7, 8}
pid := primitives.PayloadID{1, 2, 3, 3, 7, 8, 7, 8}
r = [32]byte{1, 2, 3}
cache.SetProposerAndPayloadIDs(slot, vid, pid, r)
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
cache.Set(slot, r, pid)
p, ok = cache.PayloadID(slot, r)
require.Equal(t, true, ok)
require.Equal(t, vid, i)
require.Equal(t, pid, p)
slot = primitives.Slot(9456456)
vid = primitives.ValidatorIndex(6786745)
r = [32]byte{4, 5, 6}
cache.SetProposerAndPayloadIDs(slot, vid, [pIdLength]byte{}, r)
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
cache.Set(slot, r, primitives.PayloadID{})
p, ok = cache.PayloadID(slot, r)
require.Equal(t, true, ok)
require.Equal(t, vid, i)
require.Equal(t, [pIdLength]byte{}, p)
require.Equal(t, primitives.PayloadID{}, p)
// reset cache without pid
slot = primitives.Slot(9456456)
vid = primitives.ValidatorIndex(11111)
r = [32]byte{7, 8, 9}
pid = [8]byte{3, 2, 3, 33, 72, 8, 7, 8}
cache.SetProposerAndPayloadIDs(slot, vid, pid, r)
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
cache.Set(slot, r, pid)
p, ok = cache.PayloadID(slot, r)
require.Equal(t, true, ok)
require.Equal(t, vid, i)
require.Equal(t, pid, p)
// Forked chain
r = [32]byte{1, 2, 3}
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
p, ok = cache.PayloadID(slot, r)
require.Equal(t, false, ok)
require.Equal(t, primitives.ValidatorIndex(0), i)
require.Equal(t, [pIdLength]byte{}, p)
require.Equal(t, primitives.PayloadID{}, p)
// existing pid - no change in cache
// existing pid - change the cache
slot = primitives.Slot(9456456)
vid = primitives.ValidatorIndex(11111)
r = [32]byte{7, 8, 9}
newPid := [8]byte{1, 2, 3, 33, 72, 8, 7, 1}
cache.SetProposerAndPayloadIDs(slot, vid, newPid, r)
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
newPid := primitives.PayloadID{1, 2, 3, 33, 72, 8, 7, 1}
cache.Set(slot, r, newPid)
p, ok = cache.PayloadID(slot, r)
require.Equal(t, true, ok)
require.Equal(t, vid, i)
require.Equal(t, pid, p)
require.Equal(t, newPid, p)
// remove cache entry
cache.PrunePayloadIDs(slot + 1)
i, p, ok = cache.GetProposerPayloadIDs(slot, r)
cache.prune(slot + 1)
p, ok = cache.PayloadID(slot, r)
require.Equal(t, false, ok)
require.Equal(t, primitives.ValidatorIndex(0), i)
require.Equal(t, [pIdLength]byte{}, p)
require.Equal(t, primitives.PayloadID{}, p)
}

View File

@@ -42,17 +42,15 @@ var (
// root would be for slot 32 if present.
type ProposerIndicesCache struct {
sync.Mutex
indices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
unsafeIndices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
rootMap map[forkchoicetypes.Checkpoint][32]byte // A map from checkpoint root to state root
indices map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
rootMap map[forkchoicetypes.Checkpoint][32]byte // A map from checkpoint root to state root
}
// NewProposerIndicesCache returns a newly created cache
func NewProposerIndicesCache() *ProposerIndicesCache {
return &ProposerIndicesCache{
indices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
unsafeIndices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
rootMap: make(map[forkchoicetypes.Checkpoint][32]byte),
indices: make(map[primitives.Epoch]map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex),
rootMap: make(map[forkchoicetypes.Checkpoint][32]byte),
}
}
@@ -74,18 +72,6 @@ func (p *ProposerIndicesCache) ProposerIndices(epoch primitives.Epoch, root [32]
return indices, exists
}
// UnsafeProposerIndices returns the proposer indices (unsafe) for the given root
func (p *ProposerIndicesCache) UnsafeProposerIndices(epoch primitives.Epoch, root [32]byte) ([fieldparams.SlotsPerEpoch]primitives.ValidatorIndex, bool) {
p.Lock()
defer p.Unlock()
inner, ok := p.unsafeIndices[epoch]
if !ok {
return [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}, false
}
indices, exists := inner[root]
return indices, exists
}
// Prune resets the ProposerIndicesCache to its initial state
func (p *ProposerIndicesCache) Prune(epoch primitives.Epoch) {
p.Lock()
@@ -95,11 +81,6 @@ func (p *ProposerIndicesCache) Prune(epoch primitives.Epoch) {
delete(p.indices, key)
}
}
for key := range p.unsafeIndices {
if key < epoch {
delete(p.unsafeIndices, key)
}
}
for key := range p.rootMap {
if key.Epoch+1 < epoch {
delete(p.rootMap, key)
@@ -120,18 +101,6 @@ func (p *ProposerIndicesCache) Set(epoch primitives.Epoch, root [32]byte, indice
inner[root] = indices
}
// SetUnsafe sets the unsafe proposer indices for the given root as key
func (p *ProposerIndicesCache) SetUnsafe(epoch primitives.Epoch, root [32]byte, indices [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex) {
p.Lock()
defer p.Unlock()
inner, ok := p.unsafeIndices[epoch]
if !ok {
inner = make(map[[32]byte][fieldparams.SlotsPerEpoch]primitives.ValidatorIndex)
p.unsafeIndices[epoch] = inner
}
inner[root] = indices
}
// SetCheckpoint updates the map from checkpoints to state roots
func (p *ProposerIndicesCache) SetCheckpoint(c forkchoicetypes.Checkpoint, root [32]byte) {
p.Lock()

View File

@@ -4,11 +4,26 @@
package cache
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
forkchoicetypes "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/types"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
)
var (
// ProposerIndicesCacheMiss tracks the number of proposerIndices requests that aren't present in the cache.
ProposerIndicesCacheMiss = promauto.NewCounter(prometheus.CounterOpts{
Name: "proposer_indices_cache_miss",
Help: "The number of proposer indices requests that aren't present in the cache.",
})
// ProposerIndicesCacheHit tracks the number of proposerIndices requests that are in the cache.
ProposerIndicesCacheHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "proposer_indices_cache_hit",
Help: "The number of proposer indices requests that are present in the cache.",
})
)
// FakeProposerIndicesCache is a struct with 1 queue for looking up proposer indices by root.
type FakeProposerIndicesCache struct {
}

View File

@@ -34,29 +34,6 @@ func TestProposerCache_Set(t *testing.T) {
require.Equal(t, emptyIndices, received)
}
func TestProposerCache_SetUnsafe(t *testing.T) {
cache := NewProposerIndicesCache()
bRoot := [32]byte{'A'}
indices, ok := cache.UnsafeProposerIndices(0, bRoot)
require.Equal(t, false, ok)
emptyIndices := [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}
require.Equal(t, indices, emptyIndices, "Expected committee count not to exist in empty cache")
emptyIndices[0] = 1
cache.SetUnsafe(0, bRoot, emptyIndices)
received, ok := cache.UnsafeProposerIndices(0, bRoot)
require.Equal(t, true, ok)
require.Equal(t, received, emptyIndices)
newRoot := [32]byte{'B'}
copy(emptyIndices[3:], []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6})
cache.SetUnsafe(0, newRoot, emptyIndices)
received, ok = cache.UnsafeProposerIndices(0, newRoot)
require.Equal(t, true, ok)
require.Equal(t, emptyIndices, received)
}
func TestProposerCache_CheckpointAndPrune(t *testing.T) {
cache := NewProposerIndicesCache()
indices := [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex{}
@@ -65,7 +42,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
copy(indices[3:], []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6})
for i := 1; i < 10; i++ {
cache.Set(primitives.Epoch(i), root, indices)
cache.SetUnsafe(primitives.Epoch(i), root, indices)
cache.SetCheckpoint(forkchoicetypes.Checkpoint{Epoch: primitives.Epoch(i - 1), Root: cpRoot}, root)
}
received, ok := cache.ProposerIndices(1, root)
@@ -80,18 +56,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.UnsafeProposerIndices(1, root)
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.UnsafeProposerIndices(4, root)
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.UnsafeProposerIndices(9, root)
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.IndicesFromCheckpoint(forkchoicetypes.Checkpoint{Epoch: 0, Root: cpRoot})
require.Equal(t, true, ok)
require.Equal(t, indices, received)
@@ -123,18 +87,6 @@ func TestProposerCache_CheckpointAndPrune(t *testing.T) {
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.UnsafeProposerIndices(1, root)
require.Equal(t, false, ok)
require.Equal(t, emptyIndices, received)
received, ok = cache.UnsafeProposerIndices(4, root)
require.Equal(t, false, ok)
require.Equal(t, emptyIndices, received)
received, ok = cache.UnsafeProposerIndices(9, root)
require.Equal(t, true, ok)
require.Equal(t, indices, received)
received, ok = cache.IndicesFromCheckpoint(forkchoicetypes.Checkpoint{Epoch: 0, Root: cpRoot})
require.Equal(t, false, ok)
require.Equal(t, emptyIndices, received)

View File

@@ -0,0 +1,43 @@
package cache
import (
"sync"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
)
type TrackedValidator struct {
Active bool
FeeRecipient primitives.ExecutionAddress
Index primitives.ValidatorIndex
}
type TrackedValidatorsCache struct {
sync.Mutex
trackedValidators map[primitives.ValidatorIndex]TrackedValidator
}
func NewTrackedValidatorsCache() *TrackedValidatorsCache {
return &TrackedValidatorsCache{
trackedValidators: make(map[primitives.ValidatorIndex]TrackedValidator),
}
}
func (t *TrackedValidatorsCache) Validator(index primitives.ValidatorIndex) (TrackedValidator, bool) {
t.Lock()
defer t.Unlock()
val, ok := t.trackedValidators[index]
return val, ok
}
func (t *TrackedValidatorsCache) Set(val TrackedValidator) {
t.Lock()
defer t.Unlock()
t.trackedValidators[val.Index] = val
}
func (t *TrackedValidatorsCache) Prune() {
t.Lock()
defer t.Unlock()
t.trackedValidators = make(map[primitives.ValidatorIndex]TrackedValidator)
}

View File

@@ -26,6 +26,12 @@ const (
// BlobSidecarReceived is sent after a blob sidecar is received from gossip or rpc.
BlobSidecarReceived = 6
// ProposerSlashingReceived is sent after a proposer slashing is received from gossip or rpc
ProposerSlashingReceived = 7
// AttesterSlashingReceived is sent after an attester slashing is received from gossip or rpc
AttesterSlashingReceived = 8
)
// UnAggregatedAttReceivedData is the data sent with UnaggregatedAttReceived events.
@@ -61,3 +67,13 @@ type BLSToExecutionChangeReceivedData struct {
type BlobSidecarReceivedData struct {
Blob *blocks.VerifiedROBlob
}
// ProposerSlashingReceivedData is the data sent with ProposerSlashingReceived events.
type ProposerSlashingReceivedData struct {
ProposerSlashing *ethpb.ProposerSlashing
}
// AttesterSlashingReceivedData is the data sent with AttesterSlashingReceived events.
type AttesterSlashingReceivedData struct {
AttesterSlashing *ethpb.AttesterSlashing
}

View File

@@ -346,7 +346,7 @@ func UpdateProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaco
if err != nil {
return err
}
root, err := state.StateRootAtIndex(uint64(slot % params.BeaconConfig().SlotsPerHistoricalRoot))
root, err := StateRootAtSlot(state, slot)
if err != nil {
return err
}
@@ -391,49 +391,6 @@ func UpdateCachedCheckpointToStateRoot(state state.ReadOnlyBeaconState, cp *fork
return nil
}
// UpdateUnsafeProposerIndicesInCache updates proposer indices entry of the
// cache one epoch in advance.
// Input state is used to retrieve active validator indices.
// Input root is to use as key in the cache.
// Input epoch is the epoch to retrieve proposer indices for.
func UpdateUnsafeProposerIndicesInCache(ctx context.Context, state state.ReadOnlyBeaconState, epoch primitives.Epoch) error {
// The cache uses the state root at the end of (current epoch - 2) as key.
// (e.g. for epoch 2, the key is root at slot 31)
if epoch <= params.BeaconConfig().GenesisEpoch+2*params.BeaconConfig().MinSeedLookahead {
return nil
}
slot, err := slots.EpochEnd(epoch - 2)
if err != nil {
return err
}
root, err := state.StateRootAtIndex(uint64(slot % params.BeaconConfig().SlotsPerHistoricalRoot))
if err != nil {
return err
}
// Skip cache update if the key already exists
_, ok := proposerIndicesCache.UnsafeProposerIndices(epoch, [32]byte(root))
if ok {
return nil
}
indices, err := ActiveValidatorIndices(ctx, state, epoch)
if err != nil {
return err
}
proposerIndices, err := precomputeProposerIndices(state, indices, epoch)
if err != nil {
return err
}
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
return errors.New("invalid proposer length returned from state")
}
// This is here to deal with tests only
var indicesArray [fieldparams.SlotsPerEpoch]primitives.ValidatorIndex
copy(indicesArray[:], proposerIndices)
proposerIndicesCache.Prune(epoch - 2)
proposerIndicesCache.SetUnsafe(epoch, [32]byte(root), indicesArray)
return nil
}
// ClearCache clears the beacon committee cache and sync committee cache.
func ClearCache() {
committeeCache.Clear()

View File

@@ -20,10 +20,14 @@ import (
"go.opencensus.io/trace"
)
var CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "committee_cache_in_progress_hit",
Help: "The number of committee requests that are present in the cache.",
})
var (
CommitteeCacheInProgressHit = promauto.NewCounter(prometheus.CounterOpts{
Name: "committee_cache_in_progress_hit",
Help: "The number of committee requests that are present in the cache.",
})
errProposerIndexMiss = errors.New("propoposer index not found in cache")
)
// IsActiveValidator returns the boolean value on whether the validator
// is active or not.
@@ -259,10 +263,32 @@ func ValidatorActivationChurnLimitDeneb(activeValidatorCount uint64) uint64 {
// indices = get_active_validator_indices(state, epoch)
// return compute_proposer_index(state, indices, seed)
func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (primitives.ValidatorIndex, error) {
e := time.CurrentEpoch(state)
return BeaconProposerIndexAtSlot(ctx, state, state.Slot())
}
// cachedProposerIndexAtSlot returns the proposer index at the given slot from
// the cache at the given root key.
func cachedProposerIndexAtSlot(slot primitives.Slot, root [32]byte) (primitives.ValidatorIndex, error) {
proposerIndices, has := proposerIndicesCache.ProposerIndices(slots.ToEpoch(slot), root)
if !has {
cache.ProposerIndicesCacheMiss.Inc()
return 0, errProposerIndexMiss
}
if len(proposerIndices) != int(params.BeaconConfig().SlotsPerEpoch) {
cache.ProposerIndicesCacheMiss.Inc()
return 0, errProposerIndexMiss
}
return proposerIndices[slot%params.BeaconConfig().SlotsPerEpoch], nil
}
// BeaconProposerIndexAtSlot returns proposer index at the given slot from the
// point of view of the given state as head state
func BeaconProposerIndexAtSlot(ctx context.Context, state state.ReadOnlyBeaconState, slot primitives.Slot) (primitives.ValidatorIndex, error) {
e := slots.ToEpoch(slot)
// The cache uses the state root of the previous epoch - minimum_seed_lookahead last slot as key. (e.g. Starting epoch 1, slot 32, the key would be block root at slot 31)
// For simplicity, the node will skip caching of genesis epoch.
if e > params.BeaconConfig().GenesisEpoch+params.BeaconConfig().MinSeedLookahead {
wantedEpoch := time.PrevEpoch(state)
s, err := slots.EpochEnd(wantedEpoch)
s, err := slots.EpochEnd(e - 1)
if err != nil {
return 0, err
}
@@ -271,12 +297,16 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
return 0, err
}
if r != nil && !bytes.Equal(r, params.BeaconConfig().ZeroHash[:]) {
proposerIndices, ok := proposerIndicesCache.ProposerIndices(wantedEpoch+1, bytesutil.ToBytes32(r))
if ok {
return proposerIndices[state.Slot()%params.BeaconConfig().SlotsPerEpoch], nil
pid, err := cachedProposerIndexAtSlot(slot, [32]byte(r))
if err == nil {
return pid, nil
}
if err := UpdateProposerIndicesInCache(ctx, state, time.CurrentEpoch(state)); err != nil {
return 0, errors.Wrap(err, "could not update committee cache")
if err := UpdateProposerIndicesInCache(ctx, state, e); err != nil {
return 0, errors.Wrap(err, "could not update proposer index cache")
}
pid, err = cachedProposerIndexAtSlot(slot, [32]byte(r))
if err == nil {
return pid, nil
}
}
}
@@ -286,7 +316,7 @@ func BeaconProposerIndex(ctx context.Context, state state.ReadOnlyBeaconState) (
return 0, errors.Wrap(err, "could not generate seed")
}
seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(state.Slot()))...)
seedWithSlot := append(seed[:], bytesutil.Bytes8(uint64(slot))...)
seedWithSlotHash := hash.Hash(seedWithSlot)
indices, err := ActiveValidatorIndices(ctx, state, e)

View File

@@ -9,6 +9,7 @@ import (
"path/filepath"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -34,18 +35,22 @@ const (
sszExt = "ssz"
partExt = "part"
firstPruneEpoch = 0
bufferEpochs = 2
directoryPermissions = 0700
)
// BlobStorageOption is a functional option for configuring a BlobStorage.
type BlobStorageOption func(*BlobStorage)
type BlobStorageOption func(*BlobStorage) error
// WithBlobRetentionEpochs is an option that changes the number of epochs blobs will be persisted.
func WithBlobRetentionEpochs(e primitives.Epoch) BlobStorageOption {
return func(b *BlobStorage) {
b.retentionEpochs = e
return func(b *BlobStorage) error {
s, err := slots.EpochStart(e + bufferEpochs)
if err != nil {
return errors.Wrap(err, "could not set retentionSlots")
}
b.retentionSlots = s
return nil
}
}
@@ -59,21 +64,21 @@ func NewBlobStorage(base string, opts ...BlobStorageOption) (*BlobStorage, error
}
fs := afero.NewBasePathFs(afero.NewOsFs(), base)
b := &BlobStorage{
fs: fs,
retentionEpochs: params.BeaconConfig().MinEpochsForBlobsSidecarsRequest,
lastPrunedEpoch: firstPruneEpoch,
fs: fs,
}
for _, o := range opts {
o(b)
if err := o(b); err != nil {
return nil, fmt.Errorf("failed to create blob storage at %s: %w", base, err)
}
}
return b, nil
}
// BlobStorage is the concrete implementation of the filesystem backend for saving and retrieving BlobSidecars.
type BlobStorage struct {
fs afero.Fs
retentionEpochs primitives.Epoch
lastPrunedEpoch primitives.Epoch
fs afero.Fs
retentionSlots primitives.Slot
prunedBefore atomic.Uint64
}
// Save saves blobs given a list of sidecars.
@@ -89,14 +94,7 @@ func (bs *BlobStorage) Save(sidecar blocks.VerifiedROBlob) error {
log.WithFields(logging.BlobFields(sidecar.ROBlob)).Debug("ignoring a duplicate blob sidecar Save attempt")
return nil
}
if bs.shouldPrune(sidecar.Slot()) {
go func() {
err := bs.pruneOlderThan(sidecar.Slot())
if err != nil {
log.WithError(err).Errorf("failed to prune blobs from slot %d", sidecar.Slot())
}
}()
}
bs.tryPrune(sidecar.Slot())
// Serialize the ethpb.BlobSidecar to binary data using SSZ.
sidecarData, err := sidecar.MarshalSSZ()
@@ -238,49 +236,50 @@ func (p blobNamer) path() string {
// Prune prunes blobs in the base directory based on the retention epoch.
// It deletes blobs older than currentEpoch - (retentionEpochs+bufferEpochs).
// This is so that we keep a slight buffer and blobs are deleted after n+2 epochs.
func (bs *BlobStorage) Prune(currentSlot primitives.Slot) error {
func (bs *BlobStorage) Prune(pruneBefore primitives.Slot) error {
t := time.Now()
retentionSlots, err := slots.EpochStart(bs.retentionEpochs + bufferEpochs)
if err != nil {
return err
}
if currentSlot < retentionSlots {
return nil // Overflow would occur
}
log.Debug("Pruning old blobs")
folders, err := afero.ReadDir(bs.fs, ".")
if err != nil {
return err
}
var totalPruned int
for _, folder := range folders {
if folder.IsDir() {
num, err := bs.processFolder(folder, currentSlot, retentionSlots)
if err != nil {
return err
}
blobsPrunedCounter.Add(float64(num))
blobsTotalGauge.Add(-float64(num))
totalPruned += num
var dirs []string
err := afero.Walk(bs.fs, ".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrap(err, "failed to walk blob storage directory")
}
if info.IsDir() && path != "." {
dirs = append(dirs, path)
}
return nil
})
if err != nil {
return errors.Wrap(err, "failed to build directories list")
}
pruneTime := time.Since(t)
log.WithFields(log.Fields{
"lastPrunedEpoch": slots.ToEpoch(currentSlot - retentionSlots),
"pruneTime": pruneTime,
"numberBlobsPruned": totalPruned,
}).Debug("Pruned old blobs")
var totalPruned int
for _, dir := range dirs {
num, err := bs.processFolder(dir, pruneBefore)
if err != nil {
return errors.Wrapf(err, "failed to process folder %s", dir)
}
blobsPrunedCounter.Add(float64(num))
blobsTotalGauge.Add(-float64(num))
totalPruned += num
}
if totalPruned > 0 {
pruneTime := time.Since(t)
log.WithFields(log.Fields{
"lastPrunedEpoch": slots.ToEpoch(pruneBefore),
"pruneTime": pruneTime,
"numberBlobsPruned": totalPruned,
}).Debug("Pruned old blobs")
}
return nil
}
// processFolder will delete the folder of blobs if the blob slot is outside the
// retention period. We determine the slot by looking at the first blob in the folder.
func (bs *BlobStorage) processFolder(folder os.FileInfo, currentSlot, retentionSlots primitives.Slot) (int, error) {
f, err := bs.fs.Open(filepath.Join(folder.Name(), "0."+sszExt))
func (bs *BlobStorage) processFolder(folder string, pruneBefore primitives.Slot) (int, error) {
f, err := bs.fs.Open(filepath.Join(folder, "0."+sszExt))
if err != nil {
return 0, err
}
@@ -295,12 +294,12 @@ func (bs *BlobStorage) processFolder(folder os.FileInfo, currentSlot, retentionS
return 0, err
}
var num int
if slot < (currentSlot - retentionSlots) {
num, err = bs.countFiles(folder.Name())
if slot < pruneBefore {
num, err = bs.countFiles(folder)
if err != nil {
return 0, err
}
if err = bs.fs.RemoveAll(folder.Name()); err != nil {
if err = bs.fs.RemoveAll(folder); err != nil {
return 0, errors.Wrapf(err, "failed to delete blob %s", f.Name())
}
}
@@ -328,24 +327,24 @@ func (bs *BlobStorage) Delete(root [32]byte) error {
return nil
}
// shouldPrune checks whether pruning should be triggered based on the given slot.
func (bs *BlobStorage) shouldPrune(slot primitives.Slot) bool {
if slots.SinceEpochStarts(slot) < params.BeaconConfig().SlotsPerEpoch/2 {
return false
// tryPrune checks whether we should prune and then calls prune
func (bs *BlobStorage) tryPrune(latest primitives.Slot) {
pruned := uint64(pruneBefore(latest, bs.retentionSlots))
if bs.prunedBefore.Swap(pruned) == pruned {
return
}
if slots.ToEpoch(slot) == bs.lastPrunedEpoch {
return false
}
return true
go func() {
if err := bs.Prune(primitives.Slot(pruned)); err != nil {
log.WithError(err).Errorf("failed to prune blobs from slot %d", latest)
}
}()
}
// pruneOlderThan prunes blobs in the base directory based on the retention epoch and current slot.
func (bs *BlobStorage) pruneOlderThan(slot primitives.Slot) error {
err := bs.Prune(slot)
if err != nil {
return err
func pruneBefore(latest primitives.Slot, offset primitives.Slot) primitives.Slot {
// Safely compute the first slot in the epoch for the latest slot
latest = latest - latest%params.BeaconConfig().SlotsPerEpoch
if latest < offset {
return 0
}
// Update lastPrunedEpoch to the current epoch.
bs.lastPrunedEpoch = slots.ToEpoch(slot)
return nil
return latest - offset
}

View File

@@ -79,7 +79,7 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
require.NoError(t, err)
// Slot in first half of epoch therefore should not prune
require.Equal(t, false, bs.shouldPrune(testSidecars[0].Slot()))
bs.tryPrune(testSidecars[0].Slot())
err = bs.Save(testSidecars[0])
require.NoError(t, err)
actual, err := bs.Get(testSidecars[0].BlockRoot(), testSidecars[0].Index)
@@ -92,7 +92,7 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
testSidecars1, err := verification.BlobSidecarSliceNoop(sidecars)
require.NoError(t, err)
// Slot in first half of epoch therefore should not prune
require.Equal(t, false, bs.shouldPrune(testSidecars1[0].Slot()))
bs.tryPrune(testSidecars1[0].Slot())
err = bs.Save(testSidecars1[0])
require.NoError(t, err)
// Check previous saved sidecar was not pruned
@@ -107,13 +107,13 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
require.NoError(t, err)
_, sidecars = util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 131187, fieldparams.MaxBlobsPerBlock)
testSidecars, err = verification.BlobSidecarSliceNoop(sidecars)
testSidecars2, err := verification.BlobSidecarSliceNoop(sidecars)
// Slot in second half of epoch therefore should prune
require.Equal(t, true, bs.shouldPrune(testSidecars[0].Slot()))
bs.tryPrune(testSidecars2[0].Slot())
require.NoError(t, err)
err = bs.Save(testSidecars[0])
err = bs.Save(testSidecars2[0])
require.NoError(t, err)
err = pollUntil(t, fs, 1)
err = pollUntil(t, fs, 3)
require.NoError(t, err)
})
}
@@ -143,26 +143,6 @@ func pollUntil(t *testing.T, fs afero.Fs, expected int) error {
return nil
}
func TestShouldPrune(t *testing.T) {
bs := NewEphemeralBlobStorage(t)
bs.lastPrunedEpoch = 5
// Slot is before the midpoint of the epoch
slot1 := primitives.Slot(100)
p1 := bs.shouldPrune(slot1)
require.Equal(t, false, p1)
// Slot is after the midpoint of the epoch, but same epoch as last pruning
slot2 := primitives.Slot(178)
p2 := bs.shouldPrune(slot2)
require.Equal(t, false, p2)
// Slot is after the midpoint of the epoch
slot3 := primitives.Slot(8018)
p3 := bs.shouldPrune(slot3)
require.Equal(t, true, p3)
}
func TestBlobIndicesBounds(t *testing.T) {
fs, bs, err := NewEphemeralBlobStorageWithFs(t)
require.NoError(t, err)
@@ -208,7 +188,7 @@ func TestBlobStoragePrune(t *testing.T) {
require.NoError(t, bs.Save(sidecar))
}
require.NoError(t, bs.Prune(currentSlot))
require.NoError(t, bs.Prune(currentSlot-bs.retentionSlots))
remainingFolders, err := afero.ReadDir(fs, ".")
require.NoError(t, err)
@@ -228,7 +208,7 @@ func TestBlobStoragePrune(t *testing.T) {
slot += 10000
}
require.NoError(t, bs.Prune(currentSlot))
require.NoError(t, bs.Prune(currentSlot-bs.retentionSlots))
remainingFolders, err := afero.ReadDir(fs, ".")
require.NoError(t, err)

View File

@@ -4,6 +4,7 @@ import (
"testing"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/spf13/afero"
)
@@ -18,8 +19,11 @@ func NewEphemeralBlobStorage(_ testing.TB) *BlobStorage {
// in order to interact with it outside the parameters of the BlobStorage api.
func NewEphemeralBlobStorageWithFs(_ testing.TB) (afero.Fs, *BlobStorage, error) {
fs := afero.NewMemMapFs()
retentionEpoch := params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
return fs, &BlobStorage{fs: fs, retentionEpochs: retentionEpoch}, nil
s, err := slots.EpochStart(params.BeaconConfig().MinEpochsForBlobsSidecarsRequest)
if err != nil {
return fs, &BlobStorage{}, err
}
return fs, &BlobStorage{fs: fs, retentionSlots: s}, nil
}
type BlobMocker struct {

View File

@@ -5,7 +5,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/spf13/afero"
)
@@ -31,10 +30,7 @@ var (
})
)
func (bs *BlobStorage) Initialize(lastFinalizedSlot primitives.Slot) error {
if err := bs.Prune(lastFinalizedSlot); err != nil {
return fmt.Errorf("failed to prune from finalized slot %d: %w", lastFinalizedSlot, err)
}
func (bs *BlobStorage) Initialize() error {
if err := bs.collectTotalBlobMetric(); err != nil {
return fmt.Errorf("failed to initialize blob metrics: %w", err)
}

View File

@@ -105,7 +105,6 @@ go_test(
"//beacon-chain/execution/types:go_default_library",
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",

View File

@@ -15,7 +15,6 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/types"
"github.com/prysmaticlabs/prysm/v4/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
@@ -489,6 +488,10 @@ func (s *Service) GetPayloadBodiesByHash(ctx context.Context, executionBlockHash
defer span.End()
result := make([]*pb.ExecutionPayloadBodyV1, 0)
// Exit early if there are no execution hashes.
if len(executionBlockHashes) == 0 {
return result, nil
}
err := s.rpcClient.CallContext(ctx, &result, GetPayloadBodiesByHashV1, executionBlockHashes)
for i, item := range result {
@@ -621,31 +624,15 @@ func (s *Service) ReconstructFullBellatrixBlockBatch(
}
func (s *Service) retrievePayloadFromExecutionHash(ctx context.Context, executionBlockHash common.Hash, header interfaces.ExecutionData, version int) (interfaces.ExecutionData, error) {
if features.Get().EnableOptionalEngineMethods {
pBodies, err := s.GetPayloadBodiesByHash(ctx, []common.Hash{executionBlockHash})
if err != nil {
return nil, fmt.Errorf("could not get payload body by hash %#x: %v", executionBlockHash, err)
}
if len(pBodies) != 1 {
return nil, errors.Errorf("could not retrieve the correct number of payload bodies: wanted 1 but got %d", len(pBodies))
}
bdy := pBodies[0]
return fullPayloadFromPayloadBody(header, bdy, version)
}
executionBlock, err := s.ExecutionBlockByHash(ctx, executionBlockHash, true /* with txs */)
pBodies, err := s.GetPayloadBodiesByHash(ctx, []common.Hash{executionBlockHash})
if err != nil {
return nil, fmt.Errorf("could not fetch execution block with txs by hash %#x: %v", executionBlockHash, err)
return nil, fmt.Errorf("could not get payload body by hash %#x: %v", executionBlockHash, err)
}
if executionBlock == nil {
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionBlockHash)
if len(pBodies) != 1 {
return nil, errors.Errorf("could not retrieve the correct number of payload bodies: wanted 1 but got %d", len(pBodies))
}
if bytes.Equal(executionBlock.Hash.Bytes(), []byte{}) {
return nil, ErrEmptyBlockHash
}
executionBlock.Version = version
return fullPayloadFromExecutionBlock(version, header, executionBlock)
bdy := pBodies[0]
return fullPayloadFromPayloadBody(header, bdy, version)
}
func (s *Service) retrievePayloadsFromExecutionHashes(
@@ -654,19 +641,12 @@ func (s *Service) retrievePayloadsFromExecutionHashes(
validExecPayloads []int,
blindedBlocks []interfaces.ReadOnlySignedBeaconBlock) ([]interfaces.SignedBeaconBlock, error) {
fullBlocks := make([]interfaces.SignedBeaconBlock, len(blindedBlocks))
var execBlocks []*pb.ExecutionBlock
var payloadBodies []*pb.ExecutionPayloadBodyV1
var err error
if features.Get().EnableOptionalEngineMethods {
payloadBodies, err = s.GetPayloadBodiesByHash(ctx, executionHashes)
if err != nil {
return nil, fmt.Errorf("could not fetch payload bodies by hash %#x: %v", executionHashes, err)
}
} else {
execBlocks, err = s.ExecutionBlocksByHashes(ctx, executionHashes, true /* with txs*/)
if err != nil {
return nil, fmt.Errorf("could not fetch execution blocks with txs by hash %#x: %v", executionHashes, err)
}
payloadBodies, err = s.GetPayloadBodiesByHash(ctx, executionHashes)
if err != nil {
return nil, fmt.Errorf("could not fetch payload bodies by hash %#x: %v", executionHashes, err)
}
// For each valid payload, we reconstruct the full block from it with the
@@ -674,32 +654,17 @@ func (s *Service) retrievePayloadsFromExecutionHashes(
for sliceIdx, realIdx := range validExecPayloads {
var payload interfaces.ExecutionData
bblock := blindedBlocks[realIdx]
if features.Get().EnableOptionalEngineMethods {
b := payloadBodies[sliceIdx]
if b == nil {
return nil, fmt.Errorf("received nil payload body for request by hash %#x", executionHashes[sliceIdx])
}
header, err := bblock.Block().Body().Execution()
if err != nil {
return nil, err
}
payload, err = fullPayloadFromPayloadBody(header, b, bblock.Version())
if err != nil {
return nil, err
}
} else {
b := execBlocks[sliceIdx]
if b == nil {
return nil, fmt.Errorf("received nil execution block for request by hash %#x", executionHashes[sliceIdx])
}
header, err := bblock.Block().Body().Execution()
if err != nil {
return nil, err
}
payload, err = fullPayloadFromExecutionBlock(bblock.Version(), header, b)
if err != nil {
return nil, err
}
b := payloadBodies[sliceIdx]
if b == nil {
return nil, fmt.Errorf("received nil payload body for request by hash %#x", executionHashes[sliceIdx])
}
header, err := bblock.Block().Body().Execution()
if err != nil {
return nil, err
}
payload, err = fullPayloadFromPayloadBody(header, b, bblock.Version())
if err != nil {
return nil, err
}
fullBlock, err := blocks.BuildSignedBeaconBlockFromExecutionPayload(bblock, payload.Proto())
if err != nil {

View File

@@ -20,7 +20,6 @@ import (
"github.com/holiman/uint256"
"github.com/pkg/errors"
mocks "github.com/prysmaticlabs/prysm/v4/beacon-chain/execution/testing"
"github.com/prysmaticlabs/prysm/v4/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
@@ -758,26 +757,7 @@ func TestReconstructFullBellatrixBlock(t *testing.T) {
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
jsonPayload["transactions"] = []hexutil.Bytes{encodedBinaryTxs[0]}
wrappedPayload, err := blocks.WrappedExecutionPayload(payload)
require.NoError(t, err)
@@ -792,7 +772,7 @@ func TestReconstructFullBellatrixBlock(t *testing.T) {
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": jsonPayload,
"result": []map[string]interface{}{jsonPayload},
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
@@ -869,26 +849,7 @@ func TestReconstructFullBellatrixBlockBatch(t *testing.T) {
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
jsonPayload["transactions"] = []hexutil.Bytes{encodedBinaryTxs[0]}
wrappedPayload, err := blocks.WrappedExecutionPayload(payload)
require.NoError(t, err)
@@ -912,20 +873,12 @@ func TestReconstructFullBellatrixBlockBatch(t *testing.T) {
require.NoError(t, r.Body.Close())
}()
respJSON := []map[string]interface{}{
{
"jsonrpc": "2.0",
"id": 1,
"result": jsonPayload,
},
{
"jsonrpc": "2.0",
"id": 2,
"result": jsonPayload,
},
respJSON := map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"result": []map[string]interface{}{jsonPayload, jsonPayload},
}
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
require.NoError(t, json.NewEncoder(w).Encode(respJSON))
}))
defer srv.Close()
@@ -1288,6 +1241,10 @@ func fixtures() map[string]interface{} {
BlockHash: foo[:],
Transactions: [][]byte{foo[:]},
}
executionPayloadBodyFixture := &pb.ExecutionPayloadBodyV1{
Transactions: [][]byte{foo[:]},
Withdrawals: []*pb.Withdrawal{},
}
executionPayloadFixtureCapella := &pb.ExecutionPayloadCapella{
ParentHash: foo[:],
FeeRecipient: bar,
@@ -1459,6 +1416,7 @@ func fixtures() map[string]interface{} {
}
return map[string]interface{}{
"ExecutionBlock": executionBlock,
"ExecutionPayloadBody": executionPayloadBodyFixture,
"ExecutionPayload": executionPayloadFixture,
"ExecutionPayloadCapella": executionPayloadFixtureCapella,
"ExecutionPayloadDeneb": executionPayloadFixtureDeneb,
@@ -2007,10 +1965,6 @@ func newPayloadV3Setup(t *testing.T, status *pb.PayloadStatus, payload *pb.Execu
}
func TestCapella_PayloadBodiesByHash(t *testing.T) {
resetFn := features.InitWithReset(&features.Flags{
EnableOptionalEngineMethods: true,
})
defer resetFn()
t.Run("empty response works", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@@ -2067,7 +2021,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
service := &Service{}
service.rpcClient = rpcClient
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
bRoot := [32]byte{}
copy(bRoot[:], "hash")
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
require.NoError(t, err)
require.Equal(t, 1, len(results))
@@ -2113,7 +2069,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
service := &Service{}
service.rpcClient = rpcClient
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
bRoot := [32]byte{}
copy(bRoot[:], "hash")
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot, bRoot, bRoot})
require.NoError(t, err)
require.Equal(t, 3, len(results))
@@ -2154,7 +2112,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
service := &Service{}
service.rpcClient = rpcClient
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
bRoot := [32]byte{}
copy(bRoot[:], "hash")
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
require.NoError(t, err)
require.Equal(t, 1, len(results))
@@ -2204,7 +2164,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
service := &Service{}
service.rpcClient = rpcClient
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
bRoot := [32]byte{}
copy(bRoot[:], "hash")
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot})
require.NoError(t, err)
require.Equal(t, 2, len(results))
@@ -2247,7 +2209,9 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
service := &Service{}
service.rpcClient = rpcClient
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{})
bRoot := [32]byte{}
copy(bRoot[:], "hash")
results, err := service.GetPayloadBodiesByHash(ctx, []common.Hash{bRoot, bRoot, bRoot})
require.NoError(t, err)
require.Equal(t, 3, len(results))
@@ -2258,10 +2222,6 @@ func TestCapella_PayloadBodiesByHash(t *testing.T) {
}
func TestCapella_PayloadBodiesByRange(t *testing.T) {
resetFn := features.InitWithReset(&features.Flags{
EnableOptionalEngineMethods: true,
})
defer resetFn()
t.Run("empty response works", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@@ -2509,10 +2469,6 @@ func TestCapella_PayloadBodiesByRange(t *testing.T) {
}
func Test_ExchangeCapabilities(t *testing.T) {
resetFn := features.InitWithReset(&features.Flags{
EnableOptionalEngineMethods: true,
})
defer resetFn()
t.Run("empty response works", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")

View File

@@ -28,7 +28,6 @@ go_library(
"//beacon-chain/forkchoice:go_default_library",
"//beacon-chain/forkchoice/types:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/forkchoice:go_default_library",

View File

@@ -11,10 +11,6 @@ import (
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
// orphanLateBlockFirstThreshold is the number of seconds after which we
// consider a block to be late, and thus a candidate to being reorged.
const orphanLateBlockFirstThreshold = 4
// ProcessAttestationsThreshold is the number of seconds after which we
// process attestations for the current slot
const ProcessAttestationsThreshold = 10
@@ -137,7 +133,8 @@ func (n *Node) setNodeAndParentValidated(ctx context.Context) error {
// slot will have secs = 3 below.
func (n *Node) arrivedEarly(genesisTime uint64) (bool, error) {
secs, err := slots.SecondsSinceSlotStart(n.slot, genesisTime, n.timestamp)
return secs < orphanLateBlockFirstThreshold, err
votingWindow := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
return secs < votingWindow, err
}
// arrivedAfterOrphanCheck returns whether this block was inserted after the

View File

@@ -277,6 +277,7 @@ func TestNode_TimeStampsChecks(t *testing.T) {
require.NoError(t, err)
require.Equal(t, false, late)
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
// late block
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
root = [32]byte{'b'}

View File

@@ -3,7 +3,6 @@ package doublylinkedtree
import (
"time"
"github.com/prysmaticlabs/prysm/v4/config/features"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)
@@ -98,9 +97,6 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) {
// This function needs to be called only when proposing a block and all
// attestation processing has already happened.
func (f *ForkChoice) GetProposerHead() [32]byte {
if features.Get().DisableReorgLateBlocks {
return f.CachedHeadRoot()
}
head := f.store.headNode
if head == nil {
return [32]byte{}

View File

@@ -28,6 +28,7 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) {
}
f.ProcessAttestation(ctx, attesters, root, 0)
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1)
st, root, err = prepareForkchoiceState(ctx, 2, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
require.NoError(t, err)
@@ -125,6 +126,7 @@ func TestForkChoice_GetProposerHead(t *testing.T) {
headRoot, err := f.Head(ctx)
require.NoError(t, err)
require.Equal(t, root, headRoot)
orphanLateBlockFirstThreshold := params.BeaconConfig().SecondsPerSlot / params.BeaconConfig().IntervalsPerSlot
f.store.headNode.timestamp -= params.BeaconConfig().SecondsPerSlot - orphanLateBlockFirstThreshold
t.Run("head is weak", func(t *testing.T) {
require.Equal(t, parentRoot, f.GetProposerHead())

View File

@@ -6,6 +6,7 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/gateway",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//api:go_default_library",
"//api/gateway:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -2,6 +2,7 @@ package gateway
import (
gwruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/api/gateway"
"github.com/prysmaticlabs/prysm/v4/cmd/beacon-chain/flags"
ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -40,7 +41,7 @@ func DefaultConfig(enableDebugRPCEndpoints bool, httpModules string) MuxConfig {
},
}),
gwruntime.WithMarshalerOption(
"text/event-stream", &gwruntime.EventSourceJSONPb{},
api.EventStreamMediaType, &gwruntime.EventSourceJSONPb{},
),
)
v1AlphaPbHandler = &gateway.PbMux{

View File

@@ -98,7 +98,8 @@ type BeaconNode struct {
syncCommitteePool synccommittee.Pool
blsToExecPool blstoexec.PoolManager
depositCache cache.DepositCache
proposerIdsCache *cache.ProposerPayloadIDsCache
trackedValidatorsCache *cache.TrackedValidatorsCache
payloadIDCache *cache.PayloadIDCache
stateFeed *event.Feed
blockFeed *event.Feed
opFeed *event.Feed
@@ -179,10 +180,11 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
slashingsPool: slashings.NewPool(),
syncCommitteePool: synccommittee.NewPool(),
blsToExecPool: blstoexec.NewPool(),
trackedValidatorsCache: cache.NewTrackedValidatorsCache(),
payloadIDCache: cache.NewPayloadIDCache(),
slasherBlockHeadersFeed: new(event.Feed),
slasherAttestationsFeed: new(event.Feed),
serviceFlagOpts: &serviceFlagOpts{},
proposerIdsCache: cache.NewProposerPayloadIDsCache(),
}
beacon.initialSyncComplete = make(chan struct{})
@@ -226,12 +228,8 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
return nil, err
}
if beacon.finalizedStateAtStartUp != nil {
if err := beacon.BlobStorage.Initialize(beacon.finalizedStateAtStartUp.Slot()); err != nil {
return nil, fmt.Errorf("failed to initialize blob storage: %w", err)
}
} else {
log.Warn("No finalized beacon state at startup, cannot prune blobs")
if err := beacon.BlobStorage.Initialize(); err != nil {
return nil, fmt.Errorf("failed to initialize blob storage: %w", err)
}
log.Debugln("Registering P2P Service")
@@ -659,10 +657,11 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
blockchain.WithStateGen(b.stateGen),
blockchain.WithSlasherAttestationsFeed(b.slasherAttestationsFeed),
blockchain.WithFinalizedStateAtStartUp(b.finalizedStateAtStartUp),
blockchain.WithProposerIdsCache(b.proposerIdsCache),
blockchain.WithClockSynchronizer(gs),
blockchain.WithSyncComplete(syncComplete),
blockchain.WithBlobStorage(b.BlobStorage),
blockchain.WithTrackedValidatorsCache(b.trackedValidatorsCache),
blockchain.WithPayloadIDCache(b.payloadIDCache),
)
blockchainService, err := blockchain.NewService(b.ctx, opts...)
@@ -893,11 +892,12 @@ func (b *BeaconNode) registerRPCService(router *mux.Router) error {
StateGen: b.stateGen,
EnableDebugRPCEndpoints: enableDebugRPCEndpoints,
MaxMsgSize: maxMsgSize,
ProposerIdsCache: b.proposerIdsCache,
BlockBuilder: b.fetchBuilderService(),
Router: router,
ClockWaiter: b.clockWaiter,
BlobStorage: b.BlobStorage,
TrackedValidatorsCache: b.trackedValidatorsCache,
PayloadIDCache: b.payloadIDCache,
})
return b.services.RegisterService(rpcService)

View File

@@ -23,6 +23,7 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//consensus-types/wrapper:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/metadata:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@@ -6,6 +6,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/metadata"
)
@@ -45,17 +46,17 @@ func InitializeDataMaps() {
},
bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{Body: &ethpb.BeaconBlockBodyBellatrix{}}},
&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{Body: &ethpb.BeaconBlockBodyBellatrix{ExecutionPayload: &enginev1.ExecutionPayload{}}}},
)
},
bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockCapella{Block: &ethpb.BeaconBlockCapella{Body: &ethpb.BeaconBlockBodyCapella{}}},
&ethpb.SignedBeaconBlockCapella{Block: &ethpb.BeaconBlockCapella{Body: &ethpb.BeaconBlockBodyCapella{ExecutionPayload: &enginev1.ExecutionPayloadCapella{}}}},
)
},
bytesutil.ToBytes4(params.BeaconConfig().DenebForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockDeneb{Block: &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{}}},
&ethpb.SignedBeaconBlockDeneb{Block: &ethpb.BeaconBlockDeneb{Body: &ethpb.BeaconBlockBodyDeneb{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
)
},
}

View File

@@ -955,9 +955,9 @@ func (s *Server) PublishBlindedBlock(w http.ResponseWriter, r *http.Request) {
}
isSSZ := httputil.SszRequested(r)
if isSSZ {
s.publishBlindedBlockSSZ(ctx, w, r)
s.publishBlindedBlockSSZ(ctx, w, r, false)
} else {
s.publishBlindedBlock(ctx, w, r)
s.publishBlindedBlock(ctx, w, r, false)
}
}
@@ -980,20 +980,25 @@ func (s *Server) PublishBlindedBlockV2(w http.ResponseWriter, r *http.Request) {
}
isSSZ := httputil.SszRequested(r)
if isSSZ {
s.publishBlindedBlockSSZ(ctx, w, r)
s.publishBlindedBlockSSZ(ctx, w, r, true)
} else {
s.publishBlindedBlock(ctx, w, r)
s.publishBlindedBlock(ctx, w, r, true)
}
}
func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
body, err := io.ReadAll(r.Body)
if err != nil {
httputil.HandleError(w, "Could not read request body: "+err.Error(), http.StatusInternalServerError)
return
}
versionHeader := r.Header.Get(api.VersionHeader)
if versionRequired && versionHeader == "" {
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
}
denebBlock := &eth.SignedBlindedBeaconBlockDeneb{}
if err := denebBlock.UnmarshalSSZ(body); err == nil {
if err = denebBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedDeneb{
BlindedDeneb: denebBlock,
@@ -1006,8 +1011,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Deneb) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
http.StatusBadRequest,
)
return
}
capellaBlock := &eth.SignedBlindedBeaconBlockCapella{}
if err := capellaBlock.UnmarshalSSZ(body); err == nil {
if err = capellaBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedCapella{
BlindedCapella: capellaBlock,
@@ -1020,8 +1034,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Capella) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
http.StatusBadRequest,
)
return
}
bellatrixBlock := &eth.SignedBlindedBeaconBlockBellatrix{}
if err := bellatrixBlock.UnmarshalSSZ(body); err == nil {
if err = bellatrixBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_BlindedBellatrix{
BlindedBellatrix: bellatrixBlock,
@@ -1034,10 +1057,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
http.StatusBadRequest,
)
return
}
// blinded is not supported before bellatrix hardfork
altairBlock := &eth.SignedBeaconBlockAltair{}
if err := altairBlock.UnmarshalSSZ(body); err == nil {
if err = altairBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Altair{
Altair: altairBlock,
@@ -1050,8 +1080,17 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Altair) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
http.StatusBadRequest,
)
return
}
phase0Block := &eth.SignedBeaconBlock{}
if err := phase0Block.UnmarshalSSZ(body); err == nil {
if err = phase0Block.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Phase0{
Phase0: phase0Block,
@@ -1064,20 +1103,34 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Phase0) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
http.StatusBadRequest,
)
return
}
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
body, err := io.ReadAll(r.Body)
if err != nil {
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
return
}
versionHeader := r.Header.Get(api.VersionHeader)
var blockVersionError string
if versionRequired && versionHeader == "" {
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
}
var consensusBlock *eth.GenericSignedBeaconBlock
var denebBlock *shared.SignedBlindedBeaconBlockDeneb
if err = unmarshalStrict(body, &denebBlock); err == nil {
consensusBlock, err := denebBlock.ToGeneric()
consensusBlock, err = denebBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1086,14 +1139,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Deneb) {
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Deneb), err.Error())
}
}
if versionHeader == version.String(version.Deneb) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
http.StatusBadRequest,
)
return
}
var capellaBlock *shared.SignedBlindedBeaconBlockCapella
if err = unmarshalStrict(body, &capellaBlock); err == nil {
consensusBlock, err := capellaBlock.ToGeneric()
consensusBlock, err = capellaBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1102,14 +1160,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Capella) {
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Capella), err.Error())
}
}
if versionHeader == version.String(version.Capella) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
http.StatusBadRequest,
)
return
}
var bellatrixBlock *shared.SignedBlindedBeaconBlockBellatrix
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
consensusBlock, err := bellatrixBlock.ToGeneric()
consensusBlock, err = bellatrixBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1118,13 +1181,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Bellatrix), err.Error())
}
}
if versionHeader == version.String(version.Bellatrix) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
http.StatusBadRequest,
)
return
}
var altairBlock *shared.SignedBeaconBlockAltair
if err = unmarshalStrict(body, &altairBlock); err == nil {
consensusBlock, err := altairBlock.ToGeneric()
consensusBlock, err = altairBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1133,13 +1202,19 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Altair) {
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Altair), err.Error())
}
}
if versionHeader == version.String(version.Altair) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
http.StatusBadRequest,
)
return
}
var phase0Block *shared.SignedBeaconBlock
if err = unmarshalStrict(body, &phase0Block); err == nil {
consensusBlock, err := phase0Block.ToGeneric()
consensusBlock, err = phase0Block.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1148,14 +1223,17 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter,
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Phase0) {
blockVersionError = fmt.Sprintf("could not decode %s request body into consensus block: %v", version.String(version.Phase0), err.Error())
}
}
if versionHeader == "" {
blockVersionError = fmt.Sprintf("please add the api header %s to see specific type errors", api.VersionHeader)
if versionHeader == version.String(version.Phase0) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
http.StatusBadRequest,
)
return
}
httputil.HandleError(w, "Body does not represent a valid block type: "+blockVersionError, http.StatusBadRequest)
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
// PublishBlock instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
@@ -1174,9 +1252,9 @@ func (s *Server) PublishBlock(w http.ResponseWriter, r *http.Request) {
}
isSSZ := httputil.SszRequested(r)
if isSSZ {
s.publishBlockSSZ(ctx, w, r)
s.publishBlockSSZ(ctx, w, r, false)
} else {
s.publishBlock(ctx, w, r)
s.publishBlock(ctx, w, r, false)
}
}
@@ -1197,20 +1275,26 @@ func (s *Server) PublishBlockV2(w http.ResponseWriter, r *http.Request) {
}
isSSZ := httputil.SszRequested(r)
if isSSZ {
s.publishBlockSSZ(ctx, w, r)
s.publishBlockSSZ(ctx, w, r, true)
} else {
s.publishBlock(ctx, w, r)
s.publishBlock(ctx, w, r, true)
}
}
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
body, err := io.ReadAll(r.Body)
if err != nil {
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
return
}
versionHeader := r.Header.Get(api.VersionHeader)
if versionRequired && versionHeader == "" {
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
return
}
denebBlock := &eth.SignedBeaconBlockContentsDeneb{}
if err := denebBlock.UnmarshalSSZ(body); err == nil {
if err = denebBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Deneb{
Deneb: denebBlock,
@@ -1223,8 +1307,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Deneb) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
http.StatusBadRequest,
)
return
}
capellaBlock := &eth.SignedBeaconBlockCapella{}
if err := capellaBlock.UnmarshalSSZ(body); err == nil {
if err = capellaBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Capella{
Capella: capellaBlock,
@@ -1237,8 +1330,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Capella) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
http.StatusBadRequest,
)
return
}
bellatrixBlock := &eth.SignedBeaconBlockBellatrix{}
if err := bellatrixBlock.UnmarshalSSZ(body); err == nil {
if err = bellatrixBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Bellatrix{
Bellatrix: bellatrixBlock,
@@ -1251,8 +1353,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
http.StatusBadRequest,
)
return
}
altairBlock := &eth.SignedBeaconBlockAltair{}
if err := altairBlock.UnmarshalSSZ(body); err == nil {
if err = altairBlock.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Altair{
Altair: altairBlock,
@@ -1265,8 +1376,17 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Altair) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
http.StatusBadRequest,
)
return
}
phase0Block := &eth.SignedBeaconBlock{}
if err := phase0Block.UnmarshalSSZ(body); err == nil {
if err = phase0Block.UnmarshalSSZ(body); err == nil {
genericBlock := &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Phase0{
Phase0: phase0Block,
@@ -1279,20 +1399,35 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r *
s.proposeBlock(ctx, w, genericBlock)
return
}
if versionHeader == version.String(version.Phase0) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
http.StatusBadRequest,
)
return
}
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request) {
func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, versionRequired bool) {
body, err := io.ReadAll(r.Body)
if err != nil {
httputil.HandleError(w, "Could not read request body", http.StatusInternalServerError)
return
}
versionHeader := r.Header.Get(api.VersionHeader)
var blockVersionError string
if versionRequired && versionHeader == "" {
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
return
}
var consensusBlock *eth.GenericSignedBeaconBlock
var denebBlockContents *shared.SignedBeaconBlockContentsDeneb
if err = unmarshalStrict(body, &denebBlockContents); err == nil {
consensusBlock, err := denebBlockContents.ToGeneric()
consensusBlock, err = denebBlockContents.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1301,13 +1436,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Deneb) {
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Deneb), err.Error())
}
}
if versionHeader == version.String(version.Deneb) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Deneb), err.Error()),
http.StatusBadRequest,
)
return
}
var capellaBlock *shared.SignedBeaconBlockCapella
if err = unmarshalStrict(body, &capellaBlock); err == nil {
consensusBlock, err := capellaBlock.ToGeneric()
consensusBlock, err = capellaBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1316,13 +1457,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Capella) {
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Capella), err.Error())
}
}
if versionHeader == version.String(version.Capella) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Capella), err.Error()),
http.StatusBadRequest,
)
return
}
var bellatrixBlock *shared.SignedBeaconBlockBellatrix
if err = unmarshalStrict(body, &bellatrixBlock); err == nil {
consensusBlock, err := bellatrixBlock.ToGeneric()
consensusBlock, err = bellatrixBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1331,13 +1478,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Bellatrix) {
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Bellatrix), err.Error())
}
}
if versionHeader == version.String(version.Bellatrix) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Bellatrix), err.Error()),
http.StatusBadRequest,
)
return
}
var altairBlock *shared.SignedBeaconBlockAltair
if err = unmarshalStrict(body, &altairBlock); err == nil {
consensusBlock, err := altairBlock.ToGeneric()
consensusBlock, err = altairBlock.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1346,13 +1499,19 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Altair) {
blockVersionError = fmt.Sprintf(": could not decode %s request body into consensus block: %v", version.String(version.Altair), err.Error())
}
}
if versionHeader == version.String(version.Altair) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Altair), err.Error()),
http.StatusBadRequest,
)
return
}
var phase0Block *shared.SignedBeaconBlock
if err = unmarshalStrict(body, &phase0Block); err == nil {
consensusBlock, err := phase0Block.ToGeneric()
consensusBlock, err = phase0Block.ToGeneric()
if err == nil {
if err = s.validateBroadcast(ctx, r, consensusBlock); err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
@@ -1361,14 +1520,17 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt
s.proposeBlock(ctx, w, consensusBlock)
return
}
if versionHeader == version.String(version.Phase0) {
blockVersionError = fmt.Sprintf(": Could not decode %s request body into consensus block: %v", version.String(version.Phase0), err.Error())
}
}
if versionHeader == "" {
blockVersionError = fmt.Sprintf(": please add the api header %s to see specific type errors", api.VersionHeader)
if versionHeader == version.String(version.Phase0) {
httputil.HandleError(
w,
fmt.Sprintf("Could not decode request body into %s consensus block: %v", version.String(version.Phase0), err.Error()),
http.StatusBadRequest,
)
return
}
httputil.HandleError(w, "Body does not represent a valid block type"+blockVersionError, http.StatusBadRequest)
httputil.HandleError(w, "Body does not represent a valid block type", http.StatusBadRequest)
}
func (s *Server) proposeBlock(ctx context.Context, w http.ResponseWriter, blk *eth.GenericSignedBeaconBlock) {

View File

@@ -501,6 +501,13 @@ func (s *Server) SubmitAttesterSlashing(w http.ResponseWriter, r *http.Request)
httputil.HandleError(w, "Could not insert attester slashing into pool: "+err.Error(), http.StatusInternalServerError)
return
}
// notify events
s.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.AttesterSlashingReceived,
Data: &operation.AttesterSlashingReceivedData{
AttesterSlashing: slashing,
},
})
if !features.Get().DisableBroadcastSlashings {
if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil {
httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError)
@@ -569,6 +576,15 @@ func (s *Server) SubmitProposerSlashing(w http.ResponseWriter, r *http.Request)
httputil.HandleError(w, "Could not insert proposer slashing into pool: "+err.Error(), http.StatusInternalServerError)
return
}
// notify events
s.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.ProposerSlashingReceived,
Data: &operation.ProposerSlashingReceivedData{
ProposerSlashing: slashing,
},
})
if !features.Get().DisableBroadcastSlashings {
if err = s.Broadcaster.Broadcast(ctx, slashing); err != nil {
httputil.HandleError(w, "Could not broadcast slashing object: "+err.Error(), http.StatusInternalServerError)

View File

@@ -1205,10 +1205,12 @@ func TestSubmitAttesterSlashing_Ok(t *testing.T) {
}
broadcaster := &p2pMock.MockBroadcaster{}
chainmock := &blockchainmock.ChainService{State: bs}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
ChainInfoFetcher: chainmock,
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
OperationNotifier: chainmock.OperationNotifier(),
}
toSubmit := shared.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing})
@@ -1295,10 +1297,12 @@ func TestSubmitAttesterSlashing_AcrossFork(t *testing.T) {
}
broadcaster := &p2pMock.MockBroadcaster{}
chainmock := &blockchainmock.ChainService{State: bs}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
ChainInfoFetcher: chainmock,
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
OperationNotifier: chainmock.OperationNotifier(),
}
toSubmit := shared.AttesterSlashingsFromConsensus([]*ethpbv1alpha1.AttesterSlashing{slashing})
@@ -1404,10 +1408,12 @@ func TestSubmitProposerSlashing_Ok(t *testing.T) {
}
broadcaster := &p2pMock.MockBroadcaster{}
chainmock := &blockchainmock.ChainService{State: bs}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
ChainInfoFetcher: chainmock,
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
OperationNotifier: chainmock.OperationNotifier(),
}
toSubmit := shared.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing})
@@ -1486,10 +1492,12 @@ func TestSubmitProposerSlashing_AcrossFork(t *testing.T) {
}
broadcaster := &p2pMock.MockBroadcaster{}
chainmock := &blockchainmock.ChainService{State: bs}
s := &Server{
ChainInfoFetcher: &blockchainmock.ChainService{State: bs},
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
ChainInfoFetcher: chainmock,
SlashingsPool: &slashingsmock.PoolMock{},
Broadcaster: broadcaster,
OperationNotifier: chainmock.OperationNotifier(),
}
toSubmit := shared.ProposerSlashingsFromConsensus([]*ethpbv1alpha1.ProposerSlashing{slashing})

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,9 @@ go_library(
"structs.go",
],
importpath = "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/events",
visibility = ["//beacon-chain:__subpackages__"],
visibility = ["//visibility:public"],
deps = [
"//api:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/feed:go_default_library",
"//beacon-chain/core/feed/operation:go_default_library",
@@ -18,6 +19,7 @@ go_library(
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/rpc/eth/shared:go_default_library",
"//config/params:go_default_library",
"//network/httputil:go_default_library",
"//proto/eth/v1:go_default_library",
"//runtime/version:go_default_library",
@@ -38,6 +40,7 @@ go_test(
"//beacon-chain/core/feed/operation:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/fieldparams:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",

View File

@@ -5,8 +5,10 @@ import (
"encoding/json"
"fmt"
"net/http"
time2 "time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/v4/api"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
@@ -15,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/network/httputil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
@@ -44,6 +47,10 @@ const (
PayloadAttributesTopic = "payload_attributes"
// BlobSidecarTopic represents a new blob sidecar event topic
BlobSidecarTopic = "blob_sidecar"
// ProposerSlashingTopic represents a new proposer slashing event topic
ProposerSlashingTopic = "proposer_slashing"
// AttesterSlashingTopic represents a new attester slashing event topic
AttesterSlashingTopic = "attester_slashing"
)
const topicDataMismatch = "Event data type %T does not correspond to event topic %s"
@@ -61,6 +68,8 @@ var casesHandled = map[string]bool{
BLSToExecutionChangeTopic: true,
PayloadAttributesTopic: true,
BlobSidecarTopic: true,
ProposerSlashingTopic: true,
AttesterSlashingTopic: true,
}
// StreamEvents provides an endpoint to subscribe to the beacon node Server-Sent-Events stream.
@@ -100,16 +109,24 @@ func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request) {
defer stateSub.Unsubscribe()
// Set up SSE response headers
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Content-Type", api.EventStreamMediaType)
w.Header().Set("Connection", "keep-alive")
// Handle each event received and context cancellation.
// We send a keepalive dummy message immediately to prevent clients
// stalling while waiting for the first response chunk.
// After that we send a keepalive dummy message every SECONDS_PER_SLOT
// to prevent anyone (e.g. proxy servers) from closing connections.
sendKeepalive(w, flusher)
keepaliveTicker := time2.NewTicker(time2.Duration(params.BeaconConfig().SecondsPerSlot) * time2.Second)
for {
select {
case event := <-opsChan:
handleBlockOperationEvents(w, flusher, topicsMap, event)
case event := <-stateChan:
s.handleStateEvents(ctx, w, flusher, topicsMap, event)
case <-keepaliveTicker.C:
sendKeepalive(w, flusher)
case <-ctx.Done():
return
}
@@ -190,6 +207,26 @@ func handleBlockOperationEvents(w http.ResponseWriter, flusher http.Flusher, req
KzgCommitment: hexutil.Encode(blobData.Blob.KzgCommitment),
}
send(w, flusher, BlobSidecarTopic, blobEvent)
case operation.AttesterSlashingReceived:
if _, ok := requestedTopics[AttesterSlashingTopic]; !ok {
return
}
attesterSlashingData, ok := event.Data.(*operation.AttesterSlashingReceivedData)
if !ok {
write(w, flusher, topicDataMismatch, event.Data, AttesterSlashingTopic)
return
}
send(w, flusher, AttesterSlashingTopic, shared.AttesterSlashingFromConsensus(attesterSlashingData.AttesterSlashing))
case operation.ProposerSlashingReceived:
if _, ok := requestedTopics[ProposerSlashingTopic]; !ok {
return
}
proposerSlashingData, ok := event.Data.(*operation.ProposerSlashingReceivedData)
if !ok {
write(w, flusher, topicDataMismatch, event.Data, ProposerSlashingTopic)
return
}
send(w, flusher, ProposerSlashingTopic, shared.ProposerSlashingFromConsensus(proposerSlashingData.ProposerSlashing))
}
}
@@ -405,6 +442,10 @@ func send(w http.ResponseWriter, flusher http.Flusher, name string, data interfa
write(w, flusher, "event: %s\ndata: %s\n\n", name, string(j))
}
func sendKeepalive(w http.ResponseWriter, flusher http.Flusher) {
write(w, flusher, ":\n\n")
}
func write(w http.ResponseWriter, flusher http.Flusher, format string, a ...any) {
_, err := fmt.Fprintf(w, format, a...)
if err != nil {

View File

@@ -15,6 +15,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
statefeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
@@ -41,7 +42,15 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
OperationNotifier: &mockChain.MockOperationNotifier{},
}
topics := []string{AttestationTopic, VoluntaryExitTopic, SyncCommitteeContributionTopic, BLSToExecutionChangeTopic, BlobSidecarTopic}
topics := []string{
AttestationTopic,
VoluntaryExitTopic,
SyncCommitteeContributionTopic,
BLSToExecutionChangeTopic,
BlobSidecarTopic,
AttesterSlashingTopic,
ProposerSlashingTopic,
}
for i, topic := range topics {
topics[i] = "topics=" + topic
}
@@ -124,6 +133,65 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
Blob: &vblob,
},
})
s.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.AttesterSlashingReceived,
Data: &operation.AttesterSlashingReceivedData{
AttesterSlashing: &eth.AttesterSlashing{
Attestation_1: &eth.IndexedAttestation{
AttestingIndices: []uint64{0, 1},
Data: &eth.AttestationData{
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
Source: &eth.Checkpoint{
Root: make([]byte, fieldparams.RootLength),
},
Target: &eth.Checkpoint{
Root: make([]byte, fieldparams.RootLength),
},
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
Attestation_2: &eth.IndexedAttestation{
AttestingIndices: []uint64{0, 1},
Data: &eth.AttestationData{
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
Source: &eth.Checkpoint{
Root: make([]byte, fieldparams.RootLength),
},
Target: &eth.Checkpoint{
Root: make([]byte, fieldparams.RootLength),
},
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
},
},
})
s.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.ProposerSlashingReceived,
Data: &operation.ProposerSlashingReceivedData{
ProposerSlashing: &eth.ProposerSlashing{
Header_1: &eth.SignedBeaconBlockHeader{
Header: &eth.BeaconBlockHeader{
ParentRoot: make([]byte, fieldparams.RootLength),
StateRoot: make([]byte, fieldparams.RootLength),
BodyRoot: make([]byte, fieldparams.RootLength),
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
Header_2: &eth.SignedBeaconBlockHeader{
Header: &eth.BeaconBlockHeader{
ParentRoot: make([]byte, fieldparams.RootLength),
StateRoot: make([]byte, fieldparams.RootLength),
BodyRoot: make([]byte, fieldparams.RootLength),
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
},
},
})
time.Sleep(1 * time.Second)
request.Context().Done()
@@ -307,7 +375,9 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
})
}
const operationsResult = `event: attestation
const operationsResult = `:
event: attestation
data: {"aggregation_bits":"0x00","data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}
event: attestation
@@ -325,9 +395,17 @@ data: {"message":{"validator_index":"0","from_bls_pubkey":"0x0000000000000000000
event: blob_sidecar
data: {"block_root":"0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c","index":"0","slot":"0","kzg_commitment":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","versioned_hash":"0x01b0761f87b081d5cf10757ccc89f12be355c70e2e29df288b65b30710dcbcd1"}
event: attester_slashing
data: {"attestation_1":{"attesting_indices":["0","1"],"data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"attestation_2":{"attesting_indices":["0","1"],"data":{"slot":"0","index":"0","beacon_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","source":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"target":{"epoch":"0","root":"0x0000000000000000000000000000000000000000000000000000000000000000"}},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}
event: proposer_slashing
data: {"signed_header_1":{"message":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"signed_header_2":{"message":{"slot":"0","proposer_index":"0","parent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","state_root":"0x0000000000000000000000000000000000000000000000000000000000000000","body_root":"0x0000000000000000000000000000000000000000000000000000000000000000"},"signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}
`
const stateResult = `event: head
const stateResult = `:
event: head
data: {"slot":"0","block":"0x0000000000000000000000000000000000000000000000000000000000000000","state":"0x0000000000000000000000000000000000000000000000000000000000000000","epoch_transition":true,"execution_optimistic":false,"previous_duty_dependent_root":"0x0000000000000000000000000000000000000000000000000000000000000000","current_duty_dependent_root":"0x0000000000000000000000000000000000000000000000000000000000000000"}
event: finalized_checkpoint
@@ -341,17 +419,23 @@ data: {"slot":"0","block":"0xeade62f0457b2fdf48e7d3fc4b60736688286be7c7a3ac4c9a1
`
const payloadAttributesBellatrixResult = `event: payload_attributes
const payloadAttributesBellatrixResult = `:
event: payload_attributes
data: {"version":"bellatrix","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000"}}}
`
const payloadAttributesCapellaResult = `event: payload_attributes
const payloadAttributesCapellaResult = `:
event: payload_attributes
data: {"version":"capella","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[]}}}
`
const payloadAttributesDenebResult = `event: payload_attributes
const payloadAttributesDenebResult = `:
event: payload_attributes
data: {"version":"deneb","data":{"proposer_index":"0","proposal_slot":"1","parent_block_number":"0","parent_block_root":"0x0000000000000000000000000000000000000000000000000000000000000000","parent_block_hash":"0x0000000000000000000000000000000000000000000000000000000000000000","payload_attributes":{"timestamp":"12","prev_randao":"0x0000000000000000000000000000000000000000000000000000000000000000","suggested_fee_recipient":"0x0000000000000000000000000000000000000000","withdrawals":[],"parent_beacon_block_root":"0xbef96cb938fd48b2403d3e662664325abb0102ed12737cbb80d717520e50cf4a"}}}
`

View File

@@ -40,7 +40,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["errors_test.go"],
srcs = [
"errors_test.go",
"request_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/rpc/lookup:go_default_library",

View File

@@ -769,32 +769,36 @@ func ProposerSlashingsToConsensus(src []*ProposerSlashing) ([]*eth.ProposerSlash
func ProposerSlashingsFromConsensus(src []*eth.ProposerSlashing) []*ProposerSlashing {
proposerSlashings := make([]*ProposerSlashing, len(src))
for i, s := range src {
proposerSlashings[i] = &ProposerSlashing{
SignedHeader1: &SignedBeaconBlockHeader{
Message: &BeaconBlockHeader{
Slot: fmt.Sprintf("%d", s.Header_1.Header.Slot),
ProposerIndex: fmt.Sprintf("%d", s.Header_1.Header.ProposerIndex),
ParentRoot: hexutil.Encode(s.Header_1.Header.ParentRoot),
StateRoot: hexutil.Encode(s.Header_1.Header.StateRoot),
BodyRoot: hexutil.Encode(s.Header_1.Header.BodyRoot),
},
Signature: hexutil.Encode(s.Header_1.Signature),
},
SignedHeader2: &SignedBeaconBlockHeader{
Message: &BeaconBlockHeader{
Slot: fmt.Sprintf("%d", s.Header_2.Header.Slot),
ProposerIndex: fmt.Sprintf("%d", s.Header_2.Header.ProposerIndex),
ParentRoot: hexutil.Encode(s.Header_2.Header.ParentRoot),
StateRoot: hexutil.Encode(s.Header_2.Header.StateRoot),
BodyRoot: hexutil.Encode(s.Header_2.Header.BodyRoot),
},
Signature: hexutil.Encode(s.Header_2.Signature),
},
}
proposerSlashings[i] = ProposerSlashingFromConsensus(s)
}
return proposerSlashings
}
func ProposerSlashingFromConsensus(src *eth.ProposerSlashing) *ProposerSlashing {
return &ProposerSlashing{
SignedHeader1: &SignedBeaconBlockHeader{
Message: &BeaconBlockHeader{
Slot: fmt.Sprintf("%d", src.Header_1.Header.Slot),
ProposerIndex: fmt.Sprintf("%d", src.Header_1.Header.ProposerIndex),
ParentRoot: hexutil.Encode(src.Header_1.Header.ParentRoot),
StateRoot: hexutil.Encode(src.Header_1.Header.StateRoot),
BodyRoot: hexutil.Encode(src.Header_1.Header.BodyRoot),
},
Signature: hexutil.Encode(src.Header_1.Signature),
},
SignedHeader2: &SignedBeaconBlockHeader{
Message: &BeaconBlockHeader{
Slot: fmt.Sprintf("%d", src.Header_2.Header.Slot),
ProposerIndex: fmt.Sprintf("%d", src.Header_2.Header.ProposerIndex),
ParentRoot: hexutil.Encode(src.Header_2.Header.ParentRoot),
StateRoot: hexutil.Encode(src.Header_2.Header.StateRoot),
BodyRoot: hexutil.Encode(src.Header_2.Header.BodyRoot),
},
Signature: hexutil.Encode(src.Header_2.Signature),
},
}
}
func AttesterSlashingsToConsensus(src []*AttesterSlashing) ([]*eth.AttesterSlashing, error) {
if src == nil {
return nil, errNilValue
@@ -875,54 +879,58 @@ func AttesterSlashingsToConsensus(src []*AttesterSlashing) ([]*eth.AttesterSlash
func AttesterSlashingsFromConsensus(src []*eth.AttesterSlashing) []*AttesterSlashing {
attesterSlashings := make([]*AttesterSlashing, len(src))
for i, s := range src {
a1AttestingIndices := make([]string, len(s.Attestation_1.AttestingIndices))
for j, ix := range s.Attestation_1.AttestingIndices {
a1AttestingIndices[j] = fmt.Sprintf("%d", ix)
}
a2AttestingIndices := make([]string, len(s.Attestation_2.AttestingIndices))
for j, ix := range s.Attestation_2.AttestingIndices {
a2AttestingIndices[j] = fmt.Sprintf("%d", ix)
}
attesterSlashings[i] = &AttesterSlashing{
Attestation1: &IndexedAttestation{
AttestingIndices: a1AttestingIndices,
Data: &AttestationData{
Slot: fmt.Sprintf("%d", s.Attestation_1.Data.Slot),
CommitteeIndex: fmt.Sprintf("%d", s.Attestation_1.Data.CommitteeIndex),
BeaconBlockRoot: hexutil.Encode(s.Attestation_1.Data.BeaconBlockRoot),
Source: &Checkpoint{
Epoch: fmt.Sprintf("%d", s.Attestation_1.Data.Source.Epoch),
Root: hexutil.Encode(s.Attestation_1.Data.Source.Root),
},
Target: &Checkpoint{
Epoch: fmt.Sprintf("%d", s.Attestation_1.Data.Target.Epoch),
Root: hexutil.Encode(s.Attestation_1.Data.Target.Root),
},
},
Signature: hexutil.Encode(s.Attestation_1.Signature),
},
Attestation2: &IndexedAttestation{
AttestingIndices: a2AttestingIndices,
Data: &AttestationData{
Slot: fmt.Sprintf("%d", s.Attestation_2.Data.Slot),
CommitteeIndex: fmt.Sprintf("%d", s.Attestation_2.Data.CommitteeIndex),
BeaconBlockRoot: hexutil.Encode(s.Attestation_2.Data.BeaconBlockRoot),
Source: &Checkpoint{
Epoch: fmt.Sprintf("%d", s.Attestation_2.Data.Source.Epoch),
Root: hexutil.Encode(s.Attestation_2.Data.Source.Root),
},
Target: &Checkpoint{
Epoch: fmt.Sprintf("%d", s.Attestation_2.Data.Target.Epoch),
Root: hexutil.Encode(s.Attestation_2.Data.Target.Root),
},
},
Signature: hexutil.Encode(s.Attestation_2.Signature),
},
}
attesterSlashings[i] = AttesterSlashingFromConsensus(s)
}
return attesterSlashings
}
func AttesterSlashingFromConsensus(src *eth.AttesterSlashing) *AttesterSlashing {
a1AttestingIndices := make([]string, len(src.Attestation_1.AttestingIndices))
for j, ix := range src.Attestation_1.AttestingIndices {
a1AttestingIndices[j] = fmt.Sprintf("%d", ix)
}
a2AttestingIndices := make([]string, len(src.Attestation_2.AttestingIndices))
for j, ix := range src.Attestation_2.AttestingIndices {
a2AttestingIndices[j] = fmt.Sprintf("%d", ix)
}
return &AttesterSlashing{
Attestation1: &IndexedAttestation{
AttestingIndices: a1AttestingIndices,
Data: &AttestationData{
Slot: fmt.Sprintf("%d", src.Attestation_1.Data.Slot),
CommitteeIndex: fmt.Sprintf("%d", src.Attestation_1.Data.CommitteeIndex),
BeaconBlockRoot: hexutil.Encode(src.Attestation_1.Data.BeaconBlockRoot),
Source: &Checkpoint{
Epoch: fmt.Sprintf("%d", src.Attestation_1.Data.Source.Epoch),
Root: hexutil.Encode(src.Attestation_1.Data.Source.Root),
},
Target: &Checkpoint{
Epoch: fmt.Sprintf("%d", src.Attestation_1.Data.Target.Epoch),
Root: hexutil.Encode(src.Attestation_1.Data.Target.Root),
},
},
Signature: hexutil.Encode(src.Attestation_1.Signature),
},
Attestation2: &IndexedAttestation{
AttestingIndices: a2AttestingIndices,
Data: &AttestationData{
Slot: fmt.Sprintf("%d", src.Attestation_2.Data.Slot),
CommitteeIndex: fmt.Sprintf("%d", src.Attestation_2.Data.CommitteeIndex),
BeaconBlockRoot: hexutil.Encode(src.Attestation_2.Data.BeaconBlockRoot),
Source: &Checkpoint{
Epoch: fmt.Sprintf("%d", src.Attestation_2.Data.Source.Epoch),
Root: hexutil.Encode(src.Attestation_2.Data.Source.Root),
},
Target: &Checkpoint{
Epoch: fmt.Sprintf("%d", src.Attestation_2.Data.Target.Epoch),
Root: hexutil.Encode(src.Attestation_2.Data.Target.Root),
},
},
Signature: hexutil.Encode(src.Attestation_2.Signature),
},
}
}
func AttsToConsensus(src []*Attestation) ([]*eth.Attestation, error) {
if src == nil {
return nil, errNilValue

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
@@ -15,15 +16,15 @@ import (
)
func UintFromQuery(w http.ResponseWriter, r *http.Request, name string, required bool) (string, uint64, bool) {
raw := r.URL.Query().Get(name)
if raw == "" && !required {
trimmed := strings.ReplaceAll(r.URL.Query().Get(name), " ", "")
if trimmed == "" && !required {
return "", 0, true
}
v, valid := ValidateUint(w, name, raw)
v, valid := ValidateUint(w, name, trimmed)
if !valid {
return "", 0, false
}
return raw, v, true
return trimmed, v, true
}
func UintFromRoute(w http.ResponseWriter, r *http.Request, name string) (string, uint64, bool) {

View File

@@ -0,0 +1,96 @@
package shared
import (
"math"
"net/http/httptest"
"testing"
)
func TestUintFromQuery_BuilderBoostFactor(t *testing.T) {
type args struct {
raw string
}
tests := []struct {
name string
args args
wantRaw string
wantValue uint64
wantOK bool
}{
{
name: "builder boost factor of 0 returns 0",
args: args{
raw: "0",
},
wantRaw: "0",
wantValue: 0,
wantOK: true,
},
{
name: "builder boost factor of the default, 100 returns 100",
args: args{raw: "100"},
wantRaw: "100",
wantValue: 100,
wantOK: true,
},
{
name: "builder boost factor max uint64 returns max uint64",
args: args{raw: "18446744073709551615"},
wantRaw: "18446744073709551615",
wantValue: math.MaxUint64,
wantOK: true,
},
{
name: "builder boost factor as a percentage returns error",
args: args{raw: "0.30"},
wantRaw: "",
wantValue: 0,
wantOK: false,
},
{
name: "builder boost factor negative int returns error",
args: args{raw: "-100"},
wantRaw: "",
wantValue: 0,
wantOK: false,
},
{
name: "builder boost factor max uint64 +1 returns error",
args: args{raw: "18446744073709551616"},
wantRaw: "",
wantValue: 0,
wantOK: false,
},
{
name: "builder boost factor of invalid string returns error",
args: args{raw: "asdf"},
wantRaw: "",
wantValue: 0,
wantOK: false,
},
{
name: "builder boost factor of number bigger than uint64 string returns error",
args: args{raw: "9871398721983721908372190837219837129803721983719283798217390821739081273918273918273918273981273982139812739821"},
wantRaw: "",
wantValue: 0,
wantOK: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query := "builder_boost_factor"
bbreq := httptest.NewRequest("GET", "/eth/v3/validator/blocks/{slot}?builder_boost_factor="+tt.args.raw, nil)
w := httptest.NewRecorder()
got, got1, got2 := UintFromQuery(w, bbreq, query, false)
if got != tt.wantRaw {
t.Errorf("UintFromQuery() got = %v, wantRaw %v", got, tt.wantRaw)
}
if got1 != tt.wantValue {
t.Errorf("UintFromQuery() got1 = %v, wantRaw %v", got1, tt.wantValue)
}
if got2 != tt.wantOK {
t.Errorf("UintFromQuery() got2 = %v, wantRaw %v", got2, tt.wantOK)
}
})
}
}

View File

@@ -19,7 +19,6 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/synccommittee:go_default_library",
"//beacon-chain/p2p:go_default_library",
@@ -41,13 +40,13 @@ go_library(
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_opencensus_go//trace:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
],
)
@@ -91,7 +90,6 @@ go_test(
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_gorilla_mux//:go_default_library",

View File

@@ -11,14 +11,12 @@ import (
"strconv"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/core"
rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
@@ -543,9 +541,6 @@ func (s *Server) RegisterValidator(w http.ResponseWriter, r *http.Request) {
// PrepareBeaconProposer endpoint saves the fee recipient given a validator index, this is used when proposing a block.
func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.PrepareBeaconProposer")
defer span.End()
var jsonFeeRecipients []*shared.FeeRecipient
err := json.NewDecoder(r.Body).Decode(&jsonFeeRecipients)
switch {
@@ -556,7 +551,6 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
var feeRecipients []common.Address
var validatorIndices []primitives.ValidatorIndex
// filter for found fee recipients
for _, r := range jsonFeeRecipients {
@@ -568,28 +562,25 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
if !valid {
return
}
f, err := s.BeaconDB.FeeRecipientByValidatorID(ctx, primitives.ValidatorIndex(validatorIndex))
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
feeRecipients = append(feeRecipients, common.BytesToAddress(bytesutil.SafeCopyBytes(feeRecipientBytes)))
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
case err != nil:
httputil.HandleError(w, fmt.Sprintf("Could not get fee recipient by validator index: %v", err), http.StatusInternalServerError)
return
default:
if common.BytesToAddress(feeRecipientBytes) != f {
feeRecipients = append(feeRecipients, common.BytesToAddress(bytesutil.SafeCopyBytes(feeRecipientBytes)))
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
// Use default address if the burn address is return
feeRecipient := primitives.ExecutionAddress(feeRecipientBytes)
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
feeRecipient = primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
log.WithField("validatorIndex", validatorIndex).Warn("fee recipient is the burn address")
}
}
val := cache.TrackedValidator{
Active: true, // TODO: either check or add the field in the request
Index: primitives.ValidatorIndex(validatorIndex),
FeeRecipient: feeRecipient,
}
s.TrackedValidatorsCache.Set(val)
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
}
if len(validatorIndices) == 0 {
return
}
if err := s.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
httputil.HandleError(w, fmt.Sprintf("Could not save fee recipients: %v", err), http.StatusInternalServerError)
return
}
log.WithFields(log.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses")
@@ -812,7 +803,6 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
pubkey48 := val.PublicKey()
pubkey := pubkey48[:]
for _, slot := range proposalSlots {
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, index, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
duties = append(duties, &ProposerDuty{
Pubkey: hexutil.Encode(pubkey),
ValidatorIndex: strconv.FormatUint(uint64(index), 10),
@@ -821,8 +811,6 @@ func (s *Server) GetProposerDuties(w http.ResponseWriter, r *http.Request) {
}
}
s.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
dependentRoot, err := proposalDependentRoot(st, requestedEpoch)
if err != nil {
httputil.HandleError(w, "Could not get dependent root: "+err.Error(), http.StatusInternalServerError)

View File

@@ -19,6 +19,7 @@ import (
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"go.opencensus.io/trace"
"google.golang.org/protobuf/types/known/wrapperspb"
)
type blockType uint8
@@ -155,6 +156,15 @@ func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
rawGraffiti := r.URL.Query().Get("graffiti")
rawSkipRandaoVerification := r.URL.Query().Get("skip_randao_verification")
var bbFactor *wrapperspb.UInt64Value // default the factor via fall back
rawBbFactor, bbValue, ok := shared.UintFromQuery(w, r, "builder_boost_factor", false)
if !ok {
return
}
if rawBbFactor != "" {
bbFactor = &wrapperspb.UInt64Value{Value: bbValue}
}
slot, valid := shared.ValidateUint(w, "slot", rawSlot)
if !valid {
return
@@ -182,10 +192,11 @@ func (s *Server) ProduceBlockV3(w http.ResponseWriter, r *http.Request) {
}
s.produceBlockV3(ctx, w, r, &eth.BlockRequest{
Slot: primitives.Slot(slot),
RandaoReveal: randaoReveal,
Graffiti: graffiti,
SkipMevBoost: false,
Slot: primitives.Slot(slot),
RandaoReveal: randaoReveal,
Graffiti: graffiti,
SkipMevBoost: false,
BuilderBoostFactor: bbFactor,
}, any)
}

View File

@@ -12,7 +12,6 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/gorilla/mux"
"github.com/pkg/errors"
@@ -1660,7 +1659,8 @@ func TestGetProposerDuties(t *testing.T) {
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
@@ -1681,9 +1681,6 @@ func TestGetProposerDuties(t *testing.T) {
expectedDuty = duty
}
}
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(11, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(12289), vid)
require.NotNil(t, expectedDuty, "Expected duty for slot 11 not found")
assert.Equal(t, "12289", expectedDuty.ValidatorIndex)
assert.Equal(t, hexutil.Encode(pubKeys[12289]), expectedDuty.Pubkey)
@@ -1702,7 +1699,8 @@ func TestGetProposerDuties(t *testing.T) {
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
@@ -1723,52 +1721,10 @@ func TestGetProposerDuties(t *testing.T) {
expectedDuty = duty
}
}
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(43, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(1360), vid)
require.NotNil(t, expectedDuty, "Expected duty for slot 43 not found")
assert.Equal(t, "1360", expectedDuty.ValidatorIndex)
assert.Equal(t, hexutil.Encode(pubKeys[1360]), expectedDuty.Pubkey)
})
t.Run("prune payload ID cache", func(t *testing.T) {
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
require.NoError(t, err, "Could not set up genesis state")
require.NoError(t, bs.SetSlot(params.BeaconConfig().SlotsPerEpoch))
require.NoError(t, bs.SetBlockRoots(roots))
chainSlot := params.BeaconConfig().SlotsPerEpoch
chain := &mockChain.ChainService{
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
}
s := &Server{
Stater: &testutil.MockStater{StatesBySlot: map[primitives.Slot]state.BeaconState{params.BeaconConfig().SlotsPerEpoch: bs}},
HeadFetcher: chain,
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(1, 1, [8]byte{1}, [32]byte{2})
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(31, 2, [8]byte{2}, [32]byte{3})
s.ProposerSlotIndexCache.SetProposerAndPayloadIDs(32, 4309, [8]byte{3}, [32]byte{4})
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
request = mux.SetURLVars(request, map[string]string{"epoch": "1"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetProposerDuties(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
vid, _, has := s.ProposerSlotIndexCache.GetProposerPayloadIDs(1, [32]byte{})
require.Equal(t, false, has)
require.Equal(t, primitives.ValidatorIndex(0), vid)
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(2, [32]byte{})
require.Equal(t, false, has)
require.Equal(t, primitives.ValidatorIndex(0), vid)
vid, _, has = s.ProposerSlotIndexCache.GetProposerPayloadIDs(32, [32]byte{})
require.Equal(t, true, has)
require.Equal(t, primitives.ValidatorIndex(10565), vid)
})
t.Run("epoch out of bounds", func(t *testing.T) {
bs, err := transition.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
require.NoError(t, err, "Could not set up genesis state")
@@ -1785,7 +1741,8 @@ func TestGetProposerDuties(t *testing.T) {
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
currentEpoch := slots.ToEpoch(bs.Slot())
@@ -1828,7 +1785,8 @@ func TestGetProposerDuties(t *testing.T) {
TimeFetcher: chain,
OptimisticModeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
request := httptest.NewRequest(http.MethodGet, "http://www.example.com/eth/v1/validator/duties/proposer/{epoch}", nil)
@@ -2267,21 +2225,24 @@ func TestPrepareBeaconProposer(t *testing.T) {
request := httptest.NewRequest(http.MethodPost, url, &body)
writer := httptest.NewRecorder()
db := dbutil.SetupDB(t)
ctx := context.Background()
server := &Server{
BeaconDB: db,
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
}
server.PrepareBeaconProposer(writer, request)
require.Equal(t, tt.code, writer.Code)
if tt.wantErr != "" {
require.Equal(t, strings.Contains(writer.Body.String(), tt.wantErr), true)
} else {
require.NoError(t, err)
address, err := server.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
require.NoError(t, err)
feebytes, err := hexutil.Decode(tt.request[0].FeeRecipient)
require.NoError(t, err)
require.Equal(t, common.BytesToAddress(feebytes), address)
index, err := strconv.ParseUint(tt.request[0].ValidatorIndex, 10, 64)
require.NoError(t, err)
val, tracked := server.TrackedValidatorsCache.Validator(primitives.ValidatorIndex(index))
require.Equal(t, true, tracked)
require.Equal(t, primitives.ExecutionAddress(feebytes), val.FeeRecipient)
}
})
}
@@ -2292,7 +2253,11 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
db := dbutil.SetupDB(t)
// New validator
proposerServer := &Server{BeaconDB: db}
proposerServer := &Server{
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
}
req := []*shared.FeeRecipient{{
FeeRecipient: hexutil.Encode(bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)),
ValidatorIndex: "1",
@@ -2318,7 +2283,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
writer = httptest.NewRecorder()
proposerServer.PrepareBeaconProposer(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses")
require.LogsContain(t, hook, "Updated fee recipient addresses")
// Same validator with different fee recipient
hook.Reset()
@@ -2368,13 +2333,16 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
writer = httptest.NewRecorder()
proposerServer.PrepareBeaconProposer(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses")
require.LogsContain(t, hook, "Updated fee recipient addresses")
}
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {
db := dbutil.SetupDB(b)
proposerServer := &Server{BeaconDB: db}
proposerServer := &Server{
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
}
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
recipients := make([]*shared.FeeRecipient, 0)
for i := 0; i < 10000; i++ {

View File

@@ -29,11 +29,12 @@ type Server struct {
OptimisticModeFetcher blockchain.OptimisticModeFetcher
SyncCommitteePool synccommittee.Pool
V1Alpha1Server eth.BeaconNodeValidatorServer
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
ChainInfoFetcher blockchain.ChainInfoFetcher
BeaconDB db.HeadAccessDatabase
BlockBuilder builder.BlockBuilder
OperationNotifier operation.Notifier
CoreService *core.Service
BlockRewardFetcher rewards.BlockRewardsFetcher
TrackedValidatorsCache *cache.TrackedValidatorsCache
PayloadIDCache *cache.PayloadIDCache
}

View File

@@ -201,7 +201,6 @@ go_test(
"status_mainnet_test.go",
"status_test.go",
"sync_committee_test.go",
"unblinder_test.go",
"validator_test.go",
],
embed = [":go_default_library"],

View File

@@ -135,7 +135,7 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
return nil, status.Errorf(codes.Internal, "Could not compute committee assignments: %v", err)
}
// Query the next epoch assignments for committee subnet subscriptions.
nextCommitteeAssignments, nextProposerIndexToSlots, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
nextCommitteeAssignments, _, err := helpers.CommitteeAssignments(ctx, s, req.Epoch+1)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not compute next committee assignments: %v", err)
}
@@ -178,15 +178,6 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
nextAssignment.AttesterSlot = ca.AttesterSlot
nextAssignment.CommitteeIndex = ca.CommitteeIndex
}
// Cache proposer assignment for the current epoch.
for _, slot := range proposerIndexToSlots[idx] {
// Head root is empty because it can't be known until slot - 1. Same with payload id.
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
}
// Cache proposer assignment for the next epoch.
for _, slot := range nextProposerIndexToSlots[idx] {
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(slot, idx, [8]byte{} /* payloadID */, [32]byte{} /* head root */)
}
} else {
// If the validator isn't in the beacon state, try finding their deposit to determine their status.
// We don't need the lastActiveValidatorFn because we don't use the response in this.
@@ -228,9 +219,6 @@ func (vs *Server) duties(ctx context.Context, req *ethpb.DutiesRequest) (*ethpb.
validatorAssignments = append(validatorAssignments, assignment)
nextValidatorAssignments = append(nextValidatorAssignments, nextAssignment)
}
// Prune payload ID cache for any slots before request slot.
vs.ProposerSlotIndexCache.PrunePayloadIDs(epochStartSlot)
return &ethpb.DutiesResponse{
Duties: validatorAssignments,
CurrentEpochDuties: validatorAssignments,

View File

@@ -60,10 +60,10 @@ func TestGetDuties_OK(t *testing.T) {
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -145,11 +145,11 @@ func TestGetAltairDuties_SyncCommitteeOK(t *testing.T) {
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -251,11 +251,11 @@ func TestGetBellatrixDuties_SyncCommitteeOK(t *testing.T) {
State: bs, Root: genesisRoot[:], Genesis: time.Now().Add(time.Duration(-1*int64(slot-1)) * time.Second),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -343,12 +343,12 @@ func TestGetAltairDuties_UnknownPubkey(t *testing.T) {
require.NoError(t, err)
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
DepositFetcher: depositCache,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
Eth1InfoFetcher: &mockExecution.Chain{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
DepositFetcher: depositCache,
PayloadIDCache: cache.NewPayloadIDCache(),
}
unknownPubkey := bytesutil.PadTo([]byte{'u'}, 48)
@@ -401,10 +401,10 @@ func TestGetDuties_CurrentEpoch_ShouldNotFail(t *testing.T) {
State: bState, Root: genesisRoot[:], Genesis: time.Now(),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -440,10 +440,10 @@ func TestGetDuties_MultipleKeys_OK(t *testing.T) {
State: bs, Root: genesisRoot[:], Genesis: time.Now(),
}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
PayloadIDCache: cache.NewPayloadIDCache(),
}
pubkey0 := deposits[0].Data.PublicKey
@@ -507,12 +507,12 @@ func TestStreamDuties_OK(t *testing.T) {
Genesis: time.Now(),
}
vs := &Server{
Ctx: ctx,
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
TimeFetcher: c,
StateNotifier: &mockChain.MockStateNotifier{},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
Ctx: ctx,
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
TimeFetcher: c,
StateNotifier: &mockChain.MockStateNotifier{},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -565,12 +565,12 @@ func TestStreamDuties_OK_ChainReorg(t *testing.T) {
Genesis: time.Now(),
}
vs := &Server{
Ctx: ctx,
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
TimeFetcher: c,
StateNotifier: &mockChain.MockStateNotifier{},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
Ctx: ctx,
HeadFetcher: &mockChain.ChainService{State: bs, Root: genesisRoot[:]},
SyncChecker: &mockSync.Sync{IsSyncing: false},
TimeFetcher: c,
StateNotifier: &mockChain.MockStateNotifier{},
PayloadIDCache: cache.NewPayloadIDCache(),
}
// Test the first validator in registry.
@@ -625,10 +625,9 @@ func BenchmarkCommitteeAssignment(b *testing.B) {
chain := &mockChain.ChainService{State: bs, Root: genesisRoot[:]}
vs := &Server{
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
HeadFetcher: chain,
TimeFetcher: chain,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
// Create request for all validators in the system.

View File

@@ -2,7 +2,6 @@ package validator
import (
"context"
"encoding/hex"
"fmt"
"strings"
"sync"
@@ -14,8 +13,10 @@ import (
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
blockfeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/block"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
@@ -37,9 +38,8 @@ import (
var eth1DataNotification bool
const (
// CouldNotDecodeBlock means that a signed beacon block couldn't be created from the block present in the request.
CouldNotDecodeBlock = "Could not decode block"
eth1dataTimeout = 2 * time.Second
eth1dataTimeout = 2 * time.Second
defaultBuilderBoostFactor = uint64(100)
)
// GetBeaconBlock is called by a proposer during its assigned slot to request a block to sign
@@ -69,6 +69,11 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
parentRoot := vs.ForkchoiceFetcher.GetProposerHead()
if parentRoot != headRoot {
blockchain.LateBlockAttemptedReorgCount.Inc()
log.WithFields(logrus.Fields{
"slot": req.Slot,
"parentRoot": fmt.Sprintf("%#x", parentRoot),
"headRoot": fmt.Sprintf("%#x", headRoot),
}).Warn("late block attempted reorg failed")
}
// An optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain).
@@ -104,7 +109,12 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
}
sBlk.SetProposerIndex(idx)
if err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost); err != nil {
builderBoostFactor := defaultBuilderBoostFactor
if req.BuilderBoostFactor != nil {
builderBoostFactor = req.BuilderBoostFactor.Value
}
if err = vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost, builderBoostFactor); err != nil {
return nil, errors.Wrap(err, "could not build block in parallel")
}
@@ -124,7 +134,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
return vs.constructGenericBeaconBlock(sBlk, bundleCache.get(req.Slot))
}
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool) error {
func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool, builderBoostFactor uint64) error {
// Build consensus fields in background
var wg sync.WaitGroup
wg.Add(1)
@@ -182,7 +192,7 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
}
}
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload, builderKzgCommitments); err != nil {
if err := setExecutionData(ctx, sBlk, localPayload, builderPayload, builderKzgCommitments, builderBoostFactor); err != nil {
return status.Errorf(codes.Internal, "Could not set execution data: %v", err)
}
@@ -191,129 +201,171 @@ func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.Signed
return nil
}
// ProposeBeaconBlock is called by a proposer during its assigned slot to create a block in an attempt
// to get it processed by the beacon node as the canonical head.
// ProposeBeaconBlock handles the proposal of beacon blocks.
func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeBeaconBlock")
defer span.End()
blk, err := blocks.NewSignedBeaconBlock(req.Block)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "%s: %v", CouldNotDecodeBlock, err)
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
unblinder, err := newUnblinder(blk, vs.BlockBuilder)
block, err := blocks.NewSignedBeaconBlock(req.Block)
if err != nil {
return nil, errors.Wrap(err, "could not create unblinder")
}
blinded := unblinder.b.IsBlinded() //
var scs []*ethpb.BlobSidecar
blk, scs, err = unblinder.unblindBuilderBlock(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not unblind builder block")
return nil, status.Errorf(codes.InvalidArgument, "%s: %v", "decode block failed", err)
}
// Broadcast the new block to the network.
blkPb, err := blk.Proto()
if err != nil {
return nil, errors.Wrap(err, "could not get protobuf block")
var sidecars []*ethpb.BlobSidecar
if block.IsBlinded() {
block, sidecars, err = vs.handleBlindedBlock(ctx, block)
} else {
sidecars, err = vs.handleUnblindedBlock(block, req)
}
if err := vs.P2P.Broadcast(ctx, blkPb); err != nil {
return nil, fmt.Errorf("could not broadcast block: %v", err)
if err != nil {
return nil, status.Errorf(codes.Internal, "%s: %v", "handle block failed", err)
}
root, err := blk.Block().HashTreeRoot()
root, err := block.Block().HashTreeRoot()
if err != nil {
return nil, fmt.Errorf("could not tree hash block: %v", err)
return nil, status.Errorf(codes.Internal, "Could not hash tree root: %v", err)
}
log.WithFields(logrus.Fields{
"blockRoot": hex.EncodeToString(root[:]),
}).Debug("Broadcasting block")
if blk.Version() >= version.Deneb {
if !blinded {
dbBlockContents := req.GetDeneb()
if dbBlockContents == nil {
return nil, errors.New("signed beacon block contents is empty")
}
scs, err = buildBlobSidecars(blk, dbBlockContents.Blobs, dbBlockContents.KzgProofs)
if err != nil {
return nil, fmt.Errorf("could not build blob sidecars: %v", err)
}
}
for i, sc := range scs {
if err := vs.P2P.BroadcastBlob(ctx, uint64(i), sc); err != nil {
log.WithError(err).Error("Could not broadcast blob")
}
readOnlySc, err := blocks.NewROBlobWithRoot(sc, root)
if err != nil {
return nil, fmt.Errorf("could not create ROBlob: %v", err)
}
verifiedSc := blocks.NewVerifiedROBlob(readOnlySc)
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedSc); err != nil {
log.WithError(err).Error("Could not receive blob")
}
var wg sync.WaitGroup
errChan := make(chan error, 1)
wg.Add(1)
go func() {
defer wg.Done()
if err := vs.broadcastReceiveBlock(ctx, block, root); err != nil {
errChan <- errors.Wrap(err, "broadcast/receive block failed")
return
}
errChan <- nil
}()
if err := vs.broadcastAndReceiveBlobs(ctx, sidecars, root); err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive blobs: %v", err)
}
if err := vs.BlockReceiver.ReceiveBlock(ctx, blk, root); err != nil {
return nil, fmt.Errorf("could not process beacon block: %v", err)
wg.Wait()
if err := <-errChan; err != nil {
return nil, status.Errorf(codes.Internal, "Could not broadcast/receive block: %v", err)
}
log.WithField("slot", blk.Block().Slot()).Debugf(
"Block proposal received via RPC")
return &ethpb.ProposeResponse{BlockRoot: root[:]}, nil
}
// handleBlindedBlock processes blinded beacon blocks.
func (vs *Server) handleBlindedBlock(ctx context.Context, block interfaces.SignedBeaconBlock) (interfaces.SignedBeaconBlock, []*ethpb.BlobSidecar, error) {
if block.Version() < version.Bellatrix {
return nil, nil, errors.New("pre-Bellatrix blinded block")
}
if vs.BlockBuilder == nil || !vs.BlockBuilder.Configured() {
return nil, nil, errors.New("unconfigured block builder")
}
copiedBlock, err := block.Copy()
if err != nil {
return nil, nil, err
}
payload, bundle, err := vs.BlockBuilder.SubmitBlindedBlock(ctx, block)
if err != nil {
return nil, nil, errors.Wrap(err, "submit blinded block failed")
}
if err := copiedBlock.Unblind(payload); err != nil {
return nil, nil, errors.Wrap(err, "unblind failed")
}
sidecars, err := unblindBlobsSidecars(copiedBlock, bundle)
if err != nil {
return nil, nil, errors.Wrap(err, "unblind sidecars failed")
}
return copiedBlock, sidecars, nil
}
// handleUnblindedBlock processes unblinded beacon blocks.
func (vs *Server) handleUnblindedBlock(block interfaces.SignedBeaconBlock, req *ethpb.GenericSignedBeaconBlock) ([]*ethpb.BlobSidecar, error) {
dbBlockContents := req.GetDeneb()
if dbBlockContents == nil {
return nil, nil
}
return buildBlobSidecars(block, dbBlockContents.Blobs, dbBlockContents.KzgProofs)
}
// broadcastReceiveBlock broadcasts a block and handles its reception.
func (vs *Server) broadcastReceiveBlock(ctx context.Context, block interfaces.SignedBeaconBlock, root [32]byte) error {
protoBlock, err := block.Proto()
if err != nil {
return errors.Wrap(err, "protobuf conversion failed")
}
if err := vs.P2P.Broadcast(ctx, protoBlock); err != nil {
return errors.Wrap(err, "broadcast failed")
}
vs.BlockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{SignedBlock: blk},
Data: &blockfeed.ReceivedBlockData{SignedBlock: block},
})
return vs.BlockReceiver.ReceiveBlock(ctx, block, root)
}
return &ethpb.ProposeResponse{
BlockRoot: root[:],
}, nil
// broadcastAndReceiveBlobs handles the broadcasting and reception of blob sidecars.
func (vs *Server) broadcastAndReceiveBlobs(ctx context.Context, sidecars []*ethpb.BlobSidecar, root [32]byte) error {
for i, sc := range sidecars {
if err := vs.P2P.BroadcastBlob(ctx, uint64(i), sc); err != nil {
return errors.Wrap(err, "broadcast blob failed")
}
readOnlySc, err := blocks.NewROBlobWithRoot(sc, root)
if err != nil {
return errors.Wrap(err, "ROBlob creation failed")
}
verifiedBlob := blocks.NewVerifiedROBlob(readOnlySc)
if err := vs.BlobReceiver.ReceiveBlob(ctx, verifiedBlob); err != nil {
return errors.Wrap(err, "receive blob failed")
}
vs.OperationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BlobSidecarReceived,
Data: &operation.BlobSidecarReceivedData{Blob: &verifiedBlob},
})
}
return nil
}
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
func (vs *Server) PrepareBeaconProposer(
ctx context.Context, request *ethpb.PrepareBeaconProposerRequest,
_ context.Context, request *ethpb.PrepareBeaconProposerRequest,
) (*emptypb.Empty, error) {
ctx, span := trace.StartSpan(ctx, "validator.PrepareBeaconProposer")
defer span.End()
var feeRecipients []common.Address
var validatorIndices []primitives.ValidatorIndex
newRecipients := make([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, 0, len(request.Recipients))
for _, r := range request.Recipients {
f, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, r.ValidatorIndex)
switch {
case errors.Is(err, kv.ErrNotFoundFeeRecipient):
newRecipients = append(newRecipients, r)
case err != nil:
return nil, status.Errorf(codes.Internal, "Could not get fee recipient by validator index: %v", err)
default:
if common.BytesToAddress(r.FeeRecipient) != f {
newRecipients = append(newRecipients, r)
}
}
}
if len(newRecipients) == 0 {
return &emptypb.Empty{}, nil
}
for _, recipientContainer := range newRecipients {
recipient := hexutil.Encode(recipientContainer.FeeRecipient)
recipient := hexutil.Encode(r.FeeRecipient)
if !common.IsHexAddress(recipient) {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Invalid fee recipient address: %v", recipient))
}
feeRecipients = append(feeRecipients, common.BytesToAddress(recipientContainer.FeeRecipient))
validatorIndices = append(validatorIndices, recipientContainer.ValidatorIndex)
// Use default address if the burn address is return
feeRecipient := primitives.ExecutionAddress(r.FeeRecipient)
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
feeRecipient = primitives.ExecutionAddress(params.BeaconConfig().DefaultFeeRecipient)
if feeRecipient == primitives.ExecutionAddress([20]byte{}) {
log.WithField("validatorIndex", r.ValidatorIndex).Warn("fee recipient is the burn address")
}
}
val := cache.TrackedValidator{
Active: true, // TODO: either check or add the field in the request
Index: r.ValidatorIndex,
FeeRecipient: feeRecipient,
}
vs.TrackedValidatorsCache.Set(val)
validatorIndices = append(validatorIndices, r.ValidatorIndex)
}
if err := vs.BeaconDB.SaveFeeRecipientsByValidatorIDs(ctx, validatorIndices, feeRecipients); err != nil {
return nil, status.Errorf(codes.Internal, "Could not save fee recipients: %v", err)
if len(validatorIndices) != 0 {
log.WithFields(logrus.Fields{
"validatorCount": len(validatorIndices),
}).Info("Updated fee recipient addresses for validator indices")
}
log.WithFields(logrus.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses for validator indices")
return &emptypb.Empty{}, nil
}

View File

@@ -41,7 +41,7 @@ var emptyTransactionsRoot = [32]byte{127, 254, 36, 30, 166, 1, 135, 253, 176, 24
const blockBuilderTimeout = 1 * time.Second
// Sets the execution data for the block. Execution data can come from local EL client or remote builder depends on validator registration and circuit breaker conditions.
func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, localPayload, builderPayload interfaces.ExecutionData, builderKzgCommitments [][]byte) error {
func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, localPayload, builderPayload interfaces.ExecutionData, builderKzgCommitments [][]byte, builderBoostFactor uint64) error {
_, span := trace.StartSpan(ctx, "ProposerServer.setExecutionData")
defer span.End()
@@ -80,16 +80,22 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
}
// Use builder payload if the following in true:
// builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)
// builder_bid_value * builderBoostFactor(default 100) > local_block_value * (local-block-value-boost + 100)
boost := params.BeaconConfig().LocalBlockValueBoost
higherValueBuilder := builderValueGwei*100 > localValueGwei*(100+boost)
higherValueBuilder := builderValueGwei*builderBoostFactor > localValueGwei*(100+boost)
if boost > 0 && builderBoostFactor != defaultBuilderBoostFactor {
log.WithFields(logrus.Fields{
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: both local boost and builder boost are using non default values")
}
// If we can't get the builder value, just use local block.
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
blk.SetBlinded(true)
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
blk.SetBlinded(false)
return setLocalExecution(blk, localPayload)
} else {
return nil
@@ -100,20 +106,20 @@ func setExecutionData(ctx context.Context, blk interfaces.SignedBeaconBlock, loc
"localGweiValue": localValueGwei,
"localBoostPercentage": boost,
"builderGweiValue": builderValueGwei,
"builderBoostFactor": builderBoostFactor,
}).Warn("Proposer: using local execution payload because higher value")
}
span.AddAttributes(
trace.BoolAttribute("higherValueBuilder", higherValueBuilder),
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("localGweiValue", int64(localValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("localBoostPercentage", int64(boost)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderGweiValue", int64(builderValueGwei)), // lint:ignore uintcast -- This is OK for tracing.
trace.Int64Attribute("builderBoostFactor", int64(builderBoostFactor)), // lint:ignore uintcast -- This is OK for tracing.
)
return setLocalExecution(blk, localPayload)
default: // Bellatrix case.
blk.SetBlinded(true)
if err := setBuilderExecution(blk, builderPayload, builderKzgCommitments); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
blk.SetBlinded(false)
return setLocalExecution(blk, localPayload)
} else {
return nil
@@ -315,9 +321,6 @@ func setExecution(blk interfaces.SignedBeaconBlock, execution interfaces.Executi
return errors.New("execution is nil")
}
// Set the blinded status of the block
blk.SetBlinded(isBlinded)
// Set the execution data for the block
errMessage := "failed to set local execution"
if isBlinded {

View File

@@ -2,6 +2,7 @@ package validator
import (
"context"
"math"
"math/big"
"testing"
"time"
@@ -81,9 +82,10 @@ func TestServer_setExecutionData(t *testing.T) {
HeadFetcher: &blockchainTest.ChainService{State: capellaTransitionState},
FinalizationFetcher: &blockchainTest.ChainService{},
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Cfg: &builderTest.Config{BeaconDB: beaconDB}},
ForkchoiceFetcher: &blockchainTest.ChainService{},
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
t.Run("No builder configured. Use local block", func(t *testing.T) {
@@ -95,7 +97,7 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block
@@ -155,7 +157,7 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // Local block because incorrect withdrawals
@@ -218,11 +220,137 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(2), e.BlockNumber()) // Builder block
})
t.Run("Max builder boost factor should return builder", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
require.NoError(t, err)
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
require.NoError(t, err)
sk, err := bls.RandKey()
require.NoError(t, err)
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
bid := &ethpb.BuilderBidCapella{
Header: &v1.ExecutionPayloadHeaderCapella{
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),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
ParentHash: params.BeaconConfig().ZeroHash[:],
Timestamp: uint64(ti.Unix()),
BlockNumber: 2,
WithdrawalsRoot: wr[:],
},
Pubkey: sk.PublicKey().Marshal(),
Value: bytesutil.PadTo(builderValue, 32),
}
d := params.BeaconConfig().DomainApplicationBuilder
domain, err := signing.ComputeDomain(d, nil, nil)
require.NoError(t, err)
sr, err := signing.ComputeSigningRoot(bid, domain)
require.NoError(t, err)
sBid := &ethpb.SignedBuilderBidCapella{
Message: bid,
Signature: sk.Sign(sr[:]).Marshal(),
}
vs.BlockBuilder = &builderTest.MockBuilderService{
BidCapella: sBid,
HasConfigured: true,
Cfg: &builderTest.Config{BeaconDB: beaconDB},
}
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
vs.ForkFetcher = chain
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
vs.TimeFetcher = chain
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, math.MaxUint64))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(2), e.BlockNumber()) // builder block
})
t.Run("Builder builder has higher value but forced to local payload with builder boost factor", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockCapella())
require.NoError(t, err)
require.NoError(t, vs.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []primitives.ValidatorIndex{blk.Block().ProposerIndex()},
[]*ethpb.ValidatorRegistrationV1{{FeeRecipient: make([]byte, fieldparams.FeeRecipientLength), Timestamp: uint64(time.Now().Unix()), Pubkey: make([]byte, fieldparams.BLSPubkeyLength)}}))
ti, err := slots.ToTime(uint64(time.Now().Unix()), 0)
require.NoError(t, err)
sk, err := bls.RandKey()
require.NoError(t, err)
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
bid := &ethpb.BuilderBidCapella{
Header: &v1.ExecutionPayloadHeaderCapella{
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),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: bytesutil.PadTo([]byte{1}, fieldparams.RootLength),
ParentHash: params.BeaconConfig().ZeroHash[:],
Timestamp: uint64(ti.Unix()),
BlockNumber: 2,
WithdrawalsRoot: wr[:],
},
Pubkey: sk.PublicKey().Marshal(),
Value: bytesutil.PadTo(builderValue, 32),
}
d := params.BeaconConfig().DomainApplicationBuilder
domain, err := signing.ComputeDomain(d, nil, nil)
require.NoError(t, err)
sr, err := signing.ComputeSigningRoot(bid, domain)
require.NoError(t, err)
sBid := &ethpb.SignedBuilderBidCapella{
Message: bid,
Signature: sk.Sign(sr[:]).Marshal(),
}
vs.BlockBuilder = &builderTest.MockBuilderService{
BidCapella: sBid,
HasConfigured: true,
Cfg: &builderTest.Config{BeaconDB: beaconDB},
}
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
chain := &blockchainTest.ChainService{ForkChoiceStore: doublylinkedtree.New(), Genesis: time.Now(), Block: wb}
vs.ForkFetcher = chain
vs.ForkchoiceFetcher.SetForkChoiceGenesisTime(uint64(time.Now().Unix()))
vs.TimeFetcher = chain
vs.HeadFetcher = chain
b := blk.Block()
localPayload, _, err := vs.getLocalPayload(ctx, b, capellaTransitionState)
require.NoError(t, err)
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, 0))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(1), e.BlockNumber()) // local block
})
t.Run("Builder configured. Local block has higher value", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
@@ -233,14 +361,14 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
require.LogsContain(t, hook, "builderGweiValue=1 localBoostPercentage=0 localGweiValue=2")
})
t.Run("Builder configured. Local block and boost has higher value", func(t *testing.T) {
t.Run("Builder configured. Local block and local boost has higher value", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
cfg.LocalBlockValueBoost = 1 // Boost 1%.
params.OverrideBeaconConfig(cfg)
@@ -254,7 +382,7 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.NoError(t, err)
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block
@@ -276,7 +404,7 @@ func TestServer_setExecutionData(t *testing.T) {
builderPayload, builderKzgCommitments, err := vs.getBuilderPayloadAndBlobs(ctx, b.Slot(), b.ProposerIndex())
require.ErrorIs(t, consensus_types.ErrNilObjectWrapped, err) // Builder returns fault. Use local block
require.DeepEqual(t, [][]uint8(nil), builderKzgCommitments)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(4), e.BlockNumber()) // Local block
@@ -384,7 +512,7 @@ func TestServer_setExecutionData(t *testing.T) {
localPayload, _, err := vs.getLocalPayload(ctx, blk.Block(), denebTransitionState)
require.NoError(t, err)
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments))
require.NoError(t, setExecutionData(context.Background(), blk, localPayload, builderPayload, builderKzgCommitments, defaultBuilderBoostFactor))
got, err := blk.Block().Body().BlobKzgCommitments()
require.NoError(t, err)

View File

@@ -57,7 +57,7 @@ func (c *blobsBundleCache) prune(minSlot primitives.Slot) {
}
// buildBlobSidecars given a block, builds the blob sidecars for the block.
func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproofs [][]byte) ([]*ethpb.BlobSidecar, error) {
func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgProofs [][]byte) ([]*ethpb.BlobSidecar, error) {
if blk.Version() < version.Deneb {
return nil, nil // No blobs before deneb.
}
@@ -66,7 +66,7 @@ func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproo
return nil, err
}
cLen := len(denebBlk.Block.Body.BlobKzgCommitments)
if cLen != len(blobs) || cLen != len(kzgproofs) {
if cLen != len(blobs) || cLen != len(kzgProofs) {
return nil, errors.New("blob KZG commitments don't match number of blobs or KZG proofs")
}
blobSidecars := make([]*ethpb.BlobSidecar, cLen)
@@ -84,7 +84,7 @@ func buildBlobSidecars(blk interfaces.SignedBeaconBlock, blobs [][]byte, kzgproo
Index: uint64(i),
Blob: blobs[i],
KzgCommitment: denebBlk.Block.Body.BlobKzgCommitments[i],
KzgProof: kzgproofs[i],
KzgProof: kzgProofs[i],
SignedBlockHeader: header,
CommitmentInclusionProof: proof,
}

View File

@@ -5,14 +5,12 @@ import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db/kv"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
@@ -53,44 +51,36 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
slot := blk.Slot()
vIdx := blk.ProposerIndex()
headRoot := blk.ParentRoot()
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, headRoot)
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
switch err == nil {
case true:
feeRecipient = recipient
case errors.As(err, kv.ErrNotFoundFeeRecipient):
// If fee recipient is not found in DB and not set from beacon node CLI,
// use the burn address.
if feeRecipient.String() == params.BeaconConfig().EthBurnAddressHex {
logrus.WithFields(logrus.Fields{
"validatorIndex": vIdx,
"burnAddress": params.BeaconConfig().EthBurnAddressHex,
}).Warn("Fee recipient is currently using the burn address, " +
"you will not be rewarded transaction fees on this setting. " +
"Please set a different eth address as the fee recipient. " +
"Please refer to our documentation for instructions")
}
default:
return nil, false, errors.Wrap(err, "could not get fee recipient in db")
logFields := logrus.Fields{
"validatorIndex": vIdx,
"slot": slot,
"headRoot": fmt.Sprintf("%#x", headRoot),
}
payloadId, ok := vs.PayloadIDCache.PayloadID(slot, headRoot)
val, tracked := vs.TrackedValidatorsCache.Validator(vIdx)
if !tracked {
logrus.WithFields(logFields).Warn("could not find tracked proposer index")
}
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
var pid [8]byte
var err error
if ok && payloadId != [8]byte{} {
// Payload ID is cache hit. Return the cached payload ID.
var pid primitives.PayloadID
copy(pid[:], payloadId[:])
payloadIDCacheHit.Inc()
payload, bundle, overrideBuilder, err := vs.ExecutionEngineCaller.GetPayload(ctx, pid, slot)
switch {
case err == nil:
bundleCache.add(slot, bundle)
warnIfFeeRecipientDiffers(payload, feeRecipient)
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
return payload, overrideBuilder, nil
case errors.Is(err, context.DeadlineExceeded):
default:
return nil, false, errors.Wrap(err, "could not get cached payload from execution client")
}
}
log.WithFields(logFields).Debug("payload ID cache miss")
parentHash, err := vs.getParentBlockHash(ctx, st, slot)
switch {
case errors.Is(err, errActivationNotReached) || errors.Is(err, errNoTerminalBlockHash):
@@ -112,7 +102,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
finalizedBlockHash := [32]byte{}
justifiedBlockHash := [32]byte{}
// Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
if st.Version() >= version.Altair {
if st.Version() >= version.Bellatrix {
finalizedBlockHash = vs.FinalizationFetcher.FinalizedBlockHash()
justifiedBlockHash = vs.FinalizationFetcher.UnrealizedJustifiedPayloadBlockHash()
}
@@ -137,7 +127,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV3{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
Withdrawals: withdrawals,
ParentBeaconBlockRoot: headRoot[:],
})
@@ -152,7 +142,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
attr, err = payloadattribute.New(&enginev1.PayloadAttributesV2{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
Withdrawals: withdrawals,
})
if err != nil {
@@ -162,7 +152,7 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
attr, err = payloadattribute.New(&enginev1.PayloadAttributes{
Timestamp: uint64(t.Unix()),
PrevRandao: random,
SuggestedFeeRecipient: feeRecipient.Bytes(),
SuggestedFeeRecipient: val.FeeRecipient[:],
})
if err != nil {
return nil, false, err
@@ -182,13 +172,17 @@ func (vs *Server) getLocalPayload(ctx context.Context, blk interfaces.ReadOnlyBe
return nil, false, err
}
bundleCache.add(slot, bundle)
warnIfFeeRecipientDiffers(payload, feeRecipient)
warnIfFeeRecipientDiffers(payload, val.FeeRecipient)
localValueGwei, err := payload.ValueInGwei()
if err == nil {
log.WithField("value", localValueGwei).Debug("received execution payload from local engine")
}
return payload, overrideBuilder, nil
}
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not
// match the requested one.
func warnIfFeeRecipientDiffers(payload interfaces.ExecutionData, feeRecipient common.Address) {
func warnIfFeeRecipientDiffers(payload interfaces.ExecutionData, feeRecipient primitives.ExecutionAddress) {
// Warn if the fee recipient is not the value we expect.
if payload != nil && !bytes.Equal(payload.FeeRecipient(), feeRecipient[:]) {
logrus.WithFields(logrus.Fields{
@@ -299,7 +293,7 @@ func getParentBlockHashPostMerge(st state.BeaconState) ([]byte, error) {
if err != nil {
return nil, errors.Wrap(err, "could not get post merge payload header")
}
return header.ParentHash(), nil
return header.BlockHash(), nil
}
// getParentBlockHashPreMerge retrieves the parent block hash before the merge has completed.

View File

@@ -71,8 +71,6 @@ func TestServer_getExecutionPayload(t *testing.T) {
Root: b2rCapella[:],
}))
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{{}}))
tests := []struct {
name string
st state.BeaconState
@@ -86,9 +84,10 @@ func TestServer_getExecutionPayload(t *testing.T) {
wantedOverride bool
}{
{
name: "transition completed, nil payload id",
st: transitionSt,
errString: "nil payload with block hash",
name: "transition completed, nil payload id",
st: transitionSt,
validatorIndx: 2,
errString: "nil payload with block hash",
},
{
name: "transition completed, happy case (has fee recipient in Db)",
@@ -110,6 +109,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
{
name: "transition completed, happy case, (payload ID cached)",
st: transitionSt,
payloadID: &pb.PayloadIDBytes{0x1},
validatorIndx: 100,
},
{
@@ -134,6 +134,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
st: transitionSt,
validatorIndx: 100,
override: true,
payloadID: &pb.PayloadIDBytes{0x1},
wantedOverride: true,
},
}
@@ -149,9 +150,13 @@ func TestServer_getExecutionPayload(t *testing.T) {
HeadFetcher: &chainMock.ChainService{State: tt.st},
FinalizationFetcher: &chainMock.ChainService{},
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
vs.TrackedValidatorsCache.Set(cache.TrackedValidator{Active: true, Index: tt.validatorIndx})
if tt.payloadID != nil {
vs.PayloadIDCache.Set(tt.st.Slot(), [32]byte{'a'}, [8]byte(*tt.payloadID))
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(tt.st.Slot(), 100, [8]byte{100}, [32]byte{'a'})
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = tt.st.Slot()
blk.Block.ProposerIndex = tt.validatorIndx
@@ -192,9 +197,10 @@ func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
ExecutionEngineCaller: &powtesting.EngineClient{PayloadIDBytes: &pb.PayloadIDBytes{}, ErrGetPayload: context.DeadlineExceeded, ExecutionPayload: &pb.ExecutionPayload{}},
HeadFetcher: &chainMock.ChainService{State: nonTransitionSt},
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nonTransitionSt.Slot(), 100, [8]byte{100}, [32]byte{'a'})
vs.PayloadIDCache.Set(nonTransitionSt.Slot(), [32]byte{'a'}, [8]byte{100})
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = nonTransitionSt.Slot()
@@ -231,10 +237,6 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
}))
feeRecipient := common.BytesToAddress([]byte("a"))
require.NoError(t, beaconDB.SaveFeeRecipientsByValidatorIDs(context.Background(), []primitives.ValidatorIndex{0}, []common.Address{
feeRecipient,
}))
payloadID := &pb.PayloadIDBytes{0x1}
payload := emptyPayload()
payload.FeeRecipient = feeRecipient[:]
@@ -246,8 +248,15 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
HeadFetcher: &chainMock.ChainService{State: transitionSt},
FinalizationFetcher: &chainMock.ChainService{},
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
val := cache.TrackedValidator{
Active: true,
FeeRecipient: primitives.ExecutionAddress(feeRecipient),
Index: 0,
}
vs.TrackedValidatorsCache.Set(val)
blk := util.NewBeaconBlockBellatrix()
blk.Block.Slot = transitionSt.Slot()
@@ -257,6 +266,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
gotPayload, _, err := vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
require.NoError(t, err)
require.NotNil(t, gotPayload)
require.Equal(t, common.Address(gotPayload.FeeRecipient()), feeRecipient)
// We should NOT be getting the warning.
require.LogsDoNotContain(t, hook, "Fee recipient address from execution client is not what was expected")
@@ -264,7 +274,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
evilRecipientAddress := common.BytesToAddress([]byte("evil"))
payload.FeeRecipient = evilRecipientAddress[:]
vs.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
vs.PayloadIDCache = cache.NewPayloadIDCache()
gotPayload, _, err = vs.getLocalPayload(context.Background(), b.Block(), transitionSt)
require.NoError(t, err)

View File

@@ -195,7 +195,6 @@ func TestServer_GetBeaconBlock_Altair(t *testing.T) {
func TestServer_GetBeaconBlock_Bellatrix(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
hook := logTest.NewGlobal()
terminalBlockHash := bytesutil.PadTo([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 32)
@@ -306,7 +305,6 @@ func TestServer_GetBeaconBlock_Bellatrix(t *testing.T) {
assert.DeepEqual(t, randaoReveal, bellatrixBlk.Bellatrix.Body.RandaoReveal, "Expected block to have correct randao reveal")
assert.DeepEqual(t, req.Graffiti, bellatrixBlk.Bellatrix.Body.Graffiti, "Expected block to have correct Graffiti")
require.LogsContain(t, hook, "Fee recipient is currently using the burn address")
require.DeepEqual(t, payload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
// Operator sets default fee recipient to not be burned through beacon node cli.
@@ -599,7 +597,8 @@ func getProposerServer(db db.HeadAccessDatabase, headState state.BeaconState, he
TimeFetcher: &testutil.MockGenesisTimeFetcher{
Genesis: time.Now(),
},
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
PayloadIDCache: cache.NewPayloadIDCache(),
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
BeaconDB: db,
BLSChangesPool: blstoexec.NewPool(),
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true},
@@ -629,9 +628,10 @@ func injectSlashings(t *testing.T, st state.BeaconState, keys []bls.SecretKey, s
func TestProposer_ProposeBlock_OK(t *testing.T) {
tests := []struct {
name string
block func([32]byte) *ethpb.GenericSignedBeaconBlock
err string
name string
block func([32]byte) *ethpb.GenericSignedBeaconBlock
err string
useBuilder bool
}{
{
name: "phase0",
@@ -678,6 +678,24 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
blk := &ethpb.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
useBuilder: true,
},
{
name: "blind capella no builder",
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
blockToPropose := util.NewBlindedBeaconBlockCapella()
blockToPropose.Block.Slot = 5
blockToPropose.Block.ParentRoot = parent[:]
txRoot, err := ssz.TransactionsRoot([][]byte{})
require.NoError(t, err)
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
blockToPropose.Block.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
blockToPropose.Block.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
blk := &ethpb.GenericSignedBeaconBlock_BlindedCapella{BlindedCapella: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
err: "unconfigured block builder",
},
{
name: "bellatrix",
@@ -699,6 +717,69 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
},
{
name: "deneb block some blobs",
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
blockToPropose := util.NewBeaconBlockContentsDeneb()
blockToPropose.Block.Block.Slot = 5
blockToPropose.Block.Block.ParentRoot = parent[:]
blockToPropose.Blobs = [][]byte{{0x01}, {0x02}, {0x03}}
blockToPropose.KzgProofs = [][]byte{{0x01}, {0x02}, {0x03}}
blockToPropose.Block.Block.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte("kc"), 48), bytesutil.PadTo([]byte("kc1"), 48), bytesutil.PadTo([]byte("kc2"), 48)}
blk := &ethpb.GenericSignedBeaconBlock_Deneb{Deneb: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
},
{
name: "deneb block some blobs (kzg and blob count missmatch)",
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
blockToPropose := util.NewBeaconBlockContentsDeneb()
blockToPropose.Block.Block.Slot = 5
blockToPropose.Block.Block.ParentRoot = parent[:]
blockToPropose.Blobs = [][]byte{{0x01}, {0x02}, {0x03}}
blockToPropose.KzgProofs = [][]byte{{0x01}, {0x02}, {0x03}}
blk := &ethpb.GenericSignedBeaconBlock_Deneb{Deneb: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
err: "blob KZG commitments don't match number of blobs or KZG proofs",
},
{
name: "blind deneb block some blobs",
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
blockToPropose := util.NewBlindedBeaconBlockDeneb()
blockToPropose.Message.Slot = 5
blockToPropose.Message.ParentRoot = parent[:]
txRoot, err := ssz.TransactionsRoot([][]byte{})
require.NoError(t, err)
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
blockToPropose.Message.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
blockToPropose.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
blockToPropose.Message.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte{0x01}, 48)}
blk := &ethpb.GenericSignedBeaconBlock_BlindedDeneb{BlindedDeneb: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
useBuilder: true,
},
{
name: "blind deneb block some blobs (commitment value does not match blob)",
block: func(parent [32]byte) *ethpb.GenericSignedBeaconBlock {
blockToPropose := util.NewBlindedBeaconBlockDeneb()
blockToPropose.Message.Slot = 5
blockToPropose.Message.ParentRoot = parent[:]
txRoot, err := ssz.TransactionsRoot([][]byte{})
require.NoError(t, err)
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
blockToPropose.Message.Body.ExecutionPayloadHeader.TransactionsRoot = txRoot[:]
blockToPropose.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:]
blockToPropose.Message.Body.BlobKzgCommitments = [][]byte{bytesutil.PadTo([]byte("kc"), 48)}
blk := &ethpb.GenericSignedBeaconBlock_BlindedDeneb{BlindedDeneb: blockToPropose}
return &ethpb.GenericSignedBeaconBlock{Block: blk}
},
useBuilder: true,
err: "unblind sidecars failed: commitment value doesn't match block",
},
}
for _, tt := range tests {
@@ -716,8 +797,11 @@ func TestProposer_ProposeBlock_OK(t *testing.T) {
BlockReceiver: c,
BlockNotifier: c.BlockNotifier(),
P2P: mockp2p.NewTestP2P(t),
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadCapella: emptyPayloadCapella(), PayloadDeneb: emptyPayloadDeneb(), BlobBundle: &enginev1.BlobsBundle{KzgCommitments: [][]byte{{0x01}}, Proofs: [][]byte{{0x02}}, Blobs: [][]byte{{0x03}}}},
BeaconDB: db,
BlockBuilder: &builderTest.MockBuilderService{HasConfigured: tt.useBuilder, PayloadCapella: emptyPayloadCapella(), PayloadDeneb: emptyPayloadDeneb(),
BlobBundle: &enginev1.BlobsBundle{KzgCommitments: [][]byte{bytesutil.PadTo([]byte{0x01}, 48)}, Proofs: [][]byte{{0x02}}, Blobs: [][]byte{{0x03}}}},
BeaconDB: db,
BlobReceiver: c,
OperationNotifier: c.OperationNotifier(),
}
blockToPropose := tt.block(bsRoot)
res, err := proposerServer.ProposeBeaconBlock(context.Background(), blockToPropose)
@@ -2609,16 +2693,19 @@ func TestProposer_PrepareBeaconProposer(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
proposerServer := &Server{BeaconDB: db}
proposerServer := &Server{
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
_, err := proposerServer.PrepareBeaconProposer(ctx, tt.args.request)
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, err)
return
}
require.NoError(t, err)
address, err := proposerServer.BeaconDB.FeeRecipientByValidatorID(ctx, 1)
require.NoError(t, err)
require.Equal(t, common.BytesToAddress(tt.args.request.Recipients[0].FeeRecipient), address)
val, tracked := proposerServer.TrackedValidatorsCache.Validator(1)
require.Equal(t, true, tracked)
require.Equal(t, primitives.ExecutionAddress(tt.args.request.Recipients[0].FeeRecipient), val.FeeRecipient)
})
}
@@ -2628,7 +2715,10 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
hook := logTest.NewGlobal()
db := dbutil.SetupDB(t)
ctx := context.Background()
proposerServer := &Server{BeaconDB: db}
proposerServer := &Server{
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
// New validator
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
@@ -2645,7 +2735,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
hook.Reset()
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
// Same validator with different fee recipient
hook.Reset()
@@ -2676,14 +2766,16 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
hook.Reset()
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsDoNotContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
}
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {
db := dbutil.SetupDB(b)
ctx := context.Background()
proposerServer := &Server{BeaconDB: db}
proposerServer := &Server{
BeaconDB: db,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
}
f := bytesutil.PadTo([]byte{0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF, 0x01, 0xFF}, fieldparams.FeeRecipientLength)
recipients := make([]*ethpb.PrepareBeaconProposerRequest_FeeRecipientContainer, 0)
for i := 0; i < 10000; i++ {

View File

@@ -44,7 +44,8 @@ import (
// and more.
type Server struct {
Ctx context.Context
ProposerSlotIndexCache *cache.ProposerPayloadIDsCache
PayloadIDCache *cache.PayloadIDCache
TrackedValidatorsCache *cache.TrackedValidatorsCache
HeadFetcher blockchain.HeadFetcher
ForkFetcher blockchain.ForkFetcher
ForkchoiceFetcher blockchain.ForkchoiceFetcher

View File

@@ -2,125 +2,18 @@ package validator
import (
"bytes"
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/builder"
consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types"
consensusblocks "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
type unblinder struct {
b interfaces.SignedBeaconBlock
builder builder.BlockBuilder
}
func newUnblinder(b interfaces.SignedBeaconBlock, builder builder.BlockBuilder) (*unblinder, error) {
if err := consensusblocks.BeaconBlockIsNil(b); err != nil {
return nil, err
}
if builder == nil {
return nil, errors.New("nil builder provided")
}
return &unblinder{
b: b,
builder: builder,
}, nil
}
func (u *unblinder) unblindBuilderBlock(ctx context.Context) (interfaces.SignedBeaconBlock, []*ethpb.BlobSidecar, error) {
if !u.b.IsBlinded() || u.b.Version() < version.Bellatrix {
return u.b, nil, nil
}
if u.b.IsBlinded() && !u.builder.Configured() {
return nil, nil, errors.New("builder not configured")
}
psb, err := u.blindedProtoBlock()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get blinded proto block")
}
sb, err := consensusblocks.NewSignedBeaconBlock(psb)
if err != nil {
return nil, nil, errors.Wrap(err, "could not create signed block")
}
if err = copyBlockData(u.b, sb); err != nil {
return nil, nil, errors.Wrap(err, "could not copy block data")
}
h, err := u.b.Block().Body().Execution()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get execution")
}
if err = sb.SetExecution(h); err != nil {
return nil, nil, errors.Wrap(err, "could not set execution")
}
payload, blobsBundle, err := u.builder.SubmitBlindedBlock(ctx, sb)
if err != nil {
return nil, nil, errors.Wrap(err, "could not submit blinded block")
}
headerRoot, err := h.HashTreeRoot()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get header root")
}
payloadRoot, err := payload.HashTreeRoot()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get payload root")
}
if headerRoot != payloadRoot {
return nil, nil, fmt.Errorf("header and payload root do not match, consider disconnect from relay to avoid further issues, "+
"%#x != %#x", headerRoot, payloadRoot)
}
bb, err := u.protoBlock()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get proto block")
}
wb, err := consensusblocks.NewSignedBeaconBlock(bb)
if err != nil {
return nil, nil, errors.Wrap(err, "could not create signed block")
}
if err = copyBlockData(sb, wb); err != nil {
return nil, nil, errors.Wrap(err, "could not copy block data")
}
if err = wb.SetExecution(payload); err != nil {
return nil, nil, errors.Wrap(err, "could not set execution")
}
txs, err := payload.Transactions()
if err != nil {
return nil, nil, errors.Wrap(err, "could not get transactions from payload")
}
if wb.Version() >= version.Deneb && blobsBundle != nil {
log.WithField("blobCount", len(blobsBundle.Blobs))
}
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", h.BlockHash()),
"feeRecipient": fmt.Sprintf("%#x", h.FeeRecipient()),
"gasUsed": h.GasUsed(),
"slot": u.b.Block().Slot(),
"txs": len(txs),
}).Info("Retrieved full payload from builder")
sidecars, err := unblindBlobsSidecars(u.b, blobsBundle)
if err != nil {
return nil, nil, errors.Wrap(err, "could not unblind blobs sidecars")
}
return wb, sidecars, nil
}
func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.BlobsBundle) ([]*ethpb.BlobSidecar, error) {
if bundle == nil {
if block.Version() < version.Deneb || bundle == nil {
return nil, nil
}
header, err := block.Header()
@@ -168,98 +61,3 @@ func unblindBlobsSidecars(block interfaces.SignedBeaconBlock, bundle *enginev1.B
}
return sidecars, nil
}
func copyBlockData(src interfaces.SignedBeaconBlock, dst interfaces.SignedBeaconBlock) error {
agg, err := src.Block().Body().SyncAggregate()
if err != nil {
return errors.Wrap(err, "could not get sync aggregate")
}
parentRoot := src.Block().ParentRoot()
stateRoot := src.Block().StateRoot()
randaoReveal := src.Block().Body().RandaoReveal()
graffiti := src.Block().Body().Graffiti()
sig := src.Signature()
blsToExecChanges, err := src.Block().Body().BLSToExecutionChanges()
if err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
return errors.Wrap(err, "could not get bls to execution changes")
}
kzgCommitments, err := src.Block().Body().BlobKzgCommitments()
if err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
return errors.Wrap(err, "could not get blob kzg commitments")
}
dst.SetSlot(src.Block().Slot())
dst.SetProposerIndex(src.Block().ProposerIndex())
dst.SetParentRoot(parentRoot[:])
dst.SetStateRoot(stateRoot[:])
dst.SetRandaoReveal(randaoReveal[:])
dst.SetEth1Data(src.Block().Body().Eth1Data())
dst.SetGraffiti(graffiti[:])
dst.SetProposerSlashings(src.Block().Body().ProposerSlashings())
dst.SetAttesterSlashings(src.Block().Body().AttesterSlashings())
dst.SetAttestations(src.Block().Body().Attestations())
dst.SetDeposits(src.Block().Body().Deposits())
dst.SetVoluntaryExits(src.Block().Body().VoluntaryExits())
if err = dst.SetSyncAggregate(agg); err != nil {
return errors.Wrap(err, "could not set sync aggregate")
}
dst.SetSignature(sig[:])
if err = dst.SetBLSToExecutionChanges(blsToExecChanges); err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
return errors.Wrap(err, "could not set bls to execution changes")
}
if err = dst.SetBlobKzgCommitments(kzgCommitments); err != nil && !errors.Is(err, consensus_types.ErrUnsupportedField) {
return errors.Wrap(err, "could not set bls to execution changes")
}
return nil
}
func (u *unblinder) blindedProtoBlock() (proto.Message, error) {
switch u.b.Version() {
case version.Bellatrix:
return &ethpb.SignedBlindedBeaconBlockBellatrix{
Block: &ethpb.BlindedBeaconBlockBellatrix{
Body: &ethpb.BlindedBeaconBlockBodyBellatrix{},
},
}, nil
case version.Capella:
return &ethpb.SignedBlindedBeaconBlockCapella{
Block: &ethpb.BlindedBeaconBlockCapella{
Body: &ethpb.BlindedBeaconBlockBodyCapella{},
},
}, nil
case version.Deneb:
return &ethpb.SignedBlindedBeaconBlockDeneb{
Message: &ethpb.BlindedBeaconBlockDeneb{
Body: &ethpb.BlindedBeaconBlockBodyDeneb{},
},
}, nil
default:
return nil, fmt.Errorf("invalid version %s", version.String(u.b.Version()))
}
}
func (u *unblinder) protoBlock() (proto.Message, error) {
switch u.b.Version() {
case version.Bellatrix:
return &ethpb.SignedBeaconBlockBellatrix{
Block: &ethpb.BeaconBlockBellatrix{
Body: &ethpb.BeaconBlockBodyBellatrix{},
},
}, nil
case version.Capella:
return &ethpb.SignedBeaconBlockCapella{
Block: &ethpb.BeaconBlockCapella{
Body: &ethpb.BeaconBlockBodyCapella{},
},
}, nil
case version.Deneb:
return &ethpb.SignedBeaconBlockDeneb{
Block: &ethpb.BeaconBlockDeneb{
Body: &ethpb.BeaconBlockBodyDeneb{},
},
}, nil
default:
return nil, fmt.Errorf("invalid version %s", version.String(u.b.Version()))
}
}

View File

@@ -1,409 +0,0 @@
package validator
import (
"context"
"testing"
"github.com/pkg/errors"
builderTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/builder/testing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
v1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
)
func Test_unblindBuilderBlock(t *testing.T) {
p := emptyPayload()
p.GasLimit = 123
pCapella := emptyPayloadCapella()
pCapella.GasLimit = 123
pDeneb := emptyPayloadDeneb()
pDeneb.GasLimit = 123
pDeneb.ExcessBlobGas = 456
pDeneb.BlobGasUsed = 789
denebblk, denebsidecars := util.GenerateTestDenebBlockWithSidecar(t, [32]byte{}, 0, fieldparams.MaxBlobsPerBlock)
denebCommitments, err := denebblk.Block().Body().BlobKzgCommitments()
require.NoError(t, err)
execution, err := denebblk.Block().Body().Execution()
require.NoError(t, err)
denebPayload, err := execution.PbDeneb()
require.NoError(t, err)
blobs := make([][]byte, len(denebsidecars))
for i, sidecar := range denebsidecars {
blobs[i] = sidecar.BlobSidecar.Blob
}
tests := []struct {
name string
blk interfaces.SignedBeaconBlock
mock *builderTest.MockBuilderService
err string
returnedBlk interfaces.SignedBeaconBlock
returnedBlobSidecars []blocks.ROBlob
}{
{
name: "old block version",
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
}(),
returnedBlk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
return wb
}(),
},
{
name: "blinded without configured builder",
blk: func() interfaces.SignedBeaconBlock {
wb, err := blocks.NewSignedBeaconBlock(util.NewBlindedBeaconBlockBellatrix())
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: false,
},
err: "builder not configured",
},
{
name: "non-blinded without configured builder",
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.ExecutionPayload = &v1.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),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
Transactions: make([][]byte, 0),
GasLimit: 123,
}
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: false,
Payload: p,
},
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.ExecutionPayload = p
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
},
{
name: "submit blind block error",
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
Payload: &v1.ExecutionPayload{},
HasConfigured: true,
ErrSubmitBlindedBlock: errors.New("can't submit"),
},
err: "can't submit",
},
{
name: "head and payload root mismatch",
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
Payload: p,
},
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.ExecutionPayload = p
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
err: "header and payload root do not match",
},
{
name: "can get payload Bellatrix",
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
txRoot, err := ssz.TransactionsRoot([][]byte{})
require.NoError(t, err)
b.Block.Body.ExecutionPayloadHeader = &v1.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),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: txRoot[:],
GasLimit: 123,
}
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
Payload: p,
},
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.ExecutionPayload = p
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
},
{
name: "can get payload Capella",
blk: func() interfaces.SignedBeaconBlock {
b := util.NewBlindedBeaconBlockCapella()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
{
Message: &eth.BLSToExecutionChange{
ValidatorIndex: 123,
FromBlsPubkey: []byte{'a'},
ToExecutionAddress: []byte{'a'},
},
Signature: []byte("sig123"),
},
{
Message: &eth.BLSToExecutionChange{
ValidatorIndex: 456,
FromBlsPubkey: []byte{'b'},
ToExecutionAddress: []byte{'b'},
},
Signature: []byte("sig456"),
},
}
txRoot, err := ssz.TransactionsRoot([][]byte{})
require.NoError(t, err)
withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*v1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
b.Block.Body.ExecutionPayloadHeader = &v1.ExecutionPayloadHeaderCapella{
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),
BaseFeePerGas: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
TransactionsRoot: txRoot[:],
WithdrawalsRoot: withdrawalsRoot[:],
GasLimit: 123,
}
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadCapella: pCapella,
},
returnedBlk: func() interfaces.SignedBeaconBlock {
b := util.NewBeaconBlockCapella()
b.Block.Slot = 1
b.Block.ProposerIndex = 2
b.Block.Body.BlsToExecutionChanges = []*eth.SignedBLSToExecutionChange{
{
Message: &eth.BLSToExecutionChange{
ValidatorIndex: 123,
FromBlsPubkey: []byte{'a'},
ToExecutionAddress: []byte{'a'},
},
Signature: []byte("sig123"),
},
{
Message: &eth.BLSToExecutionChange{
ValidatorIndex: 456,
FromBlsPubkey: []byte{'b'},
ToExecutionAddress: []byte{'b'},
},
Signature: []byte("sig456"),
},
}
b.Block.Body.ExecutionPayload = pCapella
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
},
{
name: "can get payload and blobs Deneb",
blk: func() interfaces.SignedBeaconBlock {
blindedBlock, err := denebblk.ToBlinded()
require.NoError(t, err)
b, err := blindedBlock.PbBlindedDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadDeneb: denebPayload,
BlobBundle: &v1.BlobsBundle{
KzgCommitments: denebCommitments,
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
Blobs: blobs,
},
},
returnedBlk: func() interfaces.SignedBeaconBlock {
b, err := denebblk.PbDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
returnedBlobSidecars: denebsidecars,
},
{
name: "deneb mismatch commitments count",
blk: func() interfaces.SignedBeaconBlock {
blindedBlock, err := denebblk.ToBlinded()
require.NoError(t, err)
b, err := blindedBlock.PbBlindedDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadDeneb: denebPayload,
BlobBundle: &v1.BlobsBundle{
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}},
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
Blobs: blobs,
},
},
err: "mismatch commitments count",
},
{
name: "deneb mismatch proofs count",
blk: func() interfaces.SignedBeaconBlock {
blindedBlock, err := denebblk.ToBlinded()
require.NoError(t, err)
b, err := blindedBlock.PbBlindedDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadDeneb: denebPayload,
BlobBundle: &v1.BlobsBundle{
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}, {'c', 5}},
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}},
Blobs: blobs,
},
},
err: "mismatch proofs count",
},
{
name: "deneb different count commitments bundle vs block",
blk: func() interfaces.SignedBeaconBlock {
blindedBlock, err := denebblk.ToBlinded()
require.NoError(t, err)
b, err := blindedBlock.PbBlindedDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadDeneb: denebPayload,
BlobBundle: &v1.BlobsBundle{
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}},
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}},
Blobs: blobs[:5],
},
},
err: "commitment count doesn't match block",
},
{
name: "deneb different value commitments bundle vs block",
blk: func() interfaces.SignedBeaconBlock {
blindedBlock, err := denebblk.ToBlinded()
require.NoError(t, err)
b, err := blindedBlock.PbBlindedDenebBlock()
require.NoError(t, err)
wb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
return wb
}(),
mock: &builderTest.MockBuilderService{
HasConfigured: true,
PayloadDeneb: denebPayload,
BlobBundle: &v1.BlobsBundle{
KzgCommitments: [][]byte{{'c', 0}, {'c', 1}, {'c', 2}, {'c', 3}, {'c', 4}, {'c', 5}},
Proofs: [][]byte{{'d', 0}, {'d', 1}, {'d', 2}, {'d', 3}, {'d', 4}, {'d', 5}},
Blobs: blobs,
},
},
err: "commitment value doesn't match block",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
unblinder, err := newUnblinder(tc.blk, tc.mock)
require.NoError(t, err)
gotBlk, gotBlobs, err := unblinder.unblindBuilderBlock(context.Background())
if tc.err != "" {
require.ErrorContains(t, tc.err, err)
} else {
require.NoError(t, err)
require.DeepEqual(t, tc.returnedBlk, gotBlk)
if tc.returnedBlobSidecars != nil {
blobs := make([]blocks.ROBlob, len(gotBlobs))
for i := range gotBlobs {
blobs[i], err = blocks.NewROBlob(gotBlobs[i])
require.NoError(t, err)
// TODO: update this check when generate function is updated for inclusion proofs require.DeepEqual(t, tc.returnedBlobSidecars[i].CommitmentInclusionProof, blobs[i].CommitmentInclusionProof)
require.DeepEqual(t, tc.returnedBlobSidecars[i].SignedBlockHeader, blobs[i].SignedBlockHeader)
require.Equal(t, len(tc.returnedBlobSidecars[i].Blob), len(blobs[i].Blob))
}
}
}
})
}
}

View File

@@ -128,12 +128,13 @@ type Config struct {
StateGen *stategen.State
MaxMsgSize int
ExecutionEngineCaller execution.EngineCaller
ProposerIdsCache *cache.ProposerPayloadIDsCache
OptimisticModeFetcher blockchain.OptimisticModeFetcher
BlockBuilder builder.BlockBuilder
Router *mux.Router
ClockWaiter startup.ClockWaiter
BlobStorage *filesystem.BlobStorage
TrackedValidatorsCache *cache.TrackedValidatorsCache
PayloadIDCache *cache.PayloadIDCache
}
// NewService instantiates a new RPC service instance that will
@@ -401,11 +402,12 @@ func (s *Service) Start() {
ReplayerBuilder: ch,
ExecutionEngineCaller: s.cfg.ExecutionEngineCaller,
BeaconDB: s.cfg.BeaconDB,
ProposerSlotIndexCache: s.cfg.ProposerIdsCache,
BlockBuilder: s.cfg.BlockBuilder,
BLSChangesPool: s.cfg.BLSChangesPool,
ClockWaiter: s.cfg.ClockWaiter,
CoreService: coreService,
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
PayloadIDCache: s.cfg.PayloadIDCache,
}
s.initializeValidatorServerRoutes(&validator.Server{
HeadFetcher: s.cfg.HeadFetcher,
@@ -418,11 +420,12 @@ func (s *Service) Start() {
V1Alpha1Server: validatorServer,
Stater: stater,
SyncCommitteePool: s.cfg.SyncCommitteeObjectPool,
ProposerSlotIndexCache: s.cfg.ProposerIdsCache,
ChainInfoFetcher: s.cfg.ChainInfoFetcher,
BeaconDB: s.cfg.BeaconDB,
BlockBuilder: s.cfg.BlockBuilder,
OperationNotifier: s.cfg.OperationNotifier,
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
PayloadIDCache: s.cfg.PayloadIDCache,
CoreService: coreService,
BlockRewardFetcher: rewardFetcher,
})

View File

@@ -164,7 +164,7 @@ func (b *BeaconState) ToProtoUnsafe() interface{} {
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
FinalizedCheckpoint: b.finalizedCheckpoint,
InactivityScores: b.inactivityScores,
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommittee,
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadHeader: b.latestExecutionPayloadHeaderDeneb,

View File

@@ -7,6 +7,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
statenative "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/config/features"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -733,3 +734,41 @@ func TestBeaconState_ValidatorMutation_Bellatrix(t *testing.T) {
assert.Equal(t, rt, rt2)
}
func TestBeaconState_InitializeInactivityScoresCorrectly_Deneb(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
EnableExperimentalState: true,
})
defer resetCfg()
st, _ := util.DeterministicGenesisStateDeneb(t, 200)
_, err := st.HashTreeRoot(context.Background())
require.NoError(t, err)
ic, err := st.InactivityScores()
require.NoError(t, err)
ic[10] = 10000
ic[100] = 1000
err = st.SetInactivityScores(ic)
require.NoError(t, err)
_, err = st.HashTreeRoot(context.Background())
require.NoError(t, err)
ic[150] = 2390239
err = st.SetInactivityScores(ic)
require.NoError(t, err)
rt, err := st.HashTreeRoot(context.Background())
require.NoError(t, err)
copiedSt, ok := st.ToProtoUnsafe().(*ethpb.BeaconStateDeneb)
if !ok {
t.Error("not ok")
}
newSt, err := statenative.InitializeFromProtoUnsafeDeneb(copiedSt)
require.NoError(t, err)
newRt, err := newSt.HashTreeRoot(context.Background())
require.NoError(t, err)
require.DeepSSZEqual(t, rt, newRt)
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)
@@ -98,7 +99,7 @@ func TestExtractBlockDataType(t *testing.T) {
chain: &mock.ChainService{ValidatorsRoot: [32]byte{}},
},
want: func() interfaces.ReadOnlySignedBeaconBlock {
wsb, err := blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{Body: &ethpb.BeaconBlockBodyBellatrix{}}})
wsb, err := blocks.NewSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{Body: &ethpb.BeaconBlockBodyBellatrix{ExecutionPayload: &enginev1.ExecutionPayload{}}}})
require.NoError(t, err)
return wsb
}(),

View File

@@ -7,6 +7,8 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/container/slice"
@@ -74,6 +76,14 @@ func (s *Service) validateAttesterSlashing(ctx context.Context, pid peer.ID, msg
}
s.cfg.chain.ReceiveAttesterSlashing(ctx, slashing)
// notify events
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.AttesterSlashingReceived,
Data: &operation.AttesterSlashingReceivedData{
AttesterSlashing: slashing,
},
})
msg.ValidatorData = slashing // Used in downstream subscriber
return pubsub.ValidationAccept, nil
}

View File

@@ -84,10 +84,11 @@ func TestValidateAttesterSlashing_ValidSlashing(t *testing.T) {
chain := &mock.ChainService{State: s, Genesis: time.Now()}
r := &Service{
cfg: &config{
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
operationNotifier: chain.OperationNotifier(),
},
seenAttesterSlashingCache: make(map[uint64]bool),
subHandler: newSubTopicHandler(),
@@ -129,10 +130,11 @@ func TestValidateAttesterSlashing_InvalidSlashing_WithdrawableEpoch(t *testing.T
chain := &mock.ChainService{State: s, Genesis: time.Now()}
r := &Service{
cfg: &config{
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
operationNotifier: chain.OperationNotifier(),
},
seenAttesterSlashingCache: make(map[uint64]bool),
subHandler: newSubTopicHandler(),

View File

@@ -6,6 +6,8 @@ import (
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/monitoring/tracing"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
@@ -55,6 +57,14 @@ func (s *Service) validateProposerSlashing(ctx context.Context, pid peer.ID, msg
return pubsub.ValidationReject, err
}
// notify events
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.ProposerSlashingReceived,
Data: &operation.ProposerSlashingReceivedData{
ProposerSlashing: slashing,
},
})
msg.ValidatorData = slashing // Used in downstream subscriber
return pubsub.ValidationAccept, nil
}

View File

@@ -117,10 +117,11 @@ func TestValidateProposerSlashing_ValidSlashing(t *testing.T) {
chain := &mock.ChainService{State: s, Genesis: time.Now()}
r := &Service{
cfg: &config{
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
initialSync: &mockSync.Sync{IsSyncing: false},
operationNotifier: chain.OperationNotifier(),
},
seenProposerSlashingCache: lruwrpr.New(10),
}

View File

@@ -14,7 +14,7 @@ var log = logrus.WithField("prefix", "db")
var Commands = &cli.Command{
Name: "db",
Category: "db",
Usage: "defines commands for interacting with the Ethereum Beacon Node database",
Usage: "Defines commands for interacting with the Ethereum Beacon Node database",
Subcommands: []*cli.Command{
{
Name: "restore",

View File

@@ -80,7 +80,7 @@ var (
// MonitoringPortFlag defines the http port used to serve prometheus metrics.
MonitoringPortFlag = &cli.IntFlag{
Name: "monitoring-port",
Usage: "Port used to listening and respond metrics for prometheus.",
Usage: "Port used to listening and respond metrics for Prometheus.",
Value: 8080,
}
// CertFlag defines a flag for the node's TLS certificate.

View File

@@ -15,12 +15,12 @@ var (
// MinimalConfigFlag declares to use the minimal config for running Ethereum consensus.
MinimalConfigFlag = &cli.BoolFlag{
Name: "minimal-config",
Usage: "Use minimal config with parameters as defined in the spec.",
Usage: "Uses minimal config with parameters as defined in the spec.",
}
// E2EConfigFlag declares to use a testing specific config for running Ethereum consensus in end-to-end testing.
E2EConfigFlag = &cli.BoolFlag{
Name: "e2e-config",
Usage: "Use the E2E testing config, only for use within end-to-end testing.",
Usage: "Enables the E2E testing config, only for use within end-to-end testing.",
}
// RPCMaxPageSizeFlag defines the maximum numbers per page returned in RPC responses from this
// beacon node (default: 500).
@@ -31,34 +31,35 @@ var (
// VerbosityFlag defines the logrus configuration.
VerbosityFlag = &cli.StringFlag{
Name: "verbosity",
Usage: "Logging verbosity (trace, debug, info=default, warn, error, fatal, panic)",
Usage: "Logging verbosity. (trace, debug, info, warn, error, fatal, panic)",
Value: "info",
}
// DataDirFlag defines a path on disk where Prysm databases are stored.
DataDirFlag = &cli.StringFlag{
Name: "datadir",
Usage: "Data directory for the databases",
Usage: "Data directory for the databases.",
Value: DefaultDataDir(),
}
// EnableBackupWebhookFlag for users to trigger db backups via an HTTP webhook.
EnableBackupWebhookFlag = &cli.BoolFlag{
Name: "enable-db-backup-webhook",
Usage: "Serve HTTP handler to initiate database backups. The handler is served on the monitoring port at path /db/backup.",
Name: "enable-db-backup-webhook",
Usage: `Serves HTTP handler to initiate database backups.
The handler is served on the monitoring port at path /db/backup.`,
}
// BackupWebhookOutputDir to customize the output directory for db backups.
BackupWebhookOutputDir = &cli.StringFlag{
Name: "db-backup-output-dir",
Usage: "Output directory for db backups",
Usage: "Output directory for db backups.",
}
// EnableTracingFlag defines a flag to enable p2p message tracing.
EnableTracingFlag = &cli.BoolFlag{
Name: "enable-tracing",
Usage: "Enable request tracing.",
Usage: "Enables request tracing.",
}
// TracingProcessNameFlag defines a flag to specify a process name.
TracingProcessNameFlag = &cli.StringFlag{
Name: "tracing-process-name",
Usage: "The name to apply to tracing tag \"process_name\"",
Usage: "Name to apply to tracing tag `process_name`.",
}
// TracingEndpointFlag flag defines the http endpoint for serving traces to Jaeger.
TracingEndpointFlag = &cli.StringFlag{
@@ -70,7 +71,7 @@ var (
// messages are sampled for tracing.
TraceSampleFractionFlag = &cli.Float64Flag{
Name: "trace-sample-fraction",
Usage: "Indicate what fraction of p2p messages are sampled for tracing.",
Usage: "Indicates what fraction of p2p messages are sampled for tracing.",
Value: 0.20,
}
// MonitoringHostFlag defines the host used to serve prometheus metrics.
@@ -82,13 +83,13 @@ var (
// DisableMonitoringFlag defines a flag to disable the metrics collection.
DisableMonitoringFlag = &cli.BoolFlag{
Name: "disable-monitoring",
Usage: "Disable monitoring service.",
Usage: "Disables monitoring service.",
}
// NoDiscovery specifies whether we are running a local network and have no need for connecting
// to the bootstrap nodes in the cloud
NoDiscovery = &cli.BoolFlag{
Name: "no-discovery",
Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes.",
Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes",
}
// StaticPeers specifies a set of peers to connect to explicitly.
StaticPeers = &cli.StringSliceFlag{
@@ -185,17 +186,17 @@ var (
// ForceClearDB removes any previously stored data at the data directory.
ForceClearDB = &cli.BoolFlag{
Name: "force-clear-db",
Usage: "Clear any previously stored data at the data directory",
Usage: "Clears any previously stored data at the data directory.",
}
// ClearDB prompts user to see if they want to remove any previously stored data at the data directory.
ClearDB = &cli.BoolFlag{
Name: "clear-db",
Usage: "Prompt for clearing any previously stored data at the data directory",
Usage: "Prompt for clearing any previously stored data at the data directory.",
}
// LogFormat specifies the log output format.
LogFormat = &cli.StringFlag{
Name: "log-format",
Usage: "Specify log formatting. Supports: text, json, fluentd, journald.",
Usage: "Specifies log formatting. Supports: text, json, fluentd, journald.",
Value: "text",
}
// MaxGoroutines specifies the maximum amount of goroutines tolerated, before a status check fails.
@@ -207,7 +208,7 @@ var (
// LogFileName specifies the log output file name.
LogFileName = &cli.StringFlag{
Name: "log-file",
Usage: "Specify log file name, relative or absolute",
Usage: "Specifies log file name, relative or absolute.",
}
// EnableUPnPFlag specifies if UPnP should be enabled or not. The default value is false.
EnableUPnPFlag = &cli.BoolFlag{
@@ -217,27 +218,27 @@ var (
// ConfigFileFlag specifies the filepath to load flag values.
ConfigFileFlag = &cli.StringFlag{
Name: "config-file",
Usage: "The filepath to a yaml file with flag values",
Usage: "Filepath to a yaml file with flag values.",
}
// ChainConfigFileFlag specifies the filepath to load flag values.
ChainConfigFileFlag = &cli.StringFlag{
Name: "chain-config-file",
Usage: "The path to a YAML file with chain config values",
Usage: "Path to a YAML file with chain config values.",
}
// GrpcMaxCallRecvMsgSizeFlag defines the max call message size for GRPC
GrpcMaxCallRecvMsgSizeFlag = &cli.IntFlag{
Name: "grpc-max-msg-size",
Usage: "Integer to define max receive message call size. If serving a public gRPC server, " +
"set this to a more reasonable size to avoid resource exhaustion from large messages. " +
"Validators with as many as 10000 keys can be run with a max message size of less than " +
"50Mb. The default here is set to a very high value for local users. " +
"(default: 2147483647 (2Gi)).",
Usage: `Integer to define max receive message call size (in bytes).
If serving a public gRPC server, set this to a more reasonable size to avoid
resource exhaustion from large messages.
Validators with as many as 10000 keys can be run with a max message size of less than
50Mb. The default here is set to a very high value for local users.`,
Value: math.MaxInt32,
}
// AcceptTosFlag specifies user acceptance of ToS for non-interactive environments.
AcceptTosFlag = &cli.BoolFlag{
Name: "accept-terms-of-use",
Usage: "Accept Terms and Conditions (for non-interactive environments)",
Usage: "Accepts Terms and Conditions (for non-interactive environments).",
}
// ValidatorMonitorIndicesFlag specifies a list of validator indices to
// track for performance updates
@@ -261,7 +262,7 @@ var (
// ApiTimeoutFlag specifies the timeout value for API requests in seconds. A timeout of zero means no timeout.
ApiTimeoutFlag = &cli.IntFlag{
Name: "api-timeout",
Usage: "Specifies the timeout value for API requests in seconds",
Usage: "Specifies the timeout value for API requests in seconds.",
Value: 120,
}
// JwtOutputFileFlag specifies the JWT file path that gets generated into when invoked by generate-jwt-secret.

View File

@@ -17,7 +17,7 @@ var log = logrus.WithField("prefix", "accounts")
var Commands = &cli.Command{
Name: "accounts",
Category: "accounts",
Usage: "defines commands for interacting with Ethereum validator accounts",
Usage: "Defines commands for interacting with Ethereum validator accounts.",
Subcommands: []*cli.Command{
{
Name: "delete",

View File

@@ -14,7 +14,7 @@ var log = logrus.WithField("prefix", "db")
var Commands = &cli.Command{
Name: "db",
Category: "db",
Usage: "defines commands for interacting with the Prysm validator database",
Usage: "Defines commands for interacting with the Prysm validator database.",
Subcommands: []*cli.Command{
{
Name: "restore",

View File

@@ -24,26 +24,26 @@ var (
// DisableAccountMetricsFlag disables the prometheus metrics for validator accounts, default false.
DisableAccountMetricsFlag = &cli.BoolFlag{
Name: "disable-account-metrics",
Usage: "Disable prometheus metrics for validator accounts. Operators with high volumes " +
"of validating keys may wish to disable granular prometheus metrics as it increases " +
"the data cardinality.",
Usage: `Disables prometheus metrics for validator accounts. Operators with high volumes
of validating keys may wish to disable granular prometheus metrics as it increases
the data cardinality.`,
}
// BeaconRPCProviderFlag defines a beacon node RPC endpoint.
BeaconRPCProviderFlag = &cli.StringFlag{
Name: "beacon-rpc-provider",
Usage: "Beacon node RPC provider endpoint",
Usage: "Beacon node RPC provider endpoint.",
Value: "127.0.0.1:4000",
}
// BeaconRPCGatewayProviderFlag defines a beacon node JSON-RPC endpoint.
BeaconRPCGatewayProviderFlag = &cli.StringFlag{
Name: "beacon-rpc-gateway-provider",
Usage: "Beacon node RPC gateway provider endpoint",
Usage: "Beacon node RPC gateway provider endpoint.",
Value: "127.0.0.1:3500",
}
// BeaconRESTApiProviderFlag defines a beacon node REST API endpoint.
BeaconRESTApiProviderFlag = &cli.StringFlag{
Name: "beacon-rest-api-provider",
Usage: "Beacon node REST API provider endpoint",
Usage: "Beacon node REST API provider endpoint.",
Value: "http://127.0.0.1:3500",
}
// CertFlag defines a flag for the node's TLS certificate.
@@ -54,25 +54,25 @@ var (
// EnableRPCFlag enables controlling the validator client via gRPC (without web UI).
EnableRPCFlag = &cli.BoolFlag{
Name: "rpc",
Usage: "Enables the RPC server for the validator client (without Web UI)",
Usage: "Enables the RPC server for the validator client (without Web UI).",
Value: false,
}
// RPCHost defines the host on which the RPC server should listen.
RPCHost = &cli.StringFlag{
Name: "rpc-host",
Usage: "Host on which the RPC server should listen",
Usage: "Host on which the RPC server should listen.",
Value: "127.0.0.1",
}
// RPCPort defines a validator client RPC port to open.
RPCPort = &cli.IntFlag{
Name: "rpc-port",
Usage: "RPC port exposed by a validator client",
Usage: "RPC port exposed by a validator client.",
Value: 7000,
}
// SlasherRPCProviderFlag defines a slasher node RPC endpoint.
SlasherRPCProviderFlag = &cli.StringFlag{
Name: "slasher-rpc-provider",
Usage: "Slasher node RPC provider endpoint",
Usage: "Slasher node RPC provider endpoint.",
Value: "127.0.0.1:4002",
}
// SlasherCertFlag defines a flag for the slasher node's TLS certificate.
@@ -83,85 +83,86 @@ var (
// DisablePenaltyRewardLogFlag defines the ability to not log reward/penalty information during deployment
DisablePenaltyRewardLogFlag = &cli.BoolFlag{
Name: "disable-rewards-penalties-logging",
Usage: "Disable reward/penalty logging during cluster deployment",
Usage: "Disables reward/penalty logging during cluster deployment.",
}
// GraffitiFlag defines the graffiti value included in proposed blocks
GraffitiFlag = &cli.StringFlag{
Name: "graffiti",
Usage: "String to include in proposed blocks",
Usage: "String to include in proposed blocks.",
}
// GrpcRetriesFlag defines the number of times to retry a failed gRPC request.
GrpcRetriesFlag = &cli.UintFlag{
Name: "grpc-retries",
Usage: "Number of attempts to retry gRPC requests",
Usage: "Number of attempts to retry gRPC requests.",
Value: 5,
}
// GrpcRetryDelayFlag defines the interval to retry a failed gRPC request.
GrpcRetryDelayFlag = &cli.DurationFlag{
Name: "grpc-retry-delay",
Usage: "The amount of time between gRPC retry requests.",
Usage: "Amount of time between gRPC retry requests.",
Value: 1 * time.Second,
}
// GrpcHeadersFlag defines a list of headers to send with all gRPC requests.
GrpcHeadersFlag = &cli.StringFlag{
Name: "grpc-headers",
Usage: "A comma separated list of key value pairs to pass as gRPC headers for all gRPC " +
"calls. Example: --grpc-headers=key=value",
Usage: `Comma separated list of key value pairs to pass as gRPC headers for all gRPC calls.
Example: --grpc-headers=key=value`,
}
// GRPCGatewayHost specifies a gRPC gateway host for the validator client.
GRPCGatewayHost = &cli.StringFlag{
Name: "grpc-gateway-host",
Usage: "The host on which the gateway server runs on",
Usage: "Host on which the gateway server runs on.",
Value: DefaultGatewayHost,
}
// GRPCGatewayPort enables a gRPC gateway to be exposed for the validator client.
GRPCGatewayPort = &cli.IntFlag{
Name: "grpc-gateway-port",
Usage: "Enable gRPC gateway for JSON requests",
Usage: "Enables gRPC gateway for JSON requests.",
Value: 7500,
}
// GPRCGatewayCorsDomain serves preflight requests when serving gRPC JSON gateway.
GPRCGatewayCorsDomain = &cli.StringFlag{
Name: "grpc-gateway-corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests " +
"(browser enforced). This flag has no effect if not used with --grpc-gateway-port.",
Usage: `Comma separated list of domains from which to accept cross origin requests (browser enforced).
This flag has no effect if not used with --grpc-gateway-port.
`,
Value: "http://localhost:7500,http://127.0.0.1:7500,http://0.0.0.0:7500,http://localhost:4242,http://127.0.0.1:4242,http://localhost:4200,http://0.0.0.0:4242,http://127.0.0.1:4200,http://0.0.0.0:4200,http://localhost:3000,http://0.0.0.0:3000,http://127.0.0.1:3000"}
// MonitoringPortFlag defines the http port used to serve prometheus metrics.
MonitoringPortFlag = &cli.IntFlag{
Name: "monitoring-port",
Usage: "Port used to listening and respond metrics for prometheus.",
Usage: "Port used to listening and respond metrics for Prometheus.",
Value: 8081,
}
// WalletDirFlag defines the path to a wallet directory for Prysm accounts.
WalletDirFlag = &cli.StringFlag{
Name: "wallet-dir",
Usage: "Path to a wallet directory on-disk for Prysm validator accounts",
Usage: "Path to a wallet directory on-disk for Prysm validator accounts.",
Value: filepath.Join(DefaultValidatorDir(), WalletDefaultDirName),
}
// AccountPasswordFileFlag is path to a file containing a password for a validator account.
AccountPasswordFileFlag = &cli.StringFlag{
Name: "account-password-file",
Usage: "Path to a plain-text, .txt file containing a password for a validator account",
Usage: "Path to a plain-text, .txt file containing a password for a validator account.",
}
// WalletPasswordFileFlag is the path to a file containing your wallet password.
WalletPasswordFileFlag = &cli.StringFlag{
Name: "wallet-password-file",
Usage: "Path to a plain-text, .txt file containing your wallet password",
Usage: "Path to a plain-text, .txt file containing your wallet password.",
}
// Mnemonic25thWordFileFlag defines a path to a file containing a "25th" word mnemonic passphrase for advanced users.
Mnemonic25thWordFileFlag = &cli.StringFlag{
Name: "mnemonic-25th-word-file",
Usage: "(Advanced) Path to a plain-text, .txt file containing a 25th word passphrase for your mnemonic for HD wallets",
Usage: "(Advanced) Path to a plain-text, `.txt` file containing a 25th word passphrase for your mnemonic for HD wallets.",
}
// SkipMnemonic25thWordCheckFlag allows for skipping a check for mnemonic 25th word passphrases for HD wallets.
SkipMnemonic25thWordCheckFlag = &cli.StringFlag{
Name: "skip-mnemonic-25th-word-check",
Usage: "Allows for skipping the check for a mnemonic 25th word passphrase for HD wallets",
Usage: "Allows for skipping the check for a mnemonic 25th word passphrase for HD wallets.",
}
// ImportPrivateKeyFileFlag allows for directly importing a private key hex string as an account.
ImportPrivateKeyFileFlag = &cli.StringFlag{
Name: "import-private-key-file",
Usage: "Path to a plain-text, .txt file containing a hex string representation of a private key to import",
Usage: "Path to a plain-text, .txt file containing a hex string representation of a private key to import.",
}
// MnemonicFileFlag is used to enter a file to mnemonic phrase for new wallet creation, non-interactively.
MnemonicFileFlag = &cli.StringFlag{
@@ -171,113 +172,113 @@ var (
// MnemonicLanguageFlag is used to specify the language of the mnemonic.
MnemonicLanguageFlag = &cli.StringFlag{
Name: "mnemonic-language",
Usage: "Allows specifying mnemonic language. Supported languages are: english|chinese_traditional|chinese_simplified|czech|french|japanese|korean|italian|spanish",
Usage: "Allows specifying mnemonic language. Supported languages are: english|chinese_traditional|chinese_simplified|czech|french|japanese|korean|italian|spanish.",
}
// ShowDepositDataFlag for accounts.
ShowDepositDataFlag = &cli.BoolFlag{
Name: "show-deposit-data",
Usage: "Display raw eth1 tx deposit data for validator accounts",
Usage: "Displays raw eth1 tx deposit data for validator accounts.",
Value: false,
}
// ShowPrivateKeysFlag for accounts.
ShowPrivateKeysFlag = &cli.BoolFlag{
Name: "show-private-keys",
Usage: "Display the private keys for validator accounts",
Usage: "Displays the private keys for validator accounts.",
Value: false,
}
// ListValidatorIndices for accounts.
ListValidatorIndices = &cli.BoolFlag{
Name: "list-validator-indices",
Usage: "List validator indices",
Usage: "Lists validator indices.",
Value: false,
}
// NumAccountsFlag defines the amount of accounts to generate for derived wallets.
NumAccountsFlag = &cli.IntFlag{
Name: "num-accounts",
Usage: "Number of accounts to generate for derived wallets",
Usage: "Number of accounts to generate for derived wallets.",
Value: 1,
}
// DeletePublicKeysFlag defines a comma-separated list of hex string public keys
// for accounts which a user desires to delete from their wallet.
DeletePublicKeysFlag = &cli.StringFlag{
Name: "delete-public-keys",
Usage: "Comma-separated list of public key hex strings to specify which validator accounts to delete",
Usage: "Comma separated list of public key hex strings to specify which validator accounts to delete.",
Value: "",
}
// BackupPublicKeysFlag defines a comma-separated list of hex string public keys
// for accounts which a user desires to backup from their wallet.
BackupPublicKeysFlag = &cli.StringFlag{
Name: "backup-public-keys",
Usage: "Comma-separated list of public key hex strings to specify which validator accounts to backup",
Usage: "Comma separated list of public key hex strings to specify which validator accounts to backup.",
Value: "",
}
// VoluntaryExitPublicKeysFlag defines a comma-separated list of hex string public keys
// for accounts on which a user wants to perform a voluntary exit.
VoluntaryExitPublicKeysFlag = &cli.StringFlag{
Name: "public-keys",
Usage: "Comma-separated list of public key hex strings to specify on which validator accounts to perform " +
"a voluntary exit",
Usage: "Comma separated list of public key hex strings to specify on which validator accounts to perform " +
"a voluntary exit.",
Value: "",
}
// ExitAllFlag allows stakers to select all validating keys for exit. This will still require the staker
// to confirm a userprompt for this action given it is a dangerous one.
ExitAllFlag = &cli.BoolFlag{
Name: "exit-all",
Usage: "Exit all validators. This will still require the staker to confirm a userprompt for the action",
Usage: "Exits all validators. This will still require the staker to confirm a userprompt for the action.",
}
// ForceExitFlag to exit without displaying the confirmation prompt.
ForceExitFlag = &cli.BoolFlag{
Name: "force-exit",
Usage: "Exit without displaying the confirmation prompt",
Usage: "Exits without displaying the confirmation prompt.",
}
VoluntaryExitJSONOutputPath = &cli.StringFlag{
Name: "exit-json-output-dir",
Usage: "The output directory to write voluntary exits as individual unencrypted JSON " +
Usage: "Output directory to write voluntary exits as individual unencrypted JSON " +
"files. If this flag is provided, voluntary exits will be written to the provided " +
"directory and will not be broadcasted.",
}
// BackupPasswordFile for encrypting accounts a user wishes to back up.
BackupPasswordFile = &cli.StringFlag{
Name: "backup-password-file",
Usage: "Path to a plain-text, .txt file containing the desired password for your backed up accounts",
Usage: "Path to a plain-text, .txt file containing the desired password for your backed up accounts.",
Value: "",
}
// BackupDirFlag defines the path for the zip backup of the wallet will be created.
BackupDirFlag = &cli.StringFlag{
Name: "backup-dir",
Usage: "Path to a directory where accounts will be backed up into a zip file",
Usage: "Path to a directory where accounts will be backed up into a zip file.",
Value: DefaultValidatorDir(),
}
// SlashingProtectionJSONFileFlag is used to enter the file path of the slashing protection JSON.
SlashingProtectionJSONFileFlag = &cli.StringFlag{
Name: "slashing-protection-json-file",
Usage: "Path to an EIP-3076 compliant JSON file containing a user's slashing protection history",
Usage: "Path to an EIP-3076 compliant JSON file containing a user's slashing protection history.",
}
// KeysDirFlag defines the path for a directory where keystores to be imported at stored.
KeysDirFlag = &cli.StringFlag{
Name: "keys-dir",
Usage: "Path to a directory where keystores to be imported are stored",
Usage: "Path to a directory where keystores to be imported are stored.",
}
// RemoteSignerCertPathFlag defines the path to a client.crt file for a wallet to connect to
// a secure signer via TLS and gRPC.
RemoteSignerCertPathFlag = &cli.StringFlag{
Name: "remote-signer-crt-path",
Usage: "/path/to/client.crt for establishing a secure, TLS gRPC connection to a remote signer server",
Usage: "/path/to/client.crt for establishing a secure, TLS gRPC connection to a remote signer server.",
Value: "",
}
// RemoteSignerKeyPathFlag defines the path to a client.key file for a wallet to connect to
// a secure signer via TLS and gRPC.
RemoteSignerKeyPathFlag = &cli.StringFlag{
Name: "remote-signer-key-path",
Usage: "/path/to/client.key for establishing a secure, TLS gRPC connection to a remote signer server",
Usage: "/path/to/client.key for establishing a secure, TLS gRPC connection to a remote signer server.",
Value: "",
}
// RemoteSignerCACertPathFlag defines the path to a ca.crt file for a wallet to connect to
// a secure signer via TLS and gRPC.
RemoteSignerCACertPathFlag = &cli.StringFlag{
Name: "remote-signer-ca-crt-path",
Usage: "/path/to/ca.crt for establishing a secure, TLS gRPC connection to a remote signer server",
Usage: "/path/to/ca.crt for establishing a secure, TLS gRPC connection to a remote signer server.",
Value: "",
}
// Web3SignerURLFlag defines the URL for a web3signer to connect to.
@@ -285,7 +286,7 @@ var (
// web3signer documentation can be found in Consensys' web3signer project docs
Web3SignerURLFlag = &cli.StringFlag{
Name: "validators-external-signer-url",
Usage: "URL for consensys' web3signer software to use with the Prysm validator client",
Usage: "URL for consensys' web3signer software to use with the Prysm validator client.",
Value: "",
}
@@ -295,65 +296,70 @@ var (
// web3signer documentation can be found in Consensys' web3signer project docs```
Web3SignerPublicValidatorKeysFlag = &cli.StringSliceFlag{
Name: "validators-external-signer-public-keys",
Usage: "comma separated list of public keys OR an external url endpoint for the validator to retrieve public keys from for usage with web3signer",
Usage: "Comma separated list of public keys OR an external url endpoint for the validator to retrieve public keys from for usage with web3signer.",
}
// KeymanagerKindFlag defines the kind of keymanager desired by a user during wallet creation.
KeymanagerKindFlag = &cli.StringFlag{
Name: "keymanager-kind",
Usage: "Kind of keymanager, either imported, derived, or remote, specified during wallet creation",
Usage: "Kind of keymanager, either imported, derived, or remote, specified during wallet creation.",
Value: "",
}
// SkipDepositConfirmationFlag skips the y/n confirmation userprompt for sending a deposit to the deposit contract.
SkipDepositConfirmationFlag = &cli.BoolFlag{
Name: "skip-deposit-confirmation",
Usage: "Skips the y/n confirmation userprompt for sending a deposit to the deposit contract",
Usage: "Skips the y/n confirmation userprompt for sending a deposit to the deposit contract.",
Value: false,
}
// EnableWebFlag enables controlling the validator client via the Prysm web ui. This is a work in progress.
EnableWebFlag = &cli.BoolFlag{
Name: "web",
Usage: "Enables the web portal for the validator client (work in progress)",
Usage: "(Work in progress): Enables the web portal for the validator client.",
Value: false,
}
// SlashingProtectionExportDirFlag allows specifying the outpt directory
// for a validator's slashing protection history.
SlashingProtectionExportDirFlag = &cli.StringFlag{
Name: "slashing-protection-export-dir",
Usage: "Allows users to specify the output directory to export their slashing protection EIP-3076 standard JSON File",
Usage: "Allows users to specify the output directory to export their slashing protection EIP-3076 standard JSON File.",
Value: "",
}
// GraffitiFileFlag specifies the file path to load graffiti values.
GraffitiFileFlag = &cli.StringFlag{
Name: "graffiti-file",
Usage: "The path to a YAML file with graffiti values",
Usage: "Path to a YAML file with graffiti values.",
}
// ProposerSettingsFlag defines the path or URL to a file with proposer config.
ProposerSettingsFlag = &cli.StringFlag{
Name: "proposer-settings-file",
Usage: "Set path to a YAML or JSON file containing validator settings used when proposing blocks such as (fee recipient and gas limit) (i.e. --proposer-settings-file=/path/to/proposer.json). File format found in docs",
Name: "proposer-settings-file",
Usage: `Sets path to a YAML or JSON file containing validator settings used when proposing blocks such as
fee recipient and gas limit. File format found in docs.`,
Value: "",
}
// ProposerSettingsURLFlag defines the path or URL to a file with proposer config.
ProposerSettingsURLFlag = &cli.StringFlag{
Name: "proposer-settings-url",
Usage: "Set URL to a REST endpoint containing validator settings used when proposing blocks such as (fee recipient) (i.e. --proposer-settings-url=https://example.com/api/getConfig). File format found in docs",
Name: "proposer-settings-url",
Usage: `Sets URL to a REST endpoint containing validator settings used when proposing blocks such as
fee recipient and gas limit. File format found in docs`,
Value: "",
}
// SuggestedFeeRecipientFlag defines the address of the fee recipient.
SuggestedFeeRecipientFlag = &cli.StringFlag{
Name: "suggested-fee-recipient",
Usage: "Sets ALL validators' mapping to a suggested eth address to receive gas fees when proposing a block." +
" note that this is only a suggestion when integrating with a Builder API, which may choose to specify a different fee recipient as payment for the blocks it builds." +
" For additional setting overrides use the --" + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " flags. ",
Usage: `Sets ALL validators' mapping to a suggested eth address to receive gas fees when proposing a block.
Note that this is only a suggestion when integrating with a Builder API, which may choose to specify
a different fee recipient as payment for the blocks it builds.For additional setting overrides use the
--` + ProposerSettingsFlag.Name + " or --" + ProposerSettingsURLFlag.Name + " flags.",
Value: params.BeaconConfig().EthBurnAddressHex,
}
// EnableBuilderFlag enables the periodic validator registration API calls that will update the custom builder with validator settings.
EnableBuilderFlag = &cli.BoolFlag{
Name: "enable-builder",
Usage: "Enables Builder validator registration APIs for the validator client to update settings such as fee recipient and gas limit. Note* this flag is not required if using proposer settings config file",
Name: "enable-builder",
Usage: `Enables builder validator registration APIs for the validator client to update settings
such as fee recipient and gas limit. This flag is not required if using proposer
settings config file.`,
Value: false,
Aliases: []string{"enable-validator-registration"},
}
@@ -361,13 +367,13 @@ var (
// BuilderGasLimitFlag defines the gas limit for the builder to use for constructing a payload.
BuilderGasLimitFlag = &cli.StringFlag{
Name: "suggested-gas-limit",
Usage: "Sets gas limit for the builder to use for constructing a payload for all the validators",
Usage: "Sets gas limit for the builder to use for constructing a payload for all the validators.",
Value: fmt.Sprint(params.BeaconConfig().DefaultBuilderGasLimit),
}
// ValidatorRegistrationBatchSizeFlag sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.
ValidatorRegistrationBatchSizeFlag = &cli.IntFlag{
Name: "validator-registration-batch-size",
// ValidatorsRegistrationBatchSizeFlag sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.
ValidatorsRegistrationBatchSizeFlag = &cli.IntFlag{
Name: "validators-registration-batch-size",
Usage: "Sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.",
Value: 0,
}

View File

@@ -8,14 +8,12 @@ import (
var (
InteropStartIndex = &cli.Uint64Flag{
Name: "interop-start-index",
Usage: "The start index to deterministically generate validator keys when used in combination with " +
"--interop-num-validators. Example: --interop-start-index=5 --interop-num-validators=3 would generate " +
"keys from index 5 to 7.",
Usage: `Start index to deterministically generate validator keys when used in combination with --interop-num-validators.
Example: --interop-start-index=5 --interop-num-validators=3 would generate keys from index 5 to 7.`,
}
InteropNumValidators = &cli.Uint64Flag{
Name: "interop-num-validators",
Usage: "The number of validators to deterministically generate. " +
"Example: --interop-start-index=5 --interop-num-validators=3 would generate " +
"keys from index 5 to 7.",
Usage: `Number of validators to deterministically generate.
Example: --interop-start-index=5 --interop-num-validators=3 would generate keys from index 5 to 7.`,
}
)

View File

@@ -79,7 +79,7 @@ var appFlags = []cli.Flag{
flags.ProposerSettingsFlag,
flags.EnableBuilderFlag,
flags.BuilderGasLimitFlag,
flags.ValidatorRegistrationBatchSizeFlag,
flags.ValidatorsRegistrationBatchSizeFlag,
////////////////////
cmd.DisableMonitoringFlag,
cmd.MonitoringHostFlag,
@@ -117,81 +117,79 @@ func init() {
}
func main() {
app := cli.App{}
app.Name = "validator"
app.Usage = `launches an Ethereum validator client that interacts with a beacon chain, starts proposer and attester services, p2p connections, and more`
app.Version = version.Version()
app.Action = func(ctx *cli.Context) error {
if err := startNode(ctx); err != nil {
return cli.Exit(err.Error(), 1)
}
return nil
}
app.Commands = []*cli.Command{
walletcommands.Commands,
accountcommands.Commands,
slashingprotectioncommands.Commands,
dbcommands.Commands,
web.Commands,
}
app.Flags = appFlags
app.Before = func(ctx *cli.Context) error {
// Load flags from config file, if specified.
if err := cmd.LoadFlagsFromConfig(ctx, app.Flags); err != nil {
return err
}
format := ctx.String(cmd.LogFormat.Name)
switch format {
case "text":
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05"
formatter.FullTimestamp = true
// If persistent log files are written - we disable the log messages coloring because
// the colors are ANSI codes and seen as Gibberish in the log files.
formatter.DisableColors = ctx.String(cmd.LogFileName.Name) != ""
logrus.SetFormatter(formatter)
case "fluentd":
f := joonix.NewFormatter()
if err := joonix.DisableTimestampFormat(f); err != nil {
panic(err)
app := cli.App{
Name: "validator",
Usage: "Launches an Ethereum validator client that interacts with a beacon chain, starts proposer and attester services, p2p connections, and more.",
Version: version.Version(),
Action: func(ctx *cli.Context) error {
if err := startNode(ctx); err != nil {
return cli.Exit(err.Error(), 1)
}
logrus.SetFormatter(f)
case "json":
logrus.SetFormatter(&logrus.JSONFormatter{})
case "journald":
if err := journald.Enable(); err != nil {
return nil
},
Commands: []*cli.Command{
walletcommands.Commands,
accountcommands.Commands,
slashingprotectioncommands.Commands,
dbcommands.Commands,
web.Commands,
},
Flags: appFlags,
Before: func(ctx *cli.Context) error {
// Load flags from config file, if specified.
if err := cmd.LoadFlagsFromConfig(ctx, appFlags); err != nil {
return err
}
default:
return fmt.Errorf("unknown log format %s", format)
}
logFileName := ctx.String(cmd.LogFileName.Name)
if logFileName != "" {
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
format := ctx.String(cmd.LogFormat.Name)
switch format {
case "text":
formatter := new(prefixed.TextFormatter)
formatter.TimestampFormat = "2006-01-02 15:04:05"
formatter.FullTimestamp = true
// If persistent log files are written - we disable the log messages coloring because
// the colors are ANSI codes and seen as Gibberish in the log files.
formatter.DisableColors = ctx.String(cmd.LogFileName.Name) != ""
logrus.SetFormatter(formatter)
case "fluentd":
f := joonix.NewFormatter()
if err := joonix.DisableTimestampFormat(f); err != nil {
panic(err)
}
logrus.SetFormatter(f)
case "json":
logrus.SetFormatter(&logrus.JSONFormatter{})
case "journald":
if err := journald.Enable(); err != nil {
return err
}
default:
return fmt.Errorf("unknown log format %s", format)
}
}
// Fix data dir for Windows users.
outdatedDataDir := filepath.Join(file.HomeDir(), "AppData", "Roaming", "Eth2Validators")
currentDataDir := flags.DefaultValidatorDir()
if err := cmd.FixDefaultDataDir(outdatedDataDir, currentDataDir); err != nil {
log.WithError(err).Error("Cannot update data directory")
}
logFileName := ctx.String(cmd.LogFileName.Name)
if logFileName != "" {
if err := logs.ConfigurePersistentLogging(logFileName); err != nil {
log.WithError(err).Error("Failed to configuring logging to disk.")
}
}
if err := debug.Setup(ctx); err != nil {
return err
}
return cmd.ValidateNoArgs(ctx)
}
// Fix data dir for Windows users.
outdatedDataDir := filepath.Join(file.HomeDir(), "AppData", "Roaming", "Eth2Validators")
currentDataDir := flags.DefaultValidatorDir()
if err := cmd.FixDefaultDataDir(outdatedDataDir, currentDataDir); err != nil {
log.WithError(err).Error("Cannot update data directory")
}
app.After = func(ctx *cli.Context) error {
debug.Exit(ctx)
return nil
if err := debug.Setup(ctx); err != nil {
return err
}
return cmd.ValidateNoArgs(ctx)
},
After: func(ctx *cli.Context) error {
debug.Exit(ctx)
return nil
},
}
defer func() {

View File

@@ -13,7 +13,7 @@ import (
var Commands = &cli.Command{
Name: "slashing-protection-history",
Category: "slashing-protection-history",
Usage: "defines commands for interacting your validator's slashing protection history",
Usage: "Defines commands for interacting your validator's slashing protection history.",
Subcommands: []*cli.Command{
{
Name: "export",

View File

@@ -14,25 +14,33 @@ import (
var appHelpTemplate = `NAME:
{{.App.Name}} - {{.App.Usage}}
USAGE:
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if .App.Version}}
AUTHOR:
{{range .App.Authors}}{{ . }}{{end}}
{{end}}{{if .App.Commands}}
GLOBAL OPTIONS:
{{if .App.Version}}
VERSION:
{{.App.Version}}
{{end -}}
{{if len .App.Authors}}
AUTHORS:
{{range .App.Authors}}{{ . }}
{{end -}}
{{end -}}
{{if .App.Commands}}
global OPTIONS:
{{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .FlagGroups}}
{{end -}}
{{end -}}
{{if .FlagGroups}}
{{range .FlagGroups}}{{.Name}} OPTIONS:
{{range .Flags}}{{.}}
{{end}}
{{end}}{{end}}{{if .App.Copyright }}
{{end -}}
{{end -}}
{{if .App.Copyright }}
COPYRIGHT:
{{.App.Copyright}}
VERSION:
{{.App.Version}}
{{end}}{{if len .App.Authors}}
{{end}}
{{end -}}
`
type flagGroup struct {
@@ -113,7 +121,7 @@ var appHelpFlagGroups = []flagGroup{
flags.SuggestedFeeRecipientFlag,
flags.EnableBuilderFlag,
flags.BuilderGasLimitFlag,
flags.ValidatorRegistrationBatchSizeFlag,
flags.ValidatorsRegistrationBatchSizeFlag,
},
},
{

View File

@@ -15,7 +15,7 @@ var log = logrus.WithField("prefix", "wallet")
var Commands = &cli.Command{
Name: "wallet",
Category: "wallet",
Usage: "defines commands for interacting with Ethereum validator wallets",
Usage: "Defines commands for interacting with Ethereum validator wallets.",
Subcommands: []*cli.Command{
{
Name: "create",

View File

@@ -15,7 +15,7 @@ import (
var Commands = &cli.Command{
Name: "web",
Category: "web",
Usage: "defines commands for interacting with the Prysm web interface",
Usage: "Defines commands for interacting with the Prysm web interface.",
Subcommands: []*cli.Command{
{
Name: "generate-auth-token",

View File

@@ -40,7 +40,6 @@ type Flags struct {
EnableExperimentalState bool // EnableExperimentalState turns on the latest and greatest (but potentially unstable) changes to the beacon state.
WriteSSZStateTransitions bool // WriteSSZStateTransitions to tmp directory.
EnablePeerScorer bool // EnablePeerScorer enables experimental peer scoring in p2p.
DisableReorgLateBlocks bool // DisableReorgLateBlocks disables reorgs of late blocks.
WriteWalletPasswordOnWebOnboarding bool // WriteWalletPasswordOnWebOnboarding writes the password to disk after Prysm web signup.
EnableDoppelGanger bool // EnableDoppelGanger enables doppelganger protection on startup for the validator.
EnableHistoricalSpaceRepresentation bool // EnableHistoricalSpaceRepresentation enables the saving of registry validators in separate buckets to save space
@@ -65,7 +64,6 @@ type Flags struct {
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails
EnableOptionalEngineMethods bool // EnableOptionalEngineMethods specifies whether to activate capella specific engine methods
EnableEIP4881 bool // EnableEIP4881 specifies whether to use the deposit tree from EIP4881
PrepareAllPayloads bool // PrepareAllPayloads informs the engine to prepare a block on every slot.
@@ -192,10 +190,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
cfg.DisableGRPCConnectionLogs = true
}
if ctx.Bool(disableReorgLateBlocks.Name) {
logEnabled(disableReorgLateBlocks)
cfg.DisableReorgLateBlocks = true
}
cfg.EnablePeerScorer = true
if ctx.Bool(disablePeerScorer.Name) {
logDisabled(disablePeerScorer)
@@ -233,11 +227,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(enableVerboseSigVerification)
cfg.EnableVerboseSigVerification = true
}
cfg.EnableOptionalEngineMethods = true
if ctx.IsSet(disableOptionalEngineMethods.Name) {
logEnabled(disableOptionalEngineMethods)
cfg.EnableOptionalEngineMethods = false
}
if ctx.IsSet(prepareAllPayloads.Name) {
logEnabled(prepareAllPayloads)
cfg.PrepareAllPayloads = true

View File

@@ -53,6 +53,16 @@ var (
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableReorgLateBlocks = &cli.BoolFlag{
Name: "disable-reorg-late-blocks",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableOptionalEngineMethods = &cli.BoolFlag{
Name: "disable-optional-engine-methods",
Usage: deprecatedUsage,
Hidden: true,
}
)
// Deprecated flags for both the beacon node and validator client.
@@ -66,6 +76,8 @@ var deprecatedFlags = []cli.Flag{
deprecatedAggregateParallel,
deprecatedEnableOptionalEngineMethods,
deprecatedDisableBuildBlockParallel,
deprecatedDisableReorgLateBlocks,
deprecatedDisableOptionalEngineMethods,
}
// deprecatedBeaconFlags contains flags that are still used by other components

Some files were not shown because too many files have changed in this diff Show More