prysm-pulse/validator/db/kv/proposal_history.go
Shay Zluf acb47f2920
Implement Standard Slashing Protection JSON With Importing Logic (#7675)
* Use new attestation protection

* tests fixes

* fix tests

* fix comment

* fix TestSetTargetData

* fix tests

* empty history handling

* fix another test

* mock domain request

* fix empty handling

* use far future epoch

* use far future epoch

* migrate data

* copy byte array to resolve sigbus error

* init validator protection on pre validation

* Import interchange json

* Import interchange json

* reduce visibility

* use return value

* raul feedback

* rename fixes

* import test

* checkout att v2 changes

* define import method for interchange format in its own package

* rename and made operations atomic

* eip comment

* begin amending test file

* finish happy path for import tests

* attempt the interchange import tests

* fixed tests

* happy and sad paths tested

* good error messages

* fix up comment with proper eip link

* tests for helpers

* helpers

* all tests pass

* proper test comment

* terence feedback

* validate metadata func

* versioning check

* begin handling duplicatesz

* handle duplicate public keys with potentially different data, first pass

* better handling of duplicate data

* ensure duplicates are taken care of

* comprehensive tests for deduplication of signed blocks

* tests for deduplication

* Update validator/slashing-protection/local/standard-protection-format/helpers_test.go

Co-authored-by: Shay Zluf <thezluf@gmail.com>

* Update validator/slashing-protection/local/standard-protection-format/helpers_test.go

Co-authored-by: Shay Zluf <thezluf@gmail.com>

* tests for maxuint64 and package level comment

* tests passing

* edge cases pass

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-11-17 22:37:43 +00:00

100 lines
3.2 KiB
Go

package kv
import (
"context"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/wealdtech/go-bytesutil"
bolt "go.etcd.io/bbolt"
"go.opencensus.io/trace"
)
// ProposalHistoryForPubkey for a validator public key.
type ProposalHistoryForPubkey struct {
Proposals []Proposal
}
type Proposal struct {
Slot uint64 `json:"slot"`
SigningRoot []byte `json:"signing_root"`
}
// ProposalHistoryForEpoch accepts a validator public key and returns the corresponding proposal history.
// Returns nil if there is no proposal history for the validator.
func (store *Store) ProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64) (bitfield.Bitlist, error) {
ctx, span := trace.StartSpan(ctx, "Validator.ProposalHistoryForEpoch")
defer span.End()
var err error
// Adding an extra byte for the bitlist length.
slotBitlist := make(bitfield.Bitlist, params.BeaconConfig().SlotsPerEpoch/8+1)
err = store.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
valBucket := bucket.Bucket(publicKey)
if valBucket == nil {
return fmt.Errorf("validator history empty for public key %#x", publicKey)
}
slotBits := valBucket.Get(bytesutil.Bytes8(epoch))
if len(slotBits) == 0 {
slotBitlist = bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
return nil
}
copy(slotBitlist, slotBits)
return nil
})
return slotBitlist, err
}
// SaveProposalHistoryForEpoch saves the proposal history for the requested validator public key.
func (store *Store) SaveProposalHistoryForEpoch(ctx context.Context, pubKey []byte, epoch uint64, slotBits bitfield.Bitlist) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveProposalHistoryForEpoch")
defer span.End()
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
valBucket := bucket.Bucket(pubKey)
if valBucket == nil {
return fmt.Errorf("validator history is empty for validator %#x", pubKey)
}
if err := valBucket.Put(bytesutil.Bytes8(epoch), slotBits); err != nil {
return err
}
return pruneProposalHistory(valBucket, epoch)
})
return err
}
// UpdatePublicKeysBuckets for a specified list of keys.
func (store *Store) OldUpdatePublicKeysBuckets(pubKeys [][48]byte) error {
return store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(historicProposalsBucket)
for _, pubKey := range pubKeys {
if _, err := bucket.CreateBucketIfNotExists(pubKey[:]); err != nil {
return errors.Wrap(err, "failed to create proposal history bucket")
}
}
return nil
})
}
func pruneProposalHistory(valBucket *bolt.Bucket, newestEpoch uint64) error {
c := valBucket.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.First() {
epoch := binary.LittleEndian.Uint64(k)
// Only delete epochs that are older than the weak subjectivity period.
if epoch+params.BeaconConfig().WeakSubjectivityPeriod <= newestEpoch {
if err := c.Delete(); err != nil {
return errors.Wrapf(err, "could not prune epoch %d in proposal history", epoch)
}
} else {
// If starting from the oldest, we dont find anything prunable, stop pruning.
break
}
}
return nil
}