github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/mpt/trie_store.go (about) 1 package mpt 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "github.com/nspcc-dev/neo-go/pkg/core/storage" 9 "github.com/nspcc-dev/neo-go/pkg/util" 10 ) 11 12 // TrieStore is an MPT-based storage implementation for storing and retrieving 13 // historic blockchain data. TrieStore is supposed to be used within transaction 14 // script invocations only, thus only contract storage related operations are 15 // supported. All storage-related operations are being performed using historical 16 // storage data retrieved from MPT state. TrieStore is read-only and does not 17 // support put-related operations, thus, it should always be wrapped into 18 // MemCachedStore for proper puts handling. TrieStore never changes the provided 19 // backend store. 20 type TrieStore struct { 21 trie *Trie 22 } 23 24 // ErrForbiddenTrieStoreOperation is returned when operation is not supposed to 25 // be performed over MPT-based Store. 26 var ErrForbiddenTrieStoreOperation = errors.New("operation is not allowed to be performed over TrieStore") 27 28 // NewTrieStore returns a new ready to use MPT-backed storage. 29 func NewTrieStore(root util.Uint256, mode TrieMode, backed storage.Store) *TrieStore { 30 cache, ok := backed.(*storage.MemCachedStore) 31 if !ok { 32 cache = storage.NewMemCachedStore(backed) 33 } 34 tr := NewTrie(NewHashNode(root), mode, cache) 35 return &TrieStore{ 36 trie: tr, 37 } 38 } 39 40 // Get implements the Store interface. 41 func (m *TrieStore) Get(key []byte) ([]byte, error) { 42 if len(key) == 0 { 43 return nil, fmt.Errorf("%w: Get is supported only for contract storage items", ErrForbiddenTrieStoreOperation) 44 } 45 switch storage.KeyPrefix(key[0]) { 46 case storage.STStorage, storage.STTempStorage: 47 res, err := m.trie.Get(key[1:]) 48 if err != nil && errors.Is(err, ErrNotFound) { 49 // Mimic the real storage behaviour. 50 return nil, storage.ErrKeyNotFound 51 } 52 return res, err 53 default: 54 return nil, fmt.Errorf("%w: Get is supported only for contract storage items", ErrForbiddenTrieStoreOperation) 55 } 56 } 57 58 // PutChangeSet implements the Store interface. 59 func (m *TrieStore) PutChangeSet(puts map[string][]byte, stor map[string][]byte) error { 60 // Only Get and Seek should be supported, as TrieStore is read-only and is always 61 // should be wrapped by MemCachedStore to properly support put operations (if any). 62 return fmt.Errorf("%w: PutChangeSet is not supported", ErrForbiddenTrieStoreOperation) 63 } 64 65 // Seek implements the Store interface. 66 func (m *TrieStore) Seek(rng storage.SeekRange, f func(k, v []byte) bool) { 67 prefix := storage.KeyPrefix(rng.Prefix[0]) 68 if prefix != storage.STStorage && prefix != storage.STTempStorage { // Prefix is always non-empty. 69 panic(fmt.Errorf("%w: Seek is supported only for contract storage items", ErrForbiddenTrieStoreOperation)) 70 } 71 prefixP := toNibbles(rng.Prefix[1:]) 72 fromP := []byte{} 73 if len(rng.Start) > 0 { 74 fromP = toNibbles(rng.Start) 75 } 76 _, start, path, err := m.trie.getWithPath(m.trie.root, prefixP, false) 77 if err != nil { 78 // Failed to determine the start node => no matching items. 79 return 80 } 81 path = path[len(prefixP):] 82 83 if len(fromP) > 0 { 84 if len(path) <= len(fromP) && bytes.HasPrefix(fromP, path) { 85 fromP = fromP[len(path):] 86 } else if len(path) > len(fromP) && bytes.HasPrefix(path, fromP) { 87 fromP = []byte{} 88 } else { 89 cmp := bytes.Compare(path, fromP) 90 if cmp < 0 == rng.Backwards { 91 // No matching items. 92 return 93 } 94 fromP = []byte{} 95 } 96 } 97 98 b := NewBillet(m.trie.root.Hash(), m.trie.mode, 0, m.trie.Store) 99 process := func(pathToNode []byte, node Node, _ []byte) bool { 100 if leaf, ok := node.(*LeafNode); ok { 101 // (*Billet).traverse includes `from` path into the result if so. It's OK for Seek, so shouldn't be filtered out. 102 kv := storage.KeyValue{ 103 Key: append(bytes.Clone(rng.Prefix), pathToNode...), // Do not cut prefix. 104 Value: bytes.Clone(leaf.value), 105 } 106 return !f(kv.Key, kv.Value) // Should return whether to stop. 107 } 108 return false 109 } 110 _, err = b.traverse(start, path, fromP, process, false, rng.Backwards) 111 if err != nil && !errors.Is(err, errStop) { 112 panic(fmt.Errorf("failed to perform Seek operation on TrieStore: %w", err)) 113 } 114 } 115 116 // SeekGC implements the Store interface. 117 func (m *TrieStore) SeekGC(rng storage.SeekRange, keep func(k, v []byte) bool) error { 118 return fmt.Errorf("%w: SeekGC is not supported", ErrForbiddenTrieStoreOperation) 119 } 120 121 // Close implements the Store interface. 122 func (m *TrieStore) Close() error { 123 m.trie = nil 124 return nil 125 }