cosmos-sdk/runtime/v2/store.go

111 lines
3.4 KiB
Go

package runtime
import (
"errors"
"fmt"
"cosmossdk.io/core/store"
"cosmossdk.io/server/v2/stf"
storev2 "cosmossdk.io/store/v2"
)
// 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)
// 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
}
// 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
addedFilter := make(map[string]struct{})
deletedFilter := make(map[string]struct{})
for _, key := range storeUpgrades.Added {
if _, ok := addedFilter[key]; ok {
return fmt.Errorf("store upgrade has duplicate key %s in added", key)
}
addedFilter[key] = struct{}{}
}
for _, key := range storeUpgrades.Deleted {
if _, ok := deletedFilter[key]; ok {
return fmt.Errorf("store upgrade has duplicate key %s in deleted", key)
}
deletedFilter[key] = struct{}{}
}
for _, key := range storeUpgrades.Added {
if _, ok := deletedFilter[key]; ok {
return fmt.Errorf("store upgrade has key %s in both added and deleted", key)
}
}
for _, key := range storeUpgrades.Deleted {
if _, ok := addedFilter[key]; ok {
return fmt.Errorf("store upgrade has key %s in both added and deleted", key)
}
}
return nil
}