diff --git a/WORKSPACE b/WORKSPACE index 19b0c5ab27..d6deb56fed 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1201,7 +1201,7 @@ go_repository( go_repository( name = "com_github_prysmaticlabs_ethereumapis", - commit = "12081efa4a35f9e8b5c44faf3215a4ef64e21516", + commit = "8a69b37df2264eb58b0b0cef5ba25ae9af2d8732", importpath = "github.com/prysmaticlabs/ethereumapis", patch_args = ["-p1"], patches = [ diff --git a/beacon-chain/db/kv/blocks.go b/beacon-chain/db/kv/blocks.go index e46634f7c3..99215e4dab 100644 --- a/beacon-chain/db/kv/blocks.go +++ b/beacon-chain/db/kv/blocks.go @@ -338,9 +338,6 @@ func fetchBlockRootsBySlotRange(bkt *bolt.Bucket, startSlotEncoded, endSlotEncod if endSlot, ok = endSlotEncoded.(uint64); !ok { endSlot = 0 } - if startSlot == endSlot && startSlot == 0 { - return nil - } min := []byte(fmt.Sprintf("%07d", startSlot)) max := []byte(fmt.Sprintf("%07d", endSlot)) var conditional func(key, max []byte) bool diff --git a/beacon-chain/rpc/beacon/attestations.go b/beacon-chain/rpc/beacon/attestations.go index 81be98cc8c..199aaedcb6 100644 --- a/beacon-chain/rpc/beacon/attestations.go +++ b/beacon-chain/rpc/beacon/attestations.go @@ -7,6 +7,7 @@ import ( ptypes "github.com/gogo/protobuf/types" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" + "github.com/prysmaticlabs/go-ssz" "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" "github.com/prysmaticlabs/prysm/shared/pagination" "github.com/prysmaticlabs/prysm/shared/params" @@ -40,6 +41,25 @@ func (bs *Server) ListAttestations( var atts []*ethpb.Attestation var err error switch q := req.QueryFilter.(type) { + case *ethpb.ListAttestationsRequest_Genesis: + blks, err := bs.BeaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(0).SetEndSlot(0)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not genesis block: %v", err) + } + if len(blks) == 0 { + return nil, status.Error(codes.Internal, "Could not find genesis block") + } + if len(blks) != 1 { + return nil, status.Error(codes.Internal, "Found more than 1 genesis block") + } + genesisRoot, err := ssz.SigningRoot(blks[0]) + if err != nil { + return nil, err + } + atts, err = bs.BeaconDB.Attestations(ctx, filters.NewFilter().SetHeadBlockRoot(genesisRoot[:])) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not fetch genesis attestations: %v", err) + } case *ethpb.ListAttestationsRequest_HeadBlockRoot: atts, err = bs.BeaconDB.Attestations(ctx, filters.NewFilter().SetHeadBlockRoot(q.HeadBlockRoot)) if err != nil { diff --git a/beacon-chain/rpc/beacon/attestations_test.go b/beacon-chain/rpc/beacon/attestations_test.go index 1374f147f2..ff9f878458 100644 --- a/beacon-chain/rpc/beacon/attestations_test.go +++ b/beacon-chain/rpc/beacon/attestations_test.go @@ -12,6 +12,7 @@ import ( ptypes "github.com/gogo/protobuf/types" ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/go-ssz" mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" dbTest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" mockOps "github.com/prysmaticlabs/prysm/beacon-chain/operations/testing" @@ -46,6 +47,82 @@ func TestServer_ListAttestations_NoResults(t *testing.T) { } } +func TestServer_ListAttestations_Genesis(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + + ctx := context.Background() + bs := &Server{ + BeaconDB: db, + HeadFetcher: &mock.ChainService{ + State: &pbp2p.BeaconState{Slot: 0}, + }, + } + + // Should throw an error if no genesis data is found. + if _, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ + QueryFilter: ðpb.ListAttestationsRequest_Genesis{ + Genesis: true, + }, + }); err != nil && !strings.Contains(err.Error(), "Could not find genesis") { + t.Fatal(err) + } + + parentRoot := [32]byte{1, 2, 3} + blk := ðpb.BeaconBlock{ + Slot: 0, + ParentRoot: parentRoot[:], + } + root, err := ssz.SigningRoot(blk) + if err != nil { + t.Fatal(err) + } + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + att := ðpb.Attestation{ + AggregationBits: bitfield.Bitlist{0b11}, + Data: ðpb.AttestationData{ + Slot: 0, + BeaconBlockRoot: root[:], + }, + CustodyBits: bitfield.Bitlist{0b10}, + } + if err := db.SaveAttestation(ctx, att); err != nil { + t.Fatal(err) + } + wanted := ðpb.ListAttestationsResponse{ + Attestations: []*ethpb.Attestation{att}, + NextPageToken: "1", + TotalSize: 1, + } + + res, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ + QueryFilter: ðpb.ListAttestationsRequest_Genesis{ + Genesis: true, + }, + }) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(wanted, res) { + t.Errorf("Wanted %v, received %v", wanted, res) + } + + // Should throw an error if there is more than 1 block + // for the genesis slot. + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + if _, err := bs.ListAttestations(ctx, ðpb.ListAttestationsRequest{ + QueryFilter: ðpb.ListAttestationsRequest_Genesis{ + Genesis: true, + }, + }); err != nil && !strings.Contains(err.Error(), "Found more than 1") { + t.Fatal(err) + } +} + func TestServer_ListAttestations_NoPagination(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) diff --git a/beacon-chain/rpc/beacon/blocks.go b/beacon-chain/rpc/beacon/blocks.go index 3765f943c8..cfa9849b8c 100644 --- a/beacon-chain/rpc/beacon/blocks.go +++ b/beacon-chain/rpc/beacon/blocks.go @@ -136,6 +136,34 @@ func (bs *Server) ListBlocks( TotalSize: int32(numBlks), NextPageToken: nextPageToken, }, nil + case *ethpb.ListBlocksRequest_Genesis: + blks, err := bs.BeaconDB.Blocks(ctx, filters.NewFilter().SetStartSlot(0).SetEndSlot(0)) + if err != nil { + return nil, status.Errorf(codes.Internal, "Could not retrieve blocks for genesis slot: %v", err) + } + numBlks := len(blks) + if numBlks == 0 { + return nil, status.Error(codes.Internal, "Could not find genesis block") + } + if numBlks != 1 { + return nil, status.Error(codes.Internal, "Found more than 1 genesis block") + } + root, err := ssz.SigningRoot(blks[0]) + if err != nil { + return nil, err + } + containers := []*ethpb.BeaconBlockContainer{ + { + Block: blks[0], + BlockRoot: root[:], + }, + } + + return ðpb.ListBlocksResponse{ + BlockContainers: containers, + TotalSize: int32(1), + NextPageToken: strconv.Itoa(0), + }, nil } return nil, status.Error(codes.InvalidArgument, "Must specify a filter criteria for fetching blocks") diff --git a/beacon-chain/rpc/beacon/blocks_test.go b/beacon-chain/rpc/beacon/blocks_test.go index 8530cff63e..3448ba1cfc 100644 --- a/beacon-chain/rpc/beacon/blocks_test.go +++ b/beacon-chain/rpc/beacon/blocks_test.go @@ -65,6 +65,73 @@ func TestServer_ListBlocks_NoResults(t *testing.T) { } } +func TestServer_ListBlocks_Genesis(t *testing.T) { + db := dbTest.SetupDB(t) + defer dbTest.TeardownDB(t, db) + + ctx := context.Background() + bs := &Server{ + BeaconDB: db, + } + + // Should throw an error if no genesis block is found. + if _, err := bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ + QueryFilter: ðpb.ListBlocksRequest_Genesis{ + Genesis: true, + }, + }); err != nil && !strings.Contains(err.Error(), "Could not find genesis") { + t.Fatal(err) + } + + // Should return the proper genesis block if it exists. + parentRoot := [32]byte{1, 2, 3} + blk := ðpb.BeaconBlock{ + Slot: 0, + ParentRoot: parentRoot[:], + } + root, err := ssz.SigningRoot(blk) + if err != nil { + t.Fatal(err) + } + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + wanted := ðpb.ListBlocksResponse{ + BlockContainers: []*ethpb.BeaconBlockContainer{ + { + Block: blk, + BlockRoot: root[:], + }, + }, + NextPageToken: "0", + TotalSize: 1, + } + res, err := bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ + QueryFilter: ðpb.ListBlocksRequest_Genesis{ + Genesis: true, + }, + }) + if err != nil { + t.Fatal(err) + } + if !proto.Equal(wanted, res) { + t.Errorf("Wanted %v, received %v", wanted, res) + } + + // Should throw an error if there is more than 1 block + // for the genesis slot. + if err := db.SaveBlock(ctx, blk); err != nil { + t.Fatal(err) + } + if _, err := bs.ListBlocks(ctx, ðpb.ListBlocksRequest{ + QueryFilter: ðpb.ListBlocksRequest_Genesis{ + Genesis: true, + }, + }); err != nil && !strings.Contains(err.Error(), "Found more than 1") { + t.Fatal(err) + } +} + func TestServer_ListBlocks_Pagination(t *testing.T) { db := dbTest.SetupDB(t) defer dbTest.TeardownDB(t, db) diff --git a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch index bd403b1560..c81149f071 100644 --- a/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch +++ b/third_party/com_github_prysmaticlabs_ethereumapis-tags.patch @@ -255,7 +255,7 @@ index 69a148a..1b6ac18 100644 + bytes signature = 4 [(gogoproto.moretags) = "ssz-size:\"96\""]; } diff --git a/eth/v1alpha1/beacon_chain.proto b/eth/v1alpha1/beacon_chain.proto -index 00db5f3..8c4c74d 100644 +index 0f490c1..592b169 100644 --- a/eth/v1alpha1/beacon_chain.proto +++ b/eth/v1alpha1/beacon_chain.proto @@ -15,6 +15,7 @@ syntax = "proto3"; @@ -266,7 +266,7 @@ index 00db5f3..8c4c74d 100644 import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; -@@ -247,7 +248,7 @@ message BeaconBlockContainer { +@@ -261,7 +262,7 @@ message BeaconBlockContainer { // Information about the head of the beacon chain. message ChainHead { // 32 byte merkle tree root of the canonical head block in the beacon node. @@ -275,7 +275,7 @@ index 00db5f3..8c4c74d 100644 // Slot of the head block. uint64 head_block_slot = 2; -@@ -262,7 +263,7 @@ message ChainHead { +@@ -276,7 +277,7 @@ message ChainHead { uint64 finalized_epoch = 5; // Most recent 32 byte finalized block root. @@ -284,7 +284,7 @@ index 00db5f3..8c4c74d 100644 // Most recent slot that contains the justified block. uint64 justified_block_slot = 7; -@@ -271,7 +272,7 @@ message ChainHead { +@@ -285,7 +286,7 @@ message ChainHead { uint64 justified_epoch = 8; // Most recent 32 byte justified block root. @@ -293,7 +293,7 @@ index 00db5f3..8c4c74d 100644 // Most recent slot that contains the previous justified block. uint64 previous_justified_slot = 10; -@@ -280,7 +281,7 @@ message ChainHead { +@@ -294,7 +295,7 @@ message ChainHead { uint64 previous_justified_epoch = 11; // Previous 32 byte justified block root. @@ -302,7 +302,7 @@ index 00db5f3..8c4c74d 100644 } message ListCommitteesRequest { -@@ -338,7 +339,7 @@ message ListValidatorBalancesRequest { +@@ -352,7 +353,7 @@ message ListValidatorBalancesRequest { // Validator 48 byte BLS public keys to filter validators for the given // epoch. @@ -311,7 +311,7 @@ index 00db5f3..8c4c74d 100644 // Validator indices to filter validators for the given epoch. repeated uint64 indices = 4; -@@ -359,7 +360,7 @@ message ValidatorBalances { +@@ -373,7 +374,7 @@ message ValidatorBalances { message Balance { // Validator's 48 byte BLS public key. @@ -320,7 +320,7 @@ index 00db5f3..8c4c74d 100644 // Validator's index in the validator set. uint64 index = 2; -@@ -435,17 +436,17 @@ message ActiveSetChanges { +@@ -449,17 +450,17 @@ message ActiveSetChanges { uint64 epoch = 1; // 48 byte validator public keys that have been activated in this epoch. @@ -342,7 +342,7 @@ index 00db5f3..8c4c74d 100644 } message ValidatorQueue { -@@ -455,11 +456,11 @@ message ValidatorQueue { +@@ -469,11 +470,11 @@ message ValidatorQueue { // Ordered list of 48 byte public keys awaiting activation. 0th index is the // next key to be processed. @@ -356,7 +356,7 @@ index 00db5f3..8c4c74d 100644 } message ListValidatorAssignmentsRequest { -@@ -471,7 +472,7 @@ message ListValidatorAssignmentsRequest { +@@ -485,7 +486,7 @@ message ListValidatorAssignmentsRequest { bool genesis = 2; } // 48 byte validator public keys to filter assignments for the given epoch. @@ -365,7 +365,7 @@ index 00db5f3..8c4c74d 100644 // Validator indicies to filter assignments for the given epoch. repeated uint64 indices = 4; -@@ -506,7 +507,7 @@ message ValidatorAssignments { +@@ -520,7 +521,7 @@ message ValidatorAssignments { uint64 proposer_slot = 4; // 48 byte BLS public key.