2018-03-17 11:53:21 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
2020-08-30 08:42:16 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
2018-03-17 11:53:21 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
|
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
|
|
|
)
|
|
|
|
|
2019-09-10 14:22:21 +00:00
|
|
|
// LevelDBOptions configuration for LevelDB.
|
|
|
|
type LevelDBOptions struct {
|
|
|
|
DataDirectoryPath string `yaml:"DataDirectoryPath"`
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// LevelDBStore is the official storage implementation for storing and retrieving
|
2018-03-17 11:53:21 +00:00
|
|
|
// blockchain data.
|
|
|
|
type LevelDBStore struct {
|
|
|
|
db *leveldb.DB
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// NewLevelDBStore returns a new LevelDBStore object that will
|
2018-03-17 11:53:21 +00:00
|
|
|
// initialize the database found at the given path.
|
2019-09-16 15:52:47 +00:00
|
|
|
func NewLevelDBStore(cfg LevelDBOptions) (*LevelDBStore, error) {
|
2020-08-30 08:42:16 +00:00
|
|
|
var opts = new(opt.Options) // should be exposed via LevelDBOptions if anything needed
|
2019-09-10 14:22:21 +00:00
|
|
|
|
2020-08-30 08:42:16 +00:00
|
|
|
opts.Filter = filter.NewBloomFilter(10)
|
2019-09-10 14:22:21 +00:00
|
|
|
db, err := leveldb.OpenFile(cfg.DataDirectoryPath, opts)
|
2018-03-17 11:53:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-02-19 11:48:48 +00:00
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
return &LevelDBStore{
|
2019-09-10 14:22:21 +00:00
|
|
|
path: cfg.DataDirectoryPath,
|
2018-03-17 11:53:21 +00:00
|
|
|
db: db,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Put(key, value []byte) error {
|
|
|
|
return s.db.Put(key, value, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Get(key []byte) ([]byte, error) {
|
2019-09-27 12:40:44 +00:00
|
|
|
value, err := s.db.Get(key, nil)
|
|
|
|
if err == leveldb.ErrNotFound {
|
|
|
|
err = ErrKeyNotFound
|
|
|
|
}
|
|
|
|
return value, err
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
2019-10-07 14:08:09 +00:00
|
|
|
// Delete implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Delete(key []byte) error {
|
|
|
|
return s.db.Delete(key, nil)
|
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// PutBatch implements the Store interface.
|
|
|
|
func (s *LevelDBStore) PutBatch(batch Batch) error {
|
|
|
|
lvldbBatch := batch.(*leveldb.Batch)
|
|
|
|
return s.db.Write(lvldbBatch, nil)
|
|
|
|
}
|
|
|
|
|
2021-08-12 10:35:09 +00:00
|
|
|
// PutChangeSet implements the Store interface.
|
|
|
|
func (s *LevelDBStore) PutChangeSet(puts map[string][]byte, dels map[string]bool) error {
|
|
|
|
tx, err := s.db.OpenTransaction()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for k := range puts {
|
|
|
|
err = tx.Put([]byte(k), puts[k], nil)
|
|
|
|
if err != nil {
|
|
|
|
tx.Discard()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k := range dels {
|
|
|
|
err = tx.Delete([]byte(k), nil)
|
|
|
|
if err != nil {
|
|
|
|
tx.Discard()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tx.Commit()
|
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// Seek implements the Store interface.
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
|
2021-12-16 13:55:50 +00:00
|
|
|
start := make([]byte, len(rng.Prefix)+len(rng.Start))
|
|
|
|
copy(start, rng.Prefix)
|
|
|
|
copy(start[len(rng.Prefix):], rng.Start)
|
2021-12-28 13:01:44 +00:00
|
|
|
if rng.Backwards {
|
|
|
|
s.seekBackwards(rng.Prefix, start, f)
|
|
|
|
} else {
|
|
|
|
s.seek(rng.Prefix, start, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *LevelDBStore) seek(key []byte, start []byte, f func(k, v []byte) bool) {
|
2021-12-28 13:01:44 +00:00
|
|
|
prefix := util.BytesPrefix(key)
|
2021-12-16 13:55:50 +00:00
|
|
|
prefix.Start = start
|
|
|
|
iter := s.db.NewIterator(prefix, nil)
|
2018-03-17 11:53:21 +00:00
|
|
|
for iter.Next() {
|
2022-01-17 17:41:51 +00:00
|
|
|
if !f(iter.Key(), iter.Value()) {
|
|
|
|
break
|
|
|
|
}
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
iter.Release()
|
|
|
|
}
|
|
|
|
|
2022-01-17 17:41:51 +00:00
|
|
|
func (s *LevelDBStore) seekBackwards(key []byte, start []byte, f func(k, v []byte) bool) {
|
2021-12-28 13:01:44 +00:00
|
|
|
iRange := util.BytesPrefix(start)
|
|
|
|
iRange.Start = key
|
|
|
|
|
|
|
|
iter := s.db.NewIterator(iRange, nil)
|
|
|
|
for ok := iter.Last(); ok; ok = iter.Prev() {
|
2022-01-17 17:41:51 +00:00
|
|
|
if !f(iter.Key(), iter.Value()) {
|
|
|
|
break
|
|
|
|
}
|
2021-12-28 13:01:44 +00:00
|
|
|
}
|
|
|
|
iter.Release()
|
|
|
|
}
|
|
|
|
|
2018-03-21 16:11:04 +00:00
|
|
|
// Batch implements the Batch interface and returns a leveldb
|
|
|
|
// compatible Batch.
|
2018-03-17 11:53:21 +00:00
|
|
|
func (s *LevelDBStore) Batch() Batch {
|
|
|
|
return new(leveldb.Batch)
|
|
|
|
}
|
2019-09-16 15:52:47 +00:00
|
|
|
|
|
|
|
// Close implements the Store interface.
|
|
|
|
func (s *LevelDBStore) Close() error {
|
|
|
|
return s.db.Close()
|
|
|
|
}
|