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 }