Highest Attestations Endpoint for Optimized Slasher (#9706)

* begin highest atts

* highest atts

* deep source
This commit is contained in:
Raul Jordan
2021-09-29 21:33:28 -05:00
committed by GitHub
parent 62dc74af2f
commit 7dae66afc9
8 changed files with 179 additions and 3 deletions

View File

@@ -147,6 +147,10 @@ type SlasherDatabase interface {
PruneProposalsAtEpoch(
ctx context.Context, maxEpoch types.Epoch,
) (numPruned uint, err error)
HighestAttestations(
ctx context.Context,
indices []types.ValidatorIndex,
) ([]*ethpb.HighestAttestation, error)
DatabasePath() string
ClearDB() error
}

View File

@@ -12,6 +12,7 @@ go_library(
deps = [
"//beacon-chain/slasher:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@org_golang_google_grpc//codes:go_default_library",
"@org_golang_google_grpc//status:go_default_library",
],
@@ -19,11 +20,15 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["server_test.go"],
srcs = [
"attestations_test.go",
"server_test.go",
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/slasher:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
],
)

View File

@@ -3,6 +3,7 @@ package slasher
import (
"context"
types "github.com/prysmaticlabs/eth2-types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -27,8 +28,16 @@ func (s *Server) IsSlashableAttestation(
// HighestAttestations returns the highest source and target epochs attested for
// validator indices that have been observed by slasher.
func (_ *Server) HighestAttestations(
func (s *Server) HighestAttestations(
ctx context.Context, req *ethpb.HighestAttestationRequest,
) (*ethpb.HighestAttestationResponse, error) {
return nil, status.Error(codes.Unimplemented, "Unimplemented")
valIndices := make([]types.ValidatorIndex, len(req.ValidatorIndices))
for i, valIdx := range req.ValidatorIndices {
valIndices[i] = types.ValidatorIndex(valIdx)
}
atts, err := s.SlashingChecker.HighestAttestations(ctx, valIndices)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get highest attestations: %v", err)
}
return &ethpb.HighestAttestationResponse{Attestations: atts}, nil
}

View File

@@ -0,0 +1,63 @@
package slasher
import (
"context"
"testing"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/beacon-chain/slasher"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/require"
)
func TestServer_HighestAttestations(t *testing.T) {
highestAtts := map[types.ValidatorIndex]*ethpb.HighestAttestation{
0: {
ValidatorIndex: 0,
HighestSourceEpoch: 1,
HighestTargetEpoch: 2,
},
1: {
ValidatorIndex: 1,
HighestSourceEpoch: 2,
HighestTargetEpoch: 3,
},
}
mockSlasher := &slasher.MockSlashingChecker{
HighestAtts: highestAtts,
}
s := Server{SlashingChecker: mockSlasher}
ctx := context.Background()
t.Run("single index found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0},
})
require.NoError(t, err)
require.Equal(t, 1, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
})
t.Run("single index not found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{3},
})
require.NoError(t, err)
require.Equal(t, 0, len(resp.Attestations))
})
t.Run("multiple indices all found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0, 1},
})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
require.DeepEqual(t, highestAtts[1], resp.Attestations[1])
})
t.Run("multiple indices some not found", func(t *testing.T) {
resp, err := s.HighestAttestations(ctx, &ethpb.HighestAttestationRequest{
ValidatorIndices: []uint64{0, 3},
})
require.NoError(t, err)
require.Equal(t, 1, len(resp.Attestations))
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
})
}

View File

@@ -3,6 +3,7 @@ package slasher
import (
"context"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
)
@@ -10,6 +11,21 @@ import (
type MockSlashingChecker struct {
AttesterSlashingFound bool
ProposerSlashingFound bool
HighestAtts map[types.ValidatorIndex]*ethpb.HighestAttestation
}
func (s *MockSlashingChecker) HighestAttestations(
_ context.Context, indices []types.ValidatorIndex,
) ([]*ethpb.HighestAttestation, error) {
atts := make([]*ethpb.HighestAttestation, 0, len(indices))
for _, valIdx := range indices {
att, ok := s.HighestAtts[valIdx]
if !ok {
continue
}
atts = append(atts, att)
}
return atts, nil
}
func (s *MockSlashingChecker) IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error) {

View File

@@ -3,12 +3,25 @@ package slasher
import (
"context"
"github.com/pkg/errors"
types "github.com/prysmaticlabs/eth2-types"
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// HighestAttestations committed for an input list of validator indices.
func (s *Service) HighestAttestations(
ctx context.Context, validatorIndices []types.ValidatorIndex,
) ([]*ethpb.HighestAttestation, error) {
atts, err := s.serviceCfg.Database.HighestAttestations(ctx, validatorIndices)
if err != nil {
return nil, errors.Wrap(err, "could not get highest attestations from database")
}
return atts, nil
}
// IsSlashableBlock checks if an input block header is slashable
// with respect to historical block proposal data.
func (s *Service) IsSlashableBlock(

View File

@@ -9,6 +9,7 @@ import (
dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
slashertypes "github.com/prysmaticlabs/prysm/beacon-chain/slasher/types"
"github.com/prysmaticlabs/prysm/config/params"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/testing/assert"
"github.com/prysmaticlabs/prysm/testing/require"
)
@@ -135,3 +136,64 @@ func TestIsSlashableAttestation(t *testing.T) {
})
}
}
func TestService_HighestAttestations(t *testing.T) {
ctx := context.Background()
slasherDB := dbtest.SetupSlasherDB(t)
currentEpoch := types.Epoch(3)
currentTime := time.Now()
totalSlots := uint64(currentEpoch) * uint64(params.BeaconConfig().SlotsPerEpoch)
secondsSinceGenesis := time.Duration(totalSlots * params.BeaconConfig().SecondsPerSlot)
genesisTime := currentTime.Add(-secondsSinceGenesis * time.Second)
s := &Service{
serviceCfg: &ServiceConfig{
Database: slasherDB,
},
params: DefaultParams(),
blksQueue: newBlocksQueue(),
genesisTime: genesisTime,
}
prevAtts := []*slashertypes.IndexedAttestationWrapper{
createAttestationWrapper(t, 0, 1, []uint64{1}, []byte{1}),
createAttestationWrapper(t, 2, 3, []uint64{2}, []byte{1}),
}
err := slasherDB.SaveAttestationRecordsForValidators(ctx, prevAtts)
require.NoError(t, err)
t.Run("single index not found", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{0})
require.NoError(t, err)
require.Equal(t, 0, len(atts))
})
t.Run("single index case 1", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1})
require.NoError(t, err)
require.Equal(t, 1, len(atts))
require.DeepEqual(t, &ethpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0])
})
t.Run("single index case 2", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{2})
require.NoError(t, err)
require.Equal(t, 1, len(atts))
require.DeepEqual(t, &ethpb.HighestAttestation{ValidatorIndex: 2, HighestSourceEpoch: 2, HighestTargetEpoch: 3}, atts[0])
})
t.Run("multiple indices all found", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1, 2})
require.NoError(t, err)
require.Equal(t, 2, len(atts))
require.DeepEqual(t, &ethpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0])
require.DeepEqual(t, &ethpb.HighestAttestation{ValidatorIndex: 2, HighestSourceEpoch: 2, HighestTargetEpoch: 3}, atts[1])
})
t.Run("multiple indices all not found", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{3, 4})
require.NoError(t, err)
require.Equal(t, 0, len(atts))
})
t.Run("multiple indices some not found", func(t *testing.T) {
atts, err := s.HighestAttestations(ctx, []types.ValidatorIndex{1, 4})
require.NoError(t, err)
require.Equal(t, 1, len(atts))
require.DeepEqual(t, &ethpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0])
})
}

View File

@@ -8,6 +8,7 @@ import (
"context"
"time"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/prysm/async/event"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
@@ -40,6 +41,9 @@ type ServiceConfig struct {
type SlashingChecker interface {
IsSlashableBlock(ctx context.Context, proposal *ethpb.SignedBeaconBlockHeader) (*ethpb.ProposerSlashing, error)
IsSlashableAttestation(ctx context.Context, attestation *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error)
HighestAttestations(
ctx context.Context, indices []types.ValidatorIndex,
) ([]*ethpb.HighestAttestation, error)
}
// Service defining a slasher implementation as part of