Remove unused DB functions and proto from Slasher (#4996)

* Remove unused DB functions
* goimports
* Fix bug and improve tests
* Merge branch 'master' of https://github.com/prysmaticlabs/Prysm into slasher-remove-old-db
This commit is contained in:
prylabs-bulldozer[bot]
2020-03-04 22:29:36 +00:00
committed by GitHub
parent 139f51efaa
commit e339b07ac7
5 changed files with 715 additions and 1148 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -27,21 +27,6 @@ message AttesterSlashingResponse {
repeated ethereum.eth.v1alpha1.AttesterSlashing attester_slashing = 1;
}
// CompressedIdxAtt is an indexed attestation with the []byte data root
// in place of its AttestationData.
message CompressedIdxAtt {
repeated uint64 indices = 1 ;
bytes data_root = 2;
// 96 bytes aggregate signature.
bytes signature = 3;
}
// CompressedIdxAttList is a list of CompressedIdxAtts used for
// accessing an array from the DB.
message CompressedIdxAttList {
repeated CompressedIdxAtt list = 1;
}
// In order to detect surrounded attestation we need to compare
// each attestation source to those spans
// see https://github.com/protolambda/eth2-surround/blob/master/README.md#min-max-surround

View File

@@ -21,14 +21,11 @@ type ReadOnlyDatabase interface {
BlockHeaders(ctx context.Context, epoch uint64, validatorID uint64) ([]*ethpb.SignedBeaconBlockHeader, error)
HasBlockHeader(ctx context.Context, epoch uint64, validatorID uint64) bool
// IndexedAttestationsForEpoch related methods.
// IndexedAttestations related methods.
HasIndexedAttestation(ctx context.Context, att *ethpb.IndexedAttestation) (bool, error)
IndexedAttestationsForTarget(ctx context.Context, targetEpoch uint64) ([]*ethpb.IndexedAttestation, error)
IdxAttsForTargetFromID(ctx context.Context, targetEpoch uint64, validatorID uint64) ([]*ethpb.IndexedAttestation, error)
IndexedAttestationsWithPrefix(ctx context.Context, targetEpoch uint64, sigBytes []byte) ([]*ethpb.IndexedAttestation, error)
LatestIndexedAttestationsTargetEpoch(ctx context.Context) (uint64, error)
LatestValidatorIdx(ctx context.Context) (uint64, error)
DoubleVotes(ctx context.Context, validatorIdx uint64, dataRoot []byte, origAtt *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error)
HasIndexedAttestation(ctx context.Context, att *ethpb.IndexedAttestation) (bool, error)
// MinMaxSpan related methods.
ValidatorSpansMap(ctx context.Context, validatorIdx uint64) (*slashpb.EpochSpanMap, error)
@@ -56,7 +53,7 @@ type WriteAccessDatabase interface {
DeleteBlockHeader(ctx context.Context, epoch uint64, validatorID uint64, blockHeader *ethpb.SignedBeaconBlockHeader) error
PruneBlockHistory(ctx context.Context, currentEpoch uint64, pruningEpochAge uint64) error
// IndexedAttestationsForEpoch related methods.
// IndexedAttestations related methods.
SaveIndexedAttestation(ctx context.Context, idxAttestation *ethpb.IndexedAttestation) error
SaveIndexedAttestations(ctx context.Context, idxAttestations []*ethpb.IndexedAttestation) error
DeleteIndexedAttestation(ctx context.Context, idxAttestation *ethpb.IndexedAttestation) error

View File

@@ -3,18 +3,12 @@ package kv
import (
"bytes"
"context"
"fmt"
"reflect"
"sort"
"github.com/boltdb/bolt"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)
@@ -29,71 +23,17 @@ func unmarshalIndexedAttestation(ctx context.Context, enc []byte) (*ethpb.Indexe
return protoIdxAtt, nil
}
func unmarshalCompressedIdxAttList(ctx context.Context, enc []byte) (*slashpb.CompressedIdxAttList, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.unmarshalCompressedIdxAttList")
defer span.End()
protoIdxAtt := &slashpb.CompressedIdxAttList{}
err := proto.Unmarshal(enc, protoIdxAtt)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal encoding")
}
return protoIdxAtt, nil
}
// IdxAttsForTargetFromID accepts a epoch and validator index and returns a list of
// indexed attestations from that validator for the given target epoch.
// Returns nil if the indexed attestation does not exist.
func (db *Store) IdxAttsForTargetFromID(ctx context.Context, targetEpoch uint64, validatorID uint64) ([]*ethpb.IndexedAttestation, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.IdxAttsForTargetFromID")
defer span.End()
var idxAtts []*ethpb.IndexedAttestation
err := db.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(compressedIdxAttsBucket)
enc := bucket.Get(bytesutil.Bytes8(targetEpoch))
if enc == nil {
return nil
}
idToIdxAttsList, err := unmarshalCompressedIdxAttList(ctx, enc)
if err != nil {
return err
}
for _, idxAtt := range idToIdxAttsList.List {
i := sort.Search(len(idxAtt.Indices), func(i int) bool {
return idxAtt.Indices[i] >= validatorID
})
if i < len(idxAtt.Indices) && idxAtt.Indices[i] == validatorID {
idxAttBucket := tx.Bucket(historicIndexedAttestationsBucket)
key := encodeEpochSig(targetEpoch, idxAtt.Signature)
enc = idxAttBucket.Get(key)
if enc == nil {
continue
}
att, err := unmarshalIndexedAttestation(ctx, enc)
if err != nil {
return err
}
idxAtts = append(idxAtts, att)
}
}
return nil
})
return idxAtts, err
}
// IndexedAttestationsForTarget accepts a target epoch and returns a list of
// indexed attestations.
// Returns nil if the indexed attestation does not exist with that target epoch.
func (db *Store) IndexedAttestationsForTarget(ctx context.Context, targetEpoch uint64) ([]*ethpb.IndexedAttestation, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.IdxAttsForTarget")
ctx, span := trace.StartSpan(ctx, "SlasherDB.IndexedAttestationsForTarget")
defer span.End()
var idxAtts []*ethpb.IndexedAttestation
key := bytesutil.Bytes8(targetEpoch)
err := db.view(func(tx *bolt.Tx) error {
c := tx.Bucket(historicIndexedAttestationsBucket).Cursor()
for k, enc := c.Seek(key); k != nil && bytes.Equal(k[:8], key); k, _ = c.Next() {
for k, enc := c.Seek(key); k != nil && bytes.Equal(k[:8], key); k, enc = c.Next() {
idxAtt, err := unmarshalIndexedAttestation(ctx, enc)
if err != nil {
return err
@@ -108,13 +48,13 @@ func (db *Store) IndexedAttestationsForTarget(ctx context.Context, targetEpoch u
// IndexedAttestationsWithPrefix accepts a target epoch and signature bytes to find all attestations with the requested prefix.
// Returns nil if the indexed attestation does not exist with that target epoch.
func (db *Store) IndexedAttestationsWithPrefix(ctx context.Context, targetEpoch uint64, sigBytes []byte) ([]*ethpb.IndexedAttestation, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.IdxAttsForTarget")
ctx, span := trace.StartSpan(ctx, "SlasherDB.IndexedAttestationsWithPrefix")
defer span.End()
var idxAtts []*ethpb.IndexedAttestation
key := encodeEpochSig(targetEpoch, sigBytes[:])
err := db.view(func(tx *bolt.Tx) error {
c := tx.Bucket(historicIndexedAttestationsBucket).Cursor()
for k, enc := c.Seek(key); k != nil && bytes.Equal(k[:len(key)], key); k, _ = c.Next() {
for k, enc := c.Seek(key); k != nil && bytes.Equal(k[:len(key)], key); k, enc = c.Next() {
idxAtt, err := unmarshalIndexedAttestation(ctx, enc)
if err != nil {
return err
@@ -126,78 +66,6 @@ func (db *Store) IndexedAttestationsWithPrefix(ctx context.Context, targetEpoch
return idxAtts, err
}
// LatestIndexedAttestationsTargetEpoch returns latest target epoch in db
// returns 0 if there is no indexed attestations in db.
func (db *Store) LatestIndexedAttestationsTargetEpoch(ctx context.Context) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.LatestIndexedAttestationsTargetEpoch")
defer span.End()
var lt uint64
err := db.view(func(tx *bolt.Tx) error {
c := tx.Bucket(historicIndexedAttestationsBucket).Cursor()
k, _ := c.Last()
if k == nil {
return nil
}
lt = bytesutil.FromBytes8(k[:8])
return nil
})
return lt, err
}
// LatestValidatorIdx returns latest validator id in db
// returns 0 if there is no validators in db.
func (db *Store) LatestValidatorIdx(ctx context.Context) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.LatestValidatorIdx")
defer span.End()
var lt uint64
err := db.view(func(tx *bolt.Tx) error {
c := tx.Bucket(compressedIdxAttsBucket).Cursor()
k, _ := c.Last()
if k == nil {
return nil
}
lt = bytesutil.FromBytes8(k[:8])
return nil
})
return lt, err
}
// DoubleVotes looks up db for slashable attesting data that were preformed by the same validator.
func (db *Store) DoubleVotes(ctx context.Context, validatorIdx uint64, dataRoot []byte, origAtt *ethpb.IndexedAttestation) ([]*ethpb.AttesterSlashing, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.DoubleVotes")
defer span.End()
idxAtts, err := db.IdxAttsForTargetFromID(ctx, origAtt.Data.Target.Epoch, validatorIdx)
if err != nil {
return nil, err
}
if idxAtts == nil || len(idxAtts) == 0 {
return nil, fmt.Errorf("can't check nil indexed attestation for double vote")
}
var idxAttsToSlash []*ethpb.IndexedAttestation
for _, att := range idxAtts {
if att.Data == nil {
continue
}
root, err := hashutil.HashProto(att.Data)
if err != nil {
return nil, err
}
if !bytes.Equal(root[:], dataRoot) {
idxAttsToSlash = append(idxAttsToSlash, att)
}
}
var as []*ethpb.AttesterSlashing
for _, idxAtt := range idxAttsToSlash {
as = append(as, &ethpb.AttesterSlashing{
Attestation_1: origAtt,
Attestation_2: idxAtt,
})
}
return as, nil
}
// HasIndexedAttestation accepts an attestation and returns true if it exists in the DB.
func (db *Store) HasIndexedAttestation(ctx context.Context, att *ethpb.IndexedAttestation) (bool, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.HasIndexedAttestation")
@@ -240,30 +108,21 @@ func (db *Store) SaveIndexedAttestation(ctx context.Context, idxAttestation *eth
return err
})
// Prune history to max size every PruneSlasherStoragePeriod epoch.
if idxAttestation.Data.Source.Epoch%params.BeaconConfig().PruneSlasherStoragePeriod == 0 {
wsPeriod := params.BeaconConfig().WeakSubjectivityPeriod
if err = db.PruneAttHistory(ctx, idxAttestation.Data.Source.Epoch, wsPeriod); err != nil {
return err
}
}
return err
}
// SaveIndexedAttestations accepts multiple indexed attestations and writes them to the DB.
func (db *Store) SaveIndexedAttestations(ctx context.Context, idxAttestations []*ethpb.IndexedAttestation) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.SaveIndexedAttestation")
ctx, span := trace.StartSpan(ctx, "SlasherDB.SaveIndexedAttestations")
defer span.End()
keys := make([][]byte, len(idxAttestations))
marshaledAtts := make([][]byte, len(idxAttestations))
for i, att := range idxAttestations {
key := encodeEpochSig(att.Data.Target.Epoch, att.Signature)
enc, err := proto.Marshal(att)
if err != nil {
return errors.Wrap(err, "failed to marshal")
}
keys[i] = key
keys[i] = encodeEpochSig(att.Data.Target.Epoch, att.Signature)
marshaledAtts[i] = enc
}
@@ -284,37 +143,6 @@ func (db *Store) SaveIndexedAttestations(ctx context.Context, idxAttestations []
return err
}
func saveCompressedIdxAttToEpochList(ctx context.Context, idxAttestation *ethpb.IndexedAttestation, tx *bolt.Tx) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.saveCompressedIdxAttToEpochList")
defer span.End()
dataRoot, err := hashutil.HashProto(idxAttestation.Data)
if err != nil {
return errors.Wrap(err, "failed to hash indexed attestation data.")
}
protoIdxAtt := &slashpb.CompressedIdxAtt{
Signature: idxAttestation.Signature,
Indices: idxAttestation.AttestingIndices,
DataRoot: dataRoot[:],
}
key := bytesutil.Bytes8(idxAttestation.Data.Target.Epoch)
bucket := tx.Bucket(compressedIdxAttsBucket)
enc := bucket.Get(key)
compressedIdxAttList, err := unmarshalCompressedIdxAttList(ctx, enc)
if err != nil {
return errors.Wrap(err, "failed to decode value into CompressedIdxAtt")
}
compressedIdxAttList.List = append(compressedIdxAttList.List, protoIdxAtt)
enc, err = proto.Marshal(compressedIdxAttList)
if err != nil {
return errors.Wrap(err, "failed to marshal")
}
if err := bucket.Put(key, enc); err != nil {
return errors.Wrap(err, "failed to save indexed attestation into historical bucket")
}
return nil
}
// DeleteIndexedAttestation deletes a indexed attestation using the slot and its root as keys in their respective buckets.
func (db *Store) DeleteIndexedAttestation(ctx context.Context, idxAttestation *ethpb.IndexedAttestation) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.DeleteIndexedAttestation")
@@ -327,55 +155,12 @@ func (db *Store) DeleteIndexedAttestation(ctx context.Context, idxAttestation *e
return nil
}
if err := bucket.Delete(key); err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
return errors.Wrapf(rollbackErr, "failed to rollback after %v", err)
}
return errors.Wrap(err, "failed to delete indexed attestation from historical bucket")
}
return nil
})
}
func removeIdxAttIndicesByEpochFromDB(ctx context.Context, idxAttestation *ethpb.IndexedAttestation, tx *bolt.Tx) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.removeIdxAttIndicesByEpochFromDB")
defer span.End()
dataRoot, err := hashutil.HashProto(idxAttestation.Data)
if err != nil {
return err
}
protoIdxAtt := &slashpb.CompressedIdxAtt{
Signature: idxAttestation.Signature,
Indices: idxAttestation.AttestingIndices,
DataRoot: dataRoot[:],
}
key := bytesutil.Bytes8(idxAttestation.Data.Target.Epoch)
bucket := tx.Bucket(compressedIdxAttsBucket)
enc := bucket.Get(key)
if enc == nil {
return errors.New("requested to delete data that is not present")
}
vIdxList, err := unmarshalCompressedIdxAttList(ctx, enc)
if err != nil {
return errors.Wrap(err, "failed to decode value into ValidatorIDToIndexedAttestationList")
}
for i, attIdx := range vIdxList.List {
if reflect.DeepEqual(attIdx, protoIdxAtt) {
copy(vIdxList.List[i:], vIdxList.List[i+1:])
vIdxList.List[len(vIdxList.List)-1] = nil // or the zero value of T
vIdxList.List = vIdxList.List[:len(vIdxList.List)-1]
break
}
}
enc, err = proto.Marshal(vIdxList)
if err != nil {
return errors.Wrap(err, "failed to marshal")
}
if err := bucket.Put(key, enc); err != nil {
return errors.Wrap(err, "failed to include indexed attestation in the historical bucket")
}
return nil
}
// PruneAttHistory removes all attestations from the DB older than the pruning epoch age.
func (db *Store) PruneAttHistory(ctx context.Context, currentEpoch uint64, pruningEpochAge uint64) error {
ctx, span := trace.StartSpan(ctx, "SlasherDB.pruneAttHistory")
@@ -394,14 +179,24 @@ func (db *Store) PruneAttHistory(ctx context.Context, currentEpoch uint64, pruni
return errors.Wrap(err, "failed to delete indexed attestation from historical bucket")
}
}
idxBucket := tx.Bucket(compressedIdxAttsBucket)
c = tx.Bucket(compressedIdxAttsBucket).Cursor()
for k, _ := c.First(); k != nil && bytes.Compare(k[:8], max) <= 0; k, _ = c.Next() {
if err := idxBucket.Delete(k); err != nil {
return errors.Wrap(err, "failed to delete indexed attestation from historical bucket")
}
}
return nil
})
}
// LatestIndexedAttestationsTargetEpoch returns latest target epoch in db
// returns 0 if there is no indexed attestations in db.
func (db *Store) LatestIndexedAttestationsTargetEpoch(ctx context.Context) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "SlasherDB.LatestIndexedAttestationsTargetEpoch")
defer span.End()
var lt uint64
err := db.view(func(tx *bolt.Tx) error {
c := tx.Bucket(historicIndexedAttestationsBucket).Cursor()
k, _ := c.Last()
if k == nil {
return nil
}
lt = bytesutil.FromBytes8(k[:8])
return nil
})
return lt, err
}

View File

@@ -48,10 +48,20 @@ func init() {
Signature: []byte{5, 6},
},
},
{
idxAtt: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 1},
Target: &ethpb.Checkpoint{Epoch: 3},
},
Signature: []byte{5, 6},
},
},
}
}
func TestNilDBHistoryIdxAtt(t *testing.T) {
func TestHasIndexedAttestation_NilDB(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
@@ -75,8 +85,7 @@ func TestSaveIndexedAttestation(t *testing.T) {
ctx := context.Background()
for _, tt := range tests {
err := db.SaveIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
if err := db.SaveIndexedAttestation(ctx, tt.idxAtt); err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
@@ -91,68 +100,631 @@ func TestSaveIndexedAttestation(t *testing.T) {
}
}
func TestIndexedAttestationWithPrefix(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
defer teardownDB(t, db)
ctx := context.Background()
func TestIndexedAttestationsWithPrefix(t *testing.T) {
type prefixTestStruct struct {
name string
attsInDB []*ethpb.IndexedAttestation
targetEpoch uint64
searchPrefix []byte
expectedResult []*ethpb.IndexedAttestation
}
prefixTests := []prefixTestStruct{
{
name: "single item, same sig, should find one",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
searchPrefix: []byte{1, 2},
targetEpoch: 1,
expectedResult: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
},
{
name: "multiple item, same sig, should find 3",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{1, 2, 5},
},
},
searchPrefix: []byte{1, 2},
targetEpoch: 1,
expectedResult: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{1, 2, 5},
},
},
},
{
name: "multiple items, different sig and epoch, should find 2",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{1, 2, 5},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 3, 1},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{0, 2, 4},
},
{
AttestingIndices: []uint64{4},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 9},
},
},
searchPrefix: []byte{1, 2},
targetEpoch: 2,
expectedResult: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{4},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 9},
},
},
},
{
name: "multiple items, different sigs, should not find any atts",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{3, 5, 3},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{3, 5, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{1, 2, 5},
},
},
searchPrefix: []byte{3, 5},
targetEpoch: 1,
},
}
for _, tt := range prefixTests {
t.Run(tt.name, func(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
defer teardownDB(t, db)
ctx := context.Background()
for _, tt := range tests {
err := db.SaveIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
if err := db.SaveIndexedAttestations(ctx, tt.attsInDB); err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
for _, att := range tt.attsInDB {
found, err := db.HasIndexedAttestation(ctx, att)
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("Expected to save %v", att)
}
}
idxAtts, err := db.IndexedAttestationsWithPrefix(ctx, tt.idxAtt.Data.Target.Epoch, tt.idxAtt.Signature[:2])
if err != nil {
t.Fatalf("failed to get indexed attestation: %v", err)
}
idxAtts, err := db.IndexedAttestationsWithPrefix(ctx, tt.targetEpoch, tt.searchPrefix)
if err != nil {
t.Fatalf("failed to get indexed attestation: %v", err)
}
if idxAtts == nil || !reflect.DeepEqual(idxAtts[0], tt.idxAtt) {
t.Fatalf("get should return indexed attestation: %v", idxAtts)
}
if !reflect.DeepEqual(tt.expectedResult, idxAtts) {
t.Fatalf("Expected %v, received: %v", tt.expectedResult, idxAtts)
}
})
}
}
func TestIndexedAttestationsForTarget(t *testing.T) {
type prefixTestStruct struct {
name string
attsInDB []*ethpb.IndexedAttestation
targetEpoch uint64
expectedResult []*ethpb.IndexedAttestation
}
prefixTests := []prefixTestStruct{
{
name: "single item, should find one",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
targetEpoch: 1,
expectedResult: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
},
{
name: "multiple items, same epoch, should find 3",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 5, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{8, 2, 5},
},
},
targetEpoch: 3,
expectedResult: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 5, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{8, 2, 5},
},
},
},
{
name: "multiple items, different epochs, should not find any atts",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{3, 5, 3},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 2},
BeaconBlockRoot: []byte("hi there"),
},
Signature: []byte{3, 5, 3},
},
{
AttestingIndices: []uint64{1},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
BeaconBlockRoot: []byte("hi there 2"),
},
Signature: []byte{1, 2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 5},
BeaconBlockRoot: []byte("hi there 3"),
},
Signature: []byte{1, 2, 5},
},
},
targetEpoch: 4,
},
}
for _, tt := range prefixTests {
t.Run(tt.name, func(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
defer teardownDB(t, db)
ctx := context.Background()
if err := db.SaveIndexedAttestations(ctx, tt.attsInDB); err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
for _, att := range tt.attsInDB {
found, err := db.HasIndexedAttestation(ctx, att)
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("Expected to save %v", att)
}
}
idxAtts, err := db.IndexedAttestationsForTarget(ctx, tt.targetEpoch)
if err != nil {
t.Fatalf("failed to get indexed attestation: %v", err)
}
if !reflect.DeepEqual(tt.expectedResult, idxAtts) {
t.Fatalf("Expected %v, received: %v", tt.expectedResult, idxAtts)
}
})
}
}
func TestDeleteIndexedAttestation(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
defer teardownDB(t, db)
ctx := context.Background()
for _, tt := range tests {
err := db.SaveIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
type deleteTestStruct struct {
name string
attsInDB []*ethpb.IndexedAttestation
deleteAtts []*ethpb.IndexedAttestation
foundArray []bool
}
for _, tt := range tests {
found, err := db.HasIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
t.Fatalf("failed to get index attestation: %v", err)
}
if !found {
t.Fatalf("Expected indexed attestation: %v", tt.idxAtt)
}
err = db.DeleteIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
t.Fatalf("delete index attestation failed: %v", err)
}
found, err = db.HasIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
t.Fatal(err)
}
if found {
t.Error("Expected indexed attestation to be deleted")
}
deleteTests := []deleteTestStruct{
{
name: "single item, should delete all",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
deleteAtts: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
},
foundArray: []bool{false},
},
{
name: "multiple items, should delete 2",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 3},
},
Signature: []byte{2, 4},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 4},
},
Signature: []byte{3, 5},
},
},
deleteAtts: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 4},
},
Signature: []byte{3, 5},
},
},
foundArray: []bool{false, true, false},
},
{
name: "multiple similar items, should delete 1",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 2},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 4},
},
},
deleteAtts: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 3},
},
},
foundArray: []bool{true, false, true},
},
{
name: "should not delete any if not in list",
attsInDB: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 2},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 3},
},
{
AttestingIndices: []uint64{0},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 4},
},
},
deleteAtts: []*ethpb.IndexedAttestation{
{
AttestingIndices: []uint64{3},
Data: &ethpb.AttestationData{
Source: &ethpb.Checkpoint{Epoch: 0},
Target: &ethpb.Checkpoint{Epoch: 1},
},
Signature: []byte{1, 2, 6},
},
},
foundArray: []bool{true, true, true},
},
}
for _, tt := range deleteTests {
t.Run(tt.name, func(t *testing.T) {
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
db := setupDB(t, cli.NewContext(app, set, nil))
defer teardownDB(t, db)
ctx := context.Background()
if err := db.SaveIndexedAttestations(ctx, tt.attsInDB); err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
for _, att := range tt.attsInDB {
found, err := db.HasIndexedAttestation(ctx, att)
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatalf("Expected to save %v", att)
}
}
for _, att := range tt.deleteAtts {
if err := db.DeleteIndexedAttestation(ctx, att); err != nil {
t.Fatal(err)
}
}
for i, att := range tt.attsInDB {
found, err := db.HasIndexedAttestation(ctx, att)
if err != nil {
t.Fatal(err)
}
if found != tt.foundArray[i] {
t.Fatalf("Expected found to be %t: %v", tt.foundArray[i], att)
}
}
})
}
}
func TestHasIndexedAttestation(t *testing.T) {
@@ -195,8 +767,7 @@ func TestPruneHistoryIndexedAttestation(t *testing.T) {
ctx := context.Background()
for _, tt := range tests {
err := db.SaveIndexedAttestation(ctx, tt.idxAtt)
if err != nil {
if err := db.SaveIndexedAttestation(ctx, tt.idxAtt); err != nil {
t.Fatalf("save indexed attestation failed: %v", err)
}
@@ -209,10 +780,9 @@ func TestPruneHistoryIndexedAttestation(t *testing.T) {
t.Fatal("Expected to find attestation in DB")
}
}
currentEpoch := uint64(3)
currentEpoch := uint64(2)
historyToKeep := uint64(1)
err := db.PruneAttHistory(ctx, currentEpoch, historyToKeep)
if err != nil {
if err := db.PruneAttHistory(ctx, currentEpoch, historyToKeep); err != nil {
t.Fatalf("failed to prune: %v", err)
}
@@ -222,7 +792,7 @@ func TestPruneHistoryIndexedAttestation(t *testing.T) {
t.Fatal(err)
}
if tt.idxAtt.Data.Source.Epoch > currentEpoch-historyToKeep {
if tt.idxAtt.Data.Target.Epoch > currentEpoch-historyToKeep {
if !exists {
t.Fatal("Expected to find attestation newer than prune age in DB")
}