Import proposal protection (#7430)

* Import proposals protection db

* Import old proposal protection data structure to new one

* test empty slots

* Update validator/db/kv/new_proposal_history.go

Co-authored-by: Victor Farazdagi <simple.square@gmail.com>

* Update validator/db/kv/new_proposal_history_test.go

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Victor Farazdagi <simple.square@gmail.com>
This commit is contained in:
Shay Zluf 2020-10-08 14:51:50 +03:00 committed by GitHub
parent 25ebed9a70
commit 1315a15d9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 3 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
@ -43,9 +44,9 @@ func (store *Store) SaveProposalHistoryForSlot(ctx context.Context, pubKey []byt
err := store.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(newhistoricProposalsBucket)
valBucket := bucket.Bucket(pubKey)
if valBucket == nil {
return fmt.Errorf("validator history is empty for validator %#x", pubKey)
valBucket, err := bucket.CreateBucketIfNotExists(pubKey)
if err != nil {
return fmt.Errorf("could not create bucket for public key %#x", pubKey)
}
if err := valBucket.Put(bytesutil.Uint64ToBytesBigEndian(slot), signingRoot); err != nil {
return err
@ -58,6 +59,78 @@ func (store *Store) SaveProposalHistoryForSlot(ctx context.Context, pubKey []byt
return err
}
// ImportProposalHistory accepts a validator public key and returns the corresponding signing root.
// Returns nil if there is no proposal history for the validator at this slot.
func (store *Store) ImportProposalHistory(ctx context.Context) error {
ctx, span := trace.StartSpan(ctx, "Validator.ImportProposalHistory")
defer span.End()
var allKeys [][]byte
err := store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(historicProposalsBucket)
if err := proposalsBucket.ForEach(func(pubKey, _ []byte) error {
pubKeyCopy := make([]byte, len(pubKey))
copy(pubKeyCopy, pubKey)
allKeys = append(allKeys, pubKeyCopy)
return nil
}); err != nil {
return errors.Wrapf(err, "could not retrieve proposals for source in %s", store.databasePath)
}
return nil
})
if err != nil {
return err
}
allKeys = removeDuplicateKeys(allKeys)
var prs []*pubKeyProposals
err = store.db.View(func(tx *bolt.Tx) error {
proposalsBucket := tx.Bucket(historicProposalsBucket)
for _, pk := range allKeys {
pr, err := getPubKeyProposals(pk, proposalsBucket)
prs = append(prs, pr)
if err != nil {
return errors.Wrap(err, "could not retrieve public key old proposals format")
}
}
return nil
})
if err != nil {
return err
}
err = store.db.Update(func(tx *bolt.Tx) error {
newProposalsBucket := tx.Bucket(newhistoricProposalsBucket)
for _, pr := range prs {
valBucket, err := newProposalsBucket.CreateBucketIfNotExists(pr.PubKey)
if err != nil {
return errors.Wrap(err, "could not could not create bucket for public key")
}
for _, epochProposals := range pr.Proposals {
// Adding an extra byte for the bitlist length.
slotBitlist := make(bitfield.Bitlist, params.BeaconConfig().SlotsPerEpoch/8+1)
slotBits := epochProposals.Proposals
if len(slotBits) == 0 {
continue
}
copy(slotBitlist, slotBits)
for i := uint64(0); i < params.BeaconConfig().SlotsPerEpoch; i++ {
if slotBitlist.BitAt(i) {
ss, err := helpers.StartSlot(bytesutil.FromBytes8(epochProposals.Epoch))
if err != nil {
return err
}
if err := valBucket.Put(bytesutil.Uint64ToBytesBigEndian(ss+i), []byte{1}); err != nil {
return err
}
}
}
}
}
return nil
})
return err
}
// UpdatePublicKeysNewBuckets for a specified list of keys.
func (store *Store) UpdatePublicKeysNewBuckets(pubKeys [][48]byte) error {
return store.update(func(tx *bolt.Tx) error {

View File

@ -4,6 +4,7 @@ import (
"context"
"testing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
@ -137,3 +138,42 @@ func TestPruneProposalHistoryBySlot_OK(t *testing.T) {
}
}
}
func TestStore_ImportProposalHistory(t *testing.T) {
pubkey := [48]byte{3}
ctx := context.Background()
db := setupDB(t, [][48]byte{pubkey})
proposedSlots := make(map[uint64]bool)
proposedSlots[0] = true
proposedSlots[1] = true
proposedSlots[20] = true
proposedSlots[31] = true
proposedSlots[32] = true
proposedSlots[33] = true
proposedSlots[1023] = true
proposedSlots[1024] = true
proposedSlots[1025] = true
lastIndex := 1025 + params.BeaconConfig().SlotsPerEpoch
for slot := range proposedSlots {
slotBitlist, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], helpers.SlotToEpoch(slot))
require.NoError(t, err)
slotBitlist.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
err = db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], helpers.SlotToEpoch(slot), slotBitlist)
require.NoError(t, err)
}
err := db.ImportProposalHistory(ctx)
require.NoError(t, err)
for slot := uint64(0); slot <= lastIndex; slot++ {
if _, ok := proposedSlots[slot]; ok {
root, err := db.ProposalHistoryForSlot(ctx, pubkey[:], slot)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{1}, 32), root, "slot: %d", slot)
continue
}
root, err := db.ProposalHistoryForSlot(ctx, pubkey[:], slot)
require.NoError(t, err)
require.DeepEqual(t, bytesutil.PadTo([]byte{}, 32), root)
}
}