mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 13:58:09 -05:00
Compare commits
83 Commits
peerDAS-do
...
blob-rotat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b8a38039b | ||
|
|
5410c30890 | ||
|
|
8024758ff4 | ||
|
|
826a930ed0 | ||
|
|
3d45b83059 | ||
|
|
9096c4a8c4 | ||
|
|
31f369c982 | ||
|
|
8a7ab754b2 | ||
|
|
752bb63ec4 | ||
|
|
52a87e912d | ||
|
|
3400af655c | ||
|
|
1e5c0778b9 | ||
|
|
a032cf2ccc | ||
|
|
f871d7668f | ||
|
|
5c2ebec369 | ||
|
|
a22d62e398 | ||
|
|
00c57e76ec | ||
|
|
1d800e42c5 | ||
|
|
1391ccedd2 | ||
|
|
e71124ee87 | ||
|
|
f04b2be9de | ||
|
|
080810ec25 | ||
|
|
37118e7815 | ||
|
|
a390c74721 | ||
|
|
9b260be2c7 | ||
|
|
bd6cf8d5d8 | ||
|
|
bff4435b72 | ||
|
|
d8187b7bb1 | ||
|
|
109a0f5f0f | ||
|
|
bc0ba2d508 | ||
|
|
5b53ad6cdf | ||
|
|
920e0757d9 | ||
|
|
bd01ffaa97 | ||
|
|
41f34aa6dd | ||
|
|
48c26b9eec | ||
|
|
eed1e47664 | ||
|
|
b18ebab455 | ||
|
|
ebe1e9ec3a | ||
|
|
b2b4aa9406 | ||
|
|
7d6f079697 | ||
|
|
b2a9a79e96 | ||
|
|
cb524c5cef | ||
|
|
2e0a6d6556 | ||
|
|
ac4a4cc80d | ||
|
|
027e49c120 | ||
|
|
ff822a6f4e | ||
|
|
901a087a6d | ||
|
|
be8c0293ad | ||
|
|
d5d1b33761 | ||
|
|
53bb657bd2 | ||
|
|
e2bf25e088 | ||
|
|
31e1999ddf | ||
|
|
cb3da77ff4 | ||
|
|
40c754e991 | ||
|
|
b6a05ecd32 | ||
|
|
3a6a7d7bcd | ||
|
|
27677da136 | ||
|
|
eb1c7bd5f6 | ||
|
|
b3d2ed1aaf | ||
|
|
bf1b5a5094 | ||
|
|
d3b6b2d8bc | ||
|
|
43814449c0 | ||
|
|
533a6b3f0c | ||
|
|
8e370da195 | ||
|
|
024ad7b433 | ||
|
|
13becadb30 | ||
|
|
e3f1a2d321 | ||
|
|
0f8c4431fc | ||
|
|
f566d50a43 | ||
|
|
7fc3a697d8 | ||
|
|
c5ee1c2735 | ||
|
|
658725a601 | ||
|
|
80546f83e9 | ||
|
|
d7489f9f0f | ||
|
|
aad947b83f | ||
|
|
7d2a257094 | ||
|
|
965280190a | ||
|
|
bcffe3d291 | ||
|
|
e3438cd9ee | ||
|
|
a8bc8af9e7 | ||
|
|
e70492ec8c | ||
|
|
ea9979f813 | ||
|
|
e504d31285 |
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(ðpb.Fork{
|
||||
PreviousVersion: state.Fork().CurrentVersion,
|
||||
CurrentVersion: params.BeaconConfig().Eip4844ForkVersion,
|
||||
Epoch: time.CurrentEpoch(state),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
180
beacon-chain/db/kv/blobs.go
Normal 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 := ðpb.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 := ðpb.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
|
||||
}
|
||||
101
beacon-chain/db/kv/blobs_test.go
Normal file
101
beacon-chain/db/kv/blobs_test.go
Normal 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, ðpb.BlobsSidecar{
|
||||
BeaconBlockRoot: root1[:],
|
||||
BeaconBlockSlot: types.Slot(i),
|
||||
Blobs: make([]*enginev1.Blob, 0),
|
||||
AggregatedProof: make([]byte, 48),
|
||||
})
|
||||
sidecars = append(sidecars, ðpb.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 := ðpb.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 = ðpb.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
|
||||
}
|
||||
@@ -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 = ðpb.SignedBeaconBlockWithBlobKZGs{}
|
||||
if err := rawBlock.UnmarshalSSZ(enc[len(eip4844Key):]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
// Marshal block bytes to phase 0 beacon block.
|
||||
rawBlock = ðpb.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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -20,12 +20,16 @@ var gossipTopicMappings = map[string]proto.Message{
|
||||
AggregateAndProofSubnetTopicFormat: ðpb.SignedAggregateAttestationAndProof{},
|
||||
SyncContributionAndProofSubnetTopicFormat: ðpb.SignedContributionAndProof{},
|
||||
SyncCommitteeSubnetTopicFormat: ðpb.SyncCommitteeMessage{},
|
||||
BlobsSubnetTopicFormat: ðpb.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 ðpb.SignedBeaconBlockWithBlobKZGs{}
|
||||
}
|
||||
if epoch >= params.BeaconConfig().BellatrixForkEpoch {
|
||||
return ðpb.SignedBeaconBlockBellatrix{}
|
||||
}
|
||||
@@ -59,4 +63,6 @@ func init() {
|
||||
// Specially handle Bellatrix objects.
|
||||
GossipTypeMapping[reflect.TypeOf(ðpb.SignedBeaconBlockBellatrix{})] = BlockSubnetTopicFormat
|
||||
GossipTypeMapping[reflect.TypeOf(ðpb.SignedBlindedBeaconBlockBellatrix{})] = BlockSubnetTopicFormat
|
||||
// Specially handle EIP4844 objects.
|
||||
GossipTypeMapping[reflect.TypeOf(ðpb.SignedBeaconBlockWithBlobKZGs{})] = BlockSubnetTopicFormat
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -48,6 +48,11 @@ func InitializeDataMaps() {
|
||||
ðpb.SignedBeaconBlockBellatrix{Block: ðpb.BeaconBlockBellatrix{Body: ðpb.BeaconBlockBodyBellatrix{}}},
|
||||
)
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().Eip4844ForkVersion): func() (interfaces.SignedBeaconBlock, error) {
|
||||
return blocks.NewSignedBeaconBlock(
|
||||
ðpb.SignedBeaconBlockWithBlobKZGs{Block: ðpb.BeaconBlockWithBlobKZGs{Body: ðpb.BeaconBlockBodyWithBlobKZGs{}}},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
// Reset our metadata map.
|
||||
@@ -61,5 +66,8 @@ func InitializeDataMaps() {
|
||||
bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): func() metadata.Metadata {
|
||||
return wrapper.WrappedMetadataV1(ðpb.MetaDataV1{})
|
||||
},
|
||||
bytesutil.ToBytes4(params.BeaconConfig().Eip4844ForkVersion): func() metadata.Metadata {
|
||||
return wrapper.WrappedMetadataV1(ðpb.MetaDataV1{})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 ðpbv2.BlockResponseV2{
|
||||
Version: ethpbv2.Version_EIP4844,
|
||||
Data: ðpbv2.SignedBeaconBlockContainer{
|
||||
Message: ðpbv2.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 := ðpbv2.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 ðpbv2.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(ð.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)
|
||||
}
|
||||
|
||||
@@ -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 ðpbv2.BeaconStateResponseV2{
|
||||
Version: ethpbv2.Version_BELLATRIX,
|
||||
Version: v,
|
||||
Data: ðpbv2.BeaconStateContainer{
|
||||
State: ðpbv2.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")
|
||||
}
|
||||
|
||||
@@ -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 ðpbv2.ProduceBlockResponseV2{
|
||||
Version: ethpbv2.Version_EIP4844,
|
||||
Data: ðpbv2.BeaconBlockContainerV2{
|
||||
Block: ðpbv2.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 ðpbv2.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 ðpbv2.SSZContainer{
|
||||
Version: ethpbv2.Version_EIP4844,
|
||||
Data: sszBlock,
|
||||
}, nil
|
||||
}
|
||||
return nil, status.Error(codes.InvalidArgument, "Unsupported block type")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 ðpb.GenericBeaconBlock{Block: ðpb.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 ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 := ðpb.BeaconBlockBellatrix{
|
||||
payload, payloadID, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex, bytesutil.ToBytes32(altairBlk.ParentRoot))
|
||||
if err != nil {
|
||||
return nil, enginev1.PayloadIDBytes{}, err
|
||||
}
|
||||
return ðpb.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(
|
||||
|
||||
@@ -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 := ðpb.BeaconBlockWithBlobKZGs{
|
||||
Slot: bellatrixBlk.Slot,
|
||||
ProposerIndex: bellatrixBlk.ProposerIndex,
|
||||
ParentRoot: bellatrixBlk.ParentRoot,
|
||||
StateRoot: params.BeaconConfig().ZeroHash[:],
|
||||
Body: ðpb.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(
|
||||
ðpb.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 = ðpb.BlobsSidecar{
|
||||
BeaconBlockRoot: r[:],
|
||||
BeaconBlockSlot: blk.Slot,
|
||||
Blobs: blobs,
|
||||
AggregatedProof: blobsBundle.AggregatedProof,
|
||||
}
|
||||
}
|
||||
return blk, sideCar, nil
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 := ðpb.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)
|
||||
|
||||
@@ -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(ð.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(ð.SignedBlobsSidecar{Message: sc}); err != nil {
|
||||
return err
|
||||
}
|
||||
blks[i] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bFunc(ctx, blks, blockRoots)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(ðpb.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 ðpb.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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
100
beacon-chain/sync/pending_sidecars_queue.go
Normal file
100
beacon-chain/sync/pending_sidecars_queue.go
Normal 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 ðpb.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
|
||||
}
|
||||
@@ -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 */)
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
|
||||
179
beacon-chain/sync/rpc_blobs_sidecars_by_range.go
Normal file
179
beacon-chain/sync/rpc_blobs_sidecars_by_range.go
Normal 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
|
||||
}
|
||||
166
beacon-chain/sync/rpc_blobs_sidecars_by_range_test.go
Normal file
166
beacon-chain/sync/rpc_blobs_sidecars_by_range_test.go
Normal 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 ðpb.SignedBlobsSidecar{
|
||||
Message: ðpb.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 := ðpb.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 := ðpb.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 := ðpb.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)))
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(ðpb.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)
|
||||
|
||||
26
beacon-chain/sync/subscriber_blobs_sidecar.go
Normal file
26
beacon-chain/sync/subscriber_blobs_sidecar.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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(ðpb.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, ðpb.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(ðpb.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: ðpb.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(ðpb.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, ðpb.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(ðpb.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: ðpb.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)
|
||||
}
|
||||
|
||||
238
beacon-chain/sync/validate_blobs_sidecar.go
Normal file
238
beacon-chain/sync/validate_blobs_sidecar.go
Normal 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 := ðpb.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
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ var appFlags = []cli.Flag{
|
||||
flags.SetGCPercent,
|
||||
flags.BlockBatchLimit,
|
||||
flags.BlockBatchLimitBurstFactor,
|
||||
flags.BlobsTransferRate,
|
||||
flags.BlobsTransferRateThresh,
|
||||
flags.InteropMockEth1DataVotesFlag,
|
||||
flags.InteropGenesisStateFlag,
|
||||
flags.InteropNumValidatorsFlag,
|
||||
|
||||
@@ -114,6 +114,8 @@ var appHelpFlagGroups = []flagGroup{
|
||||
flags.SlotsPerArchivedPoint,
|
||||
flags.BlockBatchLimit,
|
||||
flags.BlockBatchLimitBurstFactor,
|
||||
flags.BlobsTransferRate,
|
||||
flags.BlobsTransferRateThresh,
|
||||
flags.EnableDebugRPCEndpoints,
|
||||
flags.SubscribeToAllSubnets,
|
||||
flags.HistoricalSlasherNode,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ func init() {
|
||||
E2EMainnetTestConfig(),
|
||||
InteropConfig(),
|
||||
RopstenConfig(),
|
||||
EIP4844Config(),
|
||||
}
|
||||
configs = newConfigset(defaults...)
|
||||
// ensure that main net is always present and active by default
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
38
config/params/testnet_eip4844_config.go
Normal file
38
config/params/testnet_eip4844_config.go
Normal 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
Reference in New Issue
Block a user