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:
terencechain
2023-10-09 07:55:24 -07:00
committed by GitHub
parent 419dbd57f7
commit 66011d5d9c
3 changed files with 148 additions and 35 deletions

View File

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

View File

@@ -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] = &eth.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, &eth.BlobIdentifier{Index: i, BlockRoot: blockRoot[:]})
}
return ids
}

View File

@@ -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(), &ethpb.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)
}