github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/storage/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/nspcc-dev/neo-go/pkg/core/storage/dboper" 9 "github.com/syndtr/goleveldb/leveldb/util" 10 ) 11 12 // KeyPrefix constants. 13 const ( 14 DataExecutable KeyPrefix = 0x01 15 // DataMPT is used for MPT node entries identified by Uint256. 16 DataMPT KeyPrefix = 0x03 17 // DataMPTAux is used to store additional MPT data like height-root 18 // mappings and local/validated heights. 19 DataMPTAux KeyPrefix = 0x04 20 STStorage KeyPrefix = 0x70 21 // STTempStorage is used to store contract storage items during state sync process 22 // in order not to mess up the previous state which has its own items stored by 23 // STStorage prefix. Once state exchange process is completed, all items with 24 // STStorage prefix will be replaced with STTempStorage-prefixed ones. 25 STTempStorage KeyPrefix = 0x71 26 STNEP11Transfers KeyPrefix = 0x72 27 STNEP17Transfers KeyPrefix = 0x73 28 STTokenTransferInfo KeyPrefix = 0x74 29 IXHeaderHashList KeyPrefix = 0x80 30 SYSCurrentBlock KeyPrefix = 0xc0 31 SYSCurrentHeader KeyPrefix = 0xc1 32 SYSStateSyncCurrentBlockHeight KeyPrefix = 0xc2 33 SYSStateSyncPoint KeyPrefix = 0xc3 34 // SYSStateChangeStage is used to store the phase of a state changing process 35 // which is one of the state jump or state reset. Its value is one byte containing 36 // state reset / state jump stages bits (first seven bits are reserved for that) 37 // and the last bit reserved for the state reset process marker (set to 1 on 38 // unfinished state reset and to 0 on unfinished state jump). 39 SYSStateChangeStage KeyPrefix = 0xc4 40 SYSVersion KeyPrefix = 0xf0 41 ) 42 43 // Executable subtypes. 44 const ( 45 ExecBlock byte = 1 46 ExecTransaction byte = 2 47 ) 48 49 // SeekRange represents options for Store.Seek operation. 50 type SeekRange struct { 51 // Prefix denotes the Seek's lookup key. 52 // If used directly with MemCachedStore's or MemoryStore's Seek, SeekAsync or 53 // SeekGC empty Prefix is not supported due to internal MemoryStore cache 54 // architecture; otherwise empty prefix can be used safely. 55 Prefix []byte 56 // Start denotes value appended to the Prefix to start Seek from. 57 // Seeking starting from some key includes this key to the result; 58 // if no matching key was found then next suitable key is picked up. 59 // Start may be empty. Empty Start means seeking through all keys in 60 // the DB with matching Prefix. 61 // Empty Prefix and empty Start can be combined, which means seeking 62 // through all keys in the DB, but see the Prefix's comment. 63 Start []byte 64 // Backwards denotes whether Seek direction should be reversed, i.e. 65 // whether seeking should be performed in a descending way. 66 // Backwards can be safely combined with Prefix and Start. 67 Backwards bool 68 // SearchDepth is the depth of Seek operation, denotes the number of cached 69 // DAO layers to perform search. Use 1 to fetch the latest changes from upper 70 // in-memory layer of cached DAO. Default 0 value denotes searching through 71 // the whole set of cached layers. 72 SearchDepth int 73 } 74 75 // ErrKeyNotFound is an error returned by Store implementations 76 // when a certain key is not found. 77 var ErrKeyNotFound = errors.New("key not found") 78 79 type ( 80 // Store is the underlying KV backend for the blockchain data, it's 81 // not intended to be used directly, you wrap it with some memory cache 82 // layer most of the time. 83 Store interface { 84 Get([]byte) ([]byte, error) 85 // PutChangeSet allows to push prepared changeset to the Store. 86 PutChangeSet(puts map[string][]byte, stor map[string][]byte) error 87 // Seek can guarantee that provided key (k) and value (v) are the only valid until the next call to f. 88 // Seek continues iteration until false is returned from f. 89 // Key and value slices should not be modified. 90 // Seek can guarantee that key-value items are sorted by key in ascending way. 91 Seek(rng SeekRange, f func(k, v []byte) bool) 92 // SeekGC is similar to Seek, but the function should return true if current 93 // KV pair should be kept and false if it's to be deleted; there is no way to 94 // do an early exit here. SeekGC only works with the current Store, it won't 95 // go down to layers below and it takes a full write lock, so use it carefully. 96 SeekGC(rng SeekRange, keep func(k, v []byte) bool) error 97 Close() error 98 } 99 100 // KeyPrefix is a constant byte added as a prefix for each key 101 // stored. 102 KeyPrefix uint8 103 ) 104 105 func seekRangeToPrefixes(sr SeekRange) *util.Range { 106 var ( 107 rang *util.Range 108 start = make([]byte, len(sr.Prefix)+len(sr.Start)) 109 ) 110 copy(start, sr.Prefix) 111 copy(start[len(sr.Prefix):], sr.Start) 112 113 if !sr.Backwards { 114 rang = util.BytesPrefix(sr.Prefix) 115 rang.Start = start 116 } else { 117 rang = util.BytesPrefix(start) 118 rang.Start = sr.Prefix 119 } 120 return rang 121 } 122 123 // NewStore creates storage with preselected in configuration database type. 124 func NewStore(cfg dbconfig.DBConfiguration) (Store, error) { 125 var store Store 126 var err error 127 switch cfg.Type { 128 case dbconfig.LevelDB: 129 store, err = NewLevelDBStore(cfg.LevelDBOptions) 130 case dbconfig.InMemoryDB: 131 store = NewMemoryStore() 132 case dbconfig.BoltDB: 133 store, err = NewBoltDBStore(cfg.BoltDBOptions) 134 default: 135 return nil, fmt.Errorf("unknown storage: %s", cfg.Type) 136 } 137 return store, err 138 } 139 140 // BatchToOperations converts a batch of changes into array of dboper.Operation. 141 func BatchToOperations(batch *MemBatch) []dboper.Operation { 142 size := len(batch.Put) + len(batch.Deleted) 143 ops := make([]dboper.Operation, 0, size) 144 for i := range batch.Put { 145 key := batch.Put[i].Key 146 if len(key) == 0 || key[0] != byte(STStorage) && key[0] != byte(STTempStorage) { 147 continue 148 } 149 150 op := "Added" 151 if batch.Put[i].Exists { 152 op = "Changed" 153 } 154 155 ops = append(ops, dboper.Operation{ 156 State: op, 157 Key: key[1:], 158 Value: batch.Put[i].Value, 159 }) 160 } 161 162 for i := range batch.Deleted { 163 key := batch.Deleted[i].Key 164 if len(key) == 0 || !batch.Deleted[i].Exists || 165 key[0] != byte(STStorage) && key[0] != byte(STTempStorage) { 166 continue 167 } 168 169 ops = append(ops, dboper.Operation{ 170 State: "Deleted", 171 Key: key[1:], 172 }) 173 } 174 return ops 175 }