diff --git a/beacon-chain/sync/BUILD.bazel b/beacon-chain/sync/BUILD.bazel index 9a4f9ad743c6..7c14455eca74 100644 --- a/beacon-chain/sync/BUILD.bazel +++ b/beacon-chain/sync/BUILD.bazel @@ -55,6 +55,7 @@ go_library( "//beacon-chain/p2p/encoder:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/state/stateutil:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//shared:go_default_library", "//shared/attestationutil:go_default_library", @@ -132,6 +133,7 @@ go_test( "//beacon-chain/p2p/testing:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/stategen:go_default_library", + "//beacon-chain/state/stateutil:go_default_library", "//beacon-chain/sync/initial-sync/testing:go_default_library", "//proto/beacon/p2p/v1:go_default_library", "//proto/testing:go_default_library", diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range.go b/beacon-chain/sync/rpc_beacon_blocks_by_range.go index 8231a8cd2ed3..7455957207c2 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range.go @@ -6,8 +6,10 @@ import ( libp2pcore "github.com/libp2p/go-libp2p-core" "github.com/pkg/errors" + ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/traceutil" "go.opencensus.io/trace" @@ -124,6 +126,18 @@ func (r *Service) writeBlockRangeToStream(ctx context.Context, startSlot, endSlo traceutil.AnnotateError(span, err) return err } + // handle genesis case + if startSlot == 0 { + genBlock, genRoot, err := r.retrieveGenesisBlock(ctx) + if err != nil { + log.WithError(err).Error("Failed to retrieve genesis block") + r.writeErrorResponseToStream(responseCodeServerError, genericError, stream) + traceutil.AnnotateError(span, err) + return err + } + blks = append([]*ethpb.SignedBeaconBlock{genBlock}, blks...) + roots = append([][32]byte{genRoot}, roots...) + } checkpoint, err := r.db.FinalizedCheckpoint(ctx) if err != nil { log.WithError(err).Error("Failed to retrieve finalized checkpoint") @@ -159,3 +173,15 @@ func (r *Service) writeErrorResponseToStream(responseCode byte, reason string, s } } } + +func (r *Service) retrieveGenesisBlock(ctx context.Context) (*ethpb.SignedBeaconBlock, [32]byte, error) { + genBlock, err := r.db.GenesisBlock(ctx) + if err != nil { + return nil, [32]byte{}, err + } + genRoot, err := stateutil.BlockRoot(genBlock.Block) + if err != nil { + return nil, [32]byte{}, err + } + return genBlock, genRoot, nil +} diff --git a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go index ccd89f305e4d..52e129636a97 100644 --- a/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go +++ b/beacon-chain/sync/rpc_beacon_blocks_by_range_test.go @@ -12,6 +12,7 @@ import ( ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" db "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/state/stateutil" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" "github.com/prysmaticlabs/prysm/shared/testutil" ) @@ -71,3 +72,75 @@ func TestBeaconBlocksRPCHandler_ReturnsBlocks(t *testing.T) { t.Fatal("Did not receive stream within 1 sec") } } + +func TestBeaconBlocksRPCHandler_ReturnsGenesisBlock(t *testing.T) { + p1 := p2ptest.NewTestP2P(t) + p2 := p2ptest.NewTestP2P(t) + p1.Connect(p2) + if len(p1.Host.Network().Peers()) != 1 { + t.Error("Expected peers to be connected") + } + d := db.SetupDB(t) + + req := &pb.BeaconBlocksByRangeRequest{ + StartSlot: 0, + Step: 1, + Count: 4, + } + + // Populate the database with blocks that would match the request. + for i := req.StartSlot; i < req.StartSlot+(req.Step*req.Count); i++ { + // Save genesis block + if i == 0 { + rt, err := stateutil.BlockRoot(ðpb.BeaconBlock{Slot: i}) + if err != nil { + t.Fatal(err) + } + if err := d.SaveGenesisBlockRoot(context.Background(), rt); err != nil { + t.Fatal(err) + } + } + if err := d.SaveBlock(context.Background(), ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Slot: i}}); err != nil { + t.Fatal(err) + } + } + + r := &Service{p2p: p1, db: d, blocksRateLimiter: leakybucket.NewCollector(10000, 10000, false)} + pcl := protocol.ID("/testing") + + var wg sync.WaitGroup + wg.Add(1) + p2.Host.SetStreamHandler(pcl, func(stream network.Stream) { + defer wg.Done() + // check for genesis block + expectSuccess(t, r, stream) + res := ðpb.SignedBeaconBlock{} + if err := r.p2p.Encoding().DecodeWithLength(stream, res); err != nil { + t.Error(err) + } + if res.Block.Slot != 0 { + t.Fatal("genesis block was not returned") + } + for i := req.StartSlot + req.Step; i < req.Count*req.Step; i += req.Step { + expectSuccess(t, r, stream) + res := ðpb.SignedBeaconBlock{} + if err := r.p2p.Encoding().DecodeWithLength(stream, res); err != nil { + t.Error(err) + } + } + }) + + stream1, err := p1.Host.NewStream(context.Background(), p2.Host.ID(), pcl) + if err != nil { + t.Fatal(err) + } + + err = r.beaconBlocksByRangeRPCHandler(context.Background(), req, stream1) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if testutil.WaitTimeout(&wg, 1*time.Second) { + t.Fatal("Did not receive stream within 1 sec") + } +}