Skip to content

Commit

Permalink
Finish refactor for proposal history
Browse files Browse the repository at this point in the history
  • Loading branch information
0xKiwi committed Apr 30, 2020
1 parent 6a4b8a8 commit 2f13720
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 74 deletions.
10 changes: 5 additions & 5 deletions validator/db/attestation_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ func (db *Store) AttestationHistoryForPubKeys(ctx context.Context, publicKeys []
attestationHistory = &slashpb.AttestationHistory{
TargetToSource: newMap,
}
return nil
}
attestationHistory, err := unmarshalAttestationHistory(enc)
if err != nil {
return err
} else {
attestationHistory, err = unmarshalAttestationHistory(enc)
if err != nil {
return err
}
}
attestationHistoryForVals[key] = attestationHistory
}
Expand Down
8 changes: 4 additions & 4 deletions validator/db/attestation_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func TestDeleteAttestationHistory_OK(t *testing.T) {
if err != nil {
t.Fatalf("Failed to get attestation history: %v", err)
}
savedHistory := savedHistories
savedHistory := savedHistories[pubkey]
if savedHistory == nil || !reflect.DeepEqual(savedHistory, history) {
t.Fatalf("Expected DB to keep object the same, received: %v, expected %v", savedHistory, history)
}
Expand All @@ -207,7 +207,7 @@ func TestDeleteAttestationHistory_OK(t *testing.T) {
}

// Check after deleting from DB.
savedHistory, err = db.AttestationHistoryForPubKeys(context.Background(), [][48]byte{pubkey})
savedHistories, err = db.AttestationHistoryForPubKeys(context.Background(), [][48]byte{pubkey})
if err != nil {
t.Fatalf("Failed to get attestation history: %v", err)
}
Expand All @@ -216,7 +216,7 @@ func TestDeleteAttestationHistory_OK(t *testing.T) {
clean := &slashpb.AttestationHistory{
TargetToSource: cleanMap,
}
if !reflect.DeepEqual(savedHistory, clean) {
t.Fatalf("Expected attestation history to be %v, received %v", clean, savedHistory)
if !reflect.DeepEqual(savedHistories[pubkey], clean) {
t.Fatalf("Expected attestation history to be %v, received %v", clean, savedHistories[pubkey])
}
}
8 changes: 4 additions & 4 deletions validator/db/iface/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ type ValidatorDB interface {
DatabasePath() string
ClearDB() error
// Proposer protection related methods.
ProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64) (bitfield.Bitlist, error)
SaveProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64, history bitfield.Bitlist) error
ProposalHistoriesForEpoch(ctx context.Context, publicKeys [][48]byte, epoch uint64) (map[[48]byte]bitfield.Bitlist, error)
SaveProposalHistoriesForEpoch(ctx context.Context, epoch uint64, histories map[[48]byte]bitfield.Bitlist) error
DeleteProposalHistory(ctx context.Context, publicKey []byte) error
// Attester protection related methods.
AttestationHistory(ctx context.Context, publicKey []byte) (*slashpb.AttestationHistory, error)
SaveAttestationHistory(ctx context.Context, publicKey []byte, history *slashpb.AttestationHistory) error
AttestationHistoryForPubKeys(ctx context.Context, publicKeys [][48]byte) (map[[48]byte]*slashpb.AttestationHistory, error)
SaveAttestationHistoryForPubKeys(ctx context.Context, historyByPubKey map[[48]byte]*slashpb.AttestationHistory) error
DeleteAttestationHistory(ctx context.Context, publicKey []byte) error
}
55 changes: 31 additions & 24 deletions validator/db/proposal_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,55 @@ import (
"go.opencensus.io/trace"
)

// ProposalHistoryForEpoch accepts a validator public key and returns the corresponding proposal history.
// ProposalHistoriesForEpoch accepts an array of validator public keys with an epoch and returns the corresponding proposal histories.
// Returns nil if there is no proposal history for the validator.
func (db *Store) ProposalHistoryForEpoch(ctx context.Context, publicKey []byte, epoch uint64) (bitfield.Bitlist, error) {
func (db *Store) ProposalHistoriesForEpoch(ctx context.Context, publicKeys [][48]byte, epoch uint64) (map[[48]byte]bitfield.Bitlist, error) {
ctx, span := trace.StartSpan(ctx, "Validator.ProposalHistoryForEpoch")
defer span.End()

var err error
proposalHistoriesForEpoch := make(map[[48]byte]bitfield.Bitlist)
// Using 5 here since a bitfield length of 32 is always 5 bytes long.
slotBitlist := make(bitfield.Bitlist, 5)
err = db.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 slotBits == nil || len(slotBits) == 0 {
slotBitlist = bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
return nil
for _, pubKey := range publicKeys {
valBucket := bucket.Bucket(pubKey[:])
if valBucket == nil {
return fmt.Errorf("validator history empty for public key %#x", pubKey)
}
slotBits := valBucket.Get(bytesutil.Bytes8(epoch))

slotBitlist := make(bitfield.Bitlist, params.BeaconConfig().SlotsPerEpoch/8+1)
if slotBits == nil || len(slotBits) == 0 {
slotBitlist = bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
} else {
copy(slotBitlist, slotBits)
}
proposalHistoriesForEpoch[pubKey] = slotBitlist
}
copy(slotBitlist, slotBits)
return nil
})
return slotBitlist, err
return proposalHistoriesForEpoch, err
}

// SaveProposalHistoryForEpoch returns the proposal history for the requested validator public key.
func (db *Store) SaveProposalHistoryForEpoch(ctx context.Context, pubKey []byte, epoch uint64, slotBits bitfield.Bitlist) error {
// SaveProposalHistoriesForEpoch saves the provided proposal histories to the indicated validator public keys.
func (db *Store) SaveProposalHistoriesForEpoch(ctx context.Context, epoch uint64, proposalHistoriesForEpoch map[[48]byte]bitfield.Bitlist) error {
ctx, span := trace.StartSpan(ctx, "Validator.SaveProposalHistoryForEpoch")
defer span.End()

err := db.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
}
if err := pruneProposalHistory(valBucket, epoch); err != nil {
return err
for pubKey, history := range proposalHistoriesForEpoch {
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), history); err != nil {
return err
}
if err := pruneProposalHistory(valBucket, epoch); err != nil {
return err
}
}
return nil
})
Expand Down
82 changes: 45 additions & 37 deletions validator/db/proposal_history_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ import (
"github.com/prysmaticlabs/prysm/shared/params"
)

func TestProposalHistoryForEpoch_InitializesNewPubKeys(t *testing.T) {
pubkeys := [][48]byte{{30}, {25}, {20}}
db := SetupDB(t, pubkeys)
func TestProposalHistoriesForEpoch_InitializesNewPubKeys(t *testing.T) {
pubKeys := [][48]byte{{30}, {25}, {20}}
db := SetupDB(t, pubKeys)
defer TeardownDB(t, db)

for _, pub := range pubkeys {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pub[:], 0)
if err != nil {
t.Fatal(err)
}
slotBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), pubKeys, 0)
if err != nil {
t.Fatal(err)
}

cleanBits := bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
if !bytes.Equal(slotBits.Bytes(), cleanBits.Bytes()) {
t.Fatalf("Expected proposal history slot bits to be empty, received %v", slotBits.Bytes())
cleanBits := bitfield.NewBitlist(params.BeaconConfig().SlotsPerEpoch)
for _, pubKey := range pubKeys {
if !bytes.Equal(slotBitsByPubKey[pubKey], cleanBits) {
t.Fatalf("Expected proposal history slot bits to be empty, received %v", slotBitsByPubKey[pubKey])
}
}
}
Expand All @@ -35,7 +35,7 @@ func TestProposalHistoryForEpoch_NilDB(t *testing.T) {
db := SetupDB(t, [][48]byte{})
defer TeardownDB(t, db)

_, err := db.ProposalHistoryForEpoch(context.Background(), valPubkey[:], 0)
_, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{valPubkey}, 0)
if err == nil {
t.Fatal("unexpected non-error")
}
Expand All @@ -45,7 +45,7 @@ func TestProposalHistoryForEpoch_NilDB(t *testing.T) {
}
}

func TestSaveProposalHistoryForEpoch_OK(t *testing.T) {
func TestSaveProposalHistoriesForEpoch_OK(t *testing.T) {
pubkey := [48]byte{3}
db := SetupDB(t, [][48]byte{pubkey})
defer TeardownDB(t, db)
Expand All @@ -54,14 +54,17 @@ func TestSaveProposalHistoryForEpoch_OK(t *testing.T) {
slot := uint64(2)
slotBits := bitfield.Bitlist{0x04, 0x00, 0x00, 0x00, 0x04}

if err := db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], epoch, slotBits); err != nil {
historiesForEpoch := make(map[[48]byte]bitfield.Bitlist)
historiesForEpoch[pubkey] = slotBits

if err := db.SaveProposalHistoriesForEpoch(context.Background(), epoch, historiesForEpoch); err != nil {
t.Fatalf("Saving proposal history failed: %v", err)
}
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], epoch)
savedBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubkey}, epoch)
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}

savedBits := savedBitsByPubKey[pubkey]
if savedBits == nil || !bytes.Equal(slotBits, savedBits) {
t.Fatalf("Expected DB to keep object the same, received: %v", savedBits)
}
Expand Down Expand Up @@ -99,13 +102,16 @@ func TestSaveProposalHistoryForEpoch_Overwrites(t *testing.T) {
for _, tt := range tests {
db := SetupDB(t, [][48]byte{pubkey})
defer TeardownDB(t, db)
if err := db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], 0, tt.slotBits); err != nil {
slotBitsByPubKey := make(map[[48]byte]bitfield.Bitlist)
slotBitsByPubKey[pubkey] = tt.slotBits
if err := db.SaveProposalHistoriesForEpoch(context.Background(), 0, slotBitsByPubKey); err != nil {
t.Fatalf("Saving proposal history failed: %v", err)
}
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], 0)
savedBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubkey}, 0)
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
savedBits := savedBitsByPubKey[pubkey]

if savedBits == nil || !reflect.DeepEqual(savedBits, tt.slotBits) {
t.Fatalf("Expected DB to keep object the same, received: %v, expected %v", savedBits, tt.slotBits)
Expand Down Expand Up @@ -159,23 +165,23 @@ func TestProposalHistoryForEpoch_MultipleEpochs(t *testing.T) {
db := SetupDB(t, [][48]byte{pubKey})
defer TeardownDB(t, db)
for _, slot := range tt.slots {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot))
slotBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubKey}, helpers.SlotToEpoch(slot))
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
slotBits.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
if err := db.SaveProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot), slotBits); err != nil {
slotBitsByPubKey[pubKey].SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
if err := db.SaveProposalHistoriesForEpoch(context.Background(), helpers.SlotToEpoch(slot), slotBitsByPubKey); err != nil {
t.Fatalf("Saving proposal history failed: %v", err)
}
}

for i, slotBits := range tt.expectedBits {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], uint64(i))
savedBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubKey}, uint64(i))
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
if !bytes.Equal(slotBits, savedBits) {
t.Fatalf("unexpected difference in bytes for slots %v, expected %v vs received %v", tt.slots, slotBits, savedBits)
if !bytes.Equal(slotBits, savedBitsByPubKey[pubKey]) {
t.Fatalf("unexpected difference in bytes for slots %v, expected %v vs received %v", tt.slots, slotBits, savedBitsByPubKey[pubKey])
}
}
}
Expand Down Expand Up @@ -213,32 +219,32 @@ func TestPruneProposalHistory_OK(t *testing.T) {
db := SetupDB(t, [][48]byte{pubKey})
defer TeardownDB(t, db)
for _, slot := range tt.slots {
slotBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot))
slotBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubKey}, helpers.SlotToEpoch(slot))
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
slotBits.SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
if err := db.SaveProposalHistoryForEpoch(context.Background(), pubKey[:], helpers.SlotToEpoch(slot), slotBits); err != nil {
slotBitsByPubKey[pubKey].SetBitAt(slot%params.BeaconConfig().SlotsPerEpoch, true)
if err := db.SaveProposalHistoriesForEpoch(context.Background(), helpers.SlotToEpoch(slot), slotBitsByPubKey); err != nil {
t.Fatalf("Saving proposal history failed: %v", err)
}
}

for _, epoch := range tt.removedEpochs {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], epoch)
savedBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubKey}, epoch)
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
if !bytes.Equal(bitfield.NewBitlist(slotsPerEpoch), savedBits) {
t.Fatalf("unexpected difference in bytes for epoch %d, expected %#x vs received %v", epoch, bitfield.NewBitlist(slotsPerEpoch), savedBits)
if !bytes.Equal(bitfield.NewBitlist(slotsPerEpoch), savedBitsByPubKey[pubKey]) {
t.Fatalf("unexpected difference in bytes for epoch %d, expected %#x vs received %v", epoch, bitfield.NewBitlist(slotsPerEpoch), savedBitsByPubKey[pubKey])
}
}
for _, epoch := range tt.storedEpochs {
savedBits, err := db.ProposalHistoryForEpoch(context.Background(), pubKey[:], epoch)
savedBitsByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubKey}, epoch)
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
if bytes.Equal(bitfield.NewBitlist(slotsPerEpoch), savedBits) {
t.Fatalf("unexpected difference in bytes for epoch %d, expected %v vs received %v", epoch, bitfield.NewBitlist(slotsPerEpoch), savedBits)
if bytes.Equal(bitfield.NewBitlist(slotsPerEpoch), savedBitsByPubKey[pubKey]) {
t.Fatalf("unexpected difference in bytes for epoch %d, expected %v vs received %v", epoch, bitfield.NewBitlist(slotsPerEpoch), savedBitsByPubKey[pubKey])
}
}
}
Expand All @@ -250,15 +256,17 @@ func TestDeleteProposalHistory_OK(t *testing.T) {
defer TeardownDB(t, db)

slotBits := bitfield.Bitlist{0x01, 0x00, 0x00, 0x00, 0x02}

if err := db.SaveProposalHistoryForEpoch(context.Background(), pubkey[:], 0, slotBits); err != nil {
slotBitsByPubKey := make(map[[48]byte]bitfield.Bitlist)
slotBitsByPubKey[pubkey] = slotBits
if err := db.SaveProposalHistoriesForEpoch(context.Background(), 0, slotBitsByPubKey); err != nil {
t.Fatalf("Save proposal history failed: %v", err)
}
// Making sure everything is saved.
savedHistory, err := db.ProposalHistoryForEpoch(context.Background(), pubkey[:], 0)
savedHistoryByPubKey, err := db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubkey}, 0)
if err != nil {
t.Fatalf("Failed to get proposal history: %v", err)
}
savedHistory := savedHistoryByPubKey[pubkey]
if savedHistory == nil || !bytes.Equal(savedHistory, slotBits) {
t.Fatalf("Expected DB to keep object the same, received: %v, expected %v", savedHistory, slotBits)
}
Expand All @@ -267,7 +275,7 @@ func TestDeleteProposalHistory_OK(t *testing.T) {
}

// Check after deleting from DB.
_, err = db.ProposalHistoryForEpoch(context.Background(), pubkey[:], 0)
_, err = db.ProposalHistoriesForEpoch(context.Background(), [][48]byte{pubkey}, 0)
if err == nil {
t.Fatalf("Unexpected success in deleting history: %v", err)
}
Expand Down

0 comments on commit 2f13720

Please sign in to comment.