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  }