Skip to content

Commit 61f0216

Browse files
authored
fix(lib/trie): record deleted Merkle values fixed (#2873)
- Record Merkle values deleted only if they are NOT 'dirty' (the same as in the last trie snapshot) - Record Merkle values deleted when one or more nodes are deleted from the trie - Do NOT record Merkle values of inlined nodes in the deleted set of Merkle values - Lazy calculate Merkle values if they are missing (especially for delete operations) - Update tests to have full trie.go coverage again (except hashing error due to the buffer sync pools at global scope) - Fix preparing branch for mutation only once before for loop (instead of N times) - Check errors from merkle value computation (useful for lazy loading to handle database errors)
1 parent d70af4f commit 61f0216

File tree

12 files changed

+1186
-310
lines changed

12 files changed

+1186
-310
lines changed

dot/state/test_helpers.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ func generateBlockWithRandomTrie(t *testing.T, serv *Service,
244244
rand := time.Now().UnixNano()
245245
key := []byte("testKey" + fmt.Sprint(rand))
246246
value := []byte("testValue" + fmt.Sprint(rand))
247-
trieState.Put(key, value)
247+
err = trieState.Put(key, value)
248+
require.NoError(t, err)
248249

249250
trieStateRoot, err := trieState.Root()
250251
require.NoError(t, err)

lib/runtime/interfaces.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,24 @@ import (
1111

1212
// Storage runtime interface.
1313
type Storage interface {
14-
Put(key []byte, value []byte)
14+
Put(key []byte, value []byte) (err error)
1515
Get(key []byte) []byte
1616
Root() (common.Hash, error)
1717
SetChild(keyToChild []byte, child *trie.Trie) error
1818
SetChildStorage(keyToChild, key, value []byte) error
1919
GetChildStorage(keyToChild, key []byte) ([]byte, error)
20-
Delete(key []byte)
21-
DeleteChild(keyToChild []byte)
22-
DeleteChildLimit(keyToChild []byte, limit *[]byte) (uint32, bool, error)
20+
Delete(key []byte) (err error)
21+
DeleteChild(keyToChild []byte) (err error)
22+
DeleteChildLimit(keyToChild []byte, limit *[]byte) (
23+
deleted uint32, allDeleted bool, err error)
2324
ClearChildStorage(keyToChild, key []byte) error
2425
NextKey([]byte) []byte
2526
ClearPrefixInChild(keyToChild, prefix []byte) error
2627
GetChildNextKey(keyToChild, key []byte) ([]byte, error)
2728
GetChild(keyToChild []byte) (*trie.Trie, error)
28-
ClearPrefix(prefix []byte)
29-
ClearPrefixLimit(prefix []byte, limit uint32) (uint32, bool)
29+
ClearPrefix(prefix []byte) (err error)
30+
ClearPrefixLimit(prefix []byte, limit uint32) (
31+
deleted uint32, allDeleted bool, err error)
3032
BeginStorageTransaction()
3133
CommitStorageTransaction()
3234
RollbackStorageTransaction()

lib/runtime/storage/trie.go

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package storage
55

66
import (
77
"encoding/binary"
8+
"fmt"
89
"sort"
910
"sync"
1011

@@ -67,11 +68,11 @@ func (s *TrieState) RollbackStorageTransaction() {
6768
s.oldTrie = nil
6869
}
6970

70-
// Put puts thread safely a value at the specified key in the trie.
71-
func (s *TrieState) Put(key, value []byte) {
71+
// Put puts a key-value pair in the trie
72+
func (s *TrieState) Put(key, value []byte) (err error) {
7273
s.lock.Lock()
7374
defer s.lock.Unlock()
74-
s.t.Put(key, value)
75+
return s.t.Put(key, value)
7576
}
7677

7778
// Get gets a value from the trie
@@ -97,15 +98,20 @@ func (s *TrieState) Has(key []byte) bool {
9798
}
9899

99100
// Delete deletes a key from the trie
100-
func (s *TrieState) Delete(key []byte) {
101+
func (s *TrieState) Delete(key []byte) (err error) {
101102
val := s.t.Get(key)
102103
if val == nil {
103-
return
104+
return nil
104105
}
105106

106107
s.lock.Lock()
107108
defer s.lock.Unlock()
108-
s.t.Delete(key)
109+
err = s.t.Delete(key)
110+
if err != nil {
111+
return fmt.Errorf("deleting from trie: %w", err)
112+
}
113+
114+
return nil
109115
}
110116

111117
// NextKey returns the next key in the trie in lexicographical order. If it does not exist, it returns nil.
@@ -116,19 +122,19 @@ func (s *TrieState) NextKey(key []byte) []byte {
116122
}
117123

118124
// ClearPrefix deletes all key-value pairs from the trie where the key starts with the given prefix
119-
func (s *TrieState) ClearPrefix(prefix []byte) {
125+
func (s *TrieState) ClearPrefix(prefix []byte) (err error) {
120126
s.lock.Lock()
121127
defer s.lock.Unlock()
122-
s.t.ClearPrefix(prefix)
128+
return s.t.ClearPrefix(prefix)
123129
}
124130

125131
// ClearPrefixLimit deletes key-value pairs from the trie where the key starts with the given prefix till limit reached
126-
func (s *TrieState) ClearPrefixLimit(prefix []byte, limit uint32) (uint32, bool) {
132+
func (s *TrieState) ClearPrefixLimit(prefix []byte, limit uint32) (
133+
deleted uint32, allDeleted bool, err error) {
127134
s.lock.Lock()
128135
defer s.lock.Unlock()
129136

130-
num, del := s.t.ClearPrefixLimit(prefix, limit)
131-
return num, del
137+
return s.t.ClearPrefixLimit(prefix, limit)
132138
}
133139

134140
// TrieEntries returns every key-value pair in the trie
@@ -167,24 +173,29 @@ func (s *TrieState) GetChildStorage(keyToChild, key []byte) ([]byte, error) {
167173
}
168174

169175
// DeleteChild deletes a child trie from the main trie
170-
func (s *TrieState) DeleteChild(key []byte) {
176+
func (s *TrieState) DeleteChild(key []byte) (err error) {
171177
s.lock.Lock()
172178
defer s.lock.Unlock()
173-
s.t.DeleteChild(key)
179+
return s.t.DeleteChild(key)
174180
}
175181

176-
// DeleteChildLimit deletes up to limit of database entries by lexicographic order, return number
177-
// deleted, true if all delete otherwise false
178-
func (s *TrieState) DeleteChildLimit(key []byte, limit *[]byte) (uint32, bool, error) {
182+
// DeleteChildLimit deletes up to limit of database entries by lexicographic order.
183+
func (s *TrieState) DeleteChildLimit(key []byte, limit *[]byte) (
184+
deleted uint32, allDeleted bool, err error) {
179185
s.lock.Lock()
180186
defer s.lock.Unlock()
181187
tr, err := s.t.GetChild(key)
182188
if err != nil {
183189
return 0, false, err
184190
}
191+
185192
qtyEntries := uint32(len(tr.Entries()))
186193
if limit == nil {
187-
s.t.DeleteChild(key)
194+
err = s.t.DeleteChild(key)
195+
if err != nil {
196+
return 0, false, fmt.Errorf("deleting child trie: %w", err)
197+
}
198+
188199
return qtyEntries, true, nil
189200
}
190201
limitUint := binary.LittleEndian.Uint32(*limit)
@@ -194,20 +205,25 @@ func (s *TrieState) DeleteChildLimit(key []byte, limit *[]byte) (uint32, bool, e
194205
keys = append(keys, k)
195206
}
196207
sort.Strings(keys)
197-
deleted := uint32(0)
198208
for _, k := range keys {
199-
tr.Delete([]byte(k))
209+
// TODO have a transactional/atomic way to delete multiple keys in trie.
210+
// If one deletion fails, the child trie and its parent trie are then in
211+
// a bad intermediary state. Take also care of the caching of deleted Merkle
212+
// values within the tries, which is used for online pruning.
213+
// See https://github.com/ChainSafe/gossamer/issues/3032
214+
err = tr.Delete([]byte(k))
215+
if err != nil {
216+
return deleted, allDeleted, fmt.Errorf("deleting from child trie located at key 0x%x: %w", key, err)
217+
}
218+
200219
deleted++
201220
if deleted == limitUint {
202221
break
203222
}
204223
}
205224

206-
if deleted == qtyEntries {
207-
return deleted, true, nil
208-
}
209-
210-
return deleted, false, nil
225+
allDeleted = deleted == qtyEntries
226+
return deleted, allDeleted, nil
211227
}
212228

213229
// ClearChildStorage removes the child storage entry from the trie
@@ -230,7 +246,11 @@ func (s *TrieState) ClearPrefixInChild(keyToChild, prefix []byte) error {
230246
return nil
231247
}
232248

233-
child.ClearPrefix(prefix)
249+
err = child.ClearPrefix(prefix)
250+
if err != nil {
251+
return fmt.Errorf("clearing prefix in child trie located at key 0x%x: %w", keyToChild, err)
252+
}
253+
234254
return nil
235255
}
236256

lib/runtime/wasmer/helpers.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func toWasmMemoryFixedSizeOptional(context wasmer.InstanceContext, data []byte)
208208
return toWasmMemory(context, encodedOptionalFixedSize)
209209
}
210210

211-
func storageAppend(storage GetSetter, key, valueToAppend []byte) error {
211+
func storageAppend(storage GetSetter, key, valueToAppend []byte) (err error) {
212212
// this function assumes the item in storage is a SCALE encoded array of items
213213
// the valueToAppend is a new item, so it appends the item and increases the length prefix by 1
214214
currentValue := storage.Get(key)
@@ -259,7 +259,16 @@ func storageAppend(storage GetSetter, key, valueToAppend []byte) error {
259259
}
260260
}
261261

262-
logger.Debugf("resulting value: 0x%x", value)
263-
storage.Put(key, value)
262+
err = storage.Put(key, value)
263+
if err != nil {
264+
return fmt.Errorf("putting key and value in storage: %w", err)
265+
}
266+
264267
return nil
265268
}
269+
270+
func panicOnError(err error) {
271+
if err != nil {
272+
panic(err)
273+
}
274+
}

lib/runtime/wasmer/helpers_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package wasmer
55

66
import (
77
"encoding/json"
8+
"errors"
89
"os"
910
"path/filepath"
1011
"testing"
@@ -66,3 +67,13 @@ func Test_pointerSize(t *testing.T) {
6667
})
6768
}
6869
}
70+
71+
func Test_panicOnError(t *testing.T) {
72+
t.Parallel()
73+
74+
err := (error)(nil)
75+
assert.NotPanics(t, func() { panicOnError(err) })
76+
77+
err = errors.New("test error")
78+
assert.PanicsWithValue(t, err, func() { panicOnError(err) })
79+
}

lib/runtime/wasmer/imports.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,12 @@ func ext_trie_blake2_256_root_version_1(context unsafe.Pointer, dataSpan C.int64
817817
}
818818

819819
for _, kv := range kvs {
820-
t.Put(kv.Key, kv.Value)
820+
err := t.Put(kv.Key, kv.Value)
821+
if err != nil {
822+
logger.Errorf("failed putting key 0x%x and value 0x%x into trie: %s",
823+
kv.Key, kv.Value, err)
824+
return 0
825+
}
821826
}
822827

823828
// allocate memory for value and copy value to memory
@@ -865,7 +870,12 @@ func ext_trie_blake2_256_ordered_root_version_1(context unsafe.Pointer, dataSpan
865870
"put key=0x%x and value=0x%x",
866871
key, value)
867872

868-
t.Put(key, value)
873+
err = t.Put(key, value)
874+
if err != nil {
875+
logger.Errorf("failed putting key 0x%x and value 0x%x into trie: %s",
876+
key, value, err)
877+
return 0
878+
}
869879
}
870880

871881
// allocate memory for value and copy value to memory
@@ -1180,7 +1190,8 @@ func ext_default_child_storage_storage_kill_version_1(context unsafe.Pointer, ch
11801190
storage := ctx.Storage
11811191

11821192
childStorageKey := asMemorySlice(instanceContext, childStorageKeySpan)
1183-
storage.DeleteChild(childStorageKey)
1193+
err := storage.DeleteChild(childStorageKey)
1194+
panicOnError(err)
11841195
}
11851196

11861197
//export ext_default_child_storage_storage_kill_version_2
@@ -1843,7 +1854,8 @@ func ext_storage_clear_version_1(context unsafe.Pointer, keySpan C.int64_t) {
18431854
key := asMemorySlice(instanceContext, keySpan)
18441855

18451856
logger.Debugf("key: 0x%x", key)
1846-
storage.Delete(key)
1857+
err := storage.Delete(key)
1858+
panicOnError(err)
18471859
}
18481860

18491861
//export ext_storage_clear_prefix_version_1
@@ -1856,7 +1868,8 @@ func ext_storage_clear_prefix_version_1(context unsafe.Pointer, prefixSpan C.int
18561868
prefix := asMemorySlice(instanceContext, prefixSpan)
18571869
logger.Debugf("prefix: 0x%x", prefix)
18581870

1859-
storage.ClearPrefix(prefix)
1871+
err := storage.ClearPrefix(prefix)
1872+
panicOnError(err)
18601873
}
18611874

18621875
//export ext_storage_clear_prefix_version_2
@@ -1885,7 +1898,12 @@ func ext_storage_clear_prefix_version_2(context unsafe.Pointer, prefixSpan, lim
18851898
}
18861899

18871900
limitUint := binary.LittleEndian.Uint32(limit)
1888-
numRemoved, all := storage.ClearPrefixLimit(prefix, limitUint)
1901+
numRemoved, all, err := storage.ClearPrefixLimit(prefix, limitUint)
1902+
if err != nil {
1903+
logger.Errorf("failed to clear prefix limit: %s", err)
1904+
return mustToWasmMemoryNil(instanceContext)
1905+
}
1906+
18891907
encBytes, err := toKillStorageResultEnum(all, numRemoved)
18901908
if err != nil {
18911909
logger.Errorf("failed to allocate memory: %s", err)
@@ -2044,7 +2062,8 @@ func ext_storage_set_version_1(context unsafe.Pointer, keySpan, valueSpan C.int6
20442062
logger.Debugf(
20452063
"key 0x%x has value 0x%x",
20462064
key, value)
2047-
storage.Put(key, cp)
2065+
err := storage.Put(key, cp)
2066+
panicOnError(err)
20482067
}
20492068

20502069
//export ext_storage_start_transaction_version_1

lib/runtime/wasmer/interfaces.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ type Storage interface {
1717
SetChild(keyToChild []byte, child *trie.Trie) error
1818
SetChildStorage(keyToChild, key, value []byte) error
1919
GetChildStorage(keyToChild, key []byte) ([]byte, error)
20-
Delete(key []byte)
21-
DeleteChild(keyToChild []byte)
20+
Delete(key []byte) (err error)
21+
DeleteChild(keyToChild []byte) (err error)
2222
DeleteChildLimit(keyToChild []byte, limit *[]byte) (uint32, bool, error)
2323
ClearChildStorage(keyToChild, key []byte) error
2424
NextKey([]byte) []byte
2525
ClearPrefixInChild(keyToChild, prefix []byte) error
2626
GetChildNextKey(keyToChild, key []byte) ([]byte, error)
2727
GetChild(keyToChild []byte) (*trie.Trie, error)
28-
ClearPrefix(prefix []byte)
29-
ClearPrefixLimit(prefix []byte, limit uint32) (uint32, bool)
28+
ClearPrefix(prefix []byte) (err error)
29+
ClearPrefixLimit(prefix []byte, limit uint32) (
30+
deleted uint32, allDeleted bool, err error)
3031
BeginStorageTransaction()
3132
CommitStorageTransaction()
3233
RollbackStorageTransaction()
@@ -46,7 +47,7 @@ type Getter interface {
4647

4748
// Putter puts a value for a key.
4849
type Putter interface {
49-
Put(key []byte, value []byte)
50+
Put(key []byte, value []byte) (err error)
5051
}
5152

5253
// BasicNetwork interface for functions used by runtime network state function

0 commit comments

Comments
 (0)