Skip to content

Commit

Permalink
ETH2 API: Implement ListPoolAttestations endpoint (#8902)
Browse files Browse the repository at this point in the history
* Implement ListPoolAttestations

* Spacing

* add lil doc

* Fix testing and rework logic
  • Loading branch information
0xKiwi authored May 19, 2021
1 parent fe440bc commit 9db4eba
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 3 deletions.
37 changes: 34 additions & 3 deletions beacon-chain/rpc/beaconv1/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package beaconv1

import (
"context"
"errors"
"fmt"

ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1"
ethpb_alpha "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
Expand All @@ -16,9 +16,40 @@ import (
)

// ListPoolAttestations retrieves attestations known by the node but
// not necessarily incorporated into any block.
// not necessarily incorporated into any block. Allows filtering by committee index or slot.
func (bs *Server) ListPoolAttestations(ctx context.Context, req *ethpb.AttestationsPoolRequest) (*ethpb.AttestationsPoolResponse, error) {
return nil, errors.New("unimplemented")
ctx, span := trace.StartSpan(ctx, "beaconv1.ListPoolAttestations")
defer span.End()

attestations := bs.AttestationsPool.AggregatedAttestations()
unaggAtts, err := bs.AttestationsPool.UnaggregatedAttestations()
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get unaggregated attestations: %v", err)
}
attestations = append(attestations, unaggAtts...)
isEmptyReq := req.Slot == nil && req.CommitteeIndex == nil
if isEmptyReq {
fmt.Println("return all")
allAtts := make([]*ethpb.Attestation, len(attestations))
for i, att := range attestations {
allAtts[i] = migration.V1Alpha1AttestationToV1(att)
}
return &ethpb.AttestationsPoolResponse{Data: allAtts}, nil
}

filteredAtts := make([]*ethpb.Attestation, 0, len(attestations))
for _, att := range attestations {
bothDefined := req.Slot != nil && req.CommitteeIndex != nil
committeeIndexMatch := req.CommitteeIndex != nil && att.Data.CommitteeIndex == *req.CommitteeIndex
slotMatch := req.Slot != nil && att.Data.Slot == *req.Slot

if bothDefined && committeeIndexMatch && slotMatch {
filteredAtts = append(filteredAtts, migration.V1Alpha1AttestationToV1(att))
} else if !bothDefined && (committeeIndexMatch || slotMatch) {
filteredAtts = append(filteredAtts, migration.V1Alpha1AttestationToV1(att))
}
}
return &ethpb.AttestationsPoolResponse{Data: filteredAtts}, nil
}

// SubmitAttestations submits Attestation object to node. If attestation passes all validation
Expand Down
172 changes: 172 additions & 0 deletions beacon-chain/rpc/beaconv1/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,178 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)

func TestListPoolAttestations(t *testing.T) {
state, err := testutil.NewBeaconState()
require.NoError(t, err)
att1 := &eth.Attestation{
AggregationBits: []byte{1, 10},
Data: &eth.AttestationData{
Slot: 1,
CommitteeIndex: 1,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
Source: &eth.Checkpoint{
Epoch: 1,
Root: bytesutil.PadTo([]byte("sourceroot1"), 32),
},
Target: &eth.Checkpoint{
Epoch: 10,
Root: bytesutil.PadTo([]byte("targetroot1"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature1"), 96),
}
att2 := &eth.Attestation{
AggregationBits: []byte{4, 40},
Data: &eth.AttestationData{
Slot: 4,
CommitteeIndex: 4,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot4"), 32),
Source: &eth.Checkpoint{
Epoch: 4,
Root: bytesutil.PadTo([]byte("sourceroot4"), 32),
},
Target: &eth.Checkpoint{
Epoch: 40,
Root: bytesutil.PadTo([]byte("targetroot4"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature4"), 96),
}
att3 := &eth.Attestation{
AggregationBits: []byte{2, 20},
Data: &eth.AttestationData{
Slot: 2,
CommitteeIndex: 2,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
Source: &eth.Checkpoint{
Epoch: 2,
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
},
Target: &eth.Checkpoint{
Epoch: 20,
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
att4 := &eth.Attestation{
AggregationBits: []byte{2, 20},
Data: &eth.AttestationData{
Slot: 4,
CommitteeIndex: 4,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
Source: &eth.Checkpoint{
Epoch: 2,
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
},
Target: &eth.Checkpoint{
Epoch: 20,
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
att5 := &eth.Attestation{
AggregationBits: []byte{1, 10},
Data: &eth.AttestationData{
Slot: 2,
CommitteeIndex: 4,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot1"), 32),
Source: &eth.Checkpoint{
Epoch: 2,
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
},
Target: &eth.Checkpoint{
Epoch: 20,
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature1"), 96),
}
att6 := &eth.Attestation{
AggregationBits: []byte{2, 20},
Data: &eth.AttestationData{
Slot: 2,
CommitteeIndex: 4,
BeaconBlockRoot: bytesutil.PadTo([]byte("blockroot2"), 32),
Source: &eth.Checkpoint{
Epoch: 2,
Root: bytesutil.PadTo([]byte("sourceroot2"), 32),
},
Target: &eth.Checkpoint{
Epoch: 20,
Root: bytesutil.PadTo([]byte("targetroot2"), 32),
},
},
Signature: bytesutil.PadTo([]byte("signature2"), 96),
}
s := &Server{
ChainInfoFetcher: &chainMock.ChainService{State: state},
AttestationsPool: attestations.NewPool(),
}
require.NoError(t, s.AttestationsPool.SaveAggregatedAttestations([]*eth.Attestation{att1, att2, att3, att4, att5, att6}))

t.Run("empty request", func(t *testing.T) {
req := &ethpb.AttestationsPoolRequest{}
resp, err := s.ListPoolAttestations(context.Background(), req)
require.NoError(t, err)
require.Equal(t, 6, len(resp.Data))
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att1), resp.Data[0])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att2), resp.Data[1])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att3), resp.Data[2])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att4), resp.Data[3])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att5), resp.Data[4])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att6), resp.Data[5])
})

t.Run("slot request", func(t *testing.T) {
slot := eth2types.Slot(2)
req := &ethpb.AttestationsPoolRequest{
Slot: &slot,
}
resp, err := s.ListPoolAttestations(context.Background(), req)
require.NoError(t, err)
require.Equal(t, 3, len(resp.Data))
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att3), resp.Data[0])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att5), resp.Data[1])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att6), resp.Data[2])
assert.DeepEqual(t, att3.Data.Slot, slot)
})

t.Run("index request", func(t *testing.T) {
index := eth2types.CommitteeIndex(4)
req := &ethpb.AttestationsPoolRequest{
CommitteeIndex: &index,
}
resp, err := s.ListPoolAttestations(context.Background(), req)
require.NoError(t, err)
require.Equal(t, 4, len(resp.Data))
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att2), resp.Data[0])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att4), resp.Data[1])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att5), resp.Data[2])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att6), resp.Data[3])
assert.DeepEqual(t, att2.Data.CommitteeIndex, index)
})

t.Run("both slot + index request", func(t *testing.T) {
slot := eth2types.Slot(2)
index := eth2types.CommitteeIndex(4)
req := &ethpb.AttestationsPoolRequest{
Slot: &slot,
CommitteeIndex: &index,
}
resp, err := s.ListPoolAttestations(context.Background(), req)
require.NoError(t, err)
require.Equal(t, 2, len(resp.Data))
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att5), resp.Data[0])
assert.DeepSSZEqual(t, migration.V1Alpha1AttestationToV1(att6), resp.Data[1])
assert.DeepEqual(t, att5.Data.CommitteeIndex, index)
assert.DeepEqual(t, att6.Data.CommitteeIndex, index)
assert.DeepEqual(t, att5.Data.Slot, slot)
assert.DeepEqual(t, att6.Data.Slot, slot)
})
}

func TestListPoolAttesterSlashings(t *testing.T) {
state, err := testutil.NewBeaconState()
require.NoError(t, err)
Expand Down
12 changes: 12 additions & 0 deletions proto/migration/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ func V1Alpha1IndexedAttToV1(v1alpha1Att *ethpb_alpha.IndexedAttestation) *ethpb.
}
}

// V1Alpha1AttestationToV1 converts a v1alpha1 attestation to v1.
func V1Alpha1AttestationToV1(v1alpha1Att *ethpb_alpha.Attestation) *ethpb.Attestation {
if v1alpha1Att == nil {
return &ethpb.Attestation{}
}
return &ethpb.Attestation{
AggregationBits: v1alpha1Att.AggregationBits,
Data: V1Alpha1AttDataToV1(v1alpha1Att.Data),
Signature: v1alpha1Att.Signature,
}
}

// V1Alpha1AttDataToV1 converts a v1alpha1 attestation data to v1.
func V1Alpha1AttDataToV1(v1alpha1AttData *ethpb_alpha.AttestationData) *ethpb.AttestationData {
if v1alpha1AttData == nil || v1alpha1AttData.Source == nil || v1alpha1AttData.Target == nil {
Expand Down

0 comments on commit 9db4eba

Please sign in to comment.