From c2c8529ef9b384c4421c2aaf6b6b8388c6c9e156 Mon Sep 17 00:00:00 2001 From: jinmannwong <38482272+jinmannwong@users.noreply.github.com> Date: Wed, 26 Aug 2020 14:04:57 +0100 Subject: [PATCH] Port sdk v0.39.1 (#39) * v0.38.4-RC1 * add release date * client/keys/parse: honor config changes (#6340) `keys parse` uses the global configuration before before client applications have had a chance to apply their settings. This change adds a `GetSealedConfig()` helper that waits for the config to be sealed before returning it. Fixes: #5091 Origin: https://github.com/cosmos/cosmos-sdk/commit/4e328d75dbe0d06f1d1a3002d31803ca039afef3 Author: Adam Bozanich Reviewed-by: Alessio Treglia * Merge PR #6505: Backport v0.38.5: IAVL Bump (RC) & Pruning Refactor * Fix cl * Fix cl * Merge PR #6551: Ethanfrey/fix trace flag * Merge PR #6552: Add sender info to bank transfer event * Merge PR #6581: v0.38.5-RC1 * Add release date * deps: bump IAVL to 0.14 * cl++ * Merge PR #6618: backport 0.39.0 (launchpad): cherry pick #5839 * launchpad: bump tendermint to v0.33.6 (#6673) * launchpad: bump tendermint to v0.33.6 * cha-cha-cha * fix types.ChainAnteDecorators() panic (#5742) (#6669) ChainAnteDecorators() panics when no arguments are supplied. This change its behaviour and the function now returns a nil AnteHandler in case no AnteDecorator instances are supplied. Closes: #5741 * launchpad: register MsgFundCommunityPool to distribution codec (#6675) Closes: #6210 * client: backport IBC additions (#6682) * launchpad: backport cliCtx.QueryABCI * add prove flag * Save account for multi sending (#6674) Include changes from PR #6283 Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Alessio Treglia * baseapp: fix sender events accumulation (#6683) closes: #6306 original PR: #6307 * run go mod tidy * add changelog line re: issue that was not included in v0.38.5 * Fix changelog * add release notes * update CODEOWNERS as per STABLE_RELEASES.md Launchpad's Stable Release Managers team's current composition as approved by @okwme (ICF): * @alessio * @clevinson * @ethanfrey * Add milestone's URL * Revert "update CODEOWNERS as per STABLE_RELEASES.md" This reverts commit f384592dcf465ca2822a72b74bde2f0a30b98436. * fix typo * add example patch * launchpad: backport account sequence stuck (#6721) Launchpad fix for #6287 * update release notes * make explicit that the regression is fixed in 0.39 * update release notes * Update CHANGELOG.md * update release notes @ethanfrey * remove pruning info from changelog They have been moved to NEWS.md * Update NEWS.md Co-authored-by: Alexander Bezobchuk * Mege PR #6749: auth: remove custom JSON marshaling * mv NEWS.md -> RELEASE_NOTES.md Self-explanatory naming convention. * Update changelog * add reference to gaia software upgrade * x/staking: add call to iterator Close() method (#6794) Original pull request: #6791 * [ci] fix linter (#6795) * Merge PR #6842: Remove custom acc JSON marshl * Backport #5671: Add a Flag for CORS (#6853) This adds the --unsafe-cors flag functionality. From: #5671 Co-authored-by: Marko * Merge PR #6855: Allow passing through Content-Type (needed for POST) * Merge PR #6861: Revert "Backport 0.39.1: Remove Custom JSON Marshaling for Accounts" * Merge PR #6869: Backport 0.39.1: Launchpad Migration #6829 * Update changelog and release notes * Update x/auth/legacy/v0_39/types.go * Update x/auth/legacy/v0_38/types.go * Merge PR #6938: Fix v0.39 migrate test * Merge PR #6937: Bump Tendermint to v0.33.7 * Update x/auth/legacy/v0_39/types.go Co-authored-by: Alexander Bezobchuk * update RELEASE_NOTES.md * update changelog * test case with real genesis data (#6995) * incorporate Ethan Frey's suggestion * make format * Update go mod * Remove some ci files * Modify data for test Co-authored-by: Alexander Bezobchuk Co-authored-by: Aleksandr Bezobchuk Co-authored-by: Alessio Treglia Co-authored-by: Ethan Frey Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Jonathan Gimeno Co-authored-by: Marko --- CHANGELOG.md | 61 ++- Makefile | 1 + RELEASE_NOTES.md | 20 + baseapp/abci.go | 20 +- baseapp/baseapp.go | 13 +- baseapp/baseapp_test.go | 110 +++-- baseapp/options.go | 6 +- client/attestation/attestation.go | 1 + client/attestation/attestation_test.go | 6 +- client/context/context.go | 8 +- client/context/query.go | 60 ++- client/context/verifier.go | 4 +- client/flags/flags.go | 8 + client/input/input.go | 2 +- client/keys/attestation.go | 30 +- client/keys/import_test.go | 2 +- client/keys/parse.go | 54 ++- client/keys/parse_test.go | 6 +- client/lcd/root.go | 16 +- codec/codec.go | 2 +- crypto/keys/keyring.go | 3 +- crypto/keys/lazy_keybase_test.go | 2 +- crypto/keys/mintkey/mintkey.go | 2 +- crypto/keys/types_test.go | 5 +- docs/interfaces/rest.md | 33 +- go.mod | 25 +- go.sum | 92 ++-- server/config/config.go | 20 +- server/config/toml.go | 17 +- server/pruning.go | 38 ++ server/pruning_test.go | 64 +++ server/start.go | 37 +- server/tm_cmds.go | 21 +- server/util.go | 11 - simapp/utils.go | 4 +- store/cache/cache_test.go | 6 +- store/iavl/store.go | 67 +-- store/iavl/store_test.go | 130 +----- store/iavl/tree.go | 5 + store/prefix/store_test.go | 8 +- store/reexport.go | 12 +- store/rootmulti/proof_test.go | 2 +- store/rootmulti/store.go | 129 ++++-- store/rootmulti/store_test.go | 95 +++- store/store.go | 21 - store/types/iterator_test.go | 2 +- store/types/pruning.go | 107 +++-- store/types/pruning_test.go | 29 ++ store/types/store.go | 2 + tests/mocks/account_retriever.go | 10 + tests/mocks/types_handler.go | 51 +++ tests/util.go | 14 +- types/config.go | 47 +- types/config_test.go | 50 ++ types/errors/abci.go | 7 +- types/errors/abci_test.go | 83 ++-- types/handler.go | 12 +- types/handler_test.go | 28 ++ types/int.go | 4 +- types/module/simulation.go | 4 +- types/rest/rest.go | 13 +- types/result.go | 9 +- types/utils.go | 11 +- x/auth/ante/basic.go | 3 +- x/auth/ante/sigverify.go | 18 +- x/auth/ante/sigverify_test.go | 37 ++ x/auth/client/utils/tx.go | 35 +- x/auth/client/utils/tx_test.go | 51 ++- x/auth/legacy/v0_38/migrate.go | 4 +- x/auth/legacy/v0_38/types.go | 16 +- x/auth/legacy/v0_39/migrate.go | 60 +++ x/auth/legacy/v0_39/types.go | 426 ++++++++++++++++++ x/auth/types/account.go | 46 -- x/auth/types/account_test.go | 26 -- x/auth/vesting/types/vesting_account.go | 175 +++---- x/auth/vesting/types/vesting_account_test.go | 3 +- x/bank/app_test.go | 1 - x/bank/internal/keeper/keeper.go | 9 + x/bank/internal/keeper/keeper_test.go | 32 ++ x/bank/internal/types/codec.go | 1 - x/bank/simulation/operations.go | 6 +- x/crisis/internal/types/codec.go | 2 +- x/distribution/keeper/delegation_test.go | 3 +- x/distribution/types/codec.go | 5 +- x/genutil/client/cli/migrate.go | 4 +- x/genutil/client/cli/migrate_test.go | 73 ++- .../cli/testdata/commercio-genesis.json | 247 ++++++++++ x/genutil/legacy/v0_38/migrate.go | 8 +- x/genutil/legacy/v0_38/migrate_test.go | 4 +- x/genutil/legacy/v0_39/migrate.go | 33 ++ x/genutil/legacy/v0_39/migrate_test.go | 120 +++++ x/genutil/utils.go | 18 +- x/gov/types/codec.go | 1 - x/mint/internal/types/codec.go | 2 +- x/mint/internal/types/keys.go | 6 +- x/params/client/proposal_handler.go | 2 +- x/params/types/codec.go | 1 - x/simulation/account_test.go | 1 + x/simulation/event_stats.go | 2 +- x/simulation/rand_util_test.go | 1 + x/slashing/internal/keeper/keeper_test.go | 3 +- x/slashing/simulation/operations.go | 2 +- x/staking/keeper/delegation.go | 4 + x/staking/simulation/operations.go | 10 +- x/staking/types/codec.go | 2 +- x/supply/internal/types/account.go | 6 +- x/upgrade/alias.go | 2 +- 107 files changed, 2322 insertions(+), 951 deletions(-) create mode 100644 RELEASE_NOTES.md create mode 100644 server/pruning.go create mode 100644 server/pruning_test.go create mode 100644 store/types/pruning_test.go create mode 100644 tests/mocks/types_handler.go create mode 100644 types/config_test.go create mode 100644 types/handler_test.go create mode 100644 x/auth/legacy/v0_39/migrate.go create mode 100644 x/auth/legacy/v0_39/types.go create mode 100644 x/genutil/client/cli/testdata/commercio-genesis.json create mode 100644 x/genutil/legacy/v0_39/migrate.go create mode 100644 x/genutil/legacy/v0_39/migrate_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e56dae0de232..dadcf175e0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,61 @@ Ref: https://keepachangelog.com/en/1.0.0/ # Changelog -## [Unreleased] +## [v0.39.1] + +* (x/auth) [\#6861](https://github.com/cosmos/cosmos-sdk/pull/6861) Remove public key Bech32 encoding for all account types for JSON serialization, instead relying on direct Amino encoding. In addition, JSON serialization utilizes Amino instead of the Go stdlib, so integers are treated as strings. +* (client) [\#6853](https://github.com/cosmos/cosmos-sdk/pull/6853) Add --unsafe-cors flag. + +## [v0.39.0] + +### Improvements + +* (deps) Bump Tendermint version to [v0.33.6](https://github.com/tendermint/tendermint/releases/tag/v0.33.6) +* (deps) Bump IAVL version to [v0.14.0](https://github.com/cosmos/iavl/releases/tag/v0.14.0) +* (client) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) `CLIContext` additions: + * Introduce `QueryABCI` that returns the full `abci.ResponseQuery` with inclusion Merkle proofs. + * Added `prove` flag for Merkle proof verification. +* (x/staking) [\#6791)](https://github.com/cosmos/cosmos-sdk/pull/6791) Close {UBDQueue,RedelegationQueu}Iterator once used. + +### API Breaking Changes + +* (baseapp) [\#5837](https://github.com/cosmos/cosmos-sdk/issues/5837) Transaction simulation now returns a `SimulationResponse` which contains the `GasInfo` and `Result` from the execution. + +### Client Breaking Changes + +* (x/auth) [\#6745](https://github.com/cosmos/cosmos-sdk/issues/6745) Remove BaseAccount's custom JSON {,un}marshalling. + +### Bug Fixes + +* (store) [\#6475](https://github.com/cosmos/cosmos-sdk/pull/6475) Revert IAVL pruning functionality introduced in +[v0.13.0](https://github.com/cosmos/iavl/releases/tag/v0.13.0), +where the IAVL no longer keeps states in-memory in which it flushes periodically. IAVL now commits and +flushes every state to disk as it did pre-v0.13.0. The SDK's multi-store will track and ensure the proper +heights are pruned. The operator can set the pruning options via a `pruning` config via the CLI or +through `app.toml`. The `pruning` flag exposes `default|everything|nothing|custom` as options -- +see docs for further details. If the operator chooses `custom`, they may provide granular pruning +options `pruning-keep-recent`, `pruning-keep-every`, and `pruning-interval`. The former two options +dictate how many recent versions are kept on disk and the offset of what versions are kept after that +respectively, and the latter defines the height interval in which versions are deleted in a batch. +**Note, there are some client-facing API breaking changes with regard to IAVL, stores, and pruning settings.** +* (x/distribution) [\#6210](https://github.com/cosmos/cosmos-sdk/pull/6210) Register `MsgFundCommunityPool` in distribution amino codec. +* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent `ChainAnteDecorators()` from panicking when empty `AnteDecorator` slice is supplied. +* (baseapp) [\#6306](https://github.com/cosmos/cosmos-sdk/issues/6306) Prevent events emitted by the antehandler from being persisted between transactions. +* (client/keys) [\#5091](https://github.com/cosmos/cosmos-sdk/issues/5091) `keys parse` does not honor client app's configuration. +* (x/bank) [\#6674](https://github.com/cosmos/cosmos-sdk/pull/6674) Create account if recipient does not exist on handing `MsgMultiSend`. +* (x/auth) [\#6287](https://github.com/cosmos/cosmos-sdk/pull/6287) Fix nonce stuck when sending multiple transactions from an account in a same block. + +## [v0.38.5] - 2020-07-02 + +### Improvements + +* (tendermint) Bump Tendermint version to [v0.33.6](https://github.com/tendermint/tendermint/releases/tag/v0.33.6). + +## [v0.38.4] - 2020-05-21 + +### Bug Fixes + +* (x/auth) [\#5950](https://github.com/cosmos/cosmos-sdk/pull/5950) Fix `IncrementSequenceDecorator` to use is `IsReCheckTx` instead of `IsCheckTx` to allow account sequence incrementing. ## [v0.38.3] - 2020-04-09 @@ -301,7 +355,6 @@ to detail this new feature and how state transitions occur. now exists a single `Params` type with a getter and setter along with a getter for each individual parameter. ### Bug Fixes - * (rest) [\#5508](https://github.com/cosmos/cosmos-sdk/pull/5508) Fix `x/distribution` endpoints to properly return height in the response. * (x/genutil) [\#5499](https://github.com/cosmos/cosmos-sdk/pull/) Ensure `DefaultGenesis` returns valid and non-nil default genesis state. * (client) [\#5303](https://github.com/cosmos/cosmos-sdk/issues/5303) Fix ignored error in tx generate only mode. @@ -2901,7 +2954,9 @@ BUG FIXES: -[Unreleased]: https://github.com/cosmos/cosmos-sdk/compare/v0.38.2...HEAD +[Unreleased]: https://github.com/cosmos/cosmos-sdk/compare/v0.39.1...HEAD +[v0.39.1]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.1 +[v0.39.0]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.0 [v0.38.2]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.2 [v0.38.1]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.1 [v0.38.0]: https://github.com/cosmos/cosmos-sdk/releases/tag/v0.38.0 diff --git a/Makefile b/Makefile index 8d947640f75a..9d857bc8ca7c 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,7 @@ update-swagger-docs: statik mocks: $(MOCKS_DIR) mockgen -source=x/auth/types/account_retriever.go -package mocks -destination tests/mocks/account_retriever.go + mockgen -source=types/handler.go -package mocks -destination tests/mocks/types_handler.go .PHONY: mocks $(MOCKS_DIR): diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 000000000000..efbe90d5ff2a --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,20 @@ +# Cosmos SDK v0.39.1 Release Notes + +This release fixes the [issue affecting the accounts migration](https://github.com/cosmos/cosmos-sdk/issues/6828) from v0.38 to v0.39. + +See the [Cosmos SDK 0.39.1 milestone](https://github.com/cosmos/cosmos-sdk/milestone/29?closed=1) on our issue tracker for details. + +## Remove custom JSON serialization for account types + +Account types JSON serialization has now changed to Amino. Changes are significant (e.g. integers are treated +as strings) thus it is required to migrate the exported state of an application before restarting the node +with a more recent version of the Cosmos SDK. + +## REST server's --unsafe-cors mode + +This a UX improvement [back ported from master](https://github.com/cosmos/cosmos-sdk/pull/6853) that allows developers to disable CORS +restrictions during app development and testing by passing the `--unsafe-cors` option to the client's `rest-server` command. + +## Tendermint 0.33.7 + +Tendermint 0.33.7 brings an important regression fix. Please refer to [this bug report](https://github.com/tendermint/tendermint/issues/5112) for more information. diff --git a/baseapp/abci.go b/baseapp/abci.go index d9674325f8fe..0e16574dde87 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -163,7 +163,7 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { tx, err := app.txDecoder(req.Tx) if err != nil { - return sdkerrors.ResponseCheckTx(err, 0, 0, app.debug) + return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) } var mode runTxMode @@ -181,7 +181,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { gInfo, result, err := app.runTx(mode, req.Tx, tx) if err != nil { - return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.debug) + return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseCheckTx{ @@ -201,12 +201,12 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { tx, err := app.txDecoder(req.Tx) if err != nil { - return sdkerrors.ResponseDeliverTx(err, 0, 0, app.debug) + return sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace) } gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx) if err != nil { - return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.debug) + return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseDeliverTx{ @@ -326,12 +326,20 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx")) } - gInfo, _, _ := app.Simulate(txBytes, tx) + gInfo, res, err := app.Simulate(txBytes, tx) + if err != nil { + return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx")) + } + + simRes := sdk.SimulationResponse{ + GasInfo: gInfo, + Result: res, + } return abci.ResponseQuery{ Codespace: sdkerrors.RootCodespace, Height: req.Height, - Value: codec.Cdc.MustMarshalBinaryLengthPrefixed(gInfo.GasUsed), + Value: codec.Cdc.MustMarshalBinaryBare(simRes), } case "version": diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e2d5bf428864..3fd121e08a66 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -107,8 +107,8 @@ type BaseApp struct { // nolint: maligned // application's version string appVersion string - // debug flag turns on more error reporting - debug bool + // trace set will return full stack traces for errors in ABCI Log field + trace bool } // NewBaseApp returns a reference to an initialized BaseApp. It accepts a @@ -130,7 +130,7 @@ func NewBaseApp( queryRouter: NewQueryRouter(), txDecoder: txDecoder, fauxMerkleMode: false, - debug: false, + trace: false, } for _, option := range options { option(app) @@ -358,8 +358,8 @@ func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) { app.interBlockCache = cache } -func (app *BaseApp) setDebug(debug bool) { - app.debug = debug +func (app *BaseApp) setTrace(trace bool) { + app.trace = trace } // Router returns the router of the BaseApp. @@ -604,8 +604,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk. // writes do not happen if aborted/failed. This may have some // performance benefits, but it'll be more difficult to get right. anteCtx, msCache = app.cacheTxContext(ctx, txBytes) - + anteCtx = anteCtx.WithEventManager(sdk.NewEventManager()) newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate) + if !newCtx.IsZero() { // At this point, newCtx.MultiStore() is cache-wrapped, or something else // replaced by the AnteHandler. We want the original multistore, not one diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 6ee5aabbb0a8..75f5e5b617b0 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -168,7 +168,7 @@ func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) { func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) { rs := rootmulti.NewStore(db) - rs.SetPruning(store.PruneSyncable) + rs.SetPruning(store.PruneDefault) key := sdk.NewKVStoreKey(storeKey) rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil) err := rs.LoadLatestVersion() @@ -273,7 +273,7 @@ func TestSetLoader(t *testing.T) { func TestAppVersionSetterGetter(t *testing.T) { logger := defaultLogger() - pruningOpt := SetPruning(store.PruneSyncable) + pruningOpt := SetPruning(store.PruneDefault) db := dbm.NewMemDB() name := t.Name() app := NewBaseApp(name, logger, db, nil, pruningOpt) @@ -329,8 +329,9 @@ func TestLoadVersionInvalid(t *testing.T) { func TestLoadVersionPruning(t *testing.T) { logger := log.NewNopLogger() pruningOptions := store.PruningOptions{ - KeepEvery: 2, - SnapshotEvery: 6, + KeepRecent: 2, + KeepEvery: 3, + Interval: 1, } pruningOpt := SetPruning(pruningOptions) db := dbm.NewMemDB() @@ -351,61 +352,33 @@ func TestLoadVersionPruning(t *testing.T) { require.Equal(t, int64(0), lastHeight) require.Equal(t, emptyCommitID, lastID) - // execute a block - header := abci.Header{Height: 1} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res := app.Commit() - - // execute a block, collect commit ID - header = abci.Header{Height: 2} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID2 := sdk.CommitID{Version: 2, Hash: res.Data} - - // execute a block - header = abci.Header{Height: 3} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID3 := sdk.CommitID{Version: 3, Hash: res.Data} - - // reload with LoadLatestVersion, check it loads last flushed version - app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadLatestVersion(capKey) - require.Nil(t, err) - testLoadVersionHelper(t, app, int64(2), commitID2) + var lastCommitID sdk.CommitID - // re-execute block 3 and check it is same CommitID - header = abci.Header{Height: 3} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - recommitID3 := sdk.CommitID{Version: 3, Hash: res.Data} - require.Equal(t, commitID3, recommitID3, "Commits of identical blocks not equal after reload") + // Commit seven blocks, of which 7 (latest) is kept in addition to 6, 5 + // (keep recent) and 3 (keep every). + for i := int64(1); i <= 7; i++ { + app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: i}}) + res := app.Commit() + lastCommitID = sdk.CommitID{Version: i, Hash: res.Data} + } - // execute a block, collect commit ID - header = abci.Header{Height: 4} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID4 := sdk.CommitID{Version: 4, Hash: res.Data} + for _, v := range []int64{1, 2, 4} { + _, err = app.cms.CacheMultiStoreWithVersion(v) + require.Error(t, err) + } - // execute a block - header = abci.Header{Height: 5} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() + for _, v := range []int64{3, 5, 6, 7} { + _, err = app.cms.CacheMultiStoreWithVersion(v) + require.NoError(t, err) + } - // reload with LoadLatestVersion, check it loads last flushed version + // reload with LoadLatestVersion, check it loads last version app = NewBaseApp(name, logger, db, nil, pruningOpt) app.MountStores(capKey) + err = app.LoadLatestVersion(capKey) require.Nil(t, err) - testLoadVersionHelper(t, app, int64(4), commitID4) - - // reload with LoadVersion of previous flushed version - // and check it fails since previous flush should be pruned - app = NewBaseApp(name, logger, db, nil, pruningOpt) - app.MountStores(capKey) - err = app.LoadVersion(2, capKey) - require.NotNil(t, err) + testLoadVersionHelper(t, app, int64(7), lastCommitID) } func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) { @@ -685,20 +658,33 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { } func anteHandlerTxTest(t *testing.T, capKey sdk.StoreKey, storeKey []byte) sdk.AnteHandler { - return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) { + return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) { store := ctx.KVStore(capKey) txTest := tx.(txTest) if txTest.FailOnAnte { - return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure") } - _, err = incrementingCounter(t, store, storeKey, txTest.Counter) + _, err := incrementingCounter(t, store, storeKey, txTest.Counter) if err != nil { - return newCtx, err + return ctx, err } - return newCtx, nil + ctx.EventManager().EmitEvents( + counterEvent("ante_handler", txTest.Counter), + ) + + return ctx, nil + } +} + +func counterEvent(evType string, msgCount int64) sdk.Events { + return sdk.Events{ + sdk.NewEvent( + evType, + sdk.NewAttribute("update_counter", fmt.Sprintf("%d", msgCount)), + ), } } @@ -985,10 +971,13 @@ func TestSimulateTx(t *testing.T) { queryResult := app.Query(query) require.True(t, queryResult.IsOK(), queryResult.Log) - var res uint64 - err = codec.Cdc.UnmarshalBinaryLengthPrefixed(queryResult.Value, &res) + var simRes sdk.SimulationResponse + err = codec.Cdc.UnmarshalBinaryBare(queryResult.Value, &simRes) require.NoError(t, err) - require.Equal(t, gasConsumed, res) + require.Equal(t, gInfo, simRes.GasInfo) + require.Equal(t, result.Log, simRes.Result.Log) + require.Equal(t, result.Events, simRes.Result.Events) + require.True(t, bytes.Equal(result.Data, simRes.Result.Data)) app.EndBlock(abci.RequestEndBlock{}) app.Commit() } @@ -1312,6 +1301,7 @@ func TestBaseAppAnteHandler(t *testing.T) { txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx := app.getState(runTxModeDeliver).ctx @@ -1327,6 +1317,7 @@ func TestBaseAppAnteHandler(t *testing.T) { require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.Empty(t, res.Events) require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx @@ -1342,6 +1333,7 @@ func TestBaseAppAnteHandler(t *testing.T) { require.NoError(t, err) res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) + require.NotEmpty(t, res.Events) require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) ctx = app.getState(runTxModeDeliver).ctx diff --git a/baseapp/options.go b/baseapp/options.go index 6e767ef22103..b8fdef58b7f5 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -44,9 +44,9 @@ func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) { return func(app *BaseApp) { app.setInterBlockCache(cache) } } -// SetDebug will turn on or off debug flag -func SetDebug(debug bool) func(*BaseApp) { - return func(app *BaseApp) { app.setDebug(debug) } +// SetTrace will turn on or off trace flag +func SetTrace(trace bool) func(*BaseApp) { + return func(app *BaseApp) { app.setTrace(trace) } } func (app *BaseApp) SetName(name string) { diff --git a/client/attestation/attestation.go b/client/attestation/attestation.go index 192f160b36ef..34dbd6b1757e 100644 --- a/client/attestation/attestation.go +++ b/client/attestation/attestation.go @@ -3,6 +3,7 @@ package attestation import ( "bytes" "encoding/hex" + "github.com/tendermint/tendermint/crypto" ) diff --git a/client/attestation/attestation_test.go b/client/attestation/attestation_test.go index c0796b807217..d87a78c0541d 100644 --- a/client/attestation/attestation_test.go +++ b/client/attestation/attestation_test.go @@ -3,10 +3,12 @@ package attestation import ( "bytes" "crypto/rand" - "github.com/cosmos/cosmos-sdk/crypto/keys" + "testing" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" - "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys" ) func generatePrivateKey(t *testing.T) crypto.PrivKey { diff --git a/client/context/context.go b/client/context/context.go index 28bfe8e830b0..7b35d991f25e 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -7,11 +7,11 @@ import ( "github.com/pkg/errors" "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" - "github.com/tendermint/tendermint/libs/cli" tmlite "github.com/tendermint/tendermint/lite" rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" + yaml "gopkg.in/yaml.v2" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" @@ -65,7 +65,7 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext { if !genOnly { nodeURI = viper.GetString(flags.FlagNode) if nodeURI != "" { - rpc, err = rpcclient.NewHTTP(nodeURI, "/websocket") + rpc, err = rpchttp.New(nodeURI, "/websocket") if err != nil { fmt.Printf("failted to get client: %v\n", err) os.Exit(1) @@ -157,7 +157,7 @@ func (ctx CLIContext) WithTrustNode(trustNode bool) CLIContext { // WithNodeURI returns a copy of the context with an updated node URI. func (ctx CLIContext) WithNodeURI(nodeURI string) CLIContext { ctx.NodeURI = nodeURI - client, err := rpcclient.NewHTTP(nodeURI, "/websocket") + client, err := rpchttp.New(nodeURI, "/websocket") if err != nil { panic(err) } diff --git a/client/context/query.go b/client/context/query.go index 542ba4c38e59..d55799d4c353 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -22,7 +22,7 @@ import ( // error is returned. func (ctx CLIContext) GetNode() (rpcclient.Client, error) { if ctx.Client == nil { - return nil, errors.New("no RPC client defined") + return nil, errors.New("no RPC client is defined in offline mode") } return ctx.Client, nil @@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte return ctx.queryStore(key, storeName, "key") } +// QueryABCI performs a query to a Tendermint node with the provide RequestQuery. +// It returns the ResultQuery obtained from the query. +func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + return ctx.queryABCI(req) +} + // QuerySubspace performs a query to a Tendermint node with the provided // store name and subspace. It returns key value pair and height of the query // upon success or an error if the query fails. @@ -72,40 +78,50 @@ func (ctx CLIContext) GetFromName() string { return ctx.FromName } -// query performs a query to a Tendermint node with the provided store name -// and path. It returns the result and height of the query upon success -// or an error if the query fails. In addition, it will verify the returned -// proof if TrustNode is disabled. If proof verification fails or the query -// height is invalid, an error will be returned. -func (ctx CLIContext) query(path string, key tmbytes.HexBytes) (res []byte, height int64, err error) { +func (ctx CLIContext) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { node, err := ctx.GetNode() if err != nil { - return res, height, err + return abci.ResponseQuery{}, err } opts := rpcclient.ABCIQueryOptions{ Height: ctx.Height, - Prove: !ctx.TrustNode, + Prove: req.Prove || !ctx.TrustNode, } - result, err := node.ABCIQueryWithOptions(path, key, opts) + result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) if err != nil { - return res, height, err + return abci.ResponseQuery{}, err } - resp := result.Response - if !resp.IsOK() { - return res, resp.Height, errors.New(resp.Log) + if !result.Response.IsOK() { + return abci.ResponseQuery{}, errors.New(result.Response.Log) } // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil + if !opts.Prove || !isQueryStoreWithProof(req.Path) { + return result.Response, nil } - err = ctx.verifyProof(path, resp) + if err := ctx.verifyProof(req.Path, result.Response); err != nil { + return abci.ResponseQuery{}, err + } + + return result.Response, nil +} + +// query performs a query to a Tendermint node with the provided store name +// and path. It returns the result and height of the query upon success +// or an error if the query fails. In addition, it will verify the returned +// proof if TrustNode is disabled. If proof verification fails or the query +// height is invalid, an error will be returned. +func (ctx CLIContext) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) { + resp, err := ctx.queryABCI(abci.RequestQuery{ + Path: path, + Data: key, + }) if err != nil { - return res, resp.Height, err + return nil, 0, err } return resp.Value, resp.Height, nil @@ -116,7 +132,9 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) { if ctx.Verifier == nil { return tmtypes.SignedHeader{}, fmt.Errorf("missing valid certifier to verify data from distrusted node") } + check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier) + switch { case tmliteErr.IsErrCommitNotFound(err): return tmtypes.SignedHeader{}, ErrVerifyCommit(height) @@ -159,8 +177,8 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err } return nil } - err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value) - if err != nil { + + if err := prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value); err != nil { return errors.Wrap(err, "failed to prove merkle proof") } @@ -183,6 +201,7 @@ func isQueryStoreWithProof(path string) bool { } paths := strings.SplitN(path[1:], "/", 3) + switch { case len(paths) != 3: return false @@ -202,6 +221,7 @@ func parseQueryStorePath(path string) (storeName string, err error) { } paths := strings.SplitN(path[1:], "/", 3) + switch { case len(paths) != 3: return "", errors.New("expected format like /store//key") diff --git a/client/context/verifier.go b/client/context/verifier.go index 856c585b5a28..b849d3e397d1 100644 --- a/client/context/verifier.go +++ b/client/context/verifier.go @@ -7,7 +7,7 @@ import ( "github.com/tendermint/tendermint/libs/log" tmlite "github.com/tendermint/tendermint/lite" tmliteproxy "github.com/tendermint/tendermint/lite/proxy" - rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ) const ( @@ -43,7 +43,7 @@ func CreateVerifier(ctx CLIContext, cacheSize int) (tmlite.Verifier, error) { // create an RPC client based off of the RPC URI if no RPC client exists client := ctx.Client if client == nil { - client, err = rpcclient.NewHTTP(ctx.NodeURI, "/websocket") + client, err = rpchttp.New(ctx.NodeURI, "/websocket") if err != nil { return nil, err } diff --git a/client/flags/flags.go b/client/flags/flags.go index 2cdaa5f2137f..746b512b0697 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -24,7 +24,9 @@ const ( // DefaultKeyringBackend DefaultKeyringBackend = keys.BackendOS +) +const ( // BroadcastBlock defines a tx broadcasting mode where the client waits for // the tx to be committed in a block. BroadcastBlock = "block" @@ -34,7 +36,10 @@ const ( // BroadcastAsync defines a tx broadcasting mode where the client returns // immediately. BroadcastAsync = "async" +) +// List of CLI flags +const ( FlagHome = tmcli.HomeFlag FlagUseLedger = "ledger" FlagChainID = "chain-id" @@ -59,9 +64,11 @@ const ( FlagRPCWriteTimeout = "write-timeout" FlagOutputDocument = "output-document" // inspired by wget -O FlagSkipConfirmation = "yes" + FlagProve = "prove" FlagKeyringBackend = "keyring-backend" FlagPage = "page" FlagLimit = "limit" + FlagUnsafeCORS = "unsafe-cors" ) // LineBreak can be included in a command list to provide a blank line @@ -135,6 +142,7 @@ func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { cmd.Flags().Uint(FlagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Uint(FlagRPCReadTimeout, 10, "The RPC read timeout (in seconds)") cmd.Flags().Uint(FlagRPCWriteTimeout, 10, "The RPC write timeout (in seconds)") + cmd.Flags().Bool(FlagUnsafeCORS, false, "Allows CORS requests from all domains. For development purposes only, use it at your own risk.") return cmd } diff --git a/client/input/input.go b/client/input/input.go index 95f32281edd5..73dd822fd369 100644 --- a/client/input/input.go +++ b/client/input/input.go @@ -66,7 +66,7 @@ func GetCheckPassword(prompt, prompt2 string, buf *bufio.Reader) (string, error) // If the input is not recognized, it returns false and a nil error. func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) { if inputIsTty() { - fmt.Print(fmt.Sprintf("%s [y/N]: ", prompt)) + fmt.Printf("%s [y/N]: ", prompt) } response, err := readLineFromBuf(buf) diff --git a/client/keys/attestation.go b/client/keys/attestation.go index 1fe48009eaa6..a41f7cc133ae 100644 --- a/client/keys/attestation.go +++ b/client/keys/attestation.go @@ -4,38 +4,40 @@ import ( "bufio" "errors" "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/libs/bech32" + "github.com/cosmos/cosmos-sdk/client/attestation" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/input" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/bech32" ) func AttestationCmd() *cobra.Command { cmd := &cobra.Command{ Use: "attestation", Short: "Create or verify attestations", - Long: `Create or verify offline proofs to demonstrate ownership of keys`, + Long: `Create or verify offline proofs to demonstrate ownership of keys`, } cmd.AddCommand( &cobra.Command{ - Use: "create [name]", + Use: "create [name]", Short: "Create an attestation", - Long: "Create and attestation for one of the keys present in the store", - Args: cobra.ExactArgs(1), - RunE: runAttestationCreate, + Long: "Create and attestation for one of the keys present in the store", + Args: cobra.ExactArgs(1), + RunE: runAttestationCreate, }, &cobra.Command{ - Use: "verify [address] [attestation]", + Use: "verify [address] [attestation]", Short: "Verify an attestation", - Long: "Given an attestation and address, verify that one proves ownership of the other", - Args: cobra.ExactArgs(2), - RunE: runAttestationVerify, + Long: "Given an attestation and address, verify that one proves ownership of the other", + Args: cobra.ExactArgs(2), + RunE: runAttestationVerify, }, ) @@ -92,4 +94,4 @@ func runAttestationVerify(cmd *cobra.Command, args []string) error { fmt.Println("verification successful") return nil -} \ No newline at end of file +} diff --git a/client/keys/import_test.go b/client/keys/import_test.go index faac935179bb..9e5cdc64ab43 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -42,7 +42,7 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO =f3l4 -----END TENDERMINT PRIVATE KEY----- ` - require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644)) + require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0600)) // Now enter password if runningUnattended { diff --git a/client/keys/parse.go b/client/keys/parse.go index 79ccca739f0a..858b71db175b 100644 --- a/client/keys/parse.go +++ b/client/keys/parse.go @@ -1,9 +1,11 @@ package keys import ( + "context" "encoding/hex" "errors" "fmt" + "io" "strings" "github.com/spf13/cobra" @@ -17,14 +19,15 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var config = sdk.GetConfig() -var bech32Prefixes = []string{ - config.GetBech32AccountAddrPrefix(), - config.GetBech32AccountPubPrefix(), - config.GetBech32ValidatorAddrPrefix(), - config.GetBech32ValidatorPubPrefix(), - config.GetBech32ConsensusAddrPrefix(), - config.GetBech32ConsensusPubPrefix(), +func bech32Prefixes(config *sdk.Config) []string { + return []string{ + config.GetBech32AccountAddrPrefix(), + config.GetBech32AccountPubPrefix(), + config.GetBech32ValidatorAddrPrefix(), + config.GetBech32ValidatorPubPrefix(), + config.GetBech32ConsensusAddrPrefix(), + config.GetBech32ConsensusPubPrefix(), + } } type hexOutput struct { @@ -44,13 +47,16 @@ type bech32Output struct { Formats []string `json:"formats"` } -func newBech32Output(bs []byte) bech32Output { +func newBech32Output(config *sdk.Config, bs []byte) bech32Output { + bech32Prefixes := bech32Prefixes(config) out := bech32Output{Formats: make([]string, len(bech32Prefixes))} + for i, prefix := range bech32Prefixes { bech32Addr, err := bech32.ConvertAndEncode(prefix, bs) if err != nil { panic(err) } + out.Formats[i] = bech32Addr } @@ -83,38 +89,52 @@ hexadecimal into bech32 cosmos prefixed format and vice versa. return cmd } -func parseKey(_ *cobra.Command, args []string) error { +func parseKey(cmd *cobra.Command, args []string) error { + config, _ := sdk.GetSealedConfig(context.Background()) + + return doParseKey(cmd, config, args) +} + +func doParseKey(cmd *cobra.Command, config *sdk.Config, args []string) error { addr := strings.TrimSpace(args[0]) + outstream := cmd.OutOrStdout() + if len(addr) == 0 { return errors.New("couldn't parse empty input") } - if !(runFromBech32(addr) || runFromHex(addr)) { + + if !(runFromBech32(outstream, addr) || runFromHex(config, outstream, addr)) { return errors.New("couldn't find valid bech32 nor hex data") } + return nil } // print info from bech32 -func runFromBech32(bech32str string) bool { +func runFromBech32(w io.Writer, bech32str string) bool { hrp, bz, err := bech32.DecodeAndConvert(bech32str) if err != nil { return false } - displayParseKeyInfo(newHexOutput(hrp, bz)) + + displayParseKeyInfo(w, newHexOutput(hrp, bz)) + return true } // print info from hex -func runFromHex(hexstr string) bool { +func runFromHex(config *sdk.Config, w io.Writer, hexstr string) bool { bz, err := hex.DecodeString(hexstr) if err != nil { return false } - displayParseKeyInfo(newBech32Output(bz)) + + displayParseKeyInfo(w, newBech32Output(config, bz)) + return true } -func displayParseKeyInfo(stringer fmt.Stringer) { +func displayParseKeyInfo(w io.Writer, stringer fmt.Stringer) { var out []byte var err error @@ -136,5 +156,5 @@ func displayParseKeyInfo(stringer fmt.Stringer) { panic(err) } - fmt.Println(string(out)) + _, _ = fmt.Fprintln(w, string(out)) } diff --git a/client/keys/parse_test.go b/client/keys/parse_test.go index f6080e795fe3..c7330033af1f 100644 --- a/client/keys/parse_test.go +++ b/client/keys/parse_test.go @@ -4,12 +4,16 @@ import ( "testing" "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" ) func TestParseKey(t *testing.T) { bech32str := "fetch104ytdpvrx9284zd50v9ep8c6j7pua7dk9j04mp" hexstr := "EB5AE9872103497EC092EF901027049E4F39200C60040D3562CD7F104A39F62E6E5A39A818F4" + config := sdk.NewConfig() + tests := []struct { name string args []string @@ -23,7 +27,7 @@ func TestParseKey(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.wantErr, parseKey(nil, tt.args) != nil) + require.Equal(t, tt.wantErr, doParseKey(ParseKeyStringCommand(), config, tt.args) != nil) }) } } diff --git a/client/lcd/root.go b/client/lcd/root.go index 8ce6e1dcb2eb..e1f551458bfd 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -13,7 +13,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" - rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + tmrpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/flags" @@ -55,12 +55,12 @@ func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTi rs.log.Error("error closing listener", "err", err) }) - cfg := rpcserver.DefaultConfig() + cfg := tmrpcserver.DefaultConfig() cfg.MaxOpenConnections = maxOpen cfg.ReadTimeout = time.Duration(readTimeout) * time.Second cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second - rs.listener, err = rpcserver.Listen(listenAddr, cfg) + rs.listener, err = tmrpcserver.Listen(listenAddr, cfg) if err != nil { return } @@ -73,13 +73,11 @@ func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTi var h http.Handler = rs.Mux if cors { - h = handlers.CORS( - handlers.AllowedHeaders([]string{"Content-Type"}), - handlers.AllowedOrigins([]string{"*"}), - )(h) + allowAllCORS := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"})) + h = allowAllCORS(h) } - return rpcserver.StartHTTPServer(rs.listener, h, rs.log, cfg) + return tmrpcserver.Serve(rs.listener, h, rs.log, cfg) } // ServeCommand will start the application REST service as a blocking process. It @@ -101,7 +99,7 @@ func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.C viper.GetInt(flags.FlagMaxOpenConnections), uint(viper.GetInt(flags.FlagRPCReadTimeout)), uint(viper.GetInt(flags.FlagRPCWriteTimeout)), - viper.GetBool(FlagAllowCORS), + viper.GetBool(flags.FlagUnsafeCORS), ) return err diff --git a/codec/codec.go b/codec/codec.go index 6cf2f623d7e4..b2d5c090df2b 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -54,7 +54,7 @@ func MustMarshalJSONIndent(cdc *Codec, obj interface{}) []byte { //__________________________________________________________________ -// generic sealed codec to be used throughout sdk +// Cdc generic sealed codec to be used throughout sdk var Cdc *Codec func init() { diff --git a/crypto/keys/keyring.go b/crypto/keys/keyring.go index 2135c3381719..976b386a579f 100644 --- a/crypto/keys/keyring.go +++ b/crypto/keys/keyring.go @@ -15,7 +15,6 @@ import ( "github.com/pkg/errors" "github.com/tendermint/crypto/bcrypt" - "github.com/tendermint/tendermint/crypto" tmcrypto "github.com/tendermint/tendermint/crypto" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" @@ -592,7 +591,7 @@ func newRealPrompt(dir string, buf io.Reader) func(string) (string, error) { continue } - saltBytes := crypto.CRandBytes(16) + saltBytes := tmcrypto.CRandBytes(16) passwordHash, err := bcrypt.GenerateFromPassword(saltBytes, []byte(pass), 2) if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/crypto/keys/lazy_keybase_test.go b/crypto/keys/lazy_keybase_test.go index e7f4f1609437..92b39e2bee9b 100644 --- a/crypto/keys/lazy_keybase_test.go +++ b/crypto/keys/lazy_keybase_test.go @@ -425,7 +425,7 @@ func TestKeygenOverride(t *testing.T) { overrideCalled := false dummyFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) { overrideCalled = true - return testPriv(bz[:]), nil + return testPriv(bz), nil } kb := New("keybasename", dir, WithKeygenFunc(dummyFunc)) diff --git a/crypto/keys/mintkey/mintkey.go b/crypto/keys/mintkey/mintkey.go index 361c5d7f8c0a..83c0e0b91672 100644 --- a/crypto/keys/mintkey/mintkey.go +++ b/crypto/keys/mintkey/mintkey.go @@ -27,7 +27,7 @@ const ( headerType = "type" ) -// Make bcrypt security parameter var, so it can be changed within the lcd test +// BcryptSecurityParameter is a var so it can be changed within the lcd test // Making the bcrypt security parameter a var shouldn't be a security issue: // One can't verify an invalid key by maliciously changing the bcrypt // parameter during a runtime vulnerability. The main security diff --git a/crypto/keys/types_test.go b/crypto/keys/types_test.go index 5b234f0db554..f29a3acea099 100644 --- a/crypto/keys/types_test.go +++ b/crypto/keys/types_test.go @@ -8,7 +8,6 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -17,7 +16,7 @@ func Test_writeReadLedgerInfo(t *testing.T) { bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") copy(tmpKey[:], bz) - lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1), Secp256k1) + lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, sdk.CoinType, 1), Secp256k1) assert.Equal(t, TypeLedger, lInfo.GetType()) path, err := lInfo.GetPath() @@ -25,7 +24,7 @@ func Test_writeReadLedgerInfo(t *testing.T) { assert.Equal(t, "44'/118'/5'/0/1", path.String()) assert.Equal(t, "fetchpub1addwnpepqddddqg2glc8x4fl7vxjlnr7p5a3czm5kcdp4239sg6yqdc4rc2r5t35gat", - types.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, lInfo.GetPubKey())) + sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, lInfo.GetPubKey())) // Serialize and restore serialized := marshalInfo(lInfo) diff --git a/docs/interfaces/rest.md b/docs/interfaces/rest.md index 75e5a78dc3d8..9ba57ffb2af9 100644 --- a/docs/interfaces/rest.md +++ b/docs/interfaces/rest.md @@ -7,10 +7,10 @@ synopsis: "This document describes how to create a REST interface for an SDK **a ## Prerequisites {hide} -* [Query Lifecycle](./query-lifecycle.md) {prereq} -* [Application CLI](./cli.md) {prereq} +- [Query Lifecycle](./query-lifecycle.md) {prereq} +- [Application CLI](./cli.md) {prereq} -## Application REST Interface +## Application REST Interface Building the REST Interface for an application is done by [aggregating REST Routes](#registering-routes) defined in the application's modules. This interface is served by a REST Server [REST server](#rest-server), which route requests and output responses in the application itself. The SDK comes with its own REST Server by default. To enable it, the `rest.ServeCommand` command needs to be added as a subcommand of the `rootCmd` in the `main()` function of the [CLI interface](./cli.md): @@ -24,19 +24,19 @@ Users will then be able to use the application CLI to start a new REST server, a appcli rest-server --chain-id --trust-node ``` -Note that if `trust-node` is set to `false`, the REST server will verify the query proof against the merkle root (contained in the block header). +Note that if `trust-node` is set to `false`, the REST server will verify the query proof against the merkle root (contained in the block header). ## REST Server A REST Server is used to receive and route HTTP Requests, obtain the results from the application, and return a response to the user. The REST Server defined by the SDK `rest` package contains the following: -* **Router:** A router for HTTP requests. A new router can be instantiated for an application and used to match routes based on path, request method, headers, etc. The SDK uses the [Gorilla Mux Router](https://github.com/gorilla/mux). -* **CLIContext:** A [`CLIContext`](./query-lifecycle.md#clicontext) created for a user interaction. -* **Keybase:** A [Keybase](../basics/accounts.md#keybase) is a key manager. -* **Logger:** A logger from Tendermint `Log`, a log package structured around key-value pairs that allows logging level to be set differently for different keys. The logger takes `Debug()`, `Info()`, and `Error()`s. -* **Listener:** A [listener](https://golang.org/pkg/net/#Listener) from the net package. +- **Router:** A router for HTTP requests. A new router can be instantiated for an application and used to match routes based on path, request method, headers, etc. The SDK uses the [Gorilla Mux Router](https://github.com/gorilla/mux). +- **CLIContext:** A [`CLIContext`](./query-lifecycle.md#clicontext) created for a user interaction. +- **Keybase:** A [Keybase](../basics/accounts.md#keybase) is a key manager. +- **Logger:** A logger from Tendermint `Log`, a log package structured around key-value pairs that allows logging level to be set differently for different keys. The logger takes `Debug()`, `Info()`, and `Error()`s. +- **Listener:** A [listener](https://golang.org/pkg/net/#Listener) from the net package. -Of the five, the only attribute that application developers need interact with is the `router`: they need to add routes to it so that the REST server can properly handle queries. See the next section for more information on registering routes. +Of the five, the only attribute that application developers need interact with is the `router`: they need to add routes to it so that the REST server can properly handle queries. See the next section for more information on registering routes. In order to enable the REST Server in an SDK application, the `rest.ServeCommand` needs to be added to the application's command-line interface. See the [above section](#application-rest-interface) for more information. @@ -44,7 +44,7 @@ In order to enable the REST Server in an SDK application, the `rest.ServeCommand To include routes for each module in an application, the CLI must have some kind of function to register routes in its REST Server. This function is called `RegisterRoutes()`, and is utilized by the `ServeCommand` and must include routes for each of the application's modules. Since each module used by an SDK application implements a [`RegisterRESTRoutes`](../building-modules/module-interfaces.md#rest) function, application developers simply use the [Module Manager](../building-modules/module-manager.md) to call this function for each module (this is done in the [application's constructor](../basics/app-anatomy.md#constructor-function)). -At the bare minimum, a `RegisterRoutes()` function should use the SDK client package `RegisterRoutes()` function to be able to route RPC calls, and instruct the application Module Manager to call `RegisterRESTRoutes()` for all of its modules. This is done in the `main.go` file of the CLI (typically located in `./cmd/appcli/main.go`). +At the bare minimum, a `RegisterRoutes()` function should use the SDK client package `RegisterRoutes()` function to be able to route RPC calls, and instruct the application Module Manager to call `RegisterRESTRoutes()` for all of its modules. This is done in the `main.go` file of the CLI (typically located in `./cmd/appcli/main.go`). ```go func registerRoutes(rs *rest.RestServer) { @@ -58,3 +58,14 @@ This function is specific to the application and passed in to the `ServeCommand` ```go rootCmd.AddCommand(rest.ServeCommand(cdc, registerRoutes)) ``` + +## Cross-Origin Resource Sharing (CORS) + +[CORS policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) are not enabled by default to help with security. If you would like to use the rest-server in a public environment we recommend you provide a reverse proxy, this can be done with [nginx](https://www.nginx.com/). For testing and development purposes there is an `unsafe_cors` flag that can be passed to the cmd to enable accepting cors from everyone. + +```sh +gaiacli rest-server --chain-id=test \ + --laddr=tcp://localhost:1317 \ + --node tcp://localhost:26657 \ + --trust-node=true --unsafe-cors +``` diff --git a/go.mod b/go.mod index 79156a345a1c..659186cada44 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,39 @@ +go 1.13 + module github.com/cosmos/cosmos-sdk require ( github.com/99designs/keyring v1.1.3 github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d github.com/bgentry/speakeasy v0.1.0 - github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d + github.com/btcsuite/btcd v0.20.1-beta github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d github.com/cosmos/ledger-cosmos-go v0.11.1 github.com/gogo/protobuf v1.3.1 github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 github.com/gorilla/handlers v1.4.2 - github.com/gorilla/mux v1.7.3 + github.com/gorilla/mux v1.7.4 github.com/hashicorp/golang-lru v0.5.4 github.com/mattn/go-isatty v0.0.12 github.com/pelletier/go-toml v1.6.0 github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.6 github.com/spf13/afero v1.2.1 // indirect - github.com/spf13/cobra v0.0.6 + github.com/spf13/cobra v1.0.0 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.6.2 - github.com/stretchr/testify v1.5.1 + github.com/spf13/viper v1.6.3 + github.com/stretchr/testify v1.6.1 github.com/tendermint/btcd v0.1.1 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 github.com/tendermint/go-amino v0.15.1 - github.com/tendermint/iavl v0.13.2 - github.com/tendermint/tendermint v0.33.3 - github.com/tendermint/tm-db v0.5.0 - gopkg.in/yaml.v2 v2.2.8 + github.com/tendermint/iavl v0.14.0 + github.com/tendermint/tendermint v0.33.7 + github.com/tendermint/tm-db v0.5.1 + google.golang.org/grpc v1.30.0 // indirect + gopkg.in/yaml.v2 v2.3.0 ) -go 1.13 - replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 -replace github.com/tendermint/tendermint => github.com/fetchai/cosmos-consensus v0.6.0 +replace github.com/tendermint/tendermint => github.com/fetchai/cosmos-consensus v0.6.1 diff --git a/go.sum b/go.sum index f4387c55cec4..a6c0d7e27218 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,15 @@ github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2Bylw github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+UVU+Hfcihr1timk8YNXHxzZWgCo7ofnrZRApw= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= @@ -39,11 +40,15 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= @@ -63,6 +68,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -97,8 +103,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -106,8 +110,8 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fetchai/cosmos-consensus v0.6.0 h1:/mmC+oz5KLXiJsqXsPsqOwR4YIwnFoeUac/MyXLNUJk= -github.com/fetchai/cosmos-consensus v0.6.0/go.mod h1:iDSJXmKFKyNiZnvCwrGkecKkPo5aNJuZST9dR4NSp2U= +github.com/fetchai/cosmos-consensus v0.6.1 h1:4/E2sBOjOzb5OoLC40Eb0Ee4fhFzHKFvLOeU/KmSW3M= +github.com/fetchai/cosmos-consensus v0.6.1/go.mod h1:3uBzeHvXTweU0DE0ja1RmM/rxcf+tTajjRCfcF+DG3A= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -148,8 +152,12 @@ github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -172,12 +180,13 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -187,6 +196,8 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8 github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f h1:8N8XWLZelZNibkhM1FuF+3Ad3YIbgirjdMiVA0eUkaM= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -262,6 +273,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -324,8 +337,8 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= -github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -351,6 +364,8 @@ github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= @@ -376,8 +391,8 @@ github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -386,8 +401,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= -github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= +github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -398,8 +413,9 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= @@ -413,11 +429,10 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/iavl v0.13.2 h1:O1m08/Ciy53l9IYmf75uIRVvrNsfjEbre8u/yCu/oqk= -github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA= -github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= -github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= -github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= +github.com/tendermint/iavl v0.14.0 h1:Jkff+IFrXxRWtH9Jn/ga/2cxNnzMTv58xEKgCJsKUBg= +github.com/tendermint/iavl v0.14.0/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxablW5oJZ6sE= +github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= +github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -449,8 +464,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -478,6 +496,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -494,6 +514,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -505,6 +526,8 @@ golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -551,9 +574,16 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -575,8 +605,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/server/config/config.go b/server/config/config.go index 9752ae85273a..f046bbe56535 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -19,6 +19,11 @@ type BaseConfig struct { // specified in this config (e.g. 0.25token1;0.0001token2). MinGasPrices string `mapstructure:"minimum-gas-prices"` + Pruning string `mapstructure:"pruning"` + PruningKeepRecent string `mapstructure:"pruning-keep-recent"` + PruningKeepEvery string `mapstructure:"pruning-keep-every"` + PruningInterval string `mapstructure:"pruning-interval"` + // HaltHeight contains a non-zero block height at which a node will gracefully // halt and shutdown that can be used to assist upgrades and testing. // @@ -34,8 +39,6 @@ type BaseConfig struct { // InterBlockCache enables inter-block caching. InterBlockCache bool `mapstructure:"inter-block-cache"` - - Pruning string `mapstructure:"pruning"` } // Config defines the server's top level configuration @@ -73,10 +76,13 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins { // DefaultConfig returns server's default configuration. func DefaultConfig() *Config { return &Config{ - BaseConfig{ - MinGasPrices: defaultMinGasPrices, - InterBlockCache: true, - Pruning: store.PruningStrategySyncable, + BaseConfig: BaseConfig{ + MinGasPrices: defaultMinGasPrices, + InterBlockCache: true, + Pruning: storetypes.PruningOptionDefault, + PruningKeepRecent: "0", + PruningKeepEvery: "0", + PruningInterval: "0", }, } } diff --git a/server/config/toml.go b/server/config/toml.go index e556ed57656b..6fb3f166f976 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -18,6 +18,17 @@ const defaultConfigTemplate = `# This is a TOML config file. # specified in this config (e.g. 0.25token1;0.0001token2). minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}" +# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals +# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) +# everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals +# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' +pruning = "{{ .BaseConfig.Pruning }}" + +# These are applied if and only if the pruning strategy is custom. +pruning-keep-recent = "{{ .BaseConfig.PruningKeepRecent }}" +pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}" +pruning-interval = "{{ .BaseConfig.PruningInterval }}" + # HaltHeight contains a non-zero block height at which a node will gracefully # halt and shutdown that can be used to assist upgrades and testing. # @@ -33,12 +44,6 @@ halt-time = {{ .BaseConfig.HaltTime }} # InterBlockCache enables inter-block caching. inter-block-cache = {{ .BaseConfig.InterBlockCache }} - -# Pruning sets the pruning strategy: syncable, nothing, everything -# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) -# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -# everything: all saved states will be deleted, storing only the current state -pruning = "{{ .BaseConfig.Pruning }}" ` var configTemplate *template.Template diff --git a/server/pruning.go b/server/pruning.go new file mode 100644 index 000000000000..26497f4b8863 --- /dev/null +++ b/server/pruning.go @@ -0,0 +1,38 @@ +package server + +import ( + "fmt" + "strings" + + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/types" +) + +// GetPruningOptionsFromFlags parses command flags and returns the correct +// PruningOptions. If a pruning strategy is provided, that will be parsed and +// returned, otherwise, it is assumed custom pruning options are provided. +func GetPruningOptionsFromFlags() (types.PruningOptions, error) { + strategy := strings.ToLower(viper.GetString(FlagPruning)) + + switch strategy { + case types.PruningOptionDefault, types.PruningOptionNothing, types.PruningOptionEverything: + return types.NewPruningOptionsFromString(strategy), nil + + case types.PruningOptionCustom: + opts := types.NewPruningOptions( + viper.GetUint64(FlagPruningKeepRecent), + viper.GetUint64(FlagPruningKeepEvery), viper.GetUint64(FlagPruningInterval), + ) + + if err := opts.Validate(); err != nil { + return opts, fmt.Errorf("invalid custom pruning options: %w", err) + } + + return opts, nil + + default: + return store.PruningOptions{}, fmt.Errorf("unknown pruning strategy %s", strategy) + } +} diff --git a/server/pruning_test.go b/server/pruning_test.go new file mode 100644 index 000000000000..d05f2d2c2902 --- /dev/null +++ b/server/pruning_test.go @@ -0,0 +1,64 @@ +package server + +import ( + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +func TestGetPruningOptionsFromFlags(t *testing.T) { + tests := []struct { + name string + initParams func() + expectedOptions types.PruningOptions + wantErr bool + }{ + { + name: FlagPruning, + initParams: func() { + viper.Set(FlagPruning, types.PruningOptionNothing) + }, + expectedOptions: types.PruneNothing, + }, + { + name: "custom pruning options", + initParams: func() { + viper.Set(FlagPruning, types.PruningOptionCustom) + viper.Set(FlagPruningKeepRecent, 1234) + viper.Set(FlagPruningKeepEvery, 4321) + viper.Set(FlagPruningInterval, 10) + }, + expectedOptions: types.PruningOptions{ + KeepRecent: 1234, + KeepEvery: 4321, + Interval: 10, + }, + }, + { + name: types.PruningOptionDefault, + initParams: func() {}, + expectedOptions: types.PruneDefault, + }, + } + + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(j *testing.T) { + viper.Reset() + viper.SetDefault(FlagPruning, types.PruningOptionDefault) + tt.initParams() + + opts, err := GetPruningOptionsFromFlags() + if tt.wantErr { + require.Error(t, err) + return + } + + require.Equal(t, tt.expectedOptions, opts) + }) + } +} diff --git a/server/start.go b/server/start.go index 5a9cd0500884..ff1725b671fb 100644 --- a/server/start.go +++ b/server/start.go @@ -16,6 +16,8 @@ import ( "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" ) // Tendermint full-node start flags @@ -23,13 +25,18 @@ const ( flagWithTendermint = "with-tendermint" flagAddress = "address" flagTraceStore = "trace-store" - flagPruning = "pruning" flagCPUProfile = "cpu-profile" FlagMinGasPrices = "minimum-gas-prices" FlagHaltHeight = "halt-height" FlagHaltTime = "halt-time" FlagInterBlockCache = "inter-block-cache" FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades" + FlagTrace = "trace" + + FlagPruning = "pruning" + FlagPruningKeepRecent = "pruning-keep-recent" + FlagPruningKeepEvery = "pruning-keep-every" + FlagPruningInterval = "pruning-interval" ) // StartCmd runs the service passed in, either stand-alone or in-process with @@ -41,11 +48,15 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { Long: `Run the full node application with Tendermint in or out of process. By default, the application will run with Tendermint in process. -Pruning options can be provided via the '--pruning' flag. The options are as follows: +Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent', +'pruning-keep-every', and 'pruning-interval' together. -syncable: only those states not needed for state syncing will be deleted (flushes every 100th to disk and keeps every 10000th) +For '--pruning' the options are as follows: + +default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -everything: all saved states will be deleted, storing only the current state +everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals +custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval' Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During the ABCI Commit phase, the node will check if the current block height is greater than or equal to @@ -56,6 +67,10 @@ will not be able to commit subsequent blocks. For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag which accepts a path for the resulting pprof file. `, + PreRunE: func(cmd *cobra.Command, args []string) error { + _, err := GetPruningOptionsFromFlags() + return err + }, RunE: func(cmd *cobra.Command, args []string) error { if !viper.GetBool(flagWithTendermint) { ctx.Logger.Info("starting ABCI without Tendermint") @@ -73,7 +88,7 @@ which accepts a path for the resulting pprof file. cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint") cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") - cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything") + cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log") cmd.Flags().String( FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)", @@ -84,6 +99,16 @@ which accepts a path for the resulting pprof file. cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching") cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") + cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") + cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") + cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')") + cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") + viper.BindPFlag(FlagTrace, cmd.Flags().Lookup(FlagTrace)) + viper.BindPFlag(FlagPruning, cmd.Flags().Lookup(FlagPruning)) + viper.BindPFlag(FlagPruningKeepRecent, cmd.Flags().Lookup(FlagPruningKeepRecent)) + viper.BindPFlag(FlagPruningKeepEvery, cmd.Flags().Lookup(FlagPruningKeepEvery)) + viper.BindPFlag(FlagPruningInterval, cmd.Flags().Lookup(FlagPruningInterval)) + // add support for all Tendermint-specific command line options tcmd.AddNodeFlags(cmd) return cmd @@ -151,8 +176,6 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } - UpgradeOldPrivValFile(cfg) - // create & start tendermint node tmNode, err := node.NewNode( cfg, diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 5517fff4103b..ddf64f534e5c 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -4,6 +4,7 @@ package server import ( "fmt" + "github.com/cosmos/cosmos-sdk/client/attestation" "github.com/spf13/cobra" @@ -43,12 +44,13 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { Use: "show-validator", Short: "Show this node's tendermint validator info", RunE: func(cmd *cobra.Command, args []string) error { - cfg := ctx.Config - UpgradeOldPrivValFile(cfg) - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) - valPubKey := privValidator.GetPubKey() + privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + + valPubKey, err := privValidator.GetPubKey() + if err != nil { + return err + } if viper.GetString(cli.OutputFlag) == "json" { return printlnJSON(valPubKey) @@ -74,11 +76,8 @@ func ShowAddressCmd(ctx *Context) *cobra.Command { Use: "show-address", Short: "Shows this node's tendermint validator consensus address", RunE: func(cmd *cobra.Command, args []string) error { - cfg := ctx.Config - UpgradeOldPrivValFile(cfg) - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress()) if viper.GetString(cli.OutputFlag) == "json" { @@ -157,9 +156,7 @@ func AttestationCmd(ctx *Context) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config - UpgradeOldPrivValFile(cfg) - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) att, err := attestation.NewAttestation(privValidator.Key.PrivKey) if err != nil { diff --git a/server/util.go b/server/util.go index e0d5336c99c9..656b4c56dbc1 100644 --- a/server/util.go +++ b/server/util.go @@ -19,7 +19,6 @@ import ( "github.com/tendermint/tendermint/libs/cli" tmflags "github.com/tendermint/tendermint/libs/cli/flags" "github.com/tendermint/tendermint/libs/log" - pvm "github.com/tendermint/tendermint/privval" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" @@ -218,16 +217,6 @@ func TrapSignal(cleanupFunc func()) { }() } -// UpgradeOldPrivValFile converts old priv_validator.json file (prior to Tendermint 0.28) -// to the new priv_validator_key.json and priv_validator_state.json files. -func UpgradeOldPrivValFile(config *cfg.Config) { - if _, err := os.Stat(config.OldPrivValidatorFile()); !os.IsNotExist(err) { - if oldFilePV, err := pvm.LoadOldFilePV(config.OldPrivValidatorFile()); err == nil { - oldFilePV.Upgrade(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - } - } -} - func skipInterface(iface net.Interface) bool { if iface.Flags&net.FlagUp == 0 { return true // interface down diff --git a/simapp/utils.go b/simapp/utils.go index bc3e889fe68c..b1bfda3a9790 100644 --- a/simapp/utils.go +++ b/simapp/utils.go @@ -81,7 +81,7 @@ func CheckExportSimulation( return err } - if err := ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0644); err != nil { + if err := ioutil.WriteFile(config.ExportStatePath, []byte(appState), 0600); err != nil { return err } } @@ -93,7 +93,7 @@ func CheckExportSimulation( return err } - if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0644); err != nil { + if err := ioutil.WriteFile(config.ExportParamsPath, paramsBz, 0600); err != nil { return err } } diff --git a/store/cache/cache_test.go b/store/cache/cache_test.go index 3fc5f9157dbc..93ece75083ed 100644 --- a/store/cache/cache_test.go +++ b/store/cache/cache_test.go @@ -20,7 +20,7 @@ func TestGetOrSetStoreCache(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) store2 := mngr.GetStoreCache(sKey, store) require.NotNil(t, store2) @@ -34,7 +34,7 @@ func TestUnwrap(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) _ = mngr.GetStoreCache(sKey, store) require.Equal(t, store, mngr.Unwrap(sKey)) @@ -48,7 +48,7 @@ func TestStoreCache(t *testing.T) { sKey := types.NewKVStoreKey("test") tree, err := iavl.NewMutableTree(db, 100) require.NoError(t, err) - store := iavlstore.UnsafeNewStore(tree, types.PruneNothing) + store := iavlstore.UnsafeNewStore(tree) kvStore := mngr.GetStoreCache(sKey, store) for i := uint(0); i < cache.DefaultCommitKVStoreCacheSize*2; i++ { diff --git a/store/iavl/store.go b/store/iavl/store.go index d1e1d9c4cae0..f5af4099bc0b 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -1,11 +1,9 @@ package iavl import ( - "fmt" "io" "sync" - "github.com/pkg/errors" "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" @@ -31,39 +29,14 @@ var ( // Store Implements types.KVStore and CommitKVStore. type Store struct { - tree Tree - pruning types.PruningOptions + tree Tree } // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the // store's version (id) from the provided DB. An error is returned if the version // fails to load. -func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyLoading bool) (types.CommitKVStore, error) { - if !pruning.IsValid() { - return nil, fmt.Errorf("pruning options are invalid: %v", pruning) - } - - var keepRecent int64 - - // Determine the value of keepRecent based on the following: - // - // If KeepEvery = 1, keepRecent should be 0 since there is no need to keep - // latest version in a in-memory cache. - // - // If KeepEvery > 1, keepRecent should be 1 so that state changes in between - // flushed states can be saved in the in-memory latest tree. - if pruning.KeepEvery == 1 { - keepRecent = 0 - } else { - keepRecent = 1 - } - - tree, err := iavl.NewMutableTreeWithOpts( - db, - dbm.NewMemDB(), - defaultIAVLCacheSize, - iavl.PruningOptions(pruning.KeepEvery, keepRecent), - ) +func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) { + tree, err := iavl.NewMutableTree(db, defaultIAVLCacheSize) if err != nil { return nil, err } @@ -79,8 +52,7 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL } return &Store{ - tree: tree, - pruning: pruning, + tree: tree, }, nil } @@ -90,10 +62,9 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL // CONTRACT: The IAVL tree should be fully loaded. // CONTRACT: PruningOptions passed in as argument must be the same as pruning options // passed into iavl.MutableTree -func UnsafeNewStore(tree *iavl.MutableTree, po types.PruningOptions) *Store { +func UnsafeNewStore(tree *iavl.MutableTree) *Store { return &Store{ - tree: tree, - pruning: po, + tree: tree, } } @@ -113,8 +84,7 @@ func (st *Store) GetImmutable(version int64) (*Store, error) { } return &Store{ - tree: &immutableTree{iTree}, - pruning: st.pruning, + tree: &immutableTree{iTree}, }, nil } @@ -123,25 +93,9 @@ func (st *Store) GetImmutable(version int64) (*Store, error) { func (st *Store) Commit() types.CommitID { hash, version, err := st.tree.SaveVersion() if err != nil { - // TODO: Do we want to extend Commit to allow returning errors? panic(err) } - // If the version we saved got flushed to disk, check if previous flushed - // version should be deleted. - if st.pruning.FlushVersion(version) { - previous := version - st.pruning.KeepEvery - - // Previous flushed version should only be pruned if the previous version is - // not a snapshot version OR if snapshotting is disabled (SnapshotEvery == 0). - if previous != 0 && !st.pruning.SnapshotVersion(previous) { - err := st.tree.DeleteVersion(previous) - if errCause := errors.Cause(err); errCause != nil && errCause != iavl.ErrVersionDoesNotExist { - panic(err) - } - } - } - return types.CommitID{ Version: version, Hash: hash, @@ -204,6 +158,13 @@ func (st *Store) Delete(key []byte) { st.tree.Remove(key) } +// DeleteVersions deletes a series of versions from the MutableTree. An error +// is returned if any single version is invalid or the delete fails. All writes +// happen in a single batch with a single commit. +func (st *Store) DeleteVersions(versions ...int64) error { + return st.tree.DeleteVersions(versions...) +} + // Implements types.KVStore. func (st *Store) Iterator(start, end []byte) types.Iterator { var iTree *iavl.ImmutableTree diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index ed138c84964f..7a65c1880c64 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -52,7 +52,7 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { func TestGetImmutable(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) - store := UnsafeNewStore(tree, types.PruneNothing) + store := UnsafeNewStore(tree) require.True(t, tree.Set([]byte("hello"), []byte("adios"))) hash, ver, err := tree.SaveVersion() @@ -82,7 +82,7 @@ func TestGetImmutable(t *testing.T) { func TestTestGetImmutableIterator(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) - store := UnsafeNewStore(tree, types.PruneNothing) + store := UnsafeNewStore(tree) newStore, err := store.GetImmutable(cID.Version) require.NoError(t, err) @@ -105,7 +105,7 @@ func TestTestGetImmutableIterator(t *testing.T) { func TestIAVLStoreGetSetHasDelete(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) key := "hello" @@ -130,14 +130,14 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { func TestIAVLStoreNoNilSet(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz")) expected := []string{"aloha", "hello"} var i int @@ -213,7 +213,7 @@ func TestIAVLReverseIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte{0x00}, []byte("0")) iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0")) @@ -246,7 +246,7 @@ func TestIAVLPrefixIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -310,7 +310,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -373,95 +373,12 @@ func nextVersion(iavl *Store) { iavl.Commit() } -func TestIAVLDefaultPruning(t *testing.T) { - //Expected stored / deleted version numbers for: - //numRecent = 5, storeEvery = 3, snapshotEvery = 5 - var states = []pruneState{ - {[]int64{}, []int64{}}, - {[]int64{1}, []int64{}}, - {[]int64{1, 2}, []int64{}}, - {[]int64{1, 2, 3}, []int64{}}, - {[]int64{1, 2, 3, 4}, []int64{}}, - {[]int64{1, 2, 3, 4, 5}, []int64{}}, - {[]int64{2, 4, 5, 6}, []int64{1, 3}}, - {[]int64{4, 5, 6, 7}, []int64{1, 2, 3}}, - {[]int64{4, 5, 6, 7, 8}, []int64{1, 2, 3}}, - {[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}}, - {[]int64{6, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 5}}, - {[]int64{6, 7, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 5}}, - {[]int64{6, 8, 10, 11, 12}, []int64{1, 2, 3, 4, 5, 7, 9}}, - {[]int64{6, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 7, 8, 9}}, - {[]int64{6, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 7, 8, 9}}, - {[]int64{6, 11, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 7, 8, 9, 10}}, - } - testPruning(t, int64(5), int64(3), int64(6), states) -} - -func TestIAVLAlternativePruning(t *testing.T) { - //Expected stored / deleted version numbers for: - //numRecent = 3, storeEvery = 5, snapshotEvery = 10 - var states = []pruneState{ - {[]int64{}, []int64{}}, - {[]int64{1}, []int64{}}, - {[]int64{1, 2}, []int64{}}, - {[]int64{1, 2, 3}, []int64{}}, - {[]int64{2, 3, 4}, []int64{1}}, - {[]int64{3, 4, 5}, []int64{1, 2}}, - {[]int64{4, 5, 6}, []int64{1, 2, 3}}, - {[]int64{5, 6, 7}, []int64{1, 2, 3, 4}}, - {[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}}, - {[]int64{5, 7, 8, 9}, []int64{1, 2, 3, 4, 6}}, - {[]int64{8, 9, 10}, []int64{1, 2, 3, 4, 6, 7}}, - {[]int64{9, 10, 11}, []int64{1, 2, 3, 4, 6, 7, 8}}, - {[]int64{10, 11, 12}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}}, - {[]int64{10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}}, - {[]int64{10, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11}}, - {[]int64{10, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12}}, - } - testPruning(t, int64(3), int64(5), int64(10), states) -} - -type pruneState struct { - stored []int64 - deleted []int64 -} - -func testPruning(t *testing.T, numRecent int64, storeEvery int64, snapshotEvery int64, states []pruneState) { - db := dbm.NewMemDB() - pruningOpts := types.PruningOptions{ - KeepEvery: storeEvery, - SnapshotEvery: snapshotEvery, - } - iavlOpts := iavl.PruningOptions(storeEvery, numRecent) - - tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts) - require.NoError(t, err) - - iavlStore := UnsafeNewStore(tree, pruningOpts) - - for step, state := range states { - for _, ver := range state.stored { - require.True(t, iavlStore.VersionExists(ver), - "missing version %d with latest version %d; should save last %d, store every %d, and snapshot every %d", - ver, step, numRecent, storeEvery, snapshotEvery) - } - - for _, ver := range state.deleted { - require.False(t, iavlStore.VersionExists(ver), - "not pruned version %d with latest version %d; should prune all but last %d and every %d with intermediate flush interval %d", - ver, step, numRecent, snapshotEvery, storeEvery) - } - - nextVersion(iavlStore) - } -} - func TestIAVLNoPrune(t *testing.T) { db := dbm.NewMemDB() tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) nextVersion(iavlStore) for i := 1; i < 100; i++ { @@ -475,37 +392,12 @@ func TestIAVLNoPrune(t *testing.T) { } } -func TestIAVLPruneEverything(t *testing.T) { - db := dbm.NewMemDB() - iavlOpts := iavl.PruningOptions(0, 1) // only store latest version in memory - - tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts) - require.NoError(t, err) - - iavlStore := UnsafeNewStore(tree, types.PruneEverything) - nextVersion(iavlStore) - - for i := 1; i < 100; i++ { - for j := 1; j < i; j++ { - require.False(t, iavlStore.VersionExists(int64(j)), - "not pruned version %d with latest version %d; should prune all old versions", - j, i) - } - - require.True(t, iavlStore.VersionExists(int64(i)), - "missing current version on step %d; should not prune current state tree", - i) - - nextVersion(iavlStore) - } -} - func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree, err := iavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) k1, v1 := []byte("key1"), []byte("val1") k2, v2 := []byte("key2"), []byte("val2") @@ -604,7 +496,7 @@ func BenchmarkIAVLIteratorNext(b *testing.B) { tree.Set(key, value) } - iavlStore := UnsafeNewStore(tree, types.PruneNothing) + iavlStore := UnsafeNewStore(tree) iterators := make([]types.Iterator, b.N/treeSize) for i := 0; i < len(iterators); i++ { diff --git a/store/iavl/tree.go b/store/iavl/tree.go index deae294b2c5d..9873034fa550 100644 --- a/store/iavl/tree.go +++ b/store/iavl/tree.go @@ -23,6 +23,7 @@ type ( Remove(key []byte) ([]byte, bool) SaveVersion() ([]byte, int64, error) DeleteVersion(version int64) error + DeleteVersions(versions ...int64) error Version() int64 Hash() []byte VersionExists(version int64) bool @@ -55,6 +56,10 @@ func (it *immutableTree) DeleteVersion(_ int64) error { panic("cannot call 'DeleteVersion' on an immutable IAVL tree") } +func (it *immutableTree) DeleteVersions(_ ...int64) error { + panic("cannot call 'DeleteVersions' on an immutable IAVL tree") +} + func (it *immutableTree) VersionExists(version int64) bool { return it.Version() == version } diff --git a/store/prefix/store_test.go b/store/prefix/store_test.go index 427423340d8f..a4f8e6bbc0a5 100644 --- a/store/prefix/store_test.go +++ b/store/prefix/store_test.go @@ -17,11 +17,7 @@ import ( ) // copied from iavl/store_test.go -var ( - cacheSize = 100 - numRecent int64 = 5 - storeEvery int64 = 3 -) +var cacheSize = 100 func bz(s string) []byte { return []byte(s) } @@ -90,7 +86,7 @@ func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() tree, err := tiavl.NewMutableTree(db, cacheSize) require.NoError(t, err) - iavlStore := iavl.UnsafeNewStore(tree, types.PruneNothing) + iavlStore := iavl.UnsafeNewStore(tree) testPrefixStore(t, iavlStore, []byte("test")) } diff --git a/store/reexport.go b/store/reexport.go index 913a8c55a8b3..61a3d9f795a3 100644 --- a/store/reexport.go +++ b/store/reexport.go @@ -2,7 +2,6 @@ package store import ( "github.com/cosmos/cosmos-sdk/store/types" - stypes "github.com/cosmos/cosmos-sdk/store/types" ) // Import cosmos-sdk/types/store.go for convenience. @@ -27,14 +26,7 @@ type ( StoreType = types.StoreType Queryable = types.Queryable TraceContext = types.TraceContext - Gas = stypes.Gas + Gas = types.Gas GasMeter = types.GasMeter - GasConfig = stypes.GasConfig -) - -// nolint - reexport -var ( - PruneNothing = types.PruneNothing - PruneEverything = types.PruneEverything - PruneSyncable = types.PruneSyncable + GasConfig = types.GasConfig ) diff --git a/store/rootmulti/proof_test.go b/store/rootmulti/proof_test.go index 4d316f2c85aa..676fc777cd7e 100644 --- a/store/rootmulti/proof_test.go +++ b/store/rootmulti/proof_test.go @@ -14,7 +14,7 @@ import ( func TestVerifyIAVLStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - iStore, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false) + iStore, err := iavl.LoadStore(db, types.CommitID{}, false) store := iStore.(*iavl.Store) require.Nil(t, err) store.Set([]byte("MYKEY"), []byte("MYVALUE")) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 42c7b05ab589..b0dc4c269657 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -5,6 +5,8 @@ import ( "io" "strings" + "github.com/pkg/errors" + iavltree "github.com/tendermint/iavl" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" @@ -21,6 +23,7 @@ import ( const ( latestVersionKey = "s/latest" + pruneHeightsKey = "s/pruneheights" commitInfoKeyFmt = "s/%d" // s/ ) @@ -35,6 +38,7 @@ type Store struct { stores map[types.StoreKey]types.CommitKVStore keysByName map[string]types.StoreKey lazyLoading bool + pruneHeights []int64 traceWriter io.Writer traceContext types.TraceContext @@ -42,8 +46,10 @@ type Store struct { interBlockCache types.MultiStorePersistentCache } -var _ types.CommitMultiStore = (*Store)(nil) -var _ types.Queryable = (*Store)(nil) +var ( + _ types.CommitMultiStore = (*Store)(nil) + _ types.Queryable = (*Store)(nil) +) // NewStore returns a reference to a new Store object with the provided DB. The // store will be created with a PruneNothing pruning strategy by default. After @@ -56,20 +62,15 @@ func NewStore(db dbm.DB) *Store { storesParams: make(map[types.StoreKey]storeParams), stores: make(map[types.StoreKey]types.CommitKVStore), keysByName: make(map[string]types.StoreKey), + pruneHeights: make([]int64, 0), } } // SetPruning sets the pruning strategy on the root store and all the sub-stores. // Note, calling SetPruning on the root store prior to LoadVersion or // LoadLatestVersion performs a no-op as the stores aren't mounted yet. -// -// TODO: Consider removing this API altogether on sub-stores as a pruning -// strategy should only be provided on initialization. func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { rs.pruningOpts = pruningOpts - for _, substore := range rs.stores { - substore.SetPruning(pruningOpts) - } } // SetLazyLoading sets if the iavl store should be loaded lazily or not @@ -200,6 +201,12 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { rs.lastCommitInfo = cInfo rs.stores = newStores + // load any pruned heights we missed from disk to be pruned on the next run + ph, err := getPruningHeights(rs.db) + if err == nil && len(ph) > 0 { + rs.pruneHeights = ph + } + return nil } @@ -286,22 +293,59 @@ func (rs *Store) LastCommitID() types.CommitID { // Implements Committer/CommitStore. func (rs *Store) Commit() types.CommitID { - - // Commit stores. - version := rs.lastCommitInfo.Version + 1 + previousHeight := rs.lastCommitInfo.Version + version := previousHeight + 1 rs.lastCommitInfo = commitStores(version, rs.stores) - // write CommitInfo to disk only if this version was flushed to disk - if rs.pruningOpts.FlushVersion(version) { - flushCommitInfo(rs.db, version, rs.lastCommitInfo) + // Determine if pruneHeight height needs to be added to the list of heights to + // be pruned, where pruneHeight = (commitHeight - 1) - KeepRecent. + if int64(rs.pruningOpts.KeepRecent) < previousHeight { + pruneHeight := previousHeight - int64(rs.pruningOpts.KeepRecent) + // We consider this height to be pruned iff: + // + // - KeepEvery is zero as that means that all heights should be pruned. + // - KeepEvery % (height - KeepRecent) != 0 as that means the height is not + // a 'snapshot' height. + if rs.pruningOpts.KeepEvery == 0 || pruneHeight%int64(rs.pruningOpts.KeepEvery) != 0 { + rs.pruneHeights = append(rs.pruneHeights, pruneHeight) + } + } + + // batch prune if the current height is a pruning interval height + if rs.pruningOpts.Interval > 0 && version%int64(rs.pruningOpts.Interval) == 0 { + rs.pruneStores() } - // Prepare for next version. - commitID := types.CommitID{ + flushMetadata(rs.db, version, rs.lastCommitInfo, rs.pruneHeights) + + return types.CommitID{ Version: version, Hash: rs.lastCommitInfo.Hash(), } - return commitID +} + +// pruneStores will batch delete a list of heights from each mounted sub-store. +// Afterwards, pruneHeights is reset. +func (rs *Store) pruneStores() { + if len(rs.pruneHeights) == 0 { + return + } + + for key, store := range rs.stores { + if store.GetStoreType() == types.StoreTypeIAVL { + // If the store is wrapped with an inter-block cache, we must first unwrap + // it to get the underlying IAVL store. + store = rs.GetCommitKVStore(key) + + if err := store.(*iavl.Store).DeleteVersions(rs.pruneHeights...); err != nil { + if errCause := errors.Cause(err); errCause != nil && errCause != iavltree.ErrVersionDoesNotExist { + panic(err) + } + } + } + } + + rs.pruneHeights = make([]int64, 0) } // Implements CacheWrapper/Store/CommitStore. @@ -480,8 +524,6 @@ func parsePath(path string) (storeName string, subpath string, err error) { return storeName, subpath, nil } -//---------------------------------------- -// Note: why do we use key and params.key in different places. Seems like there should be only one key used. func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (types.CommitKVStore, error) { var db dbm.DB @@ -497,7 +539,7 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID panic("recursive MultiStores not yet supported") case types.StoreTypeIAVL: - store, err := iavl.LoadStore(db, id, rs.pruningOpts, rs.lazyLoading) + store, err := iavl.LoadStore(db, id, rs.lazyLoading) if err != nil { return nil, err } @@ -620,12 +662,6 @@ func getLatestVersion(db dbm.DB) int64 { return latest } -// Set the latest version. -func setLatestVersion(batch dbm.Batch, version int64) { - latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version) - batch.Set([]byte(latestVersionKey), latestBytes) -} - // Commits each store and returns a new commitInfo. func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) commitInfo { storeInfos := make([]storeInfo, 0, len(storeMap)) @@ -651,9 +687,8 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore // Gets commitInfo from disk. func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { - - // Get from DB. cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) + cInfoBytes, err := db.Get([]byte(cInfoKey)) if err != nil { return commitInfo{}, fmt.Errorf("failed to get commit info: %v", err) @@ -671,20 +706,48 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { return cInfo, nil } -// Set a commitInfo for given version. func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) { cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo) cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version) batch.Set([]byte(cInfoKey), cInfoBytes) } -// flushCommitInfo flushes a commitInfo for given version to the DB. Note, this -// needs to happen atomically. -func flushCommitInfo(db dbm.DB, version int64, cInfo commitInfo) { +func setLatestVersion(batch dbm.Batch, version int64) { + latestBytes := cdc.MustMarshalBinaryLengthPrefixed(version) + batch.Set([]byte(latestVersionKey), latestBytes) +} + +func setPruningHeights(batch dbm.Batch, pruneHeights []int64) { + bz := cdc.MustMarshalBinaryBare(pruneHeights) + batch.Set([]byte(pruneHeightsKey), bz) +} + +func getPruningHeights(db dbm.DB) ([]int64, error) { + bz, err := db.Get([]byte(pruneHeightsKey)) + if err != nil { + return nil, fmt.Errorf("failed to get pruned heights: %w", err) + } + if len(bz) == 0 { + return nil, errors.New("no pruned heights found") + } + + var prunedHeights []int64 + if err := cdc.UnmarshalBinaryBare(bz, &prunedHeights); err != nil { + return nil, fmt.Errorf("failed to unmarshal pruned heights: %w", err) + } + + return prunedHeights, nil +} + +func flushMetadata(db dbm.DB, version int64, cInfo commitInfo, pruneHeights []int64) { batch := db.NewBatch() defer batch.Close() setCommitInfo(batch, version, cInfo) setLatestVersion(batch, version) - batch.Write() + setPruningHeights(batch, pruneHeights) + + if err := batch.Write(); err != nil { + panic(fmt.Errorf("error on batch write %w", err)) + } } diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 164f231a2ac2..ddf876f45fdb 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -22,7 +22,7 @@ func TestStoreType(t *testing.T) { func TestGetCommitKVStore(t *testing.T) { var db dbm.DB = dbm.NewMemDB() - ms := newMultiStoreWithMounts(db, types.PruneSyncable) + ms := newMultiStoreWithMounts(db, types.PruneDefault) err := ms.LoadLatestVersion() require.Nil(t, err) @@ -166,7 +166,7 @@ func TestMultistoreCommitLoad(t *testing.T) { // XXX: confirm old commit is overwritten and we have rolled back // LatestVersion - store = newMultiStoreWithMounts(db, types.PruneSyncable) + store = newMultiStoreWithMounts(db, types.PruneDefault) err = store.LoadLatestVersion() require.Nil(t, err) commitID = getExpectedCommitID(store, ver+1) @@ -293,8 +293,9 @@ func TestParsePath(t *testing.T) { func TestMultiStoreRestart(t *testing.T) { db := dbm.NewMemDB() pruning := types.PruningOptions{ - KeepEvery: 3, - SnapshotEvery: 6, + KeepRecent: 2, + KeepEvery: 3, + Interval: 1, } multi := newMultiStoreWithMounts(db, pruning) err := multi.LoadLatestVersion() @@ -322,8 +323,8 @@ func TestMultiStoreRestart(t *testing.T) { multi.Commit() cinfo, err := getCommitInfo(multi.db, int64(i)) - require.NotNil(t, err) - require.Equal(t, commitInfo{}, cinfo) + require.NoError(t, err) + require.Equal(t, int64(i), cinfo.Version) } // Set and commit data in one store. @@ -347,15 +348,15 @@ func TestMultiStoreRestart(t *testing.T) { multi.Commit() postFlushCinfo, err := getCommitInfo(multi.db, 4) - require.NotNil(t, err) - require.Equal(t, commitInfo{}, postFlushCinfo, "Commit changed after in-memory commit") + require.NoError(t, err) + require.Equal(t, int64(4), postFlushCinfo.Version, "Commit changed after in-memory commit") multi = newMultiStoreWithMounts(db, pruning) err = multi.LoadLatestVersion() require.Nil(t, err) reloadedCid := multi.LastCommitID() - require.Equal(t, flushedCinfo.CommitID(), reloadedCid, "Reloaded CID is not the same as last flushed CID") + require.Equal(t, int64(4), reloadedCid.Version, "Reloaded CID is not the same as last flushed CID") // Check that store1 and store2 retained date from 3rd commit store1 = multi.getStoreByName("store1").(types.KVStore) @@ -369,7 +370,7 @@ func TestMultiStoreRestart(t *testing.T) { // Check that store3 still has data from last commit even though update happened on 2nd commit store3 = multi.getStoreByName("store3").(types.KVStore) val3 := store3.Get([]byte(k3)) - require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 2)), val3, "Reloaded value not the same as last flushed value") + require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 3)), val3, "Reloaded value not the same as last flushed value") } func TestMultiStoreQuery(t *testing.T) { @@ -442,6 +443,80 @@ func TestMultiStoreQuery(t *testing.T) { require.Equal(t, v2, qres.Value) } +func TestMultiStore_Pruning(t *testing.T) { + testCases := []struct { + name string + numVersions int64 + po types.PruningOptions + deleted []int64 + saved []int64 + }{ + {"prune nothing", 10, types.PruneNothing, nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + {"prune everything", 10, types.PruneEverything, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int64{10}}, + {"prune some; no batch", 10, types.NewPruningOptions(2, 3, 1), []int64{1, 2, 4, 5, 7}, []int64{3, 6, 8, 9, 10}}, + {"prune some; small batch", 10, types.NewPruningOptions(2, 3, 3), []int64{1, 2, 4, 5}, []int64{3, 6, 7, 8, 9, 10}}, + {"prune some; large batch", 10, types.NewPruningOptions(2, 3, 11), nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + } + + for _, tc := range testCases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + db := dbm.NewMemDB() + ms := newMultiStoreWithMounts(db, tc.po) + require.NoError(t, ms.LoadLatestVersion()) + + for i := int64(0); i < tc.numVersions; i++ { + ms.Commit() + } + + for _, v := range tc.saved { + _, err := ms.CacheMultiStoreWithVersion(v) + require.NoError(t, err, "expected error when loading height: %d", v) + } + + for _, v := range tc.deleted { + _, err := ms.CacheMultiStoreWithVersion(v) + require.Error(t, err, "expected error when loading height: %d", v) + } + }) + } +} + +func TestMultiStore_PruningRestart(t *testing.T) { + db := dbm.NewMemDB() + ms := newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11)) + require.NoError(t, ms.LoadLatestVersion()) + + // Commit enough to build up heights to prune, where on the next block we should + // batch delete. + for i := int64(0); i < 10; i++ { + ms.Commit() + } + + pruneHeights := []int64{1, 2, 4, 5, 7} + + // ensure we've persisted the current batch of heights to prune to the store's DB + ph, err := getPruningHeights(ms.db) + require.NoError(t, err) + require.Equal(t, pruneHeights, ph) + + // "restart" + ms = newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11)) + err = ms.LoadLatestVersion() + require.NoError(t, err) + require.Equal(t, pruneHeights, ms.pruneHeights) + + // commit one more block and ensure the heights have been pruned + ms.Commit() + require.Empty(t, ms.pruneHeights) + + for _, v := range pruneHeights { + _, err := ms.CacheMultiStoreWithVersion(v) + require.Error(t, err, "expected error when loading height: %d", v) + } +} + //----------------------------------------------------------------------- // utils diff --git a/store/store.go b/store/store.go index ba0311763814..2c068c413f47 100644 --- a/store/store.go +++ b/store/store.go @@ -8,13 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -// Pruning strategies that may be provided to a KVStore to enable pruning. -const ( - PruningStrategyNothing = "nothing" - PruningStrategyEverything = "everything" - PruningStrategySyncable = "syncable" -) - func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { return rootmulti.NewStore(db) } @@ -22,17 +15,3 @@ func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { func NewCommitKVStoreCacheManager() types.MultiStorePersistentCache { return cache.NewCommitKVStoreCacheManager(cache.DefaultCommitKVStoreCacheSize) } - -func NewPruningOptionsFromString(strategy string) (opt PruningOptions) { - switch strategy { - case PruningStrategyNothing: - opt = PruneNothing - case PruningStrategyEverything: - opt = PruneEverything - case PruningStrategySyncable: - opt = PruneSyncable - default: - opt = PruneSyncable - } - return -} diff --git a/store/types/iterator_test.go b/store/types/iterator_test.go index e081de80a3d5..3086917b605b 100644 --- a/store/types/iterator_test.go +++ b/store/types/iterator_test.go @@ -12,7 +12,7 @@ import ( func newMemTestKVStore(t *testing.T) types.KVStore { db := dbm.NewMemDB() - store, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false) + store, err := iavl.LoadStore(db, types.CommitID{}, false) require.NoError(t, err) return store } diff --git a/store/types/pruning.go b/store/types/pruning.go index c540443521ff..823da76c6585 100644 --- a/store/types/pruning.go +++ b/store/types/pruning.go @@ -1,66 +1,77 @@ package types +import "fmt" + +// Pruning option string constants +const ( + PruningOptionDefault = "default" + PruningOptionEverything = "everything" + PruningOptionNothing = "nothing" + PruningOptionCustom = "custom" +) + var ( - // PruneEverything defines a pruning strategy where all committed states will - // be deleted, persisting only the current state. - PruneEverything = PruningOptions{ - KeepEvery: 1, - SnapshotEvery: 0, - } + // PruneDefault defines a pruning strategy where the last 100 heights are kept + // in addition to every 100th and where to-be pruned heights are pruned at + // every 10th height. + PruneDefault = NewPruningOptions(100, 100, 10) - // PruneNothing defines a pruning strategy where all committed states will be - // kept on disk, i.e. no states will be pruned. - PruneNothing = PruningOptions{ - KeepEvery: 1, - SnapshotEvery: 1, - } + // PruneEverything defines a pruning strategy where all committed heights are + // deleted, storing only the current height and where to-be pruned heights are + // pruned at every 10th height. + PruneEverything = NewPruningOptions(0, 0, 10) - // PruneSyncable defines a pruning strategy where only those states not needed - // for state syncing will be pruned. It flushes every 100th state to disk and - // keeps every 10000th. - PruneSyncable = PruningOptions{ - KeepEvery: 100, - SnapshotEvery: 10000, - } + // PruneNothing defines a pruning strategy where all heights are kept on disk. + PruneNothing = NewPruningOptions(0, 1, 0) ) -// PruningOptions defines the specific pruning strategy every store in a multi-store -// will use when committing state, where keepEvery determines which committed -// heights are flushed to disk and snapshotEvery determines which of these heights -// are kept after pruning. +// PruningOptions defines the pruning strategy used when determining which +// heights are removed from disk when committing state. type PruningOptions struct { - KeepEvery int64 - SnapshotEvery int64 + // KeepRecent defines how many recent heights to keep on disk. + KeepRecent uint64 + + // KeepEvery defines how many offset heights are kept on disk past KeepRecent. + KeepEvery uint64 + + // Interval defines when the pruned heights are removed from disk. + Interval uint64 } -// IsValid verifies if the pruning options are valid. It returns false if invalid -// and true otherwise. Pruning options are considered valid iff: -// -// - KeepEvery > 0 -// - SnapshotEvery >= 0 -// - SnapshotEvery % KeepEvery = 0 -func (po PruningOptions) IsValid() bool { - // must flush at positive block interval - if po.KeepEvery <= 0 { - return false +func NewPruningOptions(keepRecent, keepEvery, interval uint64) PruningOptions { + return PruningOptions{ + KeepRecent: keepRecent, + KeepEvery: keepEvery, + Interval: interval, } +} - // cannot snapshot negative intervals - if po.SnapshotEvery < 0 { - return false +func (po PruningOptions) Validate() error { + if po.KeepEvery == 0 && po.Interval == 0 { + return fmt.Errorf("invalid 'Interval' when pruning everything: %d", po.Interval) + } + if po.KeepEvery == 1 && po.Interval != 0 { // prune nothing + return fmt.Errorf("invalid 'Interval' when pruning nothing: %d", po.Interval) + } + if po.KeepEvery > 1 && po.Interval == 0 { + return fmt.Errorf("invalid 'Interval' when pruning: %d", po.Interval) } - return po.SnapshotEvery%po.KeepEvery == 0 + return nil } -// FlushVersion returns a boolean signaling if the provided version/height should -// be flushed to disk. -func (po PruningOptions) FlushVersion(ver int64) bool { - return po.KeepEvery != 0 && ver%po.KeepEvery == 0 -} +func NewPruningOptionsFromString(strategy string) PruningOptions { + switch strategy { + case PruningOptionEverything: + return PruneEverything -// SnapshotVersion returns a boolean signaling if the provided version/height -// should be snapshotted (kept on disk). -func (po PruningOptions) SnapshotVersion(ver int64) bool { - return po.SnapshotEvery != 0 && ver%po.SnapshotEvery == 0 + case PruningOptionNothing: + return PruneNothing + + case PruningOptionDefault: + return PruneDefault + + default: + return PruneDefault + } } diff --git a/store/types/pruning_test.go b/store/types/pruning_test.go new file mode 100644 index 000000000000..2b88905ee8d2 --- /dev/null +++ b/store/types/pruning_test.go @@ -0,0 +1,29 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPruningOptions_Validate(t *testing.T) { + testCases := []struct { + keepRecent uint64 + keepEvery uint64 + interval uint64 + expectErr bool + }{ + {100, 500, 10, false}, // default + {0, 0, 10, false}, // everything + {0, 1, 0, false}, // nothing + {0, 10, 10, false}, + {100, 0, 0, true}, // invalid interval + {0, 1, 5, true}, // invalid interval + } + + for _, tc := range testCases { + po := NewPruningOptions(tc.keepRecent, tc.keepEvery, tc.interval) + err := po.Validate() + require.Equal(t, tc.expectErr, err != nil, "options: %v, err: %s", po, err) + } +} diff --git a/store/types/store.go b/store/types/store.go index 377b43909e11..400b3f64b080 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -18,6 +18,8 @@ type Store interface { //nolint type Committer interface { Commit() CommitID LastCommitID() CommitID + + // TODO: Deprecate after 0.38.5 SetPruning(PruningOptions) } diff --git a/tests/mocks/account_retriever.go b/tests/mocks/account_retriever.go index 9f8ee69282a2..5fa7807c8f69 100644 --- a/tests/mocks/account_retriever.go +++ b/tests/mocks/account_retriever.go @@ -1,3 +1,7 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/auth/types/account_retriever.go + +// Package mocks is a generated GoMock package. package mocks import ( @@ -6,25 +10,30 @@ import ( gomock "github.com/golang/mock/gomock" ) +// MockNodeQuerier is a mock of NodeQuerier interface type MockNodeQuerier struct { ctrl *gomock.Controller recorder *MockNodeQuerierMockRecorder } +// MockNodeQuerierMockRecorder is the mock recorder for MockNodeQuerier type MockNodeQuerierMockRecorder struct { mock *MockNodeQuerier } +// NewMockNodeQuerier creates a new mock instance func NewMockNodeQuerier(ctrl *gomock.Controller) *MockNodeQuerier { mock := &MockNodeQuerier{ctrl: ctrl} mock.recorder = &MockNodeQuerierMockRecorder{mock} return mock } +// EXPECT returns an object that allows the caller to indicate expected use func (m *MockNodeQuerier) EXPECT() *MockNodeQuerierMockRecorder { return m.recorder } +// QueryWithData mocks base method func (m *MockNodeQuerier) QueryWithData(path string, data []byte) ([]byte, int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueryWithData", path, data) @@ -34,6 +43,7 @@ func (m *MockNodeQuerier) QueryWithData(path string, data []byte) ([]byte, int64 return ret0, ret1, ret2 } +// QueryWithData indicates an expected call of QueryWithData func (mr *MockNodeQuerierMockRecorder) QueryWithData(path, data interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWithData", reflect.TypeOf((*MockNodeQuerier)(nil).QueryWithData), path, data) diff --git a/tests/mocks/types_handler.go b/tests/mocks/types_handler.go new file mode 100644 index 000000000000..c16f5d595301 --- /dev/null +++ b/tests/mocks/types_handler.go @@ -0,0 +1,51 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: types/handler.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// MockAnteDecorator is a mock of AnteDecorator interface +type MockAnteDecorator struct { + ctrl *gomock.Controller + recorder *MockAnteDecoratorMockRecorder +} + +// MockAnteDecoratorMockRecorder is the mock recorder for MockAnteDecorator +type MockAnteDecoratorMockRecorder struct { + mock *MockAnteDecorator +} + +// NewMockAnteDecorator creates a new mock instance +func NewMockAnteDecorator(ctrl *gomock.Controller) *MockAnteDecorator { + mock := &MockAnteDecorator{ctrl: ctrl} + mock.recorder = &MockAnteDecoratorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockAnteDecorator) EXPECT() *MockAnteDecoratorMockRecorder { + return m.recorder +} + +// AnteHandle mocks base method +func (m *MockAnteDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (types.Context, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AnteHandle", ctx, tx, simulate, next) + ret0, _ := ret[0].(types.Context) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AnteHandle indicates an expected call of AnteHandle +func (mr *MockAnteDecoratorMockRecorder) AnteHandle(ctx, tx, simulate, next interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnteHandle", reflect.TypeOf((*MockAnteDecorator)(nil).AnteHandle), ctx, tx, simulate, next) +} diff --git a/tests/util.go b/tests/util.go index 9b37d12c6988..45887d2ff517 100644 --- a/tests/util.go +++ b/tests/util.go @@ -10,10 +10,9 @@ import ( "time" "github.com/stretchr/testify/require" - - tmclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpcclient "github.com/tendermint/tendermint/rpc/lib/client" + tmjsonrpc "github.com/tendermint/tendermint/rpc/jsonrpc/client" "github.com/cosmos/cosmos-sdk/codec" ) @@ -29,7 +28,7 @@ func WaitForNextHeightTM(port string) { func WaitForNextNBlocksTM(n int64, port string) { // get the latest block and wait for n more url := fmt.Sprintf("http://localhost:%v", port) - cl, err := tmclient.NewHTTP(url, "/websocket") + cl, err := rpchttp.New(url, "/websocket") if err != nil { panic(fmt.Sprintf("failed to create Tendermint HTTP client: %s", err)) } @@ -56,7 +55,7 @@ func WaitForHeightTM(height int64, port string) { } func waitForHeightTM(height int64, url string) { - cl, err := tmclient.NewHTTP(url, "/websocket") + cl, err := rpchttp.New(url, "/websocket") if err != nil { panic(fmt.Sprintf("failed to create Tendermint HTTP client: %s", err)) } @@ -180,13 +179,10 @@ func WaitForStart(url string) { panic(err) } -// TODO: these functions just print to Stdout. -// consider using the logger. - // Wait for the RPC server to respond to /status func WaitForRPC(laddr string) { fmt.Println("LADDR", laddr) - client, err := rpcclient.NewJSONRPCClient(laddr) + client, err := tmjsonrpc.New(laddr) if err != nil { panic(fmt.Sprintf("failed to create Tendermint RPC client: %s", err)) } diff --git a/types/config.go b/types/config.go index 3e39fe30c272..a3181703df1e 100644 --- a/types/config.go +++ b/types/config.go @@ -1,6 +1,7 @@ package types import ( + "context" "sync" "github.com/cosmos/cosmos-sdk/version" @@ -19,19 +20,19 @@ type Config struct { mtx sync.RWMutex coinType uint32 sealed bool + sealedch chan struct{} } // cosmos-sdk wide global singleton -var sdkConfig *Config - -// GetConfig returns the config instance for the SDK. -func GetConfig() *Config { - if sdkConfig != nil { - return sdkConfig - } +var ( + sdkConfig *Config + initConfig sync.Once +) - sdkConfig = &Config{ - sealed: false, +// New returns a new Config with default values. +func NewConfig() *Config { + return &Config{ + sealedch: make(chan struct{}), bech32AddressPrefix: map[string]string{ "account_addr": Bech32PrefixAccAddr, "validator_addr": Bech32PrefixValAddr, @@ -44,9 +45,27 @@ func GetConfig() *Config { fullFundraiserPath: FullFundraiserPath, txEncoder: nil, } +} + +// GetConfig returns the config instance for the SDK. +func GetConfig() *Config { + initConfig.Do(func() { + sdkConfig = NewConfig() + }) return sdkConfig } +// GetSealedConfig returns the config instance for the SDK if/once it is sealed. +func GetSealedConfig(ctx context.Context) (*Config, error) { + config := GetConfig() + select { + case <-config.sealedch: + return config, nil + case <-ctx.Done(): + return nil, ctx.Err() + } +} + func (config *Config) assertNotSealed() { config.mtx.Lock() defer config.mtx.Unlock() @@ -108,9 +127,17 @@ func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) { // Seal seals the config such that the config state could not be modified further func (config *Config) Seal() *Config { config.mtx.Lock() - defer config.mtx.Unlock() + if config.sealed { + config.mtx.Unlock() + return config + } + + // signal sealed after state exposed/unlocked config.sealed = true + config.mtx.Unlock() + close(config.sealedch) + return config } diff --git a/types/config_test.go b/types/config_test.go new file mode 100644 index 000000000000..df208e32f361 --- /dev/null +++ b/types/config_test.go @@ -0,0 +1,50 @@ +package types_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestConfig_SetCoinType(t *testing.T) { + config := sdk.NewConfig() + config.SetCoinType(1) + require.Equal(t, uint32(1), config.GetCoinType()) + config.SetCoinType(99) + require.Equal(t, uint32(99), config.GetCoinType()) + + config.Seal() + require.Panics(t, func() { config.SetCoinType(99) }) +} + +func TestConfig_SetTxEncoder(t *testing.T) { + mockErr := errors.New("test") + config := sdk.NewConfig() + require.Nil(t, config.GetTxEncoder()) + encFunc := sdk.TxEncoder(func(tx sdk.Tx) ([]byte, error) { return nil, nil }) + config.SetTxEncoder(encFunc) + _, err := config.GetTxEncoder()(sdk.Tx(nil)) + require.Error(t, mockErr, err) + + config.Seal() + require.Panics(t, func() { config.SetTxEncoder(encFunc) }) +} + +func TestConfig_SetFullFundraiserPath(t *testing.T) { + config := sdk.NewConfig() + config.SetFullFundraiserPath("test/path") + require.Equal(t, "test/path", config.GetFullFundraiserPath()) + + config.SetFullFundraiserPath("test/poth") + require.Equal(t, "test/poth", config.GetFullFundraiserPath()) + + config.Seal() + require.Panics(t, func() { config.SetFullFundraiserPath("x/test/path") }) +} + +func TestKeyringServiceName(t *testing.T) { + require.Equal(t, sdk.DefaultKeyringServiceName, sdk.KeyringServiceName()) +} diff --git a/types/errors/abci.go b/types/errors/abci.go index 9015e6b36486..a14719a9ae60 100644 --- a/types/errors/abci.go +++ b/types/errors/abci.go @@ -1,7 +1,6 @@ package errors import ( - "errors" "fmt" "reflect" @@ -18,8 +17,6 @@ const ( // detailed error string. internalABCICodespace = UndefinedCodespace internalABCICode uint32 = 1 - internalABCILog string = "internal error" - // multiErrorABCICode uint32 = 1000 ) // ABCIInfo returns the ABCI error information as consumed by the tendermint @@ -160,10 +157,10 @@ func errIsNil(err error) bool { // originates. func Redact(err error) error { if ErrPanic.Is(err) { - return errors.New(internalABCILog) + return ErrPanic } if abciCode(err) == internalABCICode { - return errors.New(internalABCILog) + return errInternal } return err } diff --git a/types/errors/abci_test.go b/types/errors/abci_test.go index c098811ea474..1908e4f068e2 100644 --- a/types/errors/abci_test.go +++ b/types/errors/abci_test.go @@ -5,6 +5,8 @@ import ( "io" "strings" "testing" + + "github.com/stretchr/testify/require" ) func TestABCInfo(t *testing.T) { @@ -46,7 +48,7 @@ func TestABCInfo(t *testing.T) { "stdlib is generic message": { err: io.EOF, debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -60,7 +62,7 @@ func TestABCInfo(t *testing.T) { "wrapped stdlib is only a generic message": { err: Wrap(io.EOF, "cannot read file"), debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -134,7 +136,7 @@ func TestABCIInfoStacktrace(t *testing.T) { err: Wrap(fmt.Errorf("stdlib"), "wrapped"), debug: false, wantStacktrace: false, - wantErrMsg: "internal error", + wantErrMsg: "internal", }, } @@ -169,21 +171,42 @@ func TestABCIInfoHidesStacktrace(t *testing.T) { } func TestRedact(t *testing.T) { - if err := Redact(ErrPanic); ErrPanic.Is(err) { - t.Error("reduct must not pass through panic error") - } - if err := Redact(ErrUnauthorized); !ErrUnauthorized.Is(err) { - t.Error("reduct should pass through SDK error") - } - - var cerr customErr - if err := Redact(cerr); err != cerr { - t.Error("reduct should pass through ABCI code error") + cases := map[string]struct { + err error + untouched bool // if true we expect the same error after redact + changed error // if untouched == false, expect this error + }{ + "panic looses message": { + err: Wrap(ErrPanic, "some secret stack trace"), + changed: ErrPanic, + }, + "sdk errors untouched": { + err: Wrap(ErrUnauthorized, "cannot drop db"), + untouched: true, + }, + "pass though custom errors with ABCI code": { + err: customErr{}, + untouched: true, + }, + "redact stdlib error": { + err: fmt.Errorf("stdlib error"), + changed: errInternal, + }, } - serr := fmt.Errorf("stdlib error") - if err := Redact(serr); err == serr { - t.Error("reduct must not pass through a stdlib error") + for name, tc := range cases { + spec := tc + t.Run(name, func(t *testing.T) { + redacted := Redact(spec.err) + if spec.untouched { + require.Equal(t, spec.err, redacted) + } else { + // see if we got the expected redact + require.Equal(t, spec.changed, redacted) + // make sure the ABCI code did not change + require.Equal(t, abciCode(spec.err), abciCode(redacted)) + } + }) } } @@ -215,41 +238,15 @@ func TestABCIInfoSerializeErr(t *testing.T) { debug: true, exp: fmt.Sprintf("%+v", myErrDecode), }, - // "multi error default encoder": { - // src: Append(myErrMsg, myErrAddr), - // exp: Append(myErrMsg, myErrAddr).Error(), - // }, - // "multi error default with internal": { - // src: Append(myErrMsg, myPanic), - // exp: "internal error", - // }, "redact in default encoder": { src: myPanic, - exp: "internal error", + exp: "panic", }, "do not redact in debug encoder": { src: myPanic, debug: true, exp: fmt.Sprintf("%+v", myPanic), }, - // "redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: false, - // exp: "internal error", - // }, - // "no redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: true, - // exp: `2 errors occurred: - // * panic - // * test: invalid message - // `, - // }, - // "wrapped multi error with redact": { - // src: Wrap(Append(myPanic, myErrMsg), "wrap"), - // debug: false, - // exp: "internal error", - // }, } for msg, spec := range specs { spec := spec diff --git a/types/handler.go b/types/handler.go index 6815230a861f..03d1f02d5806 100644 --- a/types/handler.go +++ b/types/handler.go @@ -25,15 +25,15 @@ type AnteDecorator interface { // MUST set GasMeter with the FIRST AnteDecorator. Failing to do so will cause // transactions to be processed with an infinite gasmeter and open a DOS attack vector. // Use `ante.SetUpContextDecorator` or a custom Decorator with similar functionality. +// Returns nil when no AnteDecorator are supplied. func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { - if (chain[len(chain)-1] != Terminator{}) { - chain = append(chain, Terminator{}) + if len(chain) == 0 { + return nil } - if len(chain) == 1 { - return func(ctx Context, tx Tx, simulate bool) (Context, error) { - return chain[0].AnteHandle(ctx, tx, simulate, nil) - } + // handle non-terminated decorators chain + if (chain[len(chain)-1] != Terminator{}) { + chain = append(chain, Terminator{}) } return func(ctx Context, tx Tx, simulate bool) (Context, error) { diff --git a/types/handler_test.go b/types/handler_test.go new file mode 100644 index 000000000000..4847a113137a --- /dev/null +++ b/types/handler_test.go @@ -0,0 +1,28 @@ +package types_test + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/tests/mocks" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestChainAnteDecorators(t *testing.T) { + t.Parallel() + // test panic + require.Nil(t, sdk.ChainAnteDecorators([]sdk.AnteDecorator{}...)) + + ctx, tx := sdk.Context{}, sdk.Tx(nil) + mockCtrl := gomock.NewController(t) + mockAnteDecorator1 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1)(ctx, tx, true) + + mockAnteDecorator2 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, mockAnteDecorator2).Times(1) + mockAnteDecorator2.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, nil).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1, mockAnteDecorator2) +} diff --git a/types/int.go b/types/int.go index caabdb11318c..0029dfaa4eb4 100644 --- a/types/int.go +++ b/types/int.go @@ -53,7 +53,7 @@ func max(i *big.Int, i2 *big.Int) *big.Int { } // MarshalAmino for custom encoding scheme -func marshalAmino(i *big.Int) (string, error) { +func marshalAmino(i *big.Int) (string, error) { // nolint:interfacer bz, err := i.MarshalText() return string(bz), err } @@ -77,7 +77,7 @@ func unmarshalAmino(i *big.Int, text string) (err error) { // MarshalJSON for custom encoding scheme // Must be encoded as a string for JSON precision -func marshalJSON(i *big.Int) ([]byte, error) { +func marshalJSON(i *big.Int) ([]byte, error) { // nolint:interfacer text, err := i.MarshalText() if err != nil { return nil, err diff --git a/types/module/simulation.go b/types/module/simulation.go index fab432c8a57e..d639f4c09621 100644 --- a/types/module/simulation.go +++ b/types/module/simulation.go @@ -50,7 +50,7 @@ func NewSimulationManager(modules ...AppModuleSimulation) *SimulationManager { // GetProposalContents returns each module's proposal content generator function // with their default operation weight and key. func (sm *SimulationManager) GetProposalContents(simState SimulationState) []simulation.WeightedProposalContent { - var wContents []simulation.WeightedProposalContent + var wContents []simulation.WeightedProposalContent //nolint:prealloc for _, module := range sm.Modules { wContents = append(wContents, module.ProposalContents(simState)...) } @@ -87,7 +87,7 @@ func (sm *SimulationManager) GenerateParamChanges(seed int64) (paramChanges []si // WeightedOperations returns all the modules' weighted operations of an application func (sm *SimulationManager) WeightedOperations(simState SimulationState) []simulation.WeightedOperation { - var wOps []simulation.WeightedOperation + var wOps []simulation.WeightedOperation //nolint:prealloc for _, module := range sm.Modules { wOps = append(wOps, module.WeightedOperations(simState)...) } diff --git a/types/rest/rest.go b/types/rest/rest.go index 65c1bc094e8a..4a0fe4e9f04d 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -20,8 +20,8 @@ import ( ) const ( - DefaultPage = 1 - DefaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 + DefaultPage = 1 + DefaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19 TxMinHeightKey = "tx.minheight" // Inclusive minimum height filter TxMaxHeightKey = "tx.maxheight" // Inclusive maximum height filter ) @@ -337,13 +337,14 @@ func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, p } var tag string - if key == types.TxHeightKey { + switch key { + case types.TxHeightKey: tag = fmt.Sprintf("%s=%s", key, value) - } else if key == TxMinHeightKey { + case TxMinHeightKey: tag = fmt.Sprintf("%s>=%s", types.TxHeightKey, value) - } else if key == TxMaxHeightKey { + case TxMaxHeightKey: tag = fmt.Sprintf("%s<=%s", types.TxHeightKey, value) - } else { + default: tag = fmt.Sprintf("%s='%s'", key, value) } tags = append(tags, tag) diff --git a/types/result.go b/types/result.go index 9530be01d25c..54cc0e05330b 100644 --- a/types/result.go +++ b/types/result.go @@ -17,7 +17,7 @@ type GasInfo struct { // GasWanted is the maximum units of work we allow this tx to perform. GasWanted uint64 - // GasUsed is the amount of gas actually consumed. NOTE: unimplemented + // GasUsed is the amount of gas actually consumed. GasUsed uint64 } @@ -35,6 +35,13 @@ type Result struct { Events Events } +// SimulationResponse defines the response generated when a transaction is successfully +// simulated by the Baseapp. +type SimulationResponse struct { + GasInfo + Result *Result +} + // ABCIMessageLogs represents a slice of ABCIMessageLog. type ABCIMessageLogs []ABCIMessageLog diff --git a/types/utils.go b/types/utils.go index 2a6add6b45b4..cfef9fb3df1b 100644 --- a/types/utils.go +++ b/types/utils.go @@ -12,8 +12,15 @@ import ( var ( // This is set at compile time. Could be cleveldb, defaults is goleveldb. DBBackend = "" + backend = dbm.GoLevelDBBackend ) +func init() { + if len(DBBackend) != 0 { + backend = dbm.BackendType(DBBackend) + } +} + // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. // This method can be used to canonicalize JSON to be returned by GetSignBytes, @@ -69,10 +76,6 @@ func ParseTimeBytes(bz []byte) (time.Time, error) { // NewLevelDB instantiate a new LevelDB instance according to DBBackend. func NewLevelDB(name, dir string) (db dbm.DB, err error) { - backend := dbm.GoLevelDBBackend - if DBBackend == string(dbm.CLevelDBBackend) { - backend = dbm.CLevelDBBackend - } defer func() { if r := recover(); r != nil { err = fmt.Errorf("couldn't create db: %v", r) diff --git a/x/auth/ante/basic.go b/x/auth/ante/basic.go index 0e999a9d4781..57a41ec874e4 100644 --- a/x/auth/ante/basic.go +++ b/x/auth/ante/basic.go @@ -2,7 +2,6 @@ package ante import ( sdk "github.com/cosmos/cosmos-sdk/types" - err "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/tendermint/tendermint/crypto" @@ -67,7 +66,7 @@ func (vmd ValidateMemoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate memoLength := len(memoTx.GetMemo()) if uint64(memoLength) > params.MaxMemoCharacters { - return ctx, err.Wrapf(err.ErrMemoTooLarge, + return ctx, sdkerrors.Wrapf(sdkerrors.ErrMemoTooLarge, "maximum number of characters is %d but received %d characters", params.MaxMemoCharacters, memoLength, ) diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go index 4498b432d73d..eeeeee0ca3b0 100644 --- a/x/auth/ante/sigverify.go +++ b/x/auth/ante/sigverify.go @@ -218,13 +218,13 @@ func (svd SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simul // IncrementSequenceDecorator handles incrementing sequences of all signers. // Use the IncrementSequenceDecorator decorator to prevent replay attacks. Note, -// there is no need to execute IncrementSequenceDecorator on CheckTx or RecheckTX -// since it is merely updating the nonce. As a result, this has the side effect -// that subsequent and sequential txs orginating from the same account cannot be -// handled correctly in a reliable way. To send sequential txs orginating from the -// same account, it is recommended to instead use multiple messages in a tx. +// there is no need to execute IncrementSequenceDecorator on RecheckTX since +// CheckTx would already bump the sequence number. // -// CONTRACT: The tx must implement the SigVerifiableTx interface. +// NOTE: Since CheckTx and DeliverTx state are managed separately, subsequent and +// sequential txs orginating from the same account cannot be handled correctly in +// a reliable way unless sequence numbers are managed and tracked manually by a +// client. It is recommended to instead use multiple messages in a tx. type IncrementSequenceDecorator struct { ak keeper.AccountKeeper } @@ -236,11 +236,6 @@ func NewIncrementSequenceDecorator(ak keeper.AccountKeeper) IncrementSequenceDec } func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - // no need to increment sequence on CheckTx or RecheckTx - if ctx.IsCheckTx() && !simulate { - return next(ctx, tx, simulate) - } - sigTx, ok := tx.(SigVerifiableTx) if !ok { return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type") @@ -252,6 +247,7 @@ func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, sim if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { panic(err) } + isd.ak.SetAccount(ctx, acc) } diff --git a/x/auth/ante/sigverify_test.go b/x/auth/ante/sigverify_test.go index 58bcef1e2811..522f90ddbafc 100644 --- a/x/auth/ante/sigverify_test.go +++ b/x/auth/ante/sigverify_test.go @@ -211,3 +211,40 @@ func runSigDecorators(t *testing.T, params types.Params, multisig bool, privs .. return after - before, err } + +func TestIncrementSequenceDecorator(t *testing.T) { + app, ctx := createTestApp(true) + + priv, _, addr := types.KeyTestPubAddr() + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + require.NoError(t, acc.SetAccountNumber(uint64(50))) + app.AccountKeeper.SetAccount(ctx, acc) + + msgs := []sdk.Msg{types.NewTestMsg(addr)} + privKeys := []crypto.PrivKey{priv} + accNums := []uint64{app.AccountKeeper.GetAccount(ctx, addr).GetAccountNumber()} + accSeqs := []uint64{app.AccountKeeper.GetAccount(ctx, addr).GetSequence()} + fee := types.NewTestStdFee() + tx := types.NewTestTx(ctx, msgs, privKeys, accNums, accSeqs, fee) + + isd := ante.NewIncrementSequenceDecorator(app.AccountKeeper) + antehandler := sdk.ChainAnteDecorators(isd) + + testCases := []struct { + ctx sdk.Context + simulate bool + expectedSeq uint64 + }{ + {ctx.WithIsReCheckTx(true), false, 1}, + {ctx.WithIsCheckTx(true).WithIsReCheckTx(false), false, 2}, + {ctx.WithIsReCheckTx(true), false, 3}, + {ctx.WithIsReCheckTx(true), false, 4}, + {ctx.WithIsReCheckTx(true), true, 5}, + } + + for i, tc := range testCases { + _, err := antehandler(tc.ctx, tx, tc.simulate) + require.NoError(t, err, "unexpected error; tc #%d, %v", i, tc) + require.Equal(t, tc.expectedSeq, app.AccountKeeper.GetAccount(ctx, addr).GetSequence()) + } +} diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go index 55b7007002d9..38189bc4426d 100644 --- a/x/auth/client/utils/tx.go +++ b/x/auth/client/utils/tx.go @@ -120,26 +120,26 @@ func EnrichWithGas(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs [ } // CalculateGas simulates the execution of a transaction and returns -// both the estimate obtained by the query and the adjusted amount. +// the simulation response obtained by the query and the adjusted gas amount. func CalculateGas( queryFunc func(string, []byte) ([]byte, int64, error), cdc *codec.Codec, txBytes []byte, adjustment float64, -) (estimate, adjusted uint64, err error) { +) (sdk.SimulationResponse, uint64, error) { // run a simulation (via /app/simulate query) to // estimate gas and update TxBuilder accordingly rawRes, _, err := queryFunc("/app/simulate", txBytes) if err != nil { - return estimate, adjusted, err + return sdk.SimulationResponse{}, 0, err } - estimate, err = parseQueryResponse(cdc, rawRes) + simRes, err := parseQueryResponse(cdc, rawRes) if err != nil { - return + return sdk.SimulationResponse{}, 0, err } - adjusted = adjustGasEstimate(estimate, adjustment) - return estimate, adjusted, nil + adjusted := adjustGasEstimate(simRes.GasUsed, adjustment) + return simRes, adjusted, nil } // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. @@ -261,29 +261,28 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) { return encoder } -// nolint -// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { +// simulateMsgs simulates the transaction and returns the simulation response and +// the adjusted gas value. +func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (sdk.SimulationResponse, uint64, error) { txBytes, err := txBldr.BuildTxForSim(msgs) if err != nil { - return + return sdk.SimulationResponse{}, 0, err } - estimated, adjusted, err = CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment()) - return + return CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment()) } func adjustGasEstimate(estimate uint64, adjustment float64) uint64 { return uint64(adjustment * float64(estimate)) } -func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (uint64, error) { - var gasUsed uint64 - if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &gasUsed); err != nil { - return 0, err +func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (sdk.SimulationResponse, error) { + var simRes sdk.SimulationResponse + if err := cdc.UnmarshalBinaryBare(rawRes, &simRes); err != nil { + return sdk.SimulationResponse{}, err } - return gasUsed, nil + return simRes, nil } // PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx. diff --git a/x/auth/client/utils/tx_test.go b/x/auth/client/utils/tx_test.go index a9c33b232053..7f78664bb682 100644 --- a/x/auth/client/utils/tx_test.go +++ b/x/auth/client/utils/tx_test.go @@ -7,8 +7,8 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/cosmos/cosmos-sdk/codec" @@ -23,13 +23,19 @@ var ( func TestParseQueryResponse(t *testing.T) { cdc := makeCodec() - sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(uint64(10)) - gas, err := parseQueryResponse(cdc, sdkResBytes) - assert.Equal(t, gas, uint64(10)) - assert.Nil(t, err) - gas, err = parseQueryResponse(cdc, []byte("fuzzy")) - assert.Equal(t, gas, uint64(0)) - assert.Error(t, err) + simRes := sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: 10, GasWanted: 20}, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, + } + + bz := cdc.MustMarshalBinaryBare(simRes) + res, err := parseQueryResponse(cdc, bz) + require.NoError(t, err) + require.Equal(t, 10, int(res.GasInfo.GasUsed)) + require.NotNil(t, res.Result) + + res, err = parseQueryResponse(cdc, []byte("fuzzy")) + require.Error(t, err) } func TestCalculateGas(t *testing.T) { @@ -37,9 +43,14 @@ func TestCalculateGas(t *testing.T) { makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) { return func(string, []byte) ([]byte, int64, error) { if wantErr { - return nil, 0, errors.New("") + return nil, 0, errors.New("query failed") + } + simRes := sdk.SimulationResponse{ + GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed}, + Result: &sdk.Result{Data: []byte("tx data"), Log: "log"}, } - return cdc.MustMarshalBinaryLengthPrefixed(gasUsed), 0, nil + + return cdc.MustMarshalBinaryBare(simRes), 0, nil } } @@ -54,20 +65,26 @@ func TestCalculateGas(t *testing.T) { args args wantEstimate uint64 wantAdjusted uint64 - wantErr bool + expPass bool }{ - {"error", args{0, true, 1.2}, 0, 0, true}, - {"adjusted gas", args{10, false, 1.2}, 10, 12, false}, + {"error", args{0, true, 1.2}, 0, 0, false}, + {"adjusted gas", args{10, false, 1.2}, 10, 12, true}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { queryFunc := makeQueryFunc(tt.args.queryFuncGasUsed, tt.args.queryFuncWantErr) - gotEstimate, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) - assert.Equal(t, err != nil, tt.wantErr) - assert.Equal(t, gotEstimate, tt.wantEstimate) - assert.Equal(t, gotAdjusted, tt.wantAdjusted) + simRes, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment) + if tt.expPass { + require.NoError(t, err) + require.Equal(t, simRes.GasInfo.GasUsed, tt.wantEstimate) + require.Equal(t, gotAdjusted, tt.wantAdjusted) + require.NotNil(t, simRes.Result) + } else { + require.Error(t, err) + require.Nil(t, simRes.Result) + } }) } } diff --git a/x/auth/legacy/v0_38/migrate.go b/x/auth/legacy/v0_38/migrate.go index e76937777678..f4a32b4454c3 100644 --- a/x/auth/legacy/v0_38/migrate.go +++ b/x/auth/legacy/v0_38/migrate.go @@ -42,9 +42,9 @@ func Migrate(authGenState v036auth.GenesisState, genAccountsGenState v036genacco accounts[i] = genAccount } - accounts = sanitizeGenesisAccounts(accounts) + accounts = SanitizeGenesisAccounts(accounts) - if err := validateGenAccounts(accounts); err != nil { + if err := ValidateGenAccounts(accounts); err != nil { panic(err) } diff --git a/x/auth/legacy/v0_38/types.go b/x/auth/legacy/v0_38/types.go index 15896a39121f..67b515182fc4 100644 --- a/x/auth/legacy/v0_38/types.go +++ b/x/auth/legacy/v0_38/types.go @@ -46,7 +46,7 @@ type ( BaseAccount struct { Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` @@ -54,7 +54,7 @@ type ( baseAccountPretty struct { Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` PubKey string `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` @@ -72,7 +72,7 @@ type ( vestingAccountPretty struct { Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` PubKey string `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` @@ -104,7 +104,7 @@ type ( moduleAccountPretty struct { Address sdk.AccAddress `json:"address" yaml:"address"` - Coins sdk.Coins `json:"coins" yaml:"coins"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` PubKey string `json:"public_key" yaml:"public_key"` AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` @@ -434,7 +434,7 @@ func NewModuleAccount(baseAccount *BaseAccount, name string, permissions ...stri } func (ma ModuleAccount) Validate() error { - if err := validatePermissions(ma.Permissions...); err != nil { + if err := ValidatePermissions(ma.Permissions...); err != nil { return err } @@ -476,7 +476,7 @@ func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { return nil } -func validatePermissions(permissions ...string) error { +func ValidatePermissions(permissions ...string) error { for _, perm := range permissions { if strings.TrimSpace(perm) == "" { return fmt.Errorf("module permission is empty") @@ -486,7 +486,7 @@ func validatePermissions(permissions ...string) error { return nil } -func sanitizeGenesisAccounts(genAccounts GenesisAccounts) GenesisAccounts { +func SanitizeGenesisAccounts(genAccounts GenesisAccounts) GenesisAccounts { sort.Slice(genAccounts, func(i, j int) bool { return genAccounts[i].GetAccountNumber() < genAccounts[j].GetAccountNumber() }) @@ -500,7 +500,7 @@ func sanitizeGenesisAccounts(genAccounts GenesisAccounts) GenesisAccounts { return genAccounts } -func validateGenAccounts(genAccounts GenesisAccounts) error { +func ValidateGenAccounts(genAccounts GenesisAccounts) error { addrMap := make(map[string]bool, len(genAccounts)) for _, acc := range genAccounts { diff --git a/x/auth/legacy/v0_39/migrate.go b/x/auth/legacy/v0_39/migrate.go new file mode 100644 index 000000000000..410283dc93fd --- /dev/null +++ b/x/auth/legacy/v0_39/migrate.go @@ -0,0 +1,60 @@ +package v039 + +import ( + "fmt" + + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38" +) + +// Migrate accepts exported genesis state from v0.38 and migrates it to v0.39 +// genesis state. +func Migrate(oldAuthGenState v038auth.GenesisState) GenesisState { + accounts := make(v038auth.GenesisAccounts, len(oldAuthGenState.Accounts)) + + for i, acc := range oldAuthGenState.Accounts { + switch t := acc.(type) { + case *v038auth.BaseAccount: + accounts[i] = NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence) + + case *v038auth.BaseVestingAccount: + accounts[i] = NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ) + + case *v038auth.ContinuousVestingAccount: + accounts[i] = NewContinuousVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ), + t.StartTime, + ) + + case *v038auth.DelayedVestingAccount: + accounts[i] = NewDelayedVestingAccountRaw( + NewBaseVestingAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.OriginalVesting, t.DelegatedFree, t.DelegatedVesting, t.EndTime, + ), + ) + + case *v038auth.ModuleAccount: + accounts[i] = NewModuleAccount( + NewBaseAccount(t.Address, t.Coins, t.PubKey, t.AccountNumber, t.Sequence), + t.Name, t.Permissions..., + ) + + default: + panic(fmt.Sprintf("unexpected account type: %T", acc)) + } + } + + accounts = v038auth.SanitizeGenesisAccounts(accounts) + + if err := v038auth.ValidateGenAccounts(accounts); err != nil { + panic(err) + } + + return NewGenesisState(oldAuthGenState.Params, accounts) +} diff --git a/x/auth/legacy/v0_39/types.go b/x/auth/legacy/v0_39/types.go new file mode 100644 index 000000000000..dbf9b3b8870d --- /dev/null +++ b/x/auth/legacy/v0_39/types.go @@ -0,0 +1,426 @@ +package v039 + +// DONTCOVER +// nolint + +import ( + "bytes" + "errors" + "fmt" + "strings" + + "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + v034auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_34" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38" +) + +const ( + ModuleName = "auth" +) + +type ( + GenesisState struct { + Params v034auth.Params `json:"params" yaml:"params"` + Accounts v038auth.GenesisAccounts `json:"accounts" yaml:"accounts"` + } + + BaseAccount struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins,omitempty"` + PubKey crypto.PubKey `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + } + + BaseVestingAccount struct { + *BaseAccount + + OriginalVesting sdk.Coins `json:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting"` + + EndTime int64 `json:"end_time"` + } + + vestingAccountJSON struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" 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"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + // custom fields based on concrete vesting type which can be omitted + StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` + VestingPeriods Periods `json:"vesting_periods,omitempty" yaml:"vesting_periods,omitempty"` + } + + ContinuousVestingAccount struct { + *BaseVestingAccount + + StartTime int64 `json:"start_time"` + } + + DelayedVestingAccount struct { + *BaseVestingAccount + } + + Period struct { + Length int64 `json:"length" yaml:"length"` // length of the period, in seconds + Amount sdk.Coins `json:"amount" yaml:"amount"` // amount of coins vesting during this period + } + + Periods []Period + + PeriodicVestingAccount struct { + *BaseVestingAccount + StartTime int64 `json:"start_time" yaml:"start_time"` // when the coins start to vest + VestingPeriods Periods `json:"vesting_periods" yaml:"vesting_periods"` // the vesting schedule + } + + ModuleAccount struct { + *BaseAccount + + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } + + moduleAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins,omitempty" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + Name string `json:"name" yaml:"name"` + Permissions []string `json:"permissions" yaml:"permissions"` + } +) + +func NewGenesisState(params v034auth.Params, accounts v038auth.GenesisAccounts) GenesisState { + return GenesisState{ + Params: params, + Accounts: accounts, + } +} + +func NewBaseAccountWithAddress(addr sdk.AccAddress) BaseAccount { + return BaseAccount{ + Address: addr, + } +} + +func NewBaseAccount( + address sdk.AccAddress, coins sdk.Coins, pk crypto.PubKey, accountNumber, sequence uint64, +) *BaseAccount { + + return &BaseAccount{ + Address: address, + Coins: coins, + PubKey: pk, + AccountNumber: accountNumber, + Sequence: sequence, + } +} + +func (acc BaseAccount) GetAddress() sdk.AccAddress { + return acc.Address +} + +func (acc *BaseAccount) GetAccountNumber() uint64 { + return acc.AccountNumber +} + +func (acc *BaseAccount) GetCoins() sdk.Coins { + return acc.Coins +} + +func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { + acc.Coins = coins + return nil +} + +func (acc BaseAccount) Validate() error { + if acc.PubKey != nil && acc.Address != nil && + !bytes.Equal(acc.PubKey.Address().Bytes(), acc.Address.Bytes()) { + return errors.New("pubkey and address pair is invalid") + } + + return nil +} + +func NewBaseVestingAccount( + baseAccount *BaseAccount, originalVesting, delegatedFree, delegatedVesting sdk.Coins, endTime int64, +) *BaseVestingAccount { + + return &BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: originalVesting, + DelegatedFree: delegatedFree, + DelegatedVesting: delegatedVesting, + EndTime: endTime, + } +} + +func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: bva.Address, + Coins: bva.Coins, + PubKey: bva.PubKey, + AccountNumber: bva.AccountNumber, + Sequence: bva.Sequence, + OriginalVesting: bva.OriginalVesting, + DelegatedFree: bva.DelegatedFree, + DelegatedVesting: bva.DelegatedVesting, + EndTime: bva.EndTime, + } + + return codec.Cdc.MarshalJSON(alias) +} + +func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + bva.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence) + bva.OriginalVesting = alias.OriginalVesting + bva.DelegatedFree = alias.DelegatedFree + bva.DelegatedVesting = alias.DelegatedVesting + bva.EndTime = alias.EndTime + + return nil +} + +func (bva BaseVestingAccount) GetEndTime() int64 { + return bva.EndTime +} + +func (bva BaseVestingAccount) Validate() error { + + return bva.BaseAccount.Validate() +} + +func NewContinuousVestingAccountRaw(bva *BaseVestingAccount, startTime int64) *ContinuousVestingAccount { + return &ContinuousVestingAccount{ + BaseVestingAccount: bva, + StartTime: startTime, + } +} + +func (cva ContinuousVestingAccount) Validate() error { + if cva.StartTime >= cva.EndTime { + return errors.New("vesting start-time cannot be before end-time") + } + + return cva.BaseVestingAccount.Validate() +} + +func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: cva.Address, + Coins: cva.Coins, + PubKey: cva.PubKey, + AccountNumber: cva.AccountNumber, + Sequence: cva.Sequence, + OriginalVesting: cva.OriginalVesting, + DelegatedFree: cva.DelegatedFree, + DelegatedVesting: cva.DelegatedVesting, + EndTime: cva.EndTime, + StartTime: cva.StartTime, + } + + return codec.Cdc.MarshalJSON(alias) +} + +func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + cva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + cva.StartTime = alias.StartTime + + return nil +} + +func NewDelayedVestingAccountRaw(bva *BaseVestingAccount) *DelayedVestingAccount { + return &DelayedVestingAccount{ + BaseVestingAccount: bva, + } +} + +func (dva DelayedVestingAccount) Validate() error { + return dva.BaseVestingAccount.Validate() +} + +func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: dva.Address, + Coins: dva.Coins, + PubKey: dva.PubKey, + AccountNumber: dva.AccountNumber, + Sequence: dva.Sequence, + OriginalVesting: dva.OriginalVesting, + DelegatedFree: dva.DelegatedFree, + DelegatedVesting: dva.DelegatedVesting, + EndTime: dva.EndTime, + } + + return codec.Cdc.MarshalJSON(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. +func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + dva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + + return nil +} + +func (pva PeriodicVestingAccount) GetStartTime() int64 { + return pva.StartTime +} + +func (pva PeriodicVestingAccount) Validate() error { + if pva.GetStartTime() >= pva.GetEndTime() { + return errors.New("vesting start-time cannot be before end-time") + } + endTime := pva.StartTime + originalVesting := sdk.NewCoins() + for _, p := range pva.VestingPeriods { + endTime += p.Length + originalVesting = originalVesting.Add(p.Amount...) + } + if endTime != pva.EndTime { + return errors.New("vesting end time does not match length of all vesting periods") + } + if !originalVesting.IsEqual(pva.OriginalVesting) { + return errors.New("original vesting coins does not match the sum of all coins in vesting periods") + } + + return pva.BaseVestingAccount.Validate() +} + +func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { + alias := vestingAccountJSON{ + Address: pva.Address, + Coins: pva.Coins, + PubKey: pva.PubKey, + AccountNumber: pva.AccountNumber, + Sequence: pva.Sequence, + OriginalVesting: pva.OriginalVesting, + DelegatedFree: pva.DelegatedFree, + DelegatedVesting: pva.DelegatedVesting, + EndTime: pva.EndTime, + StartTime: pva.StartTime, + VestingPeriods: pva.VestingPeriods, + } + + return codec.Cdc.MarshalJSON(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. +func (pva *PeriodicVestingAccount) UnmarshalJSON(bz []byte) error { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + pva.BaseVestingAccount = &BaseVestingAccount{ + BaseAccount: NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + pva.StartTime = alias.StartTime + pva.VestingPeriods = alias.VestingPeriods + + return nil +} + +func NewModuleAccount(baseAccount *BaseAccount, name string, permissions ...string) *ModuleAccount { + return &ModuleAccount{ + BaseAccount: baseAccount, + Name: name, + Permissions: permissions, + } +} + +func (ma ModuleAccount) Validate() error { + if err := v038auth.ValidatePermissions(ma.Permissions...); err != nil { + return err + } + + if strings.TrimSpace(ma.Name) == "" { + return errors.New("module account name cannot be blank") + } + + if x := sdk.AccAddress(crypto.AddressHash([]byte(ma.Name))); !ma.Address.Equals(x) { + return fmt.Errorf("address %s cannot be derived from the module name '%s'; expected: %s", ma.Address, ma.Name, x) + } + + return ma.BaseAccount.Validate() +} + +// MarshalJSON returns the JSON representation of a ModuleAccount. +func (ma ModuleAccount) MarshalJSON() ([]byte, error) { + return codec.Cdc.MarshalJSON(moduleAccountPretty{ + Address: ma.Address, + Coins: ma.Coins, + PubKey: "", + AccountNumber: ma.AccountNumber, + Sequence: ma.Sequence, + Name: ma.Name, + Permissions: ma.Permissions, + }) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. +func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { + var alias moduleAccountPretty + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { + return err + } + + ma.BaseAccount = NewBaseAccount(alias.Address, alias.Coins, nil, alias.AccountNumber, alias.Sequence) + ma.Name = alias.Name + ma.Permissions = alias.Permissions + + return nil +} + +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterInterface((*v038auth.GenesisAccount)(nil), nil) + cdc.RegisterInterface((*v038auth.Account)(nil), nil) + cdc.RegisterConcrete(&BaseAccount{}, "cosmos-sdk/Account", nil) + cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil) + cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil) + cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil) + cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil) + cdc.RegisterConcrete(&ModuleAccount{}, "cosmos-sdk/ModuleAccount", nil) +} diff --git a/x/auth/types/account.go b/x/auth/types/account.go index a8d919a385ee..4aa5753117fe 100644 --- a/x/auth/types/account.go +++ b/x/auth/types/account.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "encoding/json" "errors" "time" @@ -168,48 +167,3 @@ func (acc BaseAccount) MarshalYAML() (interface{}, error) { return string(bz), err } - -// MarshalJSON returns the JSON representation of a BaseAccount. -func (acc BaseAccount) MarshalJSON() ([]byte, error) { - alias := baseAccountPretty{ - Address: acc.Address, - Coins: acc.Coins, - AccountNumber: acc.AccountNumber, - Sequence: acc.Sequence, - } - - if acc.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) -} - -// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount. -func (acc *BaseAccount) UnmarshalJSON(bz []byte) error { - var alias baseAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { - return err - } - - if alias.PubKey != "" { - pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - - acc.PubKey = pk - } - - acc.Address = alias.Address - acc.Coins = alias.Coins - acc.AccountNumber = alias.AccountNumber - acc.Sequence = alias.Sequence - - return nil -} diff --git a/x/auth/types/account_test.go b/x/auth/types/account_test.go index 7be6a25e5623..14236172a851 100644 --- a/x/auth/types/account_test.go +++ b/x/auth/types/account_test.go @@ -1,7 +1,6 @@ package types import ( - "encoding/json" "errors" "testing" @@ -129,28 +128,3 @@ func TestGenesisAccountValidate(t *testing.T) { }) } } - -func TestBaseAccountJSON(t *testing.T) { - pubkey := secp256k1.GenPrivKey().PubKey() - addr := sdk.AccAddress(pubkey.Address()) - coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5)) - baseAcc := NewBaseAccount(addr, coins, pubkey, 10, 50) - - bz, err := json.Marshal(baseAcc) - require.NoError(t, err) - - bz1, err := baseAcc.MarshalJSON() - require.NoError(t, err) - require.Equal(t, string(bz1), string(bz)) - - var a BaseAccount - require.NoError(t, json.Unmarshal(bz, &a)) - require.Equal(t, baseAcc.String(), a.String()) - - bz, err = ModuleCdc.MarshalJSON(baseAcc) - require.NoError(t, err) - - var b BaseAccount - require.NoError(t, ModuleCdc.UnmarshalJSON(bz, &b)) - require.Equal(t, baseAcc.String(), b.String()) -} diff --git a/x/auth/vesting/types/vesting_account.go b/x/auth/vesting/types/vesting_account.go index 256c9d598fec..7964ca2a4544 100644 --- a/x/auth/vesting/types/vesting_account.go +++ b/x/auth/vesting/types/vesting_account.go @@ -1,17 +1,17 @@ package types import ( - "encoding/json" "errors" "time" + "github.com/tendermint/tendermint/crypto" + "gopkg.in/yaml.v2" + + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" - - "github.com/tendermint/tendermint/crypto" - "gopkg.in/yaml.v2" ) // Compile-time type assertions @@ -185,7 +185,7 @@ func (bva BaseVestingAccount) Validate() error { return bva.BaseAccount.Validate() } -type vestingAccountPretty struct { +type vestingAccountYAML struct { Address sdk.AccAddress `json:"address" yaml:"address"` Coins sdk.Coins `json:"coins" yaml:"coins"` PubKey string `json:"public_key" yaml:"public_key"` @@ -201,6 +201,22 @@ type vestingAccountPretty struct { VestingPeriods Periods `json:"vesting_periods,omitempty" yaml:"vesting_periods,omitempty"` } +type vestingAccountJSON 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"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + + // custom fields based on concrete vesting type which can be omitted + StartTime int64 `json:"start_time,omitempty" yaml:"start_time,omitempty"` + VestingPeriods Periods `json:"vesting_periods,omitempty" yaml:"vesting_periods,omitempty"` +} + func (bva BaseVestingAccount) String() string { out, _ := bva.MarshalYAML() return out.(string) @@ -208,7 +224,7 @@ func (bva BaseVestingAccount) String() string { // MarshalYAML returns the YAML representation of a BaseVestingAccount. func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ + alias := vestingAccountYAML{ Address: bva.Address, Coins: bva.Coins, AccountNumber: bva.AccountNumber, @@ -219,8 +235,9 @@ func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { EndTime: bva.EndTime, } - if bva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) + pk := bva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -238,9 +255,10 @@ func (bva BaseVestingAccount) MarshalYAML() (interface{}, error) { // MarshalJSON returns the JSON representation of a BaseVestingAccount. func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ + alias := vestingAccountJSON{ Address: bva.Address, Coins: bva.Coins, + PubKey: bva.GetPubKey(), AccountNumber: bva.AccountNumber, Sequence: bva.Sequence, OriginalVesting: bva.OriginalVesting, @@ -249,38 +267,17 @@ func (bva BaseVestingAccount) MarshalJSON() ([]byte, error) { EndTime: bva.EndTime, } - if bva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, bva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) + return codec.Cdc.MarshalJSON(alias) } // UnmarshalJSON unmarshals raw JSON bytes into a BaseVestingAccount. func (bva *BaseVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { return err } - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - - bva.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) + bva.BaseAccount = authtypes.NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence) bva.OriginalVesting = alias.OriginalVesting bva.DelegatedFree = alias.DelegatedFree bva.DelegatedVesting = alias.DelegatedVesting @@ -393,7 +390,7 @@ func (cva ContinuousVestingAccount) String() string { // MarshalYAML returns the YAML representation of a ContinuousVestingAccount. func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ + alias := vestingAccountYAML{ Address: cva.Address, Coins: cva.Coins, AccountNumber: cva.AccountNumber, @@ -405,8 +402,9 @@ func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { StartTime: cva.StartTime, } - if cva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) + pk := cva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -424,9 +422,10 @@ func (cva ContinuousVestingAccount) MarshalYAML() (interface{}, error) { // MarshalJSON returns the JSON representation of a ContinuousVestingAccount. func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ + alias := vestingAccountJSON{ Address: cva.Address, Coins: cva.Coins, + PubKey: cva.GetPubKey(), AccountNumber: cva.AccountNumber, Sequence: cva.Sequence, OriginalVesting: cva.OriginalVesting, @@ -436,39 +435,18 @@ func (cva ContinuousVestingAccount) MarshalJSON() ([]byte, error) { StartTime: cva.StartTime, } - if cva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, cva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) + return codec.Cdc.MarshalJSON(alias) } // UnmarshalJSON unmarshals raw JSON bytes into a ContinuousVestingAccount. func (cva *ContinuousVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { return err } - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - cva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), OriginalVesting: alias.OriginalVesting, DelegatedFree: alias.DelegatedFree, DelegatedVesting: alias.DelegatedVesting, @@ -608,7 +586,7 @@ func (pva PeriodicVestingAccount) String() string { // MarshalYAML returns the YAML representation of a PeriodicVestingAccount. func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { - alias := vestingAccountPretty{ + alias := vestingAccountYAML{ Address: pva.Address, Coins: pva.Coins, AccountNumber: pva.AccountNumber, @@ -621,8 +599,9 @@ func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { VestingPeriods: pva.VestingPeriods, } - if pva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pva.PubKey) + pk := pva.GetPubKey() + if pk != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pk) if err != nil { return nil, err } @@ -640,9 +619,10 @@ func (pva PeriodicVestingAccount) MarshalYAML() (interface{}, error) { // MarshalJSON returns the JSON representation of a PeriodicVestingAccount. func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ + alias := vestingAccountJSON{ Address: pva.Address, Coins: pva.Coins, + PubKey: pva.GetPubKey(), AccountNumber: pva.AccountNumber, Sequence: pva.Sequence, OriginalVesting: pva.OriginalVesting, @@ -653,39 +633,18 @@ func (pva PeriodicVestingAccount) MarshalJSON() ([]byte, error) { VestingPeriods: pva.VestingPeriods, } - if pva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, pva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) + return codec.Cdc.MarshalJSON(alias) } // UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. func (pva *PeriodicVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { return err } - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - pva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), OriginalVesting: alias.OriginalVesting, DelegatedFree: alias.DelegatedFree, DelegatedVesting: alias.DelegatedVesting, @@ -769,9 +728,10 @@ func (dva DelayedVestingAccount) Validate() error { // MarshalJSON returns the JSON representation of a DelayedVestingAccount. func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { - alias := vestingAccountPretty{ + alias := vestingAccountJSON{ Address: dva.Address, Coins: dva.Coins, + PubKey: dva.GetPubKey(), AccountNumber: dva.AccountNumber, Sequence: dva.Sequence, OriginalVesting: dva.OriginalVesting, @@ -780,39 +740,18 @@ func (dva DelayedVestingAccount) MarshalJSON() ([]byte, error) { EndTime: dva.EndTime, } - if dva.PubKey != nil { - pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, dva.PubKey) - if err != nil { - return nil, err - } - - alias.PubKey = pks - } - - return json.Marshal(alias) + return codec.Cdc.MarshalJSON(alias) } // UnmarshalJSON unmarshals raw JSON bytes into a DelayedVestingAccount. func (dva *DelayedVestingAccount) UnmarshalJSON(bz []byte) error { - var alias vestingAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { + var alias vestingAccountJSON + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { return err } - var ( - pk crypto.PubKey - err error - ) - - if alias.PubKey != "" { - pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) - if err != nil { - return err - } - } - dva.BaseVestingAccount = &BaseVestingAccount{ - BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence), + BaseAccount: authtypes.NewBaseAccount(alias.Address, alias.Coins, alias.PubKey, alias.AccountNumber, alias.Sequence), OriginalVesting: alias.OriginalVesting, DelegatedFree: alias.DelegatedFree, DelegatedVesting: alias.DelegatedVesting, diff --git a/x/auth/vesting/types/vesting_account_test.go b/x/auth/vesting/types/vesting_account_test.go index 651416338c06..f349f7202dd5 100644 --- a/x/auth/vesting/types/vesting_account_test.go +++ b/x/auth/vesting/types/vesting_account_test.go @@ -10,6 +10,7 @@ import ( "github.com/tendermint/tendermint/crypto/secp256k1" tmtime "github.com/tendermint/tendermint/types/time" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -743,7 +744,7 @@ func TestBaseVestingAccountJSON(t *testing.T) { acc, err := NewBaseVestingAccount(baseAcc, coins, time.Now().Unix()) require.NoError(t, err) - bz, err := json.Marshal(acc) + bz, err := codec.Cdc.MarshalJSON(acc) require.NoError(t, err) bz1, err := acc.MarshalJSON() diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 7a9894aa8c3b..3f42eac4a7a1 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -49,7 +49,6 @@ var ( halfCoins = sdk.Coins{sdk.NewInt64Coin("foocoin", 5)} sendMsg1 = types.NewMsgSend(addr1, addr2, coins) - sendMsg2 = types.NewMsgSend(addr1, moduleAccAddr, coins) multiSendMsg1 = types.MsgMultiSend{ Inputs: []types.Input{types.NewInput(addr1, coins)}, diff --git a/x/bank/internal/keeper/keeper.go b/x/bank/internal/keeper/keeper.go index 8e5f5faa5c8d..5f0dfed2b221 100644 --- a/x/bank/internal/keeper/keeper.go +++ b/x/bank/internal/keeper/keeper.go @@ -211,6 +211,15 @@ func (keeper BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.In sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()), ), ) + + // Create account if recipient does not exist. + // + // NOTE: This should ultimately be removed in favor a more flexible approach + // such as delegated fee messages. + acc := keeper.ak.GetAccount(ctx, out.Address) + if acc == nil { + keeper.ak.SetAccount(ctx, keeper.ak.NewAccountWithAddress(ctx, out.Address)) + } } return nil diff --git a/x/bank/internal/keeper/keeper_test.go b/x/bank/internal/keeper/keeper_test.go index 1f8b38ab5723..e248df4b7698 100644 --- a/x/bank/internal/keeper/keeper_test.go +++ b/x/bank/internal/keeper/keeper_test.go @@ -149,6 +149,38 @@ func TestSendKeeper(t *testing.T) { require.Error(t, err) } +func TestInputOutputNewAccount(t *testing.T) { + app, ctx := createTestApp(false) + balances := sdk.NewCoins(sdk.NewInt64Coin("foo", 100), sdk.NewInt64Coin("bar", 50)) + addr1 := sdk.AccAddress([]byte("addr1")) + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + + app.AccountKeeper.SetAccount(ctx, acc1) + require.NoError(t, app.BankKeeper.SetCoins(ctx, addr1, balances)) + + acc1Balances := app.BankKeeper.GetCoins(ctx, addr1) + require.Equal(t, balances, acc1Balances) + + addr2 := sdk.AccAddress([]byte("addr2")) + + require.Nil(t, app.AccountKeeper.GetAccount(ctx, addr2)) + require.Empty(t, app.BankKeeper.GetCoins(ctx, addr2)) + + inputs := []types.Input{ + {Address: addr1, Coins: sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10))}, + } + outputs := []types.Output{ + {Address: addr2, Coins: sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10))}, + } + + require.NoError(t, app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + expected := sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10)) + acc2Balances := app.BankKeeper.GetCoins(ctx, addr2) + require.Equal(t, expected, acc2Balances) + require.NotNil(t, app.AccountKeeper.GetAccount(ctx, addr2)) +} + func TestMsgSendEvents(t *testing.T) { app, ctx := createTestApp(false) diff --git a/x/bank/internal/types/codec.go b/x/bank/internal/types/codec.go index 6d4d49adc118..f9a1609a8256 100644 --- a/x/bank/internal/types/codec.go +++ b/x/bank/internal/types/codec.go @@ -10,7 +10,6 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgMultiSend{}, "cosmos-sdk/MsgMultiSend", nil) } -// module codec var ModuleCdc *codec.Codec func init() { diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 123e0b8eb45a..c088addf41c3 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -85,7 +85,7 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operat // sendMsgSend sends a transaction with a MsgSend from a provided random account. func sendMsgSend( - r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, + r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, // nolint:interfacer msg types.MsgSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, ) error { @@ -219,7 +219,7 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.O // sendMsgMultiSend sends a transaction with a MsgMultiSend from a provided random // account. func sendMsgMultiSend( - r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, + r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, // nolint:interfacer msg types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, ) error { @@ -269,7 +269,7 @@ func sendMsgMultiSend( // randomSendFields returns the sender and recipient simulation accounts as well // as the transferred amount. func randomSendFields( - r *rand.Rand, ctx sdk.Context, accs []simulation.Account, ak types.AccountKeeper, + r *rand.Rand, ctx sdk.Context, accs []simulation.Account, ak types.AccountKeeper, // nolint:interfacer ) (simulation.Account, simulation.Account, sdk.Coins, bool, error) { simAccount, _ := simulation.RandomAcc(r, accs) diff --git a/x/crisis/internal/types/codec.go b/x/crisis/internal/types/codec.go index 92aeb130bce8..82136e7ef853 100644 --- a/x/crisis/internal/types/codec.go +++ b/x/crisis/internal/types/codec.go @@ -9,7 +9,7 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgVerifyInvariant{}, "cosmos-sdk/MsgVerifyInvariant", nil) } -// generic sealed codec to be used throughout module +// ModuleCdc is a generic sealed codec to be used throughout module var ModuleCdc *codec.Codec func init() { diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 666a0be4b7c2..35c587d02c57 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -5,9 +5,10 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" - abci "github.com/tendermint/tendermint/abci/types" ) func TestCalculateRewardsBasic(t *testing.T) { diff --git a/x/distribution/types/codec.go b/x/distribution/types/codec.go index a2d4bf11eaae..1cf6d9d6b8c6 100644 --- a/x/distribution/types/codec.go +++ b/x/distribution/types/codec.go @@ -4,15 +4,16 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// Register concrete types on codec codec +// RegisterCodec concrete distribution types on amino codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil) cdc.RegisterConcrete(MsgWithdrawValidatorCommission{}, "cosmos-sdk/MsgWithdrawValidatorCommission", nil) cdc.RegisterConcrete(MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil) cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil) + cdc.RegisterConcrete(MsgFundCommunityPool{}, "cosmos-sdk/MsgFundCommunityPool", nil) } -// generic sealed codec to be used throughout module +// ModuleCdc is a generic sealed codec to be used throughout module var ModuleCdc *codec.Codec func init() { diff --git a/x/genutil/client/cli/migrate.go b/x/genutil/client/cli/migrate.go index 3ae77c506d61..107d622971b9 100644 --- a/x/genutil/client/cli/migrate.go +++ b/x/genutil/client/cli/migrate.go @@ -16,6 +16,7 @@ import ( extypes "github.com/cosmos/cosmos-sdk/x/genutil" v036 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_36" v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_38" + v039 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_39" ) const ( @@ -29,6 +30,7 @@ const ( var migrationMap = extypes.MigrationMap{ "v0.36": v036.Migrate, "v0.38": v038.Migrate, // NOTE: v0.37 and v0.38 are genesis compatible + "v0.39": v039.Migrate, } // GetMigrationCallback returns a MigrationCallback for a given version. @@ -118,7 +120,7 @@ $ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2 return errors.Wrap(err, "failed to sort JSON genesis doc") } - fmt.Println(string(sortedBz)) + cmd.Println(string(sortedBz)) return nil }, } diff --git a/x/genutil/client/cli/migrate_test.go b/x/genutil/client/cli/migrate_test.go index 423629efcb13..acf307d85071 100644 --- a/x/genutil/client/cli/migrate_test.go +++ b/x/genutil/client/cli/migrate_test.go @@ -1,10 +1,20 @@ -package cli +package cli_test import ( "io/ioutil" + "os" "path" + "path/filepath" "testing" + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -17,6 +27,14 @@ import ( "github.com/cosmos/cosmos-sdk/tests" ) +// custom tx codec +func makeCodec() *codec.Codec { + var cdc = codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + return cdc +} + func setupCmd(genesisTime string, chainID string) *cobra.Command { c := &cobra.Command{ Use: "c", @@ -24,15 +42,15 @@ func setupCmd(genesisTime string, chainID string) *cobra.Command { Run: func(_ *cobra.Command, args []string) {}, } - c.Flags().String(flagGenesisTime, genesisTime, "") - c.Flags().String(flagChainID, chainID, "") + c.Flags().String("genesis-time", genesisTime, "") + c.Flags().String("chain-id", chainID, "") return c } func TestGetMigrationCallback(t *testing.T) { - for _, version := range GetMigrationVersions() { - require.NotNil(t, GetMigrationCallback(version)) + for _, version := range genutilcli.GetMigrationVersions() { + require.NotNil(t, genutilcli.GetMigrationCallback(version)) } } @@ -52,13 +70,52 @@ func TestMigrateGenesis(t *testing.T) { defer cleanup() // Reject if we dont' have the right parameters or genesis does not exists - require.Error(t, MigrateGenesisCmd(ctx, cdc).RunE(nil, []string{target, genesisPath})) + require.Error(t, genutilcli.MigrateGenesisCmd(ctx, cdc).RunE(nil, []string{target, genesisPath})) // Noop migration with minimal genesis emptyGenesis := []byte(`{"chain_id":"test","app_state":{}}`) - err = ioutil.WriteFile(genesisPath, emptyGenesis, 0644) + err = ioutil.WriteFile(genesisPath, emptyGenesis, 0600) require.Nil(t, err) cmd := setupCmd("", "test2") - require.NoError(t, MigrateGenesisCmd(ctx, cdc).RunE(cmd, []string{target, genesisPath})) + require.NoError(t, genutilcli.MigrateGenesisCmd(ctx, cdc).RunE(cmd, []string{target, genesisPath})) // Every migration function shuold tests its own module separately } + +func TestMigrateCommercioGenesisData(t *testing.T) { + home, cleanup := tests.NewTestCaseDir(t) + t.Cleanup(cleanup) + viper.Set(cli.HomeFlag, home) + viper.Set(flags.FlagName, "moniker") + logger := log.NewNopLogger() + cfg, err := tcmd.ParseConfig() + require.Nil(t, err) + ctx := server.NewContext(cfg, logger) + cdc := makeCodec() + + genesisPath := filepath.Join("testdata", "commercio-genesis.json") + require.NoError(t, err) + target := "v0.39" + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("did:com:", "did:com:pub") + config.Seal() + + // Migration with minimal genesis + cmd := setupCmd("2020-08-10T09:52:06.576222474Z", "test2") + _, newGenesisStream, _ := tests.ApplyMockIO(cmd) + require.NoError(t, genutilcli.MigrateGenesisCmd(ctx, cdc).RunE(cmd, []string{target, genesisPath})) + + db := dbm.NewMemDB() + app := simapp.NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + newGenesisBytes := newGenesisStream.Bytes() + + // Initialize the chain + require.NotPanics(t, func() { + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: newGenesisBytes, + }, + ) + }) +} diff --git a/x/genutil/client/cli/testdata/commercio-genesis.json b/x/genutil/client/cli/testdata/commercio-genesis.json new file mode 100644 index 000000000000..6cd4aae57c4d --- /dev/null +++ b/x/genutil/client/cli/testdata/commercio-genesis.json @@ -0,0 +1,247 @@ +{ + "genesis_time": "2020-08-06T09:52:06.576222474Z", + "chain_id": "testnet", + "consensus_params": { + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" + }, + "evidence": { + "max_age_num_blocks": "100000", + "max_age_duration": "172800000000000" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + }, + "entropy": { + "aeon_length": "100" + } + }, + "app_hash": "", + "app_state": { + "distribution": { + "params": { + "community_tax": "0.020000000000000000", + "base_proposer_reward": "0.010000000000000000", + "bonus_proposer_reward": "0.040000000000000000", + "withdraw_addr_enabled": true + }, + "fee_pool": { + "community_pool": [] + }, + "delegator_withdraw_infos": [], + "previous_proposer": "", + "outstanding_rewards": [], + "validator_accumulated_commissions": [], + "validator_historical_rewards": [], + "validator_current_rewards": [], + "delegator_starting_infos": [], + "validator_slash_events": [] + }, + "pricefeed": { + "oracles": [ + "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw" + ], + "assets": [ + "ucommercio" + ], + "raw_prices": [ + { + "oracle": "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "price": { + "asset_name": "ucommercio", + "value": "1.000000000000000000", + "expiry": "100000000" + }, + "created": "0" + } + ], + "denom_blacklist": [ + "uccc" + ] + }, + "docs": { + "documents": null, + "receipts": null, + "supported_metadata_schemes": null, + "trusted_metadata_schema_proposers": null + }, + "accreditations": { + "liquidity_pool_amount": [], + "invites": null, + "trusted_service_providers": [ + "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "did:com:1zlr8yn5plplvf39zr7laj05e8dm8mqd7ljznhf" + ], + "stable_credits_denom": "uccc", + "memberships": [ + { + "owner": "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "membership_type": "black" + } + ] + }, + "params": null, + "vbr": { + "pool_amount": [ + { + "denom": "ucommercio", + "amount": "12500000000000.000000000000000000" + } + ], + "yearly_pool_amount": null, + "year_number": "0" + }, + "commerciomint": { + "positions": [], + "pool_amount": [], + "credits_denom": "uccc", + "collateral_rate": "2.000000000000000000" + }, + "slashing": { + "params": { + "signed_blocks_window": "100", + "min_signed_per_window": "0.500000000000000000", + "downtime_jail_duration": "600000000000", + "slash_fraction_double_sign": "0.050000000000000000", + "slash_fraction_downtime": "0.010000000000000000" + }, + "signing_infos": {}, + "missed_blocks": {} + }, + "creditrisk": { + "pool": [] + }, + "bank": { + "send_enabled": true, + "blocked_accounts": [] + }, + "id": { + "did_documents": null, + "power_up_requests": null + }, + "crisis": { + "constant_fee": { + "denom": "ucommercio", + "amount": "1000" + } + }, + "staking": { + "params": { + "unbonding_time": "1814400000000000", + "max_validators": 100, + "max_entries": 7, + "historical_entries": 0, + "bond_denom": "ucommercio" + }, + "last_total_power": "0", + "last_validator_powers": null, + "validators": null, + "delegations": null, + "unbonding_delegations": null, + "redelegations": null, + "exported": false + }, + "government": { + "government_address": "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "tumbler_address": "did:com:1ej6229ezfdn443ry72l964nle6ra0sc5sstyy3" + }, + "supply": { + "supply": [] + }, + "genutil": { + "gentxs": [ + { + "type": "cosmos-sdk/StdTx", + "value": { + "msg": [ + { + "type": "cosmos-sdk/MsgCreateValidator", + "value": { + "description": { + "moniker": "testchain", + "identity": "", + "website": "", + "security_contact": "", + "details": "" + }, + "commission": { + "rate": "0.100000000000000000", + "max_rate": "0.200000000000000000", + "max_change_rate": "0.010000000000000000" + }, + "min_self_delegation": "1", + "delegator_address": "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "validator_address": "did:com:valoper1r93fsdxfthckptsz0wush4cx08t8c2fzk3w93a", + "pubkey": "did:com:valconspub1zcjduepqt3k7lh6qledmay447gnfl3ns4ejjwrzqk9n6ydcmr5clka5yvjlsdhcpag", + "value": { + "denom": "ucommercio", + "amount": "100000000" + } + } + } + ], + "fee": { + "amount": [], + "gas": "200000" + }, + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A0ZymN/QXBkfyxQJjfxvjReNhZjHjdaKjBqmAs945S4f" + }, + "signature": "oqLK+EycP7E5Ikaacltz5WI8eg1mJQOKUNEKQa6AQIlqBN9oYX4BKlMXSNfbu8Y3JlUNTDYNfOJKA2oSw6nssw==" + } + ], + "memo": "e5ecfe387a5210f0e6798f63d37802db26a19868@192.168.1.6:26656" + } + } + ] + }, + "auth": { + "params": { + "max_memo_characters": "256", + "tx_sig_limit": "7", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "590", + "sig_verify_cost_secp256k1": "1000" + }, + "accounts": [ + { + "type": "cosmos-sdk/Account", + "value": { + "address": "did:com:1r93fsdxfthckptsz0wush4cx08t8c2fz4jhruw", + "coins": [ + { + "denom": "ucommercio", + "amount": "10000000000000" + } + ], + "public_key": "", + "account_number": 0, + "sequence": 0 + } + }, + { + "type": "cosmos-sdk/Account", + "value": { + "address": "did:com:1zlr8yn5plplvf39zr7laj05e8dm8mqd7ljznhf", + "coins": [ + { + "denom": "uccc", + "amount": "10000000000" + } + ], + "public_key": "", + "account_number": 0, + "sequence": 0 + } + } + ] + } + } +} \ No newline at end of file diff --git a/x/genutil/legacy/v0_38/migrate.go b/x/genutil/legacy/v0_38/migrate.go index 59ba4772705f..ae22212c3d14 100644 --- a/x/genutil/legacy/v0_38/migrate.go +++ b/x/genutil/legacy/v0_38/migrate.go @@ -7,13 +7,13 @@ import ( v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_36" v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v0_38" v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_38" ) // Migrate migrates exported state from v0.36/v0.37 to a v0.38 genesis state. -func Migrate(appState genutil.AppMap) genutil.AppMap { +func Migrate(appState types.AppMap) types.AppMap { v036Codec := codec.New() codec.RegisterCrypto(v036Codec) @@ -34,9 +34,7 @@ func Migrate(appState genutil.AppMap) genutil.AppMap { // Migrate relative source genesis application state and marshal it into // the respective key. - appState[v038auth.ModuleName] = v038Codec.MustMarshalJSON( - v038auth.Migrate(authGenState, genAccountsGenState), - ) + appState[v038auth.ModuleName] = v038Codec.MustMarshalJSON(v038auth.Migrate(authGenState, genAccountsGenState)) } // migrate staking state diff --git a/x/genutil/legacy/v0_38/migrate_test.go b/x/genutil/legacy/v0_38/migrate_test.go index a19d1316f934..e0fc3ce48855 100644 --- a/x/genutil/legacy/v0_38/migrate_test.go +++ b/x/genutil/legacy/v0_38/migrate_test.go @@ -5,8 +5,8 @@ import ( v036auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_36" v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v0_36" - "github.com/cosmos/cosmos-sdk/x/genutil" v038 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_38" + "github.com/cosmos/cosmos-sdk/x/genutil/types" v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v0_36" "github.com/stretchr/testify/require" @@ -132,7 +132,7 @@ var genStakingState = []byte(`{ }`) func TestMigrate(t *testing.T) { - genesis := genutil.AppMap{ + genesis := types.AppMap{ v036auth.ModuleName: genAuthState, v036genaccounts.ModuleName: genAccountsState, v036staking.ModuleName: genStakingState, diff --git a/x/genutil/legacy/v0_39/migrate.go b/x/genutil/legacy/v0_39/migrate.go new file mode 100644 index 000000000000..6e614cc5a4af --- /dev/null +++ b/x/genutil/legacy/v0_39/migrate.go @@ -0,0 +1,33 @@ +package v039 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_39" + "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +// Migrate migrates exported state from v0.38 to a v0.39 genesis state. +// +// NOTE: No actual migration occurs since the types do not change, but JSON +// serialization of accounts do change. +func Migrate(appState types.AppMap) types.AppMap { + v038Codec := codec.New() + codec.RegisterCrypto(v038Codec) + v038auth.RegisterCodec(v038Codec) + + v039Codec := codec.New() + codec.RegisterCrypto(v039Codec) + v039auth.RegisterCodec(v039Codec) + + // migrate x/auth state (JSON serialization only) + if appState[v038auth.ModuleName] != nil { + var authGenState v038auth.GenesisState + v038Codec.MustUnmarshalJSON(appState[v038auth.ModuleName], &authGenState) + + delete(appState, v038auth.ModuleName) // delete old key in case the name changed + appState[v039auth.ModuleName] = v039Codec.MustMarshalJSON(v039auth.Migrate(authGenState)) + } + + return appState +} diff --git a/x/genutil/legacy/v0_39/migrate_test.go b/x/genutil/legacy/v0_39/migrate_test.go new file mode 100644 index 000000000000..44fbfee7f2fb --- /dev/null +++ b/x/genutil/legacy/v0_39/migrate_test.go @@ -0,0 +1,120 @@ +package v039_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_38" + v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v0_39" + v039 "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v0_39" + "github.com/cosmos/cosmos-sdk/x/genutil/types" +) + +var genAuthState = []byte(`{ + "params": { + "max_memo_characters": "10", + "tx_sig_limit": "10", + "tx_size_cost_per_byte": "10", + "sig_verify_cost_ed25519": "10", + "sig_verify_cost_secp256k1": "10" + }, + "accounts": [ + { + "type": "cosmos-sdk/Account", + "value": { + "address": "fetch19xav7r0kja7ldzf3jypvr7fhdd92zk6h8xtse5", + "coins": [ + { + "denom": "stake", + "amount": "400000" + } + ], + "public_key": "fetchpub1addwnpepqgrq8p4mt8n3xmvpshlyx89xr75wln0jj997auhw00cmd6emumd7wjhu8gx", + "account_number": 1, + "sequence": 1 + } + }, + { + "type": "cosmos-sdk/ModuleAccount", + "value": { + "address": "fetch1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3xxqtmq", + "coins": [ + { + "denom": "stake", + "amount": "400000000" + } + ], + "public_key": "", + "account_number": 2, + "sequence": 4, + "name": "bonded_tokens_pool", + "permissions": [ + "burner", + "staking" + ] + } + }, + { + "type": "cosmos-sdk/ContinuousVestingAccount", + "value": { + "address": "fetch13k6l84d7ceu744p660zy3zgtsz93v976zfuqml", + "coins": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "public_key": "fetchpub1addwnpepqgah4lqpza3ye0e8npm6f6cvut77heanlswg637uxs7ancttel4nunwcsfg", + "account_number": 3, + "sequence": 5, + "original_vesting": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "delegated_free": [], + "delegated_vesting": [], + "end_time": 1596125048, + "start_time": 1595952248 + } + }, + { + "type": "cosmos-sdk/DelayedVestingAccount", + "value": { + "address": "fetch1ckvh6fp75eenpkpnj88e2fv4hsdqnhc9lmd60r", + "coins": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "public_key": "fetchpub1addwnpepqg5vsltaz0x0awrhp99mvx32xuhcndtwa5yvcn9n34gh79dly6tss0pr0l0", + "account_number": 4, + "sequence": 15, + "original_vesting": [ + { + "denom": "stake", + "amount": "10000205" + } + ], + "delegated_free": [], + "delegated_vesting": [], + "end_time": 1596125048 + } + } + ] +}`) + +var expectedGenAuthState = []byte(`{"params":{"max_memo_characters":"10","tx_sig_limit":"10","tx_size_cost_per_byte":"10","sig_verify_cost_ed25519":"10","sig_verify_cost_secp256k1":"10"},"accounts":[{"type":"cosmos-sdk/Account","value":{"address":"fetch19xav7r0kja7ldzf3jypvr7fhdd92zk6h8xtse5","coins":[{"denom":"stake","amount":"400000"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"AgYDhrtZ5xNtgYX+QxymH6jvzfKRS+7y7nvxtus75tvn"},"account_number":"1","sequence":"1"}},{"type":"cosmos-sdk/ModuleAccount","value":{"address":"fetch1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3xxqtmq","coins":[{"denom":"stake","amount":"400000000"}],"public_key":"","account_number":"2","sequence":"4","name":"bonded_tokens_pool","permissions":["burner","staking"]}},{"type":"cosmos-sdk/ContinuousVestingAccount","value":{"address":"fetch13k6l84d7ceu744p660zy3zgtsz93v976zfuqml","coins":[{"denom":"stake","amount":"10000205"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"Ajt6/AEXYky/J5h3pOsM4v3r57P8HI1H3DQ92eFrz+s+"},"account_number":"3","sequence":"5","original_vesting":[{"denom":"stake","amount":"10000205"}],"delegated_free":[],"delegated_vesting":[],"end_time":"1596125048","start_time":"1595952248"}},{"type":"cosmos-sdk/DelayedVestingAccount","value":{"address":"fetch1ckvh6fp75eenpkpnj88e2fv4hsdqnhc9lmd60r","coins":[{"denom":"stake","amount":"10000205"}],"public_key":{"type":"tendermint/PubKeySecp256k1","value":"AijIfX0TzP64dwlLthoqNy+JtW7tCMxMs41RfxW/JpcI"},"account_number":"4","sequence":"15","original_vesting":[{"denom":"stake","amount":"10000205"}],"delegated_free":[],"delegated_vesting":[],"end_time":"1596125048"}}]}`) + +func TestMigrate(t *testing.T) { + genesis := types.AppMap{ + v038auth.ModuleName: genAuthState, + } + + var migrated types.AppMap + require.NotPanics(t, func() { migrated = v039.Migrate(genesis) }) + require.Equal(t, string(expectedGenAuthState), string(migrated[v039auth.ModuleName])) +} diff --git a/x/genutil/utils.go b/x/genutil/utils.go index 7d1a761ef562..4aea9d9fd500 100644 --- a/x/genutil/utils.go +++ b/x/genutil/utils.go @@ -11,8 +11,6 @@ import ( "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/server" ) // ExportGenesisFile creates and writes the genesis configuration to disk. An @@ -47,28 +45,28 @@ func ExportGenesisFileWithTime( } // InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles(config *cfg.Config, -) (nodeID string, valPubKey crypto.PubKey, err error) { - +func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) { nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { - return nodeID, valPubKey, err + return "", nil, err } nodeID = string(nodeKey.ID()) - server.UpgradeOldPrivValFile(config) pvKeyFile := config.PrivValidatorKeyFile() if err := tmos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { - return nodeID, valPubKey, nil + return "", nil, err } pvStateFile := config.PrivValidatorStateFile() if err := tmos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { - return nodeID, valPubKey, nil + return "", nil, err } - valPubKey = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey() + valPubKey, err = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey() + if err != nil { + return "", nil, err + } return nodeID, valPubKey, nil } diff --git a/x/gov/types/codec.go b/x/gov/types/codec.go index fc3de0e3f30c..82ca3c38c02b 100644 --- a/x/gov/types/codec.go +++ b/x/gov/types/codec.go @@ -4,7 +4,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// module codec var ModuleCdc = codec.New() // RegisterCodec registers all the necessary types and interfaces for diff --git a/x/mint/internal/types/codec.go b/x/mint/internal/types/codec.go index 5787f242adfe..ff10e307cf60 100644 --- a/x/mint/internal/types/codec.go +++ b/x/mint/internal/types/codec.go @@ -4,7 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// generic sealed codec to be used throughout this module +// ModuleCdc is a generic sealed codec to be used throughout this module var ModuleCdc *codec.Codec func init() { diff --git a/x/mint/internal/types/keys.go b/x/mint/internal/types/keys.go index cf395bc44ee3..0c7c71f78484 100644 --- a/x/mint/internal/types/keys.go +++ b/x/mint/internal/types/keys.go @@ -1,14 +1,14 @@ package types -// the one key to use for the keeper store +// MinterKey is used for the keeper store var MinterKey = []byte{0x00} // nolint const ( - // module name + // ModuleName ModuleName = "mint" - // default paramspace for params keeper + // DefaultParamspace params keeper DefaultParamspace = ModuleName // StoreKey is the default store key for mint diff --git a/x/params/client/proposal_handler.go b/x/params/client/proposal_handler.go index 040bebdf448f..d63f1ca3ffdc 100644 --- a/x/params/client/proposal_handler.go +++ b/x/params/client/proposal_handler.go @@ -6,5 +6,5 @@ import ( "github.com/cosmos/cosmos-sdk/x/params/client/rest" ) -// param change proposal handler +// ProposalHandler handles param change proposals var ProposalHandler = govclient.NewProposalHandler(cli.GetCmdSubmitProposal, rest.ProposalRESTHandler) diff --git a/x/params/types/codec.go b/x/params/types/codec.go index 525836346f90..2c45f5e0483c 100644 --- a/x/params/types/codec.go +++ b/x/params/types/codec.go @@ -4,7 +4,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -// module codec var ModuleCdc *codec.Codec func init() { diff --git a/x/simulation/account_test.go b/x/simulation/account_test.go index adb28dcf984f..e5bda5e4f202 100644 --- a/x/simulation/account_test.go +++ b/x/simulation/account_test.go @@ -65,6 +65,7 @@ func TestRandomFees(t *testing.T) { {"0 coins", sdk.NewCoins(sdk.NewInt64Coin("aaa", 10), sdk.NewInt64Coin("bbb", 5)), false, false}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { got, err := simulation.RandomFees(r, sdk.Context{}, tt.spendableCoins) if (err != nil) != tt.wantErr { diff --git a/x/simulation/event_stats.go b/x/simulation/event_stats.go index 1a6201aaca77..0e4289fd243e 100644 --- a/x/simulation/event_stats.go +++ b/x/simulation/event_stats.go @@ -48,7 +48,7 @@ func (es EventStats) ExportJSON(path string) { panic(err) } - err = ioutil.WriteFile(path, bz, 0644) + err = ioutil.WriteFile(path, bz, 0600) if err != nil { panic(err) } diff --git a/x/simulation/rand_util_test.go b/x/simulation/rand_util_test.go index 2128de4050aa..9778dcb699b1 100644 --- a/x/simulation/rand_util_test.go +++ b/x/simulation/rand_util_test.go @@ -46,6 +46,7 @@ func TestRandStringOfLength(t *testing.T) { {"10-size", 1_000_000_000, 1_000_000_000}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { got := simulation.RandStringOfLength(r, tt.n) require.Equal(t, tt.want, len(got)) diff --git a/x/slashing/internal/keeper/keeper_test.go b/x/slashing/internal/keeper/keeper_test.go index 62005e3deeb8..037622612754 100644 --- a/x/slashing/internal/keeper/keeper_test.go +++ b/x/slashing/internal/keeper/keeper_test.go @@ -6,10 +6,11 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/internal/types" "github.com/cosmos/cosmos-sdk/x/staking" - abci "github.com/tendermint/tendermint/abci/types" ) // Test a new validator entering the validator set diff --git a/x/slashing/simulation/operations.go b/x/slashing/simulation/operations.go index f5e263b778dc..590513fd5b0b 100644 --- a/x/slashing/simulation/operations.go +++ b/x/slashing/simulation/operations.go @@ -43,7 +43,7 @@ func WeightedOperations( // SimulateMsgUnjail generates a MsgUnjail with random values // nolint: funlen -func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { +func SimulateMsgUnjail(ak types.AccountKeeper, k keeper.Keeper, sk stakingkeeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 219beef71271..53d778c14912 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -258,6 +258,8 @@ func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context, store := ctx.KVStore(k.storeKey) // gets an iterator for all timeslices from time 0 until the current Blockheader time unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time) + defer unbondingTimesliceIterator.Close() + for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() { timeslice := []types.DVPair{} value := unbondingTimesliceIterator.Value() @@ -445,6 +447,8 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time store := ctx.KVStore(k.storeKey) // gets an iterator for all timeslices from time 0 until the current Blockheader time redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time) + defer redelegationTimesliceIterator.Close() + for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() { timeslice := []types.DVVTriplet{} value := redelegationTimesliceIterator.Value() diff --git a/x/staking/simulation/operations.go b/x/staking/simulation/operations.go index b1a932270641..a8372dc922e5 100644 --- a/x/staking/simulation/operations.go +++ b/x/staking/simulation/operations.go @@ -93,7 +93,7 @@ func WeightedOperations( // SimulateMsgCreateValidator generates a MsgCreateValidator with random values // nolint: funlen -func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { @@ -171,7 +171,7 @@ func SimulateMsgCreateValidator(ak types.AccountKeeper, k keeper.Keeper) simulat // SimulateMsgEditValidator generates a MsgEditValidator with random values // nolint: funlen -func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { @@ -236,7 +236,7 @@ func SimulateMsgEditValidator(ak types.AccountKeeper, k keeper.Keeper) simulatio // SimulateMsgDelegate generates a MsgDelegate with random values // nolint: funlen -func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { @@ -303,7 +303,7 @@ func SimulateMsgDelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Ope // SimulateMsgUndelegate generates a MsgUndelegate with random values // nolint: funlen -func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { @@ -383,7 +383,7 @@ func SimulateMsgUndelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.O // SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values // nolint: funlen -func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { +func SimulateMsgBeginRedelegate(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation { // nolint:interfacer return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { diff --git a/x/staking/types/codec.go b/x/staking/types/codec.go index 6fadfd9a68f9..91567ed51fd9 100644 --- a/x/staking/types/codec.go +++ b/x/staking/types/codec.go @@ -13,7 +13,7 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate", nil) } -// generic sealed codec to be used throughout this module +// ModuleCdc is a generic sealed codec to be used throughout this module var ModuleCdc *codec.Codec func init() { diff --git a/x/supply/internal/types/account.go b/x/supply/internal/types/account.go index ac2136ec6910..b49b38890dfc 100644 --- a/x/supply/internal/types/account.go +++ b/x/supply/internal/types/account.go @@ -1,7 +1,6 @@ package types import ( - "encoding/json" "errors" "fmt" "strings" @@ -10,6 +9,7 @@ import ( "github.com/tendermint/tendermint/crypto" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -150,7 +150,7 @@ func (ma ModuleAccount) MarshalYAML() (interface{}, error) { // MarshalJSON returns the JSON representation of a ModuleAccount. func (ma ModuleAccount) MarshalJSON() ([]byte, error) { - return json.Marshal(moduleAccountPretty{ + return codec.Cdc.MarshalJSON(moduleAccountPretty{ Address: ma.Address, Coins: ma.Coins, PubKey: "", @@ -164,7 +164,7 @@ func (ma ModuleAccount) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals raw JSON bytes into a ModuleAccount. func (ma *ModuleAccount) UnmarshalJSON(bz []byte) error { var alias moduleAccountPretty - if err := json.Unmarshal(bz, &alias); err != nil { + if err := codec.Cdc.UnmarshalJSON(bz, &alias); err != nil { return err } diff --git a/x/upgrade/alias.go b/x/upgrade/alias.go index fe36f23097bc..04e748c9799e 100644 --- a/x/upgrade/alias.go +++ b/x/upgrade/alias.go @@ -32,7 +32,7 @@ var ( ) type ( - UpgradeHandler = types.UpgradeHandler + UpgradeHandler = types.UpgradeHandler //nolint:golint Plan = types.Plan SoftwareUpgradeProposal = types.SoftwareUpgradeProposal CancelSoftwareUpgradeProposal = types.CancelSoftwareUpgradeProposal