Skip to content

Commit 6e59ad0

Browse files
feat(store/v2): route to the commitment during migration (#22290)
1 parent 2f0a2b4 commit 6e59ad0

File tree

7 files changed

+212
-44
lines changed

7 files changed

+212
-44
lines changed

store/v2/commitment/iavl/tree.go

+12
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
var (
1616
_ commitment.Tree = (*IavlTree)(nil)
17+
_ commitment.Reader = (*IavlTree)(nil)
1718
_ store.PausablePruner = (*IavlTree)(nil)
1819
)
1920

@@ -76,6 +77,7 @@ func (t *IavlTree) GetProof(version uint64, key []byte) (*ics23.CommitmentProof,
7677
return immutableTree.GetProof(key)
7778
}
7879

80+
// Get implements the Reader interface.
7981
func (t *IavlTree) Get(version uint64, key []byte) ([]byte, error) {
8082
immutableTree, err := t.tree.GetImmutable(int64(version))
8183
if err != nil {
@@ -85,6 +87,16 @@ func (t *IavlTree) Get(version uint64, key []byte) ([]byte, error) {
8587
return immutableTree.Get(key)
8688
}
8789

90+
// Iterator implements the Reader interface.
91+
func (t *IavlTree) Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) {
92+
immutableTree, err := t.tree.GetImmutable(int64(version))
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to get immutable tree at version %d: %w", version, err)
95+
}
96+
97+
return immutableTree.Iterator(start, end, ascending)
98+
}
99+
88100
// GetLatestVersion returns the latest version of the tree.
89101
func (t *IavlTree) GetLatestVersion() (uint64, error) {
90102
v, err := t.tree.GetLatestVersion()

store/v2/commitment/iavl/tree_test.go

+79
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,82 @@ func TestIavlTree(t *testing.T) {
133133
// close the db
134134
require.NoError(t, tree.Close())
135135
}
136+
137+
func TestIavlTreeIterator(t *testing.T) {
138+
// generate a new tree
139+
tree := generateTree()
140+
require.NotNil(t, tree)
141+
142+
// write a batch of version 1
143+
require.NoError(t, tree.Set([]byte("key1"), []byte("value1")))
144+
require.NoError(t, tree.Set([]byte("key2"), []byte("value2")))
145+
require.NoError(t, tree.Set([]byte("key3"), []byte("value3")))
146+
147+
// commit the batch
148+
_, _, err := tree.Commit()
149+
require.NoError(t, err)
150+
151+
// write a batch of version 2
152+
require.NoError(t, tree.Set([]byte("key4"), []byte("value4")))
153+
require.NoError(t, tree.Set([]byte("key5"), []byte("value5")))
154+
require.NoError(t, tree.Set([]byte("key6"), []byte("value6")))
155+
require.NoError(t, tree.Remove([]byte("key1"))) // delete key1
156+
_, _, err = tree.Commit()
157+
require.NoError(t, err)
158+
159+
// write a batch of version 3
160+
require.NoError(t, tree.Set([]byte("key7"), []byte("value7")))
161+
require.NoError(t, tree.Set([]byte("key8"), []byte("value8")))
162+
_, _, err = tree.Commit()
163+
require.NoError(t, err)
164+
165+
// iterate over all keys
166+
iter, err := tree.Iterator(3, nil, nil, true)
167+
require.NoError(t, err)
168+
// expect all keys to be iterated over
169+
expectedKeys := []string{"key2", "key3", "key4", "key5", "key6", "key7", "key8"}
170+
count := 0
171+
for i := 0; iter.Valid(); i++ {
172+
require.Equal(t, expectedKeys[i], string(iter.Key()))
173+
iter.Next()
174+
count++
175+
}
176+
require.Equal(t, len(expectedKeys), count)
177+
require.NoError(t, iter.Close())
178+
179+
// iterate over all keys in reverse
180+
iter, err = tree.Iterator(3, nil, nil, false)
181+
require.NoError(t, err)
182+
expectedKeys = []string{"key8", "key7", "key6", "key5", "key4", "key3", "key2"}
183+
for i := 0; iter.Valid(); i++ {
184+
require.Equal(t, expectedKeys[i], string(iter.Key()))
185+
iter.Next()
186+
}
187+
require.NoError(t, iter.Close())
188+
189+
// iterate over keys with version 1
190+
iter, err = tree.Iterator(1, nil, nil, true)
191+
require.NoError(t, err)
192+
expectedKeys = []string{"key1", "key2", "key3"}
193+
count = 0
194+
for i := 0; iter.Valid(); i++ {
195+
require.Equal(t, expectedKeys[i], string(iter.Key()))
196+
iter.Next()
197+
count++
198+
}
199+
require.Equal(t, len(expectedKeys), count)
200+
require.NoError(t, iter.Close())
201+
202+
// iterate over keys with version 2
203+
iter, err = tree.Iterator(2, nil, nil, false)
204+
require.NoError(t, err)
205+
expectedKeys = []string{"key6", "key5", "key4", "key3", "key2"}
206+
count = 0
207+
for i := 0; iter.Valid(); i++ {
208+
require.Equal(t, expectedKeys[i], string(iter.Key()))
209+
iter.Next()
210+
count++
211+
}
212+
require.Equal(t, len(expectedKeys), count)
213+
require.NoError(t, iter.Close())
214+
}

store/v2/commitment/store.go

+59-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ var (
2525
_ store.UpgradeableStore = (*CommitStore)(nil)
2626
_ snapshots.CommitSnapshotter = (*CommitStore)(nil)
2727
_ store.PausablePruner = (*CommitStore)(nil)
28+
29+
// NOTE: It is not recommended to use the CommitStore as a reader. This is only used
30+
// during the migration process. Generally, the SC layer does not provide a reader
31+
// in the store/v2.
32+
_ store.VersionedReader = (*CommitStore)(nil)
2833
)
2934

3035
// MountTreeFn is a function that mounts a tree given a store key.
@@ -275,21 +280,71 @@ func (c *CommitStore) GetProof(storeKey []byte, version uint64, key []byte) ([]p
275280
return []proof.CommitmentOp{commitOp, *storeCommitmentOp}, nil
276281
}
277282

278-
// Get implements store.VersionedReader.
279-
func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) {
280-
tree, ok := c.multiTrees[conv.UnsafeBytesToStr(storeKey)]
283+
// getReader returns a reader for the given store key. It will return an error if the
284+
// store key does not exist or the tree does not implement the Reader interface.
285+
// WARNING: This function is only used during the migration process. The SC layer
286+
// generally does not provide a reader for the CommitStore.
287+
func (c *CommitStore) getReader(storeKey string) (Reader, error) {
288+
tree, ok := c.multiTrees[storeKey]
281289
if !ok {
282290
return nil, fmt.Errorf("store %s not found", storeKey)
283291
}
284292

285-
bz, err := tree.Get(version, key)
293+
reader, ok := tree.(Reader)
294+
if !ok {
295+
return nil, fmt.Errorf("tree for store %s does not implement Reader", storeKey)
296+
}
297+
298+
return reader, nil
299+
}
300+
301+
// VersionExists implements store.VersionedReader.
302+
func (c *CommitStore) VersionExists(version uint64) (bool, error) {
303+
ci, err := c.metadata.GetCommitInfo(version)
304+
return ci != nil, err
305+
}
306+
307+
// Get implements store.VersionedReader.
308+
func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) {
309+
reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey))
310+
if err != nil {
311+
return nil, err
312+
}
313+
314+
bz, err := reader.Get(version, key)
286315
if err != nil {
287316
return nil, fmt.Errorf("failed to get key %s from store %s: %w", key, storeKey, err)
288317
}
289318

290319
return bz, nil
291320
}
292321

322+
// Has implements store.VersionedReader.
323+
func (c *CommitStore) Has(storeKey []byte, version uint64, key []byte) (bool, error) {
324+
val, err := c.Get(storeKey, version, key)
325+
return val != nil, err
326+
}
327+
328+
// Iterator implements store.VersionedReader.
329+
func (c *CommitStore) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) {
330+
reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey))
331+
if err != nil {
332+
return nil, err
333+
}
334+
335+
return reader.Iterator(version, start, end, true)
336+
}
337+
338+
// ReverseIterator implements store.VersionedReader.
339+
func (c *CommitStore) ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) {
340+
reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey))
341+
if err != nil {
342+
return nil, err
343+
}
344+
345+
return reader.Iterator(version, start, end, false)
346+
}
347+
293348
// Prune implements store.Pruner.
294349
func (c *CommitStore) Prune(version uint64) error {
295350
// prune the metadata

store/v2/commitment/tree.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
ics23 "github.com/cosmos/ics23/go"
88

9+
corestore "cosmossdk.io/core/store"
910
snapshotstypes "cosmossdk.io/store/v2/snapshots/types"
1011
)
1112

@@ -29,19 +30,20 @@ type Tree interface {
2930
SetInitialVersion(version uint64) error
3031
GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error)
3132

32-
// Get attempts to retrieve a value from the tree for a given version.
33-
//
34-
// NOTE: This method only exists to support migration from IAVL v0/v1 to v2.
35-
// Once migration is complete, this method should be removed and/or not used.
36-
Get(version uint64, key []byte) ([]byte, error)
37-
3833
Prune(version uint64) error
3934
Export(version uint64) (Exporter, error)
4035
Import(version uint64) (Importer, error)
4136

4237
io.Closer
4338
}
4439

40+
// Reader is the optional interface that is only used to read data from the tree
41+
// during the migration process.
42+
type Reader interface {
43+
Get(version uint64, key []byte) ([]byte, error)
44+
Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error)
45+
}
46+
4547
// Exporter is the interface that wraps the basic Export methods.
4648
type Exporter interface {
4749
Next() (*snapshotstypes.SnapshotIAVLItem, error)

store/v2/database.go

-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ type VersionedReader interface {
2929

3030
Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)
3131
ReverseIterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error)
32-
33-
// Close releases associated resources. It should NOT be idempotent. It must
34-
// only be called once and any call after may panic.
35-
io.Closer
3632
}
3733

3834
// UpgradableDatabase defines an API for a versioned database that allows pruning

store/v2/root/reader.go

+18-18
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,38 @@ var (
1414
// operations. This is useful for exposing a read-only view of the RootStore at
1515
// a specific version in history, which could also be the latest state.
1616
type ReaderMap struct {
17-
rootStore store.RootStore
18-
version uint64
17+
vReader store.VersionedReader
18+
version uint64
1919
}
2020

21-
func NewReaderMap(v uint64, rs store.RootStore) *ReaderMap {
21+
func NewReaderMap(v uint64, vr store.VersionedReader) *ReaderMap {
2222
return &ReaderMap{
23-
rootStore: rs,
24-
version: v,
23+
vReader: vr,
24+
version: v,
2525
}
2626
}
2727

28-
func (roa *ReaderMap) GetReader(actor []byte) (corestore.Reader, error) {
29-
return NewReader(roa.version, roa.rootStore, actor), nil
28+
func (rm *ReaderMap) GetReader(actor []byte) (corestore.Reader, error) {
29+
return NewReader(rm.version, rm.vReader, actor), nil
3030
}
3131

3232
// Reader represents a read-only adapter for accessing data from the root store.
3333
type Reader struct {
34-
version uint64 // The version of the data.
35-
rootStore store.RootStore // The root store to read data from.
36-
actor []byte // The actor associated with the data.
34+
version uint64 // The version of the data.
35+
vReader store.VersionedReader // The root store to read data from.
36+
actor []byte // The actor associated with the data.
3737
}
3838

39-
func NewReader(v uint64, rs store.RootStore, actor []byte) *Reader {
39+
func NewReader(v uint64, vr store.VersionedReader, actor []byte) *Reader {
4040
return &Reader{
41-
version: v,
42-
rootStore: rs,
43-
actor: actor,
41+
version: v,
42+
vReader: vr,
43+
actor: actor,
4444
}
4545
}
4646

4747
func (roa *Reader) Has(key []byte) (bool, error) {
48-
val, err := roa.rootStore.GetStateStorage().Has(roa.actor, roa.version, key)
48+
val, err := roa.vReader.Has(roa.actor, roa.version, key)
4949
if err != nil {
5050
return false, err
5151
}
@@ -54,13 +54,13 @@ func (roa *Reader) Has(key []byte) (bool, error) {
5454
}
5555

5656
func (roa *Reader) Get(key []byte) ([]byte, error) {
57-
return roa.rootStore.GetStateStorage().Get(roa.actor, roa.version, key)
57+
return roa.vReader.Get(roa.actor, roa.version, key)
5858
}
5959

6060
func (roa *Reader) Iterator(start, end []byte) (corestore.Iterator, error) {
61-
return roa.rootStore.GetStateStorage().Iterator(roa.actor, roa.version, start, end)
61+
return roa.vReader.Iterator(roa.actor, roa.version, start, end)
6262
}
6363

6464
func (roa *Reader) ReverseIterator(start, end []byte) (corestore.Iterator, error) {
65-
return roa.rootStore.GetStateStorage().ReverseIterator(roa.actor, roa.version, start, end)
65+
return roa.vReader.ReverseIterator(roa.actor, roa.version, start, end)
6666
}

store/v2/root/store.go

+36-12
Original file line numberDiff line numberDiff line change
@@ -118,27 +118,51 @@ func (s *Store) SetInitialVersion(v uint64) error {
118118
return s.stateCommitment.SetInitialVersion(v)
119119
}
120120

121-
func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) {
122-
v, err := s.GetLatestVersion()
121+
// getVersionedReader returns a VersionedReader based on the given version. If the
122+
// version exists in the state storage, it returns the state storage.
123+
// If not, it checks if the state commitment implements the VersionedReader interface
124+
// and the version exists in the state commitment, since the state storage will be
125+
// synced during migration.
126+
func (s *Store) getVersionedReader(version uint64) (store.VersionedReader, error) {
127+
isExist, err := s.stateStorage.VersionExists(version)
123128
if err != nil {
124-
return 0, nil, err
129+
return nil, err
130+
}
131+
if isExist {
132+
return s.stateStorage, nil
125133
}
126134

127-
return v, NewReaderMap(v, s), nil
135+
if vReader, ok := s.stateCommitment.(store.VersionedReader); ok {
136+
isExist, err := vReader.VersionExists(version)
137+
if err != nil {
138+
return nil, err
139+
}
140+
if isExist {
141+
return vReader, nil
142+
}
143+
}
144+
145+
return nil, fmt.Errorf("version %d does not exist", version)
128146
}
129147

130-
// StateAt checks if the requested version is present in ss.
131-
func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) {
132-
// check if version is present in state storage
133-
isExist, err := s.stateStorage.VersionExists(v)
148+
func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) {
149+
v, err := s.GetLatestVersion()
134150
if err != nil {
135-
return nil, err
151+
return 0, nil, err
136152
}
137-
if !isExist {
138-
return nil, fmt.Errorf("version %d does not exist", v)
153+
154+
vReader, err := s.getVersionedReader(v)
155+
if err != nil {
156+
return 0, nil, err
139157
}
140158

141-
return NewReaderMap(v, s), nil
159+
return v, NewReaderMap(v, vReader), nil
160+
}
161+
162+
// StateAt returns a read-only view of the state at a given version.
163+
func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) {
164+
vReader, err := s.getVersionedReader(v)
165+
return NewReaderMap(v, vReader), err
142166
}
143167

144168
func (s *Store) GetStateStorage() store.VersionedWriter {

0 commit comments

Comments
 (0)