@@ -5,6 +5,7 @@ package storage
55
66import (
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
0 commit comments