mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Check blob exists before requesting from peer (#13012)
* Check blob exists before requesting from peer * Potuz's feedback * Fix err * Add index check
This commit is contained in:
@@ -228,6 +228,7 @@ go_test(
|
||||
"//encoding/ssz/equality:go_default_library",
|
||||
"//network/forks:go_default_library",
|
||||
"//proto/engine/v1:go_default_library",
|
||||
"//proto/eth/v2:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//proto/prysm/v1alpha1/attestation:go_default_library",
|
||||
"//proto/prysm/v1alpha1/metadata:go_default_library",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
libp2pcore "github.com/libp2p/go-libp2p/core"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/db"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/execution"
|
||||
"github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/types"
|
||||
"github.com/prysmaticlabs/prysm/v4/config/params"
|
||||
@@ -43,7 +44,7 @@ func (s *Service) sendRecentBeaconBlocksRequest(ctx context.Context, blockRoots
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.requestPendingBlobs(ctx, blk.Block(), blkRoot[:], id); err != nil {
|
||||
if err := s.requestPendingBlobs(ctx, blk.Block(), blkRoot, id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -114,41 +115,78 @@ func (s *Service) beaconBlocksRootRPCHandler(ctx context.Context, msg interface{
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) requestPendingBlobs(ctx context.Context, b interfaces.ReadOnlyBeaconBlock, br []byte, id peer.ID) error {
|
||||
// Block before deneb has no blob.
|
||||
if b.Version() < version.Deneb {
|
||||
return nil
|
||||
}
|
||||
c, err := b.Body().BlobKzgCommitments()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// No op if the block has no blob commitments.
|
||||
if len(c) == 0 {
|
||||
return nil
|
||||
// requestPendingBlobs handles the request for pending blobs based on the given beacon block.
|
||||
func (s *Service) requestPendingBlobs(ctx context.Context, block interfaces.ReadOnlyBeaconBlock, blockRoot [32]byte, peerID peer.ID) error {
|
||||
if block.Version() < version.Deneb {
|
||||
return nil // Block before deneb has no blob.
|
||||
}
|
||||
|
||||
// Build request for blob sidecars.
|
||||
blobId := make([]*eth.BlobIdentifier, len(c))
|
||||
for i := range c {
|
||||
blobId[i] = ð.BlobIdentifier{Index: uint64(i), BlockRoot: br}
|
||||
}
|
||||
|
||||
ctxByte, err := ContextByteVersionsForValRoot(s.cfg.chain.GenesisValidatorsRoot())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := types.BlobSidecarsByRootReq(blobId)
|
||||
|
||||
// Send request to a random peer.
|
||||
blobSidecars, err := SendBlobSidecarByRoot(ctx, s.cfg.clock, s.cfg.p2p, id, ctxByte, &req)
|
||||
commitments, err := block.Body().BlobKzgCommitments()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sidecar := range blobSidecars {
|
||||
if len(commitments) == 0 {
|
||||
return nil // No operation if the block has no blob commitments.
|
||||
}
|
||||
|
||||
contextByte, err := ContextByteVersionsForValRoot(s.cfg.chain.GenesisValidatorsRoot())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
request, err := s.constructPendingBlobsRequest(ctx, blockRoot, len(commitments))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.sendAndSaveBlobSidecars(ctx, request, contextByte, peerID)
|
||||
}
|
||||
|
||||
// sendAndSaveBlobSidecars sends the blob request and saves received sidecars.
|
||||
func (s *Service) sendAndSaveBlobSidecars(ctx context.Context, request types.BlobSidecarsByRootReq, contextByte ContextByteVersions, peerID peer.ID) error {
|
||||
sidecars, err := SendBlobSidecarByRoot(ctx, s.cfg.clock, s.cfg.p2p, peerID, contextByte, &request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sidecar := range sidecars {
|
||||
log.WithFields(blobFields(sidecar)).Debug("Received blob sidecar gossip RPC")
|
||||
}
|
||||
|
||||
return s.cfg.beaconDB.SaveBlobSidecar(ctx, blobSidecars)
|
||||
return s.cfg.beaconDB.SaveBlobSidecar(ctx, sidecars)
|
||||
}
|
||||
|
||||
// constructPendingBlobsRequest creates a request for BlobSidecars by root, considering blobs already in DB.
|
||||
func (s *Service) constructPendingBlobsRequest(ctx context.Context, blockRoot [32]byte, count int) (types.BlobSidecarsByRootReq, error) {
|
||||
knownBlobs, err := s.cfg.beaconDB.BlobSidecarsByRoot(ctx, blockRoot)
|
||||
if err != nil && !errors.Is(err, db.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
knownIndices := indexSetFromBlobs(knownBlobs)
|
||||
requestedIndices := filterUnknownIndices(knownIndices, count, blockRoot)
|
||||
|
||||
return requestedIndices, nil
|
||||
}
|
||||
|
||||
// Helper function to create a set of known indices.
|
||||
func indexSetFromBlobs(blobs []*eth.BlobSidecar) map[uint64]struct{} {
|
||||
indices := make(map[uint64]struct{})
|
||||
for _, blob := range blobs {
|
||||
indices[blob.Index] = struct{}{}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// Helper function to filter out known indices.
|
||||
func filterUnknownIndices(knownIndices map[uint64]struct{}, count int, blockRoot [32]byte) []*eth.BlobIdentifier {
|
||||
var ids []*eth.BlobIdentifier
|
||||
for i := uint64(0); i < uint64(count); i++ {
|
||||
if _, exists := knownIndices[i]; exists {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, ð.BlobIdentifier{Index: i, BlockRoot: blockRoot[:]})
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
leakybucket "github.com/prysmaticlabs/prysm/v4/container/leaky-bucket"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1"
|
||||
"github.com/prysmaticlabs/prysm/v4/proto/eth/v2"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||
@@ -296,12 +297,12 @@ func TestRequestPendingBlobs(t *testing.T) {
|
||||
t.Run("old block should not fail", func(t *testing.T) {
|
||||
b, err := blocks.NewBeaconBlock(util.NewBeaconBlock().Block)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.requestPendingBlobs(context.Background(), b, []byte{}, "test"))
|
||||
require.NoError(t, s.requestPendingBlobs(context.Background(), b, [32]byte{}, "test"))
|
||||
})
|
||||
t.Run("empty commitment block should not fail", func(t *testing.T) {
|
||||
b, err := blocks.NewBeaconBlock(util.NewBeaconBlockDeneb().Block)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, s.requestPendingBlobs(context.Background(), b, []byte{}, "test"))
|
||||
require.NoError(t, s.requestPendingBlobs(context.Background(), b, [32]byte{}, "test"))
|
||||
})
|
||||
t.Run("unsupported protocol", func(t *testing.T) {
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
@@ -321,15 +322,88 @@ func TestRequestPendingBlobs(t *testing.T) {
|
||||
p1.Peers().SetChainState(p2.PeerID(), ðpb.Status{FinalizedEpoch: 1})
|
||||
s := &Service{
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(time.Unix(0, 0), [32]byte{}),
|
||||
p2p: p1,
|
||||
chain: chain,
|
||||
clock: startup.NewClock(time.Unix(0, 0), [32]byte{}),
|
||||
beaconDB: db.SetupDB(t),
|
||||
},
|
||||
}
|
||||
b := util.NewBeaconBlockDeneb()
|
||||
b.Block.Body.BlobKzgCommitments = make([][]byte, 1)
|
||||
b1, err := blocks.NewBeaconBlock(b.Block)
|
||||
require.NoError(t, err)
|
||||
require.ErrorContains(t, "protocols not supported", s.requestPendingBlobs(context.Background(), b1, []byte{}, p2.PeerID()))
|
||||
require.ErrorContains(t, "protocols not supported", s.requestPendingBlobs(context.Background(), b1, [32]byte{}, p2.PeerID()))
|
||||
})
|
||||
}
|
||||
|
||||
func TestConstructPendingBlobsRequest(t *testing.T) {
|
||||
d := db.SetupDB(t)
|
||||
s := &Service{cfg: &config{beaconDB: d}}
|
||||
ctx := context.Background()
|
||||
|
||||
// No unknown indices.
|
||||
root := [32]byte{1}
|
||||
count := 3
|
||||
actual, err := s.constructPendingBlobsRequest(ctx, root, count)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 3, len(actual))
|
||||
for i, id := range actual {
|
||||
require.Equal(t, uint64(i), id.Index)
|
||||
require.DeepEqual(t, root[:], id.BlockRoot)
|
||||
}
|
||||
|
||||
// Has indices.
|
||||
blobSidecars := []*ethpb.BlobSidecar{
|
||||
{Index: 0, BlockRoot: root[:]},
|
||||
{Index: 2, BlockRoot: root[:]},
|
||||
}
|
||||
require.NoError(t, d.SaveBlobSidecar(ctx, blobSidecars))
|
||||
|
||||
expected := []*eth.BlobIdentifier{
|
||||
{Index: 1, BlockRoot: root[:]},
|
||||
}
|
||||
actual, err = s.constructPendingBlobsRequest(ctx, root, count)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected[0].Index, actual[0].Index)
|
||||
require.DeepEqual(t, expected[0].BlockRoot, actual[0].BlockRoot)
|
||||
}
|
||||
|
||||
func TestIndexSetFromBlobs(t *testing.T) {
|
||||
blobs := []*ethpb.BlobSidecar{
|
||||
{Index: 0},
|
||||
{Index: 1},
|
||||
{Index: 2},
|
||||
}
|
||||
|
||||
expected := map[uint64]struct{}{
|
||||
0: {},
|
||||
1: {},
|
||||
2: {},
|
||||
}
|
||||
|
||||
actual := indexSetFromBlobs(blobs)
|
||||
require.DeepEqual(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestFilterUnknownIndices(t *testing.T) {
|
||||
knownIndices := map[uint64]struct{}{
|
||||
0: {},
|
||||
1: {},
|
||||
2: {},
|
||||
}
|
||||
|
||||
blockRoot := [32]byte{}
|
||||
count := 5
|
||||
|
||||
expected := []*eth.BlobIdentifier{
|
||||
{Index: 3, BlockRoot: blockRoot[:]},
|
||||
{Index: 4, BlockRoot: blockRoot[:]},
|
||||
}
|
||||
|
||||
actual := filterUnknownIndices(knownIndices, count, blockRoot)
|
||||
require.Equal(t, len(expected), len(actual))
|
||||
require.Equal(t, expected[0].Index, actual[0].Index)
|
||||
require.DeepEqual(t, actual[0].BlockRoot, expected[0].BlockRoot)
|
||||
require.Equal(t, expected[1].Index, actual[1].Index)
|
||||
require.DeepEqual(t, actual[1].BlockRoot, expected[1].BlockRoot)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user