2018-03-17 11:53:21 +00:00
|
|
|
package storage
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
2020-08-24 16:00:16 +00:00
|
|
|
"fmt"
|
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.
|
|
|
|
DataMPTAux KeyPrefix = 0x04
|
|
|
|
STContractID KeyPrefix = 0x51
|
|
|
|
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
|
2021-08-26 14:34:52 +00:00
|
|
|
SYSStateJumpStage KeyPrefix = 0xc4
|
2021-08-03 06:56:39 +00:00
|
|
|
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-03-30 13:47:15 +00:00
|
|
|
const (
|
|
|
|
// MaxStorageKeyLen is the maximum length of a key for storage items.
|
|
|
|
MaxStorageKeyLen = 64
|
|
|
|
// MaxStorageValueLen is the maximum length of a value for storage items.
|
|
|
|
// It is set to be the maximum value for uint16.
|
|
|
|
MaxStorageValueLen = 65535
|
|
|
|
)
|
|
|
|
|
2022-01-18 21:02:19 +00:00
|
|
|
// Operation represents a single KV operation (add/del/change) performed
|
|
|
|
// in the DB.
|
|
|
|
type Operation struct {
|
|
|
|
// State can be Added, Changed or Deleted.
|
|
|
|
State string `json:"state"`
|
|
|
|
Key []byte `json:"key"`
|
|
|
|
Value []byte `json:"value,omitempty"`
|
|
|
|
}
|
|
|
|
|
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.
|
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
|
|
|
// Empty Prefix is not supported.
|
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
|
|
|
|
// through all keys in the DB.
|
|
|
|
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
|
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
|
|
|
|
)
|
|
|
|
|
|
|
|
// Bytes returns the bytes representation of KeyPrefix.
|
|
|
|
func (k KeyPrefix) Bytes() []byte {
|
|
|
|
return []byte{byte(k)}
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// AppendPrefix appends byteslice b to the given KeyPrefix.
|
2021-05-12 20:17:03 +00:00
|
|
|
// AppendKeyPrefix(SYSVersion, []byte{0x00, 0x01}).
|
2018-03-17 11:53:21 +00:00
|
|
|
func AppendPrefix(k KeyPrefix, b []byte) []byte {
|
|
|
|
dest := make([]byte, len(b)+1)
|
|
|
|
dest[0] = byte(k)
|
|
|
|
copy(dest[1:], b)
|
|
|
|
return dest
|
|
|
|
}
|
|
|
|
|
|
|
|
// AppendPrefixInt append int n to the given KeyPrefix.
|
2021-05-12 20:17:03 +00:00
|
|
|
// AppendPrefixInt(SYSCurrentHeader, 10001)
|
2018-03-17 11:53:21 +00:00
|
|
|
func AppendPrefixInt(k KeyPrefix, n int) []byte {
|
|
|
|
b := make([]byte, 4)
|
|
|
|
binary.LittleEndian.PutUint32(b, uint32(n))
|
|
|
|
return AppendPrefix(k, b)
|
|
|
|
}
|
2019-09-10 14:22:21 +00:00
|
|
|
|
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.
|
2019-09-16 15:52:47 +00:00
|
|
|
func NewStore(cfg DBConfiguration) (Store, error) {
|
2019-09-10 14:22:21 +00:00
|
|
|
var store Store
|
|
|
|
var err error
|
|
|
|
switch cfg.Type {
|
|
|
|
case "leveldb":
|
2019-09-16 15:52:47 +00:00
|
|
|
store, err = NewLevelDBStore(cfg.LevelDBOptions)
|
2019-09-10 14:22:21 +00:00
|
|
|
case "inmemory":
|
|
|
|
store = NewMemoryStore()
|
2019-09-14 07:28:43 +00:00
|
|
|
case "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
|
|
|
|
|
|
|
// BatchToOperations converts a batch of changes into array of Operations.
|
|
|
|
func BatchToOperations(batch *MemBatch) []Operation {
|
|
|
|
size := len(batch.Put) + len(batch.Deleted)
|
|
|
|
ops := make([]Operation, 0, size)
|
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
|
|
|
ops = append(ops, Operation{
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
ops = append(ops, Operation{
|
|
|
|
State: "Deleted",
|
|
|
|
Key: key[1:],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return ops
|
|
|
|
}
|