github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/storage/leveldb_store.go (about)

     1  package storage
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
     8  	"github.com/syndtr/goleveldb/leveldb"
     9  	"github.com/syndtr/goleveldb/leveldb/filter"
    10  	"github.com/syndtr/goleveldb/leveldb/iterator"
    11  	"github.com/syndtr/goleveldb/leveldb/opt"
    12  )
    13  
    14  // LevelDBStore is the official storage implementation for storing and retrieving
    15  // blockchain data.
    16  type LevelDBStore struct {
    17  	db   *leveldb.DB
    18  	path string
    19  }
    20  
    21  // NewLevelDBStore returns a new LevelDBStore object that will
    22  // initialize the database found at the given path.
    23  func NewLevelDBStore(cfg dbconfig.LevelDBOptions) (*LevelDBStore, error) {
    24  	var opts = new(opt.Options) // should be exposed via LevelDBOptions if anything needed
    25  	if cfg.ReadOnly {
    26  		opts.ReadOnly = true
    27  		opts.ErrorIfMissing = true
    28  	}
    29  	opts.Filter = filter.NewBloomFilter(10)
    30  	db, err := leveldb.OpenFile(cfg.DataDirectoryPath, opts)
    31  	if err != nil {
    32  		return nil, fmt.Errorf("failed to open LevelDB instance: %w", err)
    33  	}
    34  
    35  	return &LevelDBStore{
    36  		path: cfg.DataDirectoryPath,
    37  		db:   db,
    38  	}, nil
    39  }
    40  
    41  // Get implements the Store interface.
    42  func (s *LevelDBStore) Get(key []byte) ([]byte, error) {
    43  	value, err := s.db.Get(key, nil)
    44  	if errors.Is(err, leveldb.ErrNotFound) {
    45  		err = ErrKeyNotFound
    46  	}
    47  	return value, err
    48  }
    49  
    50  // PutChangeSet implements the Store interface.
    51  func (s *LevelDBStore) PutChangeSet(puts map[string][]byte, stores map[string][]byte) error {
    52  	tx, err := s.db.OpenTransaction()
    53  	if err != nil {
    54  		return err
    55  	}
    56  	for _, m := range []map[string][]byte{puts, stores} {
    57  		for k := range m {
    58  			if m[k] != nil {
    59  				err = tx.Put([]byte(k), m[k], nil)
    60  			} else {
    61  				err = tx.Delete([]byte(k), nil)
    62  			}
    63  			if err != nil {
    64  				tx.Discard()
    65  				return err
    66  			}
    67  		}
    68  	}
    69  	return tx.Commit()
    70  }
    71  
    72  // Seek implements the Store interface.
    73  func (s *LevelDBStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
    74  	iter := s.db.NewIterator(seekRangeToPrefixes(rng), nil)
    75  	s.seek(iter, rng.Backwards, f)
    76  }
    77  
    78  // SeekGC implements the Store interface.
    79  func (s *LevelDBStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
    80  	tx, err := s.db.OpenTransaction()
    81  	if err != nil {
    82  		return err
    83  	}
    84  	iter := tx.NewIterator(seekRangeToPrefixes(rng), nil)
    85  	s.seek(iter, rng.Backwards, func(k, v []byte) bool {
    86  		if !keep(k, v) {
    87  			err = tx.Delete(k, nil)
    88  			if err != nil {
    89  				return false
    90  			}
    91  		}
    92  		return true
    93  	})
    94  	if err != nil {
    95  		return err
    96  	}
    97  	return tx.Commit()
    98  }
    99  
   100  func (s *LevelDBStore) seek(iter iterator.Iterator, backwards bool, f func(k, v []byte) bool) {
   101  	var (
   102  		next func() bool
   103  		ok   bool
   104  	)
   105  
   106  	if !backwards {
   107  		ok = iter.Next()
   108  		next = iter.Next
   109  	} else {
   110  		ok = iter.Last()
   111  		next = iter.Prev
   112  	}
   113  
   114  	for ; ok; ok = next() {
   115  		if !f(iter.Key(), iter.Value()) {
   116  			break
   117  		}
   118  	}
   119  	iter.Release()
   120  }
   121  
   122  // Close implements the Store interface.
   123  func (s *LevelDBStore) Close() error {
   124  	return s.db.Close()
   125  }