diff --git a/beacon-chain/rpc/proposer_server.go b/beacon-chain/rpc/proposer_server.go index 10e5d89c6..8b2647f63 100644 --- a/beacon-chain/rpc/proposer_server.go +++ b/beacon-chain/rpc/proposer_server.go @@ -74,6 +74,19 @@ func (ps *ProposerServer) PendingAttestations(ctx context.Context, req *pb.Pendi if err != nil { return nil, fmt.Errorf("could not retrieve pending attestations from operations service: %v", err) } + + // Remove any attestation from the list if their slot is before the start of + // the previous epoch. This should be handled in the operationService cleanup + // method, but we should filter here in case it wasn't yet processed. + lastEpochStartSlot := helpers.StartSlot(helpers.PrevEpoch(beaconState)) + attsSinceLastEpoch := make([]*pbp2p.Attestation, 0, len(atts)) + for _, att := range atts { + if att.Data.Slot >= lastEpochStartSlot { + attsSinceLastEpoch = append(attsSinceLastEpoch, att) + } + } + atts = attsSinceLastEpoch + if req.FilterReadyForInclusion { var attsReadyForInclusion []*pbp2p.Attestation for _, val := range atts { diff --git a/beacon-chain/rpc/proposer_server_test.go b/beacon-chain/rpc/proposer_server_test.go index d3d8cb3b6..7c3c1e64e 100644 --- a/beacon-chain/rpc/proposer_server_test.go +++ b/beacon-chain/rpc/proposer_server_test.go @@ -147,6 +147,47 @@ func TestPendingAttestations_FiltersWithinInclusionDelay(t *testing.T) { } } +func TestPendingAttestations_FiltersExpiredAttestations(t *testing.T) { + db := internal.SetupDB(t) + defer internal.TeardownDB(t, db) + currentSlot := params.BeaconConfig().GenesisSlot + 100000 + opService := &mockOperationService{ + pendingAttestations: []*pbp2p.Attestation{ + // Expired attestations + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: 0}}, + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot - 10000}}, + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot - 5000}}, + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot - 100}}, + // Non-expired attestations + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot - 5}}, + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot - 2}}, + &pbp2p.Attestation{Data: &pbp2p.AttestationData{Slot: currentSlot}}, + }, + } + expectedNumberOfAttestations := 3 + proposerServer := &ProposerServer{ + operationService: opService, + beaconDB: db, + } + beaconState := &pbp2p.BeaconState{ + Slot: currentSlot, + } + if err := db.SaveState(beaconState); err != nil { + t.Fatal(err) + } + res, err := proposerServer.PendingAttestations(context.Background(), &pb.PendingAttestationsRequest{}) + if err != nil { + t.Fatalf("Unexpected error fetching pending attestations: %v", err) + } + if len(res.PendingAttestations) != expectedNumberOfAttestations { + t.Errorf( + "Expected pending attestations list length %d, but was %d", + expectedNumberOfAttestations, + len(res.PendingAttestations), + ) + } +} + func TestPendingAttestations_OK(t *testing.T) { db := internal.SetupDB(t) defer internal.TeardownDB(t, db) diff --git a/beacon-chain/rpc/service_test.go b/beacon-chain/rpc/service_test.go index 4ed7160f8..4e1003a20 100644 --- a/beacon-chain/rpc/service_test.go +++ b/beacon-chain/rpc/service_test.go @@ -29,7 +29,9 @@ func (t *TestLogger) Errorf(format string, args ...interface{}) { t.testMap["error"] = true } -type mockOperationService struct{} +type mockOperationService struct { + pendingAttestations []*pb.Attestation +} func (ms *mockOperationService) IncomingAttFeed() *event.Feed { return new(event.Feed) @@ -40,6 +42,9 @@ func (ms *mockOperationService) IncomingExitFeed() *event.Feed { } func (ms *mockOperationService) PendingAttestations() ([]*pb.Attestation, error) { + if ms.pendingAttestations != nil { + return ms.pendingAttestations, nil + } return []*pb.Attestation{ { AggregationBitfield: []byte("A"),