2018-03-17 11:53:21 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2020-08-24 16:00:16 +00:00
|
|
|
"fmt"
|
2022-02-11 15:00:45 +00:00
|
|
|
|
2022-07-08 16:42:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
|
2022-07-08 18:17:52 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage/dboper"
|
2022-02-11 15:00:45 +00:00
|
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
2018-03-17 11:53:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// KeyPrefix constants.
|
|
|
|
const (
|
2021-12-07 20:05:28 +00:00
|
|
|
DataExecutable KeyPrefix = 0x01
|
2022-02-12 19:48:16 +00:00
|
|
|
// DataMPT is used for MPT node entries identified by Uint256.
|
|
|
|
DataMPT KeyPrefix = 0x03
|
|
|
|
// DataMPTAux is used to store additional MPT data like height-root
|
|
|
|
// mappings and local/validated heights.
|
2022-09-19 20:13:25 +00:00
|
|
|
DataMPTAux KeyPrefix = 0x04
|
|
|
|
STStorage KeyPrefix = 0x70
|
2021-08-23 09:02:17 +00:00
|
|
|
// STTempStorage is used to store contract storage items during state sync process
|
|
|
|
// in order not to mess up the previous state which has its own items stored by
|
|
|
|
// STStorage prefix. Once state exchange process is completed, all items with
|
|
|
|
// STStorage prefix will be replaced with STTempStorage-prefixed ones.
|
|
|
|
STTempStorage KeyPrefix = 0x71
|
2021-11-16 20:09:04 +00:00
|
|
|
STNEP11Transfers KeyPrefix = 0x72
|
|
|
|
STNEP17Transfers KeyPrefix = 0x73
|
|
|
|
STTokenTransferInfo KeyPrefix = 0x74
|
2021-08-03 06:56:39 +00:00
|
|
|
IXHeaderHashList KeyPrefix = 0x80
|
|
|
|
SYSCurrentBlock KeyPrefix = 0xc0
|
|
|
|
SYSCurrentHeader KeyPrefix = 0xc1
|
|
|
|
SYSStateSyncCurrentBlockHeight KeyPrefix = 0xc2
|
|
|
|
SYSStateSyncPoint KeyPrefix = 0xc3
|
2022-10-20 10:59:19 +00:00
|
|
|
// SYSStateChangeStage is used to store the phase of a state changing process
|
|
|
|
// which is one of the state jump or state reset. Its value is one byte containing
|
|
|
|
// state reset / state jump stages bits (first seven bits are reserved for that)
|
|
|
|
// and the last bit reserved for the state reset process marker (set to 1 on
|
|
|
|
// unfinished state reset and to 0 on unfinished state jump).
|
|
|
|
SYSStateChangeStage KeyPrefix = 0xc4
|
|
|
|
SYSVersion KeyPrefix = 0xf0
|
2018-03-17 11:53:21 +00:00
|
|
|
)
|
|
|
|
|
2021-12-07 20:05:28 +00:00
|
|
|
// Executable subtypes.
|
|
|
|
const (
|
|
|
|
ExecBlock byte = 1
|
|
|
|
ExecTransaction byte = 2
|
|
|
|
)
|
|
|
|
|
2021-12-16 13:55:50 +00:00
|
|
|
// SeekRange represents options for Store.Seek operation.
|
|
|
|
type SeekRange struct {
|
|
|
|
// Prefix denotes the Seek's lookup key.
|
2023-02-18 18:06:30 +00:00
|
|
|
// If used directly with MemCachedStore's or MemoryStore's Seek, SeekAsync or
|
|
|
|
// SeekGC empty Prefix is not supported due to internal MemoryStore cache
|
|
|
|
// architecture; otherwise empty prefix can be used safely.
|
2021-12-16 13:55:50 +00:00
|
|
|
Prefix []byte
|
2022-01-17 17:41:51 +00:00
|
|
|
// Start denotes value appended to the Prefix to start Seek from.
|
2021-12-16 13:55:50 +00:00
|
|
|
// Seeking starting from some key includes this key to the result;
|
|
|
|
// if no matching key was found then next suitable key is picked up.
|
|
|
|
// Start may be empty. Empty Start means seeking through all keys in
|
|
|
|
// the DB with matching Prefix.
|
|
|
|
// Empty Prefix and empty Start can be combined, which means seeking
|
2023-02-18 18:06:30 +00:00
|
|
|
// through all keys in the DB, but see the Prefix's comment.
|
2021-12-16 13:55:50 +00:00
|
|
|
Start []byte
|
2021-12-28 13:01:44 +00:00
|
|
|
// Backwards denotes whether Seek direction should be reversed, i.e.
|
|
|
|
// whether seeking should be performed in a descending way.
|
|
|
|
// Backwards can be safely combined with Prefix and Start.
|
|
|
|
Backwards bool
|
2022-10-05 10:42:15 +00:00
|
|
|
// SearchDepth is the depth of Seek operation, denotes the number of cached
|
|
|
|
// DAO layers to perform search. Use 1 to fetch the latest changes from upper
|
|
|
|
// in-memory layer of cached DAO. Default 0 value denotes searching through
|
|
|
|
// the whole set of cached layers.
|
|
|
|
SearchDepth int
|
2021-12-16 13:55:50 +00:00
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
// ErrKeyNotFound is an error returned by Store implementations
|
|
|
|
// when a certain key is not found.
|
|
|
|
var ErrKeyNotFound = errors.New("key not found")
|
|
|
|
|
|
|
|
type (
|
2022-02-16 13:48:47 +00:00
|
|
|
// Store is the underlying KV backend for the blockchain data, it's
|
|
|
|
// not intended to be used directly, you wrap it with some memory cache
|
|
|
|
// layer most of the time.
|
2018-03-17 11:53:21 +00:00
|
|
|
Store interface {
|
|
|
|
Get([]byte) ([]byte, error)
|
2021-08-12 10:35:09 +00:00
|
|
|
// PutChangeSet allows to push prepared changeset to the Store.
|
storage: use two maps for MemoryStore
Simple and dumb as it is, this allows to separate contract storage from other
things and dramatically improve Seek() time over storage (even though it's
still unordered!) which in turn improves block processing speed.
LevelDB LevelDB (KeepOnlyLatest) BoltDB BoltDB (KeepOnlyLatest)
Master real 16m27,936s real 10m9,440s real 16m39,369s real 8m1,227s
user 20m12,619s user 26m13,925s user 18m9,162s user 18m5,846s
sys 2m56,377s sys 1m32,051s sys 9m52,576s sys 2m9,455s
2 maps real 10m49,495s real 8m53,342s real 11m46,204s real 5m56,043s
user 14m19,922s user 24m6,225s user 13m25,691s user 15m4,694s
sys 1m53,021s sys 1m23,006s sys 4m31,735s sys 2m8,714s
neo-bench performance is mostly unaffected, ~0.5% for 1-1 test and 4% for
10K-10K test both fall within regular test error range.
2022-02-15 16:07:59 +00:00
|
|
|
PutChangeSet(puts map[string][]byte, stor map[string][]byte) error
|
2020-10-10 09:37:46 +00:00
|
|
|
// Seek can guarantee that provided key (k) and value (v) are the only valid until the next call to f.
|
2022-01-17 17:41:51 +00:00
|
|
|
// Seek continues iteration until false is returned from f.
|
|
|
|
// Key and value slices should not be modified.
|
|
|
|
// Seek can guarantee that key-value items are sorted by key in ascending way.
|
|
|
|
Seek(rng SeekRange, f func(k, v []byte) bool)
|
2022-02-11 17:35:45 +00:00
|
|
|
// SeekGC is similar to Seek, but the function should return true if current
|
|
|
|
// KV pair should be kept and false if it's to be deleted; there is no way to
|
|
|
|
// do an early exit here. SeekGC only works with the current Store, it won't
|
|
|
|
// go down to layers below and it takes a full write lock, so use it carefully.
|
|
|
|
SeekGC(rng SeekRange, keep func(k, v []byte) bool) error
|
2019-09-16 15:52:47 +00:00
|
|
|
Close() error
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// KeyPrefix is a constant byte added as a prefix for each key
|
|
|
|
// stored.
|
|
|
|
KeyPrefix uint8
|
|
|
|
)
|
|
|
|
|
2022-02-11 15:00:45 +00:00
|
|
|
func seekRangeToPrefixes(sr SeekRange) *util.Range {
|
|
|
|
var (
|
|
|
|
rang *util.Range
|
|
|
|
start = make([]byte, len(sr.Prefix)+len(sr.Start))
|
|
|
|
)
|
|
|
|
copy(start, sr.Prefix)
|
|
|
|
copy(start[len(sr.Prefix):], sr.Start)
|
|
|
|
|
|
|
|
if !sr.Backwards {
|
|
|
|
rang = util.BytesPrefix(sr.Prefix)
|
|
|
|
rang.Start = start
|
|
|
|
} else {
|
|
|
|
rang = util.BytesPrefix(start)
|
|
|
|
rang.Start = sr.Prefix
|
|
|
|
}
|
|
|
|
return rang
|
|
|
|
}
|
|
|
|
|
2019-09-10 14:22:21 +00:00
|
|
|
// NewStore creates storage with preselected in configuration database type.
|
2022-07-08 16:42:06 +00:00
|
|
|
func NewStore(cfg dbconfig.DBConfiguration) (Store, error) {
|
2019-09-10 14:22:21 +00:00
|
|
|
var store Store
|
|
|
|
var err error
|
|
|
|
switch cfg.Type {
|
2022-10-05 06:05:36 +00:00
|
|
|
case dbconfig.LevelDB:
|
2019-09-16 15:52:47 +00:00
|
|
|
store, err = NewLevelDBStore(cfg.LevelDBOptions)
|
2022-10-05 06:05:36 +00:00
|
|
|
case dbconfig.InMemoryDB:
|
2019-09-10 14:22:21 +00:00
|
|
|
store = NewMemoryStore()
|
2022-10-05 06:05:36 +00:00
|
|
|
case dbconfig.BoltDB:
|
2019-09-16 15:52:47 +00:00
|
|
|
store, err = NewBoltDBStore(cfg.BoltDBOptions)
|
2020-08-24 16:00:16 +00:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unknown storage: %s", cfg.Type)
|
2019-09-10 14:22:21 +00:00
|
|
|
}
|
|
|
|
return store, err
|
|
|
|
}
|
2022-01-18 21:02:19 +00:00
|
|
|
|
2022-07-08 18:17:52 +00:00
|
|
|
// BatchToOperations converts a batch of changes into array of dboper.Operation.
|
|
|
|
func BatchToOperations(batch *MemBatch) []dboper.Operation {
|
2022-01-18 21:02:19 +00:00
|
|
|
size := len(batch.Put) + len(batch.Deleted)
|
2022-07-08 18:17:52 +00:00
|
|
|
ops := make([]dboper.Operation, 0, size)
|
2022-01-18 21:02:19 +00:00
|
|
|
for i := range batch.Put {
|
|
|
|
key := batch.Put[i].Key
|
|
|
|
if len(key) == 0 || key[0] != byte(STStorage) && key[0] != byte(STTempStorage) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
op := "Added"
|
|
|
|
if batch.Put[i].Exists {
|
|
|
|
op = "Changed"
|
|
|
|
}
|
|
|
|
|
2022-07-08 18:17:52 +00:00
|
|
|
ops = append(ops, dboper.Operation{
|
2022-01-18 21:02:19 +00:00
|
|
|
State: op,
|
|
|
|
Key: key[1:],
|
|
|
|
Value: batch.Put[i].Value,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range batch.Deleted {
|
|
|
|
key := batch.Deleted[i].Key
|
|
|
|
if len(key) == 0 || !batch.Deleted[i].Exists ||
|
|
|
|
key[0] != byte(STStorage) && key[0] != byte(STTempStorage) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-07-08 18:17:52 +00:00
|
|
|
ops = append(ops, dboper.Operation{
|
2022-01-18 21:02:19 +00:00
|
|
|
State: "Deleted",
|
|
|
|
Key: key[1:],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return ops
|
|
|
|
}
|