diff --git a/go.mod b/go.mod index dc9c8754..914f3d10 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/dymensionxyz/dymension-rdk go 1.22.4 require ( + cosmossdk.io/collections v0.4.0 + cosmossdk.io/core v0.12.0 cosmossdk.io/errors v1.0.1 cosmossdk.io/math v1.3.0 github.com/CosmWasm/wasmd v0.33.0 @@ -37,12 +39,14 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect + cosmossdk.io/api v0.7.0 // indirect + cosmossdk.io/depinject v1.0.0-alpha.4 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect github.com/CosmWasm/wasmvm v1.2.3 // indirect - github.com/DataDog/zstd v1.5.2 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/Jorropo/jsync v1.0.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/Workiva/go-datastructures v1.0.53 // indirect @@ -80,6 +84,7 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect + github.com/cosmos/cosmos-db v1.0.0 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect @@ -111,7 +116,7 @@ require ( github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/getsentry/sentry-go v0.23.0 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect diff --git a/go.sum b/go.sum index 77a168ad..c3055dbc 100644 --- a/go.sum +++ b/go.sum @@ -193,6 +193,14 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +cosmossdk.io/api v0.7.0 h1:QsEMIWuv9xWDbF2HZnW4Lpu1/SejCztPu0LQx7t6MN4= +cosmossdk.io/api v0.7.0/go.mod h1:kJFAEMLN57y0viszHDPLMmieF0471o5QAwwApa+270M= +cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= +cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= +cosmossdk.io/core v0.12.0 h1:aFuvkG6eDv0IQC+UDjx86wxNWVAxdCFk7OABJ1Vh4RU= +cosmossdk.io/core v0.12.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w= +cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc= +cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= @@ -227,8 +235,8 @@ github.com/CosmWasm/wasmvm v1.2.3/go.mod h1:vW/E3h8j9xBQs9bCoijDuawKo9kCtxOaS8N8 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= -github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -438,6 +446,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= +github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= +github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= github.com/cosmos/cosmos-sdk v0.46.16 h1:RVGv1+RulLZeNyfCaPZrZtv0kY7ZZNAI6JGpub0Uh6o= @@ -609,8 +619,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= -github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/getsentry/sentry-go v0.23.0 h1:dn+QRCeJv4pPt9OjVXiMcGIBIefaTJPw/h0bZWO05nE= +github.com/getsentry/sentry-go v0.23.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -2357,7 +2367,10 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/proto/timeupgrade/tx.proto b/proto/timeupgrade/tx.proto new file mode 100644 index 00000000..27587b28 --- /dev/null +++ b/proto/timeupgrade/tx.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package rollapp.timeupgrade.types; + +import "gogoproto/gogo.proto"; +import "cosmos/upgrade/v1beta1/tx.proto"; +import "cosmos/msg/v1/msg.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/any.proto"; + +option go_package = "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types"; + +// Msg defines the Msg service. +service Msg { + rpc SoftwareUpgrade(MsgSoftwareUpgrade) returns (MsgSoftwareUpgradeResponse); + rpc CancelUpgrade(MsgCancelUpgrade) returns (MsgCancelUpgradeResponse); +} + +message MsgSoftwareUpgrade { +// original_upgrade is the original software upgrade message. +cosmos.upgrade.v1beta1.MsgSoftwareUpgrade original_upgrade = 1; +// upgrade_time is the time at which the upgrade is scheduled. +google.protobuf.Timestamp upgrade_time = 2; +} + +message MsgSoftwareUpgradeResponse {} + +message MsgCancelUpgrade { + // authority is the address of the governance account. + string authority = 1; +} + +message MsgCancelUpgradeResponse {} \ No newline at end of file diff --git a/testutil/app/app.go b/testutil/app/app.go index bace3883..0fe6c340 100644 --- a/testutil/app/app.go +++ b/testutil/app/app.go @@ -95,6 +95,9 @@ import ( gaslesskeeper "github.com/dymensionxyz/dymension-rdk/x/gasless/keeper" gaslesstypes "github.com/dymensionxyz/dymension-rdk/x/gasless/types" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/keeper" + timeupgradetypes "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types" + ibctransfer "github.com/cosmos/ibc-go/v6/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v6/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" @@ -143,7 +146,7 @@ var kvstorekeys = []string{ ibchost.StoreKey, upgradetypes.StoreKey, epochstypes.StoreKey, hubgentypes.StoreKey, hubtypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey, gaslesstypes.StoreKey, wasmtypes.StoreKey, - rollappparamstypes.StoreKey, + rollappparamstypes.StoreKey, timeupgradetypes.StoreKey, } func getGovProposalHandlers() []govclient.ProposalHandler { @@ -231,6 +234,7 @@ type App struct { CapabilityKeeper *capabilitykeeper.Keeper StakingKeeper stakingkeeper.Keeper SequencersKeeper seqkeeper.Keeper + TimeUpgradeKeeper keeper.Keeper MintKeeper mintkeeper.Keeper EpochsKeeper epochskeeper.Keeper DistrKeeper distrkeeper.Keeper @@ -392,6 +396,12 @@ func NewRollapp( app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + app.TimeUpgradeKeeper = keeper.NewKeeper( + appCodec, + keys[timeupgradetypes.StoreKey], + authtypes.NewModuleAddress(timeupgradetypes.ModuleName).String(), + ) + // register the staking hooks // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks app.StakingKeeper = *stakingKeeper.SetHooks(app.DistrKeeper.Hooks()) @@ -559,7 +569,7 @@ func NewRollapp( mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, app.BankKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), - sequencers.NewAppModule(appCodec, app.SequencersKeeper), + sequencers.NewAppModule(app.SequencersKeeper), epochs.NewAppModule(appCodec, app.EpochsKeeper), params.NewAppModule(app.ParamsKeeper), wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), diff --git a/testutil/keepers/keepers.go b/testutil/keepers/keepers.go index 8d545206..9b0f76c6 100644 --- a/testutil/keepers/keepers.go +++ b/testutil/keepers/keepers.go @@ -3,6 +3,8 @@ package keepers import ( "time" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + sdk "github.com/cosmos/cosmos-sdk/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -14,6 +16,7 @@ import ( mintkeeper "github.com/dymensionxyz/dymension-rdk/x/mint/keeper" rollappparamskeeper "github.com/dymensionxyz/dymension-rdk/x/rollappparams/keeper" seqkeeper "github.com/dymensionxyz/dymension-rdk/x/sequencers/keeper" + timeupgradekeeper "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/keeper" ) func NewTestEpochKeeperFromApp(app *app.App) (*epochkeeper.Keeper, sdk.Context) { @@ -28,6 +31,18 @@ func NewTestSequencerKeeperFromApp(app *app.App) (*seqkeeper.Keeper, sdk.Context return k, ctx } +func NewTestTimeupgradeKeeperFromApp(app *app.App) (timeupgradekeeper.Keeper, sdk.Context) { + k := app.TimeUpgradeKeeper + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "rollapp-1", Time: time.Now().UTC()}) + return k, ctx +} + +func NewTestUpgradeKeeperFromApp(app *app.App) (upgradekeeper.Keeper, sdk.Context) { + k := app.UpgradeKeeper + ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "rollapp-1", Time: time.Now().UTC()}) + return k, ctx +} + func NewTestMintKeeperFromApp(app *app.App) (*mintkeeper.Keeper, sdk.Context) { k := &app.MintKeeper ctx := app.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "rollapp-1", Time: time.Now().UTC()}) diff --git a/utils/collcompat/addresses.go b/utils/collcompat/addresses.go new file mode 100644 index 00000000..6ba29101 --- /dev/null +++ b/utils/collcompat/addresses.go @@ -0,0 +1,213 @@ +// A copy of https://github.com/cosmos/cosmos-sdk/blob/v0.50.8/types/collections.go. + +package collcompat + +import ( + "fmt" + "time" + + "cosmossdk.io/collections" + collcodec "cosmossdk.io/collections/codec" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + // AccAddressKey follows the same semantics of collections.BytesKey. + // It just uses humanized format for the String() and EncodeJSON(). + AccAddressKey collcodec.KeyCodec[sdk.AccAddress] = genericAddressKey[sdk.AccAddress]{ + stringDecoder: sdk.AccAddressFromBech32, + keyType: "sdk.AccAddress", + } + + // ValAddressKey follows the same semantics as AccAddressKey. + ValAddressKey collcodec.KeyCodec[sdk.ValAddress] = genericAddressKey[sdk.ValAddress]{ + stringDecoder: sdk.ValAddressFromBech32, + keyType: "sdk.ValAddress", + } + + // ConsAddressKey follows the same semantics as ConsAddressKey. + ConsAddressKey collcodec.KeyCodec[sdk.ConsAddress] = genericAddressKey[sdk.ConsAddress]{ + stringDecoder: sdk.ConsAddressFromBech32, + keyType: "sdk.ConsAddress", + } + + // IntValue represents a collections.ValueCodec to work with Int. + IntValue collcodec.ValueCodec[math.Int] = intValueCodec{} + + // TimeKey represents a collections.KeyCodec to work with time.Time + // Deprecated: exists only for state compatibility reasons, should not + // be used for new storage keys using time. Please use the time KeyCodec + // provided in the collections package. + TimeKey collcodec.KeyCodec[time.Time] = timeKeyCodec{} +) + +type addressUnion interface { + sdk.AccAddress | sdk.ValAddress | sdk.ConsAddress + String() string +} + +type genericAddressKey[T addressUnion] struct { + stringDecoder func(string) (T, error) + keyType string +} + +func (a genericAddressKey[T]) Encode(buffer []byte, key T) (int, error) { + return collections.BytesKey.Encode(buffer, key) +} + +func (a genericAddressKey[T]) Decode(buffer []byte) (int, T, error) { + return collections.BytesKey.Decode(buffer) +} + +func (a genericAddressKey[T]) Size(key T) int { + return collections.BytesKey.Size(key) +} + +func (a genericAddressKey[T]) EncodeJSON(value T) ([]byte, error) { + return collections.StringKey.EncodeJSON(value.String()) +} + +func (a genericAddressKey[T]) DecodeJSON(b []byte) (v T, err error) { + s, err := collections.StringKey.DecodeJSON(b) + if err != nil { + return + } + v, err = a.stringDecoder(s) + return +} + +func (a genericAddressKey[T]) Stringify(key T) string { + return key.String() +} + +func (a genericAddressKey[T]) KeyType() string { + return a.keyType +} + +func (a genericAddressKey[T]) EncodeNonTerminal(buffer []byte, key T) (int, error) { + return collections.BytesKey.EncodeNonTerminal(buffer, key) +} + +func (a genericAddressKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { + return collections.BytesKey.DecodeNonTerminal(buffer) +} + +func (a genericAddressKey[T]) SizeNonTerminal(key T) int { + return collections.BytesKey.SizeNonTerminal(key) +} + +// Deprecated: lengthPrefixedAddressKey is a special key codec used to retain state backwards compatibility +// when a generic address key (be: AccAddress, ValAddress, ConsAddress), is used as an index key. +// More docs can be found in the LengthPrefixedAddressKey function. +type lengthPrefixedAddressKey[T addressUnion] struct { + collcodec.KeyCodec[T] +} + +func (g lengthPrefixedAddressKey[T]) Encode(buffer []byte, key T) (int, error) { + return g.EncodeNonTerminal(buffer, key) +} + +func (g lengthPrefixedAddressKey[T]) Decode(buffer []byte) (int, T, error) { + return g.DecodeNonTerminal(buffer) +} + +func (g lengthPrefixedAddressKey[T]) Size(key T) int { return g.SizeNonTerminal(key) } + +func (g lengthPrefixedAddressKey[T]) KeyType() string { return "index_key/" + g.KeyCodec.KeyType() } + +// Deprecated: LengthPrefixedAddressKey implements an SDK backwards compatible indexing key encoder +// for addresses. +// The status quo in the SDK is that address keys are length prefixed even when they're the +// last part of a composite key. This should never be used unless to retain state compatibility. +// For example, a composite key composed of `[string, address]` in theory would need you only to +// define a way to understand when the string part finishes, we usually do this by appending a null +// byte to the string, then when you know when the string part finishes, it's logical that the +// part which remains is the address key. In the SDK instead we prepend to the address key its +// length too. +func LengthPrefixedAddressKey[T addressUnion](keyCodec collcodec.KeyCodec[T]) collcodec.KeyCodec[T] { + return lengthPrefixedAddressKey[T]{ + keyCodec, + } +} + +// Collection Codecs + +type intValueCodec struct{} + +func (i intValueCodec) Encode(value math.Int) ([]byte, error) { + return value.Marshal() +} + +func (i intValueCodec) Decode(b []byte) (math.Int, error) { + v := new(math.Int) + err := v.Unmarshal(b) + if err != nil { + return math.Int{}, err + } + return *v, nil +} + +func (i intValueCodec) EncodeJSON(value math.Int) ([]byte, error) { + return value.MarshalJSON() +} + +func (i intValueCodec) DecodeJSON(b []byte) (math.Int, error) { + v := new(math.Int) + err := v.UnmarshalJSON(b) + if err != nil { + return math.Int{}, err + } + return *v, nil +} + +func (i intValueCodec) Stringify(value math.Int) string { + return value.String() +} + +func (i intValueCodec) ValueType() string { + return "math.Int" +} + +type timeKeyCodec struct{} + +func (timeKeyCodec) Encode(buffer []byte, key time.Time) (int, error) { + return copy(buffer, sdk.FormatTimeBytes(key)), nil +} + +var timeSize = len(sdk.FormatTimeBytes(time.Time{})) + +func (timeKeyCodec) Decode(buffer []byte) (int, time.Time, error) { + if len(buffer) != timeSize { + return 0, time.Time{}, fmt.Errorf("invalid time buffer buffer size") + } + t, err := sdk.ParseTimeBytes(buffer) + if err != nil { + return 0, time.Time{}, err + } + return timeSize, t, nil +} + +func (timeKeyCodec) Size(key time.Time) int { return timeSize } + +func (timeKeyCodec) EncodeJSON(value time.Time) ([]byte, error) { return value.MarshalJSON() } + +func (timeKeyCodec) DecodeJSON(b []byte) (time.Time, error) { + t := time.Time{} + err := t.UnmarshalJSON(b) + return t, err +} + +func (timeKeyCodec) Stringify(key time.Time) string { return key.String() } +func (timeKeyCodec) KeyType() string { return "sdk/time.Time" } +func (t timeKeyCodec) EncodeNonTerminal(buffer []byte, key time.Time) (int, error) { + return t.Encode(buffer, key) +} + +func (t timeKeyCodec) DecodeNonTerminal(buffer []byte) (int, time.Time, error) { + if len(buffer) < timeSize { + return 0, time.Time{}, fmt.Errorf("invalid time buffer size, wanted: %d at least, got: %d", timeSize, len(buffer)) + } + return t.Decode(buffer[:timeSize]) +} +func (t timeKeyCodec) SizeNonTerminal(key time.Time) int { return t.Size(key) } diff --git a/utils/collcompat/addresses_test.go b/utils/collcompat/addresses_test.go new file mode 100644 index 00000000..09673a00 --- /dev/null +++ b/utils/collcompat/addresses_test.go @@ -0,0 +1,33 @@ +// A copy of https://github.com/cosmos/cosmos-sdk/blob/v0.50.8/types/collections_test.go. + +package collcompat + +import ( + "testing" + "time" + + "cosmossdk.io/collections/colltest" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestCollectionsCorrectness(t *testing.T) { + t.Run("AccAddress", func(t *testing.T) { + colltest.TestKeyCodec(t, AccAddressKey, sdk.AccAddress{0x0, 0x2, 0x3, 0x5}) + }) + + t.Run("ValAddress", func(t *testing.T) { + colltest.TestKeyCodec(t, ValAddressKey, sdk.ValAddress{0x1, 0x3, 0x4}) + }) + + t.Run("ConsAddress", func(t *testing.T) { + colltest.TestKeyCodec(t, ConsAddressKey, sdk.ConsAddress{0x32, 0x0, 0x0, 0x3}) + }) + + t.Run("AddressIndexingKey", func(t *testing.T) { + colltest.TestKeyCodec(t, LengthPrefixedAddressKey(AccAddressKey), sdk.AccAddress{0x2, 0x5, 0x8}) + }) + + t.Run("Time", func(t *testing.T) { + colltest.TestKeyCodec(t, TimeKey, time.Time{}) + }) +} diff --git a/utils/collcompat/collcompat.go b/utils/collcompat/collcompat.go new file mode 100644 index 00000000..36216015 --- /dev/null +++ b/utils/collcompat/collcompat.go @@ -0,0 +1,116 @@ +// A copy-paste file to integrate collections in Cosmos SDK v0.47.x. + +package collcompat + +import ( + "context" + + collcodec "cosmossdk.io/collections/codec" + "cosmossdk.io/core/store" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/gogo/protobuf/proto" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func NewKVStoreService(storeKey storetypes.StoreKey) store.KVStoreService { + return &kvStoreService{key: storeKey} +} + +type kvStoreService struct { + key storetypes.StoreKey +} + +func (k kvStoreService) OpenKVStore(ctx context.Context) store.KVStore { + return newKVStore(sdk.UnwrapSDKContext(ctx).KVStore(k.key)) +} + +// CoreKVStore is a wrapper of Core/Store kvstore interface +// Remove after https://github.com/cosmos/cosmos-sdk/issues/14714 is closed +type coreKVStore struct { + kvStore storetypes.KVStore +} + +// NewKVStore returns a wrapper of Core/Store kvstore interface +// Remove once store migrates to core/store kvstore interface +func newKVStore(store storetypes.KVStore) store.KVStore { + return coreKVStore{kvStore: store} +} + +// Get returns nil iff key doesn't exist. Errors on nil key. +func (store coreKVStore) Get(key []byte) ([]byte, error) { + return store.kvStore.Get(key), nil +} + +// Has checks if a key exists. Errors on nil key. +func (store coreKVStore) Has(key []byte) (bool, error) { + return store.kvStore.Has(key), nil +} + +// Set sets the key. Errors on nil key or value. +func (store coreKVStore) Set(key, value []byte) error { + store.kvStore.Set(key, value) + return nil +} + +// Delete deletes the key. Errors on nil key. +func (store coreKVStore) Delete(key []byte) error { + store.kvStore.Delete(key) + return nil +} + +// Iterator iterates over a domain of keys in ascending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// To iterate over entire domain, use store.Iterator(nil, nil) +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (store coreKVStore) Iterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.Iterator(start, end), nil +} + +func (store coreKVStore) ReverseIterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.ReverseIterator(start, end), nil +} + +type protoMessage[T any] interface { + *T + codec.ProtoMarshaler +} + +// ProtoValue inits a collections.ValueCodec for a generic gogo protobuf message. +func ProtoValue[T any, PT protoMessage[T]](cdc codec.BinaryCodec) collcodec.ValueCodec[T] { + return &collValue[T, PT]{cdc.(codec.Codec), proto.MessageName(PT(new(T)))} +} + +type collValue[T any, PT protoMessage[T]] struct { + cdc codec.Codec + messageName string +} + +func (c collValue[T, PT]) Encode(value T) ([]byte, error) { + return c.cdc.Marshal(PT(&value)) +} + +func (c collValue[T, PT]) Decode(b []byte) (value T, err error) { + err = c.cdc.Unmarshal(b, PT(&value)) + return value, err +} + +func (c collValue[T, PT]) EncodeJSON(value T) ([]byte, error) { + return c.cdc.MarshalJSON(PT(&value)) +} + +func (c collValue[T, PT]) DecodeJSON(b []byte) (value T, err error) { + err = c.cdc.UnmarshalJSON(b, PT(&value)) + return +} + +func (c collValue[T, PT]) Stringify(value T) string { + return PT(&value).String() +} + +func (c collValue[T, PT]) ValueType() string { + return "github.com/cosmos/gogoproto/" + c.messageName +} diff --git a/x/sequencers/module.go b/x/sequencers/module.go index a168c0dd..0e2e9c39 100644 --- a/x/sequencers/module.go +++ b/x/sequencers/module.go @@ -91,7 +91,6 @@ type AppModule struct { } func NewAppModule( - cdc codec.Codec, keeper keeper.Keeper, ) AppModule { return AppModule{ diff --git a/x/timeupgrade/README.md b/x/timeupgrade/README.md new file mode 100644 index 00000000..b5eac21c --- /dev/null +++ b/x/timeupgrade/README.md @@ -0,0 +1,69 @@ +# TimeUpgrade Module + +The TimeUpgrade module is an extension for executing governance proposals based on time rather than block height. It enhances the functionality of the original upgrade module by allowing proposers to specify a future date and time for the upgrade to take effect. + +## Overview + +The TimeUpgrade module wraps the original upgrade module's message with a user-defined timestamp. Once a proposal passes, it is stored in the state, waiting for the specified time to arrive. When the BlockTime exceeds the defined timestamp, the original proposal is submitted to be executed in the next block through the original governance module. + +## Usage + +To use the TimeUpgrade module, follow these steps: + +1. Create a proposal JSON file (e.g., `proposal.json`) with the following structure: + + ```json + { + "title": "Update Dymension to v0.2.1", + "description": "Upgrade Dymension to version 0.2.1 with scheduled upgrade time", + "summary": "This proposal aims to upgrade the Dymension network to version 0.2.1, implementing new features and improvements, with a scheduled upgrade time.", + "messages": [ + { + "@type": "/rollapp.timeupgrade.types.MsgSoftwareUpgrade", + "original_upgrade": { + "authority": "ethm10d07y265gmmuvt4z0w9aw880jnsr700jpva843", + "plan": { + "name": "v0.2.1", + "time": "0001-01-01T00:00:00Z", + "height": "1800", + "info": "{}", + "upgraded_client_state": null + } + }, + "upgrade_time": "2024-09-06T18:10:00Z" + } + ], + "deposit": "500arax", + "expedited": true + } + ``` + +2. Submit the proposal using the following command: + + ```bash + rollapp-evm tx gov submit-proposal proposal.json --from rol-user --keyring-backend test --fees 2000000000000arax + ``` + +3. Deposit tokens for the proposal: + + ```bash + rollapp-evm tx gov deposit 1 10000000arax --from rol-user --keyring-backend test --fees 2000000000000arax + ``` + +4. Vote on the proposal: + + ```bash + rollapp-evm tx gov vote 1 yes --from rol-user --keyring-backend test --fees 2000000000000arax + ``` + +## Key Features + +- Time-based upgrades: Specify a future date and time for upgrades to take effect. +- Compatibility: Works seamlessly with the existing governance and upgrade modules. +- Flexibility: Allows for better planning and coordination of network upgrades. + +## Note + +Ensure that the `upgrade_time` in the proposal JSON is set to a future date and time in the UTC format (e.g., "2024-09-06T18:10:00Z"). + +For more information on the TimeUpgrade module and its integration with the Dymension network, please refer to the official documentation or contact the development team. diff --git a/x/timeupgrade/abci.go b/x/timeupgrade/abci.go new file mode 100644 index 00000000..6429d94b --- /dev/null +++ b/x/timeupgrade/abci.go @@ -0,0 +1,56 @@ +package timeupgrade + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/keeper" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types" +) + +func BeginBlocker(ctx sdk.Context, k keeper.Keeper, upgradeKeeper upgradekeeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, ctx.BlockTime(), telemetry.MetricKeyBeginBlocker) + + upgradeTimeTimestamp, err := k.GetUpgradeTime(ctx) + if err != nil { + err = k.CleanTimeUpgrade(ctx) + if err != nil { + panic(fmt.Errorf("failed to clean time upgrade: %w", err)) + } + return + } + + if ctx.BlockTime().After(upgradeTimeTimestamp) { + err = setPlanToNextBlock(ctx, k, upgradeKeeper) + if err != nil { + err = k.CleanTimeUpgrade(ctx) + if err != nil { + panic(fmt.Errorf("failed to clean time upgrade: %w", err)) + } + return + } + } +} + +// setPlanToNextBlock sets the upgrade plan to the next block and schedules the upgrade +func setPlanToNextBlock(ctx sdk.Context, k keeper.Keeper, upgradeKeeper upgradekeeper.Keeper) error { + plan, err := k.UpgradePlan.Get(ctx) + if err != nil { + return err + } + + plan.Height = ctx.BlockHeight() + 1 + err = upgradeKeeper.ScheduleUpgrade(ctx, plan) + if err != nil { + return err + } + + err = k.CleanTimeUpgrade(ctx) + if err != nil { + return err + } + + return nil +} diff --git a/x/timeupgrade/abci_test.go b/x/timeupgrade/abci_test.go new file mode 100644 index 00000000..b7e6d139 --- /dev/null +++ b/x/timeupgrade/abci_test.go @@ -0,0 +1,153 @@ +package timeupgrade_test + +import ( + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + testkeepers "github.com/dymensionxyz/dymension-rdk/testutil/keepers" + "github.com/dymensionxyz/dymension-rdk/testutil/utils" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade" + "github.com/gogo/protobuf/types" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +func TestBeginBlocker_Errors(t *testing.T) { + app := utils.Setup(t, false) + tUpgradeKeeper, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + upgradeKeeper, _ := testkeepers.NewTestUpgradeKeeperFromApp(app) + + t.Run("UpgradeTime not found", func(t *testing.T) { + tUpgradeKeeper.UpgradeTime.Remove(ctx) + + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + _, err := tUpgradeKeeper.UpgradeTime.Get(ctx) + require.Error(t, err, "UpgradeTime should not exist") + + // Verify that no upgrade was added to the upgradeKeeper + plan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.False(t, exists, "No upgrade plan should exist in upgradeKeeper") + require.Empty(t, plan) + }) + + t.Run("UpgradeTime parsing error", func(t *testing.T) { + invalidTime := types.Timestamp{Seconds: -1, Nanos: -1} + err := tUpgradeKeeper.UpgradeTime.Set(ctx, invalidTime) + require.NoError(t, err) + + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + _, err = tUpgradeKeeper.UpgradeTime.Get(ctx) + require.Error(t, err, "UpgradeTime should have been removed") + + // Verify that no upgrade was added to the upgradeKeeper + plan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.False(t, exists, "No upgrade plan should exist in upgradeKeeper") + require.Empty(t, plan) + }) + + t.Run("UpgradePlan not found", func(t *testing.T) { + pastTime := types.Timestamp{Seconds: time.Now().Add(-1 * time.Hour).Unix()} + err := tUpgradeKeeper.UpgradeTime.Set(ctx, pastTime) + require.NoError(t, err) + tUpgradeKeeper.UpgradePlan.Remove(ctx) + + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + _, err = tUpgradeKeeper.UpgradeTime.Get(ctx) + require.Error(t, err, "UpgradeTime should have been removed") + _, err = tUpgradeKeeper.UpgradePlan.Get(ctx) + require.Error(t, err, "UpgradePlan should not exist") + + // Verify that no upgrade was added to the upgradeKeeper + plan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.False(t, exists, "No upgrade plan should exist in upgradeKeeper") + require.Empty(t, plan) + }) + + t.Run("ScheduleUpgrade error", func(t *testing.T) { + pastTime := types.Timestamp{Seconds: time.Now().Add(-1 * time.Hour).Unix()} + err := tUpgradeKeeper.UpgradeTime.Set(ctx, pastTime) + require.NoError(t, err) + + invalidPlan := upgradetypes.Plan{Name: "", Height: -1} // Invalid plan + err = tUpgradeKeeper.UpgradePlan.Set(ctx, invalidPlan) + require.NoError(t, err) + + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + _, err = tUpgradeKeeper.UpgradeTime.Get(ctx) + require.Error(t, err, "UpgradeTime should have been removed") + _, err = tUpgradeKeeper.UpgradePlan.Get(ctx) + require.Error(t, err, "UpgradePlan should have been removed") + + // Verify that no upgrade was added to the upgradeKeeper + plan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.False(t, exists, "No upgrade plan should exist in upgradeKeeper") + require.Empty(t, plan) + }) + + t.Run("Upgrade not scheduled (future time)", func(t *testing.T) { + futureTime := types.Timestamp{Seconds: time.Now().Add(1 * time.Hour).Unix()} + err := tUpgradeKeeper.UpgradeTime.Set(ctx, futureTime) + require.NoError(t, err) + + plan := upgradetypes.Plan{Name: "test", Height: 100} + err = tUpgradeKeeper.UpgradePlan.Set(ctx, plan) + require.NoError(t, err) + + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + _, err = tUpgradeKeeper.UpgradeTime.Get(ctx) + require.NoError(t, err, "UpgradeTime should not have been removed") + _, err = tUpgradeKeeper.UpgradePlan.Get(ctx) + require.NoError(t, err, "UpgradePlan should not have been removed") + + // Verify that no upgrade was added to the upgradeKeeper + upgradePlan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.False(t, exists, "No upgrade plan should exist in upgradeKeeper") + require.Empty(t, upgradePlan) + }) +} + +func TestBeginBlocker_HappyPath(t *testing.T) { + app := utils.Setup(t, false) + tUpgradeKeeper, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + upgradeKeeper, _ := testkeepers.NewTestUpgradeKeeperFromApp(app) + + // Set up a past upgrade time + pastTime := types.Timestamp{Seconds: time.Now().Add(-1 * time.Hour).Unix()} + err := tUpgradeKeeper.UpgradeTime.Set(ctx, pastTime) + require.NoError(t, err) + + // Set up a valid upgrade plan + initialPlan := upgradetypes.Plan{ + Name: "test_upgrade", + Height: 1000, // This height will be overwritten by BeginBlocker + Info: "Test upgrade", + } + err = tUpgradeKeeper.UpgradePlan.Set(ctx, initialPlan) + require.NoError(t, err) + + // Set current block height + currentHeight := int64(500) + ctx = ctx.WithBlockHeight(currentHeight) + + // Run BeginBlocker + timeupgrade.BeginBlocker(ctx, tUpgradeKeeper, upgradeKeeper) + + // Verify that UpgradeTime has been removed + _, err = tUpgradeKeeper.UpgradeTime.Get(ctx) + require.Error(t, err, "UpgradeTime should have been removed") + + // Verify that UpgradePlan has been removed + _, err = tUpgradeKeeper.UpgradePlan.Get(ctx) + require.Error(t, err, "UpgradePlan should have been removed") + + // Verify that the upgrade has been scheduled correctly in the upgradeKeeper + scheduledPlan, exists := upgradeKeeper.GetUpgradePlan(ctx) + require.True(t, exists, "An upgrade plan should exist in upgradeKeeper") + require.Equal(t, initialPlan.Name, scheduledPlan.Name) + require.Equal(t, currentHeight+1, scheduledPlan.Height, "The plan height should be set to the current height + 1") + require.Equal(t, initialPlan.Info, scheduledPlan.Info) +} diff --git a/x/timeupgrade/keeper/keeper.go b/x/timeupgrade/keeper/keeper.go new file mode 100644 index 00000000..1adffde6 --- /dev/null +++ b/x/timeupgrade/keeper/keeper.go @@ -0,0 +1,78 @@ +package keeper + +import ( + "fmt" + "time" + + "cosmossdk.io/collections" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + prototypes "github.com/gogo/protobuf/types" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/libs/log" + + "github.com/dymensionxyz/dymension-rdk/utils/collcompat" + "github.com/dymensionxyz/dymension-rdk/x/sequencers/types" +) + +type Keeper struct { + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + authority string + + UpgradePlan collections.Item[upgradetypes.Plan] + UpgradeTime collections.Item[prototypes.Timestamp] +} + +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + authority string, +) Keeper { + service := collcompat.NewKVStoreService(storeKey) + sb := collections.NewSchemaBuilder(service) + + return Keeper{ + cdc: cdc, + storeKey: storeKey, + authority: authority, + + UpgradePlan: collections.NewItem[upgradetypes.Plan](sb, collections.NewPrefix(0), "plan", collcompat.ProtoValue[upgradetypes.Plan](cdc)), + UpgradeTime: collections.NewItem[prototypes.Timestamp](sb, collections.NewPrefix(1), "time", collcompat.ProtoValue[prototypes.Timestamp](cdc)), + } +} + +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// GetUpgradeTime gets the upgrade time from the store +func (k Keeper) GetUpgradeTime(ctx sdk.Context) (time.Time, error) { + upgradeTime, err := k.UpgradeTime.Get(ctx) + if err != nil { + return time.Time{}, err + } + + upgradeTimeTimestamp, err := prototypes.TimestampFromProto(&upgradeTime) + if err != nil { + return time.Time{}, fmt.Errorf("failed to parse upgrade time: %w", err) + } + + return upgradeTimeTimestamp, nil +} + +// CleanTimeUpgrade removes the upgrade time and plan from the store +func (k Keeper) CleanTimeUpgrade(ctx sdk.Context) error { + err := k.UpgradeTime.Remove(ctx) + if err != nil { + return err + } + + err = k.UpgradePlan.Remove(ctx) + if err != nil { + return err + } + return nil +} diff --git a/x/timeupgrade/keeper/msg_server.go b/x/timeupgrade/keeper/msg_server.go new file mode 100644 index 00000000..1f50ce97 --- /dev/null +++ b/x/timeupgrade/keeper/msg_server.go @@ -0,0 +1,80 @@ +package keeper + +import ( + "context" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + prototypes "github.com/gogo/protobuf/types" + + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types" +) + +var _ types.MsgServer = msgServer{} + +type msgServer struct { + Keeper +} + +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +func (m msgServer) SoftwareUpgrade(ctx context.Context, req *types.MsgSoftwareUpgrade) (*types.MsgSoftwareUpgradeResponse, error) { + sdkCtx := sdk.UnwrapSDKContext(ctx) + + err := req.OriginalUpgrade.ValidateBasic() + if err != nil { + return nil, err + } + + if m.authority != req.OriginalUpgrade.Authority { + return nil, govtypes.ErrInvalidSigner + } + + upgradeTimeTimestamp, err := prototypes.TimestampFromProto(req.UpgradeTime) + if err != nil { + return nil, fmt.Errorf("failed to parse upgrade time: %w", err) + } + + if upgradeTimeTimestamp.Before(sdkCtx.BlockTime()) { + return nil, fmt.Errorf("upgrade time must be in the future: upgrade time %s, current time %s", upgradeTimeTimestamp, sdkCtx.BlockTime()) + } + + err = m.Keeper.UpgradePlan.Set(sdkCtx, req.OriginalUpgrade.Plan) + if err != nil { + return nil, err + } + + err = m.Keeper.UpgradeTime.Set(sdkCtx, *req.UpgradeTime) + if err != nil { + return nil, err + } + + return nil, nil +} + +func (m msgServer) CancelUpgrade(ctx context.Context, req *types.MsgCancelUpgrade) (*types.MsgCancelUpgradeResponse, error) { + err := req.ValidateBasic() + if err != nil { + return nil, err + } + + if m.authority != req.Authority { + return nil, govtypes.ErrInvalidSigner + } + + sdkCtx := sdk.UnwrapSDKContext(ctx) + err = m.Keeper.UpgradePlan.Remove(sdkCtx) + if err != nil { + return nil, err + } + + err = m.Keeper.UpgradeTime.Remove(sdkCtx) + if err != nil { + return nil, err + } + + return nil, nil +} diff --git a/x/timeupgrade/keeper/msg_server_test.go b/x/timeupgrade/keeper/msg_server_test.go new file mode 100644 index 00000000..02fb4e30 --- /dev/null +++ b/x/timeupgrade/keeper/msg_server_test.go @@ -0,0 +1,214 @@ +package keeper_test + +import ( + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + types2 "github.com/cosmos/cosmos-sdk/x/upgrade/types" + testkeepers "github.com/dymensionxyz/dymension-rdk/testutil/keepers" + "github.com/dymensionxyz/dymension-rdk/testutil/utils" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/keeper" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types" + types3 "github.com/gogo/protobuf/types" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +func TestMsgServer_SoftwareUpgrade_Errors(t *testing.T) { + app := utils.Setup(t, false) + k, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + msgServer := keeper.NewMsgServerImpl(k) + + govAuthorityAccount := authtypes.NewModuleAddress(types.ModuleName).String() + otherAddress := authtypes.NewModuleAddress("otherModuleAddress").String() + + timeNow := time.Now() + oneHourBefore := timeNow.Add(-time.Hour) + oneHourBeforeTimestamp, err := types3.TimestampProto(oneHourBefore) + require.NoError(t, err) + + ctx = ctx.WithBlockTime(timeNow) + + testCases := []struct { + name string + request *types.MsgSoftwareUpgrade + expectedErrMsg string + }{ + { + name: "validate basic original upgrade: notvalidated", + request: &types.MsgSoftwareUpgrade{ + OriginalUpgrade: &types2.MsgSoftwareUpgrade{ + Authority: "adkfjlakd", + Plan: types2.Plan{ + Name: "someName", + Height: 1, + Info: "", + }, + }, + }, + expectedErrMsg: "decoding bech32 failed", + }, + { + name: "only authority account can upgrade", + request: &types.MsgSoftwareUpgrade{ + UpgradeTime: oneHourBeforeTimestamp, + OriginalUpgrade: &types2.MsgSoftwareUpgrade{ + Authority: otherAddress, + Plan: types2.Plan{ + Name: "someName", + Height: 1, + Info: "", + }, + }, + }, + expectedErrMsg: "expected gov account as only signer for proposal message", + }, + { + name: "upgrade time in the past", + request: &types.MsgSoftwareUpgrade{ + UpgradeTime: oneHourBeforeTimestamp, + OriginalUpgrade: &types2.MsgSoftwareUpgrade{ + Authority: govAuthorityAccount, + Plan: types2.Plan{ + Name: "someName", + Height: 1, + Info: "", + }, + }, + }, + expectedErrMsg: "upgrade time must be in the future", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := msgServer.SoftwareUpgrade(ctx, tc.request) + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErrMsg) + }) + } +} + +func TestMsgServer_SoftwareUpgrade(t *testing.T) { + app := utils.Setup(t, false) + k, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + msgServer := keeper.NewMsgServerImpl(k) + + govAuthorityAccount := authtypes.NewModuleAddress(types.ModuleName).String() + + timeNow := time.Now() + timeNowTimestamp, err := types3.TimestampProto(timeNow) + require.NoError(t, err) + + ctx = ctx.WithBlockTime(timeNow) + + plan := types2.Plan{ + Name: "someName", + Height: 1, + Info: "", + } + + _, err = msgServer.SoftwareUpgrade(ctx, &types.MsgSoftwareUpgrade{ + UpgradeTime: timeNowTimestamp, + OriginalUpgrade: &types2.MsgSoftwareUpgrade{ + Authority: govAuthorityAccount, + Plan: plan, + }, + }) + require.NoError(t, err) + + // Retrieve the saved plan from the keeper + savedPlan, err := k.UpgradePlan.Get(ctx) + require.NoError(t, err) + require.Equal(t, plan, savedPlan) + + // Retrieve the saved upgrade time from the keeper + savedTime, err := k.UpgradeTime.Get(ctx) + require.NoError(t, err) + require.Equal(t, timeNowTimestamp, &savedTime) +} + +func TestMsgServer_CancelUpgrade_Errors(t *testing.T) { + app := utils.Setup(t, false) + k, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + msgServer := keeper.NewMsgServerImpl(k) + + govAuthorityAccount := authtypes.NewModuleAddress(types.ModuleName).String() + otherAddress := authtypes.NewModuleAddress("otherModuleAddress").String() + + testCases := []struct { + name string + authority string + expectedErrMsg string + }{ + { + name: "invalid authority address", + authority: "invalidAddress", + expectedErrMsg: "decoding bech32 failed", + }, + { + name: "non-authority account", + authority: otherAddress, + expectedErrMsg: "expected gov account as only signer for proposal message", + }, + { + name: "valid authority address", + authority: govAuthorityAccount, + expectedErrMsg: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := msgServer.CancelUpgrade(ctx, &types.MsgCancelUpgrade{ + Authority: tc.authority, + }) + if tc.expectedErrMsg == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErrMsg) + } + }) + } +} + +func TestMsgServer_CancelUpgrade_HappyPath(t *testing.T) { + app := utils.Setup(t, false) + k, ctx := testkeepers.NewTestTimeupgradeKeeperFromApp(app) + msgServer := keeper.NewMsgServerImpl(k) + + govAuthorityAccount := authtypes.NewModuleAddress(types.ModuleName).String() + + // Set the upgrade plan and time + err := k.UpgradePlan.Set(ctx, types2.Plan{ + Name: "someName", + Height: 1, + Info: "", + }) + require.NoError(t, err) + + err = k.UpgradeTime.Set(ctx, types3.Timestamp{ + Seconds: 1, + }) + require.NoError(t, err) + + // Validate that the upgrade plan and time exist in the state + _, err = k.UpgradePlan.Get(ctx) + require.NoError(t, err) + + _, err = k.UpgradeTime.Get(ctx) + require.NoError(t, err) + + // Call CancelUpgrade + _, err = msgServer.CancelUpgrade(ctx, &types.MsgCancelUpgrade{ + Authority: govAuthorityAccount, + }) + require.NoError(t, err) + + // Validate that the upgrade plan and time have been deleted from the state + _, err = k.UpgradePlan.Get(ctx) + require.Error(t, err) + + _, err = k.UpgradeTime.Get(ctx) + require.Error(t, err) +} diff --git a/x/timeupgrade/module.go b/x/timeupgrade/module.go new file mode 100644 index 00000000..4b226dda --- /dev/null +++ b/x/timeupgrade/module.go @@ -0,0 +1,104 @@ +package timeupgrade + +import ( + "encoding/json" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/keeper" + "github.com/dymensionxyz/dymension-rdk/x/timeupgrade/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.BeginBlockAppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +type AppModuleBasic struct { +} + +func (a AppModuleBasic) Name() string { + return types.ModuleName +} + +func (a AppModuleBasic) RegisterLegacyAminoCodec(amino *codec.LegacyAmino) { + types.RegisterCodec(amino) +} + +func (a AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +func (a AppModuleBasic) DefaultGenesis(codec codec.JSONCodec) json.RawMessage { + return nil +} + +func (a AppModuleBasic) ValidateGenesis(codec codec.JSONCodec, config client.TxEncodingConfig, message json.RawMessage) error { + return nil +} + +func (a AppModuleBasic) RegisterGRPCGatewayRoutes(context client.Context, mux *runtime.ServeMux) {} + +func (a AppModuleBasic) GetTxCmd() *cobra.Command { + return nil +} + +func (a AppModuleBasic) GetQueryCmd() *cobra.Command { + return nil +} + +type AppModule struct { + AppModuleBasic + + keeper keeper.Keeper + upgradeKeeper upgradekeeper.Keeper +} + +func NewAppModule(keeper keeper.Keeper, upgradeKeeper upgradekeeper.Keeper) *AppModule { + return &AppModule{keeper: keeper, upgradeKeeper: upgradeKeeper} +} + +func (a AppModule) BeginBlock(context sdk.Context, block abci.RequestBeginBlock) { + BeginBlocker(context, a.keeper, a.upgradeKeeper) +} + +func (a AppModule) InitGenesis(context sdk.Context, jsonCodec codec.JSONCodec, message json.RawMessage) []abci.ValidatorUpdate { + return nil +} + +func (a AppModule) ExportGenesis(context sdk.Context, jsonCodec codec.JSONCodec) json.RawMessage { + return nil +} + +func (a AppModule) RegisterInvariants(registry sdk.InvariantRegistry) { +} + +func (a AppModule) Route() sdk.Route { + return sdk.NewRoute(types.RouterKey, nil) +} + +func (a AppModule) QuerierRoute() string { + return types.RouterKey +} + +func (a AppModule) LegacyQuerierHandler(amino *codec.LegacyAmino) sdk.Querier { + return nil +} + +func (a AppModule) RegisterServices(configurator module.Configurator) { + types.RegisterMsgServer(configurator.MsgServer(), keeper.NewMsgServerImpl(a.keeper)) +} + +func (a AppModule) ConsensusVersion() uint64 { + return 1 +} diff --git a/x/timeupgrade/types/codec.go b/x/timeupgrade/types/codec.go new file mode 100644 index 00000000..7e303b61 --- /dev/null +++ b/x/timeupgrade/types/codec.go @@ -0,0 +1,21 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" +) + +func RegisterCodec(cdc *codec.LegacyAmino) { +} + +func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgSoftwareUpgrade{}, + &MsgCancelUpgrade{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} diff --git a/x/timeupgrade/types/keys.go b/x/timeupgrade/types/keys.go new file mode 100644 index 00000000..0884481a --- /dev/null +++ b/x/timeupgrade/types/keys.go @@ -0,0 +1,11 @@ +package types + +const ( + // ModuleName defines the module name + ModuleName = "timeupgrade" + + StoreKey = ModuleName + + // RouterKey defines the module's message routing key + RouterKey = ModuleName +) diff --git a/x/timeupgrade/types/msgs.go b/x/timeupgrade/types/msgs.go new file mode 100644 index 00000000..3ff75cca --- /dev/null +++ b/x/timeupgrade/types/msgs.go @@ -0,0 +1,30 @@ +package types + +import ( + sdkerrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _, _ sdk.Msg = &MsgSoftwareUpgrade{}, &MsgCancelUpgrade{} + +func (m *MsgSoftwareUpgrade) ValidateBasic() error { + return m.OriginalUpgrade.ValidateBasic() +} + +func (m *MsgSoftwareUpgrade) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.GetOriginalUpgrade().Authority) + return []sdk.AccAddress{addr} +} + +func (m *MsgCancelUpgrade) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil { + return sdkerrors.Wrap(err, "authority") + } + + return nil +} + +func (m *MsgCancelUpgrade) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Authority) + return []sdk.AccAddress{addr} +} diff --git a/x/timeupgrade/types/tx.pb.go b/x/timeupgrade/types/tx.pb.go new file mode 100644 index 00000000..15ac1a6c --- /dev/null +++ b/x/timeupgrade/types/tx.pb.go @@ -0,0 +1,935 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: timeupgrade/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/codec/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + types "github.com/cosmos/cosmos-sdk/x/upgrade/types" + _ "github.com/gogo/protobuf/gogoproto" + grpc1 "github.com/gogo/protobuf/grpc" + proto "github.com/gogo/protobuf/proto" + types1 "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type MsgSoftwareUpgrade struct { + // original_upgrade is the original software upgrade message. + OriginalUpgrade *types.MsgSoftwareUpgrade `protobuf:"bytes,1,opt,name=original_upgrade,json=originalUpgrade,proto3" json:"original_upgrade,omitempty"` + // upgrade_time is the time at which the upgrade is scheduled. + UpgradeTime *types1.Timestamp `protobuf:"bytes,2,opt,name=upgrade_time,json=upgradeTime,proto3" json:"upgrade_time,omitempty"` +} + +func (m *MsgSoftwareUpgrade) Reset() { *m = MsgSoftwareUpgrade{} } +func (m *MsgSoftwareUpgrade) String() string { return proto.CompactTextString(m) } +func (*MsgSoftwareUpgrade) ProtoMessage() {} +func (*MsgSoftwareUpgrade) Descriptor() ([]byte, []int) { + return fileDescriptor_309e066b5c0e7455, []int{0} +} +func (m *MsgSoftwareUpgrade) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSoftwareUpgrade) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSoftwareUpgrade.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSoftwareUpgrade) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSoftwareUpgrade.Merge(m, src) +} +func (m *MsgSoftwareUpgrade) XXX_Size() int { + return m.Size() +} +func (m *MsgSoftwareUpgrade) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSoftwareUpgrade.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSoftwareUpgrade proto.InternalMessageInfo + +func (m *MsgSoftwareUpgrade) GetOriginalUpgrade() *types.MsgSoftwareUpgrade { + if m != nil { + return m.OriginalUpgrade + } + return nil +} + +func (m *MsgSoftwareUpgrade) GetUpgradeTime() *types1.Timestamp { + if m != nil { + return m.UpgradeTime + } + return nil +} + +type MsgSoftwareUpgradeResponse struct { +} + +func (m *MsgSoftwareUpgradeResponse) Reset() { *m = MsgSoftwareUpgradeResponse{} } +func (m *MsgSoftwareUpgradeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSoftwareUpgradeResponse) ProtoMessage() {} +func (*MsgSoftwareUpgradeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_309e066b5c0e7455, []int{1} +} +func (m *MsgSoftwareUpgradeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSoftwareUpgradeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSoftwareUpgradeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgSoftwareUpgradeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSoftwareUpgradeResponse.Merge(m, src) +} +func (m *MsgSoftwareUpgradeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSoftwareUpgradeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSoftwareUpgradeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSoftwareUpgradeResponse proto.InternalMessageInfo + +type MsgCancelUpgrade struct { + // authority is the address of the governance account. + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` +} + +func (m *MsgCancelUpgrade) Reset() { *m = MsgCancelUpgrade{} } +func (m *MsgCancelUpgrade) String() string { return proto.CompactTextString(m) } +func (*MsgCancelUpgrade) ProtoMessage() {} +func (*MsgCancelUpgrade) Descriptor() ([]byte, []int) { + return fileDescriptor_309e066b5c0e7455, []int{2} +} +func (m *MsgCancelUpgrade) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelUpgrade) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelUpgrade.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCancelUpgrade) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelUpgrade.Merge(m, src) +} +func (m *MsgCancelUpgrade) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelUpgrade) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelUpgrade.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelUpgrade proto.InternalMessageInfo + +func (m *MsgCancelUpgrade) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +type MsgCancelUpgradeResponse struct { +} + +func (m *MsgCancelUpgradeResponse) Reset() { *m = MsgCancelUpgradeResponse{} } +func (m *MsgCancelUpgradeResponse) String() string { return proto.CompactTextString(m) } +func (*MsgCancelUpgradeResponse) ProtoMessage() {} +func (*MsgCancelUpgradeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_309e066b5c0e7455, []int{3} +} +func (m *MsgCancelUpgradeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgCancelUpgradeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgCancelUpgradeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgCancelUpgradeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgCancelUpgradeResponse.Merge(m, src) +} +func (m *MsgCancelUpgradeResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgCancelUpgradeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgCancelUpgradeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgCancelUpgradeResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgSoftwareUpgrade)(nil), "rollapp.timeupgrade.types.MsgSoftwareUpgrade") + proto.RegisterType((*MsgSoftwareUpgradeResponse)(nil), "rollapp.timeupgrade.types.MsgSoftwareUpgradeResponse") + proto.RegisterType((*MsgCancelUpgrade)(nil), "rollapp.timeupgrade.types.MsgCancelUpgrade") + proto.RegisterType((*MsgCancelUpgradeResponse)(nil), "rollapp.timeupgrade.types.MsgCancelUpgradeResponse") +} + +func init() { proto.RegisterFile("timeupgrade/tx.proto", fileDescriptor_309e066b5c0e7455) } + +var fileDescriptor_309e066b5c0e7455 = []byte{ + // 394 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x4f, 0x4f, 0xfa, 0x30, + 0x18, 0xc7, 0xd9, 0xef, 0x97, 0x98, 0x50, 0x34, 0x90, 0x85, 0x44, 0x68, 0xc8, 0x34, 0x3b, 0x19, + 0x0d, 0xad, 0x40, 0x3c, 0x78, 0xf0, 0xa2, 0x67, 0x2e, 0x20, 0x17, 0x2f, 0xa4, 0x83, 0x52, 0x16, + 0xb7, 0x75, 0xae, 0x1d, 0x30, 0x5f, 0x85, 0xaf, 0xc3, 0x57, 0xe2, 0x91, 0xa3, 0x47, 0x03, 0xaf, + 0xc0, 0x77, 0x60, 0xf6, 0xa7, 0x20, 0x88, 0x26, 0x9c, 0xb6, 0xb6, 0xdf, 0xe7, 0xf3, 0xfd, 0x3e, + 0x7d, 0x52, 0x50, 0x96, 0xb6, 0x4b, 0x43, 0x9f, 0x05, 0x64, 0x48, 0xb1, 0x9c, 0x21, 0x3f, 0xe0, + 0x92, 0xeb, 0xd5, 0x80, 0x3b, 0x0e, 0xf1, 0x7d, 0xf4, 0xed, 0x14, 0xc9, 0xc8, 0xa7, 0x02, 0x96, + 0x19, 0x67, 0x3c, 0x51, 0xe1, 0xf8, 0x2f, 0x2d, 0x80, 0x27, 0x03, 0x2e, 0x5c, 0x2e, 0xb0, 0x22, + 0x4d, 0x1a, 0x16, 0x95, 0xa4, 0xb1, 0x22, 0xc2, 0xe3, 0x4c, 0xe0, 0x0a, 0x86, 0x27, 0x8d, 0xf8, + 0xa3, 0x2a, 0x19, 0xe7, 0xcc, 0xa1, 0x38, 0x59, 0x59, 0xe1, 0x08, 0xc7, 0x96, 0x42, 0x12, 0xd7, + 0xcf, 0x04, 0xd5, 0x6d, 0x01, 0xf1, 0xa2, 0xf4, 0xc8, 0x7c, 0xd5, 0x80, 0xde, 0x16, 0xac, 0xcb, + 0x47, 0x72, 0x4a, 0x02, 0xda, 0x4b, 0xcd, 0xf5, 0x1e, 0x28, 0xf1, 0xc0, 0x66, 0xb6, 0x47, 0x9c, + 0x7e, 0x16, 0xa8, 0xa2, 0x9d, 0x6a, 0x67, 0x85, 0xe6, 0x39, 0x4a, 0x63, 0x20, 0xd5, 0x53, 0x96, + 0x13, 0xfd, 0xa4, 0x74, 0x8a, 0x8a, 0xa1, 0xb0, 0x37, 0xe0, 0x30, 0x2b, 0xeb, 0xc7, 0x19, 0x2b, + 0xff, 0x12, 0x24, 0x44, 0x69, 0x3e, 0xa4, 0xf2, 0xa1, 0x7b, 0xd5, 0x40, 0xa7, 0x90, 0xe9, 0xe3, + 0x1d, 0xb3, 0x06, 0xe0, 0x0e, 0x17, 0x2a, 0x7c, 0xee, 0x09, 0x6a, 0x5e, 0x82, 0x52, 0x5b, 0xb0, + 0x3b, 0xe2, 0x0d, 0xe8, 0xca, 0xb0, 0x06, 0xf2, 0x24, 0x94, 0x63, 0x1e, 0xd8, 0x32, 0x4a, 0x1a, + 0xc8, 0x77, 0xd6, 0x1b, 0x26, 0x04, 0x95, 0xed, 0x0a, 0x45, 0x6b, 0x7e, 0x6a, 0xe0, 0x7f, 0x5b, + 0x30, 0x7d, 0x0a, 0x8a, 0xdb, 0x97, 0x53, 0x47, 0xbf, 0xce, 0x76, 0xc7, 0x2d, 0xc0, 0xab, 0xbd, + 0xe4, 0x2a, 0x80, 0xfe, 0x04, 0x8e, 0x36, 0x7b, 0xb9, 0xf8, 0x9b, 0xb3, 0x21, 0x86, 0xad, 0x3d, + 0xc4, 0xca, 0xf2, 0xb6, 0xfb, 0xb6, 0x30, 0xb4, 0xf9, 0xc2, 0xd0, 0x3e, 0x16, 0x86, 0xf6, 0xb2, + 0x34, 0x72, 0xf3, 0xa5, 0x91, 0x7b, 0x5f, 0x1a, 0xb9, 0x87, 0x6b, 0x66, 0xcb, 0x71, 0x68, 0xa1, + 0x01, 0x77, 0xf1, 0x30, 0x72, 0xa9, 0x27, 0x6c, 0xee, 0xcd, 0xa2, 0xe7, 0xf5, 0xa2, 0x1e, 0x0c, + 0x1f, 0xf1, 0x0c, 0x6f, 0xbc, 0x85, 0xd8, 0xcd, 0x3a, 0x48, 0xa6, 0xda, 0xfa, 0x0a, 0x00, 0x00, + 0xff, 0xff, 0x99, 0x8a, 0x01, 0xeb, 0x27, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + SoftwareUpgrade(ctx context.Context, in *MsgSoftwareUpgrade, opts ...grpc.CallOption) (*MsgSoftwareUpgradeResponse, error) + CancelUpgrade(ctx context.Context, in *MsgCancelUpgrade, opts ...grpc.CallOption) (*MsgCancelUpgradeResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) SoftwareUpgrade(ctx context.Context, in *MsgSoftwareUpgrade, opts ...grpc.CallOption) (*MsgSoftwareUpgradeResponse, error) { + out := new(MsgSoftwareUpgradeResponse) + err := c.cc.Invoke(ctx, "/rollapp.timeupgrade.types.Msg/SoftwareUpgrade", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) CancelUpgrade(ctx context.Context, in *MsgCancelUpgrade, opts ...grpc.CallOption) (*MsgCancelUpgradeResponse, error) { + out := new(MsgCancelUpgradeResponse) + err := c.cc.Invoke(ctx, "/rollapp.timeupgrade.types.Msg/CancelUpgrade", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + SoftwareUpgrade(context.Context, *MsgSoftwareUpgrade) (*MsgSoftwareUpgradeResponse, error) + CancelUpgrade(context.Context, *MsgCancelUpgrade) (*MsgCancelUpgradeResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) SoftwareUpgrade(ctx context.Context, req *MsgSoftwareUpgrade) (*MsgSoftwareUpgradeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SoftwareUpgrade not implemented") +} +func (*UnimplementedMsgServer) CancelUpgrade(ctx context.Context, req *MsgCancelUpgrade) (*MsgCancelUpgradeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CancelUpgrade not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_SoftwareUpgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSoftwareUpgrade) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SoftwareUpgrade(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.timeupgrade.types.Msg/SoftwareUpgrade", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SoftwareUpgrade(ctx, req.(*MsgSoftwareUpgrade)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_CancelUpgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgCancelUpgrade) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).CancelUpgrade(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollapp.timeupgrade.types.Msg/CancelUpgrade", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).CancelUpgrade(ctx, req.(*MsgCancelUpgrade)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "rollapp.timeupgrade.types.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SoftwareUpgrade", + Handler: _Msg_SoftwareUpgrade_Handler, + }, + { + MethodName: "CancelUpgrade", + Handler: _Msg_CancelUpgrade_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "timeupgrade/tx.proto", +} + +func (m *MsgSoftwareUpgrade) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSoftwareUpgrade) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSoftwareUpgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UpgradeTime != nil { + { + size, err := m.UpgradeTime.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.OriginalUpgrade != nil { + { + size, err := m.OriginalUpgrade.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgSoftwareUpgradeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgSoftwareUpgradeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSoftwareUpgradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgCancelUpgrade) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCancelUpgrade) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelUpgrade) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgCancelUpgradeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgCancelUpgradeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgCancelUpgradeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgSoftwareUpgrade) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OriginalUpgrade != nil { + l = m.OriginalUpgrade.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.UpgradeTime != nil { + l = m.UpgradeTime.Size() + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgSoftwareUpgradeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgCancelUpgrade) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgCancelUpgradeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgSoftwareUpgrade) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSoftwareUpgrade: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSoftwareUpgrade: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OriginalUpgrade", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.OriginalUpgrade == nil { + m.OriginalUpgrade = &types.MsgSoftwareUpgrade{} + } + if err := m.OriginalUpgrade.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpgradeTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpgradeTime == nil { + m.UpgradeTime = &types1.Timestamp{} + } + if err := m.UpgradeTime.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgSoftwareUpgradeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgSoftwareUpgradeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSoftwareUpgradeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCancelUpgrade) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCancelUpgrade: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelUpgrade: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgCancelUpgradeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgCancelUpgradeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgCancelUpgradeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +)