github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/db/slasherkv/kv.go (about)

     1  // Package slasherkv defines a bolt-db, key-value store implementation
     2  // of the slasher database interface for Prysm.
     3  package slasherkv
     4  
     5  import (
     6  	"context"
     7  	"os"
     8  	"path"
     9  	"time"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/db/iface"
    13  	"github.com/prysmaticlabs/prysm/shared/fileutil"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  	bolt "go.etcd.io/bbolt"
    16  )
    17  
    18  var _ iface.SlasherDatabase = (*Store)(nil)
    19  
    20  const (
    21  	// DatabaseFileName is the name of the beacon node database.
    22  	DatabaseFileName = "slasher.db"
    23  	boltAllocSize    = 8 * 1024 * 1024
    24  )
    25  
    26  // Config for the bolt db kv store.
    27  type Config struct {
    28  	InitialMMapSize int
    29  }
    30  
    31  // Store defines an implementation of the Prysm Database interface
    32  // using BoltDB as the underlying persistent kv-store for Ethereum consensus.
    33  type Store struct {
    34  	db           *bolt.DB
    35  	databasePath string
    36  	ctx          context.Context
    37  }
    38  
    39  // NewKVStore initializes a new boltDB key-value store at the directory
    40  // path specified, creates the kv-buckets based on the schema, and stores
    41  // an open connection db object as a property of the Store struct.
    42  func NewKVStore(ctx context.Context, dirPath string, config *Config) (*Store, error) {
    43  	hasDir, err := fileutil.HasDir(dirPath)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	if !hasDir {
    48  		if err := fileutil.MkdirAll(dirPath); err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  	datafile := path.Join(dirPath, DatabaseFileName)
    53  	boltDB, err := bolt.Open(
    54  		datafile,
    55  		params.BeaconIoConfig().ReadWritePermissions,
    56  		&bolt.Options{
    57  			Timeout:         1 * time.Second,
    58  			InitialMmapSize: config.InitialMMapSize,
    59  		},
    60  	)
    61  	if err != nil {
    62  		if errors.Is(err, bolt.ErrTimeout) {
    63  			return nil, errors.New("cannot obtain database lock, database may be in use by another process")
    64  		}
    65  		return nil, err
    66  	}
    67  	boltDB.AllocSize = boltAllocSize
    68  	kv := &Store{
    69  		db:           boltDB,
    70  		databasePath: dirPath,
    71  		ctx:          ctx,
    72  	}
    73  
    74  	if err := kv.db.Update(func(tx *bolt.Tx) error {
    75  		return createBuckets(
    76  			tx,
    77  			// Slasher buckets.
    78  			attestedEpochsByValidator,
    79  			attestationRecordsBucket,
    80  			attestationDataRootsBucket,
    81  			proposalRecordsBucket,
    82  			slasherChunksBucket,
    83  		)
    84  	}); err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	return kv, err
    89  }
    90  
    91  // ClearDB removes the previously stored database in the data directory.
    92  func (s *Store) ClearDB() error {
    93  	if _, err := os.Stat(s.databasePath); os.IsNotExist(err) {
    94  		return nil
    95  	}
    96  	if err := os.Remove(path.Join(s.databasePath, DatabaseFileName)); err != nil {
    97  		return errors.Wrap(err, "could not remove database file")
    98  	}
    99  	return nil
   100  }
   101  
   102  // Close closes the underlying BoltDB database.
   103  func (s *Store) Close() error {
   104  	return s.db.Close()
   105  }
   106  
   107  // DatabasePath at which this database writes files.
   108  func (s *Store) DatabasePath() string {
   109  	return s.databasePath
   110  }
   111  
   112  func createBuckets(tx *bolt.Tx, buckets ...[]byte) error {
   113  	for _, bucket := range buckets {
   114  		if _, err := tx.CreateBucketIfNotExists(bucket); err != nil {
   115  			return err
   116  		}
   117  	}
   118  	return nil
   119  }