Compare commits

...

83 Commits

Author SHA1 Message Date
Raul Jordan
8b8a38039b radek feedback 2022-10-25 11:49:48 -04:00
Raul Jordan
5410c30890 Merge branch 'blob-rotating-buffer' of github.com:rauljordan/prysm into blob-rotating-buffer 2022-10-25 11:45:56 -04:00
Raul Jordan
8024758ff4 Update beacon-chain/db/kv/blobs.go
Co-authored-by: Radosław Kapka <radek@prysmaticlabs.com>
2022-10-25 11:42:14 -04:00
Raul Jordan
826a930ed0 Update beacon-chain/db/kv/blobs.go
Co-authored-by: Radosław Kapka <radek@prysmaticlabs.com>
2022-10-25 11:42:09 -04:00
Raul Jordan
3d45b83059 Update beacon-chain/db/kv/blobs.go
Co-authored-by: Radosław Kapka <radek@prysmaticlabs.com>
2022-10-25 11:39:58 -04:00
Raul Jordan
9096c4a8c4 dedup comment 2022-10-24 21:48:25 -04:00
Raul Jordan
31f369c982 comment 2022-10-24 21:42:51 -04:00
Raul Jordan
8a7ab754b2 algorithm and test done 2022-10-24 21:33:19 -04:00
Raul Jordan
752bb63ec4 save blobs test 2022-10-24 21:15:10 -04:00
Raul Jordan
52a87e912d unit test 2022-10-24 20:59:14 -04:00
Raul Jordan
3400af655c rotating buffer db schema for blobs 2022-10-24 20:45:54 -04:00
terence tsao
1e5c0778b9 Add compute_aggregated_poly_and_commitment 2022-09-27 13:09:42 -07:00
terence tsao
a032cf2ccc Sync with upstream 2022-09-23 13:46:04 -07:00
terence tsao
f871d7668f Merge branch 'develop' of github.com:terencechain/prysm into eip4844 2022-09-23 13:19:21 -07:00
terence tsao
5c2ebec369 Can build 2022-08-20 13:11:44 -07:00
terence tsao
a22d62e398 Sync with upstream 2022-08-20 12:43:41 -07:00
terence tsao
00c57e76ec Sync with upstream 2022-08-09 20:28:04 -07:00
terence tsao
1d800e42c5 Merge branch 'develop' of https://github.com/prysmaticlabs/prysm into eip4844 2022-08-09 20:20:40 -07:00
terence tsao
1391ccedd2 Refactor forkchoice valid data status 2022-08-05 17:55:50 -07:00
terence tsao
e71124ee87 Clean up db and generated pbs 2022-08-04 16:51:15 -07:00
terence tsao
f04b2be9de Sync with native block changes 2022-08-04 11:13:35 -07:00
terence tsao
080810ec25 Merge branch 'develop' of https://github.com/prysmaticlabs/prysm into eip4844 2022-08-04 10:35:14 -07:00
terence tsao
37118e7815 Clean up core 2022-08-02 16:56:17 -07:00
terence tsao
a390c74721 Encapsulate sidecar within signed block interface 2022-08-01 15:49:00 -07:00
terence tsao
9b260be2c7 Sync with upstream 2022-07-31 17:45:38 -07:00
terence tsao
bd6cf8d5d8 Sync with @inphi's clean-slate and upstream 2022-07-31 17:37:02 -07:00
terence tsao
bff4435b72 Merge branch 'inphi/clean-slate' of https://github.com/inphi/prysm into eip4844 2022-07-31 16:39:54 -07:00
terence tsao
d8187b7bb1 Merge branch 'develop' of https://github.com/prysmaticlabs/prysm into eip4844 2022-07-30 18:41:12 -07:00
inphi
109a0f5f0f update EIP4844 BeaconChainConfig 2022-07-28 15:15:29 -04:00
inphi
bc0ba2d508 Rate-limit sidecar requests during initial-sync 2022-07-28 02:59:20 -04:00
inphi
5b53ad6cdf Add EIP-4844 bootstrap node for devnet 2022-07-27 01:18:33 -04:00
inphi
920e0757d9 Update bazel deps; errcheck linting 2022-07-22 12:00:29 -04:00
Michael de Hoog
bd01ffaa97 Add aggregated proof to APIs (#10) 2022-07-21 15:18:38 -04:00
inphi
41f34aa6dd tweak eip4844 testnet configs 2022-07-13 11:56:16 -04:00
inphi
48c26b9eec configure blobs prune period in testnet 2022-07-13 01:53:09 -04:00
inphi
eed1e47664 Add --eip4844 testnet flag for ez config 2022-07-13 01:47:41 -04:00
inphi
b18ebab455 Incorporate data availability to fork-choice 2022-07-08 17:08:27 -04:00
inphi
ebe1e9ec3a nit 2022-07-05 18:38:03 -04:00
inphi
b2b4aa9406 Prune blobs older than MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS 2022-07-05 16:39:02 -04:00
inphi
7d6f079697 testing: validateBeaconBlock w/ kzgs 2022-06-30 19:47:39 -04:00
Murphy Law
b2a9a79e96 Sync: Blobs Sidecar (#8)
* Add MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS config
* Implement inital-sync for blob sidecars
* Assert data availability before transitioning blocks from INVALID to VALID
* Harden sidecar validation in pubsub subscriber
* Introduce sidecar pending queue to defer processing for out-of-order block/sidecars
2022-06-30 14:58:06 -04:00
inphi
cb524c5cef Merge remote-tracking branch 'origin/develop' into inphi/clean-slate-merge 2022-06-23 23:53:34 -04:00
Murphy Law
2e0a6d6556 p2p: Handle sidecar message broadcasts (#7)
NOTE: Syncing blobs from genesis (initial-sync) doesn't work yet. This change only allows beacon nodes to synchronized broadcast blob sidecars.
2022-06-16 13:31:26 -04:00
Michael de Hoog
ac4a4cc80d Fix BlobsBundle JSON (un)marshaling (#6)
* Register RPC handlers on EIP-4844 fork

* Fix JSON marshaling of blob bundles
2022-06-16 11:07:50 -04:00
inphi
027e49c120 add IsPreEIP4844Version utility func 2022-06-16 02:05:24 -04:00
inphi
ff822a6f4e Avoid EIP-4844 validation for older block versions 2022-06-16 01:42:40 -04:00
inphi
901a087a6d fix SaveBlobs span typo 2022-06-15 18:37:57 -04:00
inphi
be8c0293ad validate BeaconBlock for EIP-4844 2022-06-15 17:44:34 -04:00
inphi
d5d1b33761 Add rpc_blobs_sidecars_by_range_test to bazel deps 2022-06-14 17:15:57 -04:00
Michael de Hoog
53bb657bd2 EIP-4844 beacon RPCs (#5)
* Add EIP-4844 messages and generate code

* Add v1 to v2 conversion

* Add middleware

* Serialize the blob KZGs rather than the bundles

* Add SignedBlindedBeaconBlockEip4844 to SSZ code generation

* Fix version

* Add debugging conversion

* Add v1alpha blinded implementation

* Switch index of blob sidecar

* Remove BlobsBundle from SSZ for now

* Fix permissions

* Remove EIP-4844 specific beacon state

* Some more missing types

* Remove dangling method

* Use sequential proto ids

* Copy the KZGs array
2022-06-14 16:54:48 -04:00
inphi
e2bf25e088 fix BlobsSidecarsByRange RPC Request routine 2022-06-13 23:40:29 -04:00
inphi
31e1999ddf add generated ssz files for BlobsSidecarsByRangeRequest 2022-06-13 21:08:24 -04:00
inphi
cb3da77ff4 testing: Assert received fields for BlobsSidecarsByRange RPC 2022-06-13 20:44:40 -04:00
inphi
40c754e991 p2p: Implement BlobsSidecarsByRange RPC 2022-06-13 20:33:02 -04:00
inphi
b6a05ecd32 add missing bazel dependencies 2022-06-13 17:55:11 -04:00
inphi
3a6a7d7bcd persist proposed blobs sidecar 2022-06-13 17:46:53 -04:00
inphi
27677da136 assert len(blobkzgs) == len(blob_versioned_hashes) 2022-06-13 16:17:24 -04:00
Michael de Hoog
eb1c7bd5f6 Replace [32]byte with common.Hash (#4) 2022-06-13 16:00:53 -04:00
Michael de Hoog
b3d2ed1aaf TxPeekBlobVersionedHashes: fix hash copying (#3) 2022-06-13 15:50:59 -04:00
Michael de Hoog
bf1b5a5094 [EIP-4844] Fix BlobVersionedHashes SSZ parsing (#1) 2022-06-13 11:38:08 -04:00
inphi
d3b6b2d8bc Fix blobsBundle JSON parsing 2022-06-10 10:05:37 -04:00
inphi
43814449c0 Update geth dependency
Using mdehoog's branch from now on as it contains the latest changes
2022-06-09 18:12:48 -04:00
inphi
533a6b3f0c fix EIP4844 BeaconBlock signature 2022-06-09 17:57:38 -04:00
inphi
8e370da195 Add blob kzgs to the proposed BeaconBlock 2022-06-09 15:12:14 -04:00
inphi
024ad7b433 Fix bazel build
- Update deps.bzl
- Fix generated go pb files due to buggy goimports
2022-06-09 13:10:39 -04:00
inphi
13becadb30 Blobs verification and signing for block proposals
Note: local INTEROP testing works pre-eip484
2022-06-09 12:23:26 -04:00
inphi
e3f1a2d321 Add eip4844 fork configs
- Also bumped the sharding configs so its value can be reused for eip4844
- Applied 01379e0ab5cd216ea7d0afd89b2ff5f64e04d9cb
2022-06-09 10:35:47 -04:00
inphi
0f8c4431fc wip: Getting closer to full blobs impl tip
- Add side as a new field to the beacon-chain/validator gRPC API
- Retrieve blobs from EL via engine API and attach to the BeaconBlock.

Note; This implementation passes manual INTEROP tests for pre-eip4844
block production
2022-06-09 09:51:34 -04:00
inphi
f566d50a43 Merge commit '80546f83e9e15e54668e69308f9c5898db667cf1' into inphi/clean-slate 2022-06-07 01:45:14 -04:00
terence tsao
7fc3a697d8 Add verify kzgs against version hashes 2022-05-24 20:58:31 -07:00
terence tsao
c5ee1c2735 Merge branch 'develop' of https://github.com/prysmaticlabs/prysm into eip4844 2022-05-24 20:32:10 -07:00
terence tsao
658725a601 Port eip4844 changes from Prysm 2022-05-22 09:20:20 -07:00
terence tsao
80546f83e9 Initial EIP4844 support 2022-03-06 20:26:23 -08:00
prylabs-bulldozer[bot]
d7489f9f0f Merge refs/heads/develop into get-payload 2022-03-06 08:09:17 +00:00
terence tsao
aad947b83f Merge branch 'get-payload' of github.com:prysmaticlabs/prysm into get-payload 2022-03-04 15:06:29 -08:00
terence tsao
7d2a257094 Update proposer_execution_payload_test.go 2022-03-04 15:06:18 -08:00
prylabs-bulldozer[bot]
965280190a Merge refs/heads/develop into get-payload 2022-03-04 22:25:09 +00:00
terence tsao
bcffe3d291 Update proposer_execution_payload.go 2022-03-04 12:20:32 -08:00
terence tsao
e3438cd9ee Update client.go 2022-03-04 12:15:19 -08:00
terence tsao
a8bc8af9e7 Merge branch 'get-payload' of github.com:prysmaticlabs/prysm into get-payload 2022-03-04 11:14:42 -08:00
terence tsao
e70492ec8c Add prepare execution payload 2022-03-04 11:14:24 -08:00
terence tsao
ea9979f813 Add prepare execution payload 2022-03-04 11:13:05 -08:00
terence tsao
e504d31285 Add prepare execution payload 2022-03-04 11:11:02 -08:00
150 changed files with 13881 additions and 3576 deletions

View File

@@ -62,6 +62,7 @@ go_library(
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blobs:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",

View File

@@ -38,14 +38,14 @@ func logStateTransitionData(b interfaces.BeaconBlock) error {
if len(b.Body().VoluntaryExits()) > 0 {
log = log.WithField("voluntaryExits", len(b.Body().VoluntaryExits()))
}
if b.Version() == version.Altair || b.Version() == version.Bellatrix {
if b.Version() == version.Altair || b.Version() == version.Bellatrix || b.Version() == version.EIP4844 {
agg, err := b.Body().SyncAggregate()
if err != nil {
return err
}
log = log.WithField("syncBitsCount", agg.SyncCommitteeBits.Count())
}
if b.Version() == version.Bellatrix {
if b.Version() == version.Bellatrix || b.Version() == version.EIP4844 {
p, err := b.Body().Execution()
if err != nil {
return err
@@ -62,6 +62,13 @@ func logStateTransitionData(b interfaces.BeaconBlock) error {
}
}
if b.Version() == version.EIP4844 {
k, err := b.Body().BlobKzgs()
if err != nil {
return err
}
log = log.WithField("blobKzgCount", len(k))
}
log.Info("Finished applying state transition")
return nil
}

View File

@@ -328,7 +328,7 @@ func reportEpochMetrics(ctx context.Context, postState, headState state.BeaconSt
if err != nil {
return err
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
v, b, err = altair.InitializePrecomputeValidators(ctx, headState)
if err != nil {
return err

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/config/features"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
@@ -143,6 +144,16 @@ func (s *Service) onBlock(ctx context.Context, signed interfaces.SignedBeaconBlo
if err := s.insertBlockToForkchoiceStore(ctx, signed.Block(), blockRoot, postState); err != nil {
return errors.Wrapf(err, "could not insert block %d to fork choice store", signed.Block().Slot())
}
hasSideCar, err := blobs.BlockContainsSidecar(signed)
if err != nil {
return err
}
if hasSideCar {
if err := s.cfg.ForkChoiceStore.SetValidData(ctx, blockRoot); err != nil {
return err
}
}
if err := s.handleBlockAttestations(ctx, signed.Block(), postState); err != nil {
return errors.Wrap(err, "could not handle block's attestations")
}
@@ -296,8 +307,7 @@ func getStateVersionAndPayload(st state.BeaconState) (int, *enginev1.ExecutionPa
return preStateVersion, preStateHeader, nil
}
func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeaconBlock,
blockRoots [][32]byte) error {
func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
ctx, span := trace.StartSpan(ctx, "blockChain.onBlockBatch")
defer span.End()
@@ -416,6 +426,20 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
tracing.AnnotateError(span, err)
return err
}
hasSideCar, err := blobs.BlockContainsSidecar(b)
if err != nil {
return err
}
if hasSideCar {
if err := s.cfg.ForkChoiceStore.SetValidData(ctx, blockRoots[i]); err != nil {
return err
}
}
if err := s.saveSidecar(ctx, b); err != nil {
return err
}
if i > 0 && jCheckpoints[i].Epoch > jCheckpoints[i-1].Epoch {
if err := s.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, jCheckpoints[i]); err != nil {
tracing.AnnotateError(span, err)
@@ -433,6 +457,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
if err := s.cfg.ForkChoiceStore.InsertOptimisticChain(ctx, pendingNodes); err != nil {
return errors.Wrap(err, "could not insert batch to forkchoice")
}
// Insert the last block to forkchoice
lastBR := blockRoots[len(blks)-1]
if err := s.cfg.ForkChoiceStore.InsertNode(ctx, preState, lastBR); err != nil {
@@ -571,15 +596,36 @@ func (s *Service) InsertSlashingsToForkChoiceStore(ctx context.Context, slashing
func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interfaces.SignedBeaconBlock, st state.BeaconState) error {
ctx, span := trace.StartSpan(ctx, "blockChain.savePostStateInfo")
defer span.End()
if err := s.cfg.BeaconDB.SaveBlock(ctx, b); err != nil {
return errors.Wrapf(err, "could not save block from slot %d", b.Block().Slot())
}
if err := s.saveSidecar(ctx, b); err != nil {
return errors.Wrapf(err, "could not save sidecar from slot %d", b.Block().Slot())
}
if err := s.cfg.StateGen.SaveState(ctx, r, st); err != nil {
return errors.Wrap(err, "could not save state")
}
return nil
}
// Saves sidecar to the DB for the compatible block that contains the sidecar.
func (s *Service) saveSidecar(ctx context.Context, b interfaces.SignedBeaconBlock) error {
ok, err := blobs.BlockContainsSidecar(b)
if err != nil {
return errors.Wrap(err, "could not determine if block contains sidecar")
}
if !ok {
return nil
}
sc, err := b.SideCar()
if err != nil {
return errors.Wrap(err, "could not get sidecar")
}
return s.cfg.BeaconDB.SaveBlobsSidecar(ctx, sc.Message)
}
// This removes the attestations from the mem pool. It will only remove the attestations if input root `r` is canonical,
// meaning the block `b` is part of the canonical chain.
func (s *Service) pruneCanonicalAttsFromPool(ctx context.Context, r [32]byte, b interfaces.SignedBeaconBlock) error {

View File

@@ -53,7 +53,7 @@ func (c *SyncCommitteeHeadStateCache) Get(slot types.Slot) (state.BeaconState, e
return nil, ErrIncorrectType
}
switch st.Version() {
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
default:
return nil, ErrIncorrectType
}

View File

@@ -183,7 +183,7 @@ func UpdateBalance(vp []*Validator, bBal *Balance, stateVersion int) *Balance {
if stateVersion == version.Phase0 && v.IsPrevEpochAttester {
bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance
}
if (stateVersion == version.Altair || stateVersion == version.Bellatrix) && v.IsPrevEpochSourceAttester {
if (stateVersion == version.Altair || stateVersion == version.Bellatrix || stateVersion == version.EIP4844) && v.IsPrevEpochSourceAttester {
bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance
}
if v.IsPrevEpochTargetAttester {

View File

@@ -83,3 +83,15 @@ func UpgradeToBellatrix(state state.BeaconState) (state.BeaconState, error) {
return state_native.InitializeFromProtoUnsafeBellatrix(s)
}
// UpgradeToEip4844 updates inputs a generic state to return the version Eip4844 state.
func UpgradeToEip4844(state state.BeaconState) (state.BeaconState, error) {
if err := state.SetFork(&ethpb.Fork{
PreviousVersion: state.Fork().CurrentVersion,
CurrentVersion: params.BeaconConfig().Eip4844ForkVersion,
Epoch: time.CurrentEpoch(state),
}); err != nil {
return nil, err
}
return state, nil
}

View File

@@ -70,6 +70,12 @@ func CanUpgradeToBellatrix(slot types.Slot) bool {
return epochStart && bellatrixEpoch
}
func CanUpgradeToEip4844(slot types.Slot) bool {
epochStart := slots.IsEpochStart(slot)
e := slots.ToEpoch(slot) == params.BeaconConfig().Eip4844ForkEpoch
return epochStart && e
}
// CanProcessEpoch checks the eligibility to process epoch.
// The epoch can be processed at the end of the last slot of every epoch.
//

View File

@@ -36,6 +36,7 @@ go_library(
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blobs:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",

View File

@@ -255,7 +255,7 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
state, err = altair.ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
@@ -285,6 +285,14 @@ func ProcessSlots(ctx context.Context, state state.BeaconState, slot types.Slot)
return nil, err
}
}
if time.CanUpgradeToEip4844(state.Slot()) {
state, err = execution.UpgradeToEip4844(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
if highestSlot < state.Slot() {

View File

@@ -11,9 +11,11 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition/interop"
v "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/validators"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
"go.opencensus.io/trace"
@@ -167,8 +169,14 @@ func ProcessBlockNoVerifyAnySig(
return nil, nil, err
}
if st.Version() != signed.Block().Version() {
return nil, nil, fmt.Errorf("state and block are different version. %d != %d", st.Version(), signed.Block().Version())
sv := st.Version()
bv := signed.Block().Version()
switch {
case sv == bv:
case sv == version.Bellatrix && bv == version.EIP4844:
// The EIP-4844 BeaconState is the same as Bellatrix's
default:
return nil, nil, fmt.Errorf("state and block are different version. %d != %d", sv, bv)
}
blk := signed.Block()
@@ -243,7 +251,7 @@ func ProcessOperationsNoVerifyAttsSigs(
if err != nil {
return nil, err
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
state, err = altairOperations(ctx, state, signedBeaconBlock)
if err != nil {
return nil, err
@@ -255,6 +263,36 @@ func ProcessOperationsNoVerifyAttsSigs(
return state, nil
}
// ProcessBlobKzgs validates the blob kzgs in the beacon block.
// def process_blob_kzg_commitments(state: BeaconState, body: BeaconBlockBody):
// assert verify_kzg_commitments_against_transactions(body.execution_payload.transactions, body.blob_kzg_commitments
func ProcessBlobKzgs(ctx context.Context, state state.BeaconState, body interfaces.BeaconBlockBody) (state.BeaconState, error) {
_, span := trace.StartSpan(ctx, "core.state.ProocessBlobKzgs")
defer span.End()
payload, err := body.Execution()
if err != nil {
return nil, errors.Wrap(err, "could not get execution payload from block")
}
blobKzgs, err := body.BlobKzgs()
if err != nil {
return nil, errors.Wrap(err, "could not get blob kzgs from block")
}
blobKzgsInput := make([][48]byte, len(blobKzgs))
for i := range blobKzgs {
blobKzgsInput[i] = bytesutil.ToBytes48(blobKzgs[i])
}
txs, err := payload.Transactions()
if err != nil {
return nil, errors.Wrap(err, "could not get transactions from payload")
}
if err := blobs.VerifyKzgsAgainstTxs(txs, blobKzgsInput); err != nil {
return nil, err
}
return state, nil
}
// ProcessBlockForStateRoot processes the state for state root computation. It skips proposer signature
// and randao signature verifications.
//
@@ -267,6 +305,7 @@ func ProcessOperationsNoVerifyAttsSigs(
// process_eth1_data(state, block.body)
// process_operations(state, block.body)
// process_sync_aggregate(state, block.body.sync_aggregate)
// process_blob_kzgs(state, block.body) # [New in EIP-4844]
func ProcessBlockForStateRoot(
ctx context.Context,
state state.BeaconState,
@@ -342,6 +381,15 @@ func ProcessBlockForStateRoot(
return nil, errors.Wrap(err, "process_sync_aggregate failed")
}
if blocks.IsPreEIP4844Version(signed.Block().Version()) {
return state, nil
}
state, err = ProcessBlobKzgs(ctx, state, signed.Block().Body())
if err != nil {
return nil, errors.Wrap(err, "process_blob_kzgs failed")
}
return state, nil
}

View File

@@ -30,6 +30,10 @@ type ReadOnlyDatabase interface {
GenesisBlockRoot(ctx context.Context) ([32]byte, error)
IsFinalizedBlock(ctx context.Context, blockRoot [32]byte) bool
FinalizedChildBlock(ctx context.Context, blockRoot [32]byte) (interfaces.SignedBeaconBlock, error)
// Blobs related methods.
BlobsSidecar(ctx context.Context, blockRoot [32]byte) (*ethpb.BlobsSidecar, error)
BlobsSidecarsBySlot(ctx context.Context, slot types.Slot) ([]*ethpb.BlobsSidecar, error)
HasBlobsSidecar(ctx context.Context, blockRoot [32]byte) bool
HighestRootsBelowSlot(ctx context.Context, slot types.Slot) (types.Slot, [][32]byte, error)
// State related methods.
State(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
@@ -68,6 +72,9 @@ type NoHeadAccessDatabase interface {
SaveBlock(ctx context.Context, block interfaces.SignedBeaconBlock) error
SaveBlocks(ctx context.Context, blocks []interfaces.SignedBeaconBlock) error
SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error
// Blob related methods.
SaveBlobsSidecar(ctx context.Context, blob *ethpb.BlobsSidecar) error
DeleteBlobsSidecar(ctx context.Context, blockRoot [32]byte) error
// State related methods.
SaveState(ctx context.Context, state state.ReadOnlyBeaconState, blockRoot [32]byte) error
SaveStates(ctx context.Context, states []state.ReadOnlyBeaconState, blockRoots [][32]byte) error

View File

@@ -5,6 +5,7 @@ go_library(
srcs = [
"archived_point.go",
"backup.go",
"blobs.go",
"blocks.go",
"checkpoint.go",
"deposit_contract.go",

180
beacon-chain/db/kv/blobs.go Normal file
View File

@@ -0,0 +1,180 @@
package kv
import (
"bytes"
"context"
"fmt"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
const blobSidecarKeyLength = 48 // slot_to_rotating_buffer(blob.slot) ++ blob.slot ++ blob.block_root
// SaveBlobsSidecar saves the blobs for a given epoch in the sidecar bucket. When we receive a blob:
// 1. Convert slot using a modulo operator to [0, maxSlots] where maxSlots = MAX_BLOB_EPOCHS*SLOTS_PER_EPOCH
// 2. Compute key for blob as bytes(slot_to_rotating_buffer(blob.slot)) ++ bytes(blob.slot) ++ blob.block_root
// 3. Begin the save algorithm: If the incoming blob has a slot bigger than the saved slot at the spot
// in the rotating keys buffer, we overwrite all elements for that slot.
//
// firstElemKey = getFirstElement(bucket)
// shouldOverwrite = blob.slot > bytes_to_slot(firstElemKey[8:16])
// if shouldOverwrite:
// for existingKey := seek prefix bytes(slot_to_rotating_buffer(blob.slot))
// bucket.delete(existingKey)
// bucket.put(key, blob)
func (s *Store) SaveBlobsSidecar(ctx context.Context, blobSidecar *ethpb.BlobsSidecar) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SaveBlobsSidecar")
defer span.End()
return s.db.Update(func(tx *bolt.Tx) error {
encodedBlobSidecar, err := encode(ctx, blobSidecar)
if err != nil {
return err
}
bkt := tx.Bucket(blobsBucket)
c := bkt.Cursor()
key := blobSidecarKey(blobSidecar)
rotatingBufferPrefix := key[0:8]
var firstElementKey []byte
for k, _ := c.Seek(rotatingBufferPrefix); bytes.HasPrefix(k, rotatingBufferPrefix); k, _ = c.Next() {
if len(k) != 0 {
firstElementKey = k
break
}
}
// If there is no element stored at blob.slot % MAX_SLOTS_TO_PERSIST_BLOBS, then we simply
// store the blob by key and exit early.
if len(firstElementKey) == 0 {
return bkt.Put(key, encodedBlobSidecar)
} else if len(firstElementKey) != len(key) {
return fmt.Errorf(
"key length %d (%#x) != existing key length %d (%#x)",
len(key),
key,
len(firstElementKey),
firstElementKey,
)
}
slotOfFirstElement := firstElementKey[8:16]
// If we should overwrite old blobs at the spot in the rotating buffer, we clear data at that spot.
shouldOverwrite := blobSidecar.BeaconBlockSlot > bytesutil.BytesToSlotBigEndian(slotOfFirstElement)
if shouldOverwrite {
for k, _ := c.Seek(rotatingBufferPrefix); bytes.HasPrefix(k, rotatingBufferPrefix); k, _ = c.Next() {
if err := bkt.Delete(k); err != nil {
log.Warnf("Could not delete blob with key %#x: %v", k, err)
}
}
}
return bkt.Put(key, encodedBlobSidecar)
})
}
// BlobsSidecar retrieves the blobs given a beacon block root.
func (s *Store) BlobsSidecar(ctx context.Context, beaconBlockRoot [32]byte) (*ethpb.BlobsSidecar, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.BlobsSidecar")
defer span.End()
var enc []byte
if err := s.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(blobsBucket).Cursor()
// Bucket size is bounded and bolt cursors are fast. Moreover, a thin caching layer can be added.
for k, v := c.First(); k != nil; k, v = c.Next() {
if bytes.HasSuffix(k, beaconBlockRoot[:]) {
enc = v
break
}
}
return nil
}); err != nil {
return nil, err
}
if len(enc) == 0 {
return nil, nil
}
blob := &ethpb.BlobsSidecar{}
if err := decode(ctx, enc, blob); err != nil {
return nil, err
}
return blob, nil
}
// BlobsSidecarsBySlot retrieves sidecars from a slot.
func (s *Store) BlobsSidecarsBySlot(ctx context.Context, slot types.Slot) ([]*ethpb.BlobsSidecar, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.BlobsSidecarsBySlot")
defer span.End()
encodedItems := make([][]byte, 0)
if err := s.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(blobsBucket).Cursor()
// Bucket size is bounded and bolt cursors are fast. Moreover, a thin caching layer can be added.
for k, v := c.First(); k != nil; k, v = c.Next() {
if len(k) != blobSidecarKeyLength {
continue
}
slotInKey := bytesutil.BytesToSlotBigEndian(k[8:16])
if slotInKey == slot {
encodedItems = append(encodedItems, v)
}
}
return nil
}); err != nil {
return nil, err
}
sidecars := make([]*ethpb.BlobsSidecar, len(encodedItems))
if len(encodedItems) == 0 {
return sidecars, nil
}
for i, enc := range encodedItems {
blob := &ethpb.BlobsSidecar{}
if err := decode(ctx, enc, blob); err != nil {
return nil, err
}
sidecars[i] = blob
}
return sidecars, nil
}
// HasBlobsSidecar returns true if the blobs are in the db.
func (s *Store) HasBlobsSidecar(ctx context.Context, beaconBlockRoot [32]byte) bool {
ctx, span := trace.StartSpan(ctx, "BeaconDB.HasBlobsSidecar")
defer span.End()
blobSidecar, err := s.BlobsSidecar(ctx, beaconBlockRoot)
if err != nil {
return false
}
return blobSidecar != nil
}
// DeleteBlobsSidecar returns true if the blobs are in the db.
func (s *Store) DeleteBlobsSidecar(ctx context.Context, beaconBlockRoot [32]byte) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteBlobsSidecar")
defer span.End()
return s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blobsBucket)
c := bkt.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() {
if bytes.HasSuffix(k, beaconBlockRoot[:]) {
if err := bkt.Delete(k); err != nil {
return err
}
}
}
return nil
})
}
// We define a blob sidecar key as: bytes(slot_to_rotating_buffer(blob.slot)) ++ bytes(blob.slot) ++ blob.block_root
// where slot_to_rotating_buffer(slot) = slot % MAX_SLOTS_TO_PERSIST_BLOBS.
func blobSidecarKey(blob *ethpb.BlobsSidecar) []byte {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
maxEpochsToPersistBlobs := params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest
maxSlotsToPersistBlobs := types.Slot(maxEpochsToPersistBlobs.Mul(uint64(slotsPerEpoch)))
slotInRotatingBuffer := blob.BeaconBlockSlot.ModSlot(maxSlotsToPersistBlobs)
key := bytesutil.SlotToBytesBigEndian(slotInRotatingBuffer)
key = append(key, bytesutil.SlotToBytesBigEndian(blob.BeaconBlockSlot)...)
key = append(key, blob.BeaconBlockRoot...)
return key
}

View File

@@ -0,0 +1,101 @@
package kv
import (
"bytes"
"context"
"fmt"
"testing"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/testing/require"
bolt "go.etcd.io/bbolt"
)
func TestBlobsSidecar_Overwriting(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconNetworkConfig()
// For purposes of testing, we only keep blob sidecars around for 2 epochs. At third epoch, we will
// wrap around and overwrite the oldest epoch's elements as the keys for blobs work as a rotating buffer.
cfg.MinEpochsForBlobsSidecarsRequest = 2
params.OverrideBeaconNetworkConfig(cfg)
db := setupDB(t)
sidecars := make([]*ethpb.BlobsSidecar, 0)
numSlots := uint64(cfg.MinEpochsForBlobsSidecarsRequest) * uint64(params.BeaconConfig().SlotsPerEpoch)
for i := uint64(0); i < numSlots; i++ {
// There can be multiple blobs per slot with different block roots, so we create some
// in order to have a thorough test.
root1 := bytesutil.ToBytes32([]byte(fmt.Sprintf("foo-%d", i)))
root2 := bytesutil.ToBytes32([]byte(fmt.Sprintf("bar-%d", i)))
sidecars = append(sidecars, &ethpb.BlobsSidecar{
BeaconBlockRoot: root1[:],
BeaconBlockSlot: types.Slot(i),
Blobs: make([]*enginev1.Blob, 0),
AggregatedProof: make([]byte, 48),
})
sidecars = append(sidecars, &ethpb.BlobsSidecar{
BeaconBlockRoot: root2[:],
BeaconBlockSlot: types.Slot(i),
Blobs: make([]*enginev1.Blob, 0),
AggregatedProof: make([]byte, 48),
})
}
ctx := context.Background()
for _, blobSidecar := range sidecars {
require.NoError(t, db.SaveBlobsSidecar(ctx, blobSidecar))
require.Equal(t, true, db.HasBlobsSidecar(ctx, bytesutil.ToBytes32(blobSidecar.BeaconBlockRoot)))
}
// We check there are only two blob sidecars stored at slot 0, as an example.
keyPrefix := append(bytesutil.SlotToBytesBigEndian(0), bytesutil.SlotToBytesBigEndian(0)...)
numBlobs := countBlobsWithPrefix(t, db, keyPrefix)
require.Equal(t, 2, numBlobs)
// Attempting to save another blob sidecar with slot 0 and a new block root should result
// in three blob sidecars stored at slot 0. This means we are NOT overwriting old data.
root := bytesutil.ToBytes32([]byte("baz-0"))
sidecar := &ethpb.BlobsSidecar{
BeaconBlockRoot: root[:],
BeaconBlockSlot: types.Slot(0),
Blobs: make([]*enginev1.Blob, 0),
AggregatedProof: make([]byte, 48),
}
require.NoError(t, db.SaveBlobsSidecar(ctx, sidecar))
require.Equal(t, true, db.HasBlobsSidecar(ctx, bytesutil.ToBytes32(sidecar.BeaconBlockRoot)))
numBlobs = countBlobsWithPrefix(t, db, keyPrefix)
require.Equal(t, 3, numBlobs)
// Now, we attempt to save a blob sidecar with slot = MAX_SLOTS_TO_PERSIST_BLOBS. This SHOULD cause us to
// overwrite ALL old data at slot 0, as slot % MAX_SLOTS_TO_PERSIST_BLOBS will be equal to 0.
// We should expect a single blob sidecar to exist at slot 0 after this operation.
root = bytesutil.ToBytes32([]byte(fmt.Sprintf("foo-%d", numSlots)))
sidecar = &ethpb.BlobsSidecar{
BeaconBlockRoot: root[:],
BeaconBlockSlot: types.Slot(numSlots),
Blobs: make([]*enginev1.Blob, 0),
AggregatedProof: make([]byte, 48),
}
require.NoError(t, db.SaveBlobsSidecar(ctx, sidecar))
require.Equal(t, true, db.HasBlobsSidecar(ctx, bytesutil.ToBytes32(sidecar.BeaconBlockRoot)))
keyPrefix = append(bytesutil.SlotToBytesBigEndian(0), bytesutil.SlotToBytesBigEndian(64)...)
numBlobs = countBlobsWithPrefix(t, db, keyPrefix)
require.Equal(t, 1, numBlobs)
}
func countBlobsWithPrefix(t *testing.T, db *Store, prefix []byte) int {
numBlobSidecars := 0
require.NoError(t, db.db.View(func(tx *bolt.Tx) error {
c := tx.Bucket(blobsBucket).Cursor()
for k, _ := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, _ = c.Next() {
numBlobSidecars++
}
return nil
}))
return numBlobSidecars
}

View File

@@ -241,6 +241,10 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
return err
}
if err := s.DeleteBlobsSidecar(ctx, root); err != nil {
return err
}
return s.db.Update(func(tx *bolt.Tx) error {
bkt := tx.Bucket(finalizedBlockRootsIndexBucket)
if b := bkt.Get(root[:]); b != nil {
@@ -789,6 +793,11 @@ func unmarshalBlock(_ context.Context, enc []byte) (interfaces.SignedBeaconBlock
if err := rawBlock.UnmarshalSSZ(enc[len(bellatrixBlindKey):]); err != nil {
return nil, errors.Wrap(err, "could not unmarshal blinded Bellatrix block")
}
case hasEip4844Key(enc):
rawBlock = &ethpb.SignedBeaconBlockWithBlobKZGs{}
if err := rawBlock.UnmarshalSSZ(enc[len(eip4844Key):]); err != nil {
return nil, err
}
default:
// Marshal block bytes to phase 0 beacon block.
rawBlock = &ethpb.SignedBeaconBlock{}
@@ -828,6 +837,8 @@ func marshalBlock(_ context.Context, blk interfaces.SignedBeaconBlock) ([]byte,
}
}
switch blockToSave.Version() {
case version.EIP4844:
return snappy.Encode(nil, append(eip4844Key, encodedBlock...)), nil
case version.Bellatrix:
if blockToSave.IsBlinded() {
return snappy.Encode(nil, append(bellatrixBlindKey, encodedBlock...)), nil

View File

@@ -23,3 +23,10 @@ func hasBellatrixBlindKey(enc []byte) bool {
}
return bytes.Equal(enc[:len(bellatrixBlindKey)], bellatrixBlindKey)
}
func hasEip4844Key(enc []byte) bool {
if len(eip4844Key) >= len(enc) {
return false
}
return bytes.Equal(enc[:len(eip4844Key)], eip4844Key)
}

View File

@@ -74,6 +74,7 @@ var BlockCacheSize = int64(1 << 21)
// summary, it can be read in https://github.com/prysmaticlabs/prysm/issues/8274.
var blockedBuckets = [][]byte{
blocksBucket,
blobsBucket,
stateSummaryBucket,
blockParentRootIndicesBucket,
blockSlotIndicesBucket,
@@ -159,6 +160,8 @@ func NewKVStore(ctx context.Context, dirPath string) (*Store, error) {
tx,
attestationsBucket,
blocksBucket,
blobsBucket,
blobsAgesBucket,
stateBucket,
proposerSlashingsBucket,
attesterSlashingsBucket,

View File

@@ -9,6 +9,8 @@ package kv
var (
attestationsBucket = []byte("attestations")
blocksBucket = []byte("blocks")
blobsBucket = []byte("blobs")
blobsAgesBucket = []byte("blobs-ages")
stateBucket = []byte("state")
stateSummaryBucket = []byte("state-summary")
proposerSlashingsBucket = []byte("proposer-slashings")
@@ -52,6 +54,7 @@ var (
altairKey = []byte("altair")
bellatrixKey = []byte("merge")
bellatrixBlindKey = []byte("blind-bellatrix")
eip4844Key = []byte("eip4844")
// block root included in the beacon state used by weak subjectivity initial sync
originCheckpointBlockRootKey = []byte("origin-checkpoint-block-root")
// block root tracking the progress of backfill, or pointing at genesis if backfill has not been initiated

View File

@@ -31,6 +31,8 @@ const (
ForkchoiceUpdatedMethod = "engine_forkchoiceUpdatedV1"
// GetPayloadMethod v1 request string for JSON-RPC.
GetPayloadMethod = "engine_getPayloadV1"
// GetBlobsBundleMethod v1 request string for JSON-RPC.
GetBlobsBundleMethod = "engine_getBlobsBundleV1"
// ExchangeTransitionConfigurationMethod v1 request string for JSON-RPC.
ExchangeTransitionConfigurationMethod = "engine_exchangeTransitionConfigurationV1"
// ExecutionBlockByHashMethod request string for JSON-RPC.
@@ -75,6 +77,7 @@ type EngineCaller interface {
) error
ExecutionBlockByHash(ctx context.Context, hash common.Hash, withTxs bool) (*pb.ExecutionBlock, error)
GetTerminalBlockHash(ctx context.Context, transitionTime uint64) ([]byte, bool, error)
GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error)
}
// NewPayload calls the engine_newPayloadV1 method via JSON-RPC.
@@ -319,6 +322,19 @@ func (s *Service) ExecutionBlockByHash(ctx context.Context, hash common.Hash, wi
return result, handleRPCError(err)
}
// GetBlobsBundle calls the engine_getBlobsV1 method via JSON-RPC.
func (s *Service) GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error) {
ctx, span := trace.StartSpan(ctx, "powchain.engine-api-client.GetBlobsBundle")
defer span.End()
d := time.Now().Add(defaultEngineTimeout)
ctx, cancel := context.WithDeadline(ctx, d)
defer cancel()
result := &pb.BlobsBundle{}
err := s.rpcClient.CallContext(ctx, result, GetBlobsBundleMethod, pb.PayloadIDBytes(payloadId))
return result, handleRPCError(err)
}
// ExecutionBlocksByHashes fetches a batch of execution engine blocks by hash by calling
// eth_blockByHash via JSON-RPC.
func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.Hash, withTxs bool) ([]*pb.ExecutionBlock, error) {

View File

@@ -34,6 +34,7 @@ type EngineClient struct {
TerminalBlockHash []byte
TerminalBlockHashExists bool
OverrideValidHash [32]byte
BlobsBundle *pb.BlobsBundle
}
// NewPayload --
@@ -158,3 +159,8 @@ func (e *EngineClient) GetTerminalBlockHash(ctx context.Context, transitionTime
blk = parentBlk
}
}
// GetBlobsBundle --
func (e *EngineClient) GetBlobsBundle(ctx context.Context, payloadId [8]byte) (*pb.BlobsBundle, error) {
return e.BlobsBundle, nil
}

View File

@@ -394,6 +394,18 @@ func (f *ForkChoice) SetOptimisticToValid(ctx context.Context, root [fieldparams
return node.setNodeAndParentValidated(ctx)
}
// SetValidData sets the node with the given root as a data available node
func (f *ForkChoice) SetValidData(ctx context.Context, root [fieldparams.RootLength]byte) error {
f.store.nodesLock.Lock()
defer f.store.nodesLock.Unlock()
node, ok := f.store.nodeByRoot[root]
if !ok || node == nil {
return errors.Wrap(ErrNilNode, "could not set node to valid data")
}
node.validData = true
return nil
}
// BestJustifiedCheckpoint of fork choice store.
func (f *ForkChoice) BestJustifiedCheckpoint() *forkchoicetypes.Checkpoint {
f.store.checkpointsLock.RLock()

View File

@@ -54,6 +54,7 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch, currentEpoch); err != nil {
return err
}
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, finalizedEpoch, currentEpoch)
if childLeadsToViableHead && !hasViableDescendant {
// The child leads to a viable head, but the current

View File

@@ -108,7 +108,7 @@ func (s *Store) insert(ctx context.Context,
slot types.Slot,
root, parentRoot, payloadHash [fieldparams.RootLength]byte,
justifiedEpoch, finalizedEpoch types.Epoch) (*Node, error) {
ctx, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
_, span := trace.StartSpan(ctx, "doublyLinkedForkchoice.insert")
defer span.End()
s.nodesLock.Lock()

View File

@@ -59,6 +59,7 @@ type Node struct {
weight uint64 // weight of this node: the total balance including children
bestDescendant *Node // bestDescendant node of this node.
optimistic bool // whether the block has been fully validated or not
validData bool // whether the block has valid data or not
timestamp uint64 // The timestamp when the node was inserted.
}

View File

@@ -72,6 +72,7 @@ type Getter interface {
type Setter interface {
SetOptimisticToValid(context.Context, [fieldparams.RootLength]byte) error
SetOptimisticToInvalid(context.Context, [fieldparams.RootLength]byte, [fieldparams.RootLength]byte, [fieldparams.RootLength]byte) ([][32]byte, error)
SetValidData(context.Context, [fieldparams.RootLength]byte) error
UpdateJustifiedCheckpoint(*forkchoicetypes.Checkpoint) error
UpdateFinalizedCheckpoint(*forkchoicetypes.Checkpoint) error
SetGenesisTime(uint64)

View File

@@ -242,6 +242,21 @@ func (f *ForkChoice) HasParent(root [32]byte) bool {
return f.store.nodes[i].parent != NonExistentNode
}
// SetValidData sets the node with the given root as a data available node
func (f *ForkChoice) SetValidData(ctx context.Context, root [32]byte) error {
f.store.nodesLock.RLock()
defer f.store.nodesLock.RUnlock()
i, ok := f.store.nodesIndices[root]
if !ok || i >= uint64(len(f.store.nodes)) {
return ErrUnknownNodeRoot
}
f.store.nodes[i].validData = true
return nil
}
// IsCanonical returns true if the given root is part of the canonical chain.
func (f *ForkChoice) IsCanonical(root [32]byte) bool {
f.store.nodesLock.RLock()
@@ -489,7 +504,7 @@ func (s *Store) insert(ctx context.Context,
slot types.Slot,
root, parent, payloadHash [32]byte,
justifiedEpoch, finalizedEpoch types.Epoch) (*Node, error) {
ctx, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
_, span := trace.StartSpan(ctx, "protoArrayForkChoice.insert")
defer span.End()
s.nodesLock.Lock()
@@ -704,6 +719,10 @@ func (s *Store) updateBestChildAndDescendant(parentIndex, childIndex uint64) err
if err != nil {
return err
}
// optimization: only run DA checks if viable
if childLeadsToViableHead {
childLeadsToViableHead = false
}
// Define 3 variables for the 3 outcomes mentioned above. This is to
// set `parent.bestChild` and `parent.bestDescendant` to. These
@@ -886,6 +905,7 @@ func (s *Store) viableForHead(node *Node) bool {
// It's also viable if we are in genesis epoch.
justified := s.justifiedCheckpoint.Epoch == node.justifiedEpoch || s.justifiedCheckpoint.Epoch == 0
finalized := s.finalizedCheckpoint.Epoch == node.finalizedEpoch || s.finalizedCheckpoint.Epoch == 0
if features.Get().EnableDefensivePull {
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(s.genesisTime), 0))
if !justified && s.justifiedCheckpoint.Epoch+1 == currentEpoch {
@@ -897,7 +917,7 @@ func (s *Store) viableForHead(node *Node) bool {
}
}
}
return justified && finalized
return justified && finalized && node.validData
}
// Tips returns all possible chain heads (leaves of fork choice tree).

View File

@@ -59,6 +59,7 @@ type Node struct {
bestChild uint64 // bestChild index of this node.
bestDescendant uint64 // bestDescendant of this node.
status status // optimistic status of this node
validData bool // whether the block has valid data or not.
}
// enum used as optimistic status of a node

View File

@@ -16,6 +16,7 @@ go_library(
],
deps = [
"//api/gateway:go_default_library",
"//async:go_default_library",
"//async/event:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/builder:go_default_library",

View File

@@ -14,10 +14,12 @@ import (
"strings"
"sync"
"syscall"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
apigateway "github.com/prysmaticlabs/prysm/v3/api/gateway"
"github.com/prysmaticlabs/prysm/v3/async"
"github.com/prysmaticlabs/prysm/v3/async/event"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/builder"
@@ -70,6 +72,8 @@ const testSkipPowFlag = "test-skip-pow"
// 128MB max message size when enabling debug endpoints.
const debugGrpcMaxMsgSize = 1 << 27
const blobsPruneInterval = time.Minute * 15
// Used as a struct to keep cli flag options for configuring services
// for the beacon node. We keep this as a separate struct to not pollute the actual BeaconNode
// struct, as it is merely used to pass down configuration options into the appropriate services.
@@ -440,6 +444,8 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
knownContract, addr.Bytes())
}
log.Infof("Deposit contract: %#x", addr.Bytes())
go b.pruneBlobs()
return nil
}
@@ -979,3 +985,12 @@ func (b *BeaconNode) registerBuilderService() error {
}
return b.services.RegisterService(svc)
}
func (b *BeaconNode) pruneBlobs() {
async.RunEvery(b.ctx, blobsPruneInterval, func() {
err := b.db.CleanupBlobs(b.ctx)
if err != nil {
log.WithError(err).Error("Unable to prune blobs sidecars in db")
}
})
}

View File

@@ -16,7 +16,7 @@ func (s *Service) forkWatcher() {
case currSlot := <-slotTicker.C():
currEpoch := slots.ToEpoch(currSlot)
if currEpoch == params.BeaconConfig().AltairForkEpoch ||
currEpoch == params.BeaconConfig().BellatrixForkEpoch {
currEpoch == params.BeaconConfig().BellatrixForkEpoch || currEpoch == params.BeaconConfig().Eip4844ForkEpoch {
// If we are in the fork epoch, we update our enr with
// the updated fork digest. These repeatedly does
// this over the epoch, which might be slightly wasteful
@@ -27,7 +27,7 @@ func (s *Service) forkWatcher() {
}
// from Bellatrix Epoch, the MaxGossipSize and the MaxChunkSize is changed to 10Mb.
if currEpoch == params.BeaconConfig().BellatrixForkEpoch {
if currEpoch == params.BeaconConfig().BellatrixForkEpoch || currEpoch == params.BeaconConfig().Eip4844ForkEpoch {
encoder.SetMaxGossipSizeForBellatrix()
encoder.SetMaxChunkSizeForBellatrix()
}

View File

@@ -116,6 +116,9 @@ func (s *Service) topicScoreParams(topic string) (*pubsub.TopicScoreParams, erro
return defaultProposerSlashingTopicParams(), nil
case strings.Contains(topic, GossipAttesterSlashingMessage):
return defaultAttesterSlashingTopicParams(), nil
case strings.Contains(topic, GossipBlobsMessage):
// TODO(EIP-4844): Using the block topic scoring for now. But this should be updated for blobs
return defaultBlockTopicParams(), nil
default:
return nil, errors.Errorf("unrecognized topic provided for parameter registration: %s", topic)
}

View File

@@ -20,12 +20,16 @@ var gossipTopicMappings = map[string]proto.Message{
AggregateAndProofSubnetTopicFormat: &ethpb.SignedAggregateAttestationAndProof{},
SyncContributionAndProofSubnetTopicFormat: &ethpb.SignedContributionAndProof{},
SyncCommitteeSubnetTopicFormat: &ethpb.SyncCommitteeMessage{},
BlobsSubnetTopicFormat: &ethpb.SignedBlobsSidecar{},
}
// GossipTopicMappings is a function to return the assigned data type
// versioned by epoch.
func GossipTopicMappings(topic string, epoch types.Epoch) proto.Message {
if topic == BlockSubnetTopicFormat {
if epoch >= params.BeaconConfig().Eip4844ForkEpoch {
return &ethpb.SignedBeaconBlockWithBlobKZGs{}
}
if epoch >= params.BeaconConfig().BellatrixForkEpoch {
return &ethpb.SignedBeaconBlockBellatrix{}
}
@@ -59,4 +63,6 @@ func init() {
// Specially handle Bellatrix objects.
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockBellatrix{})] = BlockSubnetTopicFormat
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBlindedBeaconBlockBellatrix{})] = BlockSubnetTopicFormat
// Specially handle EIP4844 objects.
GossipTypeMapping[reflect.TypeOf(&ethpb.SignedBeaconBlockWithBlobKZGs{})] = BlockSubnetTopicFormat
}

View File

@@ -78,7 +78,7 @@ func postAltairMsgID(pmsg *pubsubpb.Message, fEpoch types.Epoch) string {
// beyond Bellatrix epoch, allow 10 Mib gossip data size
gossipPubSubSize := params.BeaconNetworkConfig().GossipMaxSize
if fEpoch >= params.BeaconConfig().BellatrixForkEpoch {
if fEpoch >= params.BeaconConfig().BellatrixForkEpoch || fEpoch >= params.BeaconConfig().Eip4844ForkEpoch {
gossipPubSubSize = params.BeaconNetworkConfig().GossipMaxSizeBellatrix
}

View File

@@ -52,11 +52,17 @@ func (s *Service) CanSubscribe(topic string) bool {
log.WithError(err).Error("Could not determine Bellatrix fork digest")
return false
}
eip4844ForkDigest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().Eip4844ForkEpoch, s.genesisValidatorsRoot)
if err != nil {
log.WithError(err).Error("Could not determine eip4844 fork digest")
return false
}
switch parts[2] {
case fmt.Sprintf("%x", phase0ForkDigest):
case fmt.Sprintf("%x", altairForkDigest):
case fmt.Sprintf("%x", bellatrixForkDigest):
case fmt.Sprintf("%x", eip4844ForkDigest):
default:
return false
}

View File

@@ -37,6 +37,9 @@ const PingMessageName = "/ping"
// MetadataMessageName specifies the name for the metadata message topic.
const MetadataMessageName = "/metadata"
// BlobsSidecarsByRangeMessageName specifies the name for the blobs sidecars by range message topic.
const BlobsSidecarsByRangeMessageName = "/blobs_sidecars_by_range"
const (
// V1 RPC Topics
// RPCStatusTopicV1 defines the v1 topic for the status rpc method.
@@ -51,6 +54,8 @@ const (
RPCPingTopicV1 = protocolPrefix + PingMessageName + SchemaVersionV1
// RPCMetaDataTopicV1 defines the v1 topic for the metadata rpc method.
RPCMetaDataTopicV1 = protocolPrefix + MetadataMessageName + SchemaVersionV1
// RPCBlobsSidecarsByRangeTopicV1 defines the v1 topic for the blobs sidecars by range rpc method.
RPCBlobsSidecarsByRangeTopicV1 = protocolPrefix + BlobsSidecarsByRangeMessageName + SchemaVersionV1
// V2 RPC Topics
// RPCBlocksByRangeTopicV2 defines v2 the topic for the blocks by range rpc method.
@@ -83,6 +88,8 @@ var RPCTopicMappings = map[string]interface{}{
// RPC Metadata Message
RPCMetaDataTopicV1: new(interface{}),
RPCMetaDataTopicV2: new(interface{}),
// RPC Blobs Sidecars By Range Message
RPCBlobsSidecarsByRangeTopicV1: new(pb.BlobsSidecarsByRangeRequest),
}
// Maps all registered protocol prefixes.
@@ -93,12 +100,13 @@ var protocolMapping = map[string]bool{
// Maps all the protocol message names for the different rpc
// topics.
var messageMapping = map[string]bool{
StatusMessageName: true,
GoodbyeMessageName: true,
BeaconBlocksByRangeMessageName: true,
BeaconBlocksByRootsMessageName: true,
PingMessageName: true,
MetadataMessageName: true,
StatusMessageName: true,
GoodbyeMessageName: true,
BeaconBlocksByRangeMessageName: true,
BeaconBlocksByRootsMessageName: true,
PingMessageName: true,
MetadataMessageName: true,
BlobsSidecarsByRangeMessageName: true,
}
// Maps all the RPC messages which are to updated in altair.

View File

@@ -487,7 +487,7 @@ func (s *Service) isInitialized() bool {
func (s *Service) increaseMaxMessageSizesForBellatrix() {
currentSlot := slots.Since(s.genesisTime)
currentEpoch := slots.ToEpoch(currentSlot)
if currentEpoch >= params.BeaconConfig().BellatrixForkEpoch {
if currentEpoch >= params.BeaconConfig().BellatrixForkEpoch || currentEpoch >= params.BeaconConfig().Eip4844ForkEpoch {
encoder.SetMaxGossipSizeForBellatrix()
encoder.SetMaxChunkSizeForBellatrix()
}

View File

@@ -26,6 +26,8 @@ const (
GossipAggregateAndProofMessage = "beacon_aggregate_and_proof"
// GossipContributionAndProofMessage is the name for the sync contribution and proof message type.
GossipContributionAndProofMessage = "sync_committee_contribution_and_proof"
// GossipBlobsMessage is the name of the blobs message type.
GossipBlobsMessage = "blobs_sidecar"
// Topic Formats
//
@@ -45,4 +47,6 @@ const (
AggregateAndProofSubnetTopicFormat = GossipProtocolAndDigest + GossipAggregateAndProofMessage
// SyncContributionAndProofSubnetTopicFormat is the topic format for the sync aggregate and proof subnet.
SyncContributionAndProofSubnetTopicFormat = GossipProtocolAndDigest + GossipContributionAndProofMessage
// BlobsSubnetTopicFormat is the topic format for the blobs sidecar subnet.
BlobsSubnetTopicFormat = GossipProtocolAndDigest + GossipBlobsMessage
)

View File

@@ -48,6 +48,11 @@ func InitializeDataMaps() {
&ethpb.SignedBeaconBlockBellatrix{Block: &ethpb.BeaconBlockBellatrix{Body: &ethpb.BeaconBlockBodyBellatrix{}}},
)
},
bytesutil.ToBytes4(params.BeaconConfig().Eip4844ForkVersion): func() (interfaces.SignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockWithBlobKZGs{Block: &ethpb.BeaconBlockWithBlobKZGs{Body: &ethpb.BeaconBlockBodyWithBlobKZGs{}}},
)
},
}
// Reset our metadata map.
@@ -61,5 +66,8 @@ func InitializeDataMaps() {
bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): func() metadata.Metadata {
return wrapper.WrappedMetadataV1(&ethpb.MetaDataV1{})
},
bytesutil.ToBytes4(params.BeaconConfig().Eip4844ForkVersion): func() metadata.Metadata {
return wrapper.WrappedMetadataV1(&ethpb.MetaDataV1{})
},
}
}

View File

@@ -238,6 +238,16 @@ type bellatrixPublishBlindedBlockRequestJson struct {
Signature string `json:"signature" hex:"true"`
}
type eip4844PublishBlockRequestJson struct {
Eip4844Block *beaconBlockEip4844Json `json:"eip4844_block"`
Signature string `json:"signature" hex:"true"`
}
type eip4844PublishBlindedBlockRequestJson struct {
Eip4844Block *blindedBeaconBlockEip4844Json `json:"eip4844_block"`
Signature string `json:"signature" hex:"true"`
}
// setInitialPublishBlockPostRequest is triggered before we deserialize the request JSON into a struct.
// We don't know which version of the block got posted, but we can determine it from the slot.
// We know that blocks of all versions have a Message field with a Slot field,
@@ -269,8 +279,10 @@ func setInitialPublishBlockPostRequest(endpoint *apimiddleware.Endpoint,
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
} else if currentEpoch < params.BeaconConfig().BellatrixForkEpoch {
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
} else {
} else if currentEpoch < params.BeaconConfig().Eip4844ForkEpoch {
endpoint.PostRequest = &signedBeaconBlockBellatrixContainerJson{}
} else {
endpoint.PostRequest = &signedBeaconBlockEip4844ContainerJson{}
}
req.Body = io.NopCloser(bytes.NewBuffer(buf))
return true, nil
@@ -308,6 +320,15 @@ func preparePublishedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWrit
endpoint.PostRequest = actualPostReq
return nil
}
if block, ok := endpoint.PostRequest.(*signedBeaconBlockEip4844ContainerJson); ok {
// Prepare post request that can be properly decoded on gRPC side.
actualPostReq := &eip4844PublishBlockRequestJson{
Eip4844Block: block.Message,
Signature: block.Signature,
}
endpoint.PostRequest = actualPostReq
return nil
}
return apimiddleware.InternalServerError(errors.New("unsupported block type"))
}
@@ -342,8 +363,10 @@ func setInitialPublishBlindedBlockPostRequest(endpoint *apimiddleware.Endpoint,
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
} else if currentEpoch < params.BeaconConfig().BellatrixForkEpoch {
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
} else {
} else if currentEpoch < params.BeaconConfig().Eip4844ForkEpoch {
endpoint.PostRequest = &signedBlindedBeaconBlockBellatrixContainerJson{}
} else {
endpoint.PostRequest = &signedBlindedBeaconBlockEip4844ContainerJson{}
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
return true, nil
@@ -381,6 +404,15 @@ func preparePublishedBlindedBlock(endpoint *apimiddleware.Endpoint, _ http.Respo
endpoint.PostRequest = actualPostReq
return nil
}
if block, ok := endpoint.PostRequest.(*signedBlindedBeaconBlockEip4844ContainerJson); ok {
// Prepare post request that can be properly decoded on gRPC side.
actualPostReq := &eip4844PublishBlindedBlockRequestJson{
Eip4844Block: block.Message,
Signature: block.Signature,
}
endpoint.PostRequest = actualPostReq
return nil
}
return apimiddleware.InternalServerError(errors.New("unsupported block type"))
}
@@ -439,6 +471,11 @@ type bellatrixBlockResponseJson struct {
ExecutionOptimistic bool `json:"execution_optimistic"`
}
type eip4844BlockResponseJson struct {
Version string `json:"version"`
Data *signedBeaconBlockEip4844ContainerJson `json:"data"`
}
func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*blockV2ResponseJson)
if !ok {
@@ -474,6 +511,14 @@ func serializeV2Block(response interface{}) (apimiddleware.RunDefault, []byte, a
},
ExecutionOptimistic: respContainer.ExecutionOptimistic,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_EIP4844.String())):
actualRespContainer = &eip4844BlockResponseJson{
Version: respContainer.Version,
Data: &signedBeaconBlockEip4844ContainerJson{
Message: respContainer.Data.Eip4844Block,
Signature: respContainer.Data.Signature,
},
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}
@@ -500,6 +545,11 @@ type bellatrixStateResponseJson struct {
Data *beaconStateBellatrixJson `json:"data"`
}
type eip4844StateResponseJson struct {
Version string `json:"version"`
Data *beaconStateEip4844Json `json:"data"`
}
func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*beaconStateV2ResponseJson)
if !ok {
@@ -523,6 +573,11 @@ func serializeV2State(response interface{}) (apimiddleware.RunDefault, []byte, a
Version: respContainer.Version,
Data: respContainer.Data.BellatrixState,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_EIP4844.String())):
actualRespContainer = &eip4844StateResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.Eip4844State,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported state version '%s'", respContainer.Version))
}
@@ -554,6 +609,16 @@ type bellatrixProduceBlindedBlockResponseJson struct {
Data *blindedBeaconBlockBellatrixJson `json:"data"`
}
type eip4844ProduceBlockResponseJson struct {
Version string `json:"version"`
Data *beaconBlockEip4844Json `json:"data"`
}
type eip4844ProduceBlindedBlockResponseJson struct {
Version string `json:"version"`
Data *blindedBeaconBlockEip4844Json `json:"data"`
}
func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, []byte, apimiddleware.ErrorJson) {
respContainer, ok := response.(*produceBlockResponseV2Json)
if !ok {
@@ -577,6 +642,11 @@ func serializeProducedV2Block(response interface{}) (apimiddleware.RunDefault, [
Version: respContainer.Version,
Data: respContainer.Data.BellatrixBlock,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_EIP4844.String())):
actualRespContainer = &eip4844ProduceBlockResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.Eip4844Block,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}
@@ -611,6 +681,11 @@ func serializeProducedBlindedBlock(response interface{}) (apimiddleware.RunDefau
Version: respContainer.Version,
Data: respContainer.Data.BellatrixBlock,
}
case strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_EIP4844.String())):
actualRespContainer = &eip4844ProduceBlindedBlockResponseJson{
Version: respContainer.Version,
Data: respContainer.Data.Eip4844Block,
}
default:
return false, nil, apimiddleware.InternalServerError(fmt.Errorf("unsupported block version '%s'", respContainer.Version))
}

View File

@@ -330,6 +330,7 @@ type signedBeaconBlockContainerV2Json struct {
Phase0Block *beaconBlockJson `json:"phase0_block"`
AltairBlock *beaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *beaconBlockBellatrixJson `json:"bellatrix_block"`
Eip4844Block *beaconBlockEip4844Json `json:"eip4844_block"`
Signature string `json:"signature" hex:"true"`
}
@@ -337,12 +338,14 @@ type beaconBlockContainerV2Json struct {
Phase0Block *beaconBlockJson `json:"phase0_block"`
AltairBlock *beaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *beaconBlockBellatrixJson `json:"bellatrix_block"`
Eip4844Block *beaconBlockEip4844Json `json:"eip4844_block"`
}
type blindedBeaconBlockContainerJson struct {
Phase0Block *beaconBlockJson `json:"phase0_block"`
AltairBlock *beaconBlockAltairJson `json:"altair_block"`
BellatrixBlock *blindedBeaconBlockBellatrixJson `json:"bellatrix_block"`
Eip4844Block *blindedBeaconBlockEip4844Json `json:"eip4844_block"`
}
type signedBeaconBlockAltairContainerJson struct {
@@ -360,6 +363,16 @@ type signedBlindedBeaconBlockBellatrixContainerJson struct {
Signature string `json:"signature" hex:"true"`
}
type signedBeaconBlockEip4844ContainerJson struct {
Message *beaconBlockEip4844Json `json:"message"`
Signature string `json:"signature" hex:"true"`
}
type signedBlindedBeaconBlockEip4844ContainerJson struct {
Message *blindedBeaconBlockEip4844Json `json:"message"`
Signature string `json:"signature" hex:"true"`
}
type beaconBlockAltairJson struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
@@ -384,6 +397,22 @@ type blindedBeaconBlockBellatrixJson struct {
Body *blindedBeaconBlockBodyBellatrixJson `json:"body"`
}
type beaconBlockEip4844Json struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot string `json:"parent_root" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
Body *beaconBlockBodyEip4844Json `json:"body"`
}
type blindedBeaconBlockEip4844Json struct {
Slot string `json:"slot"`
ProposerIndex string `json:"proposer_index"`
ParentRoot string `json:"parent_root" hex:"true"`
StateRoot string `json:"state_root" hex:"true"`
Body *blindedBeaconBlockBodyEip4844Json `json:"body"`
}
type beaconBlockBodyAltairJson struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *eth1DataJson `json:"eth1_data"`
@@ -422,6 +451,34 @@ type blindedBeaconBlockBodyBellatrixJson struct {
ExecutionPayloadHeader *executionPayloadHeaderJson `json:"execution_payload_header"`
}
type beaconBlockBodyEip4844Json struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *eth1DataJson `json:"eth1_data"`
Graffiti string `json:"graffiti" hex:"true"`
ProposerSlashings []*proposerSlashingJson `json:"proposer_slashings"`
AttesterSlashings []*attesterSlashingJson `json:"attester_slashings"`
Attestations []*attestationJson `json:"attestations"`
Deposits []*depositJson `json:"deposits"`
VoluntaryExits []*signedVoluntaryExitJson `json:"voluntary_exits"`
SyncAggregate *syncAggregateJson `json:"sync_aggregate"`
ExecutionPayload *executionPayloadJson `json:"execution_payload"`
BlobKzgs []string `json:"blob_kzgs" hex:"true"`
}
type blindedBeaconBlockBodyEip4844Json struct {
RandaoReveal string `json:"randao_reveal" hex:"true"`
Eth1Data *eth1DataJson `json:"eth1_data"`
Graffiti string `json:"graffiti" hex:"true"`
ProposerSlashings []*proposerSlashingJson `json:"proposer_slashings"`
AttesterSlashings []*attesterSlashingJson `json:"attester_slashings"`
Attestations []*attestationJson `json:"attestations"`
Deposits []*depositJson `json:"deposits"`
VoluntaryExits []*signedVoluntaryExitJson `json:"voluntary_exits"`
SyncAggregate *syncAggregateJson `json:"sync_aggregate"`
ExecutionPayloadHeader *executionPayloadHeaderJson `json:"execution_payload_header"`
BlobKzgs []string `json:"blob_kzgs" hex:"true"`
}
type executionPayloadJson struct {
ParentHash string `json:"parent_hash" hex:"true"`
FeeRecipient string `json:"fee_recipient" hex:"true"`
@@ -659,10 +716,39 @@ type beaconStateBellatrixJson struct {
LatestExecutionPayloadHeader *executionPayloadHeaderJson `json:"latest_execution_payload_header"`
}
type beaconStateEip4844Json struct {
GenesisTime string `json:"genesis_time"`
GenesisValidatorsRoot string `json:"genesis_validators_root" hex:"true"`
Slot string `json:"slot"`
Fork *forkJson `json:"fork"`
LatestBlockHeader *beaconBlockHeaderJson `json:"latest_block_header"`
BlockRoots []string `json:"block_roots" hex:"true"`
StateRoots []string `json:"state_roots" hex:"true"`
HistoricalRoots []string `json:"historical_roots" hex:"true"`
Eth1Data *eth1DataJson `json:"eth1_data"`
Eth1DataVotes []*eth1DataJson `json:"eth1_data_votes"`
Eth1DepositIndex string `json:"eth1_deposit_index"`
Validators []*validatorJson `json:"validators"`
Balances []string `json:"balances"`
RandaoMixes []string `json:"randao_mixes" hex:"true"`
Slashings []string `json:"slashings"`
PreviousEpochParticipation EpochParticipation `json:"previous_epoch_participation"`
CurrentEpochParticipation EpochParticipation `json:"current_epoch_participation"`
JustificationBits string `json:"justification_bits" hex:"true"`
PreviousJustifiedCheckpoint *checkpointJson `json:"previous_justified_checkpoint"`
CurrentJustifiedCheckpoint *checkpointJson `json:"current_justified_checkpoint"`
FinalizedCheckpoint *checkpointJson `json:"finalized_checkpoint"`
InactivityScores []string `json:"inactivity_scores"`
CurrentSyncCommittee *syncCommitteeJson `json:"current_sync_committee"`
NextSyncCommittee *syncCommitteeJson `json:"next_sync_committee"`
LatestExecutionPayloadHeader *executionPayloadHeaderJson `json:"latest_execution_payload_header"`
}
type beaconStateContainerV2Json struct {
Phase0State *beaconStateJson `json:"phase0_state"`
AltairState *beaconStateAltairJson `json:"altair_state"`
BellatrixState *beaconStateBellatrixJson `json:"bellatrix_state"`
Eip4844State *beaconStateEip4844Json `json:"eip4844_state"`
}
type forkJson struct {

View File

@@ -230,6 +230,7 @@ func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBloc
return nil, err
}
}
// TODO(EIP-4844): submitEip4844Block
return &emptypb.Empty{}, nil
}
@@ -269,7 +270,8 @@ func (bs *Server) SubmitBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer)
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err)
}
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block)
// TODO(EIP-4844): Unmarshal Sidecar in Request
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block, nil)
}
// SubmitBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
@@ -360,7 +362,8 @@ func (bs *Server) SubmitBlindedBlockSSZ(ctx context.Context, req *ethpbv2.SSZCon
if err != nil {
return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err)
}
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block)
// TODO(EIP-4844): Unmarshal sidecar in request
return &emptypb.Empty{}, bs.submitBlock(ctx, root, block, nil)
}
// GetBlock retrieves block details for given block ID.
@@ -530,6 +533,34 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) (
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
eip4844Blk, err := blk.PbEip4844Block()
if err == nil {
if eip4844Blk == nil {
return nil, status.Errorf(codes.Internal, "Nil block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockEip4844ToV2(eip4844Blk.Block)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
root, err := blk.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err)
}
isOptimistic, err := bs.OptimisticModeFetcher.IsOptimisticForRoot(ctx, root)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not check if block is optimistic: %v", err)
}
sig := blk.Signature()
return &ethpbv2.BlockResponseV2{
Version: ethpbv2.Version_EIP4844,
Data: &ethpbv2.SignedBeaconBlockContainer{
Message: &ethpbv2.SignedBeaconBlockContainer_Eip4844Block{Eip4844Block: v2Blk},
Signature: sig[:],
},
ExecutionOptimistic: isOptimistic,
}, nil
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
@@ -643,6 +674,27 @@ func (bs *Server) GetBlockSSZV2(ctx context.Context, req *ethpbv2.BlockRequestV2
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
eip4844Blk, err := blk.PbEip4844Block()
if err == nil {
if eip4844Blk == nil {
return nil, status.Errorf(codes.Internal, "Nil block")
}
v2Blk, err := migration.V1Alpha1BeaconBlockEip4844ToV2(eip4844Blk.Block)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err)
}
sig := blk.Signature()
data := &ethpbv2.SignedBeaconBlockEip4844{
Message: v2Blk,
Signature: sig[:],
}
sszData, err := data.MarshalSSZ()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ: %v", err)
}
return &ethpbv2.SSZContainer{Version: ethpbv2.Version_EIP4844, Data: sszData}, nil
}
return nil, status.Errorf(codes.Internal, "Unknown block type %T", blk)
}
@@ -849,7 +901,7 @@ func (bs *Server) submitPhase0Block(ctx context.Context, phase0Blk *ethpbv1.Beac
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
return bs.submitBlock(ctx, root, wrappedPhase0Blk)
return bs.submitBlock(ctx, root, wrappedPhase0Blk, nil)
}
func (bs *Server) submitAltairBlock(ctx context.Context, altairBlk *ethpbv2.BeaconBlockAltair, sig []byte) error {
@@ -867,7 +919,7 @@ func (bs *Server) submitAltairBlock(ctx context.Context, altairBlk *ethpbv2.Beac
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
return bs.submitBlock(ctx, root, wrappedAltairBlk)
return bs.submitBlock(ctx, root, wrappedAltairBlk, nil)
}
func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv2.BeaconBlockBellatrix, sig []byte) error {
@@ -885,7 +937,7 @@ func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}
return bs.submitBlock(ctx, root, wrappedBellatrixBlk)
return bs.submitBlock(ctx, root, wrappedBellatrixBlk, nil)
}
func (bs *Server) submitBlindedBellatrixBlock(ctx context.Context, blindedBellatrixBlk *ethpbv2.BlindedBeaconBlockBellatrix, sig []byte) error {
@@ -904,10 +956,11 @@ func (bs *Server) submitBlindedBellatrixBlock(ctx context.Context, blindedBellat
if err != nil {
return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err)
}
return nil
}
func (bs *Server) submitBlock(ctx context.Context, blockRoot [fieldparams.RootLength]byte, block interfaces.SignedBeaconBlock) error {
func (bs *Server) submitBlock(ctx context.Context, blockRoot [fieldparams.RootLength]byte, block interfaces.SignedBeaconBlock, sidecar *eth.BlobsSidecar) error {
// Do not block proposal critical path with debug logging or block feed updates.
defer func() {
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(blockRoot[:]))).Debugf(
@@ -927,6 +980,11 @@ func (bs *Server) submitBlock(ctx context.Context, blockRoot [fieldparams.RootLe
return status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}
if sidecar != nil {
if err := block.SetSideCar(&eth.SignedBlobsSidecar{Message: sidecar}); err != nil {
return err
}
}
if err := bs.BlockReceiver.ReceiveBlock(ctx, block, blockRoot); err != nil {
return status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}

View File

@@ -71,13 +71,17 @@ func (ds *Server) GetBeaconStateV2(ctx context.Context, req *ethpbv2.BeaconState
},
ExecutionOptimistic: isOptimistic,
}, nil
case version.Bellatrix:
case version.Bellatrix, version.EIP4844:
protoState, err := migration.BeaconStateBellatrixToProto(beaconSt)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not convert state to proto: %v", err)
}
v := ethpbv2.Version_BELLATRIX
if beaconSt.Version() == version.EIP4844 {
v = ethpbv2.Version_EIP4844
}
return &ethpbv2.BeaconStateResponseV2{
Version: ethpbv2.Version_BELLATRIX,
Version: v,
Data: &ethpbv2.BeaconStateContainer{
State: &ethpbv2.BeaconStateContainer_BellatrixState{BellatrixState: protoState},
},
@@ -110,6 +114,8 @@ func (ds *Server) GetBeaconStateSSZV2(ctx context.Context, req *ethpbv2.BeaconSt
ver = ethpbv2.Version_ALTAIR
case version.Bellatrix:
ver = ethpbv2.Version_BELLATRIX
case version.EIP4844:
ver = ethpbv2.Version_EIP4844
default:
return nil, status.Error(codes.Internal, "Unsupported state version")
}

View File

@@ -335,6 +335,19 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
},
}, nil
}
eip4844Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Eip4844)
if ok {
block, err := migration.V1Alpha1BeaconBlockEip4844ToV2(eip4844Block.Eip4844)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
return &ethpbv2.ProduceBlockResponseV2{
Version: ethpbv2.Version_EIP4844,
Data: &ethpbv2.BeaconBlockContainerV2{
Block: &ethpbv2.BeaconBlockContainerV2_Eip4844Block{Eip4844Block: block},
},
}, nil
}
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
}
@@ -412,6 +425,21 @@ func (vs *Server) ProduceBlockV2SSZ(ctx context.Context, req *ethpbv1.ProduceBlo
Data: sszBlock,
}, nil
}
eip4844Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Eip4844)
if ok {
block, err := migration.V1Alpha1BeaconBlockEip4844ToV2(eip4844Block.Eip4844)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
sszBlock, err := block.MarshalSSZ()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ format: %v", err)
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_EIP4844,
Data: sszBlock,
}, nil
}
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
}
@@ -573,6 +601,21 @@ func (vs *Server) ProduceBlindedBlockSSZ(ctx context.Context, req *ethpbv1.Produ
Data: sszBlock,
}, nil
}
eip4844Block, ok := v1alpha1resp.Block.(*ethpbalpha.GenericBeaconBlock_Eip4844)
if ok {
block, err := migration.V1Alpha1BeaconBlockEip4844ToV2Blinded(eip4844Block.Eip4844)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not prepare beacon block: %v", err)
}
sszBlock, err := block.MarshalSSZ()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not marshal block into SSZ format: %v", err)
}
return &ethpbv2.SSZContainer{
Version: ethpbv2.Version_EIP4844,
Data: sszBlock,
}, nil
}
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
}

View File

@@ -528,7 +528,7 @@ func (bs *Server) GetValidatorParticipation(
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not pre compute attestations: %v", err)
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
v, b, err = altair.InitializePrecomputeValidators(ctx, beaconState)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not set up altair pre compute instance: %v", err)
@@ -693,7 +693,7 @@ func (bs *Server) GetValidatorPerformance(
return nil, err
}
validatorSummary = vp
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
vp, bp, err := altair.InitializePrecomputeValidators(ctx, headState)
if err != nil {
return nil, err
@@ -865,7 +865,7 @@ func (bs *Server) GetIndividualVotes(
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not pre compute attestations: %v", err)
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
v, bal, err = altair.InitializePrecomputeValidators(ctx, st)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not set up altair pre compute instance: %v", err)

View File

@@ -14,6 +14,7 @@ go_library(
"proposer_attestations.go",
"proposer_bellatrix.go",
"proposer_deposits.go",
"proposer_eip4844.go",
"proposer_eth1data.go",
"proposer_execution_payload.go",
"proposer_phase0.go",

View File

@@ -53,14 +53,24 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (
return nil, status.Errorf(codes.Internal, "Could not fetch Altair beacon block: %v", err)
}
return &ethpb.GenericBeaconBlock{Block: &ethpb.GenericBeaconBlock_Altair{Altair: blk}}, nil
} else if slots.ToEpoch(req.Slot) < params.BeaconConfig().Eip4844ForkEpoch {
// An optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain).
if err := vs.optimisticStatus(ctx); err != nil {
return nil, err
}
return vs.getBellatrixBeaconBlock(ctx, req)
}
// An optimistic validator MUST NOT produce a block (i.e., sign across the DOMAIN_BEACON_PROPOSER domain).
if err := vs.optimisticStatus(ctx); err != nil {
return nil, err
}
return vs.getBellatrixBeaconBlock(ctx, req)
blk, sideCar, err := vs.getEip4844BeaconBlock(ctx, req)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not fetch eip4844 beacon block: %v", err)
}
return &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Eip4844{Eip4844: blk},
Sidecar: sideCar,
}, nil
}
// ProposeBeaconBlock is called by a proposer during its assigned slot to create a block in an attempt
@@ -72,7 +82,7 @@ func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSign
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not decode block: %v", err)
}
return vs.proposeGenericBeaconBlock(ctx, blk)
return vs.proposeGenericBeaconBlock(ctx, blk, req.Sidecar)
}
// PrepareBeaconProposer caches and updates the fee recipient for the given proposer.
@@ -155,7 +165,7 @@ func (vs *Server) GetFeeRecipientByPubKey(ctx context.Context, request *ethpb.Fe
}, nil
}
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.SignedBeaconBlock) (*ethpb.ProposeResponse, error) {
func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.SignedBeaconBlock, sidecar *ethpb.SignedBlobsSidecar) (*ethpb.ProposeResponse, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.proposeGenericBeaconBlock")
defer span.End()
root, err := blk.Block().HashTreeRoot()
@@ -186,6 +196,14 @@ func (vs *Server) proposeGenericBeaconBlock(ctx context.Context, blk interfaces.
if err := vs.P2P.Broadcast(ctx, blkPb); err != nil {
return nil, fmt.Errorf("could not broadcast block: %v", err)
}
if sidecar != nil {
if err := vs.P2P.Broadcast(ctx, sidecar); err != nil {
log.WithError(err).Error("Could not broadcast sidecar")
}
if err := blk.SetSideCar(sidecar); err != nil {
return nil, err
}
}
log.WithFields(logrus.Fields{
"blockRoot": hex.EncodeToString(root[:]),
}).Debug("Broadcasting block")

View File

@@ -88,7 +88,7 @@ func (a proposerAtts) filter(ctx context.Context, st state.BeaconState) (propose
switch st.Version() {
case version.Phase0:
attestationProcessor = blocks.ProcessAttestationNoVerifySignature
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
// Use a wrapper here, as go needs strong typing for the function signature.
attestationProcessor = func(ctx context.Context, st state.BeaconState, attestation *ethpb.Attestation) (state.BeaconState, error) {
totalBalance, err := helpers.TotalActiveBalance(st)

View File

@@ -26,6 +26,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/runtime/version"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
// builderGetPayloadMissCount tracks the number of misses when validator tries to get a payload from builder
@@ -38,10 +39,12 @@ var builderGetPayloadMissCount = promauto.NewCounter(prometheus.CounterOpts{
// block request. This value is known as `BUILDER_PROPOSAL_DELAY_TOLERANCE` in builder spec.
const blockBuilderTimeout = 1 * time.Second
func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.GenericBeaconBlock, error) {
func (vs *Server) buildBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlockBellatrix, enginev1.PayloadIDBytes, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.buildBellatrixBeaconBlock")
defer span.End()
altairBlk, err := vs.BuildAltairBeaconBlock(ctx, req)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
if !req.SkipMevBoost {
@@ -54,7 +57,7 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
"back to local execution client")
builderGetPayloadMissCount.Inc()
} else if builderReady {
return b, nil
return b.GetBellatrix(), enginev1.PayloadIDBytes{}, nil
}
} else if err != nil {
log.WithError(err).WithFields(logrus.Fields{
@@ -63,12 +66,12 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
}).Error("Could not determine validator has registered. Defaulting to local execution client")
}
}
payload, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex, bytesutil.ToBytes32(altairBlk.ParentRoot))
if err != nil {
return nil, err
}
blk := &ethpb.BeaconBlockBellatrix{
payload, payloadID, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex, bytesutil.ToBytes32(altairBlk.ParentRoot))
if err != nil {
return nil, enginev1.PayloadIDBytes{}, err
}
return &ethpb.BeaconBlockBellatrix{
Slot: altairBlk.Slot,
ProposerIndex: altairBlk.ProposerIndex,
ParentRoot: altairBlk.ParentRoot,
@@ -85,6 +88,15 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
SyncAggregate: altairBlk.Body.SyncAggregate,
ExecutionPayload: payload,
},
}, payloadID, nil
}
func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.GenericBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "ProposerServer.getBellatrixBeaconBlock")
defer span.End()
blk, _, err := vs.buildBellatrixBeaconBlock(ctx, req)
if err != nil {
return nil, fmt.Errorf("could not build block data: %v", err)
}
// Compute state root with the newly constructed block.
wsb, err := consensusblocks.NewSignedBeaconBlock(

View File

@@ -0,0 +1,92 @@
package validator
import (
"bytes"
"context"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition/interop"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
)
func (vs *Server) getEip4844BeaconBlock(ctx context.Context, req *ethpb.BlockRequest) (*ethpb.BeaconBlockWithBlobKZGs, *ethpb.BlobsSidecar, error) {
bellatrixBlk, payloadID, err := vs.buildBellatrixBeaconBlock(ctx, req)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get bellatrix block")
}
blobsBundle, err := vs.ExecutionEngineCaller.GetBlobsBundle(ctx, payloadID)
if err != nil {
return nil, nil, errors.Wrap(err, "could not get blobs")
}
// sanity check the blobs bundle
if bytes.Compare(blobsBundle.BlockHash, bellatrixBlk.Body.ExecutionPayload.BlockHash) != 0 {
return nil, nil, errors.New("invalid blobs bundle received")
}
if len(blobsBundle.Blobs) != len(blobsBundle.Kzgs) {
return nil, nil, errors.New("mismatched blobs and kzgs length")
}
var (
kzgs [][]byte
blobs []*enginev1.Blob
)
if len(blobsBundle.Kzgs) != 0 {
kzgs = blobsBundle.Kzgs
blobs = blobsBundle.Blobs
}
blk := &ethpb.BeaconBlockWithBlobKZGs{
Slot: bellatrixBlk.Slot,
ProposerIndex: bellatrixBlk.ProposerIndex,
ParentRoot: bellatrixBlk.ParentRoot,
StateRoot: params.BeaconConfig().ZeroHash[:],
Body: &ethpb.BeaconBlockBodyWithBlobKZGs{
RandaoReveal: bellatrixBlk.Body.RandaoReveal,
Eth1Data: bellatrixBlk.Body.Eth1Data,
Graffiti: bellatrixBlk.Body.Graffiti,
ProposerSlashings: bellatrixBlk.Body.ProposerSlashings,
AttesterSlashings: bellatrixBlk.Body.AttesterSlashings,
Attestations: bellatrixBlk.Body.Attestations,
Deposits: bellatrixBlk.Body.Deposits,
VoluntaryExits: bellatrixBlk.Body.VoluntaryExits,
SyncAggregate: bellatrixBlk.Body.SyncAggregate,
ExecutionPayload: bellatrixBlk.Body.ExecutionPayload,
BlobKzgs: kzgs,
},
}
// Compute state root with the newly constructed block.
wsb, err := blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockWithBlobKZGs{
Block: blk,
Signature: make([]byte, 96),
},
)
if err != nil {
return nil, nil, err
}
stateRoot, err := vs.computeStateRoot(ctx, wsb)
if err != nil {
interop.WriteBlockToDisk(wsb, true /*failed*/)
return nil, nil, fmt.Errorf("could not compute state root: %v", err)
}
blk.StateRoot = stateRoot
r, err := blk.HashTreeRoot()
if err != nil {
return nil, nil, err
}
var sideCar *ethpb.BlobsSidecar
if len(blobs) != 0 {
sideCar = &ethpb.BlobsSidecar{
BeaconBlockRoot: r[:],
BeaconBlockSlot: blk.Slot,
Blobs: blobs,
AggregatedProof: blobsBundle.AggregatedProof,
}
}
return blk, sideCar, nil
}

View File

@@ -40,7 +40,7 @@ var (
// This returns the execution payload of a given slot. The function has full awareness of pre and post merge.
// The payload is computed given the respected time of merge.
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx types.ValidatorIndex, headRoot [32]byte) (*enginev1.ExecutionPayload, error) {
func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx types.ValidatorIndex, headRoot [32]byte) (*enginev1.ExecutionPayload, enginev1.PayloadIDBytes, error) {
proposerID, payloadId, ok := vs.ProposerSlotIndexCache.GetProposerPayloadIDs(slot, headRoot)
feeRecipient := params.BeaconConfig().DefaultFeeRecipient
recipient, err := vs.BeaconDB.FeeRecipientByValidatorID(ctx, vIdx)
@@ -60,7 +60,7 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
"Please refer to our documentation for instructions")
}
default:
return nil, errors.Wrap(err, "could not get fee recipient in db")
return nil, enginev1.PayloadIDBytes{}, errors.Wrap(err, "could not get fee recipient in db")
}
if ok && proposerID == vIdx && payloadId != [8]byte{} { // Payload ID is cache hit. Return the cached payload ID.
@@ -71,78 +71,78 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
switch {
case err == nil:
warnIfFeeRecipientDiffers(payload, feeRecipient)
return payload, nil
return payload, payloadId, err
case errors.Is(err, context.DeadlineExceeded):
default:
return nil, errors.Wrap(err, "could not get cached payload from execution client")
return nil, enginev1.PayloadIDBytes{}, errors.Wrap(err, "could not get cached payload from execution client")
}
}
st, err := vs.HeadFetcher.HeadState(ctx)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
st, err = transition.ProcessSlotsIfPossible(ctx, st, slot)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
var parentHash []byte
var hasTerminalBlock bool
mergeComplete, err := blocks.IsMergeTransitionComplete(st)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
t, err := slots.ToTime(st.GenesisTime(), slot)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
if mergeComplete {
header, err := st.LatestExecutionPayloadHeader()
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
parentHash = header.BlockHash
} else {
if activationEpochNotReached(slot) {
return emptyPayload(), nil
return emptyPayload(), enginev1.PayloadIDBytes{}, nil
}
parentHash, hasTerminalBlock, err = vs.getTerminalBlockHashIfExists(ctx, uint64(t.Unix()))
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
if !hasTerminalBlock {
return emptyPayload(), nil
return emptyPayload(), enginev1.PayloadIDBytes{}, nil
}
}
payloadIDCacheMiss.Inc()
random, err := helpers.RandaoMix(st, time.CurrentEpoch(st))
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
finalizedBlockHash := params.BeaconConfig().ZeroHash[:]
finalizedRoot := bytesutil.ToBytes32(st.FinalizedCheckpoint().Root)
if finalizedRoot != [32]byte{} { // finalized root could be zeros before the first finalized block.
finalizedBlock, err := vs.BeaconDB.Block(ctx, bytesutil.ToBytes32(st.FinalizedCheckpoint().Root))
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
if err := consensusblocks.BeaconBlockIsNil(finalizedBlock); err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
switch finalizedBlock.Version() {
case version.Phase0, version.Altair: // Blocks before Bellatrix don't have execution payloads. Use zeros as the hash.
default:
finalizedPayload, err := finalizedBlock.Block().Body().Execution()
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
finalizedBlockHash = finalizedPayload.BlockHash()
}
}
f := &enginev1.ForkchoiceState{
HeadBlockHash: parentHash,
SafeBlockHash: parentHash,
@@ -156,17 +156,17 @@ func (vs *Server) getExecutionPayload(ctx context.Context, slot types.Slot, vIdx
}
payloadID, _, err := vs.ExecutionEngineCaller.ForkchoiceUpdated(ctx, f, p)
if err != nil {
return nil, errors.Wrap(err, "could not prepare payload")
return nil, enginev1.PayloadIDBytes{}, errors.Wrap(err, "could not prepare payload")
}
if payloadID == nil {
return nil, fmt.Errorf("nil payload with block hash: %#x", parentHash)
return nil, enginev1.PayloadIDBytes{}, fmt.Errorf("nil payload with block hash: %#x", parentHash)
}
payload, err := vs.ExecutionEngineCaller.GetPayload(ctx, *payloadID)
if err != nil {
return nil, err
return nil, enginev1.PayloadIDBytes{}, err
}
warnIfFeeRecipientDiffers(payload, feeRecipient)
return payload, nil
return payload, enginev1.PayloadIDBytes{}, nil
}
// warnIfFeeRecipientDiffers logs a warning if the fee recipient in the included payload does not

View File

@@ -124,7 +124,7 @@ func TestServer_getExecutionPayload(t *testing.T) {
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(tt.st.Slot(), 100, [8]byte{100}, [32]byte{'a'})
_, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx, [32]byte{'a'})
_, _, err := vs.getExecutionPayload(context.Background(), tt.st.Slot(), tt.validatorIndx, [32]byte{'a'})
if tt.errString != "" {
require.ErrorContains(t, tt.errString, err)
} else {
@@ -160,7 +160,7 @@ func TestServer_getExecutionPayloadContextTimeout(t *testing.T) {
}
vs.ProposerSlotIndexCache.SetProposerAndPayloadIDs(nonTransitionSt.Slot(), 100, [8]byte{100}, [32]byte{'a'})
_, err = vs.getExecutionPayload(context.Background(), nonTransitionSt.Slot(), 100, [32]byte{'a'})
_, _, err = vs.getExecutionPayload(context.Background(), nonTransitionSt.Slot(), 100, [32]byte{'a'})
require.NoError(t, err)
}
@@ -205,7 +205,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
BeaconDB: beaconDB,
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
}
gotPayload, err := vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{})
gotPayload, _, err := vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{})
require.NoError(t, err)
require.NotNil(t, gotPayload)
@@ -217,7 +217,7 @@ func TestServer_getExecutionPayload_UnexpectedFeeRecipient(t *testing.T) {
payload.FeeRecipient = evilRecipientAddress[:]
vs.ProposerSlotIndexCache = cache.NewProposerPayloadIDsCache()
gotPayload, err = vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{})
gotPayload, _, err = vs.getExecutionPayload(context.Background(), transitionSt.Slot(), 0, [32]byte{})
require.NoError(t, err)
require.NotNil(t, gotPayload)

View File

@@ -7,7 +7,7 @@ import (
func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
switch b.version {
case version.Bellatrix:
case version.EIP4844, version.Bellatrix:
return params.BeaconConfig().ProportionalSlashingMultiplierBellatrix, nil
case version.Altair:
return params.BeaconConfig().ProportionalSlashingMultiplierAltair, nil
@@ -19,7 +19,7 @@ func (b *BeaconState) ProportionalSlashingMultiplier() (uint64, error) {
func (b *BeaconState) InactivityPenaltyQuotient() (uint64, error) {
switch b.version {
case version.Bellatrix:
case version.EIP4844, version.Bellatrix:
return params.BeaconConfig().InactivityPenaltyQuotientBellatrix, nil
case version.Altair:
return params.BeaconConfig().InactivityPenaltyQuotientAltair, nil

View File

@@ -203,7 +203,7 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot types
tracing.AnnotateError(span, err)
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
case version.Altair, version.Bellatrix:
case version.Altair, version.Bellatrix, version.EIP4844:
state, err = altair.ProcessEpoch(ctx, state)
if err != nil {
tracing.AnnotateError(span, err)
@@ -233,6 +233,14 @@ func ReplayProcessSlots(ctx context.Context, state state.BeaconState, slot types
return nil, err
}
}
if prysmtime.CanUpgradeToEip4844(state.Slot()) {
state, err = execution.UpgradeToEip4844(state)
if err != nil {
tracing.AnnotateError(span, err)
return nil, err
}
}
}
return state, nil

View File

@@ -67,12 +67,12 @@ func (f FieldIndex) String(stateVersion int) string {
case Slashings:
return "slashings"
case PreviousEpochAttestations:
if version.Altair == stateVersion || version.Bellatrix == stateVersion {
if version.Altair == stateVersion || version.Bellatrix == stateVersion || version.EIP4844 == stateVersion {
return "previousEpochParticipationBits"
}
return "previousEpochAttestations"
case CurrentEpochAttestations:
if version.Altair == stateVersion || version.Bellatrix == stateVersion {
if version.Altair == stateVersion || version.Bellatrix == stateVersion || version.EIP4844 == stateVersion {
return "currentEpochParticipationBits"
}
return "currentEpochAttestations"

View File

@@ -16,10 +16,12 @@ go_library(
"options.go",
"pending_attestations_queue.go",
"pending_blocks_queue.go",
"pending_sidecars_queue.go",
"rate_limiter.go",
"rpc.go",
"rpc_beacon_blocks_by_range.go",
"rpc_beacon_blocks_by_root.go",
"rpc_blobs_sidecars_by_range.go",
"rpc_chunked_response.go",
"rpc_goodbye.go",
"rpc_metadata.go",
@@ -31,6 +33,7 @@ go_library(
"subscriber_beacon_aggregate_proof.go",
"subscriber_beacon_attestation.go",
"subscriber_beacon_blocks.go",
"subscriber_blobs_sidecar.go",
"subscriber_handlers.go",
"subscriber_sync_committee_message.go",
"subscriber_sync_contribution_proof.go",
@@ -40,6 +43,7 @@ go_library(
"validate_attester_slashing.go",
"validate_beacon_attestation.go",
"validate_beacon_blocks.go",
"validate_blobs_sidecar.go",
"validate_proposer_slashing.go",
"validate_sync_committee_message.go",
"validate_sync_contribution_proof.go",
@@ -84,6 +88,7 @@ go_library(
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blobs:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -95,6 +100,7 @@ go_library(
"//encoding/ssz/equality:go_default_library",
"//monitoring/tracing:go_default_library",
"//network/forks:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/attestation:go_default_library",
"//proto/prysm/v1alpha1/metadata:go_default_library",
@@ -115,6 +121,7 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_protolambda_go_kzg//bls:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
@@ -138,6 +145,7 @@ go_test(
"rate_limiter_test.go",
"rpc_beacon_blocks_by_range_test.go",
"rpc_beacon_blocks_by_root_test.go",
"rpc_blobs_sidecars_by_range_test.go",
"rpc_chunked_response_test.go",
"rpc_goodbye_test.go",
"rpc_metadata_test.go",
@@ -223,6 +231,7 @@ go_test(
"@com_github_golang_snappy//:go_default_library",
"@com_github_kevinms_leakybucket_go//:go_default_library",
"@com_github_libp2p_go_libp2p_core//:go_default_library",
"@com_github_libp2p_go_libp2p_core//mux:go_default_library",
"@com_github_libp2p_go_libp2p_core//network:go_default_library",
"@com_github_libp2p_go_libp2p_core//peer:go_default_library",
"@com_github_libp2p_go_libp2p_core//protocol:go_default_library",
@@ -230,6 +239,7 @@ go_test(
"@com_github_libp2p_go_libp2p_pubsub//pb:go_default_library",
"@com_github_patrickmn_go_cache//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_protolambda_ztyp//codec:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",

View File

@@ -69,6 +69,17 @@ func (s *Service) registerForUpcomingFork(currEpoch types.Epoch) error {
return nil
}
s.registerSubscribers(nextEpoch, digest)
case params.BeaconConfig().Eip4844ForkEpoch:
digest, err := forks.ForkDigestFromEpoch(nextEpoch, genRoot[:])
if err != nil {
return errors.Wrap(err, "could not retrieve fork digest")
}
if s.subHandler.digestExists(digest) {
return nil
}
s.registerSubscribers(nextEpoch, digest)
s.registerRPCHandlersEIP4844()
}
}
return nil

View File

@@ -29,12 +29,16 @@ go_library(
"//beacon-chain/sync:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blobs:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/rand:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime:go_default_library",
"//runtime/version:go_default_library",
"//time:go_default_library",
"//time/slots:go_default_library",
"@com_github_kevinms_leakybucket_go//:go_default_library",

View File

@@ -15,9 +15,12 @@ import (
prysmsync "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync"
"github.com/prysmaticlabs/prysm/v3/cmd/beacon-chain/flags"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/crypto/rand"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
p2ppb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
@@ -52,6 +55,9 @@ var (
errBlockAlreadyProcessed = errors.New("block is already processed")
errParentDoesNotExist = errors.New("beacon node doesn't have a parent in db with root")
errNoPeersWithAltBlocks = errors.New("no peers with alternative blocks found")
errInvalidSidecar = errors.New("sidecar verification failed")
errMissingSidecar = errors.New("block recieved without sidecar")
errUnexpectedSidecar = errors.New("received unexpected sidecar")
)
// blocksFetcherConfig is a config to setup the block fetcher.
@@ -100,11 +106,12 @@ type fetchRequestParams struct {
// fetchRequestResponse is a combined type to hold results of both successful executions and errors.
// Valid usage pattern will be to check whether result's `err` is nil, before using `blocks`.
type fetchRequestResponse struct {
pid peer.ID
start types.Slot
count uint64
blocks []interfaces.SignedBeaconBlock
err error
pid peer.ID
start types.Slot
count uint64
blocks []interfaces.SignedBeaconBlock
sidecars []*p2ppb.BlobsSidecar
err error
}
// newBlocksFetcher creates ready to use fetcher.
@@ -242,10 +249,11 @@ func (f *blocksFetcher) handleRequest(ctx context.Context, start types.Slot, cou
defer span.End()
response := &fetchRequestResponse{
start: start,
count: count,
blocks: []interfaces.SignedBeaconBlock{},
err: nil,
start: start,
count: count,
blocks: []interfaces.SignedBeaconBlock{},
sidecars: []*p2ppb.BlobsSidecar{},
err: nil,
}
if ctx.Err() != nil {
@@ -269,7 +277,7 @@ func (f *blocksFetcher) handleRequest(ctx context.Context, start types.Slot, cou
}
}
response.blocks, response.pid, response.err = f.fetchBlocksFromPeer(ctx, start, count, peers)
response.blocks, response.sidecars, response.pid, response.err = f.fetchBlocksFromPeer(ctx, start, count, peers)
return response
}
@@ -278,7 +286,7 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
ctx context.Context,
start types.Slot, count uint64,
peers []peer.ID,
) ([]interfaces.SignedBeaconBlock, peer.ID, error) {
) ([]interfaces.SignedBeaconBlock, []*p2ppb.BlobsSidecar, peer.ID, error) {
ctx, span := trace.StartSpan(ctx, "initialsync.fetchBlocksFromPeer")
defer span.End()
@@ -288,16 +296,34 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
Count: count,
Step: 1,
}
sidecarReq := &p2ppb.BlobsSidecarsByRangeRequest{
StartSlot: start,
Count: count,
}
var sidecars []*p2ppb.BlobsSidecar
for i := 0; i < len(peers); i++ {
blocks, err := f.requestBlocks(ctx, req, peers[i])
if err == nil {
if sidecars, err := f.requestSidecars(ctx, sidecarReq, peers[i], blocks); err == nil {
if err = checkBlocksForAvailableSidecars(blocks, sidecars); err == nil {
f.p2p.Peers().Scorers().BlockProviderScorer().Touch(peers[i])
return blocks, sidecars, peers[i], err
}
}
f.p2p.Peers().Scorers().BlockProviderScorer().Touch(peers[i])
return blocks, peers[i], err
return blocks, sidecars, peers[i], err
} else {
log.WithError(err).Debug("Could not request blocks by range")
}
log.WithFields(logrus.Fields{
"err": err,
"startSlot": start,
"count": count,
"peer": peers[i],
}).Trace("Error getting data from peer")
}
return nil, "", errNoPeersAvailable
return nil, nil, "", errNoPeersAvailable
}
// requestBlocks is a wrapper for handling BeaconBlocksByRangeRequest requests/streams.
@@ -359,6 +385,41 @@ func (f *blocksFetcher) requestBlocksByRoot(
return prysmsync.SendBeaconBlocksByRootRequest(ctx, f.chain, f.p2p, pid, req, nil)
}
func (f *blocksFetcher) requestSidecars(
ctx context.Context,
req *p2ppb.BlobsSidecarsByRangeRequest,
pid peer.ID,
blkRefs []interfaces.SignedBeaconBlock,
) ([]*p2ppb.BlobsSidecar, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
l := f.peerLock(pid)
l.Lock()
log.WithFields(logrus.Fields{
"peer": pid,
"start": req.StartSlot,
"count": req.Count,
"capacity": f.rateLimiter.Remaining(pid.String()),
"score": f.p2p.Peers().Scorers().BlockProviderScorer().FormatScorePretty(pid),
}).Debug("Requesting sidecars")
// TODO(EIP-4844): sidecar-specific rate limiting
if f.rateLimiter.Remaining(pid.String()) < int64(req.Count) {
if err := f.waitForBandwidth(pid); err != nil {
l.Unlock()
return nil, err
}
}
f.rateLimiter.Add(pid.String(), int64(req.Count))
l.Unlock()
var sidecarProcessor func(*p2ppb.BlobsSidecar) error
if blkRefs != nil {
sidecarProcessor = sidecarVerifier(blkRefs)
}
return prysmsync.SendBlobsSidecarsByRangeRequest(ctx, f.chain, f.p2p, pid, req, sidecarProcessor)
}
// waitForBandwidth blocks up until peer's bandwidth is restored.
func (f *blocksFetcher) waitForBandwidth(pid peer.ID) error {
log.WithField("peer", pid).Debug("Slowing down for rate limit")
@@ -372,3 +433,66 @@ func (f *blocksFetcher) waitForBandwidth(pid peer.ID) error {
}
return nil
}
func checkBlocksForAvailableSidecars(blks []interfaces.SignedBeaconBlock, sidecars []*p2ppb.BlobsSidecar) error {
for _, b := range blks {
if blocks.IsPreEIP4844Version(b.Version()) {
continue
}
blobKzgs, err := b.Block().Body().BlobKzgs()
if err != nil {
log.WithError(err).Error("Could not get blob kzgs")
}
if len(blobKzgs) == 0 {
continue
}
bRoot, err := b.Block().HashTreeRoot()
if err != nil {
return err
}
var foundSidecar bool
for _, s := range sidecars {
if b.Block().Slot() == s.BeaconBlockSlot && bRoot == bytesutil.ToBytes32(s.BeaconBlockRoot) {
foundSidecar = true
break
}
}
if !foundSidecar {
return fmt.Errorf("%w, slot: %d", errMissingSidecar, b.Block().Slot())
}
}
return nil
}
func sidecarVerifier(blks []interfaces.SignedBeaconBlock) func(*p2ppb.BlobsSidecar) error {
return func(sidecar *p2ppb.BlobsSidecar) error {
for _, b := range blks {
if blocks.IsPreEIP4844Version(b.Version()) {
continue
}
blobKzgs, err := b.Block().Body().BlobKzgs()
if err != nil {
return err
}
if len(blobKzgs) == 0 {
continue
}
if b.Block().Slot() != sidecar.BeaconBlockSlot {
continue
}
bRoot, err := b.Block().HashTreeRoot()
if err != nil {
return err
}
if bRoot != bytesutil.ToBytes32(sidecar.BeaconBlockRoot) {
continue
}
if err := blobs.VerifyBlobsSidecar(b.Block().Slot(), bRoot, bytesutil.ToBytes48Array(blobKzgs), sidecar); err != nil {
return errors.Wrap(errInvalidSidecar, err.Error())
}
return nil
}
// If here then we've received an unwanted sidecar. This is an error because it means some other valid sidecar got pushed out
return errUnexpectedSidecar
}
}

View File

@@ -6,6 +6,8 @@ import (
"time"
"github.com/libp2p/go-libp2p-core/peer"
p2ppb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/db"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p"
beaconsync "github.com/prysmaticlabs/prysm/v3/beacon-chain/sync"
@@ -88,8 +90,9 @@ type blocksQueue struct {
// blocksQueueFetchedData is a data container that is returned from a queue on each step.
type blocksQueueFetchedData struct {
pid peer.ID
blocks []interfaces.SignedBeaconBlock
pid peer.ID
blocks []interfaces.SignedBeaconBlock
sidecars []*p2ppb.BlobsSidecar
}
// newBlocksQueue creates initialized priority queue.
@@ -333,6 +336,7 @@ func (q *blocksQueue) onDataReceivedEvent(ctx context.Context) eventHandlerFn {
}
m.pid = response.pid
m.blocks = response.blocks
m.sidecars = response.sidecars
return stateDataParsed, nil
}
}
@@ -353,8 +357,9 @@ func (q *blocksQueue) onReadyToSendEvent(ctx context.Context) eventHandlerFn {
send := func() (stateID, error) {
data := &blocksQueueFetchedData{
pid: m.pid,
blocks: m.blocks,
pid: m.pid,
blocks: m.blocks,
sidecars: m.sidecars,
}
select {
case <-ctx.Done():

View File

@@ -254,7 +254,7 @@ func TestBlocksQueue_Loop(t *testing.T) {
highestExpectedSlot: tt.highestExpectedSlot,
})
assert.NoError(t, queue.start())
processBlock := func(block interfaces.SignedBeaconBlock) error {
processBlock := func(block interfaces.SignedBeaconBlock, sidecar *eth.BlobsSidecar) error {
if !beaconDB.HasBlock(ctx, block.Block().ParentRoot()) {
return fmt.Errorf("%w: %#x", errParentDoesNotExist, block.Block().ParentRoot())
}
@@ -268,7 +268,14 @@ func TestBlocksQueue_Loop(t *testing.T) {
var blocks []interfaces.SignedBeaconBlock
for data := range queue.fetchedData {
for _, block := range data.blocks {
if err := processBlock(block); err != nil {
var sidecar *eth.BlobsSidecar
for _, s := range data.sidecars {
if s.BeaconBlockSlot == block.Block().Slot() {
sidecar = s
break
}
}
if err := processBlock(block, sidecar); err != nil {
continue
}
blocks = append(blocks, block)

View File

@@ -9,6 +9,7 @@ import (
"github.com/libp2p/go-libp2p-core/peer"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
"github.com/prysmaticlabs/prysm/v3/time/slots"
)
@@ -42,12 +43,13 @@ type stateMachineManager struct {
// stateMachine holds a state of a single block processing FSM.
// Each FSM allows deterministic state transitions: State(S) x Event(E) -> Actions (A), State(S').
type stateMachine struct {
smm *stateMachineManager
start types.Slot
state stateID
pid peer.ID
blocks []interfaces.SignedBeaconBlock
updated time.Time
smm *stateMachineManager
start types.Slot
state stateID
pid peer.ID
blocks []interfaces.SignedBeaconBlock
sidecars []*ethpb.BlobsSidecar
updated time.Time
}
// eventHandlerFn is an event handler function's signature.
@@ -75,11 +77,12 @@ func (smm *stateMachineManager) addEventHandler(event eventID, state stateID, fn
// addStateMachine allocates memory for new FSM.
func (smm *stateMachineManager) addStateMachine(startSlot types.Slot) *stateMachine {
smm.machines[startSlot] = &stateMachine{
smm: smm,
start: startSlot,
state: stateNew,
blocks: []interfaces.SignedBeaconBlock{},
updated: prysmTime.Now(),
smm: smm,
start: startSlot,
state: stateNew,
blocks: []interfaces.SignedBeaconBlock{},
sidecars: []*ethpb.BlobsSidecar{},
updated: prysmTime.Now(),
}
smm.recalculateMachineAttribs()
return smm.machines[startSlot]
@@ -91,6 +94,7 @@ func (smm *stateMachineManager) removeStateMachine(startSlot types.Slot) error {
return fmt.Errorf("state for machine %v is not found", startSlot)
}
smm.machines[startSlot].blocks = nil
smm.machines[startSlot].sidecars = nil
delete(smm.machines, startSlot)
smm.recalculateMachineAttribs()
return nil

View File

@@ -223,6 +223,13 @@ func connectPeer(t *testing.T, host *p2pt.TestP2P, datum *peerData, peerStatus *
assert.NoError(t, beaconsync.WriteBlockChunk(stream, mChain, p.Encoding(), wsb))
}
})
p.SetStreamHandler("/eth2/beacon_chain/req/blobs_sidecars_by_range/1/ssz_snappy", func(stream network.Stream) {
defer func() {
assert.NoError(t, stream.Close())
}()
req := &ethpb.BlobsSidecarsByRangeRequest{}
assert.NoError(t, p.Encoding().DecodeWithMaxLength(stream, req))
})
p.Connect(host)
@@ -320,6 +327,15 @@ func connectPeerHavingBlocks(
}
})
p.SetStreamHandler("/eth2/beacon_chain/req/blobs_sidecars_by_range/1/ssz_snappy", func(stream network.Stream) {
defer func() {
_err := stream.Close()
_ = _err
}()
req := new(ethpb.BlobsSidecarsByRangeRequest)
assert.NoError(t, p.Encoding().DecodeWithMaxLength(stream, req))
})
p.Connect(host)
finalizedEpoch := slots.ToEpoch(finalizedSlot)

View File

@@ -12,6 +12,8 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/sirupsen/logrus"
)
@@ -128,7 +130,7 @@ func (s *Service) processFetchedData(
defer s.updatePeerScorerStats(data.pid, startSlot)
// Use Batch Block Verify to process and verify batches directly.
if err := s.processBatchedBlocks(ctx, genesis, data.blocks, s.cfg.Chain.ReceiveBlockBatch); err != nil {
if err := s.processBatchedBlocks(ctx, genesis, data.blocks, data.sidecars, s.cfg.Chain.ReceiveBlockBatch); err != nil {
log.WithError(err).Warn("Skip processing batched blocks")
}
}
@@ -142,6 +144,20 @@ func (s *Service) processFetchedDataRegSync(
invalidBlocks := 0
blksWithoutParentCount := 0
for _, blk := range data.blocks {
var sidecar *eth.BlobsSidecar
for _, s := range data.sidecars {
if s.BeaconBlockSlot == blk.Block().Slot() {
sidecar = s
break
}
}
if sidecar != nil && blk.Version() == version.EIP4844 {
if err := blk.SetSideCar(&eth.SignedBlobsSidecar{Message: sidecar}); err != nil {
log.WithError(err).Error("Could not set sidecar")
return
}
}
if err := s.processBlock(ctx, genesis, blk, blockReceiver); err != nil {
switch {
case errors.Is(err, errBlockAlreadyProcessed):
@@ -241,11 +257,12 @@ func (s *Service) processBlock(
if !s.cfg.Chain.HasBlock(ctx, blk.Block().ParentRoot()) {
return fmt.Errorf("%w: (in processBlock, slot=%d) %#x", errParentDoesNotExist, blk.Block().Slot(), blk.Block().ParentRoot())
}
return blockReceiver(ctx, blk, blkRoot)
}
func (s *Service) processBatchedBlocks(ctx context.Context, genesis time.Time,
blks []interfaces.SignedBeaconBlock, bFunc batchBlockReceiverFn) error {
blks []interfaces.SignedBeaconBlock, sidecars []*eth.BlobsSidecar, bFunc batchBlockReceiverFn) error {
if len(blks) == 0 {
return errors.New("0 blocks provided into method")
}
@@ -284,7 +301,17 @@ func (s *Service) processBatchedBlocks(ctx context.Context, genesis time.Time,
return err
}
blockRoots[i] = blkRoot
for _, sc := range sidecars {
if sc.BeaconBlockSlot == b.Block().Slot() {
if err := b.SetSideCar(&eth.SignedBlobsSidecar{Message: sc}); err != nil {
return err
}
blks[i] = b
}
}
}
return bFunc(ctx, blks, blockRoots)
}

View File

@@ -445,7 +445,7 @@ func TestService_processBlockBatch(t *testing.T) {
}
// Process block normally.
err = s.processBatchedBlocks(ctx, genesis, batch, func(
err = s.processBatchedBlocks(ctx, genesis, batch, nil, func(
ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
assert.NoError(t, s.cfg.Chain.ReceiveBlockBatch(ctx, blks, blockRoots))
return nil
@@ -453,7 +453,7 @@ func TestService_processBlockBatch(t *testing.T) {
assert.NoError(t, err)
// Duplicate processing should trigger error.
err = s.processBatchedBlocks(ctx, genesis, batch, func(
err = s.processBatchedBlocks(ctx, genesis, batch, nil, func(
ctx context.Context, blocks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
return nil
})
@@ -469,15 +469,15 @@ func TestService_processBlockBatch(t *testing.T) {
}
// Bad batch should fail because it is non linear
err = s.processBatchedBlocks(ctx, genesis, badBatch2, func(
err = s.processBatchedBlocks(ctx, genesis, badBatch2, nil, func(
ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
return nil
})
expectedSubErr := "expected linear block list"
assert.ErrorContains(t, expectedSubErr, err)
// Continue normal processing, should proceed w/o errors.
err = s.processBatchedBlocks(ctx, genesis, batch2, func(
// continue normal processing, should proceed w/o errors.
err = s.processBatchedBlocks(ctx, genesis, batch2, nil, func(
ctx context.Context, blks []interfaces.SignedBeaconBlock, blockRoots [][32]byte) error {
assert.NoError(t, s.cfg.Chain.ReceiveBlockBatch(ctx, blks, blockRoots))
return nil

View File

@@ -3,15 +3,18 @@ package sync
import (
"context"
"encoding/hex"
"math"
"sort"
"sync"
"time"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/async"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
p2ptypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
@@ -19,6 +22,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/encoding/ssz/equality"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/sirupsen/logrus"
"github.com/trailofbits/go-mutexasserts"
@@ -59,6 +63,7 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
}
ss := s.sortedPendingSlots()
var parentRoots [][32]byte
missingSidecarRefs := make(map[types.Slot][][32]byte)
span.AddAttributes(
trace.Int64Attribute("numSlots", int64(len(ss))),
@@ -78,6 +83,7 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
s.pendingQueueLock.RLock()
bs := s.pendingBlocksInCache(slot)
sidecars := s.pendingSidecarsInCache(slot)
// Skip if there's no block in the queue.
if len(bs) == 0 {
s.pendingQueueLock.RUnlock()
@@ -100,6 +106,29 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
return err
}
hasPeer := len(pids) != 0
var queuedSidecar *queuedBlobsSidecar
contains, err := blobs.BlockContainsKZGs(b.Block())
if err != nil {
return err
}
if contains {
queuedSidecar = findSidecarForBlock(b.Block(), blkRoot, sidecars)
if queuedSidecar == nil {
if hasPeer {
log.WithFields(logrus.Fields{
"currentSlot": b.Block().Slot(),
"blkRoot": hex.EncodeToString(blkRoot[:]),
}).Debug("Requesting missing sidecar")
missingSidecarRefs[b.Block().Slot()] = append(missingSidecarRefs[slot], blkRoot)
}
span.End()
continue
}
}
inDB := s.cfg.beaconDB.HasBlock(ctx, blkRoot)
// No need to process the same block twice.
if inDB {
@@ -108,6 +137,12 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
s.pendingQueueLock.Unlock()
return err
}
if queuedSidecar != nil {
if err := s.deleteSidecarFromPendingQueue(slot, queuedSidecar); err != nil {
s.pendingQueueLock.Unlock()
return err
}
}
s.pendingQueueLock.Unlock()
span.End()
continue
@@ -126,7 +161,7 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
}
parentInDb := s.cfg.beaconDB.HasBlock(ctx, b.Block().ParentRoot())
hasPeer := len(pids) != 0
hasPeer = len(pids) != 0
// Only request for missing parent block if it's not in beaconDB, not in pending cache
// and has peer in the peer list.
@@ -159,6 +194,40 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
default:
}
var sidecar *ethpb.BlobsSidecar
if queuedSidecar != nil {
// Only for gossiped sidecars that skipped validation
if queuedSidecar.sig != nil && !queuedSidecar.validated {
res, err := s.validateBlobsSidecarSignature(ctx, b, queuedSidecar.AsSignedBlobsSidecar())
if err != nil || res != pubsub.ValidationAccept {
tracing.AnnotateError(span, err)
span.End()
continue
}
}
sidecar = queuedSidecar.s
kzgs, err := b.Block().Body().BlobKzgs()
// an error shouldn't happen! A non-nil sidecar means we're dealing with a EIP-4844 block
if err != nil {
log.WithError(err).WithField("slot", b.Block().Slot()).Debug("Could not get KZGs from block")
tracing.AnnotateError(span, err)
span.End()
continue
}
if err := blobs.VerifyBlobsSidecar(b.Block().Slot(), blkRoot, bytesutil.ToBytes48Array(kzgs), queuedSidecar.s); err != nil {
log.WithError(err).WithField("slot", b.Block().Slot()).Debug("Could not verify blobs sidecar")
}
if sidecar != nil {
if err := b.SetSideCar(&ethpb.SignedBlobsSidecar{
Message: sidecar,
}); err != nil {
log.Error("Could not set sidecar on block", err)
continue
}
}
}
if err := s.cfg.chain.ReceiveBlock(ctx, b, blkRoot); err != nil {
if blockchain.IsInvalidBlock(err) {
r := blockchain.InvalidBlockRoot(err)
@@ -187,12 +256,25 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
log.WithError(err).Debug("Could not broadcast block")
}
}
if queuedSidecar != nil {
if queuedSidecar.IsSigned() {
if err := s.cfg.p2p.Broadcast(ctx, queuedSidecar.AsSignedBlobsSidecar()); err != nil {
log.WithError(err).Debug("Could not broadcast sidecar")
}
}
}
s.pendingQueueLock.Lock()
if err := s.deleteBlockFromPendingQueue(slot, b, blkRoot); err != nil {
s.pendingQueueLock.Unlock()
return err
}
if queuedSidecar != nil {
if err := s.deleteSidecarFromPendingQueue(slot, queuedSidecar); err != nil {
s.pendingQueueLock.Unlock()
return err
}
}
s.pendingQueueLock.Unlock()
log.WithFields(logrus.Fields{
@@ -204,7 +286,12 @@ func (s *Service) processPendingBlocks(ctx context.Context) error {
}
}
return s.sendBatchRootRequest(ctx, parentRoots, randGen)
// TODO(EIP-4844): Rework this to send blocks and sidecar requests in lock-step. It's a more robust way of handling partial failures
var err error
if err = s.sendBatchRootRequest(ctx, parentRoots, randGen); err == nil {
err = s.sendBatchSidecarRequest(ctx, missingSidecarRefs, randGen)
}
return err
}
func (s *Service) checkIfBlockIsBad(
@@ -280,6 +367,71 @@ func (s *Service) sendBatchRootRequest(ctx context.Context, roots [][32]byte, ra
return nil
}
func (s *Service) sendBatchSidecarRequest(ctx context.Context, reqs map[types.Slot][][32]byte, randGen *rand.Rand) error {
ctx, span := trace.StartSpan(ctx, "sendBatchSidecarRequests")
defer span.End()
if len(reqs) == 0 {
return nil
}
cp := s.cfg.chain.FinalizedCheckpt()
_, bestPeers := s.cfg.p2p.Peers().BestFinalized(maxPeerRequest, cp.Epoch)
if len(bestPeers) == 0 {
return nil
}
makeSidecarRangeRequest := func(reqs map[types.Slot][][32]byte) *ethpb.BlobsSidecarsByRangeRequest {
start := types.Slot(math.MaxUint64)
end := types.Slot(0)
for slot := range reqs {
if slot < start {
start = slot
}
if slot > end {
end = slot
}
}
count := uint64(end.SubSlot(start).Add(1))
if count > params.BeaconNetworkConfig().MaxRequestBlobsSidecars {
count = params.BeaconNetworkConfig().MaxRequestBlobsSidecars
}
return &ethpb.BlobsSidecarsByRangeRequest{
StartSlot: start,
Count: count,
}
}
// Randomly choose a peer to query from our best peers. If that peer cannot return
// all the requested blocks, we randomly select another peer.
pid := bestPeers[randGen.Int()%len(bestPeers)]
for i := 0; i < numOfTries; i++ {
sidecarReq := makeSidecarRangeRequest(reqs)
if err := s.sendRecentBlobSidecarsRequest(ctx, sidecarReq, pid); err != nil {
tracing.AnnotateError(span, err)
log.WithError(err).Debug("Could not send recent blob sidecar request")
}
newReqs := make(map[types.Slot][][32]byte)
s.pendingQueueLock.RLock()
for slot, roots := range reqs {
for _, rt := range roots {
if !s.seenPendingSidecars[rt] {
newReqs[slot] = append(newReqs[slot], rt)
}
}
}
s.pendingQueueLock.RUnlock()
if len(newReqs) == 0 {
break
}
// Choosing a new peer with the leftover set of
// roots to request.
reqs = newReqs
pid = bestPeers[randGen.Int()%len(bestPeers)]
}
return nil
}
func (s *Service) sortedPendingSlots() []types.Slot {
s.pendingQueueLock.RLock()
defer s.pendingQueueLock.RUnlock()
@@ -299,7 +451,7 @@ func (s *Service) sortedPendingSlots() []types.Slot {
// validatePendingSlots validates the pending blocks
// by their slot. If they are before the current finalized
// checkpoint, these blocks are removed from the queue.
// checkpoint, these blocks and their sidecars are removed from the queue.
func (s *Service) validatePendingSlots() error {
s.pendingQueueLock.Lock()
defer s.pendingQueueLock.Unlock()
@@ -341,6 +493,21 @@ func (s *Service) validatePendingSlots() error {
}
}
}
items = s.slotToPendingSidecars.Items()
for k := range items {
slot := cacheKeyToSlot(k)
sidecars := s.pendingSidecarsInCache(slot)
for _, sc := range sidecars {
epoch := slots.ToEpoch(slot)
if finalizedEpoch > 0 && epoch <= finalizedEpoch {
if err := s.deleteSidecarFromPendingQueue(slot, sc); err != nil {
return err
}
}
}
}
return nil
}
@@ -349,6 +516,8 @@ func (s *Service) clearPendingSlots() {
defer s.pendingQueueLock.Unlock()
s.slotToPendingBlocks.Flush()
s.seenPendingBlocks = make(map[[32]byte]bool)
s.slotToPendingSidecars.Flush()
s.seenPendingSidecars = make(map[[32]byte]bool)
}
// Delete block from the list from the pending queue using the slot as key.
@@ -462,3 +631,16 @@ func slotToCacheKey(s types.Slot) string {
b := bytesutil.SlotToBytesBigEndian(s)
return string(b)
}
func findSidecarForBlock(b interfaces.BeaconBlock, blkRoot [32]byte, sidecars []*queuedBlobsSidecar) *queuedBlobsSidecar {
for _, s := range sidecars {
if b.Slot() != s.s.BeaconBlockSlot {
continue
}
if blkRoot != bytesutil.ToBytes32(s.s.BeaconBlockRoot) {
continue
}
return s
}
return nil
}

View File

@@ -49,8 +49,10 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) {
},
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -121,8 +123,10 @@ func TestRegularSyncBeaconBlockSubscriber_OptimisticStatus(t *testing.T) {
},
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -193,8 +197,10 @@ func TestRegularSyncBeaconBlockSubscriber_ExecutionEngineTimesOut(t *testing.T)
},
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -266,8 +272,10 @@ func TestRegularSync_InsertDuplicateBlocks(t *testing.T) {
},
},
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -320,8 +328,10 @@ func TestRegularSyncBeaconBlockSubscriber_DoNotReprocessBlock(t *testing.T) {
},
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -385,8 +395,10 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks_2Chains(t *testin
},
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -484,8 +496,10 @@ func TestRegularSyncBeaconBlockSubscriber_PruneOldPendingBlocks(t *testing.T) {
},
},
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -588,8 +602,10 @@ func TestService_BatchRootRequest(t *testing.T) {
Genesis: time.Now(),
},
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -705,8 +721,10 @@ func TestService_ProcessPendingBlockOnCorrectSlot(t *testing.T) {
chain: &mockChain,
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()
@@ -783,8 +801,10 @@ func TestService_ProcessBadPendingBlocks(t *testing.T) {
chain: &mockChain,
stateGen: stategen.New(db),
},
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
slotToPendingSidecars: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
}
r.initCaches()

View File

@@ -0,0 +1,100 @@
package sync
import (
"time"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/encoding/ssz/equality"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/trailofbits/go-mutexasserts"
)
const maxSidecarsPerSlot = maxBlocksPerSlot
// represents a possibly signed BlobsSidecar
type queuedBlobsSidecar struct {
s *ethpb.BlobsSidecar
sig []byte
validated bool
}
func (s *queuedBlobsSidecar) IsSigned() bool {
return s.sig != nil
}
func (s *queuedBlobsSidecar) AsSignedBlobsSidecar() *ethpb.SignedBlobsSidecar {
return &ethpb.SignedBlobsSidecar{Message: s.s, Signature: s.sig}
}
// Delete sidecar from the list from the pending queue using the slot as key.
// Note: this helper is not thread safe.
func (s *Service) deleteSidecarFromPendingQueue(slot types.Slot, sc *queuedBlobsSidecar) error {
mutexasserts.AssertRWMutexLocked(&s.pendingQueueLock)
sidecars := s.pendingSidecarsInCache(slot)
if len(sidecars) == 0 {
return nil
}
newSidecars := make([]*queuedBlobsSidecar, 0, len(sidecars))
for _, sidecar := range sidecars {
if equality.DeepEqual(sidecar, sc) {
continue
}
newSidecars = append(newSidecars, sidecar)
}
if len(newSidecars) == 0 {
s.slotToPendingSidecars.Delete(slotToCacheKey(slot))
delete(s.seenPendingSidecars, bytesutil.ToBytes32(sc.s.BeaconBlockRoot))
return nil
}
// Decrease exp time in proportion to how many sidecars are still in the cache for slot key.
d := pendingSidecarExpTime / time.Duration(len(newSidecars))
if err := s.slotToPendingSidecars.Replace(slotToCacheKey(slot), newSidecars, d); err != nil {
return err
}
delete(s.seenPendingSidecars, bytesutil.ToBytes32(sc.s.BeaconBlockRoot))
return nil
}
// Insert sidecar to the list in the pending queue using its slot as key.
// Note: this helper is not thread safe.
func (s *Service) insertSidecarToPendingQueue(sidecar *queuedBlobsSidecar) {
mutexasserts.AssertRWMutexLocked(&s.pendingQueueLock)
root := bytesutil.ToBytes32(sidecar.s.BeaconBlockRoot)
if s.seenPendingSidecars[root] {
return
}
s.addPendingSidecarToCache(sidecar)
s.seenPendingSidecars[root] = true
}
// This returns signed sidecars given input key from slotToPendingSidecars.
func (s *Service) pendingSidecarsInCache(slot types.Slot) []*queuedBlobsSidecar {
k := slotToCacheKey(slot)
value, ok := s.slotToPendingSidecars.Get(k)
if !ok {
return []*queuedBlobsSidecar{}
}
scs, ok := value.([]*queuedBlobsSidecar)
if !ok {
return []*queuedBlobsSidecar{}
}
return scs
}
// This adds input sidecar to slotToPendingSidecars cache.
func (s *Service) addPendingSidecarToCache(sc *queuedBlobsSidecar) {
sidecars := s.pendingSidecarsInCache(sc.s.BeaconBlockSlot)
if len(sidecars) >= maxSidecarsPerSlot {
return
}
sidecars = append(sidecars, sc)
k := slotToCacheKey(sc.s.BeaconBlockSlot)
s.slotToPendingSidecars.Set(k, sidecars, pendingSidecarExpTime)
return
}

View File

@@ -36,6 +36,10 @@ func newRateLimiter(p2pProvider p2p.P2P) *limiter {
allowedBlocksPerSecond := float64(flags.Get().BlockBatchLimit)
allowedBlocksBurst := int64(flags.Get().BlockBatchLimitBurstFactor * flags.Get().BlockBatchLimit)
// Initialize blobs sidecar transfer limits.
blobsTransferRate := float64(flags.Get().BlobsTransferRate)
blobsTransferRateThresh := int64(flags.Get().BlobsTransferRateThresh)
// Set topic map for all rpc topics.
topicMap := make(map[string]*leakybucket.Collector, len(p2p.RPCTopicMappings))
// Goodbye Message
@@ -61,6 +65,9 @@ func newRateLimiter(p2pProvider p2p.P2P) *limiter {
topicMap[addEncoding(p2p.RPCBlocksByRangeTopicV1)] = blockCollector
topicMap[addEncoding(p2p.RPCBlocksByRangeTopicV2)] = blockCollectorV2
// BlobsSidecarsByRange requests
topicMap[addEncoding(p2p.RPCBlobsSidecarsByRangeTopicV1)] = leakybucket.NewCollector(blobsTransferRate, blobsTransferRateThresh, false /* deleteEmptyBucket */)
// General topic for all rpc requests.
topicMap[rpcLimiterTopic] = leakybucket.NewCollector(5, defaultBurstLimit*2, false /* deleteEmptyBuckets */)

View File

@@ -49,6 +49,9 @@ func (s *Service) registerRPCHandlers() {
s.pingHandler,
)
s.registerRPCHandlersAltair()
if currEpoch >= params.BeaconConfig().Eip4844ForkEpoch {
s.registerRPCHandlersEIP4844()
}
return
}
s.registerRPC(
@@ -93,6 +96,13 @@ func (s *Service) registerRPCHandlersAltair() {
)
}
func (s *Service) registerRPCHandlersEIP4844() {
s.registerRPC(
p2p.RPCBlobsSidecarsByRangeTopicV1,
s.blobsSidecarsByRangeRPCHandler,
)
}
// Remove all v1 Stream handlers that are no longer supported
// from altair onwards.
func (s *Service) unregisterPhase0Handlers() {

View File

@@ -25,6 +25,7 @@ func (s *Service) sendRecentBeaconBlocksRequest(ctx context.Context, blockRoots
}
s.pendingQueueLock.Lock()
if err := s.insertBlockToPendingQueue(blk.Block().Slot(), blk, blkRoot); err != nil {
s.pendingQueueLock.Unlock()
return err
}
s.pendingQueueLock.Unlock()

View File

@@ -0,0 +1,179 @@
package sync
import (
"context"
"io"
"time"
libp2pcore "github.com/libp2p/go-libp2p-core"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p"
p2ptypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/types"
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
pb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"go.opencensus.io/trace"
)
// We assume a cost of 1 MiB per sidecar responded to a range request.
const avgSidecarBlobsTransferBytes = 1 << 10
type BlobsSidecarProcessor func(sidecar *pb.BlobsSidecar) error
// blobsSidecarsByRangeRPCHandler looks up the request blobs from the database from a given start slot index
func (s *Service) blobsSidecarsByRangeRPCHandler(ctx context.Context, msg interface{}, stream libp2pcore.Stream) error {
ctx, span := trace.StartSpan(ctx, "sync.BlobsSidecarsByRangeHandler")
defer span.End()
ctx, cancel := context.WithTimeout(ctx, respTimeout)
defer cancel()
SetRPCStreamDeadlines(stream)
r, ok := msg.(*pb.BlobsSidecarsByRangeRequest)
if !ok {
return errors.New("message is not type *pb.BlobsSidecarsByRangeRequest")
}
startSlot := r.StartSlot
count := r.Count
endSlot := startSlot.Add(count)
var numBlobs uint64
maxRequestBlobsSidecars := params.BeaconNetworkConfig().MaxRequestBlobsSidecars
for slot := startSlot; slot < endSlot && numBlobs < maxRequestBlobsSidecars; slot = slot.Add(1) {
// TODO(XXX): With danksharding, there could be multiple sidecars per slot. As such, the cost requirement will be dynamic
if err := s.rateLimiter.validateRequest(stream, uint64(avgSidecarBlobsTransferBytes)); err != nil {
return err
}
sidecars, err := s.cfg.beaconDB.BlobsSidecarsBySlot(ctx, slot)
if err != nil {
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, err)
return err
}
if len(sidecars) == 0 {
continue
}
var outLen int
for _, sidecar := range sidecars {
outLen += estimateBlobsSidecarCost(sidecar)
SetStreamWriteDeadline(stream, defaultWriteDuration)
if chunkErr := WriteBlobsSidecarChunk(stream, s.cfg.chain, s.cfg.p2p.Encoding(), sidecar); chunkErr != nil {
log.WithError(chunkErr).Debug("Could not send a chunked response")
s.writeErrorResponseToStream(responseCodeServerError, p2ptypes.ErrGeneric.Error(), stream)
tracing.AnnotateError(span, chunkErr)
return chunkErr
}
}
numBlobs++
s.rateLimiter.add(stream, int64(outLen))
// Short-circuit immediately once we've sent the last blob.
if slot.Add(1) >= endSlot {
break
}
key := stream.Conn().RemotePeer().String()
sidecarLimiter, err := s.rateLimiter.topicCollector(string(stream.Protocol()))
if err != nil {
return err
}
// Throttling - wait until we have enough tokens to send the next blobs
if sidecarLimiter.Remaining(key) < avgSidecarBlobsTransferBytes {
timer := time.NewTimer(sidecarLimiter.TillEmpty(key))
select {
case <-ctx.Done():
timer.Stop()
return ctx.Err()
case <-timer.C:
timer.Stop()
}
}
}
closeStream(stream, log)
return nil
}
// sendRecentBlobsSidecarsRequest retrieves sidecars and inserts them to the pending queue
func (s *Service) sendRecentBlobSidecarsRequest(ctx context.Context, req *pb.BlobsSidecarsByRangeRequest, pid peer.ID) error {
ctx, cancel := context.WithTimeout(ctx, respTimeout)
defer cancel()
_, err := SendBlobsSidecarsByRangeRequest(ctx, s.cfg.chain, s.cfg.p2p, pid, req, func(sc *pb.BlobsSidecar) error {
s.pendingQueueLock.Lock()
s.insertSidecarToPendingQueue(&queuedBlobsSidecar{s: sc})
s.pendingQueueLock.Unlock()
return nil
})
return err
}
func SendBlobsSidecarsByRangeRequest(
ctx context.Context, chain blockchain.ChainInfoFetcher, p2pProvider p2p.P2P, pid peer.ID,
req *pb.BlobsSidecarsByRangeRequest, sidecarProcessor BlobsSidecarProcessor) ([]*pb.BlobsSidecar, error) {
topic, err := p2p.TopicFromMessage(p2p.BlobsSidecarsByRangeMessageName, slots.ToEpoch(chain.CurrentSlot()))
if err != nil {
return nil, err
}
stream, err := p2pProvider.Send(ctx, req, topic, pid)
if err != nil {
return nil, err
}
defer closeStream(stream, log)
var sidecars []*pb.BlobsSidecar
process := func(sidecar *pb.BlobsSidecar) error {
sidecars = append(sidecars, sidecar)
if sidecarProcessor != nil {
return sidecarProcessor(sidecar)
}
return nil
}
var prevSlot types.Slot
for i := uint64(0); ; i++ {
isFirstChunk := len(sidecars) == 0
sidecar, err := ReadChunkedBlobsSidecar(stream, chain, p2pProvider, isFirstChunk)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
if i >= req.Count || i >= params.BeaconNetworkConfig().MaxRequestBlobsSidecars {
return nil, ErrInvalidFetchedData
}
if sidecar.BeaconBlockSlot < req.StartSlot || sidecar.BeaconBlockSlot >= req.StartSlot.Add(req.Count) {
return nil, ErrInvalidFetchedData
}
// assert slots aren't out of order and always increasing
if prevSlot >= sidecar.BeaconBlockSlot {
return nil, ErrInvalidFetchedData
}
prevSlot = sidecar.BeaconBlockSlot
if err := process(sidecar); err != nil {
return nil, err
}
}
return sidecars, nil
}
func estimateBlobsSidecarCost(sidecar *pb.BlobsSidecar) int {
// This represents the fixed cost (in bytes) of the beacon_block_root and beacon_block_slot fields in the sidecar
const overheadCost = 32 + 8
cost := overheadCost
for _, blob := range sidecar.Blobs {
for _, b := range blob.Blob {
cost += len(b)
}
}
return cost
}

View File

@@ -0,0 +1,166 @@
package sync
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/kevinms/leakybucket-go"
"github.com/libp2p/go-libp2p-core/mux"
"github.com/libp2p/go-libp2p-core/network"
"github.com/libp2p/go-libp2p-core/protocol"
chainMock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
mock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
db "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p"
p2ptest "github.com/prysmaticlabs/prysm/v3/beacon-chain/p2p/testing"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
consensusblock "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
"github.com/prysmaticlabs/prysm/v3/testing/util"
)
func newBlobsSidecar() *ethpb.SignedBlobsSidecar {
return &ethpb.SignedBlobsSidecar{
Message: &ethpb.BlobsSidecar{
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
}
}
func TestRPCBlobsSidecarsByRange_RPCHandlerReturnsBlobsSidecars(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected")
d := db.SetupDB(t)
req := &ethpb.BlobsSidecarsByRangeRequest{
StartSlot: 100,
Count: 4,
}
var blocks []interfaces.SignedBeaconBlock
for i := req.StartSlot; i < req.StartSlot.Add(req.Count); i += types.Slot(1) {
// save the BeaconBlock to index the slots used to retrieve sidecars
blk := util.NewBeaconBlock()
blk.Block.Slot = i
wsb, err := consensusblock.NewSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, d.SaveBlock(context.Background(), wsb))
blocks = append(blocks, wsb)
sidecar := newBlobsSidecar()
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
sidecar.Message.BeaconBlockRoot = root[:]
sidecar.Message.BeaconBlockSlot = blk.Block.Slot
require.NoError(t, d.SaveBlobsSidecar(context.Background(), sidecar.Message))
}
// Start service with 160 as allowed blocks capacity (and almost zero capacity recovery).
r := &Service{cfg: &config{p2p: p1, beaconDB: d, chain: &chainMock.ChainService{}}, rateLimiter: newRateLimiter(p1)}
pcl := protocol.ID(p2p.RPCBlobsSidecarsByRangeTopicV1)
topic := string(pcl)
r.rateLimiter.limiterMap[topic] = leakybucket.NewCollector(1000, 10000, false)
var wg sync.WaitGroup
wg.Add(1)
p2.BHost.SetStreamHandler(pcl, func(stream network.Stream) {
defer wg.Done()
for i := req.StartSlot; i < req.StartSlot.Add(req.Count); i += types.Slot(1) {
expectSuccess(t, stream)
sidecar := new(ethpb.BlobsSidecar)
assert.NoError(t, r.cfg.p2p.Encoding().DecodeWithMaxLength(stream, sidecar))
assert.Equal(t, i, sidecar.BeaconBlockSlot)
idx := i - req.StartSlot
assert.Equal(t, true, int(idx) < len(blocks))
root, err := blocks[idx].Block().HashTreeRoot()
assert.NoError(t, err)
assert.DeepEqual(t, root[:], sidecar.BeaconBlockRoot)
}
})
stream1, err := p1.BHost.NewStream(context.Background(), p2.BHost.ID(), pcl)
require.NoError(t, err)
err = r.blobsSidecarsByRangeRPCHandler(context.Background(), req, stream1)
require.NoError(t, err)
// Make sure that rate limiter doesn't limit capacity exceedingly.
remainingCapacity := r.rateLimiter.limiterMap[topic].Remaining(p2.PeerID().String())
expectedCapacity := int64(10000 - 40*req.Count) // an empty sidecar is 40 bytes
require.Equal(t, expectedCapacity, remainingCapacity, "Unexpected rate limiting capacity")
if util.WaitTimeout(&wg, 1*time.Second) {
t.Fatal("Did not receive stream within 1 sec")
}
}
func TestSendRequest_SendBlobsSidecarsByRangeRequest(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
req := &ethpb.BlobsSidecarsByRangeRequest{
StartSlot: 20,
Count: 5,
}
db := db.SetupDB(t)
knownSidecars := make([]*ethpb.BlobsSidecar, 0)
for i := req.StartSlot; i < req.StartSlot.Add(req.Count); i += types.Slot(1) {
// Save the blocks that will be used to verify blobs later
blk := util.HydrateEIP4844SignedBeaconBlock(new(ethpb.SignedBeaconBlockWithBlobKZGs))
blk.Block.Slot = types.Slot(i)
wsb, err := consensusblock.NewSignedBeaconBlock(blk)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(context.Background(), wsb))
sidecar := newBlobsSidecar()
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
sidecar.Message.BeaconBlockRoot = root[:]
sidecar.Message.BeaconBlockSlot = blk.Block.Slot
knownSidecars = append(knownSidecars, sidecar.Message)
}
blobsProvider := func(p2pProvider p2p.P2P) func(stream network.Stream) {
return func(stream network.Stream) {
defer func() {
assert.NoError(t, stream.Close())
}()
req := &ethpb.BlobsSidecarsByRangeRequest{}
assert.NoError(t, p2pProvider.Encoding().DecodeWithMaxLength(stream, req))
for i := req.StartSlot; i < req.StartSlot.Add(req.Count); i += types.Slot(1) {
chain := &mock.ChainService{Genesis: time.Now(), ValidatorsRoot: [32]byte{}}
idx := i - req.StartSlot
sidecar := knownSidecars[idx]
err := WriteBlobsSidecarChunk(stream, chain, p2pProvider.Encoding(), sidecar)
if err != nil && err.Error() != mux.ErrReset.Error() {
require.NoError(t, err)
}
}
}
}
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
pcl := fmt.Sprintf("%s/ssz_snappy", p2p.RPCBlobsSidecarsByRangeTopicV1)
p2.SetStreamHandler(pcl, blobsProvider(p2))
chain := &mock.ChainService{Genesis: time.Now(), ValidatorsRoot: [32]byte{}}
sidecars, err := SendBlobsSidecarsByRangeRequest(ctx, chain, p1, p2.PeerID(), req, nil)
assert.NoError(t, err)
assert.Equal(t, req.Count, uint64(len(sidecars)))
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/network/forks"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
)
@@ -53,6 +54,13 @@ func WriteBlockChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher
return err
}
obtainedCtx = digest[:]
case version.EIP4844:
valRoot := chain.GenesisValidatorsRoot()
digest, err := forks.ForkDigestFromEpoch(params.BeaconConfig().Eip4844ForkEpoch, valRoot[:])
if err != nil {
return err
}
obtainedCtx = digest[:]
}
if err := writeContextToStream(obtainedCtx, stream, chain); err != nil {
@@ -73,6 +81,57 @@ func ReadChunkedBlock(stream libp2pcore.Stream, chain blockchain.ForkFetcher, p2
return readResponseChunk(stream, chain, p2p)
}
// WriteBlobsChunk writes blobs chunk object to stream.
// response_chunk ::= <result> | <context-bytes> | <encoding-dependent-header> | <encoded-payload>
func WriteBlobsSidecarChunk(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher, encoding encoder.NetworkEncoding, blobs *ethpb.BlobsSidecar) error {
if _, err := stream.Write([]byte{responseCodeSuccess}); err != nil {
return err
}
valRoot := chain.GenesisValidatorsRoot()
ctxBytes, err := forks.ForkDigestFromEpoch(params.BeaconConfig().Eip4844ForkEpoch, valRoot[:])
if err != nil {
return err
}
if err := writeContextToStream(ctxBytes[:], stream, chain); err != nil {
return err
}
_, err = encoding.EncodeWithMaxLength(stream, blobs)
return err
}
func ReadChunkedBlobsSidecar(stream libp2pcore.Stream, chain blockchain.ChainInfoFetcher, p2p p2p.P2P, isFirstChunk bool) (*ethpb.BlobsSidecar, error) {
var (
code uint8
errMsg string
err error
)
if isFirstChunk {
code, errMsg, err = ReadStatusCode(stream, p2p.Encoding())
} else {
SetStreamReadDeadline(stream, respTimeout)
code, errMsg, err = readStatusCodeNoDeadline(stream, p2p.Encoding())
}
if err != nil {
return nil, err
}
if code != 0 {
return nil, errors.New(errMsg)
}
// No-op for now with the rpc context.
rpcCtx, err := readContextFromStream(stream, chain)
if err != nil {
return nil, err
}
// blobs sidecars use v1
if len(rpcCtx) != 0 {
return nil, errors.New("unexpected fork digest in stream")
}
sidecar := new(ethpb.BlobsSidecar)
err = p2p.Encoding().DecodeWithMaxLength(stream, sidecar)
return sidecar, err
}
// readFirstChunkedBlock reads the first chunked block and applies the appropriate deadlines to
// it.
func readFirstChunkedBlock(stream libp2pcore.Stream, chain blockchain.ForkFetcher, p2p p2p.EncodingProvider) (interfaces.SignedBeaconBlock, error) {

View File

@@ -51,10 +51,12 @@ const seenExitSize = 100
const seenProposerSlashingSize = 100
const badBlockSize = 1000
const syncMetricsInterval = 10 * time.Second
const seenBlobsSidecarSize = 1000
var (
// Seconds in one epoch.
pendingBlockExpTime = time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second
pendingBlockExpTime = time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second
pendingSidecarExpTime = time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second
// time to allow processing early blocks.
earlyBlockProcessingTolerance = slots.MultiplySlotBy(2)
// time to allow processing early attestations.
@@ -107,7 +109,9 @@ type Service struct {
ctx context.Context
cancel context.CancelFunc
slotToPendingBlocks *gcache.Cache
slotToPendingSidecars *gcache.Cache
seenPendingBlocks map[[32]byte]bool
seenPendingSidecars map[[32]byte]bool
blkRootToPendingAtts map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof
subHandler *subTopicHandler
pendingAttsLock sync.RWMutex
@@ -135,22 +139,27 @@ type Service struct {
badBlockLock sync.RWMutex
syncContributionBitsOverlapLock sync.RWMutex
syncContributionBitsOverlapCache *lru.Cache
seenBlobsSidecarLock sync.RWMutex
seenBlobsSidecarCache *lru.Cache
signatureChan chan *signatureVerifier
}
// NewService initializes new regular sync service.
func NewService(ctx context.Context, opts ...Option) *Service {
c := gcache.New(pendingBlockExpTime /* exp time */, 2*pendingBlockExpTime /* prune time */)
ctx, cancel := context.WithCancel(ctx)
c := gcache.New(pendingBlockExpTime /* exp time */, 2*pendingBlockExpTime /* prune time */)
sidecarCache := gcache.New(pendingSidecarExpTime /* exp time */, 2*pendingSidecarExpTime /* prune time */)
r := &Service{
ctx: ctx,
cancel: cancel,
chainStarted: abool.New(),
cfg: &config{},
slotToPendingBlocks: c,
seenPendingBlocks: make(map[[32]byte]bool),
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
signatureChan: make(chan *signatureVerifier, verifierLimit),
ctx: ctx,
cancel: cancel,
chainStarted: abool.New(),
cfg: &config{},
slotToPendingBlocks: c,
slotToPendingSidecars: sidecarCache,
seenPendingBlocks: make(map[[32]byte]bool),
seenPendingSidecars: make(map[[32]byte]bool),
blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
signatureChan: make(chan *signatureVerifier, verifierLimit),
}
for _, opt := range opts {
if err := opt(r); err != nil {
@@ -227,6 +236,7 @@ func (s *Service) initCaches() {
s.seenAttesterSlashingCache = make(map[uint64]bool)
s.seenProposerSlashingCache = lruwrpr.New(seenProposerSlashingSize)
s.badBlockCache = lruwrpr.New(badBlockSize)
s.seenBlobsSidecarCache = lruwrpr.New(seenBlobsSidecarSize)
}
func (s *Service) registerHandlers() {

View File

@@ -118,6 +118,15 @@ func (s *Service) registerSubscribers(epoch types.Epoch, digest [4]byte) {
)
}
}
// EIP-4844 Fork Version
if epoch >= params.BeaconConfig().Eip4844ForkEpoch {
s.subscribe(
p2p.BlobsSubnetTopicFormat,
s.validateBlobsSidecarPubSub,
s.blobsSidecarSubscriber,
digest,
)
}
}
// subscribe to a given topic with a given validator and subscription handler.

View File

@@ -6,6 +6,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition/interop"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"google.golang.org/protobuf/proto"
@@ -29,6 +30,39 @@ func (s *Service) beaconBlockSubscriber(ctx context.Context, msg proto.Message)
return err
}
var sidecar *ethpb.BlobsSidecar
contains, err := blobs.BlockContainsKZGs(block)
if err != nil {
return err
}
if contains {
slot := block.Slot()
s.pendingQueueLock.RLock()
sidecars := s.pendingSidecarsInCache(slot)
s.pendingQueueLock.RUnlock()
queuedSidecar := findSidecarForBlock(block, root, sidecars)
if queuedSidecar == nil {
// re-schedule block to be processed later.
// TODO(XXX): This is a bit inefficient as the block will be validated again
s.pendingQueueLock.Lock()
if err := s.insertBlockToPendingQueue(slot, signed, root); err != nil {
s.pendingQueueLock.Unlock()
return err
}
s.pendingQueueLock.Unlock()
return nil
}
sidecar = queuedSidecar.s
if sidecar != nil {
if err := signed.SetSideCar(&ethpb.SignedBlobsSidecar{
Message: sidecar,
}); err != nil {
return err
}
}
}
if err := s.cfg.chain.ReceiveBlock(ctx, signed, root); err != nil {
if blockchain.IsInvalidBlock(err) {
r := blockchain.InvalidBlockRoot(err)

View File

@@ -0,0 +1,26 @@
package sync
import (
"context"
"fmt"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"google.golang.org/protobuf/proto"
)
func (s *Service) blobsSidecarSubscriber(ctx context.Context, msg proto.Message) error {
m, ok := msg.(*ethpb.SignedBlobsSidecar)
if !ok {
return fmt.Errorf("message was not type *eth.SignedBlobsSidecar, type=%T", msg)
}
if m == nil {
return errors.New("nil blobs sidecar message")
}
// Sidecars are handled by the block queue processing routine
s.pendingQueueLock.Lock()
s.insertSidecarToPendingQueue(&queuedBlobsSidecar{m.Message, m.Signature, true})
s.pendingQueueLock.Unlock()
return nil
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
"github.com/protolambda/go-kzg/bls"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed"
blockfeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/block"
@@ -16,11 +17,13 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/config/features"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blobs"
consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
prysmTime "github.com/prysmaticlabs/prysm/v3/time"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/sirupsen/logrus"
@@ -244,7 +247,7 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.Signed
return errors.New("incorrect proposer index")
}
if err = s.validateBellatrixBeaconBlock(ctx, parentState, blk.Block()); err != nil {
if err = s.validateEIP4844BeaconBlock(ctx, parentState, blk.Block()); err != nil {
if errors.Is(err, ErrOptimisticParent) {
return err
}
@@ -255,6 +258,59 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.Signed
return nil
}
// validateEIP4844BeaconBlock validates the block for the EIP-4844 fork.
// In addition to the spec for validateBellatrixBeaconBlock:
// [REJECT] The KZG commitments of the blobs are all correctly encoded compressed BLS G1 Points.
// -- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzgs)`
// [REJECT] The KZG commitments correspond to the versioned hashes in the transactions list.
// -- i.e. `verify_kzgs_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzgs)`
func (s *Service) validateEIP4844BeaconBlock(ctx context.Context, parentState state.BeaconState, blk interfaces.BeaconBlock) error {
if err := s.validateBellatrixBeaconBlock(ctx, parentState, blk); err != nil {
return err
}
body := blk.Body()
executionEnabled, err := blocks.IsExecutionEnabled(parentState, body)
if err != nil {
return err
}
if !executionEnabled {
return nil
}
payload, err := body.Execution()
if err != nil {
return err
}
if payload == nil {
return errors.New("execution payload is nil")
}
if consensusblocks.IsPreEIP4844Version(blk.Version()) {
return nil
}
blobKzgs, err := body.BlobKzgs()
if err != nil {
return err
}
blobKzgsInput := make([][48]byte, len(blobKzgs))
for i := range blobKzgs {
if len(blobKzgs[i]) != 48 {
return errors.New("invalid blob kzg length")
}
if _, err := bls.FromCompressedG1(blobKzgs[i]); err != nil {
return errors.Wrap(err, "invalid blob kzg encoding")
}
blobKzgsInput[i] = bytesutil.ToBytes48(blobKzgs[i])
}
txs, err := payload.Transactions()
if err != nil {
return err
}
return blobs.VerifyKzgsAgainstTxs(txs, blobKzgsInput)
}
// validateBellatrixBeaconBlock validates the block for the Bellatrix fork.
// spec code:
//
@@ -269,8 +325,13 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.Signed
// [IGNORE] The block's parent (defined by block.parent_root) passes all validation (including execution
// node verification of the block.body.execution_payload).
func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState state.BeaconState, blk interfaces.BeaconBlock) error {
// Error if block and state are not the same version
if parentState.Version() != blk.Version() {
sv := parentState.Version()
bv := blk.Version()
switch {
case sv == bv:
case sv == version.Bellatrix && bv == version.EIP4844:
// The EIP-4844 BeaconState is the same as Bellatrix's
default:
return errors.New("block and state are not the same version")
}

View File

@@ -1,6 +1,7 @@
package sync
import (
"bufio"
"bytes"
"context"
"errors"
@@ -9,9 +10,12 @@ import (
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
pubsub "github.com/libp2p/go-libp2p-pubsub"
pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb"
gcache "github.com/patrickmn/go-cache"
"github.com/protolambda/ztyp/codec"
"github.com/prysmaticlabs/prysm/v3/async/abool"
mock "github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
@@ -1394,3 +1398,171 @@ func Test_getBlockFields(t *testing.T) {
require.LogsContain(t, hook, "nil block")
require.LogsContain(t, hook, "bad block")
}
func TestValidateBeaconBlockPubSub_ValidBlobKzgs(t *testing.T) {
db := dbtest.SetupDB(t)
p := p2ptest.NewTestP2P(t)
ctx := context.Background()
beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, 100)
parentBlock := util.HydrateEIP4844SignedBeaconBlock(&ethpb.SignedBeaconBlockWithBlobKZGs{})
signedParentBlock, err := blocks.NewSignedBeaconBlock(parentBlock)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, signedParentBlock))
bRoot, err := parentBlock.Block.HashTreeRoot()
require.NoError(t, err)
presentTime := time.Now().Unix()
require.NoError(t, beaconState.SetGenesisTime(uint64(presentTime)))
require.NoError(t, db.SaveState(ctx, beaconState, bRoot))
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: bRoot[:]}))
copied := beaconState.Copy()
require.NoError(t, copied.SetSlot(1))
proposerIdx, err := helpers.BeaconProposerIndex(ctx, copied)
require.NoError(t, err)
msg := util.HydrateEIP4844SignedBeaconBlock(&ethpb.SignedBeaconBlockWithBlobKZGs{})
msg.Block.ParentRoot = bRoot[:]
msg.Block.Slot = 1
msg.Block.ProposerIndex = proposerIdx
msg.Block.Body.ExecutionPayload.Timestamp = uint64(presentTime) + params.BeaconConfig().SecondsPerSlot
msg.Block.Body.ExecutionPayload.GasUsed = 10
msg.Block.Body.ExecutionPayload.GasLimit = 11
msg.Block.Body.ExecutionPayload.BlockHash = bytesutil.PadTo([]byte("blockHash"), 32)
msg.Block.Body.ExecutionPayload.ParentHash = bytesutil.PadTo([]byte("parentHash"), 32)
blob := gethTypes.Blob{[32]byte{0x10, 0x11}}
commitment, ok := blob.ComputeCommitment()
require.Equal(t, ok, true)
sbt := gethTypes.SignedBlobTx{
Message: gethTypes.BlobTxMessage{
BlobVersionedHashes: []common.Hash{commitment.ComputeVersionedHash()},
},
}
var txbuf bytes.Buffer
w := bufio.NewWriter(&txbuf)
encW := codec.NewEncodingWriter(w)
require.NoError(t, sbt.Serialize(encW))
require.NoError(t, w.Flush())
var tx []byte
tx = append(tx, gethTypes.BlobTxType)
tx = append(tx, txbuf.Bytes()...)
msg.Block.Body.ExecutionPayload.Transactions = append(msg.Block.Body.ExecutionPayload.Transactions, tx)
msg.Block.Body.BlobKzgs = [][]byte{commitment[:]}
msg.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
stateGen := stategen.New(db)
chainService := &mock.ChainService{Genesis: time.Unix(presentTime-int64(params.BeaconConfig().SecondsPerSlot), 0),
DB: db,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
topic := p2p.GossipTypeMapping[reflect.TypeOf(msg)]
genesisValidatorsRoot := r.cfg.chain.GenesisValidatorsRoot()
eip4844Digest, err := signing.ComputeForkDigest(params.BeaconConfig().Eip4844ForkVersion, genesisValidatorsRoot[:])
require.NoError(t, err)
topic = r.addDigestToTopic(topic, eip4844Digest)
m := &pubsub.Message{
Message: &pubsubpb.Message{
Data: buf.Bytes(),
Topic: &topic,
},
}
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
require.NoError(t, err)
require.Equal(t, pubsub.ValidationAccept, res)
}
func TestValidateBeaconBlockPubSub_InvalidBlobKzgs(t *testing.T) {
db := dbtest.SetupDB(t)
p := p2ptest.NewTestP2P(t)
ctx := context.Background()
beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, 100)
parentBlock := util.HydrateEIP4844SignedBeaconBlock(&ethpb.SignedBeaconBlockWithBlobKZGs{})
signedParentBlock, err := blocks.NewSignedBeaconBlock(parentBlock)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(ctx, signedParentBlock))
bRoot, err := parentBlock.Block.HashTreeRoot()
require.NoError(t, err)
presentTime := time.Now().Unix()
require.NoError(t, beaconState.SetGenesisTime(uint64(presentTime)))
require.NoError(t, db.SaveState(ctx, beaconState, bRoot))
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: bRoot[:]}))
copied := beaconState.Copy()
require.NoError(t, copied.SetSlot(1))
proposerIdx, err := helpers.BeaconProposerIndex(ctx, copied)
require.NoError(t, err)
msg := util.HydrateEIP4844SignedBeaconBlock(&ethpb.SignedBeaconBlockWithBlobKZGs{})
msg.Block.ParentRoot = bRoot[:]
msg.Block.Slot = 1
msg.Block.ProposerIndex = proposerIdx
msg.Block.Body.ExecutionPayload.Timestamp = uint64(presentTime) + params.BeaconConfig().SecondsPerSlot
msg.Block.Body.ExecutionPayload.GasUsed = 10
msg.Block.Body.ExecutionPayload.GasLimit = 11
msg.Block.Body.ExecutionPayload.BlockHash = bytesutil.PadTo([]byte("blockHash"), 32)
msg.Block.Body.ExecutionPayload.ParentHash = bytesutil.PadTo([]byte("parentHash"), 32)
msg.Block.Body.ExecutionPayload.Transactions = append(msg.Block.Body.ExecutionPayload.Transactions, []byte("transaction 1"))
msg.Block.Body.ExecutionPayload.Transactions = append(msg.Block.Body.ExecutionPayload.Transactions, []byte("transaction 2"))
kzg := [48]byte{'a'}
msg.Block.Body.BlobKzgs = [][]byte{kzg[:]}
msg.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
stateGen := stategen.New(db)
chainService := &mock.ChainService{Genesis: time.Unix(presentTime-int64(params.BeaconConfig().SecondsPerSlot), 0),
DB: db,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
topic := p2p.GossipTypeMapping[reflect.TypeOf(msg)]
genesisValidatorsRoot := r.cfg.chain.GenesisValidatorsRoot()
eip4844Digest, err := signing.ComputeForkDigest(params.BeaconConfig().Eip4844ForkVersion, genesisValidatorsRoot[:])
require.NoError(t, err)
topic = r.addDigestToTopic(topic, eip4844Digest)
m := &pubsub.Message{
Message: &pubsubpb.Message{
Data: buf.Bytes(),
Topic: &topic,
},
}
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.Equal(t, pubsub.ValidationReject, res, "block with invalid parent should be ignored")
require.ErrorContains(t, "invalid blob kzg encoding", err)
}

View File

@@ -0,0 +1,238 @@
package sync
import (
"context"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
"github.com/pkg/errors"
kbls "github.com/protolambda/go-kzg/bls"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/altair"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/crypto/bls"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/monitoring/tracing"
"github.com/prysmaticlabs/prysm/v3/network/forks"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/time/slots"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
// Gossip Validation Conditions:
// [IGNORE] the sidecar.beacon_block_slot is for the current slot (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance)
// -- i.e. blobs_sidecar.beacon_block_slot == current_slot.
// [REJECT] the sidecar.blobs are all well formatted, i.e. the BLSFieldElement in valid range (x < BLS_MODULUS).
// [REJECT] the beacon proposer signature, signed_blobs_sidecar.signature, is valid
// [IGNORE] The sidecar is the first sidecar with valid signature received for the (proposer_index, sidecar.beacon_block_slot)
// combination, where proposer_index is the validator index of the beacon block proposer of blobs_sidecar.beacon_block_slot
func (s *Service) validateBlobsSidecarPubSub(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
// Accept the sidecar if it came from itself.
if pid == s.cfg.p2p.PeerID() {
return pubsub.ValidationAccept, nil
}
ctx, span := trace.StartSpan(ctx, "sync.validateBlobsSidecar")
defer span.End()
// Ignore the sidecar if the beacon node is syncing.
if s.cfg.initialSync.Syncing() {
return pubsub.ValidationIgnore, nil
}
m, err := s.decodePubsubMessage(msg)
if err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationReject, errors.Wrap(err, "Could not decode message")
}
signed, ok := m.(*ethpb.SignedBlobsSidecar)
if !ok {
return pubsub.ValidationReject, errWrongMessage
}
if signed.Message == nil {
return pubsub.ValidationReject, errors.New("nil sidecar message")
}
if signed.Signature == nil {
return pubsub.ValidationReject, errors.New("nil sidecar signature")
}
if signed.Message.BeaconBlockRoot == nil || signed.Message.Blobs == nil {
return pubsub.ValidationReject, errors.New("nil sidecar message data")
}
if s.cfg.beaconDB.HasBlobsSidecar(ctx, bytesutil.ToBytes32(signed.Message.BeaconBlockRoot)) {
return pubsub.ValidationIgnore, nil
}
if err := altair.ValidateSyncMessageTime(signed.Message.BeaconBlockSlot, s.cfg.chain.GenesisTime(), params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationIgnore, err
}
// Ensure that the sidecar isn't associated with an invalid block
if s.hasBadBlock(bytesutil.ToBytes32(signed.Message.BeaconBlockRoot)) {
return pubsub.ValidationReject, errors.New("sidecar references bad block root")
}
s.pendingQueueLock.RLock()
if s.seenPendingSidecars[bytesutil.ToBytes32(signed.Message.BeaconBlockRoot)] {
s.pendingQueueLock.RUnlock()
return pubsub.ValidationIgnore, nil
}
s.pendingQueueLock.RUnlock()
if err := validateBlobFr(signed.Message.Blobs); err != nil {
log.WithError(err).WithField("slot", signed.Message.BeaconBlockSlot).Debug("Sidecar contains invalid BLS field elements")
return pubsub.ValidationReject, err
}
blk, err := s.getPendingBlockForSidecar(signed.Message)
if err != nil {
log.WithError(err).WithField("slot", signed.Message.BeaconBlockSlot).Warn("Failed to lookup pending block in queue")
return pubsub.ValidationIgnore, err
}
if blk == nil || blk.IsNil() {
// We expect the block including this sidecar to follow shortly. Add the sidecar the queue so the pending block processor can readily retrieve it
s.pendingQueueLock.Lock()
s.insertSidecarToPendingQueue(&queuedBlobsSidecar{signed.Message, signed.Signature, false})
s.pendingQueueLock.Unlock()
return pubsub.ValidationIgnore, nil
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
log.WithError(err).WithField("slot", signed.Message.BeaconBlockSlot).Warn("Nil block found in pending queue")
return pubsub.ValidationIgnore, nil
}
validationResult, err := s.validateBlobsSidecar(ctx, blk, signed)
if err != nil {
tracing.AnnotateError(span, err)
return validationResult, err
}
if s.hasSeenBlobsSidecarIndexSlot(blk.Block().ProposerIndex(), signed.Message.BeaconBlockSlot) {
return pubsub.ValidationIgnore, nil
}
s.setSeenSidecarIndexSlot(blk.Block().ProposerIndex(), signed.Message.BeaconBlockSlot)
msg.ValidatorData = signed
span.AddAttributes(trace.Int64Attribute("numBlobs", int64(len(signed.Message.Blobs))))
log.WithFields(logrus.Fields{
"blockSlot": signed.Message.BeaconBlockRoot,
"blockRoot": signed.Message.BeaconBlockRoot,
"numBlobs": len(signed.Message.Blobs),
}).Debug("Received sidecar")
return pubsub.ValidationAccept, nil
}
func (s *Service) validateBlobsSidecar(ctx context.Context, blk interfaces.SignedBeaconBlock, m *ethpb.SignedBlobsSidecar) (pubsub.ValidationResult, error) {
return s.validateBlobsSidecarSignature(ctx, blk, m)
}
func (s *Service) validateBlobsSidecarSignature(ctx context.Context, blk interfaces.SignedBeaconBlock, m *ethpb.SignedBlobsSidecar) (pubsub.ValidationResult, error) {
ctx, span := trace.StartSpan(ctx, "sync.validateBlobsSidecarSignature")
defer span.End()
currentEpoch := slots.ToEpoch(m.Message.BeaconBlockSlot)
fork, err := forks.Fork(currentEpoch)
if err != nil {
return pubsub.ValidationIgnore, err
}
state, err := s.cfg.stateGen.StateByRoot(ctx, bytesutil.ToBytes32(m.Message.BeaconBlockRoot))
if err != nil {
return pubsub.ValidationIgnore, err
}
proposer, err := state.ValidatorAtIndex(blk.Block().ProposerIndex())
if err != nil {
return pubsub.ValidationIgnore, err
}
proposerPubKey := proposer.PublicKey
blobSigning := &ethpb.BlobsSidecar{
BeaconBlockRoot: m.Message.BeaconBlockRoot,
BeaconBlockSlot: m.Message.BeaconBlockSlot,
Blobs: m.Message.Blobs,
AggregatedProof: m.Message.AggregatedProof,
}
domain, err := signing.Domain(fork, currentEpoch, params.BeaconConfig().DomainBlobsSidecar, state.GenesisValidatorsRoot())
if err != nil {
return pubsub.ValidationReject, err
}
pKey, err := bls.PublicKeyFromBytes(proposerPubKey)
if err != nil {
return pubsub.ValidationReject, err
}
sigRoot, err := signing.ComputeSigningRoot(blobSigning, domain)
if err != nil {
return pubsub.ValidationReject, err
}
set := &bls.SignatureBatch{
Messages: [][32]byte{sigRoot},
PublicKeys: []bls.PublicKey{pKey},
Signatures: [][]byte{m.Signature},
}
return s.validateWithBatchVerifier(ctx, "blobs sidecar signature", set)
}
func validateBlobFr(blobs []*enginev1.Blob) error {
for _, blob := range blobs {
for _, b := range blob.Blob {
if len(b) != 32 {
return errors.New("invalid blob field element size")
}
if !kbls.ValidFr(bytesutil.ToBytes32(b)) {
return errors.New("invalid blob field element")
}
}
}
return nil
}
func (s *Service) hasSeenBlobsSidecarIndexSlot(proposerIndex types.ValidatorIndex, slot types.Slot) bool {
s.seenBlobsSidecarLock.RLock()
defer s.seenBlobsSidecarLock.RUnlock()
b := append(bytesutil.Bytes32(uint64(proposerIndex)), bytesutil.Bytes32(uint64(slot))...)
_, seen := s.seenBlobsSidecarCache.Get(string(b))
return seen
}
func (s *Service) setSeenSidecarIndexSlot(proposerIndex types.ValidatorIndex, slot types.Slot) {
s.seenBlobsSidecarLock.Lock()
defer s.seenBlobsSidecarLock.Unlock()
b := append(bytesutil.Bytes32(uint64(proposerIndex)), bytesutil.Bytes32(uint64(slot))...)
s.seenBlobsSidecarCache.Add(string(b), true)
}
func (s *Service) getPendingBlockForSidecar(sc *ethpb.BlobsSidecar) (interfaces.SignedBeaconBlock, error) {
blkRoot := bytesutil.ToBytes32(sc.BeaconBlockRoot)
s.pendingQueueLock.RLock()
if !s.seenPendingBlocks[blkRoot] {
s.pendingQueueLock.RUnlock()
return nil, nil
}
blks := s.pendingBlocksInCache(sc.BeaconBlockSlot)
s.pendingQueueLock.RUnlock()
for _, b := range blks {
if b.Block().Slot() != sc.BeaconBlockSlot {
continue
}
r, err := b.Block().HashTreeRoot()
if err != nil {
return nil, err
}
if r != bytesutil.ToBytes32(sc.BeaconBlockRoot) {
continue
}
return b, nil
}
return nil, nil
}

View File

@@ -161,6 +161,18 @@ var (
Usage: "The amount of blocks the local peer is bounded to request and respond to in a batch.",
Value: 64,
}
// BlobsTransferRate specifies the bytes/sec transfer rate of requested blobs.
BlobsTransferRate = &cli.IntFlag{
Name: "blobs-transfer-rate",
Usage: "The bytes/sec transfer rate of requested blobs.",
Value: 1 << 20, // 1 MiB
}
// BlobsTransferRateThresh specifies the maximum bytes per second that can be transferred.
BlobsTransferRateThresh = &cli.IntFlag{
Name: "blobs-transfer-rate-thresh",
Usage: "The maximum bytes per second that can be transferred.",
Value: 1 << 23, // 8 MiB
}
// BlockBatchLimitBurstFactor specifies the factor by which block batch size may increase.
BlockBatchLimitBurstFactor = &cli.IntFlag{
Name: "block-batch-limit-burst-factor",

View File

@@ -13,6 +13,8 @@ type GlobalFlags struct {
MinimumPeersPerSubnet int
BlockBatchLimit int
BlockBatchLimitBurstFactor int
BlobsTransferRate int
BlobsTransferRateThresh int
}
var globalConfig *GlobalFlags
@@ -38,8 +40,11 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
log.Warn("Subscribing to All Attestation Subnets")
cfg.SubscribeToAllSubnets = true
}
// TODO(EIP-4844): assert BlockBatchLimit < MAX_REQUEST_BLOBS_SIDECARS (128) as initial-sync uses this same config for sidecar fetch
cfg.BlockBatchLimit = ctx.Int(BlockBatchLimit.Name)
cfg.BlockBatchLimitBurstFactor = ctx.Int(BlockBatchLimitBurstFactor.Name)
cfg.BlobsTransferRate = ctx.Int(BlobsTransferRate.Name)
cfg.BlobsTransferRateThresh = ctx.Int(BlobsTransferRateThresh.Name)
cfg.MinimumPeersPerSubnet = ctx.Int(MinPeersPerSubnet.Name)
configureMinimumPeers(ctx, cfg)

View File

@@ -55,6 +55,8 @@ var appFlags = []cli.Flag{
flags.SetGCPercent,
flags.BlockBatchLimit,
flags.BlockBatchLimitBurstFactor,
flags.BlobsTransferRate,
flags.BlobsTransferRateThresh,
flags.InteropMockEth1DataVotesFlag,
flags.InteropGenesisStateFlag,
flags.InteropNumValidatorsFlag,

View File

@@ -114,6 +114,8 @@ var appHelpFlagGroups = []flagGroup{
flags.SlotsPerArchivedPoint,
flags.BlockBatchLimit,
flags.BlockBatchLimitBurstFactor,
flags.BlobsTransferRate,
flags.BlobsTransferRateThresh,
flags.EnableDebugRPCEndpoints,
flags.SubscribeToAllSubnets,
flags.HistoricalSlasherNode,

View File

@@ -30,6 +30,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -67,6 +68,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -101,6 +103,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -132,6 +135,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -169,6 +173,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {

View File

@@ -25,6 +25,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -53,6 +54,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {

View File

@@ -36,6 +36,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -69,6 +70,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {
@@ -101,6 +103,7 @@ var Commands = &cli.Command{
features.PraterTestnet,
features.RopstenTestnet,
features.SepoliaTestnet,
features.EIP4844Testnet,
cmd.AcceptTosFlag,
}),
Before: func(cliCtx *cli.Context) error {

View File

@@ -135,6 +135,12 @@ func configureTestnet(ctx *cli.Context) error {
}
applySepoliaFeatureFlags(ctx)
params.UseSepoliaNetworkConfig()
} else if ctx.Bool(EIP4844Testnet.Name) {
log.Warn("Running on the EIP-4844 Beacon Chain Testnet")
if err := params.SetActive(params.EIP4844Config().Copy()); err != nil {
return err
}
params.UseEIP4844NetworkConfig()
} else {
if ctx.IsSet(cmd.ChainConfigFileFlag.Name) {
log.Warn("Running on custom Ethereum network specified in a chain configuration yaml file")

View File

@@ -23,6 +23,10 @@ var (
Name: "sepolia",
Usage: "Run Prysm configured for the Sepolia beacon chain test network",
}
EIP4844Testnet = &cli.BoolFlag{
Name: "eip4844",
Usage: "Run Prysm configured for the EIP-4844 (proto-danksharding) beacon chain test network",
}
// Mainnet flag for easier tooling, no-op
Mainnet = &cli.BoolFlag{
Value: true,
@@ -136,6 +140,7 @@ var ValidatorFlags = append(deprecatedFlags, []cli.Flag{
PraterTestnet,
RopstenTestnet,
SepoliaTestnet,
EIP4844Testnet,
Mainnet,
dynamicKeyReloadDebounceInterval,
attestTimely,
@@ -156,6 +161,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
PraterTestnet,
RopstenTestnet,
SepoliaTestnet,
EIP4844Testnet,
Mainnet,
disablePeerScorer,
disableBroadcastSlashingFlag,

View File

@@ -15,6 +15,7 @@ go_library(
"minimal_config.go",
"network_config.go",
"testnet_e2e_config.go",
"testnet_eip4844_config.go",
"testnet_prater_config.go",
"testnet_ropsten_config.go",
"testnet_sepolia_config.go",

View File

@@ -111,6 +111,7 @@ type BeaconChainConfig struct {
DomainSyncCommittee [4]byte `yaml:"DOMAIN_SYNC_COMMITTEE" spec:"true"` // DomainVoluntaryExit defines the BLS signature domain for sync committee.
DomainSyncCommitteeSelectionProof [4]byte `yaml:"DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF" spec:"true"` // DomainSelectionProof defines the BLS signature domain for sync committee selection proof.
DomainContributionAndProof [4]byte `yaml:"DOMAIN_CONTRIBUTION_AND_PROOF" spec:"true"` // DomainAggregateAndProof defines the BLS signature domain for contribution and proof.
DomainBlobsSidecar [4]byte `yaml:"DOMAIN_BLOBS_SIDECAR" spec:"true"` // DomainBlobsSidecar defines the BLS signature domain.
DomainApplicationMask [4]byte `yaml:"DOMAIN_APPLICATION_MASK" spec:"true"` // DomainApplicationMask defines the BLS signature domain for application mask.
DomainApplicationBuilder [4]byte // DomainApplicationBuilder defines the BLS signature domain for application builder.
DomainBLSToExecutionChange [4]byte // DomainBLSToExecutionChange defines the BLS signature domain to change withdrawal addresses to ETH1 prefix
@@ -147,6 +148,8 @@ type BeaconChainConfig struct {
BellatrixForkEpoch types.Epoch `yaml:"BELLATRIX_FORK_EPOCH" spec:"true"` // BellatrixForkEpoch is used to represent the assigned fork epoch for bellatrix.
ShardingForkVersion []byte `yaml:"SHARDING_FORK_VERSION" spec:"true"` // ShardingForkVersion is used to represent the fork version for sharding.
ShardingForkEpoch types.Epoch `yaml:"SHARDING_FORK_EPOCH" spec:"true"` // ShardingForkEpoch is used to represent the assigned fork epoch for sharding.
Eip4844ForkVersion []byte `yaml:"EIP4844_FORK_VERSION" spec:"true"` // Eip4844ForkVersion is used to represent the fork version for Eip4844.
Eip4844ForkEpoch types.Epoch `yaml:"EIP4844_FORK_EPOCH" spec:"true"` // Eip4844ForkEpoch is used to represent the assigned fork epoch for Eip4844.
CapellaForkVersion []byte `yaml:"CAPELLA_FORK_VERSION" spec:"true"` // CapellaForkVersion is used to represent the fork version for capella.
CapellaForkEpoch types.Epoch `yaml:"CAPELLA_FORK_EPOCH" spec:"true"` // CapellaForkEpoch is used to represent the assigned fork epoch for capella.
@@ -220,6 +223,8 @@ func configForkSchedule(b *BeaconChainConfig) map[[fieldparams.VersionLength]byt
fvs[bytesutil.ToBytes4(b.AltairForkVersion)] = b.AltairForkEpoch
// Set Bellatrix fork data.
fvs[bytesutil.ToBytes4(b.BellatrixForkVersion)] = b.BellatrixForkEpoch
// Set EIP4844 fork data.
fvs[bytesutil.ToBytes4(b.Eip4844ForkVersion)] = b.Eip4844ForkEpoch
return fvs
}
@@ -231,5 +236,7 @@ func configForkNames(b *BeaconChainConfig) map[[fieldparams.VersionLength]byte]s
fvn[bytesutil.ToBytes4(b.AltairForkVersion)] = "altair"
// Set Bellatrix fork data.
fvn[bytesutil.ToBytes4(b.BellatrixForkVersion)] = "bellatrix"
// Set EIP4844 fork data.
fvn[bytesutil.ToBytes4(b.Eip4844ForkVersion)] = "eip4844"
return fvn
}

View File

@@ -9,6 +9,7 @@ func init() {
E2EMainnetTestConfig(),
InteropConfig(),
RopstenConfig(),
EIP4844Config(),
}
configs = newConfigset(defaults...)
// ensure that main net is always present and active by default

View File

@@ -9,7 +9,8 @@ func InteropConfig() *BeaconChainConfig {
c.GenesisForkVersion = []byte{0, 0, 0, 235}
c.AltairForkVersion = []byte{1, 0, 0, 235}
c.BellatrixForkVersion = []byte{2, 0, 0, 235}
c.ShardingForkVersion = []byte{3, 0, 0, 235}
c.Eip4844ForkVersion = []byte{3, 0, 0, 235}
c.ShardingForkVersion = []byte{4, 0, 0, 235}
c.InitializeForkSchedule()
return c

View File

@@ -194,6 +194,8 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
fmt.Sprintf("ALTAIR_FORK_VERSION: %#x", cfg.AltairForkVersion),
fmt.Sprintf("BELLATRIX_FORK_EPOCH: %d", cfg.BellatrixForkEpoch),
fmt.Sprintf("BELLATRIX_FORK_VERSION: %#x", cfg.BellatrixForkVersion),
fmt.Sprintf("EIP4844_FORK_EPOCH: %d", cfg.Eip4844ForkEpoch),
fmt.Sprintf("EIP4844_FORK_VERSION: %#x", cfg.Eip4844ForkVersion),
fmt.Sprintf("SHARDING_FORK_EPOCH: %d", cfg.ShardingForkEpoch),
fmt.Sprintf("SHARDING_FORK_VERSION: %#x", cfg.ShardingForkVersion),
fmt.Sprintf("INACTIVITY_SCORE_BIAS: %d", cfg.InactivityScoreBias),

View File

@@ -5,6 +5,7 @@ import (
"time"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
)
@@ -21,28 +22,31 @@ const (
genesisForkEpoch = 0
// Altair Fork Epoch for mainnet config.
mainnetAltairForkEpoch = 74240 // Oct 27, 2021, 10:56:23am UTC
// Bellatrix Fork Epoch for mainnet config.
// Bellatrix Fork Epoch for mainnet config.q
mainnetBellatrixForkEpoch = 144896 // Sept 6, 2022, 11:34:47am UTC
mainnetEip4844ForkEpoch = math.MaxUint64
)
var mainnetNetworkConfig = &NetworkConfig{
GossipMaxSize: 1 << 20, // 1 MiB
GossipMaxSizeBellatrix: 10 * 1 << 20, // 10 MiB
MaxChunkSize: 1 << 20, // 1 MiB
MaxChunkSizeBellatrix: 10 * 1 << 20, // 10 MiB
AttestationSubnetCount: 64,
AttestationPropagationSlotRange: 32,
MaxRequestBlocks: 1 << 10, // 1024
TtfbTimeout: 5 * time.Second,
RespTimeout: 10 * time.Second,
MaximumGossipClockDisparity: 500 * time.Millisecond,
MessageDomainInvalidSnappy: [4]byte{00, 00, 00, 00},
MessageDomainValidSnappy: [4]byte{01, 00, 00, 00},
ETH2Key: "eth2",
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 11184524, // Note: contract was deployed in block 11052984 but no transactions were sent until 11184524.
GossipMaxSize: 1 << 20, // 1 MiB
GossipMaxSizeBellatrix: 10 * 1 << 20, // 10 MiB
MaxChunkSize: 1 << 20, // 1 MiB
MaxChunkSizeBellatrix: 10 * 1 << 20, // 10 MiB
AttestationSubnetCount: 64,
AttestationPropagationSlotRange: 32,
MaxRequestBlocks: 1 << 10, // 1024
MaxRequestBlobsSidecars: 2 << 7, // 128
MinEpochsForBlobsSidecarsRequest: types.Epoch(2 << 13), // 8192, 1.2 months
TtfbTimeout: 5 * time.Second,
RespTimeout: 10 * time.Second,
MaximumGossipClockDisparity: 500 * time.Millisecond,
MessageDomainInvalidSnappy: [4]byte{00, 00, 00, 00},
MessageDomainValidSnappy: [4]byte{01, 00, 00, 00},
ETH2Key: "eth2",
AttSubnetKey: "attnets",
SyncCommsSubnetKey: "syncnets",
MinimumPeersInSubnetSearch: 20,
ContractDeploymentBlock: 11184524, // Note: contract was deployed in block 11052984 but no transactions were sent until 11184524.
BootstrapNodes: []string{
// Teku team's bootnode
"enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
@@ -166,6 +170,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
DomainSyncCommittee: bytesutil.Uint32ToBytes4(0x07000000),
DomainSyncCommitteeSelectionProof: bytesutil.Uint32ToBytes4(0x08000000),
DomainContributionAndProof: bytesutil.Uint32ToBytes4(0x09000000),
DomainBlobsSidecar: bytesutil.Uint32ToBytes4(0x10000000),
DomainApplicationMask: bytesutil.Uint32ToBytes4(0x00000001),
DomainApplicationBuilder: bytesutil.Uint32ToBytes4(0x00000001),
DomainBLSToExecutionChange: bytesutil.Uint32ToBytes4(0x0A000000),
@@ -206,7 +211,9 @@ var mainnetBeaconConfig = &BeaconChainConfig{
BellatrixForkEpoch: mainnetBellatrixForkEpoch,
CapellaForkVersion: []byte{3, 0, 0, 0},
CapellaForkEpoch: math.MaxUint64,
ShardingForkVersion: []byte{4, 0, 0, 0},
Eip4844ForkVersion: []byte{4, 0, 0, 0},
Eip4844ForkEpoch: math.MaxUint64,
ShardingForkVersion: []byte{8, 0, 0, 0},
ShardingForkEpoch: math.MaxUint64,
// New values introduced in Altair hard fork 1.
@@ -272,15 +279,18 @@ func FillTestVersions(c *BeaconChainConfig, b byte) {
c.GenesisForkVersion = make([]byte, fieldparams.VersionLength)
c.AltairForkVersion = make([]byte, fieldparams.VersionLength)
c.BellatrixForkVersion = make([]byte, fieldparams.VersionLength)
c.Eip4844ForkVersion = make([]byte, fieldparams.VersionLength)
c.ShardingForkVersion = make([]byte, fieldparams.VersionLength)
c.GenesisForkVersion[fieldparams.VersionLength-1] = b
c.AltairForkVersion[fieldparams.VersionLength-1] = b
c.BellatrixForkVersion[fieldparams.VersionLength-1] = b
c.Eip4844ForkVersion[fieldparams.VersionLength-1] = b
c.ShardingForkVersion[fieldparams.VersionLength-1] = b
c.GenesisForkVersion[0] = 0
c.AltairForkVersion[0] = 1
c.BellatrixForkVersion[0] = 2
c.ShardingForkVersion[0] = 3
c.Eip4844ForkVersion[0] = 3
c.ShardingForkVersion[0] = 4
}

View File

@@ -87,7 +87,9 @@ func MinimalSpecConfig() *BeaconChainConfig {
minimalConfig.BellatrixForkEpoch = math.MaxUint64
minimalConfig.CapellaForkVersion = []byte{3, 0, 0, 1}
minimalConfig.CapellaForkEpoch = math.MaxUint64
minimalConfig.ShardingForkVersion = []byte{4, 0, 0, 1}
minimalConfig.Eip4844ForkVersion = []byte{4, 0, 0, 1}
minimalConfig.Eip4844ForkEpoch = math.MaxUint64
minimalConfig.ShardingForkVersion = []byte{8, 0, 0, 1}
minimalConfig.ShardingForkEpoch = math.MaxUint64
minimalConfig.SyncCommitteeSize = 32

View File

@@ -9,18 +9,20 @@ import (
// NetworkConfig defines the spec based network parameters.
type NetworkConfig struct {
GossipMaxSize uint64 `yaml:"GOSSIP_MAX_SIZE"` // GossipMaxSize is the maximum allowed size of uncompressed gossip messages.
GossipMaxSizeBellatrix uint64 `yaml:"GOSSIP_MAX_SIZE_BELLATRIX"` // GossipMaxSizeBellatrix is the maximum allowed size of uncompressed gossip messages after the bellatrix epoch.
MaxChunkSize uint64 `yaml:"MAX_CHUNK_SIZE"` // MaxChunkSize is the maximum allowed size of uncompressed req/resp chunked responses.
MaxChunkSizeBellatrix uint64 `yaml:"MAX_CHUNK_SIZE_BELLATRIX"` // MaxChunkSizeBellatrix is the maximum allowed size of uncompressed req/resp chunked responses after the bellatrix epoch.
AttestationSubnetCount uint64 `yaml:"ATTESTATION_SUBNET_COUNT"` // AttestationSubnetCount is the number of attestation subnets used in the gossipsub protocol.
AttestationPropagationSlotRange types.Slot `yaml:"ATTESTATION_PROPAGATION_SLOT_RANGE"` // AttestationPropagationSlotRange is the maximum number of slots during which an attestation can be propagated.
MaxRequestBlocks uint64 `yaml:"MAX_REQUEST_BLOCKS"` // MaxRequestBlocks is the maximum number of blocks in a single request.
TtfbTimeout time.Duration `yaml:"TTFB_TIMEOUT"` // TtfbTimeout is the maximum time to wait for first byte of request response (time-to-first-byte).
RespTimeout time.Duration `yaml:"RESP_TIMEOUT"` // RespTimeout is the maximum time for complete response transfer.
MaximumGossipClockDisparity time.Duration `yaml:"MAXIMUM_GOSSIP_CLOCK_DISPARITY"` // MaximumGossipClockDisparity is the maximum milliseconds of clock disparity assumed between honest nodes.
MessageDomainInvalidSnappy [4]byte `yaml:"MESSAGE_DOMAIN_INVALID_SNAPPY"` // MessageDomainInvalidSnappy is the 4-byte domain for gossip message-id isolation of invalid snappy messages.
MessageDomainValidSnappy [4]byte `yaml:"MESSAGE_DOMAIN_VALID_SNAPPY"` // MessageDomainValidSnappy is the 4-byte domain for gossip message-id isolation of valid snappy messages.
GossipMaxSize uint64 `yaml:"GOSSIP_MAX_SIZE"` // GossipMaxSize is the maximum allowed size of uncompressed gossip messages.
GossipMaxSizeBellatrix uint64 `yaml:"GOSSIP_MAX_SIZE_BELLATRIX"` // GossipMaxSizeBellatrix is the maximum allowed size of uncompressed gossip messages after the bellatrix epoch.
MaxChunkSize uint64 `yaml:"MAX_CHUNK_SIZE"` // MaxChunkSize is the maximum allowed size of uncompressed req/resp chunked responses.
MaxChunkSizeBellatrix uint64 `yaml:"MAX_CHUNK_SIZE_BELLATRIX"` // MaxChunkSizeBellatrix is the maximum allowed size of uncompressed req/resp chunked responses after the bellatrix epoch.
AttestationSubnetCount uint64 `yaml:"ATTESTATION_SUBNET_COUNT"` // AttestationSubnetCount is the number of attestation subnets used in the gossipsub protocol.
AttestationPropagationSlotRange types.Slot `yaml:"ATTESTATION_PROPAGATION_SLOT_RANGE"` // AttestationPropagationSlotRange is the maximum number of slots during which an attestation can be propagated.
MaxRequestBlocks uint64 `yaml:"MAX_REQUEST_BLOCKS"` // MaxRequestBlocks is the maximum number of blocks in a single request.
MaxRequestBlobsSidecars uint64 `yaml:"MAX_REQUEST_BLOBS_SIDECARS"` // MaxRequestBlobsSidecars is the maximum number of blobs sidecars in a single request.
MinEpochsForBlobsSidecarsRequest types.Epoch `yaml:"MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS"` // MinEpochsForBlobsSidecarsRequest is the minimum epoch range over which a node must serve blobs sidecars.
TtfbTimeout time.Duration `yaml:"TTFB_TIMEOUT"` // TtfbTimeout is the maximum time to wait for first byte of request response (time-to-first-byte).
RespTimeout time.Duration `yaml:"RESP_TIMEOUT"` // RespTimeout is the maximum time for complete response transfer.
MaximumGossipClockDisparity time.Duration `yaml:"MAXIMUM_GOSSIP_CLOCK_DISPARITY"` // MaximumGossipClockDisparity is the maximum milliseconds of clock disparity assumed between honest nodes.
MessageDomainInvalidSnappy [4]byte `yaml:"MESSAGE_DOMAIN_INVALID_SNAPPY"` // MessageDomainInvalidSnappy is the 4-byte domain for gossip message-id isolation of invalid snappy messages.
MessageDomainValidSnappy [4]byte `yaml:"MESSAGE_DOMAIN_VALID_SNAPPY"` // MessageDomainValidSnappy is the 4-byte domain for gossip message-id isolation of valid snappy messages.
// DiscoveryV5 Config
ETH2Key string // ETH2Key is the ENR key of the Ethereum consensus object in an enr.

View File

@@ -3,6 +3,7 @@ package params
const (
altairE2EForkEpoch = 6
bellatrixE2EForkEpoch = 8
eip4844E2EForkEpoch = 10
)
// E2ETestConfig retrieves the configurations made specifically for E2E testing.
@@ -33,6 +34,7 @@ func E2ETestConfig() *BeaconChainConfig {
// Fork Parameters.
e2eConfig.AltairForkEpoch = altairE2EForkEpoch
e2eConfig.BellatrixForkEpoch = bellatrixE2EForkEpoch
e2eConfig.Eip4844ForkEpoch = eip4844E2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "616"
@@ -42,7 +44,8 @@ func E2ETestConfig() *BeaconChainConfig {
e2eConfig.GenesisForkVersion = []byte{0, 0, 0, 253}
e2eConfig.AltairForkVersion = []byte{1, 0, 0, 253}
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 253}
e2eConfig.ShardingForkVersion = []byte{3, 0, 0, 253}
e2eConfig.Eip4844ForkVersion = []byte{3, 0, 0, 253}
e2eConfig.ShardingForkVersion = []byte{4, 0, 0, 253}
e2eConfig.InitializeForkSchedule()
return e2eConfig
@@ -70,6 +73,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
// Altair Fork Parameters.
e2eConfig.AltairForkEpoch = altairE2EForkEpoch
e2eConfig.BellatrixForkEpoch = bellatrixE2EForkEpoch
e2eConfig.Eip4844ForkEpoch = eip4844E2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "616"
@@ -79,7 +83,8 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
e2eConfig.GenesisForkVersion = []byte{0, 0, 0, 254}
e2eConfig.AltairForkVersion = []byte{1, 0, 0, 254}
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 254}
e2eConfig.ShardingForkVersion = []byte{3, 0, 0, 254}
e2eConfig.Eip4844ForkVersion = []byte{3, 0, 0, 254}
e2eConfig.ShardingForkVersion = []byte{4, 0, 0, 254}
e2eConfig.InitializeForkSchedule()
return e2eConfig

View File

@@ -0,0 +1,38 @@
package params
// UseEIP4844NetworkConfig uses the EIP4844 beacon chain specific network config.
func UseEIP4844NetworkConfig() {
cfg := BeaconNetworkConfig().Copy()
cfg.MinEpochsForBlobsSidecarsRequest = 1200 // 1 day
cfg.ContractDeploymentBlock = 0 // deposit contract is a predeploy
cfg.BootstrapNodes = []string{
"enr:-JG4QFKX3vHhpsIZ5gwHaStj8k9Z4OudBunL8srykq4yTfL-cwX03zyOCGRXVgefXep3wUb3liC26grESiHK6Wn-7zqGAYI-FNCugmlkgnY0gmlwhCJ7uEyJc2VjcDI1NmsxoQJpeftU6RbmIhcFllICznlAMJXL3EwHEGhn73_Gk0wrCYN0Y3CCMsiDdWRwgi7g",
// TODO(EIP-4844): Coinbase boot node
}
OverrideBeaconNetworkConfig(cfg)
}
// EIP4844Config defines the config for the EIP4844 beacon chain testnet.
func EIP4844Config() *BeaconChainConfig {
cfg := MainnetConfig().Copy()
cfg.MinGenesisTime = 1653318000
cfg.MinGenesisActiveValidatorCount = 2
cfg.Eth1FollowDistance = 15
cfg.ConfigName = EIP4844Name
cfg.GenesisForkVersion = []byte{0x00, 0x00, 0x0f, 0xfd}
cfg.SecondsPerETH1Block = 14
cfg.DepositChainID = 1331
cfg.DepositNetworkID = 69
cfg.AltairForkEpoch = 1
cfg.AltairForkVersion = []byte{0x01, 0x00, 0x0f, 0xfd}
cfg.BellatrixForkEpoch = 2
cfg.BellatrixForkVersion = []byte{0x02, 0x00, 0x0f, 0xfd}
cfg.Eip4844ForkEpoch = 3
cfg.SlotsPerEpoch = 8 // 96 secs; reduced from 32 (6.4 mins) for testing
cfg.Eip4844ForkVersion = []byte{0x83, 0x00, 0x0f, 0xfd}
cfg.TerminalTotalDifficulty = "40"
cfg.DepositContractAddress = "0x8A04d14125D0FDCDc742F4A05C051De07232EDa4"
cfg.DomainBlobsSidecar = [4]byte{0x0a, 0x00, 0x00, 0x00}
cfg.InitializeForkSchedule()
return cfg
}

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