mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Highest Attestations Endpoint for Optimized Slasher (#9706)
* begin highest atts * highest atts * deep source
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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 ðpb.HighestAttestationResponse{Attestations: atts}, nil
|
||||
}
|
||||
|
||||
63
beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go
Normal file
63
beacon-chain/rpc/prysm/v1alpha1/slasher/attestations_test.go
Normal 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, ðpb.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, ðpb.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, ðpb.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, ðpb.HighestAttestationRequest{
|
||||
ValidatorIndices: []uint64{0, 3},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(resp.Attestations))
|
||||
require.DeepEqual(t, highestAtts[0], resp.Attestations[0])
|
||||
})
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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, ðpb.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, ðpb.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, ðpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0])
|
||||
require.DeepEqual(t, ðpb.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, ðpb.HighestAttestation{ValidatorIndex: 1, HighestSourceEpoch: 0, HighestTargetEpoch: 1}, atts[0])
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user