-
Notifications
You must be signed in to change notification settings - Fork 4.1k
feat(store/v2): route to the commitment during migration #22290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
c203f80
3ee0d0f
de2b5a4
f917ada
016d948
9b8deeb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -25,6 +25,10 @@ var ( | |||||||||||||||||||||||||||||||||||||||||
| _ store.UpgradeableStore = (*CommitStore)(nil) | ||||||||||||||||||||||||||||||||||||||||||
| _ snapshots.CommitSnapshotter = (*CommitStore)(nil) | ||||||||||||||||||||||||||||||||||||||||||
| _ store.PausablePruner = (*CommitStore)(nil) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // NOTE: CommitStore implements store.VersionedReader, but it is not used in the | ||||||||||||||||||||||||||||||||||||||||||
| // store v2. It is only used during the migration process. | ||||||||||||||||||||||||||||||||||||||||||
| _ store.VersionedReader = (*CommitStore)(nil) | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // MountTreeFn is a function that mounts a tree given a store key. | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -275,21 +279,67 @@ func (c *CommitStore) GetProof(storeKey []byte, version uint64, key []byte) ([]p | |||||||||||||||||||||||||||||||||||||||||
| return []proof.CommitmentOp{commitOp, *storeCommitmentOp}, nil | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Get implements store.VersionedReader. | ||||||||||||||||||||||||||||||||||||||||||
| func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||
| tree, ok := c.multiTrees[conv.UnsafeBytesToStr(storeKey)] | ||||||||||||||||||||||||||||||||||||||||||
| func (c *CommitStore) getReader(storeKey string) (Reader, error) { | ||||||||||||||||||||||||||||||||||||||||||
| tree, ok := c.multiTrees[storeKey] | ||||||||||||||||||||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("store %s not found", storeKey) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| bz, err := tree.Get(version, key) | ||||||||||||||||||||||||||||||||||||||||||
| reader, ok := tree.(Reader) | ||||||||||||||||||||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||||||||||||||||||||
| return nil, errors.New("tree does not implement Reader") | ||||||||||||||||||||||||||||||||||||||||||
tac0turtle marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| return reader, nil | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // VersionExists implements store.VersionedReader. | ||||||||||||||||||||||||||||||||||||||||||
| func (c *CommitStore) VersionExists(version uint64) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||
| ci, err := c.metadata.GetCommitInfo(version) | ||||||||||||||||||||||||||||||||||||||||||
| return ci != nil, err | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Get implements store.VersionedReader. | ||||||||||||||||||||||||||||||||||||||||||
| func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) { | ||||||||||||||||||||||||||||||||||||||||||
| reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey)) | ||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
| return nil, err | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+307
to
+313
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure safe conversion of In the error message, both Consider converting the byte slices to strings safely or formatting them as hexadecimal. Apply this diff to address the issue: func (c *CommitStore) Get(storeKey []byte, version uint64, key []byte) ([]byte, error) {
reader, err := c.getReader(conv.UnsafeBytesToStr(storeKey))
if err != nil {
return nil, err
}
bz, err := reader.Get(version, key)
if err != nil {
- return nil, fmt.Errorf("failed to get key %s from store %s: %w", key, storeKey, err)
+ return nil, fmt.Errorf("failed to get key %x from store %s: %w", key, conv.UnsafeBytesToStr(storeKey), err)
}
return bz, nil
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| bz, err := reader.Get(version, key) | ||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("failed to get key %s from store %s: %w", key, storeKey, err) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| return bz, nil | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Has implements store.VersionedReader. | ||||||||||||||||||||||||||||||||||||||||||
| func (c *CommitStore) Has(storeKey []byte, version uint64, key []byte) (bool, error) { | ||||||||||||||||||||||||||||||||||||||||||
| val, err := c.Get(storeKey, version, key) | ||||||||||||||||||||||||||||||||||||||||||
| return len(val) > 0, err | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| return len(val) > 0, err | |
| return val != nil, err |
You can use this test to verify:
func (s *CommitStoreTestSuite) TestStore_Has() {
storeKeys := []string{storeKey1}
myKey := []byte("myKey")
const initialVersion = 1
specs := map[string]struct {
src *corestore.Changeset
queryVersion uint64
expExists bool
expErr bool
}{
"known key with some value": {
src: corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{
storeKey1: {{Key: myKey, Value: []byte("my-value")}},
}),
queryVersion: initialVersion,
expExists: true,
},
"known key with empty value": {
src: corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{
storeKey1: {{Key: myKey, Value: []byte("")}},
}),
queryVersion: initialVersion,
expExists: true,
},
"unknown key": {
src: corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{}),
queryVersion: initialVersion,
expExists: false,
},
"unknown version": {
src: corestore.NewChangesetWithPairs(map[string]corestore.KVPairs{
storeKey1: {{Key: myKey, Value: []byte("")}},
}),
queryVersion: initialVersion + 1,
expErr: true,
},
}
for name, spec := range specs {
s.T().Run(name, func(t *testing.T) {
commitStore, err := s.NewStore(dbm.NewMemDB(), storeKeys, nil, coretesting.NewNopLogger())
require.NoError(t, err)
require.NoError(t, commitStore.WriteChangeset(spec.src))
_, err = commitStore.Commit(initialVersion)
require.NoError(t, err)
// when
gotResult, gotErr := commitStore.Has([]byte(storeKey1), spec.queryVersion, myKey)
// then
if spec.expErr {
require.Error(t, gotErr)
return
}
require.NoError(t, gotErr)
require.Equal(t, spec.expExists, gotResult)
})
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, let me try to add this test case!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had added the test to this file locally: https://github.com/cosmos/cosmos-sdk/blob/main/store/v2/commitment/store_test_suite.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| func (c *CommitStore) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { | |
| // WARNING: these are implemented for migration from store v1 to store v2, they should not be called | |
| func (c *CommitStore) Iterator(storeKey []byte, version uint64, start, end []byte) (corestore.Iterator, error) { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import ( | |
|
|
||
| ics23 "github.com/cosmos/ics23/go" | ||
|
|
||
| corestore "cosmossdk.io/core/store" | ||
| snapshotstypes "cosmossdk.io/store/v2/snapshots/types" | ||
| ) | ||
|
|
||
|
|
@@ -29,19 +30,20 @@ type Tree interface { | |
| SetInitialVersion(version uint64) error | ||
| GetProof(version uint64, key []byte) (*ics23.CommitmentProof, error) | ||
|
|
||
| // Get attempts to retrieve a value from the tree for a given version. | ||
| // | ||
| // NOTE: This method only exists to support migration from IAVL v0/v1 to v2. | ||
| // Once migration is complete, this method should be removed and/or not used. | ||
| Get(version uint64, key []byte) ([]byte, error) | ||
|
|
||
| Prune(version uint64) error | ||
| Export(version uint64) (Exporter, error) | ||
| Import(version uint64) (Importer, error) | ||
|
|
||
| io.Closer | ||
| } | ||
|
|
||
| // Reader is the optional interface that is only used to read data from the tree | ||
| // during the migration process. | ||
| type Reader interface { | ||
| Get(version uint64, key []byte) ([]byte, error) | ||
| Iterator(version uint64, start, end []byte, ascending bool) (corestore.Iterator, error) | ||
| } | ||
|
|
||
|
Comment on lines
+40
to
+46
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider the impact of removing By removing the |
||
| // Exporter is the interface that wraps the basic Export methods. | ||
| type Exporter interface { | ||
| Next() (*snapshotstypes.SnapshotIAVLItem, error) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,38 +14,38 @@ var ( | |
| // operations. This is useful for exposing a read-only view of the RootStore at | ||
| // a specific version in history, which could also be the latest state. | ||
| type ReaderMap struct { | ||
| rootStore store.RootStore | ||
| version uint64 | ||
| vReader store.VersionedReader | ||
| version uint64 | ||
| } | ||
|
|
||
| func NewReaderMap(v uint64, rs store.RootStore) *ReaderMap { | ||
| func NewReaderMap(v uint64, vr store.VersionedReader) *ReaderMap { | ||
| return &ReaderMap{ | ||
| rootStore: rs, | ||
| version: v, | ||
| vReader: vr, | ||
| version: v, | ||
|
Comment on lines
+21
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Use descriptive parameter names for clarity. In the constructors Apply this diff to update the parameter names: -func NewReaderMap(v uint64, vr store.VersionedReader) *ReaderMap {
+func NewReaderMap(version uint64, versionedReader store.VersionedReader) *ReaderMap {
return &ReaderMap{
- vReader: vr,
- version: v,
+ vReader: versionedReader,
+ version: version,
}
}
-func NewReader(v uint64, vr store.VersionedReader, actor []byte) *Reader {
+func NewReader(version uint64, versionedReader store.VersionedReader, actor []byte) *Reader {
return &Reader{
- version: v,
- vReader: vr,
+ version: version,
+ vReader: versionedReader,
actor: actor,
}
}Also applies to: 39-43 |
||
| } | ||
| } | ||
|
|
||
| func (roa *ReaderMap) GetReader(actor []byte) (corestore.Reader, error) { | ||
| return NewReader(roa.version, roa.rootStore, actor), nil | ||
| return NewReader(roa.version, roa.vReader, actor), nil | ||
|
||
| } | ||
|
|
||
| // Reader represents a read-only adapter for accessing data from the root store. | ||
| type Reader struct { | ||
| version uint64 // The version of the data. | ||
| rootStore store.RootStore // The root store to read data from. | ||
| actor []byte // The actor associated with the data. | ||
| version uint64 // The version of the data. | ||
| vReader store.VersionedReader // The root store to read data from. | ||
| actor []byte // The actor associated with the data. | ||
| } | ||
|
|
||
| func NewReader(v uint64, rs store.RootStore, actor []byte) *Reader { | ||
| func NewReader(v uint64, vr store.VersionedReader, actor []byte) *Reader { | ||
| return &Reader{ | ||
| version: v, | ||
| rootStore: rs, | ||
| actor: actor, | ||
| version: v, | ||
| vReader: vr, | ||
| actor: actor, | ||
| } | ||
| } | ||
|
|
||
| func (roa *Reader) Has(key []byte) (bool, error) { | ||
| val, err := roa.rootStore.GetStateStorage().Has(roa.actor, roa.version, key) | ||
| val, err := roa.vReader.Has(roa.actor, roa.version, key) | ||
| if err != nil { | ||
| return false, err | ||
| } | ||
|
|
@@ -54,13 +54,13 @@ func (roa *Reader) Has(key []byte) (bool, error) { | |
| } | ||
|
|
||
| func (roa *Reader) Get(key []byte) ([]byte, error) { | ||
| return roa.rootStore.GetStateStorage().Get(roa.actor, roa.version, key) | ||
| return roa.vReader.Get(roa.actor, roa.version, key) | ||
| } | ||
|
|
||
| func (roa *Reader) Iterator(start, end []byte) (corestore.Iterator, error) { | ||
| return roa.rootStore.GetStateStorage().Iterator(roa.actor, roa.version, start, end) | ||
| return roa.vReader.Iterator(roa.actor, roa.version, start, end) | ||
| } | ||
|
|
||
| func (roa *Reader) ReverseIterator(start, end []byte) (corestore.Iterator, error) { | ||
| return roa.rootStore.GetStateStorage().ReverseIterator(roa.actor, roa.version, start, end) | ||
| return roa.vReader.ReverseIterator(roa.actor, roa.version, start, end) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,27 +118,46 @@ func (s *Store) SetInitialVersion(v uint64) error { | |
| return s.stateCommitment.SetInitialVersion(v) | ||
| } | ||
|
|
||
| func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) { | ||
| v, err := s.GetLatestVersion() | ||
| func (s *Store) getVersionedReader(version uint64) (store.VersionedReader, error) { | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| isExist, err := s.stateStorage.VersionExists(version) | ||
| if err != nil { | ||
| return 0, nil, err | ||
| return nil, err | ||
| } | ||
| if isExist { | ||
| return s.stateStorage, nil | ||
| } | ||
|
|
||
| return v, NewReaderMap(v, s), nil | ||
| if vReader, ok := s.stateCommitment.(store.VersionedReader); ok { | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| isExist, err := vReader.VersionExists(version) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if isExist { | ||
| return vReader, nil | ||
| } | ||
| } | ||
|
|
||
| return nil, fmt.Errorf("version %d does not exist", version) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: this error is also returned when the type cast failed.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, it is expected |
||
| } | ||
|
|
||
| // StateAt checks if the requested version is present in ss. | ||
| func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) { | ||
| // check if version is present in state storage | ||
| isExist, err := s.stateStorage.VersionExists(v) | ||
| func (s *Store) StateLatest() (uint64, corestore.ReaderMap, error) { | ||
| v, err := s.GetLatestVersion() | ||
| if err != nil { | ||
| return nil, err | ||
| return 0, nil, err | ||
| } | ||
| if !isExist { | ||
| return nil, fmt.Errorf("version %d does not exist", v) | ||
|
|
||
| vReader, err := s.getVersionedReader(v) | ||
| if err != nil { | ||
| return 0, nil, err | ||
| } | ||
|
|
||
| return NewReaderMap(v, s), nil | ||
| return v, NewReaderMap(v, vReader), nil | ||
| } | ||
|
|
||
| // StateAt returns a read-only view of the state at a given version. | ||
| func (s *Store) StateAt(v uint64) (corestore.ReaderMap, error) { | ||
| vReader, err := s.getVersionedReader(v) | ||
| return NewReaderMap(v, vReader), err | ||
| } | ||
|
|
||
| func (s *Store) GetStateStorage() store.VersionedWriter { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this error could poise a problem, if a user does not implement reader on the commitment structure and this code path is called it would end in an error that can not be recovered.
can we leave some code comments about why this is here?