Merge branch 'master' into jonathan/5684-prune-syncable
This commit is contained in:
commit
56c08949a4
|
@ -3,7 +3,7 @@ version: 2.1
|
|||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: circleci/golang:1.13
|
||||
- image: circleci/golang:1.14
|
||||
docs:
|
||||
docker:
|
||||
- image: tendermintdev/docker-website-deployment
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
pull_request_rules:
|
||||
- name: automerge to master with label automerge and branch protection passing
|
||||
conditions:
|
||||
- "#approved-reviews-by>=1"
|
||||
- base=master
|
||||
- label=automerge
|
||||
actions:
|
||||
merge:
|
||||
method: squash
|
||||
strict: true
|
|
@ -46,6 +46,7 @@ balances or a single balance by denom when the `denom` query parameter is presen
|
|||
|
||||
### API Breaking Changes
|
||||
|
||||
* [\#5719](https://github.com/cosmos/cosmos-sdk/pull/5719) Bump Go requirement to 1.14+
|
||||
* (x/params) [\#5619](https://github.com/cosmos/cosmos-sdk/pull/5619) The `x/params` keeper now accepts a `codec.Marshaller` instead of
|
||||
a reference to an amino codec. Amino is still used for JSON serialization.
|
||||
* (types) [\#5579](https://github.com/cosmos/cosmos-sdk/pull/5579) The `keepRecent` field has been removed from the `PruningOptions` type.
|
||||
|
|
|
@ -22,7 +22,7 @@ It is being used to build [`Gaia`](https://github.com/cosmos/gaia), the first im
|
|||
**WARNING**: The SDK has mostly stabilized, but we are still making some
|
||||
breaking changes.
|
||||
|
||||
**Note**: Requires [Go 1.13+](https://golang.org/dl/)
|
||||
**Note**: Requires [Go 1.14+](https://golang.org/dl/)
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package baseapp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
@ -17,7 +14,6 @@ import (
|
|||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
@ -226,62 +222,6 @@ func DefaultStoreLoader(ms sdk.CommitMultiStore) error {
|
|||
return ms.LoadLatestVersion()
|
||||
}
|
||||
|
||||
// StoreLoaderWithUpgrade is used to prepare baseapp with a fixed StoreLoader
|
||||
// pattern. This is useful in test cases, or with custom upgrade loading logic.
|
||||
func StoreLoaderWithUpgrade(upgrades *storetypes.StoreUpgrades) StoreLoader {
|
||||
return func(ms sdk.CommitMultiStore) error {
|
||||
return ms.LoadLatestVersionAndUpgrade(upgrades)
|
||||
}
|
||||
}
|
||||
|
||||
// UpgradeableStoreLoader can be configured by SetStoreLoader() to check for the
|
||||
// existence of a given upgrade file - json encoded StoreUpgrades data.
|
||||
//
|
||||
// If not file is present, it will peform the default load (no upgrades to store).
|
||||
//
|
||||
// If the file is present, it will parse the file and execute those upgrades
|
||||
// (rename or delete stores), while loading the data. It will also delete the
|
||||
// upgrade file upon successful load, so that the upgrade is only applied once,
|
||||
// and not re-applied on next restart
|
||||
//
|
||||
// This is useful for in place migrations when a store key is renamed between
|
||||
// two versions of the software. (TODO: this code will move to x/upgrades
|
||||
// when PR #4233 is merged, here mainly to help test the design)
|
||||
func UpgradeableStoreLoader(upgradeInfoPath string) StoreLoader {
|
||||
return func(ms sdk.CommitMultiStore) error {
|
||||
_, err := os.Stat(upgradeInfoPath)
|
||||
if os.IsNotExist(err) {
|
||||
return DefaultStoreLoader(ms)
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// there is a migration file, let's execute
|
||||
data, err := ioutil.ReadFile(upgradeInfoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read upgrade file %s: %v", upgradeInfoPath, err)
|
||||
}
|
||||
|
||||
var upgrades storetypes.StoreUpgrades
|
||||
err = json.Unmarshal(data, &upgrades)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse upgrade file: %v", err)
|
||||
}
|
||||
|
||||
err = ms.LoadLatestVersionAndUpgrade(&upgrades)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load and upgrade database: %v", err)
|
||||
}
|
||||
|
||||
// if we have a successful load, we delete the file
|
||||
err = os.Remove(upgradeInfoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting upgrade file %s: %v", upgradeInfoPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LoadVersion loads the BaseApp application version. It will panic if called
|
||||
// more than once on a running baseapp.
|
||||
func (app *BaseApp) LoadVersion(version int64, baseKey *sdk.KVStoreKey) error {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
@ -137,18 +136,6 @@ func useDefaultLoader(app *BaseApp) {
|
|||
app.SetStoreLoader(DefaultStoreLoader)
|
||||
}
|
||||
|
||||
func useUpgradeLoader(upgrades *store.StoreUpgrades) func(*BaseApp) {
|
||||
return func(app *BaseApp) {
|
||||
app.SetStoreLoader(StoreLoaderWithUpgrade(upgrades))
|
||||
}
|
||||
}
|
||||
|
||||
func useFileUpgradeLoader(upgradeInfoPath string) func(*BaseApp) {
|
||||
return func(app *BaseApp) {
|
||||
app.SetStoreLoader(UpgradeableStoreLoader(upgradeInfoPath))
|
||||
}
|
||||
}
|
||||
|
||||
func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) {
|
||||
rs := rootmulti.NewStore(db)
|
||||
rs.SetPruning(store.PruneNothing)
|
||||
|
@ -184,19 +171,6 @@ func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte
|
|||
// Test that we can make commits and then reload old versions.
|
||||
// Test that LoadLatestVersion actually does.
|
||||
func TestSetLoader(t *testing.T) {
|
||||
// write a renamer to a file
|
||||
f, err := ioutil.TempFile("", "upgrade-*.json")
|
||||
require.NoError(t, err)
|
||||
data := []byte(`{"renamed":[{"old_key": "bnk", "new_key": "banker"}]}`)
|
||||
_, err = f.Write(data)
|
||||
require.NoError(t, err)
|
||||
configName := f.Name()
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
// make sure it exists before running everything
|
||||
_, err = os.Stat(configName)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := map[string]struct {
|
||||
setLoader func(*BaseApp)
|
||||
origStoreKey string
|
||||
|
@ -211,26 +185,6 @@ func TestSetLoader(t *testing.T) {
|
|||
origStoreKey: "foo",
|
||||
loadStoreKey: "foo",
|
||||
},
|
||||
"rename with inline opts": {
|
||||
setLoader: useUpgradeLoader(&store.StoreUpgrades{
|
||||
Renamed: []store.StoreRename{{
|
||||
OldKey: "foo",
|
||||
NewKey: "bar",
|
||||
}},
|
||||
}),
|
||||
origStoreKey: "foo",
|
||||
loadStoreKey: "bar",
|
||||
},
|
||||
"file loader with missing file": {
|
||||
setLoader: useFileUpgradeLoader(configName + "randomchars"),
|
||||
origStoreKey: "bnk",
|
||||
loadStoreKey: "bnk",
|
||||
},
|
||||
"file loader with existing file": {
|
||||
setLoader: useFileUpgradeLoader(configName),
|
||||
origStoreKey: "bnk",
|
||||
loadStoreKey: "banker",
|
||||
},
|
||||
}
|
||||
|
||||
k := []byte("key")
|
||||
|
@ -265,10 +219,6 @@ func TestSetLoader(t *testing.T) {
|
|||
checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
// ensure config file was deleted
|
||||
_, err = os.Stat(configName)
|
||||
require.True(t, os.IsNotExist(err))
|
||||
}
|
||||
|
||||
func TestAppVersionSetterGetter(t *testing.T) {
|
||||
|
|
2
go.mod
2
go.mod
|
@ -35,4 +35,4 @@ require (
|
|||
|
||||
replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.1
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
|
13
go.sum
13
go.sum
|
@ -35,7 +35,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
|
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
|
@ -45,7 +44,6 @@ github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6
|
|||
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
|
||||
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
|
||||
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU=
|
||||
github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=
|
||||
|
@ -196,8 +194,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
|||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
|
||||
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk=
|
||||
|
@ -209,7 +205,6 @@ github.com/regen-network/protobuf v1.3.2-alpha.regen.1/go.mod h1:lye6mqhOn/GCw1z
|
|||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
|
@ -226,8 +221,6 @@ github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
|
|||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
|
@ -236,7 +229,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
|
|||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
||||
|
@ -249,8 +241,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw=
|
||||
github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
|
@ -279,7 +269,6 @@ github.com/tendermint/tm-db v0.4.1 h1:TvX7JWjJOVZ+N3y+I86wddrGttOdMmmBxXcu0/Y7ZJ
|
|||
github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
|
||||
|
@ -292,7 +281,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
|
|||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
|
@ -327,7 +315,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
|
@ -122,7 +122,7 @@ type SimApp struct {
|
|||
// NewSimApp returns a reference to an initialized SimApp.
|
||||
func NewSimApp(
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool,
|
||||
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp),
|
||||
homePath string, invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp),
|
||||
) *SimApp {
|
||||
|
||||
// TODO: Remove cdc in favor of appCodec once all modules are migrated.
|
||||
|
@ -189,7 +189,7 @@ func NewSimApp(
|
|||
app.CrisisKeeper = crisis.NewKeeper(
|
||||
app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName,
|
||||
)
|
||||
app.UpgradeKeeper = upgrade.NewKeeper(skipUpgradeHeights, keys[upgrade.StoreKey], appCodec)
|
||||
app.UpgradeKeeper = upgrade.NewKeeper(skipUpgradeHeights, keys[upgrade.StoreKey], appCodec, homePath)
|
||||
|
||||
// create evidence keeper with router
|
||||
evidenceKeeper := evidence.NewKeeper(
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
func TestSimAppExport(t *testing.T) {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0)
|
||||
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0)
|
||||
|
||||
genesisState := NewDefaultGenesisState()
|
||||
stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
|
||||
|
@ -31,7 +31,7 @@ func TestSimAppExport(t *testing.T) {
|
|||
app.Commit()
|
||||
|
||||
// Making a new app object with the db, so that initchain hasn't been called
|
||||
app2 := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0)
|
||||
app2 := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0)
|
||||
_, _, err = app2.ExportAppStateAndValidators(false, []string{})
|
||||
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func TestSimAppExport(t *testing.T) {
|
|||
// ensure that black listed addresses are properly set in bank keeper
|
||||
func TestBlackListedAddrs(t *testing.T) {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0)
|
||||
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0)
|
||||
|
||||
for acc := range maccPerms {
|
||||
require.Equal(t, !allowedReceivingModAcc[acc], app.BankKeeper.BlacklistedAddr(app.SupplyKeeper.GetModuleAddress(acc)))
|
||||
|
|
|
@ -26,7 +26,7 @@ func BenchmarkFullAppSimulation(b *testing.B) {
|
|||
}
|
||||
}()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt())
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, interBlockCacheOpt())
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
|
@ -65,7 +65,7 @@ func BenchmarkInvariants(b *testing.B) {
|
|||
}
|
||||
}()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt())
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, interBlockCacheOpt())
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
|
|
|
@ -63,7 +63,7 @@ func TestFullAppSimulation(t *testing.T) {
|
|||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// run randomized simulation
|
||||
|
@ -95,7 +95,7 @@ func TestAppImportExport(t *testing.T) {
|
|||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
|
@ -129,7 +129,7 @@ func TestAppImportExport(t *testing.T) {
|
|||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
require.Equal(t, "SimApp", newApp.Name())
|
||||
|
||||
var genesisState GenesisState
|
||||
|
@ -181,7 +181,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
require.Equal(t, "SimApp", app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
|
@ -220,7 +220,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
|||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
newApp := NewSimApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, fauxMerkleModeOpt)
|
||||
require.Equal(t, "SimApp", newApp.Name())
|
||||
|
||||
newApp.InitChain(abci.RequestInitChain{
|
||||
|
@ -266,7 +266,7 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, FlagPeriodValue, interBlockCacheOpt())
|
||||
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, FlagPeriodValue, interBlockCacheOpt())
|
||||
|
||||
fmt.Printf(
|
||||
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
// Setup initializes a new SimApp. A Nop logger is set in SimApp.
|
||||
func Setup(isCheckTx bool) *SimApp {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0)
|
||||
app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0)
|
||||
if !isCheckTx {
|
||||
// init chain must be called to stop deliverState from being nil
|
||||
genesisState := NewDefaultGenesisState()
|
||||
|
@ -48,7 +48,7 @@ func Setup(isCheckTx bool) *SimApp {
|
|||
// accounts and possible balances.
|
||||
func SetupWithGenesisAccounts(genAccs []authexported.GenesisAccount, balances ...bank.Balance) *SimApp {
|
||||
db := dbm.NewMemDB()
|
||||
app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0)
|
||||
app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, DefaultNodeHome, 0)
|
||||
|
||||
// initialize the chain with the passed in genesis accounts
|
||||
genesisState := NewDefaultGenesisState()
|
||||
|
|
|
@ -44,6 +44,13 @@ type StoreUpgrades struct {
|
|||
Deleted []string `json:"deleted"`
|
||||
}
|
||||
|
||||
// UpgradeInfo defines height and name of the upgrade
|
||||
// to ensure multistore upgrades happen only at matching height.
|
||||
type UpgradeInfo struct {
|
||||
Name string `json:"name"`
|
||||
Height int64 `json:"height"`
|
||||
}
|
||||
|
||||
// StoreRename defines a name change of a sub-store.
|
||||
// All data previously under a PrefixStore with OldKey will be copied
|
||||
// to a PrefixStore with NewKey, then deleted from OldKey store.
|
||||
|
|
|
@ -25,7 +25,7 @@ var (
|
|||
|
||||
func createTestApp() (*simapp.SimApp, sdk.Context, []sdk.AccAddress) {
|
||||
db := dbm.NewMemDB()
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 1)
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 1)
|
||||
ctx := app.NewContext(true, abci.Header{})
|
||||
|
||||
constantFee := sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
func createTestApp() *simapp.SimApp {
|
||||
db := dbm.NewMemDB()
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 5)
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5)
|
||||
// init chain must be called to stop deliverState from being nil
|
||||
genesisState := simapp.NewDefaultGenesisState()
|
||||
stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
|
||||
|
|
|
@ -63,7 +63,7 @@ func TestImportExportQueues(t *testing.T) {
|
|||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0)
|
||||
app2 := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 0)
|
||||
|
||||
app2.InitChain(
|
||||
abci.RequestInitChain{
|
||||
|
|
|
@ -38,6 +38,14 @@ func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) {
|
|||
upgradeMsg := fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info)
|
||||
// We don't have an upgrade handler for this upgrade name, meaning this software is out of date so shutdown
|
||||
ctx.Logger().Error(upgradeMsg)
|
||||
|
||||
// Write the upgrade info to disk. The UpgradeStoreLoader uses this info to perform or skip
|
||||
// store migrations.
|
||||
err := k.DumpUpgradeInfoToDisk(ctx.BlockHeight(), plan.Name)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error()))
|
||||
}
|
||||
|
||||
panic(upgradeMsg)
|
||||
}
|
||||
// We have an upgrade handler for this upgrade name, so apply the upgrade
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package upgrade_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
@ -31,7 +36,7 @@ var s TestSuite
|
|||
|
||||
func setupTest(height int64, skip map[int64]bool) TestSuite {
|
||||
db := dbm.NewMemDB()
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, skip, 0)
|
||||
app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, skip, simapp.DefaultNodeHome, 0)
|
||||
genesisState := simapp.NewDefaultGenesisState()
|
||||
stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
|
||||
if err != nil {
|
||||
|
@ -393,3 +398,30 @@ func TestUpgradeWithoutSkip(t *testing.T) {
|
|||
VerifyDoUpgrade(t)
|
||||
VerifyDone(t, s.ctx, "test")
|
||||
}
|
||||
|
||||
func TestDumpUpgradeInfoToFile(t *testing.T) {
|
||||
s := setupTest(10, map[int64]bool{})
|
||||
|
||||
planHeight := s.ctx.BlockHeight() + 1
|
||||
name := "test"
|
||||
t.Log("verify if upgrade height is dumped to file")
|
||||
err := s.keeper.DumpUpgradeInfoToDisk(planHeight, name)
|
||||
require.Nil(t, err)
|
||||
|
||||
upgradeInfoFilePath, err := s.keeper.GetUpgradeInfoPath()
|
||||
require.Nil(t, err)
|
||||
|
||||
data, err := ioutil.ReadFile(upgradeInfoFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
var upgradeInfo storetypes.UpgradeInfo
|
||||
err = json.Unmarshal(data, &upgradeInfo)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Log("Verify upgrade height from file matches ")
|
||||
require.Equal(t, upgradeInfo.Height, planHeight)
|
||||
|
||||
// clear the test file
|
||||
err = os.Remove(upgradeInfoFilePath)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ var (
|
|||
NewSoftwareUpgradeProposal = types.NewSoftwareUpgradeProposal
|
||||
NewCancelSoftwareUpgradeProposal = types.NewCancelSoftwareUpgradeProposal
|
||||
NewQueryAppliedParams = types.NewQueryAppliedParams
|
||||
UpgradeStoreLoader = types.UpgradeStoreLoader
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
)
|
||||
|
|
|
@ -68,6 +68,27 @@ as well as providing the opportunity for the upgraded software to perform any ne
|
|||
(with the old binary) and applying the migration (with the new binary) are enforced in the state machine. Actually
|
||||
switching the binaries is an ops task and not handled inside the sdk / abci app.
|
||||
|
||||
Here is a sample code to set store migrations with an upgrade:
|
||||
|
||||
// this configures a no-op upgrade handler for the "my-fancy-upgrade" upgrade
|
||||
app.UpgradeKeeper.SetUpgradeHandler("my-fancy-upgrade", func(ctx sdk.Context, plan upgrade.Plan) {
|
||||
// upgrade changes here
|
||||
})
|
||||
|
||||
upgradeInfo := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
|
||||
if upgradeInfo.Name == "my-fancy-upgrade" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
|
||||
storeUpgrades := store.StoreUpgrades{
|
||||
Renamed: []store.StoreRename{{
|
||||
OldKey: "foo",
|
||||
NewKey: "bar",
|
||||
}},
|
||||
Deleted: []string{},
|
||||
}
|
||||
|
||||
// configure store loader that checks if version == upgradeHeight and applies store upgrades
|
||||
app.SetStoreLoader(upgrade.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
|
||||
}
|
||||
|
||||
Halt Behavior
|
||||
|
||||
Before halting the ABCI state machine in the BeginBlocker method, the upgrade module will log an error
|
||||
|
|
|
@ -2,19 +2,30 @@ package keeper
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/upgrade/types"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
store "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
|
||||
// UpgradeInfoFileName file to store upgrade information
|
||||
const UpgradeInfoFileName string = "upgrade-info.json"
|
||||
|
||||
type Keeper struct {
|
||||
homePath string
|
||||
skipUpgradeHeights map[int64]bool
|
||||
storeKey sdk.StoreKey
|
||||
cdc codec.Marshaler
|
||||
|
@ -22,8 +33,9 @@ type Keeper struct {
|
|||
}
|
||||
|
||||
// NewKeeper constructs an upgrade Keeper
|
||||
func NewKeeper(skipUpgradeHeights map[int64]bool, storeKey sdk.StoreKey, cdc codec.Marshaler) Keeper {
|
||||
func NewKeeper(skipUpgradeHeights map[int64]bool, storeKey sdk.StoreKey, cdc codec.Marshaler, homePath string) Keeper {
|
||||
return Keeper{
|
||||
homePath: homePath,
|
||||
skipUpgradeHeights: skipUpgradeHeights,
|
||||
storeKey: storeKey,
|
||||
cdc: cdc,
|
||||
|
@ -131,3 +143,59 @@ func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) {
|
|||
func (k Keeper) IsSkipHeight(height int64) bool {
|
||||
return k.skipUpgradeHeights[height]
|
||||
}
|
||||
|
||||
// DumpUpgradeInfoToDisk writes upgrade information to UpgradeInfoFileName.
|
||||
func (k Keeper) DumpUpgradeInfoToDisk(height int64, name string) error {
|
||||
upgradeInfoFilePath, err := k.GetUpgradeInfoPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeInfo := store.UpgradeInfo{
|
||||
Name: name,
|
||||
Height: height,
|
||||
}
|
||||
info, err := json.Marshal(upgradeInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(upgradeInfoFilePath, info, 0644)
|
||||
}
|
||||
|
||||
// GetUpgradeInfoPath returns the upgrade info file path
|
||||
func (k Keeper) GetUpgradeInfoPath() (string, error) {
|
||||
upgradeInfoFileDir := path.Join(k.getHomeDir(), "data")
|
||||
err := tmos.EnsureDir(upgradeInfoFileDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(upgradeInfoFileDir, UpgradeInfoFileName), nil
|
||||
}
|
||||
|
||||
// getHomeDir returns the height at which the given upgrade was executed
|
||||
func (k Keeper) getHomeDir() string {
|
||||
return k.homePath
|
||||
}
|
||||
|
||||
// ReadUpgradeInfoFromDisk returns the name and height of the upgrade
|
||||
// which is written to disk by the old binary when panic'ing
|
||||
// if there's an error in reading the info,
|
||||
// it assumes that the upgrade info is not available
|
||||
func (k Keeper) ReadUpgradeInfoFromDisk() (upgradeInfo store.UpgradeInfo) {
|
||||
upgradeInfoPath, err := k.GetUpgradeInfoPath()
|
||||
// if error in reading the path, assume there are no upgrades
|
||||
if err != nil {
|
||||
return upgradeInfo
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(upgradeInfoPath)
|
||||
// if error in reading the file, assume there are no upgrades
|
||||
if err != nil {
|
||||
return upgradeInfo
|
||||
}
|
||||
|
||||
json.Unmarshal(data, &upgradeInfo)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -56,6 +56,33 @@ During each `EndBlock` execution, the `x/upgrade` module checks if there exists
|
|||
`Handler` is executed. If the `Plan` is expected to execute but no `Handler` is registered
|
||||
or if the binary was upgraded too early, the node will gracefully panic and exit.
|
||||
|
||||
## StoreLoader
|
||||
|
||||
|
||||
The `x/upgrade` module also facilitates store migrations as part of the upgrade. The
|
||||
`StoreLoader` sets the migrations that need to occur before the new binary can
|
||||
successfully run the chain. This `StoreLoader` is also application specific and
|
||||
not defined on a per-module basis. Registering this `StoreLoader` is done via
|
||||
`app#SetStoreLoader` in the application.
|
||||
|
||||
```go
|
||||
func UpgradeStoreLoader (upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader
|
||||
```
|
||||
|
||||
If there's a planned upgrade and the upgrade height is reached, the old binary writes `UpgradeInfo` to the disk before panic'ing.
|
||||
|
||||
```go
|
||||
type UpgradeInfo struct {
|
||||
Name string
|
||||
Height int64
|
||||
}
|
||||
```
|
||||
|
||||
This information is critical to ensure the `StoreUpgrades` happens smoothly at correct height and
|
||||
expected upgrade. It eliminiates the chances for the new binary to execute `StoreUpgrades` multiple
|
||||
times everytime on restart. Also if there are multiple upgrades planned on same height, the `Name`
|
||||
will ensure these `StoreUpgrades` takes place only in planned upgrade handler.
|
||||
|
||||
## Proposal
|
||||
|
||||
Typically, a `Plan` is proposed and submitted through governance via a `SoftwareUpgradeProposal`.
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
store "github.com/cosmos/cosmos-sdk/store/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// UpgradeStoreLoader is used to prepare baseapp with a fixed StoreLoader
|
||||
// pattern. This is useful for custom upgrade loading logic.
|
||||
func UpgradeStoreLoader(upgradeHeight int64, storeUpgrades *store.StoreUpgrades) baseapp.StoreLoader {
|
||||
return func(ms sdk.CommitMultiStore) error {
|
||||
if upgradeHeight == ms.LastCommitID().Version {
|
||||
// Check if the current commit version and upgrade height matches
|
||||
if len(storeUpgrades.Renamed) > 0 || len(storeUpgrades.Deleted) > 0 {
|
||||
return ms.LoadLatestVersionAndUpgrade(storeUpgrades)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise load default store loader
|
||||
return baseapp.DefaultStoreLoader(ms)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/store/rootmulti"
|
||||
store "github.com/cosmos/cosmos-sdk/store/types"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func useUpgradeLoader(height int64, upgrades *store.StoreUpgrades) func(*baseapp.BaseApp) {
|
||||
return func(app *baseapp.BaseApp) {
|
||||
app.SetStoreLoader(UpgradeStoreLoader(height, upgrades))
|
||||
}
|
||||
}
|
||||
|
||||
func defaultLogger() log.Logger {
|
||||
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||
}
|
||||
|
||||
func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) {
|
||||
rs := rootmulti.NewStore(db)
|
||||
rs.SetPruning(store.PruneNothing)
|
||||
key := sdk.NewKVStoreKey(storeKey)
|
||||
rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil)
|
||||
err := rs.LoadLatestVersion()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, int64(0), rs.LastCommitID().Version)
|
||||
|
||||
// write some data in substore
|
||||
kv, _ := rs.GetStore(key).(store.KVStore)
|
||||
require.NotNil(t, kv)
|
||||
kv.Set(k, v)
|
||||
commitID := rs.Commit()
|
||||
require.Equal(t, int64(1), commitID.Version)
|
||||
}
|
||||
|
||||
func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) {
|
||||
rs := rootmulti.NewStore(db)
|
||||
rs.SetPruning(store.PruneNothing)
|
||||
key := sdk.NewKVStoreKey(storeKey)
|
||||
rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil)
|
||||
err := rs.LoadLatestVersion()
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, ver, rs.LastCommitID().Version)
|
||||
|
||||
// query data in substore
|
||||
kv, _ := rs.GetStore(key).(store.KVStore)
|
||||
|
||||
require.NotNil(t, kv)
|
||||
require.Equal(t, v, kv.Get(k))
|
||||
}
|
||||
|
||||
// Test that we can make commits and then reload old versions.
|
||||
// Test that LoadLatestVersion actually does.
|
||||
func TestSetLoader(t *testing.T) {
|
||||
// set a temporary home dir
|
||||
homeDir, cleanUp := tests.NewTestCaseDir(t)
|
||||
defer cleanUp()
|
||||
// TODO cleanup viper
|
||||
viper.Set(flags.FlagHome, homeDir)
|
||||
|
||||
upgradeInfoFilePath := filepath.Join(homeDir, "upgrade-info.json")
|
||||
upgradeInfo := &store.UpgradeInfo{
|
||||
Name: "test", Height: 0,
|
||||
}
|
||||
data, err := json.Marshal(upgradeInfo)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile(upgradeInfoFilePath, data, 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure it exists before running everything
|
||||
_, err = os.Stat(upgradeInfoFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := map[string]struct {
|
||||
setLoader func(*baseapp.BaseApp)
|
||||
origStoreKey string
|
||||
loadStoreKey string
|
||||
}{
|
||||
"don't set loader": {
|
||||
origStoreKey: "foo",
|
||||
loadStoreKey: "foo",
|
||||
},
|
||||
"rename with inline opts": {
|
||||
setLoader: useUpgradeLoader(0, &store.StoreUpgrades{
|
||||
Renamed: []store.StoreRename{{
|
||||
OldKey: "foo",
|
||||
NewKey: "bar",
|
||||
}},
|
||||
}),
|
||||
origStoreKey: "foo",
|
||||
loadStoreKey: "bar",
|
||||
},
|
||||
}
|
||||
|
||||
k := []byte("key")
|
||||
v := []byte("value")
|
||||
|
||||
for name, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// prepare a db with some data
|
||||
db := dbm.NewMemDB()
|
||||
|
||||
initStore(t, db, tc.origStoreKey, k, v)
|
||||
|
||||
// load the app with the existing db
|
||||
opts := []func(*baseapp.BaseApp){baseapp.SetPruning(store.PruneNothing)}
|
||||
if tc.setLoader != nil {
|
||||
opts = append(opts, tc.setLoader)
|
||||
}
|
||||
|
||||
app := baseapp.NewBaseApp(t.Name(), defaultLogger(), db, nil, opts...)
|
||||
capKey := sdk.NewKVStoreKey(baseapp.MainStoreKey)
|
||||
app.MountStores(capKey)
|
||||
app.MountStores(sdk.NewKVStoreKey(tc.loadStoreKey))
|
||||
err := app.LoadLatestVersion(capKey)
|
||||
require.Nil(t, err)
|
||||
|
||||
// "execute" one block
|
||||
app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: 2}})
|
||||
res := app.Commit()
|
||||
require.NotNil(t, res.Data)
|
||||
|
||||
// check db is properly updated
|
||||
checkStore(t, db, 2, tc.loadStoreKey, k, v)
|
||||
checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue