-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
(cherry picked from commit c9f0e2e) # Conflicts: # runtime/v2/store.go
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package runtime | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
corestore "cosmossdk.io/core/store" | ||
) | ||
|
||
func TestCheckStoreUpgrade(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
storeUpgrades *corestore.StoreUpgrades | ||
errMsg string | ||
}{ | ||
{ | ||
name: "Nil StoreUpgrades", | ||
storeUpgrades: nil, | ||
errMsg: "store upgrades cannot be nil", | ||
}, | ||
{ | ||
name: "Valid StoreUpgrades", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2"}, | ||
Deleted: []string{"store3", "store4"}, | ||
}, | ||
}, | ||
{ | ||
name: "Duplicate key in Added", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2", "store1"}, | ||
Deleted: []string{"store3"}, | ||
}, | ||
errMsg: "store upgrade has duplicate key store1 in added", | ||
}, | ||
{ | ||
name: "Duplicate key in Deleted", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1"}, | ||
Deleted: []string{"store2", "store3", "store2"}, | ||
}, | ||
errMsg: "store upgrade has duplicate key store2 in deleted", | ||
}, | ||
{ | ||
name: "Key in both Added and Deleted", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2"}, | ||
Deleted: []string{"store2", "store3"}, | ||
}, | ||
errMsg: "store upgrade has key store2 in both added and deleted", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := checkStoreUpgrade(tt.storeUpgrades) | ||
if tt.errMsg == "" { | ||
require.NoError(t, err) | ||
} else { | ||
require.ErrorContains(t, err, tt.errMsg) | ||
} | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package runtime | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"cosmossdk.io/core/store" | ||
"cosmossdk.io/server/v2/stf" | ||
Check failure on line 8 in runtime/v2/store.go GitHub Actions / split-test-files
Check failure on line 8 in runtime/v2/store.go GitHub Actions / dependency-review
Check failure on line 8 in runtime/v2/store.go GitHub Actions / dependency-review
|
||
storev2 "cosmossdk.io/store/v2" | ||
Check failure on line 9 in runtime/v2/store.go GitHub Actions / split-test-files
Check failure on line 9 in runtime/v2/store.go GitHub Actions / dependency-review
Check failure on line 9 in runtime/v2/store.go GitHub Actions / dependency-review
|
||
"cosmossdk.io/store/v2/proof" | ||
Check failure on line 10 in runtime/v2/store.go GitHub Actions / split-test-files
Check failure on line 10 in runtime/v2/store.go GitHub Actions / dependency-review
Check failure on line 10 in runtime/v2/store.go GitHub Actions / dependency-review
|
||
) | ||
|
||
// NewKVStoreService creates a new KVStoreService. | ||
// This wrapper is kept for backwards compatibility. | ||
// When migrating from runtime to runtime/v2, use runtimev2.NewKVStoreService(storeKey.Name()) instead of runtime.NewKVStoreService(storeKey). | ||
func NewKVStoreService(storeKey string) store.KVStoreService { | ||
return stf.NewKVStoreService([]byte(storeKey)) | ||
} | ||
|
||
type Store interface { | ||
// GetLatestVersion returns the latest version that consensus has been made on | ||
GetLatestVersion() (uint64, error) | ||
// StateLatest returns a readonly view over the latest | ||
// committed state of the store. Alongside the version | ||
// associated with it. | ||
StateLatest() (uint64, store.ReaderMap, error) | ||
|
||
// StateAt returns a readonly view over the provided | ||
// version. Must error when the version does not exist. | ||
StateAt(version uint64) (store.ReaderMap, error) | ||
|
||
// SetInitialVersion sets the initial version of the store. | ||
SetInitialVersion(uint64) error | ||
|
||
// WorkingHash writes the provided changeset to the state and returns | ||
// the working hash of the state. | ||
WorkingHash(changeset *store.Changeset) (store.Hash, error) | ||
|
||
// Commit commits the provided changeset and returns the new state root of the state. | ||
Commit(changeset *store.Changeset) (store.Hash, error) | ||
|
||
// Query is a key/value query directly to the underlying database. This skips the appmanager | ||
Query(storeKey []byte, version uint64, key []byte, prove bool) (storev2.QueryResult, error) | ||
|
||
// GetStateStorage returns the SS backend. | ||
GetStateStorage() storev2.VersionedDatabase | ||
|
||
// GetStateCommitment returns the SC backend. | ||
GetStateCommitment() storev2.Committer | ||
|
||
// LoadVersion loads the RootStore to the given version. | ||
LoadVersion(version uint64) error | ||
|
||
// LoadLatestVersion behaves identically to LoadVersion except it loads the | ||
// latest version implicitly. | ||
LoadLatestVersion() error | ||
|
||
// LastCommitID returns the latest commit ID | ||
LastCommitID() (proof.CommitID, error) | ||
} | ||
|
||
// StoreLoader allows for custom loading of the store, this is useful when upgrading the store from a previous version | ||
type StoreLoader func(store Store) error | ||
|
||
// DefaultStoreLoader just calls LoadLatestVersion on the store | ||
func DefaultStoreLoader(store Store) error { | ||
return store.LoadLatestVersion() | ||
} | ||
|
||
// UpgradeStoreLoader upgrades the store if the upgrade height matches the current version, it is used as a replacement | ||
// for the DefaultStoreLoader when there are store upgrades | ||
func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) StoreLoader { | ||
// sanity checks on store upgrades | ||
if err := checkStoreUpgrade(storeUpgrades); err != nil { | ||
panic(err) | ||
} | ||
|
||
return func(store Store) error { | ||
latestVersion, err := store.GetLatestVersion() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if uint64(upgradeHeight) == latestVersion+1 { | ||
if len(storeUpgrades.Deleted) > 0 || len(storeUpgrades.Added) > 0 { | ||
if upgrader, ok := store.(storev2.UpgradeableStore); ok { | ||
return upgrader.LoadVersionAndUpgrade(latestVersion, storeUpgrades) | ||
} | ||
|
||
return fmt.Errorf("store does not support upgrades") | ||
} | ||
} | ||
|
||
return DefaultStoreLoader(store) | ||
} | ||
} | ||
|
||
// checkStoreUpgrade performs sanity checks on the store upgrades | ||
func checkStoreUpgrade(storeUpgrades *store.StoreUpgrades) error { | ||
if storeUpgrades == nil { | ||
return errors.New("store upgrades cannot be nil") | ||
} | ||
|
||
// check for duplicates | ||
exists := make(map[string]bool) | ||
for _, key := range storeUpgrades.Added { | ||
if exists[key] { | ||
return fmt.Errorf("store upgrade has duplicate key %s in added", key) | ||
} | ||
|
||
if storeUpgrades.IsDeleted(key) { | ||
return fmt.Errorf("store upgrade has key %s in both added and deleted", key) | ||
} | ||
|
||
exists[key] = true | ||
} | ||
exists = make(map[string]bool) | ||
for _, key := range storeUpgrades.Deleted { | ||
if exists[key] { | ||
return fmt.Errorf("store upgrade has duplicate key %s in deleted", key) | ||
} | ||
|
||
if storeUpgrades.IsAdded(key) { | ||
return fmt.Errorf("store upgrade has key %s in both added and deleted", key) | ||
} | ||
|
||
exists[key] = true | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package runtime | ||
|
||
import ( | ||
"testing" | ||
|
||
corestore "cosmossdk.io/core/store" | ||
) | ||
|
||
func TestCheckStoreUpgrade(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
storeUpgrades *corestore.StoreUpgrades | ||
wantErr bool | ||
errMsg string | ||
}{ | ||
{ | ||
name: "Nil StoreUpgrades", | ||
storeUpgrades: nil, | ||
wantErr: true, | ||
errMsg: "store upgrades cannot be nil", | ||
}, | ||
{ | ||
name: "Valid StoreUpgrades", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2"}, | ||
Deleted: []string{"store3", "store4"}, | ||
}, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Duplicate key in Added", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2", "store1"}, | ||
Deleted: []string{"store3"}, | ||
}, | ||
wantErr: true, | ||
errMsg: "store upgrade has duplicate key store1 in added", | ||
}, | ||
{ | ||
name: "Duplicate key in Deleted", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1"}, | ||
Deleted: []string{"store2", "store3", "store2"}, | ||
}, | ||
wantErr: true, | ||
errMsg: "store upgrade has duplicate key store2 in deleted", | ||
}, | ||
{ | ||
name: "Key in both Added and Deleted", | ||
storeUpgrades: &corestore.StoreUpgrades{ | ||
Added: []string{"store1", "store2"}, | ||
Deleted: []string{"store2", "store3"}, | ||
}, | ||
wantErr: true, | ||
errMsg: "store upgrade has key store2 in both added and deleted", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := checkStoreUpgrade(tt.storeUpgrades) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("checkStoreUpgrade() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if err != nil && err.Error() != tt.errMsg { | ||
t.Errorf("checkStoreUpgrade() error message = %v, want %v", err.Error(), tt.errMsg) | ||
} | ||
}) | ||
} | ||
} |