diff --git a/CHANGELOG.md b/CHANGELOG.md index da47d74fbfd8..8350a6fb8ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9427](https://github.com/cosmos/cosmos-sdk/pull/9427) Move simapp `FundAccount` and `FundModuleAccount` to `x/bank/testutil` * (client/tx) [\#9421](https://github.com/cosmos/cosmos-sdk/pull/9421/) `BuildUnsignedTx`, `BuildSimTx`, `PrintUnsignedStdTx` functions are moved to the Tx Factory as methods. +* (client/keys) [\#9407](https://github.com/cosmos/cosmos-sdk/pull/9601) Added `keys rename` CLI command and `Keyring.Rename` interface method to rename a key in the keyring. * (x/slashing) [\#9458](https://github.com/cosmos/cosmos-sdk/pull/9458) Coins burned from slashing is now returned from Slash function and included in Slash event. * [\#9246](https://github.com/cosmos/cosmos-sdk/pull/9246) The `New` method for the network package now returns an error. * (codec) [\#9521](https://github.com/cosmos/cosmos-sdk/pull/9521) Removed deprecated `clientCtx.JSONCodec` from `client.Context`. @@ -58,6 +59,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) `types/rest` package moved to `testutil/rest`. * [\#9432](https://github.com/cosmos/cosmos-sdk/pull/9432) `ConsensusParamsKeyTable` moved from `params/keeper` to `params/types` * [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled +* [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules. ### Client Breaking Changes @@ -75,12 +77,14 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* [\#9720](https://github.com/cosmos/cosmos-sdk/pull/9720) Feegrant grant cli granter now accepts key name as well as address in general and accepts only address in --generate-only mode * [\#9651](https://github.com/cosmos/cosmos-sdk/pull/9651) Change inconsistent limit of `0` to `MaxUint64` on InfiniteGasMeter and add GasRemaining func to GasMeter. * [\#9639](https://github.com/cosmos/cosmos-sdk/pull/9639) Check store keys length before accessing them by making sure that `key` is of length `m+1` (for `key[n:m]`) * (types) [\#9627](https://github.com/cosmos/cosmos-sdk/pull/9627) Fix nil pointer panic on `NewBigIntFromInt` * (x/genutil) [\#9574](https://github.com/cosmos/cosmos-sdk/pull/9575) Actually use the `gentx` client tx flags (like `--keyring-dir`) * (x/distribution) [\#9599](https://github.com/cosmos/cosmos-sdk/pull/9599) Withdraw rewards event now includes a value attribute even if there are 0 rewards (due to situations like 100% commission). * (x/genutil) [\#9638](https://github.com/cosmos/cosmos-sdk/pull/9638) Added missing validator key save when recovering from mnemonic +* (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. ## [v0.43.0-rc0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.43.0-rc0) - 2021-06-25 @@ -207,7 +211,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/params) [\#9481](https://github.com/cosmos/cosmos-sdk/issues/9481) Speedup simulator for parameter change proposals. * (x/staking) [\#9423](https://github.com/cosmos/cosmos-sdk/pull/9423) Staking delegations now returns empty list instead of rpc error when no records found. * (x/auth) [\#9553](https://github.com/cosmos/cosmos-sdk/pull/9553) The `--multisig` flag now accepts both a name and address. -* (baseapp) [\#9578](https://github.com/cosmos/cosmos-sdk/pull/9578) Return `Baseapp`'s `trace` value for logging error stack traces. * [\#8549](https://github.com/cosmos/cosmos-sdk/pull/8549) Make gRPC requests go through tendermint Query * [\#8093](https://github.com/cosmos/cosmos-sdk/pull/8093) Limit usage of context.background. * [\#8460](https://github.com/cosmos/cosmos-sdk/pull/8460) Ensure b.ReportAllocs() in all the benchmarks @@ -222,13 +225,12 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger * (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command * (x/bank/types) [\#9112](https://github.com/cosmos/cosmos-sdk/pull/9112) fix AddressFromBalancesStore address length overflow -* (x/bank) [\#9229](https://github.com/cosmos/cosmos-sdk/pull/9229) Now zero coin balances cannot be added to balances & supply stores. If any denom becomes zero corresponding key gets deleted from store. +* (x/bank) [\#9229](https://github.com/cosmos/cosmos-sdk/pull/9229) Now zero coin balances cannot be added to balances & supply stores. If any denom becomes zero corresponding key gets deleted from store. State migration: [\#9664](https://github.com/cosmos/cosmos-sdk/pull/9664). * [\#9363](https://github.com/cosmos/cosmos-sdk/pull/9363) Check store key uniqueness in app wiring. * [\#9460](https://github.com/cosmos/cosmos-sdk/pull/9460) Fix lint error in `MigratePrefixAddress`. * [\#9480](https://github.com/cosmos/cosmos-sdk/pull/9480) Fix added keys when using `--dry-run`. * (types) [\#9511](https://github.com/cosmos/cosmos-sdk/pull/9511) Change `maxBitLen` of `sdk.Int` and `sdk.Dec` to handle max ERC20 value. * [\#9454](https://github.com/cosmos/cosmos-sdk/pull/9454) Fix testnet command with --node-dir-prefix accepts `-` and change `node-dir-prefix token` to `testtoken`. -* (keyring) [\#9562](https://github.com/cosmos/cosmos-sdk/pull/9563) fix keyring kwallet backend when using with empty wallet. * (keyring) [\#9583](https://github.com/cosmos/cosmos-sdk/pull/9583) Fix correct population of legacy `Vote.Option` field for votes with 1 VoteOption of weight 1. * (x/distinction) [\#8918](https://github.com/cosmos/cosmos-sdk/pull/8918) Fix module's parameters validation. * (x/gov/types) [\#8586](https://github.com/cosmos/cosmos-sdk/pull/8586) Fix bug caused by NewProposal that unnecessarily creates a Proposal object that’s discarded on any error. @@ -244,6 +246,20 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (grpc) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `tx` field in `SimulateRequest` has been deprecated, prefer to pass `tx_bytes` instead. * (sdk types) [\#9498](https://github.com/cosmos/cosmos-sdk/pull/9498) `clientContext.JSONCodec` will be removed in the next version. use `clientContext.Codec` instead. +## [v0.42.7](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.7) - 2021-07-09 + +### Improvements + +* (baseapp) [\#9578](https://github.com/cosmos/cosmos-sdk/pull/9578) Return `Baseapp`'s `trace` value for logging error stack traces. + +### Bug Fixes + +* (x/ibc) [\#9640](https://github.com/cosmos/cosmos-sdk/pull/9640) Fix IBC Transfer Ack Success event as it was initially emitting opposite value. +* [\#9645](https://github.com/cosmos/cosmos-sdk/pull/9645) Use correct Prometheus format for metric labels. +* [\#9299](https://github.com/cosmos/cosmos-sdk/pull/9299) Fix `[appd] keys parse cosmos1...` freezing. +* (keyring) [\#9563](https://github.com/cosmos/cosmos-sdk/pull/9563) fix keyring kwallet backend when using with empty wallet. +* (x/capability) [\#9392](https://github.com/cosmos/cosmos-sdk/pull/9392) initialization fix, which fixes the consensus error when using statesync. + ## [v0.42.6](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.6) - 2021-06-18 ### Improvements diff --git a/client/keys/rename.go b/client/keys/rename.go new file mode 100644 index 000000000000..2c5f11263de0 --- /dev/null +++ b/client/keys/rename.go @@ -0,0 +1,67 @@ +package keys + +import ( + "bufio" + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" +) + +// RenameKeyCommand renames a key from the key store. +func RenameKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "rename ", + Short: "Rename an existing key", + Long: `Rename a key from the Keybase backend. + +Note that renaming offline or ledger keys will rename +only the public key references stored locally, i.e. +private keys stored in a ledger device cannot be renamed with the CLI. +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + buf := bufio.NewReader(cmd.InOrStdin()) + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + oldName, newName := args[0], args[1] + + info, err := clientCtx.Keyring.Key(oldName) + if err != nil { + return err + } + + // confirm rename, unless -y is passed + if skip, _ := cmd.Flags().GetBool(flagYes); !skip { + prompt := fmt.Sprintf("Key reference will be renamed from %s to %s. Continue?", args[0], args[1]) + if yes, err := input.GetConfirmation(prompt, buf, cmd.ErrOrStderr()); err != nil { + return err + } else if !yes { + return nil + } + } + + if err := clientCtx.Keyring.Rename(oldName, newName); err != nil { + return err + } + + if info.GetType() == keyring.TypeLedger || info.GetType() == keyring.TypeOffline { + cmd.PrintErrln("Public key reference renamed") + return nil + } + + cmd.PrintErrln(fmt.Sprintf("Key was successfully renamed from %s to %s", oldName, newName)) + + return nil + }, + } + + cmd.Flags().BoolP(flagYes, "y", false, "Skip confirmation prompt when renaming offline or ledger key references") + + return cmd +} diff --git a/client/keys/rename_test.go b/client/keys/rename_test.go new file mode 100644 index 000000000000..37d15ecba25e --- /dev/null +++ b/client/keys/rename_test.go @@ -0,0 +1,98 @@ +package keys + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func Test_runRenameCmd(t *testing.T) { + // temp keybase + kbHome := t.TempDir() + cmd := RenameKeyCommand() + cmd.Flags().AddFlagSet(Commands(kbHome).PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + yesF, _ := cmd.Flags().GetBool(flagYes) + require.False(t, yesF) + + fakeKeyName1 := "runRenameCmd_Key1" + fakeKeyName2 := "runRenameCmd_Key2" + + path := sdk.GetConfig().GetFullBIP44Path() + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) + require.NoError(t, err) + + // put fakeKeyName1 in keyring + _, err = kb.NewAccount(fakeKeyName1, testutil.TestMnemonic, "", path, hd.Secp256k1) + require.NoError(t, err) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb) + + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + // rename a key 'blah' which doesnt exist + cmd.SetArgs([]string{"blah", "blaah", fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome)}) + err = cmd.ExecuteContext(ctx) + require.Error(t, err) + require.EqualError(t, err, "blah.info: key not found") + + // User confirmation missing + cmd.SetArgs([]string{ + fakeKeyName1, + "nokey", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + err = cmd.Execute() + require.Error(t, err) + require.Equal(t, "EOF", err.Error()) + + oldKey, err := kb.Key(fakeKeyName1) + require.NoError(t, err) + + // add a confirmation + cmd.SetArgs([]string{ + fakeKeyName1, + fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flagYes), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + require.NoError(t, cmd.Execute()) + + // key1 is gone + _, err = kb.Key(fakeKeyName1) + require.Error(t, err) + + // key2 exists now + renamedKey, err := kb.Key(fakeKeyName2) + require.NoError(t, err) + + require.Equal(t, oldKey.GetPubKey(), renamedKey.GetPubKey()) + require.Equal(t, oldKey.GetType(), renamedKey.GetType()) + require.Equal(t, oldKey.GetAddress(), renamedKey.GetAddress()) + require.Equal(t, oldKey.GetAlgo(), renamedKey.GetAlgo()) + + // try to rename key1 but it doesnt exist anymore so error + cmd.SetArgs([]string{ + fakeKeyName1, + fakeKeyName2, + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=true", flagYes), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + }) + require.Error(t, cmd.Execute()) +} diff --git a/client/keys/root.go b/client/keys/root.go index 938a0d53a14c..e8b1ef156987 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -45,6 +45,7 @@ The pass backend requires GnuPG: https://gnupg.org/ ListKeysCmd(), ShowKeysCmd(), DeleteKeyCommand(), + RenameKeyCommand(), ParseKeyStringCommand(), MigrateCommand(), ) diff --git a/client/keys/root_test.go b/client/keys/root_test.go index b6c2f5f88f48..f66ae9265de6 100644 --- a/client/keys/root_test.go +++ b/client/keys/root_test.go @@ -11,5 +11,5 @@ func TestCommands(t *testing.T) { assert.NotNil(t, rootCommands) // Commands are registered - assert.Equal(t, 9, len(rootCommands.Commands())) + assert.Equal(t, 10, len(rootCommands.Commands())) } diff --git a/cosmovisor/README.md b/cosmovisor/README.md index c32c39834582..33becafcb9e6 100644 --- a/cosmovisor/README.md +++ b/cosmovisor/README.md @@ -1,6 +1,8 @@ # Cosmosvisor Quick Start -`cosmovisor` is a small process manager for Cosmos SDK binaries that monitors the governance module via stdout for incoming chain upgrade proposals. If it sees a proposal that gets approved, it can be run manually or automatically to download the new binary, stop the current binary, run the migration script, replace the old node binary with the new one, and finally restart the node with the new genesis file. +`cosmovisor` is a small process manager for Cosmos SDK application binaries that monitors the governance module via stdout for incoming chain upgrade proposals. If it sees a proposal that gets approved, `cosmovisor` can automatically download the new binary, stop the current binary, switch from the old binary to the new one, and finally restart the node with the new binary. + +*Note: If new versions of the application are not set up to run in-place store migrations, migrations will need to be run manually before restarting `cosmovisor` with the new binary. For this reason, we recommend applications adopt in-place store migrations.* ## Installation @@ -12,16 +14,16 @@ go get github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor ## Command Line Arguments And Environment Variables -All arguments passed to the `cosmovisor` program will be passed to the current daemon binary (as a subprocess). `cosmovisor` will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. For this reason, `cosmovisor` cannot accept any command line arguments, nor print anything to output (unless it terminates unexpectedly before executing a binary). +All arguments passed to `cosmovisor` will be passed to the application binary (as a subprocess). `cosmovisor` will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. For this reason, `cosmovisor` cannot accept any command-line arguments other than those available to the application binary, nor will it print anything to output other than what is printed by the application binary. `cosmovisor` reads its configuration from environment variables: -* `DAEMON_HOME` is the location where the `cosmovisor/` directory is kept that contains the upgrade binaries (e.g. `$HOME/.gaiad`, `$HOME/.regend`, `$HOME/.simd`, etc.). +* `DAEMON_HOME` is the location where the `cosmovisor/` directory is kept that contains the genesis binary, the upgrade binaries, and any additional auxiliary files associated with each binary (e.g. `$HOME/.gaiad`, `$HOME/.regend`, `$HOME/.simd`, etc.). * `DAEMON_NAME` is the name of the binary itself (e.g. `gaiad`, `regend`, `simd`, etc.). * `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*), if set to `true`, will enable auto-downloading of new binaries (for security reasons, this is intended for full nodes rather than validators). By default, `cosmovisor` will not auto-download new binaries. -* `DAEMON_RESTART_AFTER_UPGRADE` (*optional*), if set to `true`, will restart the subprocess with the same command line arguments and flags (but with the new binary) after a successful upgrade. By default, `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note that `cosmovisor` will not auto-restart the subprocess if there was an error. +* `DAEMON_RESTART_AFTER_UPGRADE` (*optional*), if set to `true`, will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. By default, `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note that `cosmovisor` will not auto-restart the subprocess if there was an error. -## Data Folder Layout +## Folder Layout `$DAEMON_HOME/cosmovisor` is expected to belong completely to `cosmovisor` and the subprocesses that are controlled by it. The folder content is organized as follows: @@ -37,11 +39,9 @@ All arguments passed to the `cosmovisor` program will be passed to the current d └── $DAEMON_NAME ``` -Each version of the Cosmos SDK application is stored under either `genesis` or `upgrades/`, which holds `bin/$DAEMON_NAME` along with any other needed files such as auxiliary client programs or libraries. `current` is a symbolic link to the currently active folder and `current/bin/$DAEMON_NAME` is the currently active binary. - -*Note: The `name` variable in `upgrades/` holds the URI-encoded name of the upgrade as specified in the upgrade module plan.* +The `cosmovisor/` directory incudes a subdirectory for each version of the application (i.e. `genesis` or `upgrades/`). Within each subdirectory is the application binary (i.e. `bin/$DAEMON_NAME`) and any additional auxiliary files associated with each binary. `current` is a symbolic link to the currently active directory (i.e `genesis` or `upgrades/`). The `name` variable in `upgrades/` is the URI-encoded name of the upgrade as specified in the upgrade module plan. -Please note that `$DAEMON_HOME/cosmovisor` just stores the *binaries* and associated *program code*. The `cosmovisor` binary can be stored in any typical location (e.g. `/usr/local/bin`). The actual blockchain program will store its data under the default data directory (e.g. `$HOME/.gaiad`) which is independent of `$DAEMON_HOME`. `$DAEMON_HOME` can be set to any location. If you set `$DAEMON_HOME` to the default data directory, you will end up with a configuation like the following: +Please note that `$DAEMON_HOME/cosmovisor` only stores the *application binaries*. The `cosmovisor` binary itself can be stored in any typical location (e.g. `/usr/local/bin`). The application will continue to store its data in the default data directory (e.g. `$HOME/.gaiad`) or the data directory specified with the `--home` flag. `$DAEMON_HOME` is independent of the data directory and can be set to any location. If you set `$DAEMON_HOME` to the same directory as the data directory, you will end up with a configuation like the following: ``` .gaiad @@ -57,20 +57,20 @@ The system administrator is responsible for: - installing the `cosmovisor` binary - configuring the host's init system (e.g. `systemd`, `launchd`, etc.) - appropriately setting the environmental variables -- installing the `genesis` folder manually -- installing the `upgrades/` folders manually +- manually installing the `genesis` folder +- manually installing the `upgrades/` folders -`cosmovisor` will set the `current` link to point to `genesis` at first start (when no `current` link exists) and will handle switching binaries at the correct points in time so that the system administrator can prepare days in advance and relax at upgrade time. +`cosmovisor` will set the `current` link to point to `genesis` at first start (i.e. when no `current` link exists) and then handle switching binaries at the correct points in time so that the system administrator can prepare days in advance and relax at upgrade time. -Note that blockchain applications that wish to support upgrades may package up a genesis `cosmovisor` tarball with this information, just as they prepare the genesis binary tarball. In fact, they may package up a tarball with all upgrades up to a current point so that the upgrades can be easily downloaded for others who wish to sync a fullnode from start. +In order to support downloadable binaries, a tarball for each upgrade binary will need to be packaged up and made available through a canonical URL. Additionally, a tarball that includes the genesis binary and all available upgrade binaries can be packaged up and made available so that all the necessary binaries required to sync a fullnode from start can be easily downloaded. The `DAEMON` specific code and operations (e.g. tendermint config, the application db, syncing blocks, etc.) all work as expected. The application binaries' directives such as command-line flags and environment variables also work as expected. ## Auto-Download -Generally, the system requires that the system administrator place all relevant binaries on the disk before the upgrade happens. However, for people who don't need such control and want an easier setup (maybe they are syncing a non-validating fullnode and want to do little maintenance), there is another option. +Generally, `cosmovisor` requires that the system administrator place all relevant binaries on disk before the upgrade happens. However, for people who don't need such control and want an easier setup (maybe they are syncing a non-validating fullnode and want to do little maintenance), there is another option. -If you set `DAEMON_ALLOW_DOWNLOAD_BINARIES=true`, and no local binary can be found when an upgrade is triggered, `cosmovisor` will attempt to download and install the binary itself. The plan stored in the upgrade module has an info field for arbitrary JSON. This info is expected to be outputed on the halt log message. There are two valid formats to specify a download in such a message: +If `DAEMON_ALLOW_DOWNLOAD_BINARIES` is set to `true`, and no local binary can be found when an upgrade is triggered, `cosmovisor` will attempt to download and install the binary itself. The plan stored in the upgrade module has an info field for arbitrary JSON. This info is expected to be outputed on the halt log message. There are two valid formats to specify a download in such a message: 1. Store an os/architecture -> binary URI map in the upgrade plan info field as JSON under the `"binaries"` key. For example: @@ -88,11 +88,9 @@ If you set `DAEMON_ALLOW_DOWNLOAD_BINARIES=true`, and no local binary can be fou https://example.com/testnet-1001-info.json?checksum=sha256:deaaa99fda9407c4dbe1d04bd49bab0cc3c1dd76fa392cd55a9425be074af01e ``` -This file contained in the link will be retrieved by [go-getter](https://github.com/hashicorp/go-getter) and the `"binaries"` field will be parsed as above. - -If there is no local binary, `DAEMON_ALLOW_DOWNLOAD_BINARIES=true`, and we can access a canonical url for the new binary, then the `cosmovisor` will download it with [go-getter](https://github.com/hashicorp/go-getter) and unpack it into the `upgrades/` folder to be run as if we installed it manually. +When `cosmovisor` is triggered to download the new binary, `cosmovisor` will parse the `"binaries"` field, download the new binary with [go-getter](https://github.com/hashicorp/go-getter), and unpack the new binary in the `upgrades/` folder so that it can be run as if it was installed manually. -Note that for this mechanism to provide strong security guarantees, all URLs should include a SHA 256/512 checksum. This ensures that no false binary is run, even if someone hacks the server or hijacks the DNS. `go-getter` will always ensure the downloaded file matches the checksum if it is provided. `go-getter` will also handle unpacking archives into directories (in this case the download link should point to a `zip` file of all data in the `bin` directory). +Note that for this mechanism to provide strong security guarantees, all URLs should include a SHA 256/512 checksum. This ensures that no false binary is run, even if someone hacks the server or hijacks the DNS. `go-getter` will always ensure the downloaded file matches the checksum if it is provided. To properly create a sha256 checksum on linux, you can use the `sha256sum` utility. For example: @@ -104,80 +102,91 @@ The result will look something like the following: `29139e1381b8177aec909fab9a75 You can also use `sha512sum` if you would prefer to use longer hashes, or `md5sum` if you would prefer to use broken hashes. Whichever you choose, make sure to set the hash algorithm properly in the checksum argument to the URL. -## Example: simd +## Example: SimApp Upgrade -The following instructions provide a demonstration of `cosmovisor`'s integration with the `simd` application shipped along the Cosmos SDK's source code. The following commands are to be run from within the `cosmos-sdk` repository. +The following instructions provide a demonstration of `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code. The following commands are to be run from within the `cosmos-sdk` repository. -First compile `simd`: +First compile the `simd` binary: ``` make build ``` -Create a new key and set up the `simd` node: +Reset `~/.simapp` (never do this in a production environment): ``` -rm -rf $HOME/.simapp -./build/simd keys --keyring-backend=test add validator -./build/simd init testing --chain-id test -./build/simd add-genesis-account --keyring-backend=test validator 1000000000stake -./build/simd gentx --keyring-backend test --chain-id test validator 1000000stake -./build/simd collect-gentxs +./build/simd unsafe-reset-all ``` -Set the required environment variables: +Configure the `simd` binary for testing: ``` -export DAEMON_NAME=simd -export DAEMON_HOME=$HOME/.simapp +./build/simd config chain-id test +./build/simd config keyring-backend test +./build/simd config broadcast-mode block ``` -Set the optional environment variable to trigger an automatic restart: +Initialize the node and overwrite any previous genesis file (never do this in a production environment): + + ``` -export DAEMON_RESTART_AFTER_UPGRADE=true +./build/simd init test --chain-id test --overwrite ``` -Create the `cosmovisor`’s genesis folders and copy the `simd` binary: +Set the minimum gas price to `0stake` in `~/.simapp/config/app.toml`: ``` -mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin -cp ./build/simd $DAEMON_HOME/cosmovisor/genesis/bin +minimum-gas-prices = "0stake" ``` -For the sake of this demonstration, amend `voting_params.voting_period` in `$HOME/.simapp/config/genesis.json` to a reduced time of ~5 minutes (`300s`) and then start `cosmosvisor`: +Create a new key for the validator, then add a genesis account and transaction: + + + ``` -cosmovisor start +./build/simd keys add validator +./build/simd add-genesis-account validator 1000000000stake --keyring-backend test +./build/simd gentx validator 1000000stake --chain-id test +./build/simd collect-gentxs ``` -Open a new terminal window and submit a software upgrade proposal: +Set the required environment variables: ``` -./build/simd tx gov submit-proposal software-upgrade test1 --title "upgrade-demo" --description "upgrade" --from validator --upgrade-height 100 --deposit 10000000stake --chain-id test --keyring-backend test -y +export DAEMON_NAME=simd +export DAEMON_HOME=$HOME/.simapp ``` -Query the proposal to ensure it was correctly broadcast and added to a block: +Set the optional environment variable to trigger an automatic restart: ``` -./build/simd query gov proposal 1 +export DAEMON_RESTART_AFTER_UPGRADE=true ``` -Submit a `yes` vote for the upgrade proposal: +Create the folder for the genesis binary and copy the `simd` binary: ``` -./build/simd tx gov vote 1 yes --from validator --keyring-backend test --chain-id test -y +mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin +cp ./build/simd $DAEMON_HOME/cosmovisor/genesis/bin ``` -For the sake of this demonstration, we will hardcode a modification in `simapp` to simulate a code change. In `simapp/app.go`, find the line containing the `UpgradeKeeper` initialization. It should look like the following: +For the sake of this demonstration, amend `voting_period` in `genesis.json` to a reduced time of 20 seconds (`20s`): ``` +cat <<< $(jq '.app_state.gov.voting_params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json +``` + +Next, we will hardcode a modification in `simapp` to simulate a code change. In `simapp/app.go`, find the line containing the `UpgradeKeeper` initialization. It should look like the following: + +```go app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath) ``` -After that line, add the following snippet: +After that line, add the following: - ``` + ```go app.UpgradeKeeper.SetUpgradeHandler("test1", func(ctx sdk.Context, plan upgradetypes.Plan) { // Add some coins to a random account addr, err := sdk.AccAddressFromBech32("cosmos18cgkqduwuh253twzmhedesw3l7v3fm37sppt58") @@ -191,12 +200,31 @@ app.UpgradeKeeper.SetUpgradeHandler("test1", func(ctx sdk.Context, plan upgradet }) ``` -Now recompile a new binary and make a copy of it in `$DAEMON_HOME/cosmosvisor/upgrades/test1/bin` (you may need to run `export DAEMON_HOME=$HOME/.simapp` again if you are using a new window): +Now recompile the `simd` binary with the added upgrade handler: ``` make build +``` + +Create the folder for the upgrade binary and copy the `simd` binary: + +``` mkdir -p $DAEMON_HOME/cosmovisor/upgrades/test1/bin cp ./build/simd $DAEMON_HOME/cosmovisor/upgrades/test1/bin ``` -The upgrade will occur automatically at height 100. +Start `cosmosvisor`: + +``` +cosmovisor start +``` + +Open a new terminal window and submit an upgrade proposal along with a deposit and a vote (these commands must be run within 20 seconds of each other): + +``` +./build/simd tx gov submit-proposal software-upgrade test1 --title upgrade --description upgrade --upgrade-height 20 --from validator --yes +./build/simd tx gov deposit 1 10000000stake --from validator --yes +./build/simd tx gov vote 1 yes --from validator --yes +``` + +The upgrade will occur automatically at height 20. diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index eb5762497688..717f589fa789 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -41,6 +41,9 @@ const ( keyringFileDirName = "keyring-file" keyringTestDirName = "keyring-test" passKeyringPrefix = "keyring-%s" + + // temporary pass phrase for exporting a key during a key rename + passPhrase = "temp" ) var ( @@ -64,6 +67,9 @@ type Keyring interface { Delete(uid string) error DeleteByAddress(address sdk.Address) error + // Rename an existing key from the Keyring + Rename(from string, to string) error + // NewMnemonic generates a new mnemonic, derives a hierarchical deterministic key from it, and // persists the key to storage. Returns the generated mnemonic and the key Info. // It returns an error if it fails to generate a key for the given algo type, or if @@ -288,8 +294,10 @@ func (ks keystore) ExportPrivKeyArmorByAddress(address sdk.Address, encryptPassp } func (ks keystore) ImportPrivKey(uid, armor, passphrase string) error { - if _, err := ks.Key(uid); err == nil { - return fmt.Errorf("cannot overwrite key: %s", uid) + if k, err := ks.Key(uid); err == nil { + if uid == k.GetName() { + return fmt.Errorf("cannot overwrite key: %s", uid) + } } privKey, algo, err := crypto.UnarmorDecryptPrivKey(armor, passphrase) @@ -426,6 +434,30 @@ func (ks keystore) DeleteByAddress(address sdk.Address) error { return nil } +func (ks keystore) Rename(oldName, newName string) error { + _, err := ks.Key(newName) + if err == nil { + return fmt.Errorf("rename failed: %s already exists in the keyring", newName) + } + + armor, err := ks.ExportPrivKeyArmor(oldName, passPhrase) + if err != nil { + return err + } + + err = ks.ImportPrivKey(newName, armor, passPhrase) + if err != nil { + return err + } + + err = ks.Delete(oldName) + if err != nil { + return err + } + + return nil +} + func (ks keystore) Delete(uid string) error { info, err := ks.Key(uid) if err != nil { @@ -783,16 +815,22 @@ func (ks keystore) writeInfo(info Info) error { } // existsInDb returns true if key is in DB. Error is returned only when we have error -// different thant ErrKeyNotFound +// different than ErrKeyNotFound func (ks keystore) existsInDb(info Info) (bool, error) { - if _, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil { - return true, nil // address lookup succeeds - info exists + if item, err := ks.db.Get(addrHexKeyAsString(info.GetAddress())); err == nil { + if item.Key == info.GetName() { + return true, nil // address lookup succeeds - info exists + } + return false, nil } else if err != keyring.ErrKeyNotFound { return false, err // received unexpected error - returns error } - if _, err := ks.db.Get(infoKey(info.GetName())); err == nil { - return true, nil // uid lookup succeeds - info exists + if item, err := ks.db.Get(infoKey(info.GetName())); err == nil { + if item.Key == info.GetName() { + return true, nil // uid lookup succeeds - info exists + } + return false, nil } else if err != keyring.ErrKeyNotFound { return false, err // received unexpected error - returns } diff --git a/crypto/keyring/keyring_test.go b/crypto/keyring/keyring_test.go index 2f8e928e7a7e..c2a0df70a7d9 100644 --- a/crypto/keyring/keyring_test.go +++ b/crypto/keyring/keyring_test.go @@ -1153,6 +1153,72 @@ func TestBackendConfigConstructors(t *testing.T) { require.Equal(t, "keyring-test", backend.PassPrefix) } +func TestRenameKey(t *testing.T) { + testCases := []struct { + name string + run func(Keyring) + }{ + { + name: "rename a key", + run: func(kr Keyring) { + oldKeyUID, newKeyUID := "old", "new" + oldKeyInfo := newKeyInfo(t, kr, oldKeyUID) + err := kr.Rename(oldKeyUID, newKeyUID) // rename from "old" to "new" + require.NoError(t, err) + newInfo, err := kr.Key(newKeyUID) // new key should be in keyring + require.NoError(t, err) + requireEqualRenamedKey(t, newInfo, oldKeyInfo) // oldinfo and newinfo should be the same + oldKeyInfo, err = kr.Key(oldKeyUID) // old key should be gone from keyring + require.Error(t, err) + }, + }, + { + name: "cant rename a key that doesnt exist", + run: func(kr Keyring) { + err := kr.Rename("bogus", "bogus2") + require.Error(t, err) + }, + }, + { + name: "cant rename a key to an already existing key name", + run: func(kr Keyring) { + key1, key2 := "existingKey", "existingKey2" // create 2 keys + newKeyInfo(t, kr, key1) + newKeyInfo(t, kr, key2) + err := kr.Rename(key2, key1) + require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", key1), err) + assertKeysExist(t, kr, key1, key2) // keys should still exist after failed rename + }, + }, + { + name: "cant rename key to itself", + run: func(kr Keyring) { + keyName := "keyName" + newKeyInfo(t, kr, keyName) + err := kr.Rename(keyName, keyName) + require.Equal(t, fmt.Errorf("rename failed: %s already exists in the keyring", keyName), err) + assertKeysExist(t, kr, keyName) + }, + }, + } + + for _, tc := range testCases { + tc := tc + kr := newKeyring(t, "testKeyring") + t.Run(tc.name, func(t *testing.T) { + tc.run(kr) + }) + } +} + +func requireEqualRenamedKey(t *testing.T, newKey, oldKey Info) { + require.NotEqual(t, newKey.GetName(), oldKey.GetName()) + require.Equal(t, newKey.GetAddress(), oldKey.GetAddress()) + require.Equal(t, newKey.GetPubKey(), oldKey.GetPubKey()) + require.Equal(t, newKey.GetAlgo(), oldKey.GetAlgo()) + require.Equal(t, newKey.GetType(), oldKey.GetType()) +} + func requireEqualInfo(t *testing.T, key Info, mnemonic Info) { require.Equal(t, key.GetName(), mnemonic.GetName()) require.Equal(t, key.GetAddress(), mnemonic.GetAddress()) @@ -1161,4 +1227,23 @@ func requireEqualInfo(t *testing.T, key Info, mnemonic Info) { require.Equal(t, key.GetType(), mnemonic.GetType()) } +func newKeyring(t *testing.T, name string) Keyring { + kr, err := New(name, "test", t.TempDir(), nil) + require.NoError(t, err) + return kr +} + +func newKeyInfo(t *testing.T, kr Keyring, name string) Info { + keyInfo, _, err := kr.NewMnemonic(name, English, sdk.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1) + require.NoError(t, err) + return keyInfo +} + +func assertKeysExist(t *testing.T, kr Keyring, names ...string) { + for _, n := range names { + _, err := kr.Key(n) + require.NoError(t, err) + } +} + func accAddr(info Info) sdk.AccAddress { return info.GetAddress() } diff --git a/docs/building-modules/upgrade.md b/docs/building-modules/upgrade.md index 2f9eaa98f339..f1c37930cffa 100644 --- a/docs/building-modules/upgrade.md +++ b/docs/building-modules/upgrade.md @@ -2,9 +2,9 @@ order: 13 --> -# In-Place Store Migrations +# Upgrading Modules -In-place store migrations allow your modules to upgrade to new versions that include breaking changes. This document outlines how to build modules to take advantage of this functionality. {synopsis} +[In-Place Store Migrations](../core/upgrade.html) allow your modules to upgrade to new versions that include breaking changes. This document outlines how to build modules to take advantage of this functionality. {synopsis} ## Prerequisite Readings @@ -12,16 +12,16 @@ In-place store migrations allow your modules to upgrade to new versions that inc ## Consensus Version -Successful upgrades of existing modules require your `AppModule` to implement the function `ConsensusVersion() uint64`. +Successful upgrades of existing modules require each `AppModule` to implement the function `ConsensusVersion() uint64`. - The versions must be hard-coded by the module developer. - The initial version **must** be set to 1. -Consensus versions serve as state-breaking versions of app modules and are incremented when the module is upgraded. +Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes. ## Registering Migrations -To register the functionality that takes place during a module upgrade, you must register which migrations we want to take place. +To register the functionality that takes place during a module upgrade, you must register which migrations you want to take place. Migration registration takes place in the `Configurator` using the `RegisterMigration` method. The `AppModule` reference to the configurator is in the `RegisterServices` method. @@ -54,4 +54,4 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error { } ``` -To see example code of changes that were implemented in a migration of balance keys, check out the [func migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/36f68eb9e041e20a5bb47e216ac5eb8b91f95471/x/bank/legacy/v043/store.go#L41-L62) code. For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](../architecture/adr-028-public-key-addresses.md). +To see example code of changes that were implemented in a migration of balance keys, check out [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/36f68eb9e041e20a5bb47e216ac5eb8b91f95471/x/bank/legacy/v043/store.go#L41-L62). For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](../architecture/adr-028-public-key-addresses.md). diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index eb819d75c36f..0502d41e69a9 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -606,6 +606,7 @@ - [ModeInfo.Multi](#cosmos.tx.v1beta1.ModeInfo.Multi) - [ModeInfo.Single](#cosmos.tx.v1beta1.ModeInfo.Single) - [SignDoc](#cosmos.tx.v1beta1.SignDoc) + - [SignDocJSON](#cosmos.tx.v1beta1.SignDocJSON) - [SignerInfo](#cosmos.tx.v1beta1.SignerInfo) - [Tx](#cosmos.tx.v1beta1.Tx) - [TxBody](#cosmos.tx.v1beta1.TxBody) @@ -8543,10 +8544,11 @@ SignMode represents a signing mode with its own security guarantees. | Name | Number | Description | | ---- | ------ | ----------- | -| SIGN_MODE_UNSPECIFIED | 0 | SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be rejected | -| SIGN_MODE_DIRECT | 1 | SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is verified with raw bytes from Tx | -| SIGN_MODE_TEXTUAL | 2 | SIGN_MODE_TEXTUAL is a future signing mode that will verify some human-readable textual representation on top of the binary representation from SIGN_MODE_DIRECT | -| SIGN_MODE_LEGACY_AMINO_JSON | 127 | SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses Amino JSON and will be removed in the future | +| SIGN_MODE_UNSPECIFIED | 0 | SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be rejected. | +| SIGN_MODE_DIRECT | 1 | SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is verified with raw bytes from Tx. | +| SIGN_MODE_TEXTUAL | 2 | SIGN_MODE_TEXTUAL is a future signing mode that will verify some human-readable textual representation on top of the binary representation from SIGN_MODE_DIRECT. It is currently not supported. | +| SIGN_MODE_DIRECT_JSON | 3 | SIGN_MODE_DIRECT_JSON specifies a signing mode which uses SignDocJSON. It is verified using a canonical JSON representation of the bytes used in SIGN_MODE_DIRECT. | +| SIGN_MODE_LEGACY_AMINO_JSON | 127 | SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses Amino JSON and will be removed in the future. | @@ -8668,6 +8670,28 @@ SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. + + +### SignDocJSON +SignDocJSON is the type used for generating sign bytes for +SIGN_MODE_DIRECT_JSON. It is designed to be serialized as proto3 JSON +following the rules defined here: +https://github.com/regen-network/canonical-proto3/blob/master/README.md#json. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `body` | [TxBody](#cosmos.tx.v1beta1.TxBody) | | body is the processable content of the transaction | +| `auth_info` | [AuthInfo](#cosmos.tx.v1beta1.AuthInfo) | | auth_info is the authorization related content of the transaction, specifically signers, signer modes and fee | +| `chain_id` | [string](#string) | | chain_id is the identifier of the chain this transaction targets. It prevents signed transactions from being used on another chain by an attacker | +| `account_number` | [uint64](#uint64) | | account_number is the account number of the signing account in state | +| `sign_doc_sha256_hash` | [bytes](#bytes) | | sign_doc_sha256_hash is the SHA-256 hash of SignDoc. It is included here to reduce the malleability attack surface of SIGN_MODE_DIRECT_JSON vs SIGN_MODE_DIRECT to zero. Basically this means that any discrepancy between protobuf bytes over the wire and protobuf bytes that are signed cannot be exploited. This information is obviously redundant with information already in SignDocJSON, but is included as a security check for scenarios where this information may have inadvertently been excluded. We include the hash of SignDoc rather than the full SignDoc bytes to reduce the size of SignDocJSON for scenarios where large payloads could cause problems for hardware wallets. | + + + + + + ### SignerInfo diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index c37d813871c7..717013b7c4cf 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -8,13 +8,13 @@ order: 15 Read and understand all of the in-place store migration documentation before you run a migration on a live chain. ::: -Upgrade your app modules smoothly with custom in-place migration logic. {synopsis} +Upgrade your app modules smoothly with custom in-place store migration logic. {synopsis} The Cosmos SDK uses two methods to perform upgrades. -- Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See the [Chain Upgrade Guide](../migrations/chain-upgrade-guide-040.md#upgrade-procedure). +- Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See [Chain Upgrade Guide to v0.42](/v0.42/migrations/chain-upgrade-guide-040.html). -- Version v0.43 and later can perform upgrades in place to significantly decrease the upgrade time for chains with a larger state. Use the [Migration Upgrade Guide](../building-modules/upgrade.md) guide to set up your application modules to take advantage of in-place upgrades. +- Version v0.43 and later can perform upgrades in place to significantly decrease the upgrade time for chains with a larger state. Use the [Module Upgrade Guide](../building-modules/upgrade.md) to set up your application modules to take advantage of in-place upgrades. This document provides steps to use the In-Place Store Migrations upgrade method. @@ -48,13 +48,13 @@ The version map is a mapping of module names to consensus versions. The map is p Upgrades use an `UpgradeHandler` to facilitate migrations. The `UpgradeHandler` functions implemented by the app developer must conform to the following function signature. These functions retrieve the `VersionMap` from x/upgrade's state and return the new `VersionMap` to be stored in x/upgrade after the upgrade. The diff between the two `VersionMap`s determines which modules need upgrading. -```golang +```go type UpgradeHandler func(ctx sdk.Context, plan Plan, fromVM VersionMap) (VersionMap, error) ``` Inside these functions, you must perform any upgrade logic to include in the provided `plan`. All upgrade handler functions must end with the following line of code: -```golang +```go return app.mm.RunMigrations(ctx, cfg, fromVM) ``` @@ -62,7 +62,7 @@ Inside these functions, you must perform any upgrade logic to include in the pro Migrations are run inside of an `UpgradeHandler` using `app.mm.RunMigrations(ctx, cfg, vm)`. The `UpgradeHandler` functions describe the functionality to occur during an upgrade. The `RunMigration` function loops through the `VersionMap` argument and runs the migration scripts for all versions that are less than the versions of the new binary app module. After the migrations are finished, a new `VersionMap` is returned to persist the upgraded module versions to state. -```golang +```go cfg := module.NewConfigurator(...) app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { @@ -76,7 +76,7 @@ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgrad }) ``` -To learn more about configuring migration scripts for your modules, see the [Migration Upgrade Guide](../building-modules/upgrade.md). +To learn more about configuring migration scripts for your modules, see the [Module Upgrade Guide](../building-modules/upgrade.md). ## Adding New Modules During Upgrades @@ -86,7 +86,7 @@ You can introduce entirely new modules to the application during an upgrade. New All chains preparing to run in-place store migrations will need to manually add store upgrades for new modules and then configure the store loader to apply those upgrades. This ensures that the new module's stores are added to the multistore before the migrations begin. -```golang +```go upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() if err != nil { panic(err) diff --git a/docs/ibc/overview.md b/docs/ibc/overview.md index 6aca66899724..e6662ce9bb94 100644 --- a/docs/ibc/overview.md +++ b/docs/ibc/overview.md @@ -144,7 +144,7 @@ After an acknowledgement is received successfully on the original sender the cha To learn more about IBC, check out the following specifications: - [IBC specs](https://github.com/cosmos/ibc/tree/master/spec) -- [IBC protocol on the Cosmos SDK](https://github.com/cosmos/ibc-go/blob/main/docs/spec.md) +- [IBC protocol on the Cosmos SDK](https://github.com/cosmos/ibc-go/tree/main/docs) ## Next {hide} diff --git a/docs/migrations/app_and_modules.md b/docs/migrations/app_and_modules.md deleted file mode 100644 index 7c93a1f6d5b2..000000000000 --- a/docs/migrations/app_and_modules.md +++ /dev/null @@ -1,239 +0,0 @@ - - -# App and Modules Migration - -The following document describes the changes to update your app and modules from Cosmos SDK v0.39 to v0.40, -a.k.a. Stargate release. {synopsis} - -## Update Tooling - -Make sure to have the following dependencies before updating your app to v0.40: - -- Go 1.15+ -- Docker -- Node.js v12.0+ (optional, for generating Swagger docs) - -In Cosmos-SDK we manage the project using Makefile. Your own app can use a similar Makefile to the [Cosmos SDK's one](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/Makefile). More specifically, below are some _make_ commands that might be useful for your own app, related to the introduction of Protocol Buffers: - -- `proto-update-deps` - To download/update the required thirdparty `proto` definitions. -- `proto-gen` - To auto generate proto code. -- `proto-check-breaking` - To check proto breaking changes. -- `proto-format` - To format proto files. - -## Updating Modules - -This section outlines how to upgrade your module to v0.40. There is also a whole section about [building modules](../building-modules/README.md) from scratch, it might serve as a useful guide. - -### Protocol Buffers - -As outlined in our [encoding guide](../core/encoding.md), one of the most significant improvements introduced in Cosmos SDK v0.40 is Protobuf. - -The rule of thumb is that any object that needs to be serialized (into binary or JSON) must implement `proto.Message` and must be serializable into Protobuf format. The easiest way to do it is to use Protobuf type definition and `protoc` compiler to generate the structures and functions for you. In practice, the three following categories of types must be converted to Protobuf messages: - -- client-facing types: `Msg`s, query requests and responses. This is because client will send these types over the wire to your app. -- objects that are stored in state. This is because the SDK stores the binary representation of these types in state. -- genesis types. These are used when importing and exporting state snapshots during chain upgrades. - -Let's have a look at [x/auth's](../../x/auth/spec/README.md) `BaseAccount` objects, which are stored in a state. The migration looks like: - -```diff -// We were definining `MsgSend` as a Go struct in v0.39. -- // https://github.com/cosmos/cosmos-sdk/blob/v0.39.2/x/bank/internal/types/msgs.go#L12-L16 -- type BaseAccount struct { -- Address sdk.AccAddress `json:"address" yaml:"address"` -- Coins sdk.Coins `json:"coins" yaml:"coins"` -- PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` -- AccountNumber uint64 `json:"account_number" yaml:"account_number"` -- Sequence uint64 `json:"sequence" yaml:"sequence"` -- } - -// And it should be converted to a Protobuf message in v0.40. -+ // https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/auth/v1beta1/auth.proto#L13-L25 -+ message BaseAccount { -+ string address = 1; -+ google.protobuf.Any pub_key = 2 -+ [(gogoproto.jsontag) = "public_key,omitempty", (gogoproto.moretags) = "yaml:\"public_key\""]; -+ uint64 account_number = 3 [(gogoproto.moretags) = "yaml:\"account_number\""]; -+ uint64 sequence = 4; -+ } -} -``` - -In general, we recommend to put all the Protobuf definitions in your module's subdirectory under a root `proto/` folder, as described in [ADR-023](../architecture/adr-023-protobuf-naming.md). This ADR also contains other useful information on naming conventions. - -You might have noticed that the `PubKey` interface in v0.39's `BaseAccount` has been transformed into an `Any`. For storing interfaces, we use Protobuf's `Any` message, which is a struct that can hold arbitrary content. Please refer to the [encoding FAQ](../core/encoding.md#faq) to learn how to handle interfaces and `Any`s. - -Once all your Protobuf messages are defined, use the `make proto-gen` command defined in the [tooling section](#tooling) to generate Go structs. These structs will be generated into `*.pb.go` files. As a quick example, here is the generated Go struct for the Protobuf BaseAccount we defined above: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/auth/types/auth.pb.go#L28-L36 - -There might be some back and forth removing old Go structs/interfaces and defining new Protobuf messages before your Go app compiles and your tests pass. - -### Create `Msg` and `Query` Services - -Cosmos SDK v0.40 uses Protobuf services to define state transitions (`Msg`s) and state queries, please read [the building modules guide on those services](../building-modules/messages-and-queries.md) for an overview. - -#### `Msg` Service - -The handler pattern (inside the `handler.go` file) for handling `Msg`s is deprecated. You may still keep it if you wish to support `Msg`s defined in pre-Stargate versions of the SDK. However, it is strongly recommended to add a `Msg` service to your Protobuf files, and each old `Msg` handler should be converted into a service method. Taking [x/bank's](../../x/bank/spec/README.md) `MsgSend` as an example, we have a corresponding `/cosmos.bank.v1beta1.MsgSend` RPC: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/bank/v1beta1/tx.proto#L10-L31 - -A state transition is therefore modelized as a Protobuf service method, with a method request, and an (optionally empty) method response. - -After defining your `Msg` service, run the `make proto-gen` script again to generate `Msg` server interfaces. The name of this interface is simply `MsgServer`. The implementation of this interface should follow exactly the implementation of the old `Msg` handlers, which, in most cases, defers the actual state transition logic to the [keeper](../building-modules/keeper.md). You may implement a `MsgServer` directly on the keeper, or you can do it using a new struct (e.g. called `msgServer`) that references the module's keeper. - -For more information, please check our [`Msg` service guide](../building-modules/msg-services.md). - -#### `Query` Service - -The querier pattern (inside the `querier.go` file) for handling state queries is deprecated. You may still keep this file to support legacy queries, but it is strongly recommended to use a Protobuf `Query` service to handle state queries. - -Each query endpoint is now defined as a separate service method in the `Query` service. Still taking `x/bank` as an example, here are the queries to fetch an account's balances: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/bank/v1beta1/query.proto#L12-L23 - -Each query has its own `Request` and `Response` types. Please also note the `google.api.http` option (coming from [`grpc-gateway`](https://github.com/grpc-ecosystem/grpc-gateway)) on each service method. `grpc-gateway` is a tool that exposes `Query` service methods as REST endpoints. Adding this annotation will expose these endpoints not only as gRPC endpoints, but also as REST endpoints. An overview of gRPC versus REST can be found [here](../core/grpc_rest.md). - -After defining the `Query` Protobuf service, run the `make proto-gen` command to generate corresponding interfaces. The interface that needs to be implemented by your module is `QueryServer`. This interface can be implemented on the [keeper](../building-modules/keeper.md) directly, or on a struct (e.g. called `queryServer`) that references the module's keeper. The logic of the implementation, namely the logic that fetches data from the module's store and performs unmarshalling, can be deferred to the keeper. - -Cosmos SDK v0.40 also comes with an efficient pagination, it now uses `Prefix` stores to make queries. There are 2 helpers for pagination, [`Paginate`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/query/pagination.go#L40-L42), [`FilteredPaginate`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/types/query/filtered_pagination.go#L9-L17). - -For more information, please check our [`Query` service guide](../building-modules/query-services.md). - -#### Wiring up `Msg` and `Query` Services - -We added a new `RegisterServices` method that registers a module's `Msg` service and a `Query` service. It should be implemented by all modules, using a `Configurator` object: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L99-L103 - -If you wish to expose your `Query` endpoints as REST endpoints (as proposed in the [`Query` Services paragraph](#query-services)), make sure to also implement the `RegisterGRPCGatewayRoutes` method: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L69-L72 - -### Codec - -If you still use Amino (which is deprecated since Stargate), you must register related types using the `RegisterLegacyAminoCodec(cdc *codec.LegacyAmino)` method (previously it was called `RegisterCodec(cdc *codec.Codec)`). - -Moreover, a new `RegisterInterfaces` method has been added to the `AppModule` interface that all modules must implement. This method must register the interfaces that Protobuf messages implement, as well as the service request `Msg`s used in the module. An example from x/bank is given below: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/types/codec.go#L21-L34 - -### Keeper - -The `Keeper` constructor now takes a `codec.Codec` instead of a concrete Amino codec. This `codec.Codec` is used to encode types as binary and save the bytes into the state. With an interface, you can define `codec.Codec` to be Amino or Protobuf on an app level, and keepers will use that encoding library to encode state. Please note that Amino is deprecated in v0.40, and we strongly recommend to use Protobuf. Internally, the SDK still uses Amino in a couple of places (legacy REST API, x/params, keyring...), but Amino is planned to be removed in a future release. - -Keeping Amino for now can be useful if you wish to update to SDK v0.40 without doing a chain upgrade with a genesis migration. This will not require updating the stores, so you can migrate to Cosmos SDK v0.40 with less effort. However, as Amino will be removed in a future release, the chain migration with genesis export/import will need to be performed then. - -Related to the keepers, each module's `AppModuleBasic` now also includes this `codec.Codec`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/module.go#L35-L38 - -### CLI - -Each modules may optionally expose CLI commands, and some changes are needed in these commands. - -First, `context.CLIContext` is renamed to `client.Context` and moved to `github.com/cosmos/cosmos-sdk/client`. - -Second, the global `viper` usage is removed from client and is replaced with Cobra' `cmd.Flags()`. There are two helpers to read common flags for CLI txs and queries: - -```go -clientCtx, err := client.GetClientQueryContext(cmd) -clientCtx, err := client.GetClientTxContext(cmd) -``` - -Some other flags helper functions are transformed: `flags.PostCommands(cmds ...*cobra.Command) []*cobra.Command` and `flags.GetCommands(...)` usage is now replaced by `flags.AddTxFlagsToCmd(cmd *cobra.Command)` and `flags.AddQueryFlagsToCmd(cmd *cobra.Command)` respectively. - -Moreover, new CLI commands don't take any codec as input anymore. Instead, the `clientCtx` can be retrieved from the `cmd` itself using the `GetClient{Query,Tx}Context` function above, and the codec as `clientCtx.Codec`. - -```diff -// v0.39 -- func SendTxCmd(cdc *codec.Codec) *cobra.Command { -- cdc.MarshalJSON(...) -- } - -// v0.43 -+ func NewSendTxCmd() *cobra.Command { -+ clientCtx, err := client.GetClientTxContext(cmd) -+ clientCtx.Codec.MarshalJSON(...) -+} -``` - -Finally, once your [`Query` services](#query-service) are wired up, the CLI commands should preferably use gRPC to communicate with the node. The gist is to create a `Query` or `Msg` client using the command's `clientCtx`, and perform the request using Protobuf's generated code. An example for querying x/bank balances is given here: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/x/bank/client/cli/query.go#L66-L94 - -### Miscelleanous - -A number of other smaller breaking changes are also noteworthy. - -| Before | After | Comment | -| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `alias.go` file | Removed | `alias` usage is removed, please see [#6311](https://github.com/cosmos/cosmos-sdk/issues/6311) for details. | -| `codec.New()` | `codec.NewLegacyAmino()` | Simple rename. | -| `DefaultGenesis()` | `DefaultGenesis(cdc codec.JSONCodec)` | `DefaultGenesis` takes a codec argument now | -| `ValidateGenesis()` | `ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage)` | `ValidateGenesis` now requires `Marshaler`, `TxEncodingConfig`, `json.RawMessage` as input. | -| `Route() string` | `Route() sdk.Route` | For legacy handlers, return type of `Route()` method is changed from `string` to `"github.com/cosmos/cosmos-sdk/types".Route`. It should return a `NewRoute()` which includes `RouterKey` and `NewHandler` as params. | -| `QuerierHandler` | `LegacyQuerierHandler` | Simple rename. | -| `InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate` | InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate | `InitGenesis` now takes a codec input. | -| `ExportGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate` | ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate | `ExportGenesis` now takes a codec input. | - -## Updating Your App - -For a reference implementation used for demo purposes, you can refer to the SDK's [SimApp](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc6/simapp) for your app's migration. The most important changes are described in this section. - -### Creating Codecs - -With the introduction of Protobuf, each app needs to define the encoding library (Amino or Protobuf) to be used throughout the app. There is a central struct, [`EncodingConfig`](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/params/encoding.go#L9-L11), which defines all information necessary for codecs. In your app, an example `EncodingConfig` with Protobuf as default codec might look like: - -```go -// MakeEncodingConfig creates an EncodingConfig -func MakeEncodingConfig() params.EncodingConfig { - amino := codec.NewLegacyAmino() - interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) - - encodingConfig := params.EncodingConfig{ - InterfaceRegistry: interfaceRegistry, - Marshaler: marshaler, - TxConfig: txCfg, - Amino: amino, - } - std.RegisterLegacyAminoCodec(encodingConfig.Amino) - std.RegisterInterfaces(encodingConfig.InterfaceRegistry) - ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) - ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) - - return encodingConfig -} -``` - -These codecs are used to populate the following fields on your app (again, we are using SimApp for demo purposes): - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L146-L153 - -As explained in the [modules migration section](#updating-modules), some functions and structs in modules require an additional `codec.Codec` argument. You should pass `app.appCodec` in these cases, and this will be the default codec used throughout the app. - -### Registering Non-Module Protobuf Services - -We described in the [modules migration section](#updating-modules) `Query` and `Msg` services defined in each module. The SDK also exposes two more module-agnostic services: - -- the [Tx Service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/tx/v1beta1/service.proto), to perform operations on transactions, -- the [Tendermint service](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/proto/cosmos/base/tendermint/v1beta1/query.proto), to have a more idiomatic interface to the [Tendermint RPC](https://docs.tendermint.com/master/rpc/). - -These services are optional. To use them, or if you wish to add more module-agnostic Protobuf services into your app, you need to register them inside `app.go`: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L577-L585 - -### Registering `grpc-gateway` Routes - -The exising `RegisterAPIRoutes` method on the `app` only registers [Legacy API routes](../core/grpc_rest.md#legacy-rest-api-routes). If you are using `grpc-gateway` REST endpoints as described [above](#query-service), then these endpoints need to be wired up to a HTTP server: - -+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc6/simapp/app.go#L555-L575 - -## Next {hide} - -Learn how to perform a [chain upgrade](./chain-upgrade-guide-040.md) to 0.40. diff --git a/docs/migrations/chain-upgrade-guide-040.md b/docs/migrations/chain-upgrade-guide-040.md deleted file mode 100644 index 7a2848d3e7d3..000000000000 --- a/docs/migrations/chain-upgrade-guide-040.md +++ /dev/null @@ -1,166 +0,0 @@ - - -# Chain Upgrade Guide to v0.42 - -This document explains how to perform a chain upgrade from v0.39 to v0.42. {synopsis} - -::: tip -Please note that the three SDK versions v0.40, v0.41 and v0.42 are functionally equivalent, together called the "Stargate" series. The version bumps are consequences of post-release state-breaking bugfixes. -::: - -## Risks - -As a validator, performing the upgrade procedure on your consensus nodes carries a heightened risk of double-signing and -being slashed: if your validator node votes for a block, and, in the same block time, restarts the upgraded node, this may lead to double-voting on a block. - -The riskiest thing a validator can do is to discover that they made a mistake and repeat the upgrade procedure again during -the network startup. If you discover a mistake in the process, the best thing to do is wait for the network to start -before correcting it. If the network is halted and you have started with a different genesis file than the expected one, -seek advice from the validator community. - -## Recovery - -- Prior to exporting the state, the validators are encouraged to take a full data snapshot at exported height. Exported - height will be determined by a governance proposal. Data backup is usually done by copying daemon home directory, - e.g.: `~/.simd` - -**Note:** we use "simd" as our app throughout this doc, be sure to replace with the name of your own binary. - -It is critically important to back-up the validator state file, e.g.: `~/.simd/data/priv_validator_state.json` file -after stopping your daemon process. This file is updated every block as your validator participates in a consensus -rounds. It is a critical file needed to prevent double-signing, in case the upgrade fails, and the previous chain needs -to be restarted. - -In the event that the upgrade does not succeed, validators and operators must downgrade back to old version of the -software and restore to their latest snapshot before restarting their nodes. - -## Upgrade procedure - -1. The procedure is to export the state from the old binary, and import it with the new binary. First, verify your old binary version (which should use `github.com/cosmos/cosmos-sdk@0.39.*`) before exporting the state. - - ```shell - simd version --long - ``` - -1. Export the state from existing chain using the old binary. - - ```shell - simd export --for-zero-height --height > v039_exported_state.json - ``` - -1. Verify the SHA256 of the (sorted) exported genesis file: - - ```shell - $ jq -S -c -M '' v039_exported_state.json | shasum -a 256 - [SHASUM_PLACEHOLDER] v039_exported_state.json - ``` - -1. Cross check the hash with other peers (other validators) in the chat rooms. - -1. Install the latest binary (which uses `github.com/cosmos/cosmos-sdk@0.40.*`). - -1. Migrate the exported state to `github.com/cosmos/cosmos-sdk@0.40.*` compatible genesis state. - - ```shell - simd migrate v0.42 v039_exported_state.json --chain-id --genesis-time > new_v042_genesis.json - ``` - - **Note:** The migrate command takes an input genesis state and migrates it to a targeted version. New `genesis-time` is usually mentioned in the governance proposal, and should be passed as flag argument. If the flag is omitted, then the genesis time of the upgraded chain will be the same as the old one, which may cause confusion. - -1. All the necessary state changes are handled in the `simd migrate v0.42` migration command. However, Tendermint parameters are **not** handled in this command. You might need to update these parameters manually. - - In the recent versions of Tendermint, the following changes have been made: - - - `consensus_params.evidence.max_num` has been renamed to `consensus_params.evidence.max_bytes`. - - `consensus_params.evidence.max_age` has been removed, and replaced by `consensus_params.evidence.max_age_duration` and `consensus_params.evidence.max_age_num_blocks`. - - Make sure that your genesis JSON files contains the correct values specific to your chain. If the `simd migrate` errors with a message saying that the genesis file cannot be parsed, these are the fields to check first. - -1. Verify the SHA256 of the migrated genesis file with other validators to make sure there are no manual errors in the process. - - ```shell - $ jq -S -c -M '' new_v042_genesis.json | shasum -a 256 - [SHASUM_PLACEHOLDER] new_v042_genesis.json - ``` - -1. Make sure to update the genesis parameters in the new genesis if any. All these details will be generally present in - the governance proposal. - -1) If your chain is using IBC, make sure to add IBC initial genesis state to the genesis file. You can use the following command to add IBC initial genesis state to the genesis file. - - ```shell - cat new_v042_genesis.json | jq '.app_state |= . + {"ibc":{"client_genesis":{"clients":[],"clients_consensus":[],"create_localhost":false},"connection_genesis":{"connections":[],"client_connection_paths":[]},"channel_genesis":{"channels":[],"acknowledgements":[],"commitments":[],"receipts":[],"send_sequences":[],"recv_sequences":[],"ack_sequences":[]}},"transfer":{"port_id":"transfer","denom_traces":[],"params":{"send_enabled":false,"receive_enabled":false}},"capability":{"index":"1","owners":[]}}' > tmp_genesis.json && mv tmp_genesis.json new_v042_genesis.json - ``` - - **Note:** This would add IBC state with IBC's `send_enabled: false` and `receive_enabled: false`. Make sure to update them to `true` in the above command if are planning to enable IBC transactions with chain upgrade. Otherwise you can do it via a governance proposal. - -1) Reset the old state. - - **Note:** Be sure you have a complete backed up state of your node before proceeding with this step. - See Recovery for details on how to proceed. - - ```shell - simd unsafe-reset-all - ``` - -1) Move the new genesis.json to your daemon config directory. Ex - - ```shell - cp new_v042_genesis.json ~/.simd/config/genesis.json - ``` - -1) Update `~/.simd/config/app.toml` to include latest app configurations. [Here is the link](https://github.com/cosmos/cosmos-sdk/blob/v0.42.0-rc6/server/config/toml.go#L11-L164) to the default template for v0.42's `app.toml`. Make sure to - update your custom configurations as per your validator design, e.g. `gas_price`. - - Compared to v0.39, some notable updates to `app.toml` are: - - - API server is now configured to run in-process with daemon, previously it was a separate process, invoked by running rest-server - command i.e., `gaiacli rest-server`. Now it is in-process with daemon and can be enabled/disabled by API configuration: - - ```yaml - [api] - # Enable defines if the API server should be enabled. - enable = false - # Swagger defines if swagger documentation should automatically be registered. - swagger = false - ``` - - `swagger` setting refers to enabling/disabling swagger docs API, i.e, `/swagger/` API endpoint. - - - gRPC Configuration - - ```yaml - [grpc] - # Enable defines if the gRPC server should be enabled. - enable = true - # Address defines the gRPC server address to bind to. - address = "0.0.0.0:9090" - ``` - - - State Sync Configuration - - ```yaml - # State sync snapshots allow other nodes to rapidly join the network without replaying historical - # blocks, instead downloading and applying a snapshot of the application state at a given height. - [state-sync] - # snapshot-interval specifies the block interval at which local state sync snapshots are - # taken (0 to disable). Must be a multiple of pruning-keep-every. - snapshot-interval = 0 - # snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all). - snapshot-keep-recent = 2 - ``` - -1) Kill if any external `rest-server` process is running. - -1) All set now! You can (re)start your daemon to validate on the upgraded network. Make sure to check your binary version - before starting the daemon: - - ``` - simd version --long - ``` - -## Next {hide} - -Once your chain is upgraded, make sure to [update your clients' REST endpoints](./rest.md). diff --git a/docs/migrations/chain-upgrade-guide-043.md b/docs/migrations/chain-upgrade-guide-043.md new file mode 100644 index 000000000000..129d22314c59 --- /dev/null +++ b/docs/migrations/chain-upgrade-guide-043.md @@ -0,0 +1,237 @@ + + +# Chain Upgrade Guide to v0.43 + +This document provides guidelines for a chain upgrade from v0.42 to v0.43 and an example of the upgrade process using `simapp`. {synopsis} + +::: tip +You must upgrade to Stargate v0.42 before upgrading to v0.43. If you have not done so, please see [Chain Upgrade Guide to v0.42](/v0.42/migrations/chain-upgrade-guide-040.html). +::: + +## Prerequisite Readings + +- [Upgrading Modules](../building-modules/upgrade.html) {prereq} +- [In-Place Store Migrations](../core/upgrade.html) {prereq} +- [Cosmovisor](../run-node/cosmovisor.html) {prereq} + +Cosmos SDK v0.43 introduces a new way of handling chain upgrades that no longer requires exporting state to JSON, making the necesssary changes, and then creating a new chain with the modified JSON as the new genesis file. + +Instead of starting a new chain, the upgrade binary will read the existing database and perform in-place store migrations. This new way of handling chain upgrades can be used alongside [Cosmovisor](../run-node/cosmovisor.html) to make the upgrade process seamless. + +## In-Place Store Migrations + +We recommend using [In-Place Store Migrations](../core/upgrade.html) to upgrade your chain from v0.42 to v0.43. The first step is to make sure all your modules follow the [Module Upgrade Guide](../building-modules/upgrade.html). The second step is to add an [upgrade handler](../core/upgrade.html#running-migrations) to `app.go`. + +In this document, we'll provide an example of what the upgrade handler looks like for a chain upgrading module versions for the first time. It's critical to note that the initial version of each module must be set to `1` rather than `0` or else the upgrade handler will re-initialize each module. + +In addition to migrating existing modules, the upgrade handler also performs store upgrades for new modules. In the example below, we'll be adding store migrations for two new modules made available in v0.43: `x/authz` and `x/feegrant`. + +## Using Cosmovisor + +We recommend validators use [Cosmovisor](../run-node/cosmovisor.html), which is a process manager for running application binaries. For security reasons, we recommend validators build their own upgrade binaries rather than enabling the auto-download option. Validators may still choose to use the auto-download option if the necessary security guarantees are in place (i.e. the URL provided in the upgrade proposal for the downloadable upgrade binary includes a proper checksum). + +Validators can use the auto-restart option to prevent unecessary downtime during the upgrade process. The auto-restart option will automatically restart the chain with the upgrade binary once the chain has halted at the proposed upgrade height. With the auto-restart option, validators can prepare the upgrade binary in advance and then relax at the time of the upgrade. + +## Migrating app.toml + +With the update to `v0.43`, new server configuration options have been added to `app.toml`. The updates include new configuration sections for Rosetta and gRPC Web as well as a new configuration option for State Sync. Check out the default [`app.toml`](https://github.com/cosmos/cosmos-sdk/blob/release/v0.43.x/server/config/toml.go) file in the latest version of `v0.43` for more information. + +## Example: Simapp Upgrade + +The following example will walk through the upgrade process using `simapp` as our blockchain application. We will be upgrading `simapp` from v0.42 to v0.43. We will be building the upgrade binary ourselves and enabling the auto-restart option. + +*Note: In this example, we will be starting a new chain from `v0.42`. The binary for this version will be the genesis binary. For validators using Cosmovisor for the first time, the binary for the current version of the chain should be used as the genesis binary (i.e. the starting binary). For more information, see [Cosmovisor](../run-node/cosmovisor.html).* + +### Initial Setup + +From within the `cosmos-sdk` repository, check out the latest `v0.42.x` release: + +``` +git checkout release/v0.42.x +``` + +Build the `simd` binary for the latest `v0.42.x` release (the genesis binary): + +``` +make build +``` + +Reset `~/.simapp` (never do this in a production environment): + +``` +./build/simd unsafe-reset-all +``` + +Configure the `simd` binary for testing: + +``` +./build/simd config chain-id test +./build/simd config keyring-backend test +./build/simd config broadcast-mode block +``` + +Initialize the node and overwrite any previous genesis file (never do this in a production environment): + + + +``` +./build/simd init test --chain-id test --overwrite +``` + +Set the minimum gas price to `0stake` in `~/.simapp/config/app.toml`: + +``` +minimum-gas-prices = "0stake" +``` + +For the purpose of this demonstration, change `voting_period` in `genesis.json` to a reduced time of 20 seconds (`20s`): + +``` +cat <<< $(jq '.app_state.gov.voting_params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json +``` + +Create a new key for the validator, then add a genesis account and transaction: + + + + +``` +./build/simd keys add validator +./build/simd add-genesis-account validator 5000000000stake --keyring-backend test +./build/simd gentx validator 1000000stake --chain-id test +./build/simd collect-gentxs +``` + +Now that our node is initialized and we are ready to start a new `simapp` chain, let's set up `cosmovisor` and the genesis binary. + +### Cosmovisor Setup + +First, install or update `cosmovisor`: + +``` +go get github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor +``` + +Set the required environment variables: + +``` +export DAEMON_NAME=simd +export DAEMON_HOME=$HOME/.simapp +``` + +Set the optional environment variable to trigger an automatic restart: + +``` +export DAEMON_RESTART_AFTER_UPGRADE=true +``` + +Create the folder for the genesis binary and copy the `v0.42.x` binary: + +``` +mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin +cp ./build/simd $DAEMON_HOME/cosmovisor/genesis/bin +``` + +Now that `cosmovisor` is installed and the genesis binary has been added, let's add the upgrade handler to `simapp/app.go` and prepare the upgrade binary. + +### Chain Upgrade + + + +Check out `release/v0.43.x`: + +``` +git checkout release/v0.43.x +``` + +Add the following to `simapp/app.go` inside `NewSimApp` and after `app.UpgradeKeeper`: + +```go + app.registerUpgradeHandlers() +``` + +Add the following to `simapp/app.go` after `NewSimApp` (to learn more about the upgrade handler, see the [In-Place Store Migrations](../core/upgrade.html)): + +```go +func (app *SimApp) registerUpgradeHandlers() { + app.UpgradeKeeper.SetUpgradeHandler("v0.43", func(ctx sdk.Context, plan upgradetypes.Plan, _ module.VersionMap) (module.VersionMap, error) { + // 1st-time running in-store migrations, using 1 as fromVersion to + // avoid running InitGenesis. + fromVM := map[string]uint64{ + "auth": 1, + "bank": 1, + "capability": 1, + "crisis": 1, + "distribution": 1, + "evidence": 1, + "gov": 1, + "mint": 1, + "params": 1, + "slashing": 1, + "staking": 1, + "upgrade": 1, + "vesting": 1, + "ibc": 1, + "genutil": 1, + "transfer": 1, + } + + return app.mm.RunMigrations(ctx, app.configurator, fromVM) + }) + + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(err) + } + + if upgradeInfo.Name == "v0.43" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{"authz", "feegrant"}, + } + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } +} +``` + +Add `storetypes` to imports: + +```go + storetypes "github.com/cosmos/cosmos-sdk/store/types" +``` + +Build the `simd` binary for `v0.43.x` (the upgrade binary): + +``` +make build +``` + +Create the folder for the upgrade binary and copy the `v0.43.x` binary: + +``` +mkdir -p $DAEMON_HOME/cosmovisor/upgrades/v0.43/bin +cp ./build/simd $DAEMON_HOME/cosmovisor/upgrades/v0.43/bin +``` + +Now that we have added the upgrade handler and prepared the upgrade binary, we are ready to start `cosmovisor` and simulate the upgrade proposal process. + +### Upgrade Proposal + +Start the node using `cosmovisor`: + +``` +cosmovisor start +``` + +Open a new terminal window and submit an upgrade proposal along with a deposit and a vote (these commands must be run within 20 seconds of each other): + +``` +./build/simd tx gov submit-proposal software-upgrade v0.43 --title upgrade --description upgrade --upgrade-height 20 --from validator --yes +./build/simd tx gov deposit 1 10000000stake --from validator --yes +./build/simd tx gov vote 1 yes --from validator --yes +``` + +Confirm the chain automatically upgrades at height 20. diff --git a/docs/migrations/keyring.md b/docs/migrations/keyring.md deleted file mode 100644 index 4fe3a3ebc03f..000000000000 --- a/docs/migrations/keyring.md +++ /dev/null @@ -1,33 +0,0 @@ - - -# Keyring Migrate Quick Start - -`keyring` is the Cosmos SDK mechanism to manage the public/private keypair. Cosmos SDK v0.42 (Stargate) introduced breaking changes in the keyring. - -To upgrade your chain from v0.39 (Launchpad) and earlier to Stargate, you must migrate your keys inside the keyring to the latest version. For details on configuring and using the keyring, see [Setting up the keyring](../run-node/keyring.md). - -This guide describes how to migrate your keyrings. - -The following command migrates your keyrings: - -```bash -Usage -simd keys migrate -``` - -The migration process moves key information from the legacy db-based Keybase to the [keyring](https://github.com/99designs/keyring)-based Keyring. The legacy Keybase persists keys in a LevelDB database in a 'keys' sub-directory of the client application home directory (`old_home_dir`). For example, `$HOME/.gaiacli/keys/` for [Gaia](https://github.com/cosmos/gaia). - -You can migrate or skip the migration for each key entry found in the specified `old_home_dir` directory. Each key migration requires a valid passphrase. If an invalid passphrase is entered, the command exits. Run the command again to restart the keyring migration. - -The `migrate` command takes the following flags: - -- `--dry-run` boolean - - - true - run the migration but do not persist changes to the new Keybase. - - false - run the migration and persist keys to the new Keybase. - -Recommended: Use `--dry-run true` to test the migration without persisting changes before you migrate and persist keys. - -- `--keyring-backend` string flag. It allows you to select a backend. For more detailed information about the available backends, you can read [the keyring guide](../run-node/keyring.md). diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md index 93f890a7718b..9978974a2730 100644 --- a/docs/migrations/rest.md +++ b/docs/migrations/rest.md @@ -1,14 +1,14 @@ # REST Endpoints Migration -Migrate your REST endpoints to the Stargate ones. {synopsis} +Migrate your REST endpoints. Legacy REST endpoints have been deprecated since v0.40 and they will be removed in v0.44. {synopsis} ## Deprecation of Legacy REST Endpoints -The Cosmos SDK versions v0.39 and earlier provided REST endpoints to query the state and broadcast transactions. These endpoints are kept in Cosmos SDK v0.40 (Stargate), but they are marked as deprecated, and will be removed in v0.41. We therefore call these endpoints legacy REST endpoints. +The Cosmos SDK versions v0.39 and earlier provided REST endpoints to query the state and broadcast transactions. These endpoints were kept in Cosmos SDK v0.40 and they are still available in v0.43, but they are marked as deprecated and will be removed in v0.44. We therefore call these endpoints legacy REST endpoints. Some important information concerning all legacy REST endpoints: @@ -32,9 +32,9 @@ Some important information concerning all legacy REST endpoints: ## Migrating to New REST Endpoints -Thanks to the Protocol Buffers migration in v0.40, we are able to take advantage of a vast number of gRPC tools and solutions. For most of the legacy REST endpoints, Cosmos SDK v0.40 provides new REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gRPC-gateway REST endpoints_. +Thanks to the Protocol Buffers migration in v0.40, we are able to take advantage of a vast number of gRPC tools and solutions. Most of the legacy REST endpoints have been replaced by REST endpoints generated from [gRPC `Query` services](../building-modules/query-services.md) using [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/). We usually call them _gRPC-gateway REST endpoints_. -Some modules expose legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints have been removed. We recommend to use [service `Msg`s](../building-modules/msg-services.md) directly, and use Protobuf to do client-side transaction generation. A guide can be found [here](../run-node/txs.md). +Previously, some modules exposed legacy `POST` endpoints to generate unsigned transactions for their `Msg`s. These `POST` endpoints were removed in v0.40. We recommend using [Protobuf `Msg` service](../building-modules/msg-services.md) directly to do client-side transaction generation. A guide can be found [here](../run-node/txs.md). | Legacy REST Endpoint | Description | New gRPC-gateway REST Endpoint | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | diff --git a/docs/run-node/README.md b/docs/run-node/README.md index 27ee9726508a..995882c0b691 100644 --- a/docs/run-node/README.md +++ b/docs/run-node/README.md @@ -9,8 +9,9 @@ parent: This folder contains documentation on how to run a node and interact with it. 1. [Setting up the keyring](./keyring.md) -1. [Running a Node](./run-node.md) -1. [Interacting with a Node](./interact-node.md) -1. [Generating, Signing and Broadcasting Transactions](./txs.md) -1. [Cosmos Upgrade Manager](./cosmovisor.md) -1. [Rosetta API](./rosetta.md) +2. [Running a Node](./run-node.md) +3. [Interacting with a Node](./interact-node.md) +4. [Generating, Signing and Broadcasting Transactions](./txs.md) +5. [Cosmos Upgrade Manager](./cosmovisor.md) +6. [Rosetta API](./rosetta.md) +7. [Running a Testnet](./run-testnet.md) diff --git a/docs/run-node/cosmovisor.md b/docs/run-node/cosmovisor.md index 6c54eb9e5c22..fb4d57402b8a 100644 --- a/docs/run-node/cosmovisor.md +++ b/docs/run-node/cosmovisor.md @@ -1,3 +1,7 @@ + + # Cosmosvisor Quick Start `cosmovisor` is a small process manager around Cosmos SDK binaries that monitors the governance module via stdout to see if there's a chain upgrade proposal coming in. If it see a proposal that gets approved it can be run manually or automatically to download the new code, stop the node, run the migration script, replace the node binary, and start with the new genesis file. diff --git a/docs/run-node/rosetta.md b/docs/run-node/rosetta.md index 98121867cfb7..398d9a9f47e5 100644 --- a/docs/run-node/rosetta.md +++ b/docs/run-node/rosetta.md @@ -1,3 +1,7 @@ + + # Rosetta Package rosetta implements the rosetta API for the current cosmos sdk release series. diff --git a/docs/run-node/run-testnet.md b/docs/run-node/run-testnet.md new file mode 100644 index 000000000000..3f8478b4689d --- /dev/null +++ b/docs/run-node/run-testnet.md @@ -0,0 +1,99 @@ + + +# Running a Testnet + +The `simd testnet` subcommand makes it easy to initialize and start a simulated test network for testing purposes. {synopsis} + +In addition to the commands for [running a node](./run-node.html), the `simd` binary also includes a `testnet` command that allows you to start a simulated test network in-process or to initialize files for a simulated test network that runs in a separate process. + +## Initialize Files + +First, let's take a look at the `init-files` subcommand. + +This is similar to the `init` command when initializing a single node, but in this case we are initializing multiple nodes, generating the genesis transactions for each node, and then collecting those transactions. + +The `init-files` subcommand initializes the necessary files to run a test network in a separate process (i.e. using a Docker container). Running this command is not a prerequisite for the `start` subcommand ([see below](#start-testnet)). + +In order to initialize the files for a test network, run the following command: + +```bash +simd testnet init-files +``` + +You should see the following output in your terminal: + +```bash +Successfully initialized 4 node directories +``` + +The default output directory is a relative `.testnets` directory. Let's take a look at the files created within the `.testnets` directory. + +### gentxs + +The `gentxs` directory includes a genesis transaction for each validator node. Each file includes a JSON encoded genesis transaction used to register a validator node at the time of genesis. The genesis transactions are added to the `genesis.json` file within each node directory during the initilization process. + +### nodes + +A node directory is created for each validator node. Within each node directory is a `simd` directory. The `simd` directory is the home directory for each node, which includes the configuration and data files for that node (i.e. the same files included in the default `~/.simapp` directory when running a single node). + +## Start Testnet + +Now, let's take a look at the `start` subcommand. + +The `start` subcommand both initializes and starts an in-process test network. This is the fastest way to spin up a local test network for testing purposes. + +You can start the local test network by running the following command: + +```bash +simd testnet start +``` + +You should see something similar to the following: + +```bash +acquiring test network lock +preparing test network with chain-id "chain-mtoD9v" + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ THIS MNEMONIC IS FOR TESTING PURPOSES ONLY ++ +++ DO NOT USE IN PRODUCTION ++ +++ ++ +++ sustain know debris minute gate hybrid stereo custom ++ +++ divorce cross spoon machine latin vibrant term oblige ++ +++ moment beauty laundry repeat grab game bronze truly ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + +starting test network... +started test network +press the Enter Key to terminate +``` + +The first validator node is now running in-process, which means the test network will terminate once you either close the terminal window or you press the Enter key. In the output, the mnemonic phrase for the first validator node is provided for testing purposes. The validator node is using the same default addresses being used when initializing and starting a single node (no need to provide a `--node` flag). + +Check the status of the first validator node: + +``` +simd status +``` + +Import the key from the provided mnemonic: + +``` +simd keys add test --recover --keyring-backend test +``` + +Check the balance of the account address: + +``` +simd q bank balances [address] +``` + +Use this test account to manually test against the test network. + +## Testnet Options + +You can customize the configuration of the test network with flags. In order to see all flag options, append the `--help` flag to each command. diff --git a/go.mod b/go.mod index 451f1688ca6a..f336f1a982f2 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 github.com/improbable-eng/grpc-web v0.14.0 - github.com/jhump/protoreflect v1.8.2 + github.com/jhump/protoreflect v1.9.0 github.com/magiconair/properties v1.8.5 github.com/mattn/go-isatty v0.0.13 github.com/otiai10/copy v1.6.0 diff --git a/go.sum b/go.sum index 4fb6d7ceebe1..d0a67c8a1cbe 100644 --- a/go.sum +++ b/go.sum @@ -442,8 +442,8 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.8.2 h1:k2xE7wcUomeqwY0LDCYA16y4WWfyTcMx5mKhk0d4ua0= -github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= diff --git a/proto/cosmos/tx/signing/v1beta1/signing.proto b/proto/cosmos/tx/signing/v1beta1/signing.proto index 4c1be40594d1..58d33e709d60 100644 --- a/proto/cosmos/tx/signing/v1beta1/signing.proto +++ b/proto/cosmos/tx/signing/v1beta1/signing.proto @@ -9,20 +9,25 @@ option go_package = "github.com/cosmos/cosmos-sdk/types/tx/signing"; // SignMode represents a signing mode with its own security guarantees. enum SignMode { // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be - // rejected + // rejected. SIGN_MODE_UNSPECIFIED = 0; // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is - // verified with raw bytes from Tx + // verified with raw bytes from Tx. SIGN_MODE_DIRECT = 1; // SIGN_MODE_TEXTUAL is a future signing mode that will verify some // human-readable textual representation on top of the binary representation - // from SIGN_MODE_DIRECT + // from SIGN_MODE_DIRECT. It is currently not supported. SIGN_MODE_TEXTUAL = 2; + // SIGN_MODE_DIRECT_JSON specifies a signing mode which uses SignDocJSON. It + // is verified using a canonical JSON representation of the bytes used in + // SIGN_MODE_DIRECT. + SIGN_MODE_DIRECT_JSON = 3; + // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses - // Amino JSON and will be removed in the future + // Amino JSON and will be removed in the future. SIGN_MODE_LEGACY_AMINO_JSON = 127; } diff --git a/proto/cosmos/tx/v1beta1/tx.proto b/proto/cosmos/tx/v1beta1/tx.proto index 6d5caf12c71c..6d00d92cfe59 100644 --- a/proto/cosmos/tx/v1beta1/tx.proto +++ b/proto/cosmos/tx/v1beta1/tx.proto @@ -63,6 +63,39 @@ message SignDoc { uint64 account_number = 4; } +// SignDocJSON is the type used for generating sign bytes for +// SIGN_MODE_DIRECT_JSON. It is designed to be serialized as proto3 JSON +// following the rules defined here: +// https://github.com/regen-network/canonical-proto3/blob/master/README.md#json. +message SignDocJSON { + // body is the processable content of the transaction + TxBody body = 1; + + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo auth_info = 2; + + // chain_id is the identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + string chain_id = 3; + + // account_number is the account number of the signing account in state + uint64 account_number = 4; + + // sign_doc_sha256_hash is the SHA-256 hash of SignDoc. It is included here to + // reduce the malleability attack surface of SIGN_MODE_DIRECT_JSON vs + // SIGN_MODE_DIRECT to zero. Basically this means that any discrepancy between + // protobuf bytes over the wire and protobuf bytes that are signed cannot be + // exploited. This information is obviously redundant with information already + // in SignDocJSON, but is included as a security check for scenarios where + // this information may have inadvertently been excluded. We include the hash + // of SignDoc rather than the full SignDoc bytes to reduce the size of + // SignDocJSON for scenarios where large payloads could cause problems for + // hardware wallets. + bytes sign_doc_sha256_hash = 5; +} + // TxBody is the body of a transaction that all signers sign over. message TxBody { // messages is a list of messages to be executed. The required signers of diff --git a/server/grpc/grpc_web.go b/server/grpc/grpc_web.go index 593779835a78..e08f007c73f0 100644 --- a/server/grpc/grpc_web.go +++ b/server/grpc/grpc_web.go @@ -1,12 +1,15 @@ package grpc import ( + "fmt" "net/http" + "time" "github.com/improbable-eng/grpc-web/go/grpcweb" "google.golang.org/grpc" "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/server/types" ) // StartGRPCWeb starts a gRPC-Web server on the given address. @@ -21,15 +24,22 @@ func StartGRPCWeb(grpcSrv *grpc.Server, config config.Config) (*http.Server, err } wrappedServer := grpcweb.WrapServer(grpcSrv, options...) - handler := func(resp http.ResponseWriter, req *http.Request) { - wrappedServer.ServeHTTP(resp, req) - } grpcWebSrv := &http.Server{ Addr: config.GRPCWeb.Address, - Handler: http.HandlerFunc(handler), + Handler: wrappedServer, } - if err := grpcWebSrv.ListenAndServe(); err != nil { + + errCh := make(chan error) + go func() { + if err := grpcWebSrv.ListenAndServe(); err != nil { + errCh <- fmt.Errorf("[grpc] failed to serve: %w", err) + } + }() + + select { + case err := <-errCh: return nil, err + case <-time.After(types.ServerStartTime): // assume server started successfully + return grpcWebSrv, nil } - return grpcWebSrv, nil } diff --git a/server/grpc/server.go b/server/grpc/server.go index 5bc146d8d12d..40a3c7716d97 100644 --- a/server/grpc/server.go +++ b/server/grpc/server.go @@ -54,7 +54,7 @@ func StartGRPCServer(clientCtx client.Context, app types.Application, address st select { case err := <-errCh: return nil, err - case <-time.After(5 * time.Second): // assume server started successfully + case <-time.After(types.ServerStartTime): // assume server started successfully return grpcSrv, nil } } diff --git a/server/start.go b/server/start.go index 8257f5d8f594..055ffa43ddb3 100644 --- a/server/start.go +++ b/server/start.go @@ -312,7 +312,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App select { case err := <-errCh: return err - case <-time.After(5 * time.Second): // assume server started successfully + case <-time.After(types.ServerStartTime): // assume server started successfully } } @@ -366,7 +366,7 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App select { case err := <-errCh: return err - case <-time.After(5 * time.Second): // assume server started successfully + case <-time.After(types.ServerStartTime): // assume server started successfully } } diff --git a/server/types/app.go b/server/types/app.go index ba702074b2a6..cf6b6ad9a1ff 100644 --- a/server/types/app.go +++ b/server/types/app.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "io" + "time" "github.com/gogo/protobuf/grpc" "github.com/spf13/cobra" @@ -16,6 +17,10 @@ import ( "github.com/cosmos/cosmos-sdk/server/config" ) +// ServerStartTime defines the time duration that the server need to stay running after startup +// for the startup be considered successful +const ServerStartTime = 5 * time.Second + type ( // AppOptions defines an interface that is passed into an application // constructor, typically used to set BaseApp options that are either supplied diff --git a/testutil/network/util.go b/testutil/network/util.go index c6a2bd815904..e7542113a87b 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/server/api" servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" + srvtypes "github.com/cosmos/cosmos-sdk/server/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" @@ -90,7 +91,7 @@ func startInProcess(cfg Config, val *Validator) error { select { case err := <-errCh: return err - case <-time.After(5 * time.Second): // assume server started successfully + case <-time.After(srvtypes.ServerStartTime): // assume server started successfully } val.api = apiSrv @@ -105,21 +106,10 @@ func startInProcess(cfg Config, val *Validator) error { val.grpc = grpcSrv if val.AppConfig.GRPCWeb.Enable { - errCh1 := make(chan error) - go func() { - grpcWeb, err := servergrpc.StartGRPCWeb(grpcSrv, *val.AppConfig) - if err != nil { - errCh1 <- err - } - - val.grpcWeb = grpcWeb - }() - select { - case err := <-errCh1: + val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, *val.AppConfig) + if err != nil { return err - case <-time.After(5 * time.Second): // assume server started successfully } - } } diff --git a/types/tx/signing/signing.pb.go b/types/tx/signing/signing.pb.go index f06a270c7cbd..10d8e96cfa32 100644 --- a/types/tx/signing/signing.pb.go +++ b/types/tx/signing/signing.pb.go @@ -29,17 +29,21 @@ type SignMode int32 const ( // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be - // rejected + // rejected. SignMode_SIGN_MODE_UNSPECIFIED SignMode = 0 // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is - // verified with raw bytes from Tx + // verified with raw bytes from Tx. SignMode_SIGN_MODE_DIRECT SignMode = 1 // SIGN_MODE_TEXTUAL is a future signing mode that will verify some // human-readable textual representation on top of the binary representation - // from SIGN_MODE_DIRECT + // from SIGN_MODE_DIRECT. It is currently not supported. SignMode_SIGN_MODE_TEXTUAL SignMode = 2 + // SIGN_MODE_DIRECT_JSON specifies a signing mode which uses SignDocJSON. It + // is verified using a canonical JSON representation of the bytes used in + // SIGN_MODE_DIRECT. + SignMode_SIGN_MODE_DIRECT_JSON SignMode = 3 // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses - // Amino JSON and will be removed in the future + // Amino JSON and will be removed in the future. SignMode_SIGN_MODE_LEGACY_AMINO_JSON SignMode = 127 ) @@ -47,6 +51,7 @@ var SignMode_name = map[int32]string{ 0: "SIGN_MODE_UNSPECIFIED", 1: "SIGN_MODE_DIRECT", 2: "SIGN_MODE_TEXTUAL", + 3: "SIGN_MODE_DIRECT_JSON", 127: "SIGN_MODE_LEGACY_AMINO_JSON", } @@ -54,6 +59,7 @@ var SignMode_value = map[string]int32{ "SIGN_MODE_UNSPECIFIED": 0, "SIGN_MODE_DIRECT": 1, "SIGN_MODE_TEXTUAL": 2, + "SIGN_MODE_DIRECT_JSON": 3, "SIGN_MODE_LEGACY_AMINO_JSON": 127, } @@ -391,41 +397,42 @@ func init() { } var fileDescriptor_9a54958ff3d0b1b9 = []byte{ - // 544 bytes of a gzipped FileDescriptorProto + // 552 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xcf, 0x6e, 0xd3, 0x40, - 0x10, 0xc6, 0xed, 0x26, 0x8d, 0xd2, 0x29, 0x42, 0x61, 0x49, 0xa5, 0xc4, 0x20, 0x13, 0x95, 0x03, + 0x10, 0xc6, 0xed, 0x26, 0xad, 0xd2, 0x29, 0x42, 0x61, 0x49, 0xa5, 0xc4, 0x20, 0x13, 0x95, 0x03, 0x11, 0x52, 0xd6, 0x6a, 0x72, 0x40, 0x70, 0xcb, 0x1f, 0x93, 0x86, 0x36, 0x09, 0xd8, 0xa9, 0x04, - 0x5c, 0x2c, 0xdb, 0xd9, 0x1a, 0xab, 0xb1, 0xd7, 0x78, 0xd7, 0xa8, 0x3e, 0xf1, 0x0a, 0xbc, 0x06, - 0xcf, 0xc1, 0x85, 0x63, 0x8f, 0x1c, 0x51, 0xf2, 0x0c, 0xdc, 0x51, 0xec, 0x38, 0x09, 0x52, 0x11, - 0x22, 0x27, 0x6b, 0x66, 0xbe, 0xfd, 0xcd, 0xb7, 0x9a, 0x59, 0xc3, 0x13, 0x9b, 0x32, 0x8f, 0x32, - 0x85, 0x5f, 0x2b, 0xcc, 0x75, 0x7c, 0xd7, 0x77, 0x94, 0x4f, 0x27, 0x16, 0xe1, 0xe6, 0x49, 0x16, - 0xe3, 0x20, 0xa4, 0x9c, 0xa2, 0x6a, 0x2a, 0xc4, 0xfc, 0x1a, 0x67, 0x85, 0x95, 0x50, 0x6a, 0xac, - 0x18, 0x76, 0x18, 0x07, 0x9c, 0x2a, 0x5e, 0x34, 0xe3, 0x2e, 0x73, 0x37, 0xa0, 0x2c, 0x91, 0x92, - 0xa4, 0xaa, 0x43, 0xa9, 0x33, 0x23, 0x4a, 0x12, 0x59, 0xd1, 0xa5, 0x62, 0xfa, 0x71, 0x5a, 0x3a, - 0xbe, 0x84, 0xb2, 0xee, 0x3a, 0xbe, 0xc9, 0xa3, 0x90, 0xf4, 0x08, 0xb3, 0x43, 0x37, 0xe0, 0x34, - 0x64, 0x68, 0x04, 0xc0, 0xb2, 0x3c, 0xab, 0x88, 0xb5, 0x5c, 0xfd, 0xb0, 0x89, 0xf1, 0x5f, 0x1d, - 0xe1, 0x5b, 0x20, 0xda, 0x16, 0xe1, 0xf8, 0x57, 0x1e, 0xee, 0xdf, 0xa2, 0x41, 0x2d, 0x80, 0x20, - 0xb2, 0x66, 0xae, 0x6d, 0x5c, 0x91, 0xb8, 0x22, 0xd6, 0xc4, 0xfa, 0x61, 0xb3, 0x8c, 0x53, 0xbf, - 0x38, 0xf3, 0x8b, 0xdb, 0x7e, 0xac, 0x1d, 0xa4, 0xba, 0x33, 0x12, 0xa3, 0x3e, 0xe4, 0xa7, 0x26, - 0x37, 0x2b, 0x7b, 0x89, 0xbc, 0xf5, 0x7f, 0xb6, 0x70, 0xcf, 0xe4, 0xa6, 0x96, 0x00, 0x90, 0x04, - 0x45, 0x46, 0x3e, 0x46, 0xc4, 0xb7, 0x49, 0x25, 0x57, 0x13, 0xeb, 0x79, 0x6d, 0x1d, 0x4b, 0xdf, - 0x72, 0x90, 0x5f, 0x4a, 0xd1, 0x04, 0x0a, 0xcc, 0xf5, 0x9d, 0x19, 0x59, 0xd9, 0x7b, 0xb1, 0x43, - 0x3f, 0xac, 0x27, 0x84, 0x53, 0x41, 0x5b, 0xb1, 0xd0, 0x1b, 0xd8, 0x4f, 0xa6, 0xb4, 0xba, 0xc4, - 0xf3, 0x5d, 0xa0, 0xc3, 0x25, 0xe0, 0x54, 0xd0, 0x52, 0x92, 0x64, 0x40, 0x21, 0x6d, 0x83, 0x9e, - 0x41, 0xde, 0xa3, 0xd3, 0xd4, 0xf0, 0xdd, 0xe6, 0xe3, 0x7f, 0xb0, 0x87, 0x74, 0x4a, 0xb4, 0xe4, - 0x00, 0x7a, 0x08, 0x07, 0xeb, 0xa1, 0x25, 0xce, 0xee, 0x68, 0x9b, 0x84, 0xf4, 0x55, 0x84, 0xfd, - 0xa4, 0x27, 0x3a, 0x83, 0xa2, 0xe5, 0x72, 0x33, 0x0c, 0xcd, 0x6c, 0x68, 0x4a, 0xd6, 0x24, 0xdd, - 0x49, 0xbc, 0x5e, 0xc1, 0xac, 0x53, 0x97, 0x7a, 0x81, 0x69, 0xf3, 0x8e, 0xcb, 0xdb, 0xcb, 0x63, - 0xda, 0x1a, 0x80, 0xf4, 0x3f, 0x76, 0x6d, 0x2f, 0xd9, 0xb5, 0x9d, 0x86, 0xba, 0x85, 0xe9, 0xec, - 0x43, 0x8e, 0x45, 0xde, 0x53, 0x06, 0xc5, 0xec, 0x8a, 0xa8, 0x0a, 0x47, 0xfa, 0xa0, 0x3f, 0x32, - 0x86, 0xe3, 0x9e, 0x6a, 0x5c, 0x8c, 0xf4, 0xd7, 0x6a, 0x77, 0xf0, 0x72, 0xa0, 0xf6, 0x4a, 0x02, - 0x2a, 0x43, 0x69, 0x53, 0xea, 0x0d, 0x34, 0xb5, 0x3b, 0x29, 0x89, 0xe8, 0x08, 0xee, 0x6d, 0xb2, - 0x13, 0xf5, 0xed, 0xe4, 0xa2, 0x7d, 0x5e, 0xda, 0x43, 0x8f, 0xe0, 0xc1, 0x26, 0x7d, 0xae, 0xf6, - 0xdb, 0xdd, 0x77, 0x46, 0x7b, 0x38, 0x18, 0x8d, 0x8d, 0x57, 0xfa, 0x78, 0x54, 0xfa, 0xdc, 0xe9, - 0x7f, 0x9f, 0xcb, 0xe2, 0xcd, 0x5c, 0x16, 0x7f, 0xce, 0x65, 0xf1, 0xcb, 0x42, 0x16, 0x6e, 0x16, - 0xb2, 0xf0, 0x63, 0x21, 0x0b, 0xef, 0x1b, 0x8e, 0xcb, 0x3f, 0x44, 0x16, 0xb6, 0xa9, 0xa7, 0x64, - 0x6f, 0x38, 0xf9, 0x34, 0xd8, 0xf4, 0x4a, 0xe1, 0x71, 0x40, 0xb6, 0x7f, 0x0c, 0x56, 0x21, 0x79, - 0x01, 0xad, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x86, 0x87, 0x55, 0xbf, 0x34, 0x04, 0x00, 0x00, + 0x5c, 0x2c, 0xdb, 0xd9, 0x1a, 0xab, 0xb1, 0xd7, 0x78, 0xd7, 0xa8, 0x3e, 0xf1, 0x06, 0x88, 0xd7, + 0xe0, 0x39, 0xb8, 0x70, 0xec, 0x91, 0x23, 0x4a, 0x9e, 0x81, 0x3b, 0x8a, 0x1d, 0x27, 0xa9, 0x54, + 0x84, 0xc8, 0xc9, 0x9a, 0x99, 0x6f, 0x7f, 0xf3, 0xad, 0x66, 0xd6, 0xf0, 0xc4, 0xa6, 0xcc, 0xa3, + 0x4c, 0xe1, 0x57, 0x0a, 0x73, 0x1d, 0xdf, 0xf5, 0x1d, 0xe5, 0xd3, 0xb1, 0x45, 0xb8, 0x79, 0x9c, + 0xc5, 0x38, 0x08, 0x29, 0xa7, 0xa8, 0x92, 0x0a, 0x31, 0xbf, 0xc2, 0x59, 0x61, 0x29, 0x94, 0xea, + 0x4b, 0x86, 0x1d, 0xc6, 0x01, 0xa7, 0x8a, 0x17, 0x4d, 0xb9, 0xcb, 0xdc, 0x35, 0x28, 0x4b, 0xa4, + 0x24, 0xa9, 0xe2, 0x50, 0xea, 0x4c, 0x89, 0x92, 0x44, 0x56, 0x74, 0xa1, 0x98, 0x7e, 0x9c, 0x96, + 0x8e, 0x2e, 0xa0, 0xa4, 0xbb, 0x8e, 0x6f, 0xf2, 0x28, 0x24, 0x5d, 0xc2, 0xec, 0xd0, 0x0d, 0x38, + 0x0d, 0x19, 0x1a, 0x02, 0xb0, 0x2c, 0xcf, 0xca, 0x62, 0x35, 0x57, 0x3b, 0x68, 0x60, 0xfc, 0x57, + 0x47, 0xf8, 0x16, 0x88, 0xb6, 0x41, 0x38, 0xfa, 0x9d, 0x87, 0xfb, 0xb7, 0x68, 0x50, 0x13, 0x20, + 0x88, 0xac, 0xa9, 0x6b, 0x1b, 0x97, 0x24, 0x2e, 0x8b, 0x55, 0xb1, 0x76, 0xd0, 0x28, 0xe1, 0xd4, + 0x2f, 0xce, 0xfc, 0xe2, 0x96, 0x1f, 0x6b, 0xfb, 0xa9, 0xee, 0x94, 0xc4, 0xa8, 0x07, 0xf9, 0x89, + 0xc9, 0xcd, 0xf2, 0x4e, 0x22, 0x6f, 0xfe, 0x9f, 0x2d, 0xdc, 0x35, 0xb9, 0xa9, 0x25, 0x00, 0x24, + 0x41, 0x81, 0x91, 0x8f, 0x11, 0xf1, 0x6d, 0x52, 0xce, 0x55, 0xc5, 0x5a, 0x5e, 0x5b, 0xc5, 0xd2, + 0xf7, 0x1c, 0xe4, 0x17, 0x52, 0x34, 0x86, 0x3d, 0xe6, 0xfa, 0xce, 0x94, 0x2c, 0xed, 0xbd, 0xd8, + 0xa2, 0x1f, 0xd6, 0x13, 0xc2, 0x89, 0xa0, 0x2d, 0x59, 0xe8, 0x0d, 0xec, 0x26, 0x53, 0x5a, 0x5e, + 0xe2, 0xf9, 0x36, 0xd0, 0xc1, 0x02, 0x70, 0x22, 0x68, 0x29, 0x49, 0x32, 0x60, 0x2f, 0x6d, 0x83, + 0x9e, 0x41, 0xde, 0xa3, 0x93, 0xd4, 0xf0, 0xdd, 0xc6, 0xe3, 0x7f, 0xb0, 0x07, 0x74, 0x42, 0xb4, + 0xe4, 0x00, 0x7a, 0x08, 0xfb, 0xab, 0xa1, 0x25, 0xce, 0xee, 0x68, 0xeb, 0x84, 0xf4, 0x4d, 0x84, + 0xdd, 0xa4, 0x27, 0x3a, 0x85, 0x82, 0xe5, 0x72, 0x33, 0x0c, 0xcd, 0x6c, 0x68, 0x4a, 0xd6, 0x24, + 0xdd, 0x49, 0xbc, 0x5a, 0xc1, 0xac, 0x53, 0x87, 0x7a, 0x81, 0x69, 0xf3, 0xb6, 0xcb, 0x5b, 0x8b, + 0x63, 0xda, 0x0a, 0x80, 0xf4, 0x1b, 0xbb, 0xb6, 0x93, 0xec, 0xda, 0x56, 0x43, 0xdd, 0xc0, 0xb4, + 0x77, 0x21, 0xc7, 0x22, 0xef, 0xe9, 0x17, 0x11, 0x0a, 0xd9, 0x1d, 0x51, 0x05, 0x0e, 0xf5, 0x7e, + 0x6f, 0x68, 0x0c, 0x46, 0x5d, 0xd5, 0x38, 0x1f, 0xea, 0xaf, 0xd5, 0x4e, 0xff, 0x65, 0x5f, 0xed, + 0x16, 0x05, 0x54, 0x82, 0xe2, 0xba, 0xd4, 0xed, 0x6b, 0x6a, 0x67, 0x5c, 0x14, 0xd1, 0x21, 0xdc, + 0x5b, 0x67, 0xc7, 0xea, 0xdb, 0xf1, 0x79, 0xeb, 0xac, 0xb8, 0x73, 0x93, 0x93, 0x8a, 0x8d, 0x57, + 0xfa, 0x68, 0x58, 0xcc, 0xa1, 0x47, 0xf0, 0x60, 0x5d, 0x3a, 0x53, 0x7b, 0xad, 0xce, 0x3b, 0xa3, + 0x35, 0xe8, 0x0f, 0x47, 0xa9, 0xe0, 0x73, 0xbb, 0xf7, 0x63, 0x26, 0x8b, 0xd7, 0x33, 0x59, 0xfc, + 0x35, 0x93, 0xc5, 0xaf, 0x73, 0x59, 0xb8, 0x9e, 0xcb, 0xc2, 0xcf, 0xb9, 0x2c, 0xbc, 0xaf, 0x3b, + 0x2e, 0xff, 0x10, 0x59, 0xd8, 0xa6, 0x9e, 0x92, 0xbd, 0xef, 0xe4, 0x53, 0x67, 0x93, 0x4b, 0x85, + 0xc7, 0x01, 0xd9, 0xfc, 0x69, 0x58, 0x7b, 0xc9, 0xeb, 0x68, 0xfe, 0x09, 0x00, 0x00, 0xff, 0xff, + 0x93, 0x71, 0x12, 0x94, 0x50, 0x04, 0x00, 0x00, } func (m *SignatureDescriptors) Marshal() (dAtA []byte, err error) { diff --git a/types/tx/tx.pb.go b/types/tx/tx.pb.go index 174248a9812f..96fe19b6ac79 100644 --- a/types/tx/tx.pb.go +++ b/types/tx/tx.pb.go @@ -244,6 +244,103 @@ func (m *SignDoc) GetAccountNumber() uint64 { return 0 } +// SignDocJSON is the type used for generating sign bytes for +// SIGN_MODE_DIRECT_JSON. It is designed to be serialized as proto3 JSON +// following the rules defined here: +// https://github.com/regen-network/canonical-proto3/blob/master/README.md#json. +type SignDocJSON struct { + // body is the processable content of the transaction + Body *TxBody `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"` + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo *AuthInfo `protobuf:"bytes,2,opt,name=auth_info,json=authInfo,proto3" json:"auth_info,omitempty"` + // chain_id is the identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + ChainId string `protobuf:"bytes,3,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // account_number is the account number of the signing account in state + AccountNumber uint64 `protobuf:"varint,4,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + // sign_doc_sha256_hash is the SHA-256 hash of SignDoc. It is included here to + // reduce the malleability attack surface of SIGN_MODE_DIRECT_JSON vs + // SIGN_MODE_DIRECT to zero. Basically this means that any discrepancy between + // protobuf bytes over the wire and protobuf bytes that are signed cannot be + // exploited. This information is obviously redundant with information already + // in SignDocJSON, but is included as a security check for scenarios where + // this information may have inadvertently been excluded. We include the hash + // of SignDoc rather than the full SignDoc bytes to reduce the size of + // SignDocJSON for scenarios where large payloads could cause problems for + // hardware wallets. + SignDocSha256Hash []byte `protobuf:"bytes,5,opt,name=sign_doc_sha256_hash,json=signDocSha256Hash,proto3" json:"sign_doc_sha256_hash,omitempty"` +} + +func (m *SignDocJSON) Reset() { *m = SignDocJSON{} } +func (m *SignDocJSON) String() string { return proto.CompactTextString(m) } +func (*SignDocJSON) ProtoMessage() {} +func (*SignDocJSON) Descriptor() ([]byte, []int) { + return fileDescriptor_96d1575ffde80842, []int{3} +} +func (m *SignDocJSON) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignDocJSON) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignDocJSON.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 *SignDocJSON) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignDocJSON.Merge(m, src) +} +func (m *SignDocJSON) XXX_Size() int { + return m.Size() +} +func (m *SignDocJSON) XXX_DiscardUnknown() { + xxx_messageInfo_SignDocJSON.DiscardUnknown(m) +} + +var xxx_messageInfo_SignDocJSON proto.InternalMessageInfo + +func (m *SignDocJSON) GetBody() *TxBody { + if m != nil { + return m.Body + } + return nil +} + +func (m *SignDocJSON) GetAuthInfo() *AuthInfo { + if m != nil { + return m.AuthInfo + } + return nil +} + +func (m *SignDocJSON) GetChainId() string { + if m != nil { + return m.ChainId + } + return "" +} + +func (m *SignDocJSON) GetAccountNumber() uint64 { + if m != nil { + return m.AccountNumber + } + return 0 +} + +func (m *SignDocJSON) GetSignDocSha256Hash() []byte { + if m != nil { + return m.SignDocSha256Hash + } + return nil +} + // TxBody is the body of a transaction that all signers sign over. type TxBody struct { // messages is a list of messages to be executed. The required signers of @@ -275,7 +372,7 @@ func (m *TxBody) Reset() { *m = TxBody{} } func (m *TxBody) String() string { return proto.CompactTextString(m) } func (*TxBody) ProtoMessage() {} func (*TxBody) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{3} + return fileDescriptor_96d1575ffde80842, []int{4} } func (m *TxBody) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -358,7 +455,7 @@ func (m *AuthInfo) Reset() { *m = AuthInfo{} } func (m *AuthInfo) String() string { return proto.CompactTextString(m) } func (*AuthInfo) ProtoMessage() {} func (*AuthInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{4} + return fileDescriptor_96d1575ffde80842, []int{5} } func (m *AuthInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -421,7 +518,7 @@ func (m *SignerInfo) Reset() { *m = SignerInfo{} } func (m *SignerInfo) String() string { return proto.CompactTextString(m) } func (*SignerInfo) ProtoMessage() {} func (*SignerInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{5} + return fileDescriptor_96d1575ffde80842, []int{6} } func (m *SignerInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -486,7 +583,7 @@ func (m *ModeInfo) Reset() { *m = ModeInfo{} } func (m *ModeInfo) String() string { return proto.CompactTextString(m) } func (*ModeInfo) ProtoMessage() {} func (*ModeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{6} + return fileDescriptor_96d1575ffde80842, []int{7} } func (m *ModeInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -572,7 +669,7 @@ func (m *ModeInfo_Single) Reset() { *m = ModeInfo_Single{} } func (m *ModeInfo_Single) String() string { return proto.CompactTextString(m) } func (*ModeInfo_Single) ProtoMessage() {} func (*ModeInfo_Single) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{6, 0} + return fileDescriptor_96d1575ffde80842, []int{7, 0} } func (m *ModeInfo_Single) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -621,7 +718,7 @@ func (m *ModeInfo_Multi) Reset() { *m = ModeInfo_Multi{} } func (m *ModeInfo_Multi) String() string { return proto.CompactTextString(m) } func (*ModeInfo_Multi) ProtoMessage() {} func (*ModeInfo_Multi) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{6, 1} + return fileDescriptor_96d1575ffde80842, []int{7, 1} } func (m *ModeInfo_Multi) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -687,7 +784,7 @@ func (m *Fee) Reset() { *m = Fee{} } func (m *Fee) String() string { return proto.CompactTextString(m) } func (*Fee) ProtoMessage() {} func (*Fee) Descriptor() ([]byte, []int) { - return fileDescriptor_96d1575ffde80842, []int{7} + return fileDescriptor_96d1575ffde80842, []int{8} } func (m *Fee) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -748,6 +845,7 @@ func init() { proto.RegisterType((*Tx)(nil), "cosmos.tx.v1beta1.Tx") proto.RegisterType((*TxRaw)(nil), "cosmos.tx.v1beta1.TxRaw") proto.RegisterType((*SignDoc)(nil), "cosmos.tx.v1beta1.SignDoc") + proto.RegisterType((*SignDocJSON)(nil), "cosmos.tx.v1beta1.SignDocJSON") proto.RegisterType((*TxBody)(nil), "cosmos.tx.v1beta1.TxBody") proto.RegisterType((*AuthInfo)(nil), "cosmos.tx.v1beta1.AuthInfo") proto.RegisterType((*SignerInfo)(nil), "cosmos.tx.v1beta1.SignerInfo") @@ -760,60 +858,63 @@ func init() { func init() { proto.RegisterFile("cosmos/tx/v1beta1/tx.proto", fileDescriptor_96d1575ffde80842) } var fileDescriptor_96d1575ffde80842 = []byte{ - // 843 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xdd, 0x6e, 0xdc, 0x44, - 0x14, 0x5e, 0xef, 0x5f, 0xd6, 0x27, 0x49, 0x4b, 0x47, 0x11, 0xda, 0x6c, 0x54, 0x37, 0x18, 0x15, - 0xf6, 0x26, 0x76, 0x9b, 0x5e, 0xf0, 0x23, 0x24, 0xc8, 0x16, 0xaa, 0x54, 0xa5, 0x20, 0x4d, 0x72, - 0xd5, 0x1b, 0x6b, 0xec, 0x9d, 0x78, 0x47, 0x5d, 0xcf, 0x2c, 0x9e, 0x71, 0xb1, 0x1f, 0x02, 0xa9, - 0x42, 0x42, 0xbc, 0x03, 0x2f, 0xc0, 0x2b, 0xf4, 0xb2, 0x97, 0x5c, 0x41, 0x95, 0x3c, 0x08, 0x68, - 0xc6, 0x63, 0x27, 0x82, 0x55, 0x72, 0xc3, 0x95, 0xe7, 0x9c, 0xf9, 0xce, 0x37, 0x9f, 0xcf, 0x1f, - 0x4c, 0x12, 0x21, 0x33, 0x21, 0x43, 0x55, 0x86, 0xaf, 0x1e, 0xc6, 0x54, 0x91, 0x87, 0xa1, 0x2a, - 0x83, 0x55, 0x2e, 0x94, 0x40, 0x77, 0xea, 0xbb, 0x40, 0x95, 0x81, 0xbd, 0x9b, 0xec, 0xa4, 0x22, - 0x15, 0xe6, 0x36, 0xd4, 0xa7, 0x1a, 0x38, 0x39, 0xb0, 0x24, 0x49, 0x5e, 0xad, 0x94, 0x08, 0xb3, - 0x62, 0xa9, 0x98, 0x64, 0x69, 0xcb, 0xd8, 0x38, 0x2c, 0xdc, 0xb3, 0xf0, 0x98, 0x48, 0xda, 0x62, - 0x12, 0xc1, 0xb8, 0xbd, 0xff, 0xf8, 0x52, 0x93, 0x64, 0x29, 0x67, 0xfc, 0x92, 0xc9, 0xda, 0x16, - 0xb8, 0x9b, 0x0a, 0x91, 0x2e, 0x69, 0x68, 0xac, 0xb8, 0x38, 0x0b, 0x09, 0xaf, 0xea, 0x2b, 0xff, - 0x27, 0x07, 0xba, 0xa7, 0x25, 0x3a, 0x80, 0x7e, 0x2c, 0xe6, 0xd5, 0xd8, 0xd9, 0x77, 0xa6, 0x9b, - 0x87, 0xbb, 0xc1, 0x7f, 0xfe, 0x28, 0x38, 0x2d, 0x67, 0x62, 0x5e, 0x61, 0x03, 0x43, 0x9f, 0x82, - 0x4b, 0x0a, 0xb5, 0x88, 0x18, 0x3f, 0x13, 0xe3, 0xae, 0x89, 0xd9, 0x5b, 0x13, 0x73, 0x54, 0xa8, - 0xc5, 0x53, 0x7e, 0x26, 0xf0, 0x88, 0xd8, 0x13, 0xf2, 0x00, 0xb4, 0x36, 0xa2, 0x8a, 0x9c, 0xca, - 0x71, 0x6f, 0xbf, 0x37, 0xdd, 0xc2, 0x57, 0x3c, 0x3e, 0x87, 0xc1, 0x69, 0x89, 0xc9, 0x8f, 0xe8, - 0x2e, 0x80, 0x7e, 0x2a, 0x8a, 0x2b, 0x45, 0xa5, 0xd1, 0xb5, 0x85, 0x5d, 0xed, 0x99, 0x69, 0x07, - 0xfa, 0x08, 0x6e, 0xb7, 0x0a, 0x2c, 0xa6, 0x6b, 0x30, 0xdb, 0xcd, 0x53, 0x35, 0xee, 0xa6, 0xf7, - 0x7e, 0x76, 0x60, 0xe3, 0x84, 0xa5, 0xfc, 0x6b, 0x91, 0xfc, 0x5f, 0x4f, 0xee, 0xc2, 0x28, 0x59, - 0x10, 0xc6, 0x23, 0x36, 0x1f, 0xf7, 0xf6, 0x9d, 0xa9, 0x8b, 0x37, 0x8c, 0xfd, 0x74, 0x8e, 0xee, - 0xc3, 0x2d, 0x92, 0x24, 0xa2, 0xe0, 0x2a, 0xe2, 0x45, 0x16, 0xd3, 0x7c, 0xdc, 0xdf, 0x77, 0xa6, - 0x7d, 0xbc, 0x6d, 0xbd, 0xdf, 0x19, 0xa7, 0xff, 0x4b, 0x17, 0x86, 0x75, 0xbe, 0xd1, 0x03, 0x18, - 0x65, 0x54, 0x4a, 0x92, 0x1a, 0x45, 0xbd, 0xe9, 0xe6, 0xe1, 0x4e, 0x50, 0x57, 0x33, 0x68, 0xaa, - 0x19, 0x1c, 0xf1, 0x0a, 0xb7, 0x28, 0x84, 0xa0, 0x9f, 0xd1, 0xac, 0x2e, 0x8b, 0x8b, 0xcd, 0x59, - 0xbf, 0xab, 0x58, 0x46, 0x45, 0xa1, 0xa2, 0x05, 0x65, 0xe9, 0x42, 0x19, 0x61, 0x7d, 0xbc, 0x6d, - 0xbd, 0xc7, 0xc6, 0x89, 0x66, 0x70, 0x87, 0x96, 0x8a, 0x72, 0xc9, 0x04, 0x8f, 0xc4, 0x4a, 0x31, - 0xc1, 0xe5, 0xf8, 0xef, 0x8d, 0x6b, 0x9e, 0x7d, 0xaf, 0xc5, 0x7f, 0x5f, 0xc3, 0xd1, 0x0b, 0xf0, - 0xb8, 0xe0, 0x51, 0x92, 0x33, 0xc5, 0x12, 0xb2, 0x8c, 0xd6, 0x10, 0xde, 0xbe, 0x86, 0x70, 0x8f, - 0x0b, 0xfe, 0xd8, 0xc6, 0x7e, 0xf3, 0x2f, 0x6e, 0xff, 0x15, 0x8c, 0x9a, 0x96, 0x42, 0x5f, 0xc1, - 0x96, 0x2e, 0x23, 0xcd, 0x4d, 0x3d, 0x9a, 0xe4, 0xdc, 0x5d, 0xd3, 0x85, 0x27, 0x06, 0x66, 0xfa, - 0x70, 0x53, 0xb6, 0x67, 0x89, 0xa6, 0xd0, 0x3b, 0xa3, 0xd4, 0xb6, 0xef, 0xfb, 0x6b, 0x02, 0x9f, - 0x50, 0x8a, 0x35, 0xc4, 0xff, 0xd5, 0x01, 0xb8, 0x64, 0x41, 0x8f, 0x00, 0x56, 0x45, 0xbc, 0x64, - 0x49, 0xf4, 0x92, 0x36, 0x23, 0xb3, 0xfe, 0x6f, 0xdc, 0x1a, 0xf7, 0x8c, 0x9a, 0x91, 0xc9, 0xc4, - 0x9c, 0xde, 0x34, 0x32, 0xcf, 0xc5, 0x9c, 0xd6, 0x23, 0x93, 0xd9, 0x13, 0x9a, 0xc0, 0x48, 0xd2, - 0x1f, 0x0a, 0xca, 0x13, 0x6a, 0xcb, 0xd6, 0xda, 0xfe, 0xbb, 0x2e, 0x8c, 0x9a, 0x10, 0xf4, 0x05, - 0x0c, 0x25, 0xe3, 0xe9, 0x92, 0x5a, 0x4d, 0xfe, 0x35, 0xfc, 0xc1, 0x89, 0x41, 0x1e, 0x77, 0xb0, - 0x8d, 0x41, 0x9f, 0xc1, 0xc0, 0xec, 0x1f, 0x2b, 0xee, 0x83, 0xeb, 0x82, 0x9f, 0x6b, 0xe0, 0x71, - 0x07, 0xd7, 0x11, 0x93, 0x23, 0x18, 0xd6, 0x74, 0xe8, 0x13, 0xe8, 0x6b, 0xdd, 0x46, 0xc0, 0xad, - 0xc3, 0x0f, 0xaf, 0x70, 0x34, 0x1b, 0xe9, 0x6a, 0x55, 0x34, 0x1f, 0x36, 0x01, 0x93, 0xd7, 0x0e, - 0x0c, 0x0c, 0x2b, 0x7a, 0x06, 0xa3, 0x98, 0x29, 0x92, 0xe7, 0xa4, 0xc9, 0x6d, 0xd8, 0xd0, 0xd4, - 0x7b, 0x33, 0x68, 0xd7, 0x64, 0xc3, 0xf5, 0x58, 0x64, 0x2b, 0x92, 0xa8, 0x19, 0x53, 0x47, 0x3a, - 0x0c, 0xb7, 0x04, 0xe8, 0x73, 0x80, 0x36, 0xeb, 0x7a, 0x5c, 0x7b, 0x37, 0xa5, 0xdd, 0x6d, 0xd2, - 0x2e, 0x67, 0x03, 0xe8, 0xc9, 0x22, 0xf3, 0x7f, 0x77, 0xa0, 0xf7, 0x84, 0x52, 0x94, 0xc0, 0x90, - 0x64, 0x7a, 0x48, 0x6d, 0xab, 0xb5, 0x4b, 0x52, 0xaf, 0xe7, 0x2b, 0x52, 0x18, 0x9f, 0x3d, 0x78, - 0xf3, 0xe7, 0xbd, 0xce, 0x6f, 0x7f, 0xdd, 0x9b, 0xa6, 0x4c, 0x2d, 0x8a, 0x38, 0x48, 0x44, 0x16, - 0x36, 0xab, 0xdf, 0x7c, 0x0e, 0xe4, 0xfc, 0x65, 0xa8, 0xaa, 0x15, 0x95, 0x26, 0x40, 0x62, 0x4b, - 0x8d, 0xf6, 0xc0, 0x4d, 0x89, 0x8c, 0x96, 0x2c, 0x63, 0xca, 0x14, 0xa2, 0x8f, 0x47, 0x29, 0x91, - 0xdf, 0x6a, 0x1b, 0xed, 0xc0, 0x60, 0x45, 0x2a, 0x9a, 0xdb, 0xad, 0x52, 0x1b, 0x68, 0x0c, 0x1b, - 0x69, 0x4e, 0xb8, 0xb2, 0xcb, 0xc4, 0xc5, 0x8d, 0x39, 0xfb, 0xf2, 0xcd, 0xb9, 0xe7, 0xbc, 0x3d, - 0xf7, 0x9c, 0x77, 0xe7, 0x9e, 0xf3, 0xfa, 0xc2, 0xeb, 0xbc, 0xbd, 0xf0, 0x3a, 0x7f, 0x5c, 0x78, - 0x9d, 0x17, 0xf7, 0x6f, 0x16, 0x16, 0xaa, 0x32, 0x1e, 0x9a, 0x66, 0x7e, 0xf4, 0x4f, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xd4, 0xb7, 0x75, 0x7d, 0xfd, 0x06, 0x00, 0x00, + // 895 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0x16, 0xf5, 0x67, 0x69, 0x6c, 0x27, 0xf5, 0xc2, 0x28, 0x64, 0x19, 0x61, 0x5c, 0x16, 0x69, + 0x75, 0x31, 0x99, 0x38, 0xe8, 0x2f, 0x0a, 0xb4, 0x56, 0xda, 0xc0, 0x69, 0x9a, 0x04, 0x58, 0xf9, + 0x94, 0x0b, 0xb1, 0xa4, 0xd6, 0xe4, 0x22, 0xe2, 0xae, 0xca, 0x5d, 0xa6, 0xd2, 0x43, 0x14, 0x08, + 0x0a, 0x14, 0x7d, 0x87, 0xbe, 0x40, 0x5f, 0x21, 0xc7, 0x1c, 0x7b, 0x6a, 0x0d, 0xfb, 0xd4, 0xa7, + 0x68, 0xb1, 0xcb, 0x25, 0x6d, 0xb4, 0x82, 0x7d, 0x68, 0xd1, 0x93, 0x76, 0x66, 0xbe, 0xf9, 0xf6, + 0xdb, 0x99, 0xd1, 0x10, 0x86, 0xb1, 0x90, 0x99, 0x90, 0x81, 0x5a, 0x04, 0x2f, 0xef, 0x45, 0x54, + 0x91, 0x7b, 0x81, 0x5a, 0xf8, 0xf3, 0x5c, 0x28, 0x81, 0xb6, 0xca, 0x98, 0xaf, 0x16, 0xbe, 0x8d, + 0x0d, 0xb7, 0x13, 0x91, 0x08, 0x13, 0x0d, 0xf4, 0xa9, 0x04, 0x0e, 0xf7, 0x2d, 0x49, 0x9c, 0x2f, + 0xe7, 0x4a, 0x04, 0x59, 0x31, 0x53, 0x4c, 0xb2, 0xa4, 0x66, 0xac, 0x1c, 0x16, 0xee, 0x5a, 0x78, + 0x44, 0x24, 0xad, 0x31, 0xb1, 0x60, 0xdc, 0xc6, 0xdf, 0xbf, 0xd0, 0x24, 0x59, 0xc2, 0x19, 0xbf, + 0x60, 0xb2, 0xb6, 0x05, 0xee, 0x24, 0x42, 0x24, 0x33, 0x1a, 0x18, 0x2b, 0x2a, 0x4e, 0x02, 0xc2, + 0x97, 0x65, 0xc8, 0xfb, 0xde, 0x81, 0xe6, 0xf1, 0x02, 0xed, 0x43, 0x3b, 0x12, 0xd3, 0xe5, 0xc0, + 0xd9, 0x73, 0x46, 0xeb, 0x07, 0x3b, 0xfe, 0x3f, 0x5e, 0xe4, 0x1f, 0x2f, 0xc6, 0x62, 0xba, 0xc4, + 0x06, 0x86, 0x3e, 0x86, 0x3e, 0x29, 0x54, 0x1a, 0x32, 0x7e, 0x22, 0x06, 0x4d, 0x93, 0xb3, 0xbb, + 0x22, 0xe7, 0xb0, 0x50, 0xe9, 0x23, 0x7e, 0x22, 0x70, 0x8f, 0xd8, 0x13, 0x72, 0x01, 0xb4, 0x36, + 0xa2, 0x8a, 0x9c, 0xca, 0x41, 0x6b, 0xaf, 0x35, 0xda, 0xc0, 0x97, 0x3c, 0x1e, 0x87, 0xce, 0xf1, + 0x02, 0x93, 0xef, 0xd0, 0x2d, 0x00, 0x7d, 0x55, 0x18, 0x2d, 0x15, 0x95, 0x46, 0xd7, 0x06, 0xee, + 0x6b, 0xcf, 0x58, 0x3b, 0xd0, 0x7b, 0x70, 0xb3, 0x56, 0x60, 0x31, 0x4d, 0x83, 0xd9, 0xac, 0xae, + 0x2a, 0x71, 0xd7, 0xdd, 0xf7, 0x83, 0x03, 0x6b, 0x13, 0x96, 0xf0, 0x2f, 0x45, 0xfc, 0x5f, 0x5d, + 0xb9, 0x03, 0xbd, 0x38, 0x25, 0x8c, 0x87, 0x6c, 0x3a, 0x68, 0xed, 0x39, 0xa3, 0x3e, 0x5e, 0x33, + 0xf6, 0xa3, 0x29, 0xba, 0x03, 0x37, 0x48, 0x1c, 0x8b, 0x82, 0xab, 0x90, 0x17, 0x59, 0x44, 0xf3, + 0x41, 0x7b, 0xcf, 0x19, 0xb5, 0xf1, 0xa6, 0xf5, 0x3e, 0x35, 0x4e, 0xef, 0x0f, 0x07, 0xd6, 0xad, + 0xa8, 0xaf, 0x27, 0xcf, 0x9e, 0xfe, 0x7f, 0xdd, 0xf9, 0xd7, 0xd2, 0x51, 0x00, 0xdb, 0xba, 0xba, + 0xe1, 0x54, 0xc4, 0xa1, 0x4c, 0xc9, 0xc1, 0x07, 0x1f, 0x86, 0x29, 0x91, 0xe9, 0xa0, 0x63, 0x2a, + 0xb5, 0x25, 0xcb, 0x57, 0x4d, 0x4c, 0xe4, 0x88, 0xc8, 0xd4, 0xfb, 0xb1, 0x09, 0xdd, 0x52, 0x3d, + 0xba, 0x0b, 0xbd, 0x8c, 0x4a, 0x49, 0x12, 0x53, 0xfd, 0xd6, 0x68, 0xfd, 0x60, 0xdb, 0x2f, 0x27, + 0xd7, 0xaf, 0x26, 0xd7, 0x3f, 0xe4, 0x4b, 0x5c, 0xa3, 0x10, 0x82, 0x76, 0x46, 0xb3, 0xf2, 0x91, + 0x7d, 0x6c, 0xce, 0x5a, 0xa8, 0x62, 0x19, 0x15, 0x85, 0x0a, 0x53, 0xca, 0x92, 0x54, 0x99, 0x97, + 0xb4, 0xf1, 0xa6, 0xf5, 0x1e, 0x19, 0x27, 0x1a, 0xc3, 0x16, 0x5d, 0x28, 0xca, 0x25, 0x13, 0x3c, + 0x14, 0x73, 0xc5, 0x04, 0x97, 0x83, 0x3f, 0xd7, 0xae, 0xb8, 0xf6, 0xad, 0x1a, 0xff, 0xac, 0x84, + 0xa3, 0xe7, 0xe0, 0x72, 0xc1, 0xc3, 0x38, 0x67, 0x8a, 0xc5, 0x64, 0x16, 0xae, 0x20, 0xbc, 0x79, + 0x05, 0xe1, 0x2e, 0x17, 0xfc, 0x81, 0xcd, 0xfd, 0xea, 0x6f, 0xdc, 0xde, 0x4b, 0xe8, 0x55, 0x0d, + 0x42, 0x5f, 0xc0, 0x86, 0x2e, 0x1c, 0xcd, 0x4d, 0x4b, 0xab, 0xe2, 0xdc, 0x5a, 0xd1, 0xd3, 0x89, + 0x81, 0x99, 0xae, 0xae, 0xcb, 0xfa, 0x2c, 0xd1, 0x08, 0x5a, 0x27, 0x94, 0xda, 0x61, 0x78, 0x7b, + 0x45, 0xe2, 0x43, 0x4a, 0xb1, 0x86, 0x78, 0x3f, 0x39, 0x00, 0x17, 0x2c, 0xe8, 0x3e, 0xc0, 0xbc, + 0x88, 0x66, 0x2c, 0x0e, 0x5f, 0xd0, 0x6a, 0x00, 0x57, 0xbf, 0xa6, 0x5f, 0xe2, 0x1e, 0x53, 0x33, + 0x80, 0x99, 0x98, 0xd2, 0xeb, 0x06, 0xf0, 0x89, 0x98, 0xd2, 0x72, 0x00, 0x33, 0x7b, 0x42, 0x43, + 0xe8, 0x49, 0xfa, 0x6d, 0x41, 0x79, 0x4c, 0x6d, 0xdb, 0x6a, 0xdb, 0x3b, 0x6d, 0x42, 0xaf, 0x4a, + 0x41, 0x9f, 0x41, 0x57, 0x32, 0x9e, 0xcc, 0xa8, 0xd5, 0xe4, 0x5d, 0xc1, 0xef, 0x4f, 0x0c, 0xf2, + 0xa8, 0x81, 0x6d, 0x0e, 0xfa, 0x04, 0x3a, 0x66, 0xd7, 0x5a, 0x71, 0xef, 0x5c, 0x95, 0xfc, 0x44, + 0x03, 0x8f, 0x1a, 0xb8, 0xcc, 0x18, 0x1e, 0x42, 0xb7, 0xa4, 0x43, 0x1f, 0x41, 0x5b, 0xeb, 0x36, + 0x02, 0x6e, 0x1c, 0xbc, 0x7b, 0x89, 0xa3, 0xda, 0xbe, 0x97, 0xbb, 0xa2, 0xf9, 0xb0, 0x49, 0x18, + 0xbe, 0x72, 0xa0, 0x63, 0x58, 0xd1, 0x63, 0xe8, 0x45, 0x4c, 0x91, 0x3c, 0x27, 0x55, 0x6d, 0x83, + 0x8a, 0xa6, 0xfc, 0x46, 0xf8, 0xf5, 0x27, 0xa1, 0xe2, 0x7a, 0x20, 0xb2, 0x39, 0x89, 0xd5, 0x98, + 0xa9, 0x43, 0x9d, 0x86, 0x6b, 0x02, 0xf4, 0x29, 0x40, 0x5d, 0x75, 0xbd, 0x9a, 0x5a, 0xd7, 0x95, + 0xbd, 0x5f, 0x95, 0x5d, 0x8e, 0x3b, 0xd0, 0x92, 0x45, 0xe6, 0xfd, 0xe2, 0x40, 0xeb, 0x21, 0xa5, + 0x28, 0x86, 0x2e, 0xc9, 0xf4, 0xbf, 0xda, 0x8e, 0x5a, 0xbd, 0x72, 0xf4, 0xa7, 0xe8, 0x92, 0x14, + 0xc6, 0xc7, 0x77, 0x5f, 0xff, 0x76, 0xbb, 0xf1, 0xf3, 0xef, 0xb7, 0x47, 0x09, 0x53, 0x69, 0x11, + 0xf9, 0xb1, 0xc8, 0x82, 0xea, 0x33, 0x67, 0x7e, 0xf6, 0xe5, 0xf4, 0x45, 0xa0, 0x96, 0x73, 0x2a, + 0x4d, 0x82, 0xc4, 0x96, 0x1a, 0xed, 0x42, 0x3f, 0x21, 0x32, 0x9c, 0xb1, 0x8c, 0x29, 0xd3, 0x88, + 0x36, 0xee, 0x25, 0x44, 0x7e, 0xa3, 0x6d, 0xb4, 0x0d, 0x9d, 0x39, 0x59, 0xd2, 0xdc, 0xae, 0xa1, + 0xd2, 0x40, 0x03, 0x58, 0x4b, 0x72, 0xc2, 0x95, 0xdd, 0x3e, 0x7d, 0x5c, 0x99, 0xe3, 0xcf, 0x5f, + 0x9f, 0xb9, 0xce, 0x9b, 0x33, 0xd7, 0x39, 0x3d, 0x73, 0x9d, 0x57, 0xe7, 0x6e, 0xe3, 0xcd, 0xb9, + 0xdb, 0xf8, 0xf5, 0xdc, 0x6d, 0x3c, 0xbf, 0x73, 0xbd, 0xb0, 0x40, 0x2d, 0xa2, 0xae, 0x19, 0xe6, + 0xfb, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x34, 0xab, 0x74, 0x31, 0xe9, 0x07, 0x00, 0x00, } func (m *Tx) Marshal() (dAtA []byte, err error) { @@ -967,6 +1068,72 @@ func (m *SignDoc) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SignDocJSON) 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 *SignDocJSON) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignDocJSON) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SignDocSha256Hash) > 0 { + i -= len(m.SignDocSha256Hash) + copy(dAtA[i:], m.SignDocSha256Hash) + i = encodeVarintTx(dAtA, i, uint64(len(m.SignDocSha256Hash))) + i-- + dAtA[i] = 0x2a + } + if m.AccountNumber != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AccountNumber)) + i-- + dAtA[i] = 0x20 + } + if len(m.ChainId) > 0 { + i -= len(m.ChainId) + copy(dAtA[i:], m.ChainId) + i = encodeVarintTx(dAtA, i, uint64(len(m.ChainId))) + i-- + dAtA[i] = 0x1a + } + if m.AuthInfo != nil { + { + size, err := m.AuthInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Body != nil { + { + size, err := m.Body.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 *TxBody) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1437,6 +1604,34 @@ func (m *SignDoc) Size() (n int) { return n } +func (m *SignDocJSON) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Body != nil { + l = m.Body.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.AuthInfo != nil { + l = m.AuthInfo.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.ChainId) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AccountNumber != 0 { + n += 1 + sovTx(uint64(m.AccountNumber)) + } + l = len(m.SignDocSha256Hash) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + func (m *TxBody) Size() (n int) { if m == nil { return 0 @@ -2082,6 +2277,213 @@ func (m *SignDoc) Unmarshal(dAtA []byte) error { } return nil } +func (m *SignDocJSON) 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: SignDocJSON: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignDocJSON: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Body", 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.Body == nil { + m.Body = &TxBody{} + } + if err := m.Body.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthInfo", 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.AuthInfo == nil { + m.AuthInfo = &AuthInfo{} + } + if err := m.AuthInfo.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", 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.ChainId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountNumber", wireType) + } + m.AccountNumber = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AccountNumber |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignDocSha256Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SignDocSha256Hash = append(m.SignDocSha256Hash[:0], dAtA[iNdEx:postIndex]...) + if m.SignDocSha256Hash == nil { + m.SignDocSha256Hash = []byte{} + } + 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 *TxBody) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/auth/module.go b/x/auth/module.go index c48d723c12b4..dfb125058bd9 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -111,8 +111,10 @@ func (AppModule) Name() string { // RegisterInvariants performs a no-op. func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the auth module. -func (AppModule) Route() sdk.Route { return sdk.Route{} } +// Deprecated: Route returns the message routing key for the auth module. +func (AppModule) Route() sdk.Route { + return sdk.Route{} +} // QuerierRoute returns the auth module's querier route name. func (AppModule) QuerierRoute() string { diff --git a/x/auth/tx/direct_test.go b/x/auth/tx/direct_test.go index bef1e81ed974..fd1e343229b6 100644 --- a/x/auth/tx/direct_test.go +++ b/x/auth/tx/direct_test.go @@ -131,6 +131,7 @@ func TestDirectModeHandler(t *testing.T) { func TestDirectModeHandler_nonDIRECT_MODE(t *testing.T) { invalidModes := []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT_JSON, signingtypes.SignMode_SIGN_MODE_TEXTUAL, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingtypes.SignMode_SIGN_MODE_UNSPECIFIED, diff --git a/x/auth/vesting/handler.go b/x/auth/vesting/handler.go deleted file mode 100644 index 1d32e9969bb5..000000000000 --- a/x/auth/vesting/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package vesting - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/keeper" - "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" -) - -// NewHandler returns a handler for x/auth message types. -func NewHandler(ak keeper.AccountKeeper, bk types.BankKeeper) sdk.Handler { - msgServer := NewMsgServerImpl(ak, bk) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgCreateVestingAccount: - res, err := msgServer.CreateVestingAccount(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/auth/vesting/handler_test.go b/x/auth/vesting/handler_test.go deleted file mode 100644 index 8af58073e90d..000000000000 --- a/x/auth/vesting/handler_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package vesting_test - -import ( - "testing" - - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" -) - -type HandlerTestSuite struct { - suite.Suite - - handler sdk.Handler - app *simapp.SimApp -} - -func (suite *HandlerTestSuite) SetupTest() { - checkTx := false - app := simapp.Setup(checkTx) - - suite.handler = vesting.NewHandler(app.AccountKeeper, app.BankKeeper) - suite.app = app -} - -func (suite *HandlerTestSuite) TestMsgCreateVestingAccount() { - ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) - - balances := sdk.NewCoins(sdk.NewInt64Coin("test", 1000)) - addr1 := sdk.AccAddress([]byte("addr1_______________")) - addr2 := sdk.AccAddress([]byte("addr2_______________")) - addr3 := sdk.AccAddress([]byte("addr3_______________")) - - acc1 := suite.app.AccountKeeper.NewAccountWithAddress(ctx, addr1) - suite.app.AccountKeeper.SetAccount(ctx, acc1) - suite.Require().NoError(testutil.FundAccount(suite.app.BankKeeper, ctx, addr1, balances)) - - testCases := []struct { - name string - msg *types.MsgCreateVestingAccount - expectErr bool - }{ - { - name: "create delayed vesting account", - msg: types.NewMsgCreateVestingAccount(addr1, addr2, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, true), - expectErr: false, - }, - { - name: "create continuous vesting account", - msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), - expectErr: false, - }, - { - name: "continuous vesting account already exists", - msg: types.NewMsgCreateVestingAccount(addr1, addr3, sdk.NewCoins(sdk.NewInt64Coin("test", 100)), ctx.BlockTime().Unix()+10000, false), - expectErr: true, - }, - } - - for _, tc := range testCases { - tc := tc - - suite.Run(tc.name, func() { - res, err := suite.handler(ctx, tc.msg) - if tc.expectErr { - suite.Require().Error(err) - } else { - suite.Require().NoError(err) - suite.Require().NotNil(res) - - toAddr, err := sdk.AccAddressFromBech32(tc.msg.ToAddress) - suite.Require().NoError(err) - accI := suite.app.AccountKeeper.GetAccount(ctx, toAddr) - suite.Require().NotNil(accI) - - if tc.msg.Delayed { - acc, ok := accI.(*types.DelayedVestingAccount) - suite.Require().True(ok) - suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) - } else { - acc, ok := accI.(*types.ContinuousVestingAccount) - suite.Require().True(ok) - suite.Require().Equal(tc.msg.Amount, acc.GetVestingCoins(ctx.BlockTime())) - } - } - }) - } -} - -func TestHandlerTestSuite(t *testing.T) { - suite.Run(t, new(HandlerTestSuite)) -} diff --git a/x/auth/vesting/module.go b/x/auth/vesting/module.go index f0594784ca05..272ebf2656bf 100644 --- a/x/auth/vesting/module.go +++ b/x/auth/vesting/module.go @@ -92,9 +92,9 @@ func NewAppModule(ak keeper.AccountKeeper, bk types.BankKeeper) AppModule { // RegisterInvariants performs a no-op; there are no invariants to enforce. func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the module's message router and handler. +// Deprecated: Route returns the module's message router and handler. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.accountKeeper, am.bankKeeper)) + return sdk.Route{} } // QuerierRoute returns an empty string as the module contains no query diff --git a/x/authz/module/module.go b/x/authz/module/module.go index 38b4eb535081..b1afdc06e12f 100644 --- a/x/authz/module/module.go +++ b/x/authz/module/module.go @@ -117,9 +117,9 @@ func (AppModule) Name() string { // RegisterInvariants does nothing, there are no invariants to enforce func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the staking module. +// Deprecated: Route returns the message routing key for the authz module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(authz.RouterKey, nil) + return sdk.Route{} } func (am AppModule) NewHandler() sdk.Handler { diff --git a/x/bank/handler.go b/x/bank/handler.go deleted file mode 100644 index 0fb0f53a4cd8..000000000000 --- a/x/bank/handler.go +++ /dev/null @@ -1,30 +0,0 @@ -package bank - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/bank/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -// NewHandler returns a handler for "bank" type messages. -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgSend: - res, err := msgServer.Send(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgMultiSend: - res, err := msgServer.MultiSend(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized bank message type: %T", msg) - } - } -} diff --git a/x/bank/handler_test.go b/x/bank/handler_test.go deleted file mode 100644 index 5fcdf02f1a93..000000000000 --- a/x/bank/handler_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package bank_test - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/bank" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func TestInvalidMsg(t *testing.T) { - h := bank.NewHandler(nil) - - res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - - _, _, log := sdkerrors.ABCIInfo(err, false) - require.True(t, strings.Contains(log, "unrecognized bank message type")) -} - -// A module account cannot be the recipient of bank sends unless it has been marked as such -func TestSendToModuleAccount(t *testing.T) { - priv1 := secp256k1.GenPrivKey() - addr1 := sdk.AccAddress(priv1.PubKey().Address()) - moduleAccAddr := authtypes.NewModuleAddress(stakingtypes.BondedPoolName) - coins := sdk.Coins{sdk.NewInt64Coin("foocoin", 10)} - - tests := []struct { - name string - expectedError error - msg *types.MsgSend - }{ - { - name: "not allowed module account", - msg: types.NewMsgSend(addr1, moduleAccAddr, coins), - expectedError: sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", moduleAccAddr), - }, - { - name: "allowed module account", - msg: types.NewMsgSend(addr1, authtypes.NewModuleAddress(stakingtypes.ModuleName), coins), - expectedError: nil, - }, - } - - acc1 := &authtypes.BaseAccount{ - Address: addr1.String(), - } - accs := authtypes.GenesisAccounts{acc1} - balances := []types.Balance{ - { - Address: addr1.String(), - Coins: coins, - }, - } - - app := simapp.SetupWithGenesisAccounts(accs, balances...) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - - app.BankKeeper = bankkeeper.NewBaseKeeper( - app.AppCodec(), app.GetKey(types.StoreKey), app.AccountKeeper, app.GetSubspace(types.ModuleName), map[string]bool{ - moduleAccAddr.String(): true, - }, - ) - handler := bank.NewHandler(app.BankKeeper) - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - _, err := handler(ctx, tc.msg) - if tc.expectedError != nil { - require.EqualError(t, err, tc.expectedError.Error()) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/x/bank/module.go b/x/bank/module.go index 3c8918c5a159..55fda845c317 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -123,9 +123,9 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.keeper) } -// Route returns the message routing key for the bank module. +// Deprecated: Route returns the message routing key for the bank module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the bank module's querier route name. diff --git a/x/capability/module.go b/x/capability/module.go index 8047f31f056a..9d298c26e0b1 100644 --- a/x/capability/module.go +++ b/x/capability/module.go @@ -103,8 +103,10 @@ func (am AppModule) Name() string { return am.AppModuleBasic.Name() } -// Route returns the capability module's message routing key. -func (AppModule) Route() sdk.Route { return sdk.Route{} } +// Deprecated: Route returns the capability module's message routing key. +func (AppModule) Route() sdk.Route { + return sdk.Route{} +} // QuerierRoute returns the capability module's query routing key. func (AppModule) QuerierRoute() string { return "" } diff --git a/x/crisis/handler.go b/x/crisis/handler.go deleted file mode 100644 index 0e6cf985f877..000000000000 --- a/x/crisis/handler.go +++ /dev/null @@ -1,25 +0,0 @@ -package crisis - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/crisis/types" -) - -// RouterKey -const RouterKey = types.ModuleName - -func NewHandler(k types.MsgServer) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgVerifyInvariant: - res, err := k.VerifyInvariant(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized crisis message type: %T", msg) - } - } -} diff --git a/x/crisis/handler_test.go b/x/crisis/handler_test.go deleted file mode 100644 index e981a1a78749..000000000000 --- a/x/crisis/handler_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package crisis_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/crisis" - "github.com/cosmos/cosmos-sdk/x/crisis/types" - distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -var ( - testModuleName = "dummy" - dummyRouteWhichPasses = types.NewInvarRoute(testModuleName, "which-passes", func(_ sdk.Context) (string, bool) { return "", false }) - dummyRouteWhichFails = types.NewInvarRoute(testModuleName, "which-fails", func(_ sdk.Context) (string, bool) { return "whoops", true }) -) - -func createTestApp() (*simapp.SimApp, sdk.Context, []sdk.AccAddress) { - app := simapp.Setup(false) - ctx := app.NewContext(false, tmproto.Header{}) - - constantFee := sdk.NewInt64Coin(sdk.DefaultBondDenom, 10) - app.CrisisKeeper.SetConstantFee(ctx, constantFee) - app.StakingKeeper.SetParams(ctx, stakingtypes.DefaultParams()) - - app.CrisisKeeper.RegisterRoute(testModuleName, dummyRouteWhichPasses.Route, dummyRouteWhichPasses.Invar) - app.CrisisKeeper.RegisterRoute(testModuleName, dummyRouteWhichFails.Route, dummyRouteWhichFails.Invar) - - feePool := distrtypes.InitialFeePool() - feePool.CommunityPool = sdk.NewDecCoinsFromCoins(sdk.NewCoins(constantFee)...) - app.DistrKeeper.SetFeePool(ctx, feePool) - - addrs := simapp.AddTestAddrs(app, ctx, 1, sdk.NewInt(10000)) - - return app, ctx, addrs -} - -func TestHandleMsgVerifyInvariant(t *testing.T) { - app, ctx, addrs := createTestApp() - sender := addrs[0] - - cases := []struct { - name string - msg sdk.Msg - expectedResult string - }{ - {"bad invariant route", types.NewMsgVerifyInvariant(sender, testModuleName, "route-that-doesnt-exist"), "fail"}, - {"invariant broken", types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route), "panic"}, - {"invariant passing", types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route), "pass"}, - {"invalid msg", testdata.NewTestMsg(), "fail"}, - } - - for _, tc := range cases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - h := crisis.NewHandler(app.CrisisKeeper) - - switch tc.expectedResult { - case "fail": - res, err := h(ctx, tc.msg) - require.Error(t, err) - require.Nil(t, res) - - case "pass": - res, err := h(ctx, tc.msg) - require.NoError(t, err) - require.NotNil(t, res) - - case "panic": - require.Panics(t, func() { - h(ctx, tc.msg) // nolint:errcheck - }) - } - }) - } -} - -func TestHandleMsgVerifyInvariantWithNotEnoughSenderCoins(t *testing.T) { - app, ctx, addrs := createTestApp() - sender := addrs[0] - coin := app.BankKeeper.GetAllBalances(ctx, sender)[0] - excessCoins := sdk.NewCoin(coin.Denom, coin.Amount.AddRaw(1)) - app.CrisisKeeper.SetConstantFee(ctx, excessCoins) - - h := crisis.NewHandler(app.CrisisKeeper) - msg := types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichPasses.Route) - - res, err := h(ctx, msg) - require.Error(t, err) - require.Nil(t, res) -} - -func TestHandleMsgVerifyInvariantWithInvariantBrokenAndNotEnoughPoolCoins(t *testing.T) { - app, ctx, addrs := createTestApp() - sender := addrs[0] - - // set the community pool to empty - feePool := app.DistrKeeper.GetFeePool(ctx) - feePool.CommunityPool = sdk.DecCoins{} - app.DistrKeeper.SetFeePool(ctx, feePool) - - h := crisis.NewHandler(app.CrisisKeeper) - msg := types.NewMsgVerifyInvariant(sender, testModuleName, dummyRouteWhichFails.Route) - - var res *sdk.Result - require.Panics(t, func() { - res, _ = h(ctx, msg) - }, fmt.Sprintf("%v", res)) -} diff --git a/x/crisis/module.go b/x/crisis/module.go index 4a29fc21736b..428e8adc1a05 100644 --- a/x/crisis/module.go +++ b/x/crisis/module.go @@ -119,9 +119,9 @@ func (AppModule) Name() string { // RegisterInvariants performs a no-op. func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the crisis module. +// Deprecated: Route returns the message routing key for the crisis module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(RouterKey, NewHandler(*am.keeper)) + return sdk.Route{} } // QuerierRoute returns no querier route. diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 279a6bf726de..f356aee32845 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -8,35 +8,6 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgSetWithdrawAddress: - res, err := msgServer.SetWithdrawAddress(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgWithdrawDelegatorReward: - res, err := msgServer.WithdrawDelegatorReward(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgWithdrawValidatorCommission: - res, err := msgServer.WithdrawValidatorCommission(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgFundCommunityPool: - res, err := msgServer.FundCommunityPool(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized distribution message type: %T", msg) - } - } -} - func NewCommunityPoolSpendProposalHandler(k keeper.Keeper) govtypes.Handler { return func(ctx sdk.Context, content govtypes.Content) error { switch c := content.(type) { diff --git a/x/distribution/module.go b/x/distribution/module.go index e66fc6764e5f..09232331e333 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -121,9 +121,9 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.keeper) } -// Route returns the message routing key for the distribution module. +// Deprecated: Route returns the message routing key for the distribution module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the distribution module's querier route name. diff --git a/x/evidence/handler.go b/x/evidence/handler.go deleted file mode 100644 index 93da4d7bf50d..000000000000 --- a/x/evidence/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package evidence - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/evidence/keeper" - "github.com/cosmos/cosmos-sdk/x/evidence/types" -) - -// NewHandler returns a handler for evidence messages. -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgSubmitEvidence: - res, err := msgServer.SubmitEvidence(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/evidence/handler_test.go b/x/evidence/handler_test.go deleted file mode 100644 index f6294f3e5526..000000000000 --- a/x/evidence/handler_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package evidence_test - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/simapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/evidence" - "github.com/cosmos/cosmos-sdk/x/evidence/exported" - "github.com/cosmos/cosmos-sdk/x/evidence/keeper" - "github.com/cosmos/cosmos-sdk/x/evidence/types" -) - -type HandlerTestSuite struct { - suite.Suite - - handler sdk.Handler - app *simapp.SimApp -} - -func testMsgSubmitEvidence(r *require.Assertions, e exported.Evidence, s sdk.AccAddress) exported.MsgSubmitEvidenceI { - msg, err := types.NewMsgSubmitEvidence(s, e) - r.NoError(err) - return msg -} - -func testEquivocationHandler(k interface{}) types.Handler { - return func(ctx sdk.Context, e exported.Evidence) error { - if err := e.ValidateBasic(); err != nil { - return err - } - - ee, ok := e.(*types.Equivocation) - if !ok { - return fmt.Errorf("unexpected evidence type: %T", e) - } - if ee.Height%2 == 0 { - return fmt.Errorf("unexpected even evidence height: %d", ee.Height) - } - - return nil - } -} - -func (suite *HandlerTestSuite) SetupTest() { - checkTx := false - app := simapp.Setup(checkTx) - - // recreate keeper in order to use custom testing types - evidenceKeeper := keeper.NewKeeper( - app.AppCodec(), app.GetKey(types.StoreKey), app.StakingKeeper, app.SlashingKeeper, - ) - router := types.NewRouter() - router = router.AddRoute(types.RouteEquivocation, testEquivocationHandler(*evidenceKeeper)) - evidenceKeeper.SetRouter(router) - - app.EvidenceKeeper = *evidenceKeeper - - suite.handler = evidence.NewHandler(*evidenceKeeper) - suite.app = app -} - -func (suite *HandlerTestSuite) TestMsgSubmitEvidence() { - pk := ed25519.GenPrivKey() - s := sdk.AccAddress("test________________") - - testCases := []struct { - msg sdk.Msg - expectErr bool - }{ - { - testMsgSubmitEvidence( - suite.Require(), - &types.Equivocation{ - Height: 11, - Time: time.Now().UTC(), - Power: 100, - ConsensusAddress: pk.PubKey().Address().String(), - }, - s, - ), - false, - }, - { - testMsgSubmitEvidence( - suite.Require(), - &types.Equivocation{ - Height: 10, - Time: time.Now().UTC(), - Power: 100, - ConsensusAddress: pk.PubKey().Address().String(), - }, - s, - ), - true, - }, - } - - for i, tc := range testCases { - ctx := suite.app.BaseApp.NewContext(false, tmproto.Header{Height: suite.app.LastBlockHeight() + 1}) - - res, err := suite.handler(ctx, tc.msg) - if tc.expectErr { - suite.Require().Error(err, "expected error; tc #%d", i) - } else { - suite.Require().NoError(err, "unexpected error; tc #%d", i) - suite.Require().NotNil(res, "expected non-nil result; tc #%d", i) - - msg := tc.msg.(exported.MsgSubmitEvidenceI) - - var resultData types.MsgSubmitEvidenceResponse - suite.app.AppCodec().Unmarshal(res.Data, &resultData) - suite.Require().Equal(msg.GetEvidence().Hash().Bytes(), resultData.Hash, "invalid hash; tc #%d", i) - } - } -} - -func TestHandlerTestSuite(t *testing.T) { - suite.Run(t, new(HandlerTestSuite)) -} diff --git a/x/evidence/keeper/keeper_test.go b/x/evidence/keeper/keeper_test.go index 102f9773e61d..5352867de1fd 100644 --- a/x/evidence/keeper/keeper_test.go +++ b/x/evidence/keeper/keeper_test.go @@ -19,7 +19,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/keeper" "github.com/cosmos/cosmos-sdk/x/evidence/types" - "github.com/cosmos/cosmos-sdk/x/staking" ) var ( @@ -77,7 +76,6 @@ type KeeperTestSuite struct { app *simapp.SimApp queryClient types.QueryClient - stakingHdl sdk.Handler } func (suite *KeeperTestSuite) SetupTest() { @@ -106,7 +104,6 @@ func (suite *KeeperTestSuite) SetupTest() { queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, app.EvidenceKeeper) suite.queryClient = types.NewQueryClient(queryHelper) - suite.stakingHdl = staking.NewHandler(app.StakingKeeper) } func (suite *KeeperTestSuite) populateEvidence(ctx sdk.Context, numEvidence int) []exported.Evidence { diff --git a/x/evidence/module.go b/x/evidence/module.go index 367716dd746b..cb6a358c164a 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -126,9 +126,9 @@ func (am AppModule) Name() string { return am.AppModuleBasic.Name() } -// Route returns the evidence module's message routing key. +// Deprecated: Route returns the evidence module's message routing key. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the evidence module's query routing key. diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go index ec2a5b0f6d4b..44b673d809b0 100644 --- a/x/feegrant/client/cli/tx.go +++ b/x/feegrant/client/cli/tx.go @@ -46,7 +46,7 @@ func GetTxCmd() *cobra.Command { // NewCmdFeeGrant returns a CLI command handler for creating a MsgGrantAllowance transaction. func NewCmdFeeGrant() *cobra.Command { cmd := &cobra.Command{ - Use: "grant [granter] [grantee]", + Use: "grant [granter_key_or_address] [grantee]", Short: "Grant Fee allowance to an address", Long: strings.TrimSpace( fmt.Sprintf( @@ -63,10 +63,6 @@ Examples: ), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - _, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - return err - } cmd.Flags().Set(flags.FlagFrom, args[0]) clientCtx, err := client.GetClientTxContext(cmd) diff --git a/x/feegrant/client/testutil/suite.go b/x/feegrant/client/testutil/suite.go index 74fe155e66ea..0c716fec2c45 100644 --- a/x/feegrant/client/testutil/suite.go +++ b/x/feegrant/client/testutil/suite.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/suite" tmcli "github.com/tendermint/tendermint/libs/cli" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -258,6 +259,10 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { alreadyExistedGrantee := s.addedGrantee clientCtx := val.ClientCtx + fromAddr, fromName, _, err := client.GetFromFields(clientCtx.Keyring, granter.String(), clientCtx.GenerateOnly) + s.Require().Equal(fromAddr, granter) + s.Require().NoError(err) + commonFlags := []string{ fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), @@ -297,6 +302,19 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { ), true, 0, nil, }, + { + "wrong granter key name", + append( + []string{ + "invalid_granter", + "cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, granter), + }, + commonFlags..., + ), + true, 0, nil, + }, { "valid basic fee grant", append( @@ -310,6 +328,19 @@ func (s *IntegrationTestSuite) TestNewCmdFeeGrant() { ), false, 0, &sdk.TxResponse{}, }, + { + "valid basic fee grant with granter key name", + append( + []string{ + fromName, + "cosmos16dun6ehcc86e03wreqqww89ey569wuj4em572w", + fmt.Sprintf("--%s=%s", cli.FlagSpendLimit, "100stake"), + fmt.Sprintf("--%s=%s", flags.FlagFrom, fromName), + }, + commonFlags..., + ), + false, 0, &sdk.TxResponse{}, + }, { "valid basic fee grant with amino", append( diff --git a/x/feegrant/module/module.go b/x/feegrant/module/module.go index b53a4ad5ad55..df6089b40fba 100644 --- a/x/feegrant/module/module.go +++ b/x/feegrant/module/module.go @@ -132,9 +132,9 @@ func (AppModule) Name() string { // RegisterInvariants registers the feegrant module invariants. func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} -// Route returns the message routing key for the feegrant module. +// Deprecated: Route returns the message routing key for the feegrant module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(feegrant.RouterKey, nil) + return sdk.Route{} } // NewHandler returns an sdk.Handler for the feegrant module. diff --git a/x/gov/abci_test.go b/x/gov/abci_test.go index 7681bae95cf6..168e544c9f4b 100644 --- a/x/gov/abci_test.go +++ b/x/gov/abci_test.go @@ -4,7 +4,6 @@ import ( "testing" "time" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -12,8 +11,11 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/keeper" "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestTickExpiredDepositPeriod(t *testing.T) { @@ -24,7 +26,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - govHandler := gov.NewHandler(app.GovKeeper) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) @@ -37,7 +39,8 @@ func TestTickExpiredDepositPeriod(t *testing.T) { ) require.NoError(t, err) - res, err := govHandler(ctx, newProposalMsg) + wrapCtx := sdk.WrapSDKContext(ctx) + res, err := govMsgSvr.SubmitProposal(wrapCtx, newProposalMsg) require.NoError(t, err) require.NotNil(t, res) @@ -76,7 +79,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - govHandler := gov.NewHandler(app.GovKeeper) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) @@ -89,7 +92,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { ) require.NoError(t, err) - res, err := govHandler(ctx, newProposalMsg) + res, err := govMsgSvr.SubmitProposal(sdk.WrapSDKContext(ctx), newProposalMsg) require.NoError(t, err) require.NotNil(t, res) @@ -112,7 +115,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { ) require.NoError(t, err) - res, err = govHandler(ctx, newProposalMsg2) + res, err = govMsgSvr.SubmitProposal(sdk.WrapSDKContext(ctx), newProposalMsg2) require.NoError(t, err) require.NotNil(t, res) @@ -153,7 +156,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - govHandler := gov.NewHandler(app.GovKeeper) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) @@ -169,15 +172,11 @@ func TestTickPassedDepositPeriod(t *testing.T) { ) require.NoError(t, err) - res, err := govHandler(ctx, newProposalMsg) + res, err := govMsgSvr.SubmitProposal(sdk.WrapSDKContext(ctx), newProposalMsg) require.NoError(t, err) require.NotNil(t, res) - var proposalData types.MsgSubmitProposalResponse - err = proto.Unmarshal(res.Data, &proposalData) - require.NoError(t, err) - - proposalID := proposalData.ProposalId + proposalID := res.ProposalId inactiveQueue = app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) @@ -193,9 +192,9 @@ func TestTickPassedDepositPeriod(t *testing.T) { newDepositMsg := types.NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 5)}) - res, err = govHandler(ctx, newDepositMsg) + res1, err := govMsgSvr.Deposit(sdk.WrapSDKContext(ctx), newDepositMsg) require.NoError(t, err) - require.NotNil(t, res) + require.NotNil(t, res1) activeQueue = app.GovKeeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, activeQueue.Valid()) @@ -212,7 +211,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) - govHandler := gov.NewHandler(app.GovKeeper) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) inactiveQueue := app.GovKeeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) require.False(t, inactiveQueue.Valid()) @@ -225,15 +224,13 @@ func TestTickPassedVotingPeriod(t *testing.T) { newProposalMsg, err := types.NewMsgSubmitProposal(TestProposal, proposalCoins, addrs[0]) require.NoError(t, err) - res, err := govHandler(ctx, newProposalMsg) - require.NoError(t, err) - require.NotNil(t, res) + wrapCtx := sdk.WrapSDKContext(ctx) - var proposalData types.MsgSubmitProposalResponse - err = proto.Unmarshal(res.Data, &proposalData) + res, err := govMsgSvr.SubmitProposal(wrapCtx, newProposalMsg) require.NoError(t, err) + require.NotNil(t, res) - proposalID := proposalData.ProposalId + proposalID := res.ProposalId newHeader := ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) @@ -241,9 +238,9 @@ func TestTickPassedVotingPeriod(t *testing.T) { newDepositMsg := types.NewMsgDeposit(addrs[1], proposalID, proposalCoins) - res, err = govHandler(ctx, newDepositMsg) + res1, err := govMsgSvr.Deposit(wrapCtx, newDepositMsg) require.NoError(t, err) - require.NotNil(t, res) + require.NotNil(t, res1) newHeader = ctx.BlockHeader() newHeader.Time = ctx.BlockHeader().Time.Add(app.GovKeeper.GetDepositParams(ctx).MaxDepositPeriod).Add(app.GovKeeper.GetVotingParams(ctx).VotingPeriod) @@ -277,15 +274,15 @@ func TestProposalPassedEndblocker(t *testing.T) { SortAddresses(addrs) - handler := gov.NewHandler(app.GovKeeper) - stakingHandler := staking.NewHandler(app.StakingKeeper) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) + stakingMsgSvr := stakingkeeper.NewMsgServerImpl(app.StakingKeeper) header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) valAddr := sdk.ValAddress(addrs[0]) - createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10}) + createValidators(t, stakingMsgSvr, ctx, []sdk.ValAddress{valAddr}, []int64{10}) staking.EndBlocker(ctx, app.StakingKeeper) macc := app.GovKeeper.GetGovernanceAccount(ctx) @@ -298,7 +295,9 @@ func TestProposalPassedEndblocker(t *testing.T) { proposalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 10))} newDepositMsg := types.NewMsgDeposit(addrs[0], proposal.ProposalId, proposalCoins) - handleAndCheck(t, handler, ctx, newDepositMsg) + res, err := govMsgSvr.Deposit(sdk.WrapSDKContext(ctx), newDepositMsg) + require.NoError(t, err) + require.NotNil(t, res) macc = app.GovKeeper.GetGovernanceAccount(ctx) require.NotNil(t, macc) @@ -328,13 +327,13 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { SortAddresses(addrs) - stakingHandler := staking.NewHandler(app.StakingKeeper) + stakingMsgSvr := stakingkeeper.NewMsgServerImpl(app.StakingKeeper) header := tmproto.Header{Height: app.LastBlockHeight() + 1} app.BeginBlock(abci.RequestBeginBlock{Header: header}) valAddr := sdk.ValAddress(addrs[0]) - createValidators(t, stakingHandler, ctx, []sdk.ValAddress{valAddr}, []int64{10}) + createValidators(t, stakingMsgSvr, ctx, []sdk.ValAddress{valAddr}, []int64{10}) staking.EndBlocker(ctx, app.StakingKeeper) // Create a proposal where the handler will pass for the test proposal @@ -346,7 +345,10 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { proposalCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 10))) newDepositMsg := types.NewMsgDeposit(addrs[0], proposal.ProposalId, proposalCoins) - handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg) + govMsgSvr := keeper.NewMsgServerImpl(app.GovKeeper) + res, err := govMsgSvr.Deposit(sdk.WrapSDKContext(ctx), newDepositMsg) + require.NoError(t, err) + require.NotNil(t, res) err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) require.NoError(t, err) @@ -362,3 +364,19 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { // validate that the proposal fails/has been rejected gov.EndBlocker(ctx, app.GovKeeper) } + +func createValidators(t *testing.T, stakingMsgSvr stakingtypes.MsgServer, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { + require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") + + for i := 0; i < len(addrs); i++ { + valTokens := sdk.TokensFromConsensusPower(powerAmt[i], sdk.DefaultPowerReduction) + valCreateMsg, err := stakingtypes.NewMsgCreateValidator( + addrs[i], pubkeys[i], sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + TestDescription, TestCommissionRates, sdk.OneInt(), + ) + require.NoError(t, err) + res, err := stakingMsgSvr.CreateValidator(sdk.WrapSDKContext(ctx), valCreateMsg) + require.NoError(t, err) + require.NotNil(t, res) + } +} diff --git a/x/gov/common_test.go b/x/gov/common_test.go index 3ff367e15972..e0a79835280c 100644 --- a/x/gov/common_test.go +++ b/x/gov/common_test.go @@ -4,9 +4,6 @@ import ( "bytes" "log" "sort" - "testing" - - "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -77,23 +74,3 @@ var ( ed25519.GenPrivKey().PubKey(), } ) - -func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { - require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") - - for i := 0; i < len(addrs); i++ { - valTokens := sdk.TokensFromConsensusPower(powerAmt[i], sdk.DefaultPowerReduction) - valCreateMsg, err := stakingtypes.NewMsgCreateValidator( - addrs[i], pubkeys[i], sdk.NewCoin(sdk.DefaultBondDenom, valTokens), - TestDescription, TestCommissionRates, sdk.OneInt(), - ) - require.NoError(t, err) - handleAndCheck(t, stakingHandler, ctx, valCreateMsg) - } -} - -func handleAndCheck(t *testing.T, h sdk.Handler, ctx sdk.Context, msg sdk.Msg) { - res, err := h(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) -} diff --git a/x/gov/handler.go b/x/gov/handler.go deleted file mode 100644 index 6af451d3dd31..000000000000 --- a/x/gov/handler.go +++ /dev/null @@ -1,38 +0,0 @@ -package gov - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/gov/keeper" - "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -// NewHandler creates an sdk.Handler for all the gov type messages -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgDeposit: - res, err := msgServer.Deposit(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgSubmitProposal: - res, err := msgServer.SubmitProposal(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgVote: - res, err := msgServer.Vote(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgVoteWeighted: - res, err := msgServer.VoteWeighted(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/gov/handler_test.go b/x/gov/handler_test.go deleted file mode 100644 index a62134e4259e..000000000000 --- a/x/gov/handler_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package gov_test - -import ( - "strings" - "testing" - - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/testutil/testdata" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/gov/keeper" -) - -func TestInvalidMsg(t *testing.T) { - k := keeper.Keeper{} - h := gov.NewHandler(k) - - res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized gov message type")) -} diff --git a/x/gov/module.go b/x/gov/module.go index a8121e3ff225..659e5559275c 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -130,9 +130,9 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.keeper, am.bankKeeper) } -// Route returns the message routing key for the gov module. +// Deprecated: Route returns the message routing key for the gov module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the gov module's querier route name. diff --git a/x/mint/module.go b/x/mint/module.go index f502acc87c7f..e923790dc0fb 100644 --- a/x/mint/module.go +++ b/x/mint/module.go @@ -107,7 +107,7 @@ func (AppModule) Name() string { // RegisterInvariants registers the mint module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the mint module. +// Deprecated: Route returns the message routing key for the mint module. func (AppModule) Route() sdk.Route { return sdk.Route{} } // QuerierRoute returns the mint module's querier route name. diff --git a/x/params/module.go b/x/params/module.go index 3f14475cd087..e27eaf44a2ef 100644 --- a/x/params/module.go +++ b/x/params/module.go @@ -95,7 +95,10 @@ func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONCodec, _ json.RawMess return []abci.ValidatorUpdate{} } -func (AppModule) Route() sdk.Route { return sdk.Route{} } +// Deprecated: Route returns the message routing key for the params module. +func (AppModule) Route() sdk.Route { + return sdk.Route{} +} // GenerateGenesisState performs a no-op. func (AppModule) GenerateGenesisState(simState *module.SimulationState) {} diff --git a/x/slashing/handler.go b/x/slashing/handler.go deleted file mode 100644 index eab71edb4188..000000000000 --- a/x/slashing/handler.go +++ /dev/null @@ -1,26 +0,0 @@ -package slashing - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -// NewHandler creates an sdk.Handler for all the slashing type messages -func NewHandler(k keeper.Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - msgServer := keeper.NewMsgServerImpl(k) - - switch msg := msg.(type) { - case *types.MsgUnjail: - res, err := msgServer.Unjail(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go deleted file mode 100644 index e6b6af64b68d..000000000000 --- a/x/slashing/handler_test.go +++ /dev/null @@ -1,295 +0,0 @@ -package slashing_test - -import ( - "errors" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/testslashing" - "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func TestCannotUnjailUnlessJailed(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - slh := slashing.NewHandler(app.SlashingKeeper) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - - amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) - staking.EndBlocker(ctx, app.StakingKeeper) - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, - ) - require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) - - // assert non-jailed validator can't be unjailed - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(types.ErrValidatorNotJailed, err)) -} - -func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - slh := slashing.NewHandler(app.SlashingKeeper) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - msg := tstaking.CreateValidatorMsg(addr, val, amt) - msg.MinSelfDelegation = amt - tstaking.Handle(msg, true) - - staking.EndBlocker(ctx, app.StakingKeeper) - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))}, - ) - - tstaking.Undelegate(sdk.AccAddress(addr), addr, sdk.OneInt(), true) - require.True(t, app.StakingKeeper.Validator(ctx, addr).IsJailed()) - - // assert non-jailed validator can't be unjailed - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(types.ErrSelfDelegationTooLowToUnjail, err)) -} - -func TestJailedValidatorDelegations(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) - pks := simapp.CreateTestPubKeys(3) - - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 20)) - app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - stakingParams := app.StakingKeeper.GetParams(ctx) - app.StakingKeeper.SetParams(ctx, stakingParams) - valAddr, consAddr := sdk.ValAddress(pks[1].Address()), sdk.ConsAddress(pks[0].Address()) - - amt := tstaking.CreateValidatorWithValPower(valAddr, pks[1], 10, true) - staking.EndBlocker(ctx, app.StakingKeeper) - - // set dummy signing info - newInfo := types.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) - app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) - - // delegate tokens to the validator - delAddr := sdk.AccAddress(pks[2].Address()) - tstaking.Delegate(delAddr, valAddr, amt) - - // unbond validator total self-delegations (which should jail the validator) - valAcc := sdk.AccAddress(valAddr) - tstaking.Undelegate(valAcc, valAddr, amt, true) - _, err := app.StakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) - require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) - - // verify validator still exists and is jailed - validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.True(t, validator.IsJailed()) - - // verify the validator cannot unjail itself - res, err := slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) - require.Error(t, err) - require.Nil(t, res) - - // self-delegate to validator - tstaking.Delegate(valAcc, valAddr, amt) - - // verify the validator can now unjail itself - res, err = slashing.NewHandler(app.SlashingKeeper)(ctx, types.NewMsgUnjail(valAddr)) - require.NoError(t, err) - require.NotNil(t, res) -} - -func TestInvalidMsg(t *testing.T) { - k := keeper.Keeper{} - h := slashing.NewHandler(k) - - res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) -} - -// Test a validator through uptime, downtime, revocation, -// unrevocation, starting height reset, and revocation again -func TestHandleAbsentValidator(t *testing.T) { - // initial setup - app := simapp.Setup(false) - ctx := app.BaseApp.NewContext(false, tmproto.Header{Time: time.Unix(0, 0)}) - pks := simapp.CreateTestPubKeys(1) - simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) - app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) - - power := int64(100) - addr, val := sdk.ValAddress(pks[0].Address()), pks[0] - slh := slashing.NewHandler(app.SlashingKeeper) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) - staking.EndBlocker(ctx, app.StakingKeeper) - - require.Equal( - t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), - ) - require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) - - // will exist since the validator has been bonded - info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.IndexOffset) - require.Equal(t, int64(0), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - height := int64(0) - - // 1000 first blocks OK - for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // 500 blocks missed - for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx)); height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) - - // validator should be bonded still - validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - bondPool := app.StakingKeeper.GetBondedPool(ctx) - require.True(sdk.IntEq(t, amt, app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) - - // 501st block missed - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // counter now reset to zero - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should have been jailed - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) - - slashAmt := amt.ToDec().Mul(app.SlashingKeeper.SlashFractionDowntime(ctx)).RoundInt() - - // validator should have been slashed - require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) - - // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) - height++ - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should not have been slashed any more, since it was already jailed - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.True(t, amt.Sub(slashAmt).Equal(validator.GetTokens())) - - // unrevocation should fail prior to jail expiration - res, err := slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - - // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(1, 0).Add(app.SlashingKeeper.DowntimeJailDuration(ctx))}) - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should be rebonded now - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - // validator should have been slashed - require.True(t, amt.Sub(slashAmt).Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) - - // Validator start height should not have been changed - info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // validator should not be immediately jailed again - height++ - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) - - // 500 signed blocks - nextHeight := height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 - for ; height < nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator should be jailed again after 500 unsigned blocks - nextHeight = height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 - for ; height <= nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, app.StakingKeeper) - - validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) -} diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go index 7f3d42b0a7ed..c0cf249855f8 100644 --- a/x/slashing/keeper/keeper_test.go +++ b/x/slashing/keeper/keeper_test.go @@ -42,7 +42,9 @@ func TestUnJailNotBonded(t *testing.T) { amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 50) msg := tstaking.CreateValidatorMsg(addr, val, amt) msg.MinSelfDelegation = amt - tstaking.Handle(msg, true) + res, err := tstaking.CreateValidatorWithMsg(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + require.NotNil(t, res) staking.EndBlocker(ctx, app.StakingKeeper) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) diff --git a/x/slashing/module.go b/x/slashing/module.go index 030a26c5917c..68c3c2919cdd 100644 --- a/x/slashing/module.go +++ b/x/slashing/module.go @@ -119,9 +119,9 @@ func (AppModule) Name() string { // RegisterInvariants registers the slashing module invariants. func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route returns the message routing key for the slashing module. +// Deprecated: Route returns the message routing key for the slashing module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the slashing module's querier route name. diff --git a/x/staking/handler.go b/x/staking/handler.go deleted file mode 100644 index 6d89c7a29c53..000000000000 --- a/x/staking/handler.go +++ /dev/null @@ -1,41 +0,0 @@ -package staking - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func NewHandler(k keeper.Keeper) sdk.Handler { - msgServer := keeper.NewMsgServerImpl(k) - - return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { - ctx = ctx.WithEventManager(sdk.NewEventManager()) - - switch msg := msg.(type) { - case *types.MsgCreateValidator: - res, err := msgServer.CreateValidator(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgEditValidator: - res, err := msgServer.EditValidator(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgDelegate: - res, err := msgServer.Delegate(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgBeginRedelegate: - res, err := msgServer.BeginRedelegate(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - case *types.MsgUndelegate: - res, err := msgServer.Undelegate(sdk.WrapSDKContext(ctx), msg) - return sdk.WrapServiceResult(ctx, res, err) - - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg) - } - } -} diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go deleted file mode 100644 index 240ddfee6a5a..000000000000 --- a/x/staking/handler_test.go +++ /dev/null @@ -1,1217 +0,0 @@ -package staking_test - -import ( - "strings" - "testing" - "time" - - "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmtypes "github.com/tendermint/tendermint/types" - - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/cosmos/cosmos-sdk/x/staking/teststaking" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -func bootstrapHandlerGenesisTest(t *testing.T, power int64, numAddrs int, accAmount sdk.Int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { - _, app, ctx := getBaseSimappWithCustomKeeper() - - addrDels, addrVals := generateAddresses(app, ctx, numAddrs, accAmount) - - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels))))) - - notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx) - - // set non bonded pool balance - app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) - require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) - return app, ctx, addrDels, addrVals -} - -func TestValidatorByPowerIndex(t *testing.T) { - initPower := int64(1000000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, validatorAddr3 := valAddrs[0], valAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create validator - initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) - - // must end-block - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - // verify the self-delegation exists - bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found) - gotBond := bond.Shares.RoundInt() - require.Equal(t, initBond, gotBond) - - // verify that the by power index exists - validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - power := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) - require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) - - // create a second validator keep it bonded - tstaking.CreateValidatorWithValPower(validatorAddr3, PKs[2], initPower, true) - - // must end-block - updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - // slash and jail the first validator - consAddr0 := sdk.ConsAddress(PKs[0].Address()) - app.StakingKeeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) - app.StakingKeeper.Jail(ctx, consAddr0) - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - - validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.Equal(t, types.Unbonding, validator.Status) // ensure is unbonding - require.Equal(t, initBond.QuoRaw(2), validator.Tokens) // ensure tokens slashed - app.StakingKeeper.Unjail(ctx, consAddr0) - - // the old power record should have been deleted as the power changed - require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) - - // but the new power record should have been created - validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - power2 := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) - require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power2)) - - // now the new record power index should be the same as the original record - power3 := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) - require.Equal(t, power2, power3) - - // unbond self-delegation - totalBond := validator.TokensFromShares(bond.GetShares()).TruncateInt() - res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, totalBond, true) - - var resData types.MsgUndelegateResponse - err = proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - staking.EndBlocker(ctx, app.StakingKeeper) - staking.EndBlocker(ctx, app.StakingKeeper) - - // verify that by power key nolonger exists - _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.False(t, found) - require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power3)) -} - -func TestDuplicatesMsgCreateValidator(t *testing.T) { - initPower := int64(1000000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - addr1, addr2 := valAddrs[0], valAddrs[1] - pk1, pk2 := PKs[0], PKs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - valTokens := tstaking.CreateValidatorWithValPower(addr1, pk1, 10, true) - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - - validator := tstaking.CheckValidator(addr1, types.Bonded, false) - assert.Equal(t, addr1.String(), validator.OperatorAddress) - consKey, err := validator.TmConsPublicKey() - require.NoError(t, err) - tmPk1, err := cryptocodec.ToTmProtoPublicKey(pk1) - require.NoError(t, err) - assert.Equal(t, tmPk1, consKey) - assert.Equal(t, valTokens, validator.BondedTokens()) - assert.Equal(t, valTokens.ToDec(), validator.DelegatorShares) - assert.Equal(t, types.Description{}, validator.Description) - - // two validators can't have the same operator address - tstaking.CreateValidator(addr1, pk2, valTokens, false) - - // two validators can't have the same pubkey - tstaking.CreateValidator(addr2, pk1, valTokens, false) - - // must have different pubkey and operator - tstaking.CreateValidator(addr2, pk2, valTokens, true) - - // must end-block - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - validator = tstaking.CheckValidator(addr2, types.Bonded, false) - assert.Equal(t, addr2.String(), validator.OperatorAddress) - consPk, err := validator.TmConsPublicKey() - require.NoError(t, err) - tmPk2, err := cryptocodec.ToTmProtoPublicKey(pk2) - require.NoError(t, err) - assert.Equal(t, tmPk2, consPk) - assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) - assert.True(sdk.DecEq(t, valTokens.ToDec(), validator.DelegatorShares)) - assert.Equal(t, types.Description{}, validator.Description) -} - -func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ - Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}}, - }) - - addr := valAddrs[0] - invalidPk := secp256k1.GenPrivKey().PubKey() - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // invalid pukKey type should not be allowed - tstaking.CreateValidator(addr, invalidPk, sdk.NewInt(10), false) -} - -func TestBothPubKeyTypesMsgCreateValidator(t *testing.T) { - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, 1000, 2, sdk.NewInt(1000)) - ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ - Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519, tmtypes.ABCIPubKeyTypeSecp256k1}}, - }) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - testCases := []struct { - name string - addr sdk.ValAddress - pk cryptotypes.PubKey - }{ - { - "can create a validator with ed25519 pubkey", - valAddrs[0], - ed25519.GenPrivKey().PubKey(), - }, - { - "can create a validator with secp256k1 pubkey", - valAddrs[1], - secp256k1.GenPrivKey().PubKey(), - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(*testing.T) { - tstaking.CreateValidator(tc.addr, tc.pk, sdk.NewInt(10), true) - }) - } -} - -func TestLegacyValidatorDelegations(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - valAddr := valAddrs[0] - valConsPubKey, valConsAddr := PKs[0], sdk.ConsAddress(PKs[0].Address()) - delAddr := delAddrs[1] - - // create validator - bondAmount := tstaking.CreateValidatorWithValPower(valAddr, valConsPubKey, 10, true) - - // must end-block - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - // verify the validator exists and has the correct attributes - validator := tstaking.CheckValidator(valAddr, types.Bonded, false) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) - require.Equal(t, bondAmount, validator.BondedTokens()) - - // delegate tokens to the validator - tstaking.Delegate(delAddr, valAddr, bondAmount) - - // verify validator bonded shares - validator = tstaking.CheckValidator(valAddr, types.Bonded, false) - require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) - require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens()) - - // unbond validator total self-delegations (which should jail the validator) - res := tstaking.Undelegate(sdk.AccAddress(valAddr), valAddr, bondAmount, true) - - var resData types.MsgUndelegateResponse - err = proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - tstaking.Ctx = ctx - staking.EndBlocker(ctx, app.StakingKeeper) - - // verify the validator record still exists, is jailed, and has correct tokens - validator = tstaking.CheckValidator(valAddr, -1, true) - require.Equal(t, bondAmount, validator.Tokens) - - // verify delegation still exists - bond, found := app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) - require.True(t, found) - require.Equal(t, bondAmount, bond.Shares.RoundInt()) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) - - // verify the validator can still self-delegate - tstaking.Delegate(sdk.AccAddress(valAddr), valAddr, bondAmount) - - // verify validator bonded shares - validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) - require.Equal(t, bondAmount.MulRaw(2), validator.Tokens) - - // unjail the validator now that is has non-zero self-delegated shares - app.StakingKeeper.Unjail(ctx, valConsAddr) - - // verify the validator can now accept delegations - tstaking.Delegate(delAddr, valAddr, bondAmount) - - // verify validator bonded shares - validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) - require.Equal(t, bondAmount.MulRaw(3), validator.Tokens) - - // verify new delegation - bond, found = app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) - require.True(t, found) - require.Equal(t, bondAmount.MulRaw(2), bond.Shares.RoundInt()) - require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) -} - -func TestIncrementsMsgDelegate(t *testing.T) { - initPower := int64(1000) - initBond := sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - params := app.StakingKeeper.GetParams(ctx) - validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // first create validator - bondAmount := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) - - // apply TM updates - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - - validator := tstaking.CheckValidator(validatorAddr, types.Bonded, false) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) - require.Equal(t, bondAmount, validator.BondedTokens(), "validator: %v", validator) - - tstaking.CheckDelegator(delegatorAddr, validatorAddr, false) - - bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found) - require.Equal(t, bondAmount, bond.Shares.RoundInt()) - - bondedTokens := app.StakingKeeper.TotalBondedTokens(ctx) - require.Equal(t, bondAmount, bondedTokens) - - for i := int64(0); i < 5; i++ { - ctx = ctx.WithBlockHeight(i) - tstaking.Ctx = ctx - tstaking.Delegate(delegatorAddr, validatorAddr, bondAmount) - - //Check that the accounts and the bond account have the appropriate values - validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) - require.True(t, found) - - expBond := bondAmount.MulRaw(i + 1) - expDelegatorShares := bondAmount.MulRaw(i + 2) // (1 self delegation) - expDelegatorAcc := initBond.Sub(expBond) - - gotBond := bond.Shares.RoundInt() - gotDelegatorShares := validator.DelegatorShares.RoundInt() - gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount - - require.Equal(t, expBond, gotBond, - "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", - i, expBond, gotBond, validator, bond) - require.Equal(t, expDelegatorShares, gotDelegatorShares, - "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", - i, expDelegatorShares, gotDelegatorShares, validator, bond) - require.Equal(t, expDelegatorAcc, gotDelegatorAcc, - "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", - i, expDelegatorAcc, gotDelegatorAcc, validator, bond) - } -} - -func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) { - initPower := int64(100) - initBond := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - validatorAddr := valAddrs[0] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create validator - msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) - msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) - tstaking.Handle(msgCreateValidator, true) - - // must end-block - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - // verify the self-delegation exists - bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found) - gotBond := bond.Shares.RoundInt() - require.Equal(t, initBond, gotBond, - "initBond: %v\ngotBond: %v\nbond: %v\n", - initBond, gotBond, bond) - - newMinSelfDelegation := sdk.OneInt() - msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) - tstaking.Handle(msgEditValidator, false) -} - -func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) { - initPower := int64(100) - initBond := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) - - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr := valAddrs[0] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create validator - msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) - msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) - tstaking.Handle(msgCreateValidator, true) - - // must end-block - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 1, len(updates)) - - // verify the self-delegation exists - bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found) - gotBond := bond.Shares.RoundInt() - require.Equal(t, initBond, gotBond, - "initBond: %v\ngotBond: %v\nbond: %v\n", - initBond, gotBond, bond) - - newMinSelfDelegation := initBond.Add(sdk.OneInt()) - msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) - tstaking.Handle(msgEditValidator, false) -} - -func TestIncrementsMsgUnbond(t *testing.T) { - initPower := int64(1000) - - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - params := app.StakingKeeper.GetParams(ctx) - denom := params.BondDenom - - // create validator, delegate - validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] - initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) - - // initial balance - amt1 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount - - tstaking.Delegate(delegatorAddr, validatorAddr, initBond) - - // balance should have been subtracted after delegation - amt2 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount - require.True(sdk.IntEq(t, amt1.Sub(initBond), amt2)) - - // apply TM updates - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - - validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.RoundInt()) - require.Equal(t, initBond.MulRaw(2), validator.BondedTokens()) - - // just send the same msgUnbond multiple times - // TODO use decimals here - unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgUndelegate := types.NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) - numUnbonds := int64(5) - - for i := int64(0); i < numUnbonds; i++ { - res := tstaking.Handle(msgUndelegate, true) - - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - tstaking.Ctx = ctx - staking.EndBlocker(ctx, app.StakingKeeper) - - // check that the accounts and the bond account have the appropriate values - validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) - require.True(t, found) - - expBond := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) - expDelegatorShares := initBond.MulRaw(2).Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) - expDelegatorAcc := initBond.Sub(expBond) - - gotBond := bond.Shares.RoundInt() - gotDelegatorShares := validator.DelegatorShares.RoundInt() - gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount - - require.Equal(t, expBond, gotBond, - "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", - i, expBond, gotBond, validator, bond) - require.Equal(t, expDelegatorShares, gotDelegatorShares, - "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", - i, expDelegatorShares, gotDelegatorShares, validator, bond) - require.Equal(t, expDelegatorAcc, gotDelegatorAcc, - "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", - i, expDelegatorAcc, gotDelegatorAcc, validator, bond) - } - - // these are more than we have bonded now - errorCases := []sdk.Int{ - //1<<64 - 1, // more than int64 power - //1<<63 + 1, // more than int64 power - app.StakingKeeper.TokensFromConsensusPower(ctx, 1<<63-1), - app.StakingKeeper.TokensFromConsensusPower(ctx, 1<<31), - initBond, - } - - for _, c := range errorCases { - tstaking.Undelegate(delegatorAddr, validatorAddr, c, false) - } - - // should be able to unbond remaining - leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds))) - tstaking.Undelegate(delegatorAddr, validatorAddr, leftBonded, true) -} - -func TestMultipleMsgCreateValidator(t *testing.T) { - initPower := int64(1000) - initTokens := sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - params := app.StakingKeeper.GetParams(ctx) - blockTime := time.Now().UTC() - ctx = ctx.WithBlockTime(blockTime) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - validatorAddrs := []sdk.ValAddress{ - valAddrs[0], - valAddrs[1], - valAddrs[2], - } - delegatorAddrs := []sdk.AccAddress{ - delAddrs[0], - delAddrs[1], - delAddrs[2], - } - - // bond them all - amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) - for i, validatorAddr := range validatorAddrs { - tstaking.CreateValidator(validatorAddr, PKs[i], amt, true) - // verify that the account is bonded - validators := app.StakingKeeper.GetValidators(ctx, 100) - require.Equal(t, (i + 1), len(validators)) - - val := validators[i] - balanceExpd := initTokens.Sub(amt) - balanceGot := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount - - require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) - require.Equal(t, amt, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", amt, val.DelegatorShares) - require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) - } - - staking.EndBlocker(ctx, app.StakingKeeper) - - // unbond them all by removing delegation - for i, validatorAddr := range validatorAddrs { - _, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - - res := tstaking.Undelegate(delegatorAddrs[i], validatorAddr, amt, true) - - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - // adds validator into unbonding queue - staking.EndBlocker(ctx, app.StakingKeeper) - - // removes validator from queue and set - staking.EndBlocker(ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)), app.StakingKeeper) - - // Check that the validator is deleted from state - validators := app.StakingKeeper.GetValidators(ctx, 100) - require.Equal(t, len(validatorAddrs)-(i+1), len(validators), - "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) - - _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.False(t, found) - - gotBalance := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount - require.Equal(t, initTokens, gotBalance, "expected account to have %d, got %d", initTokens, gotBalance) - } -} - -func TestMultipleMsgDelegate(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 50, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, delegatorAddrs := valAddrs[0], delAddrs[1:] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - var amount int64 = 10 - - // first make a validator - tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amount), true) - - // delegate multiple parties - for _, delegatorAddr := range delegatorAddrs { - tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) - tstaking.CheckDelegator(delegatorAddr, validatorAddr, true) - } - - // unbond them all - for _, delegatorAddr := range delegatorAddrs { - res := tstaking.Undelegate(delegatorAddr, validatorAddr, sdk.NewInt(amount), true) - - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.Ctx = ctx - - // check that the account is unbonded - _, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) - require.False(t, found) - } -} - -func TestJailValidator(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - var amt int64 = 10 - - // create the validator and delegate - tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) - tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(amt)) - - // unbond the validators bond portion - unamt := sdk.NewInt(amt) - res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unamt, true) - - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.Ctx = ctx - - tstaking.CheckValidator(validatorAddr, -1, true) - - // test that the delegator can still withdraw their bonds - tstaking.Undelegate(delegatorAddr, validatorAddr, unamt, true) - - err = proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime) - staking.EndBlocker(ctx, app.StakingKeeper) - tstaking.Ctx = ctx - - // verify that the pubkey can now be reused - tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) -} - -func TestValidatorQueue(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 7 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validator and make a bond - amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) - tstaking.Delegate(delegatorAddr, validatorAddr, amt) - staking.EndBlocker(ctx, app.StakingKeeper) - - // unbond the all self-delegation to put validator in unbonding state - res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) - - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - finishTime := resData.CompletionTime - - ctx = tstaking.TurnBlock(finishTime) - origHeader := ctx.BlockHeader() - - validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.True(t, validator.IsUnbonding(), "%v", validator) - - // should still be unbonding at time 6 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) - - validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.True(t, validator.IsUnbonding(), "%v", validator) - - // should be in unbonded state at time 7 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) - - validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) - require.True(t, found) - require.True(t, validator.IsUnbonded(), "%v", validator) -} - -func TestUnbondingPeriod(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr := valAddrs[0] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 7 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validator - amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) - staking.EndBlocker(ctx, app.StakingKeeper) - - // begin unbonding - tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) - - origHeader := ctx.BlockHeader() - - _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found, "should not have unbonded") - - // cannot complete unbonding at same time - staking.EndBlocker(ctx, app.StakingKeeper) - _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found, "should not have unbonded") - - // cannot complete unbonding at time 6 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) - _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.True(t, found, "should not have unbonded") - - // can complete unbonding at time 7 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) - _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) - require.False(t, found, "should have unbonded") -} - -func TestUnbondingFromUnbondingValidator(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create the validator and delegate - tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) - tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) - - // unbond the validators bond portion - unbondAmt := sdk.NewInt(10) - res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt, true) - - // change the ctx to Block Time one second before the validator would have unbonded - var resData types.MsgUndelegateResponse - err := proto.Unmarshal(res.Data, &resData) - require.NoError(t, err) - - ctx = ctx.WithBlockTime(resData.CompletionTime.Add(time.Second * -1)) - - // unbond the delegator from the validator - res = tstaking.Undelegate(delegatorAddr, validatorAddr, unbondAmt, true) - - ctx = tstaking.TurnBlockTimeDiff(app.StakingKeeper.UnbondingTime(ctx)) - tstaking.Ctx = ctx - - // Check to make sure that the unbonding delegation is no longer in state - // (meaning it was deleted in the above EndBlocker) - _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) - require.False(t, found, "should be removed from state") -} - -func TestRedelegationPeriod(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - validatorAddr, validatorAddr2 := valAddrs[0], valAddrs[1] - denom := app.StakingKeeper.GetParams(ctx).BondDenom - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 7 * time.Second - app.StakingKeeper.SetParams(ctx, params) - // initial balance - amt1 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount - - // create the validators - tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) - - // balance should have been subtracted after creation - amt2 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount - require.Equal(t, amt1.Sub(sdk.NewInt(10)), amt2, "expected coins to be subtracted") - - tstaking.CreateValidator(validatorAddr2, PKs[1], sdk.NewInt(10), true) - bal1 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) - - // begin redelegate - redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) - tstaking.Handle(msgBeginRedelegate, true) - - // origin account should not lose tokens as with a regular delegation - bal2 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) - require.Equal(t, bal1, bal2) - - origHeader := ctx.BlockHeader() - - // cannot complete redelegation at same time - staking.EndBlocker(ctx, app.StakingKeeper) - _, found := app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) - require.True(t, found, "should not have unbonded") - - // cannot complete redelegation at time 6 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) - _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) - require.True(t, found, "should not have unbonded") - - // can complete redelegation at time 7 seconds later - ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) - _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) - require.False(t, found, "should have unbonded") -} - -func TestTransitiveRedelegation(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - - val1, val2, val3 := valAddrs[0], valAddrs[1], valAddrs[2] - blockTime := time.Now().UTC() - ctx = ctx.WithBlockTime(blockTime) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // create the validators - tstaking.CreateValidator(val1, PKs[0], sdk.NewInt(10), true) - tstaking.CreateValidator(val2, PKs[1], sdk.NewInt(10), true) - tstaking.CreateValidator(val3, PKs[2], sdk.NewInt(10), true) - - // begin redelegate - redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) - msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val1, val2, redAmt) - tstaking.Handle(msgBeginRedelegate, true) - - // cannot redelegation to next validator while first delegation exists - msgBeginRedelegate = types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val2, val3, redAmt) - tstaking.Handle(msgBeginRedelegate, false) - - params := app.StakingKeeper.GetParams(ctx) - ctx = ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)) - tstaking.Ctx = ctx - - // complete first redelegation - staking.EndBlocker(ctx, app.StakingKeeper) - - // now should be able to redelegate from the second validator to the third - tstaking.Handle(msgBeginRedelegate, true) -} - -func TestMultipleRedelegationAtSameTime(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valAddr := valAddrs[0] - valAddr2 := valAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 1 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validators - valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) - tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) - - // end block to bond them - staking.EndBlocker(ctx, app.StakingKeeper) - - // begin a redelegate - selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) - tstaking.Handle(msgBeginRedelegate, true) - - // there should only be one entry in the redelegation object - rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.True(t, found) - require.Len(t, rd.Entries, 1) - - // start a second redelegation at this same time as the first - tstaking.Handle(msgBeginRedelegate, true) - - // now there should be two entries - rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.True(t, found) - require.Len(t, rd.Entries, 2) - - // move forward in time, should complete both redelegations - ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) - rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.False(t, found) -} - -func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valAddr := valAddrs[0] - valAddr2 := valAddrs[1] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 10 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validators - valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) - tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) - - // end block to bond them - staking.EndBlocker(ctx, app.StakingKeeper) - - // begin a redelegate - selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) - msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) - tstaking.Handle(msgBeginRedelegate, true) - - // move forward in time and start a second redelegation - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - tstaking.Ctx = ctx - tstaking.Handle(msgBeginRedelegate, true) - - // now there should be two entries - rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.True(t, found) - require.Len(t, rd.Entries, 2) - - // move forward in time, should complete the first redelegation, but not the second - ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) - rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.True(t, found) - require.Len(t, rd.Entries, 1) - - // move forward in time, should complete the second redelegation - ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) - rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) - require.False(t, found) -} - -func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valAddr := valAddrs[0] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 1 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validators - valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) - - // end block to bond - staking.EndBlocker(ctx, app.StakingKeeper) - - // begin an unbonding delegation - selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) - - // there should only be one entry in the ubd object - ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // start a second ubd at this same time as the first - tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) - - // now there should be two entries - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.True(t, found) - require.Len(t, ubd.Entries, 2) - - // move forwaubd in time, should complete both ubds - ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.False(t, found) -} - -func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valAddr := valAddrs[0] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.UnbondingTime = 10 * time.Second - app.StakingKeeper.SetParams(ctx, params) - - // create the validator - valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) - - // end block to bond - staking.EndBlocker(ctx, app.StakingKeeper) - - // begin an unbonding delegation - selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) - - // there should only be one entry in the ubd object - ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // move forwaubd in time and start a second redelegation - ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) - tstaking.Ctx = ctx - tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) - - // now there should be two entries - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.True(t, found) - require.Len(t, ubd.Entries, 2) - - // move forwaubd in time, should complete the first redelegation, but not the second - ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - - // move forwaubd in time, should complete the second redelegation - ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) - require.False(t, found) -} - -func TestUnbondingWhenExcessValidators(t *testing.T) { - initPower := int64(1000) - app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - val1 := valAddrs[0] - val2 := valAddrs[1] - val3 := valAddrs[2] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - // set the unbonding time - params := app.StakingKeeper.GetParams(ctx) - params.MaxValidators = 2 - app.StakingKeeper.SetParams(ctx, params) - - // add three validators - tstaking.CreateValidatorWithValPower(val1, PKs[0], 50, true) - // apply TM updates - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 1, len(app.StakingKeeper.GetLastValidators(ctx))) - - valTokens2 := tstaking.CreateValidatorWithValPower(val2, PKs[1], 30, true) - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) - - tstaking.CreateValidatorWithValPower(val3, PKs[2], 10, true) - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) - - // unbond the validator-2 - tstaking.Undelegate(sdk.AccAddress(val2), val2, valTokens2, true) - // apply TM updates - app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - - // because there are extra validators waiting to get in, the queued - // validator (aka. validator-1) should make it into the bonded group, thus - // the total number of validators should stay the same - vals := app.StakingKeeper.GetLastValidators(ctx) - require.Equal(t, 2, len(vals), "vals %v", vals) - tstaking.CheckValidator(val1, types.Bonded, false) -} - -func TestBondUnbondRedelegateSlashTwice(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valA, valB, del := valAddrs[0], valAddrs[1], delAddrs[2] - consAddr0 := sdk.ConsAddress(PKs[0].Address()) - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - valTokens := tstaking.CreateValidatorWithValPower(valA, PKs[0], 10, true) - tstaking.CreateValidator(valB, PKs[1], valTokens, true) - - // delegate 10 stake - tstaking.Delegate(del, valA, valTokens) - - // apply Tendermint updates - updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 2, len(updates)) - - // a block passes - ctx = ctx.WithBlockHeight(1) - tstaking.Ctx = ctx - - // begin unbonding 4 stake - unbondAmt := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) - tstaking.Undelegate(del, valA, unbondAmt, true) - - // begin redelegate 6 stake - redAmt := sdk.NewCoin(sdk.DefaultBondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 6)) - msgBeginRedelegate := types.NewMsgBeginRedelegate(del, valA, valB, redAmt) - tstaking.Handle(msgBeginRedelegate, true) - - // destination delegation should have 6 shares - delegation, found := app.StakingKeeper.GetDelegation(ctx, del, valB) - require.True(t, found) - require.Equal(t, sdk.NewDecFromInt(redAmt.Amount), delegation.Shares) - - // must apply validator updates - updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) - require.NoError(t, err) - require.Equal(t, 2, len(updates)) - - // slash the validator by half - app.StakingKeeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) - - // unbonding delegation should have been slashed by half - ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) - - // redelegation should have been slashed by half - redelegation, found := app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) - require.True(t, found) - require.Len(t, redelegation.Entries, 1) - - // destination delegation should have been slashed by half - delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) - require.True(t, found) - require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) - - // validator power should have been reduced by half - validator, found := app.StakingKeeper.GetValidator(ctx, valA) - require.True(t, found) - require.Equal(t, valTokens.QuoRaw(2), validator.GetBondedTokens()) - - // slash the validator for an infraction committed after the unbonding and redelegation begin - ctx = ctx.WithBlockHeight(3) - app.StakingKeeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) - tstaking.Ctx = ctx - - // unbonding delegation should be unchanged - ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) - require.True(t, found) - require.Len(t, ubd.Entries, 1) - require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) - - // redelegation should be unchanged - redelegation, found = app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) - require.True(t, found) - require.Len(t, redelegation.Entries, 1) - - // destination delegation should be unchanged - delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) - require.True(t, found) - require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) - - // end blocker - staking.EndBlocker(ctx, app.StakingKeeper) - - // validator power should have been reduced to zero - // validator should be in unbonding state - validator, _ = app.StakingKeeper.GetValidator(ctx, valA) - require.Equal(t, validator.GetStatus(), types.Unbonding) -} - -func TestInvalidMsg(t *testing.T) { - k := keeper.Keeper{} - h := staking.NewHandler(k) - - res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized staking message type")) -} - -func TestInvalidCoinDenom(t *testing.T) { - initPower := int64(1000) - app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) - valA, valB, delAddr := valAddrs[0], valAddrs[1], delAddrs[2] - tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) - - valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) - invalidCoin := sdk.NewCoin("churros", valTokens) - validCoin := sdk.NewCoin(sdk.DefaultBondDenom, valTokens) - oneCoin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()) - - commission := types.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.ZeroDec()) - msgCreate, err := types.NewMsgCreateValidator(valA, PKs[0], invalidCoin, types.Description{}, commission, sdk.OneInt()) - require.NoError(t, err) - tstaking.Handle(msgCreate, false) - - msgCreate, err = types.NewMsgCreateValidator(valA, PKs[0], validCoin, types.Description{}, commission, sdk.OneInt()) - require.NoError(t, err) - tstaking.Handle(msgCreate, true) - - msgCreate, err = types.NewMsgCreateValidator(valB, PKs[1], validCoin, types.Description{}, commission, sdk.OneInt()) - require.NoError(t, err) - tstaking.Handle(msgCreate, true) - - msgDelegate := types.NewMsgDelegate(delAddr, valA, invalidCoin) - tstaking.Handle(msgDelegate, false) - - msgDelegate = types.NewMsgDelegate(delAddr, valA, validCoin) - tstaking.Handle(msgDelegate, true) - - msgUndelegate := types.NewMsgUndelegate(delAddr, valA, invalidCoin) - tstaking.Handle(msgUndelegate, false) - - msgUndelegate = types.NewMsgUndelegate(delAddr, valA, oneCoin) - tstaking.Handle(msgUndelegate, true) - - msgRedelegate := types.NewMsgBeginRedelegate(delAddr, valA, valB, invalidCoin) - tstaking.Handle(msgRedelegate, false) - - msgRedelegate = types.NewMsgBeginRedelegate(delAddr, valA, valB, oneCoin) - tstaking.Handle(msgRedelegate, true) -} diff --git a/x/staking/module.go b/x/staking/module.go index 71cf34eec4de..aa395b80ca17 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -117,9 +117,9 @@ func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { keeper.RegisterInvariants(ir, am.keeper) } -// Route returns the message routing key for the staking module. +// Deprecated: Route returns the message routing key for the staking module. func (am AppModule) Route() sdk.Route { - return sdk.NewRoute(types.RouterKey, NewHandler(am.keeper)) + return sdk.Route{} } // QuerierRoute returns the staking module's querier route name. diff --git a/x/staking/teststaking/helper.go b/x/staking/teststaking/helper.go index 25b021df07a1..012f21f00eda 100644 --- a/x/staking/teststaking/helper.go +++ b/x/staking/teststaking/helper.go @@ -1,6 +1,7 @@ package teststaking import ( + "context" "testing" "time" @@ -13,12 +14,12 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// Helper is a structure which wraps the staking handler +// Helper is a structure which wraps the staking message server // and provides methods useful in tests type Helper struct { - t *testing.T - h sdk.Handler - k keeper.Keeper + t *testing.T + msgSrvr stakingtypes.MsgServer + k keeper.Keeper Ctx sdk.Context Commission stakingtypes.CommissionRates @@ -26,18 +27,18 @@ type Helper struct { Denom string } -// NewHelper creates staking Handler wrapper for tests +// NewHelper creates a new instance of Helper. func NewHelper(t *testing.T, ctx sdk.Context, k keeper.Keeper) *Helper { - return &Helper{t, staking.NewHandler(k), k, ctx, ZeroCommission(), sdk.DefaultBondDenom} + return &Helper{t, keeper.NewMsgServerImpl(k), k, ctx, ZeroCommission(), sdk.DefaultBondDenom} } -// CreateValidator calls handler to create a new staking validator +// CreateValidator calls staking module `MsgServer/CreateValidator` to create a new validator func (sh *Helper) CreateValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, stakeAmount sdk.Int, ok bool) { coin := sdk.NewCoin(sh.Denom, stakeAmount) sh.createValidator(addr, pk, coin, ok) } -// CreateValidatorWithValPower calls handler to create a new staking validator with zero +// CreateValidatorWithValPower calls staking module `MsgServer/CreateValidator` to create a new validator with zero // commission func (sh *Helper) CreateValidatorWithValPower(addr sdk.ValAddress, pk cryptotypes.PubKey, valPower int64, ok bool) sdk.Int { amount := sh.k.TokensFromConsensusPower(sh.Ctx, valPower) @@ -54,36 +55,47 @@ func (sh *Helper) CreateValidatorMsg(addr sdk.ValAddress, pk cryptotypes.PubKey, return msg } +// CreateValidatorWithMsg calls staking module `MsgServer/CreateValidator` +func (sh *Helper) CreateValidatorWithMsg(ctx context.Context, msg *stakingtypes.MsgCreateValidator) (*stakingtypes.MsgCreateValidatorResponse, error) { + return sh.msgSrvr.CreateValidator(ctx, msg) +} + func (sh *Helper) createValidator(addr sdk.ValAddress, pk cryptotypes.PubKey, coin sdk.Coin, ok bool) { msg, err := stakingtypes.NewMsgCreateValidator(addr, pk, coin, stakingtypes.Description{}, sh.Commission, sdk.OneInt()) require.NoError(sh.t, err) - sh.Handle(msg, ok) + res, err := sh.msgSrvr.CreateValidator(sdk.WrapSDKContext(sh.Ctx), msg) + if ok { + require.NoError(sh.t, err) + require.NotNil(sh.t, res) + } else { + require.Error(sh.t, err) + require.Nil(sh.t, res) + } } -// Delegate calls handler to delegate stake for a validator +// Delegate calls staking module staking module `MsgServer/Delegate` to delegate stake for a validator func (sh *Helper) Delegate(delegator sdk.AccAddress, val sdk.ValAddress, amount sdk.Int) { coin := sdk.NewCoin(sh.Denom, amount) msg := stakingtypes.NewMsgDelegate(delegator, val, coin) - sh.Handle(msg, true) + res, err := sh.msgSrvr.Delegate(sdk.WrapSDKContext(sh.Ctx), msg) + require.NoError(sh.t, err) + require.NotNil(sh.t, res) } -// DelegateWithPower calls handler to delegate stake for a validator +// DelegateWithPower calls staking module `MsgServer/Delegate` to delegate stake for a validator func (sh *Helper) DelegateWithPower(delegator sdk.AccAddress, val sdk.ValAddress, power int64) { coin := sdk.NewCoin(sh.Denom, sh.k.TokensFromConsensusPower(sh.Ctx, power)) msg := stakingtypes.NewMsgDelegate(delegator, val, coin) - sh.Handle(msg, true) + res, err := sh.msgSrvr.Delegate(sdk.WrapSDKContext(sh.Ctx), msg) + require.NoError(sh.t, err) + require.NotNil(sh.t, res) } -// Undelegate calls handler to unbound some stake from a validator. -func (sh *Helper) Undelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount sdk.Int, ok bool) *sdk.Result { +// Undelegate calls staking module `MsgServer/Undelegate` to unbound some stake from a validator. +func (sh *Helper) Undelegate(delegator sdk.AccAddress, val sdk.ValAddress, amount sdk.Int, ok bool) { unbondAmt := sdk.NewCoin(sh.Denom, amount) msg := stakingtypes.NewMsgUndelegate(delegator, val, unbondAmt) - return sh.Handle(msg, ok) -} - -// Handle calls staking handler on a given message -func (sh *Helper) Handle(msg sdk.Msg, ok bool) *sdk.Result { - res, err := sh.h(sh.Ctx, msg) + res, err := sh.msgSrvr.Undelegate(sdk.WrapSDKContext(sh.Ctx), msg) if ok { require.NoError(sh.t, err) require.NotNil(sh.t, res) @@ -91,7 +103,6 @@ func (sh *Helper) Handle(msg sdk.Msg, ok bool) *sdk.Result { require.Error(sh.t, err) require.Nil(sh.t, res) } - return res } // CheckValidator asserts that a validor exists and has a given status (if status!="") diff --git a/x/upgrade/module.go b/x/upgrade/module.go index b586193a6b27..00e966912d13 100644 --- a/x/upgrade/module.go +++ b/x/upgrade/module.go @@ -82,8 +82,10 @@ func NewAppModule(keeper keeper.Keeper) AppModule { // RegisterInvariants does nothing, there are no invariants to enforce func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} -// Route is empty, as we do not handle Messages (just proposals) -func (AppModule) Route() sdk.Route { return sdk.Route{} } +// Deprecated: Route returns the message routing key for the upgrade module. +func (AppModule) Route() sdk.Route { + return sdk.Route{} +} // QuerierRoute returns the route we respond to for abci queries func (AppModule) QuerierRoute() string { return types.QuerierKey }