prysm-pulse/validator/db/db.go
Ivan Martinez 4ab0a91e51
Validator Slashing Protection DB (#4389)
* Begin adding DB to validator client

Begin adding ValidatorProposalHistory

Implement most of proposal history

Finish tests

Fix marking a proposal for the first time

Change proposalhistory to not using bit shifting

Add pb.go

Change after proto/slashing added

Finally fix protos

Fix most tests

Fix all tests for double proposal protection

Start initialiing DB in validator client

Add db to validator struct

Add DB to ProposeBlock

Fix test errors and begin mocking

Fix test formatting and pass test for validator protection!

Fix merge issues

Fix renames

Fix tests

* Fix tests

* Fix first startup on DB

* Fix nil check tests

* Fix E2E

* Fix e2e flag

* Fix comments

* Fix for comments

* Move proposal hepers to validator/client to keep DB clean

* Add clear-db flag to validator client

* Fix formatting

* Clear out unintended changes

* Fix build issues

* Fix build issues

* Gazelle

* Fix mock test

* Remove proposal history

* Add terminal confirmation to DB clearing

* Add interface for validatorDB, add context to DB functions

* Add force-clear-db flag

* Cleanup

* Update validator/node/node.go

Co-Authored-By: Raul Jordan <raul@prysmaticlabs.com>

* Change db to clear file, not whole folder

* Fix db test

* Fix teardown test

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
2020-01-08 13:16:17 -05:00

124 lines
3.1 KiB
Go

package db
import (
"context"
"os"
"path/filepath"
"time"
"github.com/boltdb/bolt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
slashpb "github.com/prysmaticlabs/prysm/proto/slashing"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/validator/db/iface"
"github.com/sirupsen/logrus"
)
var log = logrus.WithField("prefix", "db")
var _ = iface.ValidatorDB(&Store{})
var databaseFileName = "validator.db"
// Store defines an implementation of the Prysm Database interface
// using BoltDB as the underlying persistent kv-store for eth2.
type Store struct {
db *bolt.DB
databasePath string
}
// Close closes the underlying boltdb database.
func (db *Store) Close() error {
return db.db.Close()
}
func (db *Store) update(fn func(*bolt.Tx) error) error {
return db.db.Update(fn)
}
func (db *Store) batch(fn func(*bolt.Tx) error) error {
return db.db.Batch(fn)
}
func (db *Store) view(fn func(*bolt.Tx) error) error {
return db.db.View(fn)
}
// ClearDB removes the previously stored directory at the data directory.
func (db *Store) ClearDB() error {
if _, err := os.Stat(db.databasePath); os.IsNotExist(err) {
return nil
}
return os.Remove(filepath.Join(db.databasePath, databaseFileName))
}
// DatabasePath at which this database writes files.
func (db *Store) DatabasePath() string {
return db.databasePath
}
func createBuckets(tx *bolt.Tx, buckets ...[]byte) error {
for _, bucket := range buckets {
if _, err := tx.CreateBucketIfNotExists(bucket); err != nil {
return err
}
}
return nil
}
// NewKVStore initializes a new boltDB key-value store at the directory
// path specified, creates the kv-buckets based on the schema, and stores
// an open connection db object as a property of the Store struct.
func NewKVStore(dirPath string, pubkeys [][48]byte) (*Store, error) {
if err := os.MkdirAll(dirPath, 0700); err != nil {
return nil, err
}
datafile := filepath.Join(dirPath, databaseFileName)
boltDB, err := bolt.Open(datafile, 0600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
if err == bolt.ErrTimeout {
return nil, errors.New("cannot obtain database lock, database may be in use by another process")
}
return nil, err
}
kv := &Store{db: boltDB, databasePath: dirPath}
if err := kv.db.Update(func(tx *bolt.Tx) error {
return createBuckets(
tx,
historicProposalsBucket,
validatorsMinMaxSpanBucket,
)
}); err != nil {
return nil, err
}
// Initialize the required pubkeys into the DB to ensure they're not empty.
for _, pubkey := range pubkeys {
history, err := kv.ProposalHistory(context.Background(), pubkey[:])
if err != nil {
return nil, err
}
if history == nil {
cleanHistory := &slashpb.ProposalHistory{
EpochBits: bitfield.NewBitlist(params.BeaconConfig().WeakSubjectivityPeriod),
}
if err := kv.SaveProposalHistory(context.Background(), pubkey[:], cleanHistory); err != nil {
return nil, err
}
}
}
return kv, err
}
// Size returns the db size in bytes.
func (db *Store) Size() (int64, error) {
var size int64
err := db.db.View(func(tx *bolt.Tx) error {
size = tx.Size()
return nil
})
return size, err
}