Skip to content

Commit 53fdf45

Browse files
committed
Don't panic during any store operations.
1 parent 7016a94 commit 53fdf45

File tree

3 files changed

+110
-77
lines changed

3 files changed

+110
-77
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) (bool, storetypes.KVStore) {
3636
return ws.getStore(key)
3737
}
3838

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

Lines changed: 51 additions & 18 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"
@@ -44,72 +43,95 @@ func newMigrateClientWrappedStore(subjectStore, substituteStore storetypes.KVSto
4443

4544
// Get implements the storetypes.KVStore interface. It allows reads from both the subjectStore and substituteStore.
4645
//
47-
// Get will panic if the key is not prefixed with either "subject/" or "substitute/".
46+
// Get will return an empty byte slice if the key is not prefixed with either "subject/" or "substitute/".
4847
func (ws migrateClientWrappedStore) Get(key []byte) []byte {
4948
prefix, key := splitPrefix(key)
49+
found, store := ws.getStore(prefix)
50+
if !found {
51+
// return a nil as KVStore.Get() does by default
52+
return []byte(nil)
53+
}
5054

51-
return ws.getStore(prefix).Get(key)
55+
return store.Get(key)
5256
}
5357

5458
// Has implements the storetypes.KVStore interface. It allows reads from both the subjectStore and substituteStore.
5559
//
5660
// Note: contracts do not have access to the Has method, it is only implemented here to satisfy the storetypes.KVStore interface.
5761
func (ws migrateClientWrappedStore) Has(key []byte) bool {
5862
prefix, key := splitPrefix(key)
63+
found, store := ws.getStore(prefix)
64+
if !found {
65+
// return false as value when store is not found
66+
return false
67+
}
5968

60-
return ws.getStore(prefix).Has(key)
69+
return store.Has(key)
6170
}
6271

6372
// Set implements the storetypes.KVStore interface. It allows writes solely to the subjectStore.
6473
//
65-
// Set will panic if the key is not prefixed with "subject/".
74+
// Set will no-op if the key is not prefixed with "subject/".
6675
func (ws migrateClientWrappedStore) Set(key, value []byte) {
6776
prefix, key := splitPrefix(key)
6877
if !bytes.Equal(prefix, subjectPrefix) {
69-
panic(fmt.Errorf("writes only allowed on subject store; key must be prefixed with \"%s\"", subjectPrefix))
78+
return // no-op
7079
}
7180

7281
ws.subjectStore.Set(key, value)
7382
}
7483

7584
// Delete implements the storetypes.KVStore interface. It allows deletions solely to the subjectStore.
7685
//
77-
// Delete will panic if the key is not prefixed with "subject/".
86+
// Delete will no-op if the key is not prefixed with "subject/".
7887
func (ws migrateClientWrappedStore) Delete(key []byte) {
7988
prefix, key := splitPrefix(key)
8089
if !bytes.Equal(prefix, subjectPrefix) {
81-
panic(fmt.Errorf("writes only allowed on subject store; key must be prefixed with \"%s\"", subjectPrefix))
90+
return // no-op
8291
}
8392

8493
ws.subjectStore.Delete(key)
8594
}
8695

8796
// Iterator implements the storetypes.KVStore interface. It allows iteration over both the subjectStore and substituteStore.
8897
//
89-
// Iterator will panic if the start or end keys are not prefixed with either "subject/" or "substitute/".
98+
// Iterator will return a closed iterator if the start or end keys are not prefixed with either "subject/" or "substitute/".
9099
func (ws migrateClientWrappedStore) Iterator(start, end []byte) storetypes.Iterator {
91100
prefixStart, start := splitPrefix(start)
92101
prefixEnd, end := splitPrefix(end)
93102

94103
if !bytes.Equal(prefixStart, prefixEnd) {
95-
panic(errors.New("start and end keys must be prefixed with the same prefix"))
104+
// return an invalid iterator
105+
return ws.closedIterator()
106+
}
107+
found, store := ws.getStore(prefixStart)
108+
if !found {
109+
// return an invalid iterator
110+
return ws.closedIterator()
96111
}
97112

98-
return ws.getStore(prefixStart).Iterator(start, end)
113+
return store.Iterator(start, end)
99114
}
100115

101116
// ReverseIterator implements the storetypes.KVStore interface. It allows iteration over both the subjectStore and substituteStore.
102117
//
103-
// ReverseIterator will panic if the start or end keys are not prefixed with either "subject/" or "substitute/".
118+
// ReverseIterator will return a closed iterator if the start or end keys are not prefixed with either "subject/" or "substitute/".
104119
func (ws migrateClientWrappedStore) ReverseIterator(start, end []byte) storetypes.Iterator {
105120
prefixStart, start := splitPrefix(start)
106121
prefixEnd, end := splitPrefix(end)
107122

108123
if !bytes.Equal(prefixStart, prefixEnd) {
109-
panic(errors.New("start and end keys must be prefixed with the same prefix"))
124+
// return an invalid iterator
125+
return ws.closedIterator()
126+
}
127+
128+
found, store := ws.getStore(prefixStart)
129+
if !found {
130+
// return an invalid iterator
131+
return ws.closedIterator()
110132
}
111133

112-
return ws.getStore(prefixStart).ReverseIterator(start, end)
134+
return store.ReverseIterator(start, end)
113135
}
114136

115137
// GetStoreType implements the storetypes.KVStore interface, it is implemented solely to satisfy the interface.
@@ -131,14 +153,25 @@ func (ws migrateClientWrappedStore) CacheWrapWithTrace(w io.Writer, tc storetype
131153
// is returned. If the key is prefixed with "substitute/", the substituteStore is returned.
132154
//
133155
// If the key is not prefixed with either "subject/" or "substitute/", a panic is thrown.
134-
func (ws migrateClientWrappedStore) getStore(prefix []byte) storetypes.KVStore {
156+
func (ws migrateClientWrappedStore) getStore(prefix []byte) (bool, storetypes.KVStore) {
135157
if bytes.Equal(prefix, subjectPrefix) {
136-
return ws.subjectStore
158+
return true, ws.subjectStore
137159
} else if bytes.Equal(prefix, substitutePrefix) {
138-
return ws.substituteStore
160+
return true, ws.substituteStore
139161
}
140162

141-
panic(fmt.Errorf("key must be prefixed with either \"%s\" or \"%s\"", subjectPrefix, substitutePrefix))
163+
return false, nil
164+
}
165+
166+
// closedIterator returns an iterator that is always closed, used when ws.Iterator() or ws.ReverseIterator() is called
167+
// with invalid prefixes.
168+
// TODO(jim): could alternatively create an invalid iterator, callers should always check for validity before using.
169+
func (ws migrateClientWrappedStore) closedIterator() storetypes.Iterator {
170+
// Create a dummy iterator that is always closed right away.
171+
it := ws.subjectStore.Iterator([]byte{0}, []byte{1})
172+
it.Close()
173+
174+
return it
142175
}
143176

144177
// 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)