Skip to content

Commit ff0311e

Browse files
DimitrisJimmergify[bot]
authored andcommitted
Don't panic during any store operations. (#5294)
* Don't panic during any store operations. * Panic when creating wrapped store if substores are nil. * Address Damian's feedback. (cherry picked from commit b7d6a30)
1 parent d630841 commit ff0311e

File tree

3 files changed

+171
-82
lines changed

3 files changed

+171
-82
lines changed

modules/light-clients/08-wasm/types/export_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func NewMigrateProposalWrappedStore(subjectStore, substituteStore storetypes.KVS
3232
}
3333

3434
// GetStore is a wrapper around getStore to allow the function to be directly called in tests.
35-
func (ws migrateClientWrappedStore) GetStore(key []byte) storetypes.KVStore {
35+
func (ws migrateClientWrappedStore) GetStore(key []byte) (storetypes.KVStore, bool) {
3636
return ws.getStore(key)
3737
}
3838

modules/light-clients/08-wasm/types/store.go

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package types
33
import (
44
"bytes"
55
"errors"
6-
"fmt"
76
"io"
87
"reflect"
98
"strings"
@@ -29,13 +28,20 @@ var (
2928
//
3029
// Both stores are used for reads, but only the subjectStore is used for writes. For all operations, the key
3130
// is checked to determine which store to use and must be prefixed with either "subject/" or "substitute/" accordingly.
32-
// If the key is not prefixed with either "subject/" or "substitute/", a panic is thrown.
31+
// If the key is not prefixed with either "subject/" or "substitute/", a default action is taken (e.g. no-op for Set/Delete).
3332
type migrateClientWrappedStore struct {
3433
subjectStore storetypes.KVStore
3534
substituteStore storetypes.KVStore
3635
}
3736

3837
func newMigrateClientWrappedStore(subjectStore, substituteStore storetypes.KVStore) migrateClientWrappedStore {
38+
if subjectStore == nil {
39+
panic(errors.New("subjectStore must not be nil"))
40+
}
41+
if substituteStore == nil {
42+
panic(errors.New("substituteStore must not be nil"))
43+
}
44+
3945
return migrateClientWrappedStore{
4046
subjectStore: subjectStore,
4147
substituteStore: substituteStore,
@@ -44,11 +50,17 @@ func newMigrateClientWrappedStore(subjectStore, substituteStore storetypes.KVSto
4450

4551
// Get implements the storetypes.KVStore interface. It allows reads from both the subjectStore and substituteStore.
4652
//
47-
// Get will panic if the key is not prefixed with either "subject/" or "substitute/".
53+
// Get will return an empty byte slice if the key is not prefixed with either "subject/" or "substitute/".
4854
func (ws migrateClientWrappedStore) Get(key []byte) []byte {
4955
prefix, key := splitPrefix(key)
5056

51-
return ws.getStore(prefix).Get(key)
57+
store, found := ws.getStore(prefix)
58+
if !found {
59+
// return a nil byte slice as KVStore.Get() does by default
60+
return []byte(nil)
61+
}
62+
63+
return store.Get(key)
5264
}
5365

5466
// Has implements the storetypes.KVStore interface. It allows reads from both the subjectStore and substituteStore.
@@ -57,59 +69,75 @@ func (ws migrateClientWrappedStore) Get(key []byte) []byte {
5769
func (ws migrateClientWrappedStore) Has(key []byte) bool {
5870
prefix, key := splitPrefix(key)
5971

60-
return ws.getStore(prefix).Has(key)
72+
store, found := ws.getStore(prefix)
73+
if !found {
74+
// return false as value when store is not found
75+
return false
76+
}
77+
78+
return store.Has(key)
6179
}
6280

6381
// Set implements the storetypes.KVStore interface. It allows writes solely to the subjectStore.
6482
//
65-
// Set will panic if the key is not prefixed with "subject/".
83+
// Set will no-op if the key is not prefixed with "subject/".
6684
func (ws migrateClientWrappedStore) Set(key, value []byte) {
6785
prefix, key := splitPrefix(key)
6886
if !bytes.Equal(prefix, subjectPrefix) {
69-
panic(fmt.Errorf("writes only allowed on subject store; key must be prefixed with \"%s\"", subjectPrefix))
87+
return // no-op
7088
}
7189

7290
ws.subjectStore.Set(key, value)
7391
}
7492

7593
// Delete implements the storetypes.KVStore interface. It allows deletions solely to the subjectStore.
7694
//
77-
// Delete will panic if the key is not prefixed with "subject/".
95+
// Delete will no-op if the key is not prefixed with "subject/".
7896
func (ws migrateClientWrappedStore) Delete(key []byte) {
7997
prefix, key := splitPrefix(key)
8098
if !bytes.Equal(prefix, subjectPrefix) {
81-
panic(fmt.Errorf("writes only allowed on subject store; key must be prefixed with \"%s\"", subjectPrefix))
99+
return // no-op
82100
}
83101

84102
ws.subjectStore.Delete(key)
85103
}
86104

87105
// Iterator implements the storetypes.KVStore interface. It allows iteration over both the subjectStore and substituteStore.
88106
//
89-
// Iterator will panic if the start or end keys are not prefixed with either "subject/" or "substitute/".
107+
// Iterator will return a closed iterator if the start or end keys are not prefixed with either "subject/" or "substitute/".
90108
func (ws migrateClientWrappedStore) Iterator(start, end []byte) storetypes.Iterator {
91109
prefixStart, start := splitPrefix(start)
92110
prefixEnd, end := splitPrefix(end)
93111

94112
if !bytes.Equal(prefixStart, prefixEnd) {
95-
panic(errors.New("start and end keys must be prefixed with the same prefix"))
113+
return ws.closedIterator()
96114
}
97115

98-
return ws.getStore(prefixStart).Iterator(start, end)
116+
store, found := ws.getStore(prefixStart)
117+
if !found {
118+
return ws.closedIterator()
119+
}
120+
121+
return store.Iterator(start, end)
99122
}
100123

101124
// ReverseIterator implements the storetypes.KVStore interface. It allows iteration over both the subjectStore and substituteStore.
102125
//
103-
// ReverseIterator will panic if the start or end keys are not prefixed with either "subject/" or "substitute/".
126+
// ReverseIterator will return a closed iterator if the start or end keys are not prefixed with either "subject/" or "substitute/".
104127
func (ws migrateClientWrappedStore) ReverseIterator(start, end []byte) storetypes.Iterator {
105128
prefixStart, start := splitPrefix(start)
106129
prefixEnd, end := splitPrefix(end)
107130

108131
if !bytes.Equal(prefixStart, prefixEnd) {
109-
panic(errors.New("start and end keys must be prefixed with the same prefix"))
132+
return ws.closedIterator()
133+
}
134+
135+
store, found := ws.getStore(prefixStart)
136+
if !found {
137+
return ws.closedIterator()
110138
}
111139

112-
return ws.getStore(prefixStart).ReverseIterator(start, end)
140+
return store.ReverseIterator(start, end)
113141
}
114142

115143
// GetStoreType implements the storetypes.KVStore interface, it is implemented solely to satisfy the interface.
@@ -127,18 +155,29 @@ func (ws migrateClientWrappedStore) CacheWrapWithTrace(w io.Writer, tc storetype
127155
return cachekv.NewStore(tracekv.NewStore(ws, w, tc))
128156
}
129157

130-
// getStore returns the store to be used for the given key. If the key is prefixed with "subject/", the subjectStore
131-
// is returned. If the key is prefixed with "substitute/", the substituteStore is returned.
158+
// getStore returns the store to be used for the given key and a boolean flag indicating if that store was found.
159+
// If the key is prefixed with "subject/", the subjectStore is returned. If the key is prefixed with "substitute/",
160+
// the substituteStore is returned.
132161
//
133-
// If the key is not prefixed with either "subject/" or "substitute/", a panic is thrown.
134-
func (ws migrateClientWrappedStore) getStore(prefix []byte) storetypes.KVStore {
162+
// If the key is not prefixed with either "subject/" or "substitute/", a nil store is returned and the boolean flag is false.
163+
func (ws migrateClientWrappedStore) getStore(prefix []byte) (storetypes.KVStore, bool) {
135164
if bytes.Equal(prefix, subjectPrefix) {
136-
return ws.subjectStore
165+
return ws.subjectStore, true
137166
} else if bytes.Equal(prefix, substitutePrefix) {
138-
return ws.substituteStore
167+
return ws.substituteStore, true
139168
}
140169

141-
panic(fmt.Errorf("key must be prefixed with either \"%s\" or \"%s\"", subjectPrefix, substitutePrefix))
170+
return nil, false
171+
}
172+
173+
// closedIterator returns an iterator that is always closed, used when Iterator() or ReverseIterator() is called
174+
// with an invalid prefix or start/end key.
175+
func (ws migrateClientWrappedStore) closedIterator() storetypes.Iterator {
176+
// Create a dummy iterator that is always closed right away.
177+
it := ws.subjectStore.Iterator([]byte{0}, []byte{1})
178+
it.Close()
179+
180+
return it
142181
}
143182

144183
// splitPrefix splits the key into the prefix and the key itself, if the key is prefixed with either "subject/" or "substitute/".

0 commit comments

Comments
 (0)