diff --git a/server/mock/store.go b/server/mock/store.go index 2f74d87eb..6096f35d4 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -230,6 +230,10 @@ func (kv kvStore) VersionExists(version int64) bool { panic("not implemented") } +func (kv kvStore) DeleteAll(start, end []byte) error { + panic("not implemented") +} + func NewCommitMultiStore() sdk.CommitMultiStore { return multiStore{kv: make(map[sdk.StoreKey]kvStore)} } diff --git a/store/cachekv/store.go b/store/cachekv/store.go index 9797a3281..d3c71d373 100644 --- a/store/cachekv/store.go +++ b/store/cachekv/store.go @@ -372,3 +372,18 @@ func (store *Store) isDeleted(key string) bool { func (store *Store) GetParent() types.KVStore { return store.parent } + +func (store *Store) DeleteAll(start, end []byte) error { + store.dirtyItems(start, end) + // memdb iterator + cachedIter, err := store.sortedCache.Iterator(start, end) + if err != nil { + return err + } + defer cachedIter.Close() + for ; cachedIter.Valid(); cachedIter.Next() { + // `Delete` would not touch sortedCache so it's okay to perform inside iterator + store.Delete(cachedIter.Key()) + } + return nil +} diff --git a/store/cachekv/store_test.go b/store/cachekv/store_test.go index 65c3f4eaa..c6e7ab5b6 100644 --- a/store/cachekv/store_test.go +++ b/store/cachekv/store_test.go @@ -65,6 +65,16 @@ func TestCacheKVStore(t *testing.T) { // GetParent returns parent store require.NotNil(t, st.GetParent()) + + // DeleteAll deletes all entries in cache but not affect mem + st = cachekv.NewStore(mem, types.NewKVStoreKey("CacheKvTest"), types.DefaultCacheSizeLimit) + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(2)) + st.Set(keyFmt(2), valFmt(3)) + require.Nil(t, st.DeleteAll(nil, nil)) + require.Nil(t, st.Get(keyFmt(1))) + require.Nil(t, st.Get(keyFmt(2))) + require.Equal(t, valFmt(1), mem.Get(keyFmt(1))) } func TestCacheKVStoreNoNilSet(t *testing.T) { diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go index 04b3910ac..b8de090f1 100644 --- a/store/dbadapter/store.go +++ b/store/dbadapter/store.go @@ -99,5 +99,18 @@ func (dsa Store) VersionExists(version int64) bool { panic("no versioning for dbadater") } +func (dsa Store) DeleteAll(start, end []byte) error { + iter := dsa.Iterator(start, end) + keys := [][]byte{} + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + iter.Close() + for _, key := range keys { + dsa.Delete(key) + } + return nil +} + // dbm.DB implements KVStore so we can CacheKVStore it. var _ types.KVStore = Store{} diff --git a/store/dbadapter/store_test.go b/store/dbadapter/store_test.go index 467ba6421..f13209cbe 100644 --- a/store/dbadapter/store_test.go +++ b/store/dbadapter/store_test.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/dbadapter" "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/tests/mocks" + dbm "github.com/tendermint/tm-db" ) var errFoo = errors.New("dummy") @@ -74,6 +75,17 @@ func TestAccessors(t *testing.T) { require.Panics(t, func() { store.ReverseIterator(start, end) }) } +func TestDeleteAll(t *testing.T) { + mem := dbadapter.Store{DB: dbm.NewMemDB()} + mem.Set([]byte("1"), []byte("2")) + mem.Set([]byte("3"), []byte("4")) + require.NotNil(t, mem.Get([]byte("1"))) + require.NotNil(t, mem.Get([]byte("3"))) + require.Nil(t, mem.DeleteAll(nil, nil)) + require.Nil(t, mem.Get([]byte("1"))) + require.Nil(t, mem.Get([]byte("3"))) +} + func TestCacheWraps(t *testing.T) { mockCtrl := gomock.NewController(t) mockDB := mocks.NewMockDB(mockCtrl) diff --git a/store/gaskv/store.go b/store/gaskv/store.go index 4792becbe..62e3d89c4 100644 --- a/store/gaskv/store.go +++ b/store/gaskv/store.go @@ -130,6 +130,10 @@ func (gs *Store) VersionExists(version int64) bool { return gs.parent.VersionExists(version) } +func (gs *Store) DeleteAll(start, end []byte) error { + return gs.parent.DeleteAll(start, end) +} + type gasIterator struct { gasMeter types.GasMeter gasConfig types.GasConfig diff --git a/store/iavl/store.go b/store/iavl/store.go index 3bcc9af97..2d5bfad88 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -423,6 +423,19 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return res } +func (st *Store) DeleteAll(start, end []byte) error { + iter := st.Iterator(start, end) + keys := [][]byte{} + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + iter.Close() + for _, key := range keys { + st.Delete(key) + } + return nil +} + // Takes a MutableTree, a key, and a flag for creating existence or absence proof and returns the // appropriate merkle.Proof. Since this must be called after querying for the value, this function should never error // Thus, it will panic on error rather than returning it diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index 3cf0194e9..de56b4277 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -463,6 +463,21 @@ func TestIAVLNoPrune(t *testing.T) { } } +func TestIAVLStoreDeleteAll(t *testing.T) { + db := dbm.NewMemDB() + tree, err := iavl.NewMutableTree(db, cacheSize, false) + require.NoError(t, err) + + iavlStore := UnsafeNewStore(tree) + iavlStore.Set([]byte("1"), []byte("2")) + iavlStore.Set([]byte("3"), []byte("4")) + require.NotNil(t, iavlStore.Get([]byte("1"))) + require.NotNil(t, iavlStore.Get([]byte("3"))) + require.Nil(t, iavlStore.DeleteAll(nil, nil)) + require.Nil(t, iavlStore.Get([]byte("1"))) + require.Nil(t, iavlStore.Get([]byte("3"))) +} + func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree, err := iavl.NewMutableTree(db, cacheSize, false) diff --git a/store/listenkv/store.go b/store/listenkv/store.go index 99bdce48e..20f103ec0 100644 --- a/store/listenkv/store.go +++ b/store/listenkv/store.go @@ -161,3 +161,7 @@ func (s *Store) onWrite(delete bool, key, value []byte) { l.OnWrite(s.parentStoreKey, key, value, delete) } } + +func (s *Store) DeleteAll(start, end []byte) error { + return s.parent.DeleteAll(start, end) +} diff --git a/store/prefix/store.go b/store/prefix/store.go index bb79f93f4..fd1f2672b 100644 --- a/store/prefix/store.go +++ b/store/prefix/store.go @@ -90,6 +90,18 @@ func (s Store) Delete(key []byte) { s.parent.Delete(s.key(key)) } +func (s Store) DeleteAll(start, end []byte) error { + newstart := cloneAppend(s.prefix, start) + + var newend []byte + if end == nil { + newend = cpIncr(s.prefix) + } else { + newend = cloneAppend(s.prefix, end) + } + return s.parent.DeleteAll(newstart, newend) +} + // Implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L106 func (s Store) Iterator(start, end []byte) types.Iterator { diff --git a/store/tracekv/store.go b/store/tracekv/store.go index f18ab2c77..81fe2034c 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -186,6 +186,10 @@ func (tkv *Store) VersionExists(version int64) bool { return tkv.parent.VersionExists(version) } +func (tkv *Store) DeleteAll(start, end []byte) error { + return tkv.parent.DeleteAll(start, end) +} + // writeOperation writes a KVStore operation to the underlying io.Writer as // JSON-encoded data where the key/value pair is base64 encoded. func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value []byte) { diff --git a/store/types/store.go b/store/types/store.go index 7ed731afa..e1f4b0a97 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -255,6 +255,8 @@ type KVStore interface { GetWorkingHash() ([]byte, error) VersionExists(version int64) bool + + DeleteAll(start, end []byte) error } // Iterator is an alias db's Iterator for convenience. diff --git a/storev2/commitment/store.go b/storev2/commitment/store.go index 406419ad9..f48362873 100644 --- a/storev2/commitment/store.go +++ b/storev2/commitment/store.go @@ -182,3 +182,16 @@ func (st *Store) VersionExists(version int64) bool { // one version per SC tree return version == st.tree.Version() } + +func (st *Store) DeleteAll(start, end []byte) error { + iter := st.Iterator(start, end) + keys := [][]byte{} + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + iter.Close() + for _, key := range keys { + st.Delete(key) + } + return nil +} diff --git a/storev2/state/store.go b/storev2/state/store.go index 0257e2309..b9c9c7599 100644 --- a/storev2/state/store.go +++ b/storev2/state/store.go @@ -134,3 +134,16 @@ func (st *Store) VersionExists(version int64) bool { } return version >= earliest } + +func (st *Store) DeleteAll(start, end []byte) error { + iter := st.Iterator(start, end) + keys := [][]byte{} + for ; iter.Valid(); iter.Next() { + keys = append(keys, iter.Key()) + } + iter.Close() + for _, key := range keys { + st.Delete(key) + } + return nil +}