Skip to content

Commit e1e6e67

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

File tree

3 files changed

+110
-81
lines changed

3 files changed

+110
-81
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 & 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,7 +28,7 @@ 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
@@ -44,72 +43,91 @@ 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 byte slice 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 ws.closedIterator()
105+
}
106+
found, store := ws.getStore(prefixStart)
107+
if !found {
108+
return ws.closedIterator()
96109
}
97110

98-
return ws.getStore(prefixStart).Iterator(start, end)
111+
return store.Iterator(start, end)
99112
}
100113

101114
// ReverseIterator implements the storetypes.KVStore interface. It allows iteration over both the subjectStore and substituteStore.
102115
//
103-
// ReverseIterator will panic if the start or end keys are not prefixed with either "subject/" or "substitute/".
116+
// ReverseIterator will return a closed iterator if the start or end keys are not prefixed with either "subject/" or "substitute/".
104117
func (ws migrateClientWrappedStore) ReverseIterator(start, end []byte) storetypes.Iterator {
105118
prefixStart, start := splitPrefix(start)
106119
prefixEnd, end := splitPrefix(end)
107120

108121
if !bytes.Equal(prefixStart, prefixEnd) {
109-
panic(errors.New("start and end keys must be prefixed with the same prefix"))
122+
return ws.closedIterator()
123+
}
124+
125+
found, store := ws.getStore(prefixStart)
126+
if !found {
127+
return ws.closedIterator()
110128
}
111129

112-
return ws.getStore(prefixStart).ReverseIterator(start, end)
130+
return store.ReverseIterator(start, end)
113131
}
114132

115133
// GetStoreType implements the storetypes.KVStore interface, it is implemented solely to satisfy the interface.
@@ -127,18 +145,29 @@ func (ws migrateClientWrappedStore) CacheWrapWithTrace(w io.Writer, tc storetype
127145
return cachekv.NewStore(tracekv.NewStore(ws, w, tc))
128146
}
129147

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.
148+
// getStore returns the store to be used for the given key and a boolean flag indicating if that store was found.
149+
// If the key is prefixed with "subject/", the subjectStore is returned. If the key is prefixed with "substitute/",
150+
// the substituteStore is returned.
132151
//
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 {
152+
// If the key is not prefixed with either "subject/" or "substitute/", a nil store is returned and the boolean flag is false.
153+
func (ws migrateClientWrappedStore) getStore(prefix []byte) (bool, storetypes.KVStore) {
135154
if bytes.Equal(prefix, subjectPrefix) {
136-
return ws.subjectStore
155+
return true, ws.subjectStore
137156
} else if bytes.Equal(prefix, substitutePrefix) {
138-
return ws.substituteStore
157+
return true, ws.substituteStore
139158
}
140159

141-
panic(fmt.Errorf("key must be prefixed with either \"%s\" or \"%s\"", subjectPrefix, substitutePrefix))
160+
return false, nil
161+
}
162+
163+
// closedIterator returns an iterator that is always closed, used when Iterator() or ReverseIterator() is called
164+
// with an invalid prefix or start/end key.
165+
func (ws migrateClientWrappedStore) closedIterator() storetypes.Iterator {
166+
// Create a dummy iterator that is always closed right away.
167+
it := ws.subjectStore.Iterator([]byte{0}, []byte{1})
168+
it.Close()
169+
170+
return it
142171
}
143172

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