· 5 years ago · Jul 21, 2020, 03:56 PM
1diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
2index 37f702ef6..308307c52 100644
3--- a/.github/workflows/lint.yml
4+++ b/.github/workflows/lint.yml
5@@ -9,6 +9,6 @@ jobs:
6 - uses: golangci/golangci-lint-action@master
7 with:
8 # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
9- version: v1.27
10+ version: v1.28
11 args: --timeout 10m
12 github-token: ${{ secrets.github_token }}
13diff --git a/CHANGELOG.md b/CHANGELOG.md
14index b1e0172ce..00a3a19b0 100644
15--- a/CHANGELOG.md
16+++ b/CHANGELOG.md
17@@ -35,6 +35,45 @@ Ref: https://keepachangelog.com/en/1.0.0/
18
19 # Changelog
20
21+## [v0.39.0]
22+
23+### Improvements
24+
25+* (deps) Bump Tendermint version to [v0.33.6](https://github.com/tendermint/tendermint/releases/tag/v0.33.6)
26+* (deps) Bump IAVL version to [v0.14.0](https://github.com/cosmos/iavl/releases/tag/v0.14.0)
27+* (client) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) `CLIContext` additions:
28+ * Introduce `QueryABCI` that returns the full `abci.ResponseQuery` with inclusion Merkle proofs.
29+ * Added `prove` flag for Merkle proof verification.
30+* (x/staking) [\#6791)](https://github.com/cosmos/cosmos-sdk/pull/6791) Close {UBDQueue,RedelegationQueu}Iterator once used.
31+
32+### API Breaking Changes
33+
34+* (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.
35+
36+### Client Breaking Changes
37+
38+* (x/auth) [\#6745](https://github.com/cosmos/cosmos-sdk/issues/6745) Remove BaseAccount's custom JSON {,un}marshalling.
39+
40+### Bug Fixes
41+
42+* (store) [\#6475](https://github.com/cosmos/cosmos-sdk/pull/6475) Revert IAVL pruning functionality introduced in
43+[v0.13.0](https://github.com/cosmos/iavl/releases/tag/v0.13.0),
44+where the IAVL no longer keeps states in-memory in which it flushes periodically. IAVL now commits and
45+flushes every state to disk as it did pre-v0.13.0. The SDK's multi-store will track and ensure the proper
46+heights are pruned. The operator can set the pruning options via a `pruning` config via the CLI or
47+through `app.toml`. The `pruning` flag exposes `default|everything|nothing|custom` as options --
48+see docs for further details. If the operator chooses `custom`, they may provide granular pruning
49+options `pruning-keep-recent`, `pruning-keep-every`, and `pruning-interval`. The former two options
50+dictate how many recent versions are kept on disk and the offset of what versions are kept after that
51+respectively, and the latter defines the height interval in which versions are deleted in a batch.
52+**Note, there are some client-facing API breaking changes with regard to IAVL, stores, and pruning settings.**
53+* (x/distribution) [\#6210](https://github.com/cosmos/cosmos-sdk/pull/6210) Register `MsgFundCommunityPool` in distribution amino codec.
54+* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent `ChainAnteDecorators()` from panicking when empty `AnteDecorator` slice is supplied.
55+* (baseapp) [\#6306](https://github.com/cosmos/cosmos-sdk/issues/6306) Prevent events emitted by the antehandler from being persisted between transactions.
56+* (client/keys) [\#5091](https://github.com/cosmos/cosmos-sdk/issues/5091) `keys parse` does not honor client app's configuration.
57+* (x/bank) [\#6674](https://github.com/cosmos/cosmos-sdk/pull/6674) Create account if recipient does not exist on handing `MsgMultiSend`.
58+* (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.
59+
60 ## [v0.38.5] - 2020-07-02
61
62 ### Improvements
63@@ -311,7 +350,6 @@ to detail this new feature and how state transitions occur.
64 now exists a single `Params` type with a getter and setter along with a getter for each individual parameter.
65
66 ### Bug Fixes
67-
68 * (rest) [\#5508](https://github.com/cosmos/cosmos-sdk/pull/5508) Fix `x/distribution` endpoints to properly return height in the response.
69 * (x/genutil) [\#5499](https://github.com/cosmos/cosmos-sdk/pull/) Ensure `DefaultGenesis` returns valid and non-nil default genesis state.
70 * (client) [\#5303](https://github.com/cosmos/cosmos-sdk/issues/5303) Fix ignored error in tx generate only mode.
71diff --git a/Makefile b/Makefile
72index 8d947640f..9d857bc8c 100644
73--- a/Makefile
74+++ b/Makefile
75@@ -35,6 +35,7 @@ update-swagger-docs: statik
76
77 mocks: $(MOCKS_DIR)
78 mockgen -source=x/auth/types/account_retriever.go -package mocks -destination tests/mocks/account_retriever.go
79+ mockgen -source=types/handler.go -package mocks -destination tests/mocks/types_handler.go
80 .PHONY: mocks
81
82 $(MOCKS_DIR):
83diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
84new file mode 100644
85index 000000000..85975749b
86--- /dev/null
87+++ b/RELEASE_NOTES.md
88@@ -0,0 +1,131 @@
89+# Cosmos SDK v0.39.0 Release Notes
90+
91+This is the inaugural release of the **Cosmos SDK 0.39 «Launchpad»** release series.
92+
93+See the [Cosmos SDK 0.39.0 milestone](https://github.com/cosmos/cosmos-sdk/milestone/27?closed=1) on our issue tracker for details.
94+
95+## Changes to IAVL and store pruning
96+
97+The pruning features introduced in the `0.38` release series are buggy and might lead to data loss,
98+even after upgrading to `v0.39.0`. When upgrading from `0.38` it is important to follow the instructions
99+below, to prevent data loss and database corruption.
100+
101+**Note: there are are several breaking changes with regard to IAVL, stores, and pruning settings that affect command line clients, server configuration, and Golang API.**
102+
103+### Migrate an application from 0.38.5 to 0.39.0
104+
105+The IAVL's `v0.13.0` release introduced a pruning functionality that turned out to be buggy and flawed.
106+IAVL's new `v0.14.0` release now commits and flushes every state to disk as it did in pre-`v0.13.0` release.
107+The SDK's multi-store will track and ensure the proper heights are pruned. The operator can now set the pruning
108+options by passing a `pruning` configuration via command line option or `app.toml`. The `pruning` flag supports the following
109+options: `default`, `everything`, `nothing`, `custom` - see docs for further details. If the operator chooses `custom`, they
110+may want to provide either of the granular pruning values:
111+- `pruning-keep-recent`
112+- `pruning-keep-every`
113+- `pruning-interval`
114+
115+The former two options dictate how many recent versions are kept on disk and the offset of what versions are kept after that
116+respectively, and the latter defines the height interval in which versions are deleted in a batch. **Note: there are are some
117+client application breaking changes with regard to IAVL, stores, and pruning settings.** An example patch follows:
118+
119+```patch
120+From 5884171ba73c3054e98564c39adc9cbbab8d4646 Mon Sep 17 00:00:00 2001
121+From: Alessio Treglia <alessio@tendermint.com>
122+Date: Tue, 14 Jul 2020 14:54:19 +0100
123+Subject: [PATCH 2/4] use new pruning options
124+
125+---
126+ cmd/cnd/main.go | 8 ++++++--
127+ 1 file changed, 6 insertions(+), 2 deletions(-)
128+
129+diff --git a/cmd/cnd/main.go b/cmd/cnd/main.go
130+index b0c86f4a..4a3a8518 100644
131+--- a/cmd/cnd/main.go
132++++ b/cmd/cnd/main.go
133+@@ -23,7 +23,6 @@ import (
134+ comgenutilcli "github.com/commercionetwork/commercionetwork/x/genutil/client/cli"
135+ "github.com/cosmos/cosmos-sdk/baseapp"
136+ "github.com/cosmos/cosmos-sdk/server"
137+- "github.com/cosmos/cosmos-sdk/store"
138+ genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
139+ "github.com/cosmos/cosmos-sdk/x/staking"
140+ )
141+@@ -87,9 +86,14 @@ func main() {
142+ }
143+
144+ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
145++ pruningOpts, err := server.GetPruningOptionsFromFlags()
146++ if err != nil {
147++ panic(err)
148++ }
149++
150+ return app.NewCommercioNetworkApp(
151+ logger, db, traceStore, true, invCheckPeriod,
152+- baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
153++ baseapp.SetPruning(pruningOpts),
154+ baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
155+ baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))),
156+ )
157+```
158+
159+### Migrate a node from 0.38.5 to 0.39.0
160+
161+Note: **do not modify pruning settings with any release prior to `v0.39.0` as that may cause data corruption**.
162+
163+The following instructions assume that **pruning settings have not been modified since the node started using 0.38.x. Note: the default pruning setting `syncable` used `KeepEvery:100`.
164+
165+#### Chain Hard Fork (also know as The Tested Upgrade Path)
166+
167+This strategy entails performing a hard fork of your chain.
168+It takes time, coordination and a few technical steps that the validators of the network must follow. Note: a software upgrade guide for `gaia`, the application that powers the Cosmos Hub, is [available too](https://hub.cosmos.network/master/gaia-tutorials/upgrade-node.html). It contains detailed instructions on how to upgrade network nodes that apply to the vast majority of Cosmos SDK applications.
169+
170+In preparation of the upgrade, you need to export the current state. This operation should be be performed on one node:
171+
172+ * Stop the node and export the current state, e.g.: `appd export --for-zero-height > export_genesis.json`.
173+ * Manually replace the chain id and genesis time fields in `export_genesis.json` with the values that the network had agreed upon.
174+
175+Follow these steps to perform the upgrade:
176+
177+* Make a backup copy of the old `genesis.json` file in your server application's config directory (e.g. `$HOME/.appd/config/genesis.json`) and replace it with `export_genesis.json`. Note: do rename `export_genesis.json` to `genesis.json`.
178+* Replace the old binary with the new one and restart the service using the new binary.
179+
180+#### Alternative strategies
181+
182+Alternatively, you can follow *one of* the following strategies:
183+
184+* Replace the application server's binary and perform a full sync of the node from scratch.
185+
186+* If your node had started with using `KeepEvery:1` (e.g. pruning settings `nothing` or `everything`), upgrading to `v0.39.0` should be simple and safe.
187+
188+* Do halt block processing with `--halt-height` after committing a height divisible by `KeepEvery` - e.g. at block 147600 with `KeepEvery:100`. The **node must never have processed a height beyond that at any time in its past**. Upgrading to `v0.39.0` is then safe.
189+
190+* Set the `KeepEvery` setting to the same as the previous `KeepEvery` setting (both `<=v0.38.5` and `v0.39.0` default to `KeepEvery:100`). Upgrade to `v0.39.0` is then safe as long as you wait one `KeepEvery` interval plus one `KeepRecent` interval **plus** one pruning `Interval` before changing pruning settings or deleting the last `<=v0.38.5` height (so wait *210* heights with the default configuration).
191+
192+* Otherwise, make sure the last version persisted with `<=v0.38.5` is never deleted after upgrading to `v0.39.0`, as doing so may cause data loss and data corruption.
193+
194+## Regression in the signature verification when multiple transactions in the same block are sent from the same account
195+
196+When multiple transactions in the same block are sent (and correctly signed) by the same account, chances are that some of them could be rejected and the error `unauthorized: signature verification failed` would be returned due to the account's sequence (*nonce*) getting stuck and not being incremented by the ante handler. This behaviour was [a regression](https://github.com/cosmos/cosmos-sdk/issues/6287) introduced in the `v0.38` release series, it did not occur in the `v0.37` release series and is now fixed in this release.
197+
198+## Changes to ABCI Query's "app/simulate" path
199+
200+The `app/simulate` query path is used to simulate the execution transactions in order to obtain an estimate
201+of the gas consumption that would be required to actually execute them. The response used to return only
202+the amount of gas, it now returns the result of the transaction as well.
203+
204+## bank.send event comes with sender information
205+
206+The `bank.send` event used to carry only the recipient and amount. It was assumed that the sender of the funds was `message.sender`.
207+This is often not true when a module call the bank keeper directly. This may be due to staking distribution, or via a cosmwasm contract that released funds (where I discovered the issue).
208+
209+`bank.send` now contains the entire triple `(sender, recipient, amount)`.
210+
211+## trace option is no longer ignored
212+
213+The `--trace` option is reintroduced. It comes in very handy for debugging as it causes the full stack trace to be included in the ABCI error logs.
214+
215+## appcli keys parse command didn't honor client application's bech32 prefixes
216+
217+The `key parse` command ignored the application-specific address bech32
218+prefixes and used to return `cosmos*1`-prefixed addresses regardless
219+of the client application's configuration.
220diff --git a/baseapp/abci.go b/baseapp/abci.go
221index 57bcec661..0e16574dd 100644
222--- a/baseapp/abci.go
223+++ b/baseapp/abci.go
224@@ -163,7 +163,7 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
225 func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
226 tx, err := app.txDecoder(req.Tx)
227 if err != nil {
228- return sdkerrors.ResponseCheckTx(err, 0, 0)
229+ return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace)
230 }
231
232 var mode runTxMode
233@@ -181,7 +181,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
234
235 gInfo, result, err := app.runTx(mode, req.Tx, tx)
236 if err != nil {
237- return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed)
238+ return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace)
239 }
240
241 return abci.ResponseCheckTx{
242@@ -201,12 +201,12 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
243 func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
244 tx, err := app.txDecoder(req.Tx)
245 if err != nil {
246- return sdkerrors.ResponseDeliverTx(err, 0, 0)
247+ return sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace)
248 }
249
250 gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx)
251 if err != nil {
252- return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed)
253+ return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace)
254 }
255
256 return abci.ResponseDeliverTx{
257@@ -326,12 +326,20 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res
258 return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to decode tx"))
259 }
260
261- gInfo, _, _ := app.Simulate(txBytes, tx)
262+ gInfo, res, err := app.Simulate(txBytes, tx)
263+ if err != nil {
264+ return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to simulate tx"))
265+ }
266+
267+ simRes := sdk.SimulationResponse{
268+ GasInfo: gInfo,
269+ Result: res,
270+ }
271
272 return abci.ResponseQuery{
273 Codespace: sdkerrors.RootCodespace,
274 Height: req.Height,
275- Value: codec.Cdc.MustMarshalBinaryLengthPrefixed(gInfo.GasUsed),
276+ Value: codec.Cdc.MustMarshalBinaryBare(simRes),
277 }
278
279 case "version":
280diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
281index 6fa6a943d..3fd121e08 100644
282--- a/baseapp/baseapp.go
283+++ b/baseapp/baseapp.go
284@@ -106,6 +106,9 @@ type BaseApp struct { // nolint: maligned
285
286 // application's version string
287 appVersion string
288+
289+ // trace set will return full stack traces for errors in ABCI Log field
290+ trace bool
291 }
292
293 // NewBaseApp returns a reference to an initialized BaseApp. It accepts a
294@@ -127,6 +130,7 @@ func NewBaseApp(
295 queryRouter: NewQueryRouter(),
296 txDecoder: txDecoder,
297 fauxMerkleMode: false,
298+ trace: false,
299 }
300 for _, option := range options {
301 option(app)
302@@ -354,6 +358,10 @@ func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) {
303 app.interBlockCache = cache
304 }
305
306+func (app *BaseApp) setTrace(trace bool) {
307+ app.trace = trace
308+}
309+
310 // Router returns the router of the BaseApp.
311 func (app *BaseApp) Router() sdk.Router {
312 if app.sealed {
313@@ -596,8 +604,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk.
314 // writes do not happen if aborted/failed. This may have some
315 // performance benefits, but it'll be more difficult to get right.
316 anteCtx, msCache = app.cacheTxContext(ctx, txBytes)
317-
318+ anteCtx = anteCtx.WithEventManager(sdk.NewEventManager())
319 newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate)
320+
321 if !newCtx.IsZero() {
322 // At this point, newCtx.MultiStore() is cache-wrapped, or something else
323 // replaced by the AnteHandler. We want the original multistore, not one
324diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go
325index 6ee5aabbb..75f5e5b61 100644
326--- a/baseapp/baseapp_test.go
327+++ b/baseapp/baseapp_test.go
328@@ -168,7 +168,7 @@ func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) {
329
330 func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte) {
331 rs := rootmulti.NewStore(db)
332- rs.SetPruning(store.PruneSyncable)
333+ rs.SetPruning(store.PruneDefault)
334 key := sdk.NewKVStoreKey(storeKey)
335 rs.MountStoreWithDB(key, store.StoreTypeIAVL, nil)
336 err := rs.LoadLatestVersion()
337@@ -273,7 +273,7 @@ func TestSetLoader(t *testing.T) {
338
339 func TestAppVersionSetterGetter(t *testing.T) {
340 logger := defaultLogger()
341- pruningOpt := SetPruning(store.PruneSyncable)
342+ pruningOpt := SetPruning(store.PruneDefault)
343 db := dbm.NewMemDB()
344 name := t.Name()
345 app := NewBaseApp(name, logger, db, nil, pruningOpt)
346@@ -329,8 +329,9 @@ func TestLoadVersionInvalid(t *testing.T) {
347 func TestLoadVersionPruning(t *testing.T) {
348 logger := log.NewNopLogger()
349 pruningOptions := store.PruningOptions{
350- KeepEvery: 2,
351- SnapshotEvery: 6,
352+ KeepRecent: 2,
353+ KeepEvery: 3,
354+ Interval: 1,
355 }
356 pruningOpt := SetPruning(pruningOptions)
357 db := dbm.NewMemDB()
358@@ -351,61 +352,33 @@ func TestLoadVersionPruning(t *testing.T) {
359 require.Equal(t, int64(0), lastHeight)
360 require.Equal(t, emptyCommitID, lastID)
361
362- // execute a block
363- header := abci.Header{Height: 1}
364- app.BeginBlock(abci.RequestBeginBlock{Header: header})
365- res := app.Commit()
366-
367- // execute a block, collect commit ID
368- header = abci.Header{Height: 2}
369- app.BeginBlock(abci.RequestBeginBlock{Header: header})
370- res = app.Commit()
371- commitID2 := sdk.CommitID{Version: 2, Hash: res.Data}
372-
373- // execute a block
374- header = abci.Header{Height: 3}
375- app.BeginBlock(abci.RequestBeginBlock{Header: header})
376- res = app.Commit()
377- commitID3 := sdk.CommitID{Version: 3, Hash: res.Data}
378-
379- // reload with LoadLatestVersion, check it loads last flushed version
380- app = NewBaseApp(name, logger, db, nil, pruningOpt)
381- app.MountStores(capKey)
382- err = app.LoadLatestVersion(capKey)
383- require.Nil(t, err)
384- testLoadVersionHelper(t, app, int64(2), commitID2)
385+ var lastCommitID sdk.CommitID
386
387- // re-execute block 3 and check it is same CommitID
388- header = abci.Header{Height: 3}
389- app.BeginBlock(abci.RequestBeginBlock{Header: header})
390- res = app.Commit()
391- recommitID3 := sdk.CommitID{Version: 3, Hash: res.Data}
392- require.Equal(t, commitID3, recommitID3, "Commits of identical blocks not equal after reload")
393+ // Commit seven blocks, of which 7 (latest) is kept in addition to 6, 5
394+ // (keep recent) and 3 (keep every).
395+ for i := int64(1); i <= 7; i++ {
396+ app.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{Height: i}})
397+ res := app.Commit()
398+ lastCommitID = sdk.CommitID{Version: i, Hash: res.Data}
399+ }
400
401- // execute a block, collect commit ID
402- header = abci.Header{Height: 4}
403- app.BeginBlock(abci.RequestBeginBlock{Header: header})
404- res = app.Commit()
405- commitID4 := sdk.CommitID{Version: 4, Hash: res.Data}
406+ for _, v := range []int64{1, 2, 4} {
407+ _, err = app.cms.CacheMultiStoreWithVersion(v)
408+ require.Error(t, err)
409+ }
410
411- // execute a block
412- header = abci.Header{Height: 5}
413- app.BeginBlock(abci.RequestBeginBlock{Header: header})
414- res = app.Commit()
415+ for _, v := range []int64{3, 5, 6, 7} {
416+ _, err = app.cms.CacheMultiStoreWithVersion(v)
417+ require.NoError(t, err)
418+ }
419
420- // reload with LoadLatestVersion, check it loads last flushed version
421+ // reload with LoadLatestVersion, check it loads last version
422 app = NewBaseApp(name, logger, db, nil, pruningOpt)
423 app.MountStores(capKey)
424+
425 err = app.LoadLatestVersion(capKey)
426 require.Nil(t, err)
427- testLoadVersionHelper(t, app, int64(4), commitID4)
428-
429- // reload with LoadVersion of previous flushed version
430- // and check it fails since previous flush should be pruned
431- app = NewBaseApp(name, logger, db, nil, pruningOpt)
432- app.MountStores(capKey)
433- err = app.LoadVersion(2, capKey)
434- require.NotNil(t, err)
435+ testLoadVersionHelper(t, app, int64(7), lastCommitID)
436 }
437
438 func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) {
439@@ -685,20 +658,33 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder {
440 }
441
442 func anteHandlerTxTest(t *testing.T, capKey sdk.StoreKey, storeKey []byte) sdk.AnteHandler {
443- return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
444+ return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
445 store := ctx.KVStore(capKey)
446 txTest := tx.(txTest)
447
448 if txTest.FailOnAnte {
449- return newCtx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
450+ return ctx, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "ante handler failure")
451 }
452
453- _, err = incrementingCounter(t, store, storeKey, txTest.Counter)
454+ _, err := incrementingCounter(t, store, storeKey, txTest.Counter)
455 if err != nil {
456- return newCtx, err
457+ return ctx, err
458 }
459
460- return newCtx, nil
461+ ctx.EventManager().EmitEvents(
462+ counterEvent("ante_handler", txTest.Counter),
463+ )
464+
465+ return ctx, nil
466+ }
467+}
468+
469+func counterEvent(evType string, msgCount int64) sdk.Events {
470+ return sdk.Events{
471+ sdk.NewEvent(
472+ evType,
473+ sdk.NewAttribute("update_counter", fmt.Sprintf("%d", msgCount)),
474+ ),
475 }
476 }
477
478@@ -985,10 +971,13 @@ func TestSimulateTx(t *testing.T) {
479 queryResult := app.Query(query)
480 require.True(t, queryResult.IsOK(), queryResult.Log)
481
482- var res uint64
483- err = codec.Cdc.UnmarshalBinaryLengthPrefixed(queryResult.Value, &res)
484+ var simRes sdk.SimulationResponse
485+ err = codec.Cdc.UnmarshalBinaryBare(queryResult.Value, &simRes)
486 require.NoError(t, err)
487- require.Equal(t, gasConsumed, res)
488+ require.Equal(t, gInfo, simRes.GasInfo)
489+ require.Equal(t, result.Log, simRes.Result.Log)
490+ require.Equal(t, result.Events, simRes.Result.Events)
491+ require.True(t, bytes.Equal(result.Data, simRes.Result.Data))
492 app.EndBlock(abci.RequestEndBlock{})
493 app.Commit()
494 }
495@@ -1312,6 +1301,7 @@ func TestBaseAppAnteHandler(t *testing.T) {
496 txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx)
497 require.NoError(t, err)
498 res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
499+ require.Empty(t, res.Events)
500 require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
501
502 ctx := app.getState(runTxModeDeliver).ctx
503@@ -1327,6 +1317,7 @@ func TestBaseAppAnteHandler(t *testing.T) {
504 require.NoError(t, err)
505
506 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
507+ require.Empty(t, res.Events)
508 require.False(t, res.IsOK(), fmt.Sprintf("%v", res))
509
510 ctx = app.getState(runTxModeDeliver).ctx
511@@ -1342,6 +1333,7 @@ func TestBaseAppAnteHandler(t *testing.T) {
512 require.NoError(t, err)
513
514 res = app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
515+ require.NotEmpty(t, res.Events)
516 require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
517
518 ctx = app.getState(runTxModeDeliver).ctx
519diff --git a/baseapp/options.go b/baseapp/options.go
520index ea3b42ac2..b8fdef58b 100644
521--- a/baseapp/options.go
522+++ b/baseapp/options.go
523@@ -44,6 +44,11 @@ func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) {
524 return func(app *BaseApp) { app.setInterBlockCache(cache) }
525 }
526
527+// SetTrace will turn on or off trace flag
528+func SetTrace(trace bool) func(*BaseApp) {
529+ return func(app *BaseApp) { app.setTrace(trace) }
530+}
531+
532 func (app *BaseApp) SetName(name string) {
533 if app.sealed {
534 panic("SetName() on sealed BaseApp")
535diff --git a/client/context/query.go b/client/context/query.go
536index 542ba4c38..d55799d4c 100644
537--- a/client/context/query.go
538+++ b/client/context/query.go
539@@ -22,7 +22,7 @@ import (
540 // error is returned.
541 func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
542 if ctx.Client == nil {
543- return nil, errors.New("no RPC client defined")
544+ return nil, errors.New("no RPC client is defined in offline mode")
545 }
546
547 return ctx.Client, nil
548@@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte
549 return ctx.queryStore(key, storeName, "key")
550 }
551
552+// QueryABCI performs a query to a Tendermint node with the provide RequestQuery.
553+// It returns the ResultQuery obtained from the query.
554+func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) {
555+ return ctx.queryABCI(req)
556+}
557+
558 // QuerySubspace performs a query to a Tendermint node with the provided
559 // store name and subspace. It returns key value pair and height of the query
560 // upon success or an error if the query fails.
561@@ -72,40 +78,50 @@ func (ctx CLIContext) GetFromName() string {
562 return ctx.FromName
563 }
564
565-// query performs a query to a Tendermint node with the provided store name
566-// and path. It returns the result and height of the query upon success
567-// or an error if the query fails. In addition, it will verify the returned
568-// proof if TrustNode is disabled. If proof verification fails or the query
569-// height is invalid, an error will be returned.
570-func (ctx CLIContext) query(path string, key tmbytes.HexBytes) (res []byte, height int64, err error) {
571+func (ctx CLIContext) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) {
572 node, err := ctx.GetNode()
573 if err != nil {
574- return res, height, err
575+ return abci.ResponseQuery{}, err
576 }
577
578 opts := rpcclient.ABCIQueryOptions{
579 Height: ctx.Height,
580- Prove: !ctx.TrustNode,
581+ Prove: req.Prove || !ctx.TrustNode,
582 }
583
584- result, err := node.ABCIQueryWithOptions(path, key, opts)
585+ result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts)
586 if err != nil {
587- return res, height, err
588+ return abci.ResponseQuery{}, err
589 }
590
591- resp := result.Response
592- if !resp.IsOK() {
593- return res, resp.Height, errors.New(resp.Log)
594+ if !result.Response.IsOK() {
595+ return abci.ResponseQuery{}, errors.New(result.Response.Log)
596 }
597
598 // data from trusted node or subspace query doesn't need verification
599- if ctx.TrustNode || !isQueryStoreWithProof(path) {
600- return resp.Value, resp.Height, nil
601+ if !opts.Prove || !isQueryStoreWithProof(req.Path) {
602+ return result.Response, nil
603 }
604
605- err = ctx.verifyProof(path, resp)
606+ if err := ctx.verifyProof(req.Path, result.Response); err != nil {
607+ return abci.ResponseQuery{}, err
608+ }
609+
610+ return result.Response, nil
611+}
612+
613+// query performs a query to a Tendermint node with the provided store name
614+// and path. It returns the result and height of the query upon success
615+// or an error if the query fails. In addition, it will verify the returned
616+// proof if TrustNode is disabled. If proof verification fails or the query
617+// height is invalid, an error will be returned.
618+func (ctx CLIContext) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) {
619+ resp, err := ctx.queryABCI(abci.RequestQuery{
620+ Path: path,
621+ Data: key,
622+ })
623 if err != nil {
624- return res, resp.Height, err
625+ return nil, 0, err
626 }
627
628 return resp.Value, resp.Height, nil
629@@ -116,7 +132,9 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) {
630 if ctx.Verifier == nil {
631 return tmtypes.SignedHeader{}, fmt.Errorf("missing valid certifier to verify data from distrusted node")
632 }
633+
634 check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier)
635+
636 switch {
637 case tmliteErr.IsErrCommitNotFound(err):
638 return tmtypes.SignedHeader{}, ErrVerifyCommit(height)
639@@ -159,8 +177,8 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err
640 }
641 return nil
642 }
643- err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value)
644- if err != nil {
645+
646+ if err := prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value); err != nil {
647 return errors.Wrap(err, "failed to prove merkle proof")
648 }
649
650@@ -183,6 +201,7 @@ func isQueryStoreWithProof(path string) bool {
651 }
652
653 paths := strings.SplitN(path[1:], "/", 3)
654+
655 switch {
656 case len(paths) != 3:
657 return false
658@@ -202,6 +221,7 @@ func parseQueryStorePath(path string) (storeName string, err error) {
659 }
660
661 paths := strings.SplitN(path[1:], "/", 3)
662+
663 switch {
664 case len(paths) != 3:
665 return "", errors.New("expected format like /store/<storeName>/key")
666diff --git a/client/flags/flags.go b/client/flags/flags.go
667index 2cdaa5f21..64dc2f25a 100644
668--- a/client/flags/flags.go
669+++ b/client/flags/flags.go
670@@ -24,7 +24,9 @@ const (
671
672 // DefaultKeyringBackend
673 DefaultKeyringBackend = keys.BackendOS
674+)
675
676+const (
677 // BroadcastBlock defines a tx broadcasting mode where the client waits for
678 // the tx to be committed in a block.
679 BroadcastBlock = "block"
680@@ -34,7 +36,10 @@ const (
681 // BroadcastAsync defines a tx broadcasting mode where the client returns
682 // immediately.
683 BroadcastAsync = "async"
684+)
685
686+// List of CLI flags
687+const (
688 FlagHome = tmcli.HomeFlag
689 FlagUseLedger = "ledger"
690 FlagChainID = "chain-id"
691@@ -59,6 +64,7 @@ const (
692 FlagRPCWriteTimeout = "write-timeout"
693 FlagOutputDocument = "output-document" // inspired by wget -O
694 FlagSkipConfirmation = "yes"
695+ FlagProve = "prove"
696 FlagKeyringBackend = "keyring-backend"
697 FlagPage = "page"
698 FlagLimit = "limit"
699diff --git a/client/keys/parse.go b/client/keys/parse.go
700index 79ccca739..858b71db1 100644
701--- a/client/keys/parse.go
702+++ b/client/keys/parse.go
703@@ -1,9 +1,11 @@
704 package keys
705
706 import (
707+ "context"
708 "encoding/hex"
709 "errors"
710 "fmt"
711+ "io"
712 "strings"
713
714 "github.com/spf13/cobra"
715@@ -17,14 +19,15 @@ import (
716 sdk "github.com/cosmos/cosmos-sdk/types"
717 )
718
719-var config = sdk.GetConfig()
720-var bech32Prefixes = []string{
721- config.GetBech32AccountAddrPrefix(),
722- config.GetBech32AccountPubPrefix(),
723- config.GetBech32ValidatorAddrPrefix(),
724- config.GetBech32ValidatorPubPrefix(),
725- config.GetBech32ConsensusAddrPrefix(),
726- config.GetBech32ConsensusPubPrefix(),
727+func bech32Prefixes(config *sdk.Config) []string {
728+ return []string{
729+ config.GetBech32AccountAddrPrefix(),
730+ config.GetBech32AccountPubPrefix(),
731+ config.GetBech32ValidatorAddrPrefix(),
732+ config.GetBech32ValidatorPubPrefix(),
733+ config.GetBech32ConsensusAddrPrefix(),
734+ config.GetBech32ConsensusPubPrefix(),
735+ }
736 }
737
738 type hexOutput struct {
739@@ -44,13 +47,16 @@ type bech32Output struct {
740 Formats []string `json:"formats"`
741 }
742
743-func newBech32Output(bs []byte) bech32Output {
744+func newBech32Output(config *sdk.Config, bs []byte) bech32Output {
745+ bech32Prefixes := bech32Prefixes(config)
746 out := bech32Output{Formats: make([]string, len(bech32Prefixes))}
747+
748 for i, prefix := range bech32Prefixes {
749 bech32Addr, err := bech32.ConvertAndEncode(prefix, bs)
750 if err != nil {
751 panic(err)
752 }
753+
754 out.Formats[i] = bech32Addr
755 }
756
757@@ -83,38 +89,52 @@ hexadecimal into bech32 cosmos prefixed format and vice versa.
758 return cmd
759 }
760
761-func parseKey(_ *cobra.Command, args []string) error {
762+func parseKey(cmd *cobra.Command, args []string) error {
763+ config, _ := sdk.GetSealedConfig(context.Background())
764+
765+ return doParseKey(cmd, config, args)
766+}
767+
768+func doParseKey(cmd *cobra.Command, config *sdk.Config, args []string) error {
769 addr := strings.TrimSpace(args[0])
770+ outstream := cmd.OutOrStdout()
771+
772 if len(addr) == 0 {
773 return errors.New("couldn't parse empty input")
774 }
775- if !(runFromBech32(addr) || runFromHex(addr)) {
776+
777+ if !(runFromBech32(outstream, addr) || runFromHex(config, outstream, addr)) {
778 return errors.New("couldn't find valid bech32 nor hex data")
779 }
780+
781 return nil
782 }
783
784 // print info from bech32
785-func runFromBech32(bech32str string) bool {
786+func runFromBech32(w io.Writer, bech32str string) bool {
787 hrp, bz, err := bech32.DecodeAndConvert(bech32str)
788 if err != nil {
789 return false
790 }
791- displayParseKeyInfo(newHexOutput(hrp, bz))
792+
793+ displayParseKeyInfo(w, newHexOutput(hrp, bz))
794+
795 return true
796 }
797
798 // print info from hex
799-func runFromHex(hexstr string) bool {
800+func runFromHex(config *sdk.Config, w io.Writer, hexstr string) bool {
801 bz, err := hex.DecodeString(hexstr)
802 if err != nil {
803 return false
804 }
805- displayParseKeyInfo(newBech32Output(bz))
806+
807+ displayParseKeyInfo(w, newBech32Output(config, bz))
808+
809 return true
810 }
811
812-func displayParseKeyInfo(stringer fmt.Stringer) {
813+func displayParseKeyInfo(w io.Writer, stringer fmt.Stringer) {
814 var out []byte
815 var err error
816
817@@ -136,5 +156,5 @@ func displayParseKeyInfo(stringer fmt.Stringer) {
818 panic(err)
819 }
820
821- fmt.Println(string(out))
822+ _, _ = fmt.Fprintln(w, string(out))
823 }
824diff --git a/client/keys/parse_test.go b/client/keys/parse_test.go
825index 759deb386..4c226cdff 100644
826--- a/client/keys/parse_test.go
827+++ b/client/keys/parse_test.go
828@@ -3,6 +3,7 @@ package keys
829 import (
830 "testing"
831
832+ sdk "github.com/cosmos/cosmos-sdk/types"
833 "github.com/stretchr/testify/require"
834 )
835
836@@ -10,6 +11,8 @@ func TestParseKey(t *testing.T) {
837 bech32str := "cosmos104ytdpvrx9284zd50v9ep8c6j7pua7dkk0x3ek"
838 hexstr := "EB5AE9872103497EC092EF901027049E4F39200C60040D3562CD7F104A39F62E6E5A39A818F4"
839
840+ config := sdk.NewConfig()
841+
842 tests := []struct {
843 name string
844 args []string
845@@ -23,7 +26,7 @@ func TestParseKey(t *testing.T) {
846 for _, tt := range tests {
847 tt := tt
848 t.Run(tt.name, func(t *testing.T) {
849- require.Equal(t, tt.wantErr, parseKey(nil, tt.args) != nil)
850+ require.Equal(t, tt.wantErr, doParseKey(ParseKeyStringCommand(), config, tt.args) != nil)
851 })
852 }
853 }
854diff --git a/go.mod b/go.mod
855index 319e7e8bb..43d37e1a2 100644
856--- a/go.mod
857+++ b/go.mod
858@@ -22,14 +22,15 @@ require (
859 github.com/spf13/jwalterweatherman v1.1.0 // indirect
860 github.com/spf13/pflag v1.0.5
861 github.com/spf13/viper v1.6.3
862- github.com/stretchr/testify v1.5.1
863+ github.com/stretchr/testify v1.6.1
864 github.com/tendermint/btcd v0.1.1
865 github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
866 github.com/tendermint/go-amino v0.15.1
867- github.com/tendermint/iavl v0.13.2
868+ github.com/tendermint/iavl v0.14.0
869 github.com/tendermint/tendermint v0.33.6
870 github.com/tendermint/tm-db v0.5.1
871- gopkg.in/yaml.v2 v2.2.8
872+ google.golang.org/grpc v1.30.0 // indirect
873+ gopkg.in/yaml.v2 v2.3.0
874 )
875
876 replace github.com/keybase/go-keychain => github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4
877diff --git a/go.sum b/go.sum
878index ac6e00772..f826ee5ea 100644
879--- a/go.sum
880+++ b/go.sum
881@@ -6,7 +6,6 @@ github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2Bylw
882 github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904=
883 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
884 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
885-github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
886 github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
887 github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
888 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
889@@ -103,7 +102,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
890 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
891 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
892 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
893-github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
894 github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
895 github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
896 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
897@@ -149,7 +147,6 @@ github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a
898 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
899 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
900 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
901-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
902 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
903 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
904 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
905@@ -180,7 +177,6 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
906 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
907 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
908 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
909-github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
910 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
911 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
912 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
913@@ -332,7 +328,6 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
914 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
915 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
916 github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
917-github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
918 github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
919 github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
920 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
921@@ -386,7 +381,6 @@ github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd
922 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
923 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
924 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
925-github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
926 github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
927 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
928 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
929@@ -397,7 +391,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
930 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
931 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
932 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
933-github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
934 github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
935 github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
936 github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
937@@ -410,8 +403,9 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
938 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
939 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
940 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
941-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
942 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
943+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
944+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
945 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
946 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
947 github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs=
948@@ -425,13 +419,12 @@ github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM
949 github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso=
950 github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ=
951 github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
952-github.com/tendermint/iavl v0.13.2 h1:O1m08/Ciy53l9IYmf75uIRVvrNsfjEbre8u/yCu/oqk=
953-github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA=
954-github.com/tendermint/tendermint v0.33.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk=
955+github.com/tendermint/iavl v0.14.0 h1:Jkff+IFrXxRWtH9Jn/ga/2cxNnzMTv58xEKgCJsKUBg=
956+github.com/tendermint/iavl v0.14.0/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxablW5oJZ6sE=
957+github.com/tendermint/tendermint v0.33.5 h1:jYgRd9ImkzA9iOyhpmgreYsqSB6tpDa6/rXYPb8HKE8=
958+github.com/tendermint/tendermint v0.33.5/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM=
959 github.com/tendermint/tendermint v0.33.6 h1:W4UOsXY4ROJZ3TLLGVVv71VXD4WK2gJRb3gzeced+mg=
960 github.com/tendermint/tendermint v0.33.6/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM=
961-github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY=
962-github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U=
963 github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY=
964 github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4=
965 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
966@@ -467,8 +460,9 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
967 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
968 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
969 golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
970-golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
971 golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
972+golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
973+golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
974 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
975 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
976 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
977@@ -572,10 +566,10 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
978 google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
979 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
980 google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
981-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
982 google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
983-google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k=
984 google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
985+google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
986+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
987 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
988 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
989 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
990@@ -603,8 +597,10 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
991 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
992 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
993 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
994-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
995-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
996+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
997+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
998+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
999+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
1000 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1001 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1002 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1003diff --git a/server/config/config.go b/server/config/config.go
1004index 9752ae852..f046bbe56 100644
1005--- a/server/config/config.go
1006+++ b/server/config/config.go
1007@@ -4,7 +4,7 @@ import (
1008 "fmt"
1009 "strings"
1010
1011- "github.com/cosmos/cosmos-sdk/store"
1012+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
1013 sdk "github.com/cosmos/cosmos-sdk/types"
1014 )
1015
1016@@ -19,6 +19,11 @@ type BaseConfig struct {
1017 // specified in this config (e.g. 0.25token1;0.0001token2).
1018 MinGasPrices string `mapstructure:"minimum-gas-prices"`
1019
1020+ Pruning string `mapstructure:"pruning"`
1021+ PruningKeepRecent string `mapstructure:"pruning-keep-recent"`
1022+ PruningKeepEvery string `mapstructure:"pruning-keep-every"`
1023+ PruningInterval string `mapstructure:"pruning-interval"`
1024+
1025 // HaltHeight contains a non-zero block height at which a node will gracefully
1026 // halt and shutdown that can be used to assist upgrades and testing.
1027 //
1028@@ -34,8 +39,6 @@ type BaseConfig struct {
1029
1030 // InterBlockCache enables inter-block caching.
1031 InterBlockCache bool `mapstructure:"inter-block-cache"`
1032-
1033- Pruning string `mapstructure:"pruning"`
1034 }
1035
1036 // Config defines the server's top level configuration
1037@@ -73,10 +76,13 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins {
1038 // DefaultConfig returns server's default configuration.
1039 func DefaultConfig() *Config {
1040 return &Config{
1041- BaseConfig{
1042- MinGasPrices: defaultMinGasPrices,
1043- InterBlockCache: true,
1044- Pruning: store.PruningStrategySyncable,
1045+ BaseConfig: BaseConfig{
1046+ MinGasPrices: defaultMinGasPrices,
1047+ InterBlockCache: true,
1048+ Pruning: storetypes.PruningOptionDefault,
1049+ PruningKeepRecent: "0",
1050+ PruningKeepEvery: "0",
1051+ PruningInterval: "0",
1052 },
1053 }
1054 }
1055diff --git a/server/config/toml.go b/server/config/toml.go
1056index e556ed576..6fb3f166f 100644
1057--- a/server/config/toml.go
1058+++ b/server/config/toml.go
1059@@ -18,6 +18,17 @@ const defaultConfigTemplate = `# This is a TOML config file.
1060 # specified in this config (e.g. 0.25token1;0.0001token2).
1061 minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"
1062
1063+# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
1064+# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
1065+# everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals
1066+# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
1067+pruning = "{{ .BaseConfig.Pruning }}"
1068+
1069+# These are applied if and only if the pruning strategy is custom.
1070+pruning-keep-recent = "{{ .BaseConfig.PruningKeepRecent }}"
1071+pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}"
1072+pruning-interval = "{{ .BaseConfig.PruningInterval }}"
1073+
1074 # HaltHeight contains a non-zero block height at which a node will gracefully
1075 # halt and shutdown that can be used to assist upgrades and testing.
1076 #
1077@@ -33,12 +44,6 @@ halt-time = {{ .BaseConfig.HaltTime }}
1078
1079 # InterBlockCache enables inter-block caching.
1080 inter-block-cache = {{ .BaseConfig.InterBlockCache }}
1081-
1082-# Pruning sets the pruning strategy: syncable, nothing, everything
1083-# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th)
1084-# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
1085-# everything: all saved states will be deleted, storing only the current state
1086-pruning = "{{ .BaseConfig.Pruning }}"
1087 `
1088
1089 var configTemplate *template.Template
1090diff --git a/server/pruning.go b/server/pruning.go
1091new file mode 100644
1092index 000000000..26497f4b8
1093--- /dev/null
1094+++ b/server/pruning.go
1095@@ -0,0 +1,38 @@
1096+package server
1097+
1098+import (
1099+ "fmt"
1100+ "strings"
1101+
1102+ "github.com/spf13/viper"
1103+
1104+ "github.com/cosmos/cosmos-sdk/store"
1105+ "github.com/cosmos/cosmos-sdk/store/types"
1106+)
1107+
1108+// GetPruningOptionsFromFlags parses command flags and returns the correct
1109+// PruningOptions. If a pruning strategy is provided, that will be parsed and
1110+// returned, otherwise, it is assumed custom pruning options are provided.
1111+func GetPruningOptionsFromFlags() (types.PruningOptions, error) {
1112+ strategy := strings.ToLower(viper.GetString(FlagPruning))
1113+
1114+ switch strategy {
1115+ case types.PruningOptionDefault, types.PruningOptionNothing, types.PruningOptionEverything:
1116+ return types.NewPruningOptionsFromString(strategy), nil
1117+
1118+ case types.PruningOptionCustom:
1119+ opts := types.NewPruningOptions(
1120+ viper.GetUint64(FlagPruningKeepRecent),
1121+ viper.GetUint64(FlagPruningKeepEvery), viper.GetUint64(FlagPruningInterval),
1122+ )
1123+
1124+ if err := opts.Validate(); err != nil {
1125+ return opts, fmt.Errorf("invalid custom pruning options: %w", err)
1126+ }
1127+
1128+ return opts, nil
1129+
1130+ default:
1131+ return store.PruningOptions{}, fmt.Errorf("unknown pruning strategy %s", strategy)
1132+ }
1133+}
1134diff --git a/server/pruning_test.go b/server/pruning_test.go
1135new file mode 100644
1136index 000000000..d05f2d2c2
1137--- /dev/null
1138+++ b/server/pruning_test.go
1139@@ -0,0 +1,64 @@
1140+package server
1141+
1142+import (
1143+ "testing"
1144+
1145+ "github.com/spf13/viper"
1146+ "github.com/stretchr/testify/require"
1147+
1148+ "github.com/cosmos/cosmos-sdk/store/types"
1149+)
1150+
1151+func TestGetPruningOptionsFromFlags(t *testing.T) {
1152+ tests := []struct {
1153+ name string
1154+ initParams func()
1155+ expectedOptions types.PruningOptions
1156+ wantErr bool
1157+ }{
1158+ {
1159+ name: FlagPruning,
1160+ initParams: func() {
1161+ viper.Set(FlagPruning, types.PruningOptionNothing)
1162+ },
1163+ expectedOptions: types.PruneNothing,
1164+ },
1165+ {
1166+ name: "custom pruning options",
1167+ initParams: func() {
1168+ viper.Set(FlagPruning, types.PruningOptionCustom)
1169+ viper.Set(FlagPruningKeepRecent, 1234)
1170+ viper.Set(FlagPruningKeepEvery, 4321)
1171+ viper.Set(FlagPruningInterval, 10)
1172+ },
1173+ expectedOptions: types.PruningOptions{
1174+ KeepRecent: 1234,
1175+ KeepEvery: 4321,
1176+ Interval: 10,
1177+ },
1178+ },
1179+ {
1180+ name: types.PruningOptionDefault,
1181+ initParams: func() {},
1182+ expectedOptions: types.PruneDefault,
1183+ },
1184+ }
1185+
1186+ for _, tt := range tests {
1187+ tt := tt
1188+
1189+ t.Run(tt.name, func(j *testing.T) {
1190+ viper.Reset()
1191+ viper.SetDefault(FlagPruning, types.PruningOptionDefault)
1192+ tt.initParams()
1193+
1194+ opts, err := GetPruningOptionsFromFlags()
1195+ if tt.wantErr {
1196+ require.Error(t, err)
1197+ return
1198+ }
1199+
1200+ require.Equal(t, tt.expectedOptions, opts)
1201+ })
1202+ }
1203+}
1204diff --git a/server/start.go b/server/start.go
1205index fc8aa0219..ff1725b67 100644
1206--- a/server/start.go
1207+++ b/server/start.go
1208@@ -16,6 +16,8 @@ import (
1209 "github.com/tendermint/tendermint/p2p"
1210 pvm "github.com/tendermint/tendermint/privval"
1211 "github.com/tendermint/tendermint/proxy"
1212+
1213+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
1214 )
1215
1216 // Tendermint full-node start flags
1217@@ -23,13 +25,18 @@ const (
1218 flagWithTendermint = "with-tendermint"
1219 flagAddress = "address"
1220 flagTraceStore = "trace-store"
1221- flagPruning = "pruning"
1222 flagCPUProfile = "cpu-profile"
1223 FlagMinGasPrices = "minimum-gas-prices"
1224 FlagHaltHeight = "halt-height"
1225 FlagHaltTime = "halt-time"
1226 FlagInterBlockCache = "inter-block-cache"
1227 FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades"
1228+ FlagTrace = "trace"
1229+
1230+ FlagPruning = "pruning"
1231+ FlagPruningKeepRecent = "pruning-keep-recent"
1232+ FlagPruningKeepEvery = "pruning-keep-every"
1233+ FlagPruningInterval = "pruning-interval"
1234 )
1235
1236 // StartCmd runs the service passed in, either stand-alone or in-process with
1237@@ -41,11 +48,15 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command {
1238 Long: `Run the full node application with Tendermint in or out of process. By
1239 default, the application will run with Tendermint in process.
1240
1241-Pruning options can be provided via the '--pruning' flag. The options are as follows:
1242+Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent',
1243+'pruning-keep-every', and 'pruning-interval' together.
1244+
1245+For '--pruning' the options are as follows:
1246
1247-syncable: only those states not needed for state syncing will be deleted (flushes every 100th to disk and keeps every 10000th)
1248+default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
1249 nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
1250-everything: all saved states will be deleted, storing only the current state
1251+everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals
1252+custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
1253
1254 Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During
1255 the ABCI Commit phase, the node will check if the current block height is greater than or equal to
1256@@ -56,6 +67,10 @@ will not be able to commit subsequent blocks.
1257 For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag
1258 which accepts a path for the resulting pprof file.
1259 `,
1260+ PreRunE: func(cmd *cobra.Command, args []string) error {
1261+ _, err := GetPruningOptionsFromFlags()
1262+ return err
1263+ },
1264 RunE: func(cmd *cobra.Command, args []string) error {
1265 if !viper.GetBool(flagWithTendermint) {
1266 ctx.Logger.Info("starting ABCI without Tendermint")
1267@@ -73,7 +88,7 @@ which accepts a path for the resulting pprof file.
1268 cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint")
1269 cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
1270 cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file")
1271- cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything")
1272+ cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log")
1273 cmd.Flags().String(
1274 FlagMinGasPrices, "",
1275 "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)",
1276@@ -84,6 +99,16 @@ which accepts a path for the resulting pprof file.
1277 cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching")
1278 cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file")
1279
1280+ cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)")
1281+ cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')")
1282+ cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')")
1283+ cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')")
1284+ viper.BindPFlag(FlagTrace, cmd.Flags().Lookup(FlagTrace))
1285+ viper.BindPFlag(FlagPruning, cmd.Flags().Lookup(FlagPruning))
1286+ viper.BindPFlag(FlagPruningKeepRecent, cmd.Flags().Lookup(FlagPruningKeepRecent))
1287+ viper.BindPFlag(FlagPruningKeepEvery, cmd.Flags().Lookup(FlagPruningKeepEvery))
1288+ viper.BindPFlag(FlagPruningInterval, cmd.Flags().Lookup(FlagPruningInterval))
1289+
1290 // add support for all Tendermint-specific command line options
1291 tcmd.AddNodeFlags(cmd)
1292 return cmd
1293diff --git a/store/cache/cache_test.go b/store/cache/cache_test.go
1294index 3fc5f9157..93ece7508 100644
1295--- a/store/cache/cache_test.go
1296+++ b/store/cache/cache_test.go
1297@@ -20,7 +20,7 @@ func TestGetOrSetStoreCache(t *testing.T) {
1298 sKey := types.NewKVStoreKey("test")
1299 tree, err := iavl.NewMutableTree(db, 100)
1300 require.NoError(t, err)
1301- store := iavlstore.UnsafeNewStore(tree, types.PruneNothing)
1302+ store := iavlstore.UnsafeNewStore(tree)
1303 store2 := mngr.GetStoreCache(sKey, store)
1304
1305 require.NotNil(t, store2)
1306@@ -34,7 +34,7 @@ func TestUnwrap(t *testing.T) {
1307 sKey := types.NewKVStoreKey("test")
1308 tree, err := iavl.NewMutableTree(db, 100)
1309 require.NoError(t, err)
1310- store := iavlstore.UnsafeNewStore(tree, types.PruneNothing)
1311+ store := iavlstore.UnsafeNewStore(tree)
1312 _ = mngr.GetStoreCache(sKey, store)
1313
1314 require.Equal(t, store, mngr.Unwrap(sKey))
1315@@ -48,7 +48,7 @@ func TestStoreCache(t *testing.T) {
1316 sKey := types.NewKVStoreKey("test")
1317 tree, err := iavl.NewMutableTree(db, 100)
1318 require.NoError(t, err)
1319- store := iavlstore.UnsafeNewStore(tree, types.PruneNothing)
1320+ store := iavlstore.UnsafeNewStore(tree)
1321 kvStore := mngr.GetStoreCache(sKey, store)
1322
1323 for i := uint(0); i < cache.DefaultCommitKVStoreCacheSize*2; i++ {
1324diff --git a/store/iavl/store.go b/store/iavl/store.go
1325index d1e1d9c4c..f5af4099b 100644
1326--- a/store/iavl/store.go
1327+++ b/store/iavl/store.go
1328@@ -1,11 +1,9 @@
1329 package iavl
1330
1331 import (
1332- "fmt"
1333 "io"
1334 "sync"
1335
1336- "github.com/pkg/errors"
1337 "github.com/tendermint/iavl"
1338 abci "github.com/tendermint/tendermint/abci/types"
1339 "github.com/tendermint/tendermint/crypto/merkle"
1340@@ -31,39 +29,14 @@ var (
1341
1342 // Store Implements types.KVStore and CommitKVStore.
1343 type Store struct {
1344- tree Tree
1345- pruning types.PruningOptions
1346+ tree Tree
1347 }
1348
1349 // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the
1350 // store's version (id) from the provided DB. An error is returned if the version
1351 // fails to load.
1352-func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyLoading bool) (types.CommitKVStore, error) {
1353- if !pruning.IsValid() {
1354- return nil, fmt.Errorf("pruning options are invalid: %v", pruning)
1355- }
1356-
1357- var keepRecent int64
1358-
1359- // Determine the value of keepRecent based on the following:
1360- //
1361- // If KeepEvery = 1, keepRecent should be 0 since there is no need to keep
1362- // latest version in a in-memory cache.
1363- //
1364- // If KeepEvery > 1, keepRecent should be 1 so that state changes in between
1365- // flushed states can be saved in the in-memory latest tree.
1366- if pruning.KeepEvery == 1 {
1367- keepRecent = 0
1368- } else {
1369- keepRecent = 1
1370- }
1371-
1372- tree, err := iavl.NewMutableTreeWithOpts(
1373- db,
1374- dbm.NewMemDB(),
1375- defaultIAVLCacheSize,
1376- iavl.PruningOptions(pruning.KeepEvery, keepRecent),
1377- )
1378+func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) {
1379+ tree, err := iavl.NewMutableTree(db, defaultIAVLCacheSize)
1380 if err != nil {
1381 return nil, err
1382 }
1383@@ -79,8 +52,7 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL
1384 }
1385
1386 return &Store{
1387- tree: tree,
1388- pruning: pruning,
1389+ tree: tree,
1390 }, nil
1391 }
1392
1393@@ -90,10 +62,9 @@ func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions, lazyL
1394 // CONTRACT: The IAVL tree should be fully loaded.
1395 // CONTRACT: PruningOptions passed in as argument must be the same as pruning options
1396 // passed into iavl.MutableTree
1397-func UnsafeNewStore(tree *iavl.MutableTree, po types.PruningOptions) *Store {
1398+func UnsafeNewStore(tree *iavl.MutableTree) *Store {
1399 return &Store{
1400- tree: tree,
1401- pruning: po,
1402+ tree: tree,
1403 }
1404 }
1405
1406@@ -113,8 +84,7 @@ func (st *Store) GetImmutable(version int64) (*Store, error) {
1407 }
1408
1409 return &Store{
1410- tree: &immutableTree{iTree},
1411- pruning: st.pruning,
1412+ tree: &immutableTree{iTree},
1413 }, nil
1414 }
1415
1416@@ -123,25 +93,9 @@ func (st *Store) GetImmutable(version int64) (*Store, error) {
1417 func (st *Store) Commit() types.CommitID {
1418 hash, version, err := st.tree.SaveVersion()
1419 if err != nil {
1420- // TODO: Do we want to extend Commit to allow returning errors?
1421 panic(err)
1422 }
1423
1424- // If the version we saved got flushed to disk, check if previous flushed
1425- // version should be deleted.
1426- if st.pruning.FlushVersion(version) {
1427- previous := version - st.pruning.KeepEvery
1428-
1429- // Previous flushed version should only be pruned if the previous version is
1430- // not a snapshot version OR if snapshotting is disabled (SnapshotEvery == 0).
1431- if previous != 0 && !st.pruning.SnapshotVersion(previous) {
1432- err := st.tree.DeleteVersion(previous)
1433- if errCause := errors.Cause(err); errCause != nil && errCause != iavl.ErrVersionDoesNotExist {
1434- panic(err)
1435- }
1436- }
1437- }
1438-
1439 return types.CommitID{
1440 Version: version,
1441 Hash: hash,
1442@@ -204,6 +158,13 @@ func (st *Store) Delete(key []byte) {
1443 st.tree.Remove(key)
1444 }
1445
1446+// DeleteVersions deletes a series of versions from the MutableTree. An error
1447+// is returned if any single version is invalid or the delete fails. All writes
1448+// happen in a single batch with a single commit.
1449+func (st *Store) DeleteVersions(versions ...int64) error {
1450+ return st.tree.DeleteVersions(versions...)
1451+}
1452+
1453 // Implements types.KVStore.
1454 func (st *Store) Iterator(start, end []byte) types.Iterator {
1455 var iTree *iavl.ImmutableTree
1456diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go
1457index ed138c849..7a65c1880 100644
1458--- a/store/iavl/store_test.go
1459+++ b/store/iavl/store_test.go
1460@@ -52,7 +52,7 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) {
1461 func TestGetImmutable(t *testing.T) {
1462 db := dbm.NewMemDB()
1463 tree, cID := newAlohaTree(t, db)
1464- store := UnsafeNewStore(tree, types.PruneNothing)
1465+ store := UnsafeNewStore(tree)
1466
1467 require.True(t, tree.Set([]byte("hello"), []byte("adios")))
1468 hash, ver, err := tree.SaveVersion()
1469@@ -82,7 +82,7 @@ func TestGetImmutable(t *testing.T) {
1470 func TestTestGetImmutableIterator(t *testing.T) {
1471 db := dbm.NewMemDB()
1472 tree, cID := newAlohaTree(t, db)
1473- store := UnsafeNewStore(tree, types.PruneNothing)
1474+ store := UnsafeNewStore(tree)
1475
1476 newStore, err := store.GetImmutable(cID.Version)
1477 require.NoError(t, err)
1478@@ -105,7 +105,7 @@ func TestTestGetImmutableIterator(t *testing.T) {
1479 func TestIAVLStoreGetSetHasDelete(t *testing.T) {
1480 db := dbm.NewMemDB()
1481 tree, _ := newAlohaTree(t, db)
1482- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1483+ iavlStore := UnsafeNewStore(tree)
1484
1485 key := "hello"
1486
1487@@ -130,14 +130,14 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) {
1488 func TestIAVLStoreNoNilSet(t *testing.T) {
1489 db := dbm.NewMemDB()
1490 tree, _ := newAlohaTree(t, db)
1491- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1492+ iavlStore := UnsafeNewStore(tree)
1493 require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic")
1494 }
1495
1496 func TestIAVLIterator(t *testing.T) {
1497 db := dbm.NewMemDB()
1498 tree, _ := newAlohaTree(t, db)
1499- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1500+ iavlStore := UnsafeNewStore(tree)
1501 iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz"))
1502 expected := []string{"aloha", "hello"}
1503 var i int
1504@@ -213,7 +213,7 @@ func TestIAVLReverseIterator(t *testing.T) {
1505 tree, err := iavl.NewMutableTree(db, cacheSize)
1506 require.NoError(t, err)
1507
1508- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1509+ iavlStore := UnsafeNewStore(tree)
1510
1511 iavlStore.Set([]byte{0x00}, []byte("0"))
1512 iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0"))
1513@@ -246,7 +246,7 @@ func TestIAVLPrefixIterator(t *testing.T) {
1514 tree, err := iavl.NewMutableTree(db, cacheSize)
1515 require.NoError(t, err)
1516
1517- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1518+ iavlStore := UnsafeNewStore(tree)
1519
1520 iavlStore.Set([]byte("test1"), []byte("test1"))
1521 iavlStore.Set([]byte("test2"), []byte("test2"))
1522@@ -310,7 +310,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) {
1523 tree, err := iavl.NewMutableTree(db, cacheSize)
1524 require.NoError(t, err)
1525
1526- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1527+ iavlStore := UnsafeNewStore(tree)
1528
1529 iavlStore.Set([]byte("test1"), []byte("test1"))
1530 iavlStore.Set([]byte("test2"), []byte("test2"))
1531@@ -373,95 +373,12 @@ func nextVersion(iavl *Store) {
1532 iavl.Commit()
1533 }
1534
1535-func TestIAVLDefaultPruning(t *testing.T) {
1536- //Expected stored / deleted version numbers for:
1537- //numRecent = 5, storeEvery = 3, snapshotEvery = 5
1538- var states = []pruneState{
1539- {[]int64{}, []int64{}},
1540- {[]int64{1}, []int64{}},
1541- {[]int64{1, 2}, []int64{}},
1542- {[]int64{1, 2, 3}, []int64{}},
1543- {[]int64{1, 2, 3, 4}, []int64{}},
1544- {[]int64{1, 2, 3, 4, 5}, []int64{}},
1545- {[]int64{2, 4, 5, 6}, []int64{1, 3}},
1546- {[]int64{4, 5, 6, 7}, []int64{1, 2, 3}},
1547- {[]int64{4, 5, 6, 7, 8}, []int64{1, 2, 3}},
1548- {[]int64{5, 6, 7, 8, 9}, []int64{1, 2, 3, 4}},
1549- {[]int64{6, 7, 8, 9, 10}, []int64{1, 2, 3, 4, 5}},
1550- {[]int64{6, 7, 8, 9, 10, 11}, []int64{1, 2, 3, 4, 5}},
1551- {[]int64{6, 8, 10, 11, 12}, []int64{1, 2, 3, 4, 5, 7, 9}},
1552- {[]int64{6, 10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 7, 8, 9}},
1553- {[]int64{6, 10, 11, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 7, 8, 9}},
1554- {[]int64{6, 11, 12, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 7, 8, 9, 10}},
1555- }
1556- testPruning(t, int64(5), int64(3), int64(6), states)
1557-}
1558-
1559-func TestIAVLAlternativePruning(t *testing.T) {
1560- //Expected stored / deleted version numbers for:
1561- //numRecent = 3, storeEvery = 5, snapshotEvery = 10
1562- var states = []pruneState{
1563- {[]int64{}, []int64{}},
1564- {[]int64{1}, []int64{}},
1565- {[]int64{1, 2}, []int64{}},
1566- {[]int64{1, 2, 3}, []int64{}},
1567- {[]int64{2, 3, 4}, []int64{1}},
1568- {[]int64{3, 4, 5}, []int64{1, 2}},
1569- {[]int64{4, 5, 6}, []int64{1, 2, 3}},
1570- {[]int64{5, 6, 7}, []int64{1, 2, 3, 4}},
1571- {[]int64{5, 6, 7, 8}, []int64{1, 2, 3, 4}},
1572- {[]int64{5, 7, 8, 9}, []int64{1, 2, 3, 4, 6}},
1573- {[]int64{8, 9, 10}, []int64{1, 2, 3, 4, 6, 7}},
1574- {[]int64{9, 10, 11}, []int64{1, 2, 3, 4, 6, 7, 8}},
1575- {[]int64{10, 11, 12}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}},
1576- {[]int64{10, 11, 12, 13}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}},
1577- {[]int64{10, 12, 13, 14}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11}},
1578- {[]int64{10, 13, 14, 15}, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12}},
1579- }
1580- testPruning(t, int64(3), int64(5), int64(10), states)
1581-}
1582-
1583-type pruneState struct {
1584- stored []int64
1585- deleted []int64
1586-}
1587-
1588-func testPruning(t *testing.T, numRecent int64, storeEvery int64, snapshotEvery int64, states []pruneState) {
1589- db := dbm.NewMemDB()
1590- pruningOpts := types.PruningOptions{
1591- KeepEvery: storeEvery,
1592- SnapshotEvery: snapshotEvery,
1593- }
1594- iavlOpts := iavl.PruningOptions(storeEvery, numRecent)
1595-
1596- tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts)
1597- require.NoError(t, err)
1598-
1599- iavlStore := UnsafeNewStore(tree, pruningOpts)
1600-
1601- for step, state := range states {
1602- for _, ver := range state.stored {
1603- require.True(t, iavlStore.VersionExists(ver),
1604- "missing version %d with latest version %d; should save last %d, store every %d, and snapshot every %d",
1605- ver, step, numRecent, storeEvery, snapshotEvery)
1606- }
1607-
1608- for _, ver := range state.deleted {
1609- require.False(t, iavlStore.VersionExists(ver),
1610- "not pruned version %d with latest version %d; should prune all but last %d and every %d with intermediate flush interval %d",
1611- ver, step, numRecent, snapshotEvery, storeEvery)
1612- }
1613-
1614- nextVersion(iavlStore)
1615- }
1616-}
1617-
1618 func TestIAVLNoPrune(t *testing.T) {
1619 db := dbm.NewMemDB()
1620 tree, err := iavl.NewMutableTree(db, cacheSize)
1621 require.NoError(t, err)
1622
1623- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1624+ iavlStore := UnsafeNewStore(tree)
1625 nextVersion(iavlStore)
1626
1627 for i := 1; i < 100; i++ {
1628@@ -475,37 +392,12 @@ func TestIAVLNoPrune(t *testing.T) {
1629 }
1630 }
1631
1632-func TestIAVLPruneEverything(t *testing.T) {
1633- db := dbm.NewMemDB()
1634- iavlOpts := iavl.PruningOptions(0, 1) // only store latest version in memory
1635-
1636- tree, err := iavl.NewMutableTreeWithOpts(db, dbm.NewMemDB(), cacheSize, iavlOpts)
1637- require.NoError(t, err)
1638-
1639- iavlStore := UnsafeNewStore(tree, types.PruneEverything)
1640- nextVersion(iavlStore)
1641-
1642- for i := 1; i < 100; i++ {
1643- for j := 1; j < i; j++ {
1644- require.False(t, iavlStore.VersionExists(int64(j)),
1645- "not pruned version %d with latest version %d; should prune all old versions",
1646- j, i)
1647- }
1648-
1649- require.True(t, iavlStore.VersionExists(int64(i)),
1650- "missing current version on step %d; should not prune current state tree",
1651- i)
1652-
1653- nextVersion(iavlStore)
1654- }
1655-}
1656-
1657 func TestIAVLStoreQuery(t *testing.T) {
1658 db := dbm.NewMemDB()
1659 tree, err := iavl.NewMutableTree(db, cacheSize)
1660 require.NoError(t, err)
1661
1662- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1663+ iavlStore := UnsafeNewStore(tree)
1664
1665 k1, v1 := []byte("key1"), []byte("val1")
1666 k2, v2 := []byte("key2"), []byte("val2")
1667@@ -604,7 +496,7 @@ func BenchmarkIAVLIteratorNext(b *testing.B) {
1668 tree.Set(key, value)
1669 }
1670
1671- iavlStore := UnsafeNewStore(tree, types.PruneNothing)
1672+ iavlStore := UnsafeNewStore(tree)
1673 iterators := make([]types.Iterator, b.N/treeSize)
1674
1675 for i := 0; i < len(iterators); i++ {
1676diff --git a/store/iavl/tree.go b/store/iavl/tree.go
1677index deae294b2..9873034fa 100644
1678--- a/store/iavl/tree.go
1679+++ b/store/iavl/tree.go
1680@@ -23,6 +23,7 @@ type (
1681 Remove(key []byte) ([]byte, bool)
1682 SaveVersion() ([]byte, int64, error)
1683 DeleteVersion(version int64) error
1684+ DeleteVersions(versions ...int64) error
1685 Version() int64
1686 Hash() []byte
1687 VersionExists(version int64) bool
1688@@ -55,6 +56,10 @@ func (it *immutableTree) DeleteVersion(_ int64) error {
1689 panic("cannot call 'DeleteVersion' on an immutable IAVL tree")
1690 }
1691
1692+func (it *immutableTree) DeleteVersions(_ ...int64) error {
1693+ panic("cannot call 'DeleteVersions' on an immutable IAVL tree")
1694+}
1695+
1696 func (it *immutableTree) VersionExists(version int64) bool {
1697 return it.Version() == version
1698 }
1699diff --git a/store/prefix/store_test.go b/store/prefix/store_test.go
1700index eb1f0a4d4..a4f8e6bbc 100644
1701--- a/store/prefix/store_test.go
1702+++ b/store/prefix/store_test.go
1703@@ -86,7 +86,7 @@ func TestIAVLStorePrefix(t *testing.T) {
1704 db := dbm.NewMemDB()
1705 tree, err := tiavl.NewMutableTree(db, cacheSize)
1706 require.NoError(t, err)
1707- iavlStore := iavl.UnsafeNewStore(tree, types.PruneNothing)
1708+ iavlStore := iavl.UnsafeNewStore(tree)
1709
1710 testPrefixStore(t, iavlStore, []byte("test"))
1711 }
1712diff --git a/store/reexport.go b/store/reexport.go
1713index 9e4dba0ed..61a3d9f79 100644
1714--- a/store/reexport.go
1715+++ b/store/reexport.go
1716@@ -30,10 +30,3 @@ type (
1717 GasMeter = types.GasMeter
1718 GasConfig = types.GasConfig
1719 )
1720-
1721-// nolint - reexport
1722-var (
1723- PruneNothing = types.PruneNothing
1724- PruneEverything = types.PruneEverything
1725- PruneSyncable = types.PruneSyncable
1726-)
1727diff --git a/store/rootmulti/proof_test.go b/store/rootmulti/proof_test.go
1728index 4d316f2c8..676fc777c 100644
1729--- a/store/rootmulti/proof_test.go
1730+++ b/store/rootmulti/proof_test.go
1731@@ -14,7 +14,7 @@ import (
1732 func TestVerifyIAVLStoreQueryProof(t *testing.T) {
1733 // Create main tree for testing.
1734 db := dbm.NewMemDB()
1735- iStore, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false)
1736+ iStore, err := iavl.LoadStore(db, types.CommitID{}, false)
1737 store := iStore.(*iavl.Store)
1738 require.Nil(t, err)
1739 store.Set([]byte("MYKEY"), []byte("MYVALUE"))
1740diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go
1741index 42c7b05ab..b0dc4c269 100644
1742--- a/store/rootmulti/store.go
1743+++ b/store/rootmulti/store.go
1744@@ -5,6 +5,8 @@ import (
1745 "io"
1746 "strings"
1747
1748+ "github.com/pkg/errors"
1749+ iavltree "github.com/tendermint/iavl"
1750 abci "github.com/tendermint/tendermint/abci/types"
1751 "github.com/tendermint/tendermint/crypto/merkle"
1752 "github.com/tendermint/tendermint/crypto/tmhash"
1753@@ -21,6 +23,7 @@ import (
1754
1755 const (
1756 latestVersionKey = "s/latest"
1757+ pruneHeightsKey = "s/pruneheights"
1758 commitInfoKeyFmt = "s/%d" // s/<version>
1759 )
1760
1761@@ -35,6 +38,7 @@ type Store struct {
1762 stores map[types.StoreKey]types.CommitKVStore
1763 keysByName map[string]types.StoreKey
1764 lazyLoading bool
1765+ pruneHeights []int64
1766
1767 traceWriter io.Writer
1768 traceContext types.TraceContext
1769@@ -42,8 +46,10 @@ type Store struct {
1770 interBlockCache types.MultiStorePersistentCache
1771 }
1772
1773-var _ types.CommitMultiStore = (*Store)(nil)
1774-var _ types.Queryable = (*Store)(nil)
1775+var (
1776+ _ types.CommitMultiStore = (*Store)(nil)
1777+ _ types.Queryable = (*Store)(nil)
1778+)
1779
1780 // NewStore returns a reference to a new Store object with the provided DB. The
1781 // store will be created with a PruneNothing pruning strategy by default. After
1782@@ -56,20 +62,15 @@ func NewStore(db dbm.DB) *Store {
1783 storesParams: make(map[types.StoreKey]storeParams),
1784 stores: make(map[types.StoreKey]types.CommitKVStore),
1785 keysByName: make(map[string]types.StoreKey),
1786+ pruneHeights: make([]int64, 0),
1787 }
1788 }
1789
1790 // SetPruning sets the pruning strategy on the root store and all the sub-stores.
1791 // Note, calling SetPruning on the root store prior to LoadVersion or
1792 // LoadLatestVersion performs a no-op as the stores aren't mounted yet.
1793-//
1794-// TODO: Consider removing this API altogether on sub-stores as a pruning
1795-// strategy should only be provided on initialization.
1796 func (rs *Store) SetPruning(pruningOpts types.PruningOptions) {
1797 rs.pruningOpts = pruningOpts
1798- for _, substore := range rs.stores {
1799- substore.SetPruning(pruningOpts)
1800- }
1801 }
1802
1803 // SetLazyLoading sets if the iavl store should be loaded lazily or not
1804@@ -200,6 +201,12 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error {
1805 rs.lastCommitInfo = cInfo
1806 rs.stores = newStores
1807
1808+ // load any pruned heights we missed from disk to be pruned on the next run
1809+ ph, err := getPruningHeights(rs.db)
1810+ if err == nil && len(ph) > 0 {
1811+ rs.pruneHeights = ph
1812+ }
1813+
1814 return nil
1815 }
1816
1817@@ -286,22 +293,59 @@ func (rs *Store) LastCommitID() types.CommitID {
1818
1819 // Implements Committer/CommitStore.
1820 func (rs *Store) Commit() types.CommitID {
1821-
1822- // Commit stores.
1823- version := rs.lastCommitInfo.Version + 1
1824+ previousHeight := rs.lastCommitInfo.Version
1825+ version := previousHeight + 1
1826 rs.lastCommitInfo = commitStores(version, rs.stores)
1827
1828- // write CommitInfo to disk only if this version was flushed to disk
1829- if rs.pruningOpts.FlushVersion(version) {
1830- flushCommitInfo(rs.db, version, rs.lastCommitInfo)
1831+ // Determine if pruneHeight height needs to be added to the list of heights to
1832+ // be pruned, where pruneHeight = (commitHeight - 1) - KeepRecent.
1833+ if int64(rs.pruningOpts.KeepRecent) < previousHeight {
1834+ pruneHeight := previousHeight - int64(rs.pruningOpts.KeepRecent)
1835+ // We consider this height to be pruned iff:
1836+ //
1837+ // - KeepEvery is zero as that means that all heights should be pruned.
1838+ // - KeepEvery % (height - KeepRecent) != 0 as that means the height is not
1839+ // a 'snapshot' height.
1840+ if rs.pruningOpts.KeepEvery == 0 || pruneHeight%int64(rs.pruningOpts.KeepEvery) != 0 {
1841+ rs.pruneHeights = append(rs.pruneHeights, pruneHeight)
1842+ }
1843+ }
1844+
1845+ // batch prune if the current height is a pruning interval height
1846+ if rs.pruningOpts.Interval > 0 && version%int64(rs.pruningOpts.Interval) == 0 {
1847+ rs.pruneStores()
1848 }
1849
1850- // Prepare for next version.
1851- commitID := types.CommitID{
1852+ flushMetadata(rs.db, version, rs.lastCommitInfo, rs.pruneHeights)
1853+
1854+ return types.CommitID{
1855 Version: version,
1856 Hash: rs.lastCommitInfo.Hash(),
1857 }
1858- return commitID
1859+}
1860+
1861+// pruneStores will batch delete a list of heights from each mounted sub-store.
1862+// Afterwards, pruneHeights is reset.
1863+func (rs *Store) pruneStores() {
1864+ if len(rs.pruneHeights) == 0 {
1865+ return
1866+ }
1867+
1868+ for key, store := range rs.stores {
1869+ if store.GetStoreType() == types.StoreTypeIAVL {
1870+ // If the store is wrapped with an inter-block cache, we must first unwrap
1871+ // it to get the underlying IAVL store.
1872+ store = rs.GetCommitKVStore(key)
1873+
1874+ if err := store.(*iavl.Store).DeleteVersions(rs.pruneHeights...); err != nil {
1875+ if errCause := errors.Cause(err); errCause != nil && errCause != iavltree.ErrVersionDoesNotExist {
1876+ panic(err)
1877+ }
1878+ }
1879+ }
1880+ }
1881+
1882+ rs.pruneHeights = make([]int64, 0)
1883 }
1884
1885 // Implements CacheWrapper/Store/CommitStore.
1886@@ -480,8 +524,6 @@ func parsePath(path string) (storeName string, subpath string, err error) {
1887 return storeName, subpath, nil
1888 }
1889
1890-//----------------------------------------
1891-// Note: why do we use key and params.key in different places. Seems like there should be only one key used.
1892 func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (types.CommitKVStore, error) {
1893 var db dbm.DB
1894
1895@@ -497,7 +539,7 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID
1896 panic("recursive MultiStores not yet supported")
1897
1898 case types.StoreTypeIAVL:
1899- store, err := iavl.LoadStore(db, id, rs.pruningOpts, rs.lazyLoading)
1900+ store, err := iavl.LoadStore(db, id, rs.lazyLoading)
1901 if err != nil {
1902 return nil, err
1903 }
1904@@ -620,12 +662,6 @@ func getLatestVersion(db dbm.DB) int64 {
1905 return latest
1906 }
1907
1908-// Set the latest version.
1909-func setLatestVersion(batch dbm.Batch, version int64) {
1910- latestBytes, _ := cdc.MarshalBinaryLengthPrefixed(version)
1911- batch.Set([]byte(latestVersionKey), latestBytes)
1912-}
1913-
1914 // Commits each store and returns a new commitInfo.
1915 func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) commitInfo {
1916 storeInfos := make([]storeInfo, 0, len(storeMap))
1917@@ -651,9 +687,8 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore
1918
1919 // Gets commitInfo from disk.
1920 func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) {
1921-
1922- // Get from DB.
1923 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver)
1924+
1925 cInfoBytes, err := db.Get([]byte(cInfoKey))
1926 if err != nil {
1927 return commitInfo{}, fmt.Errorf("failed to get commit info: %v", err)
1928@@ -671,20 +706,48 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) {
1929 return cInfo, nil
1930 }
1931
1932-// Set a commitInfo for given version.
1933 func setCommitInfo(batch dbm.Batch, version int64, cInfo commitInfo) {
1934 cInfoBytes := cdc.MustMarshalBinaryLengthPrefixed(cInfo)
1935 cInfoKey := fmt.Sprintf(commitInfoKeyFmt, version)
1936 batch.Set([]byte(cInfoKey), cInfoBytes)
1937 }
1938
1939-// flushCommitInfo flushes a commitInfo for given version to the DB. Note, this
1940-// needs to happen atomically.
1941-func flushCommitInfo(db dbm.DB, version int64, cInfo commitInfo) {
1942+func setLatestVersion(batch dbm.Batch, version int64) {
1943+ latestBytes := cdc.MustMarshalBinaryLengthPrefixed(version)
1944+ batch.Set([]byte(latestVersionKey), latestBytes)
1945+}
1946+
1947+func setPruningHeights(batch dbm.Batch, pruneHeights []int64) {
1948+ bz := cdc.MustMarshalBinaryBare(pruneHeights)
1949+ batch.Set([]byte(pruneHeightsKey), bz)
1950+}
1951+
1952+func getPruningHeights(db dbm.DB) ([]int64, error) {
1953+ bz, err := db.Get([]byte(pruneHeightsKey))
1954+ if err != nil {
1955+ return nil, fmt.Errorf("failed to get pruned heights: %w", err)
1956+ }
1957+ if len(bz) == 0 {
1958+ return nil, errors.New("no pruned heights found")
1959+ }
1960+
1961+ var prunedHeights []int64
1962+ if err := cdc.UnmarshalBinaryBare(bz, &prunedHeights); err != nil {
1963+ return nil, fmt.Errorf("failed to unmarshal pruned heights: %w", err)
1964+ }
1965+
1966+ return prunedHeights, nil
1967+}
1968+
1969+func flushMetadata(db dbm.DB, version int64, cInfo commitInfo, pruneHeights []int64) {
1970 batch := db.NewBatch()
1971 defer batch.Close()
1972
1973 setCommitInfo(batch, version, cInfo)
1974 setLatestVersion(batch, version)
1975- batch.Write()
1976+ setPruningHeights(batch, pruneHeights)
1977+
1978+ if err := batch.Write(); err != nil {
1979+ panic(fmt.Errorf("error on batch write %w", err))
1980+ }
1981 }
1982diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go
1983index 164f231a2..ddf876f45 100644
1984--- a/store/rootmulti/store_test.go
1985+++ b/store/rootmulti/store_test.go
1986@@ -22,7 +22,7 @@ func TestStoreType(t *testing.T) {
1987
1988 func TestGetCommitKVStore(t *testing.T) {
1989 var db dbm.DB = dbm.NewMemDB()
1990- ms := newMultiStoreWithMounts(db, types.PruneSyncable)
1991+ ms := newMultiStoreWithMounts(db, types.PruneDefault)
1992 err := ms.LoadLatestVersion()
1993 require.Nil(t, err)
1994
1995@@ -166,7 +166,7 @@ func TestMultistoreCommitLoad(t *testing.T) {
1996
1997 // XXX: confirm old commit is overwritten and we have rolled back
1998 // LatestVersion
1999- store = newMultiStoreWithMounts(db, types.PruneSyncable)
2000+ store = newMultiStoreWithMounts(db, types.PruneDefault)
2001 err = store.LoadLatestVersion()
2002 require.Nil(t, err)
2003 commitID = getExpectedCommitID(store, ver+1)
2004@@ -293,8 +293,9 @@ func TestParsePath(t *testing.T) {
2005 func TestMultiStoreRestart(t *testing.T) {
2006 db := dbm.NewMemDB()
2007 pruning := types.PruningOptions{
2008- KeepEvery: 3,
2009- SnapshotEvery: 6,
2010+ KeepRecent: 2,
2011+ KeepEvery: 3,
2012+ Interval: 1,
2013 }
2014 multi := newMultiStoreWithMounts(db, pruning)
2015 err := multi.LoadLatestVersion()
2016@@ -322,8 +323,8 @@ func TestMultiStoreRestart(t *testing.T) {
2017 multi.Commit()
2018
2019 cinfo, err := getCommitInfo(multi.db, int64(i))
2020- require.NotNil(t, err)
2021- require.Equal(t, commitInfo{}, cinfo)
2022+ require.NoError(t, err)
2023+ require.Equal(t, int64(i), cinfo.Version)
2024 }
2025
2026 // Set and commit data in one store.
2027@@ -347,15 +348,15 @@ func TestMultiStoreRestart(t *testing.T) {
2028 multi.Commit()
2029
2030 postFlushCinfo, err := getCommitInfo(multi.db, 4)
2031- require.NotNil(t, err)
2032- require.Equal(t, commitInfo{}, postFlushCinfo, "Commit changed after in-memory commit")
2033+ require.NoError(t, err)
2034+ require.Equal(t, int64(4), postFlushCinfo.Version, "Commit changed after in-memory commit")
2035
2036 multi = newMultiStoreWithMounts(db, pruning)
2037 err = multi.LoadLatestVersion()
2038 require.Nil(t, err)
2039
2040 reloadedCid := multi.LastCommitID()
2041- require.Equal(t, flushedCinfo.CommitID(), reloadedCid, "Reloaded CID is not the same as last flushed CID")
2042+ require.Equal(t, int64(4), reloadedCid.Version, "Reloaded CID is not the same as last flushed CID")
2043
2044 // Check that store1 and store2 retained date from 3rd commit
2045 store1 = multi.getStoreByName("store1").(types.KVStore)
2046@@ -369,7 +370,7 @@ func TestMultiStoreRestart(t *testing.T) {
2047 // Check that store3 still has data from last commit even though update happened on 2nd commit
2048 store3 = multi.getStoreByName("store3").(types.KVStore)
2049 val3 := store3.Get([]byte(k3))
2050- require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 2)), val3, "Reloaded value not the same as last flushed value")
2051+ require.Equal(t, []byte(fmt.Sprintf("%s:%d", v3, 3)), val3, "Reloaded value not the same as last flushed value")
2052 }
2053
2054 func TestMultiStoreQuery(t *testing.T) {
2055@@ -442,6 +443,80 @@ func TestMultiStoreQuery(t *testing.T) {
2056 require.Equal(t, v2, qres.Value)
2057 }
2058
2059+func TestMultiStore_Pruning(t *testing.T) {
2060+ testCases := []struct {
2061+ name string
2062+ numVersions int64
2063+ po types.PruningOptions
2064+ deleted []int64
2065+ saved []int64
2066+ }{
2067+ {"prune nothing", 10, types.PruneNothing, nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
2068+ {"prune everything", 10, types.PruneEverything, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int64{10}},
2069+ {"prune some; no batch", 10, types.NewPruningOptions(2, 3, 1), []int64{1, 2, 4, 5, 7}, []int64{3, 6, 8, 9, 10}},
2070+ {"prune some; small batch", 10, types.NewPruningOptions(2, 3, 3), []int64{1, 2, 4, 5}, []int64{3, 6, 7, 8, 9, 10}},
2071+ {"prune some; large batch", 10, types.NewPruningOptions(2, 3, 11), nil, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
2072+ }
2073+
2074+ for _, tc := range testCases {
2075+ tc := tc
2076+
2077+ t.Run(tc.name, func(t *testing.T) {
2078+ db := dbm.NewMemDB()
2079+ ms := newMultiStoreWithMounts(db, tc.po)
2080+ require.NoError(t, ms.LoadLatestVersion())
2081+
2082+ for i := int64(0); i < tc.numVersions; i++ {
2083+ ms.Commit()
2084+ }
2085+
2086+ for _, v := range tc.saved {
2087+ _, err := ms.CacheMultiStoreWithVersion(v)
2088+ require.NoError(t, err, "expected error when loading height: %d", v)
2089+ }
2090+
2091+ for _, v := range tc.deleted {
2092+ _, err := ms.CacheMultiStoreWithVersion(v)
2093+ require.Error(t, err, "expected error when loading height: %d", v)
2094+ }
2095+ })
2096+ }
2097+}
2098+
2099+func TestMultiStore_PruningRestart(t *testing.T) {
2100+ db := dbm.NewMemDB()
2101+ ms := newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11))
2102+ require.NoError(t, ms.LoadLatestVersion())
2103+
2104+ // Commit enough to build up heights to prune, where on the next block we should
2105+ // batch delete.
2106+ for i := int64(0); i < 10; i++ {
2107+ ms.Commit()
2108+ }
2109+
2110+ pruneHeights := []int64{1, 2, 4, 5, 7}
2111+
2112+ // ensure we've persisted the current batch of heights to prune to the store's DB
2113+ ph, err := getPruningHeights(ms.db)
2114+ require.NoError(t, err)
2115+ require.Equal(t, pruneHeights, ph)
2116+
2117+ // "restart"
2118+ ms = newMultiStoreWithMounts(db, types.NewPruningOptions(2, 3, 11))
2119+ err = ms.LoadLatestVersion()
2120+ require.NoError(t, err)
2121+ require.Equal(t, pruneHeights, ms.pruneHeights)
2122+
2123+ // commit one more block and ensure the heights have been pruned
2124+ ms.Commit()
2125+ require.Empty(t, ms.pruneHeights)
2126+
2127+ for _, v := range pruneHeights {
2128+ _, err := ms.CacheMultiStoreWithVersion(v)
2129+ require.Error(t, err, "expected error when loading height: %d", v)
2130+ }
2131+}
2132+
2133 //-----------------------------------------------------------------------
2134 // utils
2135
2136diff --git a/store/store.go b/store/store.go
2137index ba0311763..2c068c413 100644
2138--- a/store/store.go
2139+++ b/store/store.go
2140@@ -8,13 +8,6 @@ import (
2141 "github.com/cosmos/cosmos-sdk/store/types"
2142 )
2143
2144-// Pruning strategies that may be provided to a KVStore to enable pruning.
2145-const (
2146- PruningStrategyNothing = "nothing"
2147- PruningStrategyEverything = "everything"
2148- PruningStrategySyncable = "syncable"
2149-)
2150-
2151 func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore {
2152 return rootmulti.NewStore(db)
2153 }
2154@@ -22,17 +15,3 @@ func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore {
2155 func NewCommitKVStoreCacheManager() types.MultiStorePersistentCache {
2156 return cache.NewCommitKVStoreCacheManager(cache.DefaultCommitKVStoreCacheSize)
2157 }
2158-
2159-func NewPruningOptionsFromString(strategy string) (opt PruningOptions) {
2160- switch strategy {
2161- case PruningStrategyNothing:
2162- opt = PruneNothing
2163- case PruningStrategyEverything:
2164- opt = PruneEverything
2165- case PruningStrategySyncable:
2166- opt = PruneSyncable
2167- default:
2168- opt = PruneSyncable
2169- }
2170- return
2171-}
2172diff --git a/store/types/iterator_test.go b/store/types/iterator_test.go
2173index e081de80a..3086917b6 100644
2174--- a/store/types/iterator_test.go
2175+++ b/store/types/iterator_test.go
2176@@ -12,7 +12,7 @@ import (
2177
2178 func newMemTestKVStore(t *testing.T) types.KVStore {
2179 db := dbm.NewMemDB()
2180- store, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing, false)
2181+ store, err := iavl.LoadStore(db, types.CommitID{}, false)
2182 require.NoError(t, err)
2183 return store
2184 }
2185diff --git a/store/types/pruning.go b/store/types/pruning.go
2186index c54044352..823da76c6 100644
2187--- a/store/types/pruning.go
2188+++ b/store/types/pruning.go
2189@@ -1,66 +1,77 @@
2190 package types
2191
2192+import "fmt"
2193+
2194+// Pruning option string constants
2195+const (
2196+ PruningOptionDefault = "default"
2197+ PruningOptionEverything = "everything"
2198+ PruningOptionNothing = "nothing"
2199+ PruningOptionCustom = "custom"
2200+)
2201+
2202 var (
2203- // PruneEverything defines a pruning strategy where all committed states will
2204- // be deleted, persisting only the current state.
2205- PruneEverything = PruningOptions{
2206- KeepEvery: 1,
2207- SnapshotEvery: 0,
2208- }
2209+ // PruneDefault defines a pruning strategy where the last 100 heights are kept
2210+ // in addition to every 100th and where to-be pruned heights are pruned at
2211+ // every 10th height.
2212+ PruneDefault = NewPruningOptions(100, 100, 10)
2213
2214- // PruneNothing defines a pruning strategy where all committed states will be
2215- // kept on disk, i.e. no states will be pruned.
2216- PruneNothing = PruningOptions{
2217- KeepEvery: 1,
2218- SnapshotEvery: 1,
2219- }
2220+ // PruneEverything defines a pruning strategy where all committed heights are
2221+ // deleted, storing only the current height and where to-be pruned heights are
2222+ // pruned at every 10th height.
2223+ PruneEverything = NewPruningOptions(0, 0, 10)
2224
2225- // PruneSyncable defines a pruning strategy where only those states not needed
2226- // for state syncing will be pruned. It flushes every 100th state to disk and
2227- // keeps every 10000th.
2228- PruneSyncable = PruningOptions{
2229- KeepEvery: 100,
2230- SnapshotEvery: 10000,
2231- }
2232+ // PruneNothing defines a pruning strategy where all heights are kept on disk.
2233+ PruneNothing = NewPruningOptions(0, 1, 0)
2234 )
2235
2236-// PruningOptions defines the specific pruning strategy every store in a multi-store
2237-// will use when committing state, where keepEvery determines which committed
2238-// heights are flushed to disk and snapshotEvery determines which of these heights
2239-// are kept after pruning.
2240+// PruningOptions defines the pruning strategy used when determining which
2241+// heights are removed from disk when committing state.
2242 type PruningOptions struct {
2243- KeepEvery int64
2244- SnapshotEvery int64
2245+ // KeepRecent defines how many recent heights to keep on disk.
2246+ KeepRecent uint64
2247+
2248+ // KeepEvery defines how many offset heights are kept on disk past KeepRecent.
2249+ KeepEvery uint64
2250+
2251+ // Interval defines when the pruned heights are removed from disk.
2252+ Interval uint64
2253 }
2254
2255-// IsValid verifies if the pruning options are valid. It returns false if invalid
2256-// and true otherwise. Pruning options are considered valid iff:
2257-//
2258-// - KeepEvery > 0
2259-// - SnapshotEvery >= 0
2260-// - SnapshotEvery % KeepEvery = 0
2261-func (po PruningOptions) IsValid() bool {
2262- // must flush at positive block interval
2263- if po.KeepEvery <= 0 {
2264- return false
2265+func NewPruningOptions(keepRecent, keepEvery, interval uint64) PruningOptions {
2266+ return PruningOptions{
2267+ KeepRecent: keepRecent,
2268+ KeepEvery: keepEvery,
2269+ Interval: interval,
2270 }
2271+}
2272
2273- // cannot snapshot negative intervals
2274- if po.SnapshotEvery < 0 {
2275- return false
2276+func (po PruningOptions) Validate() error {
2277+ if po.KeepEvery == 0 && po.Interval == 0 {
2278+ return fmt.Errorf("invalid 'Interval' when pruning everything: %d", po.Interval)
2279+ }
2280+ if po.KeepEvery == 1 && po.Interval != 0 { // prune nothing
2281+ return fmt.Errorf("invalid 'Interval' when pruning nothing: %d", po.Interval)
2282+ }
2283+ if po.KeepEvery > 1 && po.Interval == 0 {
2284+ return fmt.Errorf("invalid 'Interval' when pruning: %d", po.Interval)
2285 }
2286
2287- return po.SnapshotEvery%po.KeepEvery == 0
2288+ return nil
2289 }
2290
2291-// FlushVersion returns a boolean signaling if the provided version/height should
2292-// be flushed to disk.
2293-func (po PruningOptions) FlushVersion(ver int64) bool {
2294- return po.KeepEvery != 0 && ver%po.KeepEvery == 0
2295-}
2296+func NewPruningOptionsFromString(strategy string) PruningOptions {
2297+ switch strategy {
2298+ case PruningOptionEverything:
2299+ return PruneEverything
2300
2301-// SnapshotVersion returns a boolean signaling if the provided version/height
2302-// should be snapshotted (kept on disk).
2303-func (po PruningOptions) SnapshotVersion(ver int64) bool {
2304- return po.SnapshotEvery != 0 && ver%po.SnapshotEvery == 0
2305+ case PruningOptionNothing:
2306+ return PruneNothing
2307+
2308+ case PruningOptionDefault:
2309+ return PruneDefault
2310+
2311+ default:
2312+ return PruneDefault
2313+ }
2314 }
2315diff --git a/store/types/pruning_test.go b/store/types/pruning_test.go
2316new file mode 100644
2317index 000000000..2b88905ee
2318--- /dev/null
2319+++ b/store/types/pruning_test.go
2320@@ -0,0 +1,29 @@
2321+package types
2322+
2323+import (
2324+ "testing"
2325+
2326+ "github.com/stretchr/testify/require"
2327+)
2328+
2329+func TestPruningOptions_Validate(t *testing.T) {
2330+ testCases := []struct {
2331+ keepRecent uint64
2332+ keepEvery uint64
2333+ interval uint64
2334+ expectErr bool
2335+ }{
2336+ {100, 500, 10, false}, // default
2337+ {0, 0, 10, false}, // everything
2338+ {0, 1, 0, false}, // nothing
2339+ {0, 10, 10, false},
2340+ {100, 0, 0, true}, // invalid interval
2341+ {0, 1, 5, true}, // invalid interval
2342+ }
2343+
2344+ for _, tc := range testCases {
2345+ po := NewPruningOptions(tc.keepRecent, tc.keepEvery, tc.interval)
2346+ err := po.Validate()
2347+ require.Equal(t, tc.expectErr, err != nil, "options: %v, err: %s", po, err)
2348+ }
2349+}
2350diff --git a/store/types/store.go b/store/types/store.go
2351index 377b43909..400b3f64b 100644
2352--- a/store/types/store.go
2353+++ b/store/types/store.go
2354@@ -18,6 +18,8 @@ type Store interface { //nolint
2355 type Committer interface {
2356 Commit() CommitID
2357 LastCommitID() CommitID
2358+
2359+ // TODO: Deprecate after 0.38.5
2360 SetPruning(PruningOptions)
2361 }
2362
2363diff --git a/tests/mocks/account_retriever.go b/tests/mocks/account_retriever.go
2364index 9f8ee6928..a3086c77b 100644
2365--- a/tests/mocks/account_retriever.go
2366+++ b/tests/mocks/account_retriever.go
2367@@ -1,30 +1,38 @@
2368+// Code generated by MockGen. DO NOT EDIT.
2369+// Source: x/auth/types/account_retriever.go
2370+
2371+// Package mocks is a generated GoMock package.
2372 package mocks
2373
2374 import (
2375- reflect "reflect"
2376-
2377 gomock "github.com/golang/mock/gomock"
2378+ reflect "reflect"
2379 )
2380
2381+// MockNodeQuerier is a mock of NodeQuerier interface
2382 type MockNodeQuerier struct {
2383 ctrl *gomock.Controller
2384 recorder *MockNodeQuerierMockRecorder
2385 }
2386
2387+// MockNodeQuerierMockRecorder is the mock recorder for MockNodeQuerier
2388 type MockNodeQuerierMockRecorder struct {
2389 mock *MockNodeQuerier
2390 }
2391
2392+// NewMockNodeQuerier creates a new mock instance
2393 func NewMockNodeQuerier(ctrl *gomock.Controller) *MockNodeQuerier {
2394 mock := &MockNodeQuerier{ctrl: ctrl}
2395 mock.recorder = &MockNodeQuerierMockRecorder{mock}
2396 return mock
2397 }
2398
2399+// EXPECT returns an object that allows the caller to indicate expected use
2400 func (m *MockNodeQuerier) EXPECT() *MockNodeQuerierMockRecorder {
2401 return m.recorder
2402 }
2403
2404+// QueryWithData mocks base method
2405 func (m *MockNodeQuerier) QueryWithData(path string, data []byte) ([]byte, int64, error) {
2406 m.ctrl.T.Helper()
2407 ret := m.ctrl.Call(m, "QueryWithData", path, data)
2408@@ -34,6 +42,7 @@ func (m *MockNodeQuerier) QueryWithData(path string, data []byte) ([]byte, int64
2409 return ret0, ret1, ret2
2410 }
2411
2412+// QueryWithData indicates an expected call of QueryWithData
2413 func (mr *MockNodeQuerierMockRecorder) QueryWithData(path, data interface{}) *gomock.Call {
2414 mr.mock.ctrl.T.Helper()
2415 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueryWithData", reflect.TypeOf((*MockNodeQuerier)(nil).QueryWithData), path, data)
2416diff --git a/tests/mocks/types_handler.go b/tests/mocks/types_handler.go
2417new file mode 100644
2418index 000000000..da80614ae
2419--- /dev/null
2420+++ b/tests/mocks/types_handler.go
2421@@ -0,0 +1,49 @@
2422+// Code generated by MockGen. DO NOT EDIT.
2423+// Source: types/handler.go
2424+
2425+// Package mocks is a generated GoMock package.
2426+package mocks
2427+
2428+import (
2429+ types "github.com/cosmos/cosmos-sdk/types"
2430+ gomock "github.com/golang/mock/gomock"
2431+ reflect "reflect"
2432+)
2433+
2434+// MockAnteDecorator is a mock of AnteDecorator interface
2435+type MockAnteDecorator struct {
2436+ ctrl *gomock.Controller
2437+ recorder *MockAnteDecoratorMockRecorder
2438+}
2439+
2440+// MockAnteDecoratorMockRecorder is the mock recorder for MockAnteDecorator
2441+type MockAnteDecoratorMockRecorder struct {
2442+ mock *MockAnteDecorator
2443+}
2444+
2445+// NewMockAnteDecorator creates a new mock instance
2446+func NewMockAnteDecorator(ctrl *gomock.Controller) *MockAnteDecorator {
2447+ mock := &MockAnteDecorator{ctrl: ctrl}
2448+ mock.recorder = &MockAnteDecoratorMockRecorder{mock}
2449+ return mock
2450+}
2451+
2452+// EXPECT returns an object that allows the caller to indicate expected use
2453+func (m *MockAnteDecorator) EXPECT() *MockAnteDecoratorMockRecorder {
2454+ return m.recorder
2455+}
2456+
2457+// AnteHandle mocks base method
2458+func (m *MockAnteDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (types.Context, error) {
2459+ m.ctrl.T.Helper()
2460+ ret := m.ctrl.Call(m, "AnteHandle", ctx, tx, simulate, next)
2461+ ret0, _ := ret[0].(types.Context)
2462+ ret1, _ := ret[1].(error)
2463+ return ret0, ret1
2464+}
2465+
2466+// AnteHandle indicates an expected call of AnteHandle
2467+func (mr *MockAnteDecoratorMockRecorder) AnteHandle(ctx, tx, simulate, next interface{}) *gomock.Call {
2468+ mr.mock.ctrl.T.Helper()
2469+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AnteHandle", reflect.TypeOf((*MockAnteDecorator)(nil).AnteHandle), ctx, tx, simulate, next)
2470+}
2471diff --git a/tests/util.go b/tests/util.go
2472index f7c06d16f..45887d2ff 100644
2473--- a/tests/util.go
2474+++ b/tests/util.go
2475@@ -181,6 +181,7 @@ func WaitForStart(url string) {
2476
2477 // Wait for the RPC server to respond to /status
2478 func WaitForRPC(laddr string) {
2479+ fmt.Println("LADDR", laddr)
2480 client, err := tmjsonrpc.New(laddr)
2481 if err != nil {
2482 panic(fmt.Sprintf("failed to create Tendermint RPC client: %s", err))
2483diff --git a/types/config.go b/types/config.go
2484index 3e39fe30c..a3181703d 100644
2485--- a/types/config.go
2486+++ b/types/config.go
2487@@ -1,6 +1,7 @@
2488 package types
2489
2490 import (
2491+ "context"
2492 "sync"
2493
2494 "github.com/cosmos/cosmos-sdk/version"
2495@@ -19,19 +20,19 @@ type Config struct {
2496 mtx sync.RWMutex
2497 coinType uint32
2498 sealed bool
2499+ sealedch chan struct{}
2500 }
2501
2502 // cosmos-sdk wide global singleton
2503-var sdkConfig *Config
2504-
2505-// GetConfig returns the config instance for the SDK.
2506-func GetConfig() *Config {
2507- if sdkConfig != nil {
2508- return sdkConfig
2509- }
2510+var (
2511+ sdkConfig *Config
2512+ initConfig sync.Once
2513+)
2514
2515- sdkConfig = &Config{
2516- sealed: false,
2517+// New returns a new Config with default values.
2518+func NewConfig() *Config {
2519+ return &Config{
2520+ sealedch: make(chan struct{}),
2521 bech32AddressPrefix: map[string]string{
2522 "account_addr": Bech32PrefixAccAddr,
2523 "validator_addr": Bech32PrefixValAddr,
2524@@ -44,9 +45,27 @@ func GetConfig() *Config {
2525 fullFundraiserPath: FullFundraiserPath,
2526 txEncoder: nil,
2527 }
2528+}
2529+
2530+// GetConfig returns the config instance for the SDK.
2531+func GetConfig() *Config {
2532+ initConfig.Do(func() {
2533+ sdkConfig = NewConfig()
2534+ })
2535 return sdkConfig
2536 }
2537
2538+// GetSealedConfig returns the config instance for the SDK if/once it is sealed.
2539+func GetSealedConfig(ctx context.Context) (*Config, error) {
2540+ config := GetConfig()
2541+ select {
2542+ case <-config.sealedch:
2543+ return config, nil
2544+ case <-ctx.Done():
2545+ return nil, ctx.Err()
2546+ }
2547+}
2548+
2549 func (config *Config) assertNotSealed() {
2550 config.mtx.Lock()
2551 defer config.mtx.Unlock()
2552@@ -108,9 +127,17 @@ func (config *Config) SetFullFundraiserPath(fullFundraiserPath string) {
2553 // Seal seals the config such that the config state could not be modified further
2554 func (config *Config) Seal() *Config {
2555 config.mtx.Lock()
2556- defer config.mtx.Unlock()
2557
2558+ if config.sealed {
2559+ config.mtx.Unlock()
2560+ return config
2561+ }
2562+
2563+ // signal sealed after state exposed/unlocked
2564 config.sealed = true
2565+ config.mtx.Unlock()
2566+ close(config.sealedch)
2567+
2568 return config
2569 }
2570
2571diff --git a/types/config_test.go b/types/config_test.go
2572new file mode 100644
2573index 000000000..df208e32f
2574--- /dev/null
2575+++ b/types/config_test.go
2576@@ -0,0 +1,50 @@
2577+package types_test
2578+
2579+import (
2580+ "errors"
2581+ "testing"
2582+
2583+ "github.com/stretchr/testify/require"
2584+
2585+ sdk "github.com/cosmos/cosmos-sdk/types"
2586+)
2587+
2588+func TestConfig_SetCoinType(t *testing.T) {
2589+ config := sdk.NewConfig()
2590+ config.SetCoinType(1)
2591+ require.Equal(t, uint32(1), config.GetCoinType())
2592+ config.SetCoinType(99)
2593+ require.Equal(t, uint32(99), config.GetCoinType())
2594+
2595+ config.Seal()
2596+ require.Panics(t, func() { config.SetCoinType(99) })
2597+}
2598+
2599+func TestConfig_SetTxEncoder(t *testing.T) {
2600+ mockErr := errors.New("test")
2601+ config := sdk.NewConfig()
2602+ require.Nil(t, config.GetTxEncoder())
2603+ encFunc := sdk.TxEncoder(func(tx sdk.Tx) ([]byte, error) { return nil, nil })
2604+ config.SetTxEncoder(encFunc)
2605+ _, err := config.GetTxEncoder()(sdk.Tx(nil))
2606+ require.Error(t, mockErr, err)
2607+
2608+ config.Seal()
2609+ require.Panics(t, func() { config.SetTxEncoder(encFunc) })
2610+}
2611+
2612+func TestConfig_SetFullFundraiserPath(t *testing.T) {
2613+ config := sdk.NewConfig()
2614+ config.SetFullFundraiserPath("test/path")
2615+ require.Equal(t, "test/path", config.GetFullFundraiserPath())
2616+
2617+ config.SetFullFundraiserPath("test/poth")
2618+ require.Equal(t, "test/poth", config.GetFullFundraiserPath())
2619+
2620+ config.Seal()
2621+ require.Panics(t, func() { config.SetFullFundraiserPath("x/test/path") })
2622+}
2623+
2624+func TestKeyringServiceName(t *testing.T) {
2625+ require.Equal(t, sdk.DefaultKeyringServiceName, sdk.KeyringServiceName())
2626+}
2627diff --git a/types/errors/abci.go b/types/errors/abci.go
2628index cc5633543..a14719a9a 100644
2629--- a/types/errors/abci.go
2630+++ b/types/errors/abci.go
2631@@ -1,7 +1,6 @@
2632 package errors
2633
2634 import (
2635- "errors"
2636 "fmt"
2637 "reflect"
2638
2639@@ -18,8 +17,6 @@ const (
2640 // detailed error string.
2641 internalABCICodespace = UndefinedCodespace
2642 internalABCICode uint32 = 1
2643- internalABCILog string = "internal error"
2644- // multiErrorABCICode uint32 = 1000
2645 )
2646
2647 // ABCIInfo returns the ABCI error information as consumed by the tendermint
2648@@ -44,8 +41,8 @@ func ABCIInfo(err error, debug bool) (codespace string, code uint32, log string)
2649
2650 // ResponseCheckTx returns an ABCI ResponseCheckTx object with fields filled in
2651 // from the given error and gas values.
2652-func ResponseCheckTx(err error, gw, gu uint64) abci.ResponseCheckTx {
2653- space, code, log := ABCIInfo(err, false)
2654+func ResponseCheckTx(err error, gw, gu uint64, debug bool) abci.ResponseCheckTx {
2655+ space, code, log := ABCIInfo(err, debug)
2656 return abci.ResponseCheckTx{
2657 Codespace: space,
2658 Code: code,
2659@@ -57,8 +54,8 @@ func ResponseCheckTx(err error, gw, gu uint64) abci.ResponseCheckTx {
2660
2661 // ResponseDeliverTx returns an ABCI ResponseDeliverTx object with fields filled in
2662 // from the given error and gas values.
2663-func ResponseDeliverTx(err error, gw, gu uint64) abci.ResponseDeliverTx {
2664- space, code, log := ABCIInfo(err, false)
2665+func ResponseDeliverTx(err error, gw, gu uint64, debug bool) abci.ResponseDeliverTx {
2666+ space, code, log := ABCIInfo(err, debug)
2667 return abci.ResponseDeliverTx{
2668 Codespace: space,
2669 Code: code,
2670@@ -160,10 +157,10 @@ func errIsNil(err error) bool {
2671 // originates.
2672 func Redact(err error) error {
2673 if ErrPanic.Is(err) {
2674- return errors.New(internalABCILog)
2675+ return ErrPanic
2676 }
2677 if abciCode(err) == internalABCICode {
2678- return errors.New(internalABCILog)
2679+ return errInternal
2680 }
2681 return err
2682 }
2683diff --git a/types/errors/abci_test.go b/types/errors/abci_test.go
2684index c098811ea..1908e4f06 100644
2685--- a/types/errors/abci_test.go
2686+++ b/types/errors/abci_test.go
2687@@ -5,6 +5,8 @@ import (
2688 "io"
2689 "strings"
2690 "testing"
2691+
2692+ "github.com/stretchr/testify/require"
2693 )
2694
2695 func TestABCInfo(t *testing.T) {
2696@@ -46,7 +48,7 @@ func TestABCInfo(t *testing.T) {
2697 "stdlib is generic message": {
2698 err: io.EOF,
2699 debug: false,
2700- wantLog: "internal error",
2701+ wantLog: "internal",
2702 wantCode: 1,
2703 wantSpace: UndefinedCodespace,
2704 },
2705@@ -60,7 +62,7 @@ func TestABCInfo(t *testing.T) {
2706 "wrapped stdlib is only a generic message": {
2707 err: Wrap(io.EOF, "cannot read file"),
2708 debug: false,
2709- wantLog: "internal error",
2710+ wantLog: "internal",
2711 wantCode: 1,
2712 wantSpace: UndefinedCodespace,
2713 },
2714@@ -134,7 +136,7 @@ func TestABCIInfoStacktrace(t *testing.T) {
2715 err: Wrap(fmt.Errorf("stdlib"), "wrapped"),
2716 debug: false,
2717 wantStacktrace: false,
2718- wantErrMsg: "internal error",
2719+ wantErrMsg: "internal",
2720 },
2721 }
2722
2723@@ -169,21 +171,42 @@ func TestABCIInfoHidesStacktrace(t *testing.T) {
2724 }
2725
2726 func TestRedact(t *testing.T) {
2727- if err := Redact(ErrPanic); ErrPanic.Is(err) {
2728- t.Error("reduct must not pass through panic error")
2729- }
2730- if err := Redact(ErrUnauthorized); !ErrUnauthorized.Is(err) {
2731- t.Error("reduct should pass through SDK error")
2732- }
2733-
2734- var cerr customErr
2735- if err := Redact(cerr); err != cerr {
2736- t.Error("reduct should pass through ABCI code error")
2737+ cases := map[string]struct {
2738+ err error
2739+ untouched bool // if true we expect the same error after redact
2740+ changed error // if untouched == false, expect this error
2741+ }{
2742+ "panic looses message": {
2743+ err: Wrap(ErrPanic, "some secret stack trace"),
2744+ changed: ErrPanic,
2745+ },
2746+ "sdk errors untouched": {
2747+ err: Wrap(ErrUnauthorized, "cannot drop db"),
2748+ untouched: true,
2749+ },
2750+ "pass though custom errors with ABCI code": {
2751+ err: customErr{},
2752+ untouched: true,
2753+ },
2754+ "redact stdlib error": {
2755+ err: fmt.Errorf("stdlib error"),
2756+ changed: errInternal,
2757+ },
2758 }
2759
2760- serr := fmt.Errorf("stdlib error")
2761- if err := Redact(serr); err == serr {
2762- t.Error("reduct must not pass through a stdlib error")
2763+ for name, tc := range cases {
2764+ spec := tc
2765+ t.Run(name, func(t *testing.T) {
2766+ redacted := Redact(spec.err)
2767+ if spec.untouched {
2768+ require.Equal(t, spec.err, redacted)
2769+ } else {
2770+ // see if we got the expected redact
2771+ require.Equal(t, spec.changed, redacted)
2772+ // make sure the ABCI code did not change
2773+ require.Equal(t, abciCode(spec.err), abciCode(redacted))
2774+ }
2775+ })
2776 }
2777 }
2778
2779@@ -215,41 +238,15 @@ func TestABCIInfoSerializeErr(t *testing.T) {
2780 debug: true,
2781 exp: fmt.Sprintf("%+v", myErrDecode),
2782 },
2783- // "multi error default encoder": {
2784- // src: Append(myErrMsg, myErrAddr),
2785- // exp: Append(myErrMsg, myErrAddr).Error(),
2786- // },
2787- // "multi error default with internal": {
2788- // src: Append(myErrMsg, myPanic),
2789- // exp: "internal error",
2790- // },
2791 "redact in default encoder": {
2792 src: myPanic,
2793- exp: "internal error",
2794+ exp: "panic",
2795 },
2796 "do not redact in debug encoder": {
2797 src: myPanic,
2798 debug: true,
2799 exp: fmt.Sprintf("%+v", myPanic),
2800 },
2801- // "redact in multi error": {
2802- // src: Append(myPanic, myErrMsg),
2803- // debug: false,
2804- // exp: "internal error",
2805- // },
2806- // "no redact in multi error": {
2807- // src: Append(myPanic, myErrMsg),
2808- // debug: true,
2809- // exp: `2 errors occurred:
2810- // * panic
2811- // * test: invalid message
2812- // `,
2813- // },
2814- // "wrapped multi error with redact": {
2815- // src: Wrap(Append(myPanic, myErrMsg), "wrap"),
2816- // debug: false,
2817- // exp: "internal error",
2818- // },
2819 }
2820 for msg, spec := range specs {
2821 spec := spec
2822diff --git a/types/handler.go b/types/handler.go
2823index 6815230a8..03d1f02d5 100644
2824--- a/types/handler.go
2825+++ b/types/handler.go
2826@@ -25,15 +25,15 @@ type AnteDecorator interface {
2827 // MUST set GasMeter with the FIRST AnteDecorator. Failing to do so will cause
2828 // transactions to be processed with an infinite gasmeter and open a DOS attack vector.
2829 // Use `ante.SetUpContextDecorator` or a custom Decorator with similar functionality.
2830+// Returns nil when no AnteDecorator are supplied.
2831 func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler {
2832- if (chain[len(chain)-1] != Terminator{}) {
2833- chain = append(chain, Terminator{})
2834+ if len(chain) == 0 {
2835+ return nil
2836 }
2837
2838- if len(chain) == 1 {
2839- return func(ctx Context, tx Tx, simulate bool) (Context, error) {
2840- return chain[0].AnteHandle(ctx, tx, simulate, nil)
2841- }
2842+ // handle non-terminated decorators chain
2843+ if (chain[len(chain)-1] != Terminator{}) {
2844+ chain = append(chain, Terminator{})
2845 }
2846
2847 return func(ctx Context, tx Tx, simulate bool) (Context, error) {
2848diff --git a/types/handler_test.go b/types/handler_test.go
2849new file mode 100644
2850index 000000000..4847a1131
2851--- /dev/null
2852+++ b/types/handler_test.go
2853@@ -0,0 +1,28 @@
2854+package types_test
2855+
2856+import (
2857+ "testing"
2858+
2859+ "github.com/golang/mock/gomock"
2860+ "github.com/stretchr/testify/require"
2861+
2862+ "github.com/cosmos/cosmos-sdk/tests/mocks"
2863+ sdk "github.com/cosmos/cosmos-sdk/types"
2864+)
2865+
2866+func TestChainAnteDecorators(t *testing.T) {
2867+ t.Parallel()
2868+ // test panic
2869+ require.Nil(t, sdk.ChainAnteDecorators([]sdk.AnteDecorator{}...))
2870+
2871+ ctx, tx := sdk.Context{}, sdk.Tx(nil)
2872+ mockCtrl := gomock.NewController(t)
2873+ mockAnteDecorator1 := mocks.NewMockAnteDecorator(mockCtrl)
2874+ mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1)
2875+ sdk.ChainAnteDecorators(mockAnteDecorator1)(ctx, tx, true)
2876+
2877+ mockAnteDecorator2 := mocks.NewMockAnteDecorator(mockCtrl)
2878+ mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, mockAnteDecorator2).Times(1)
2879+ mockAnteDecorator2.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, nil).Times(1)
2880+ sdk.ChainAnteDecorators(mockAnteDecorator1, mockAnteDecorator2)
2881+}
2882diff --git a/types/result.go b/types/result.go
2883index 9530be01d..54cc0e053 100644
2884--- a/types/result.go
2885+++ b/types/result.go
2886@@ -17,7 +17,7 @@ type GasInfo struct {
2887 // GasWanted is the maximum units of work we allow this tx to perform.
2888 GasWanted uint64
2889
2890- // GasUsed is the amount of gas actually consumed. NOTE: unimplemented
2891+ // GasUsed is the amount of gas actually consumed.
2892 GasUsed uint64
2893 }
2894
2895@@ -35,6 +35,13 @@ type Result struct {
2896 Events Events
2897 }
2898
2899+// SimulationResponse defines the response generated when a transaction is successfully
2900+// simulated by the Baseapp.
2901+type SimulationResponse struct {
2902+ GasInfo
2903+ Result *Result
2904+}
2905+
2906 // ABCIMessageLogs represents a slice of ABCIMessageLog.
2907 type ABCIMessageLogs []ABCIMessageLog
2908
2909diff --git a/x/auth/ante/sigverify.go b/x/auth/ante/sigverify.go
2910index 994ad4378..eeeeee0ca 100644
2911--- a/x/auth/ante/sigverify.go
2912+++ b/x/auth/ante/sigverify.go
2913@@ -236,11 +236,6 @@ func NewIncrementSequenceDecorator(ak keeper.AccountKeeper) IncrementSequenceDec
2914 }
2915
2916 func (isd IncrementSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
2917- // no need to increment sequence on RecheckTx
2918- if ctx.IsReCheckTx() && !simulate {
2919- return next(ctx, tx, simulate)
2920- }
2921-
2922 sigTx, ok := tx.(SigVerifiableTx)
2923 if !ok {
2924 return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "invalid transaction type")
2925diff --git a/x/auth/ante/sigverify_test.go b/x/auth/ante/sigverify_test.go
2926index d12e98998..522f90ddb 100644
2927--- a/x/auth/ante/sigverify_test.go
2928+++ b/x/auth/ante/sigverify_test.go
2929@@ -235,11 +235,11 @@ func TestIncrementSequenceDecorator(t *testing.T) {
2930 simulate bool
2931 expectedSeq uint64
2932 }{
2933- {ctx.WithIsReCheckTx(true), false, 0},
2934- {ctx.WithIsCheckTx(true).WithIsReCheckTx(false), false, 1},
2935 {ctx.WithIsReCheckTx(true), false, 1},
2936- {ctx.WithIsReCheckTx(true), false, 1},
2937- {ctx.WithIsReCheckTx(true), true, 2},
2938+ {ctx.WithIsCheckTx(true).WithIsReCheckTx(false), false, 2},
2939+ {ctx.WithIsReCheckTx(true), false, 3},
2940+ {ctx.WithIsReCheckTx(true), false, 4},
2941+ {ctx.WithIsReCheckTx(true), true, 5},
2942 }
2943
2944 for i, tc := range testCases {
2945diff --git a/x/auth/client/utils/tx.go b/x/auth/client/utils/tx.go
2946index 55b700700..38189bc44 100644
2947--- a/x/auth/client/utils/tx.go
2948+++ b/x/auth/client/utils/tx.go
2949@@ -120,26 +120,26 @@ func EnrichWithGas(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs [
2950 }
2951
2952 // CalculateGas simulates the execution of a transaction and returns
2953-// both the estimate obtained by the query and the adjusted amount.
2954+// the simulation response obtained by the query and the adjusted gas amount.
2955 func CalculateGas(
2956 queryFunc func(string, []byte) ([]byte, int64, error), cdc *codec.Codec,
2957 txBytes []byte, adjustment float64,
2958-) (estimate, adjusted uint64, err error) {
2959+) (sdk.SimulationResponse, uint64, error) {
2960
2961 // run a simulation (via /app/simulate query) to
2962 // estimate gas and update TxBuilder accordingly
2963 rawRes, _, err := queryFunc("/app/simulate", txBytes)
2964 if err != nil {
2965- return estimate, adjusted, err
2966+ return sdk.SimulationResponse{}, 0, err
2967 }
2968
2969- estimate, err = parseQueryResponse(cdc, rawRes)
2970+ simRes, err := parseQueryResponse(cdc, rawRes)
2971 if err != nil {
2972- return
2973+ return sdk.SimulationResponse{}, 0, err
2974 }
2975
2976- adjusted = adjustGasEstimate(estimate, adjustment)
2977- return estimate, adjusted, nil
2978+ adjusted := adjustGasEstimate(simRes.GasUsed, adjustment)
2979+ return simRes, adjusted, nil
2980 }
2981
2982 // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
2983@@ -261,29 +261,28 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) {
2984 return encoder
2985 }
2986
2987-// nolint
2988-// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
2989-func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (estimated, adjusted uint64, err error) {
2990+// simulateMsgs simulates the transaction and returns the simulation response and
2991+// the adjusted gas value.
2992+func simulateMsgs(txBldr authtypes.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (sdk.SimulationResponse, uint64, error) {
2993 txBytes, err := txBldr.BuildTxForSim(msgs)
2994 if err != nil {
2995- return
2996+ return sdk.SimulationResponse{}, 0, err
2997 }
2998
2999- estimated, adjusted, err = CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment())
3000- return
3001+ return CalculateGas(cliCtx.QueryWithData, cliCtx.Codec, txBytes, txBldr.GasAdjustment())
3002 }
3003
3004 func adjustGasEstimate(estimate uint64, adjustment float64) uint64 {
3005 return uint64(adjustment * float64(estimate))
3006 }
3007
3008-func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (uint64, error) {
3009- var gasUsed uint64
3010- if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &gasUsed); err != nil {
3011- return 0, err
3012+func parseQueryResponse(cdc *codec.Codec, rawRes []byte) (sdk.SimulationResponse, error) {
3013+ var simRes sdk.SimulationResponse
3014+ if err := cdc.UnmarshalBinaryBare(rawRes, &simRes); err != nil {
3015+ return sdk.SimulationResponse{}, err
3016 }
3017
3018- return gasUsed, nil
3019+ return simRes, nil
3020 }
3021
3022 // PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx.
3023diff --git a/x/auth/client/utils/tx_test.go b/x/auth/client/utils/tx_test.go
3024index a9c33b232..7f78664bb 100644
3025--- a/x/auth/client/utils/tx_test.go
3026+++ b/x/auth/client/utils/tx_test.go
3027@@ -7,8 +7,8 @@ import (
3028 "os"
3029 "testing"
3030
3031- "github.com/stretchr/testify/assert"
3032 "github.com/stretchr/testify/require"
3033+
3034 "github.com/tendermint/tendermint/crypto/ed25519"
3035
3036 "github.com/cosmos/cosmos-sdk/codec"
3037@@ -23,13 +23,19 @@ var (
3038
3039 func TestParseQueryResponse(t *testing.T) {
3040 cdc := makeCodec()
3041- sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(uint64(10))
3042- gas, err := parseQueryResponse(cdc, sdkResBytes)
3043- assert.Equal(t, gas, uint64(10))
3044- assert.Nil(t, err)
3045- gas, err = parseQueryResponse(cdc, []byte("fuzzy"))
3046- assert.Equal(t, gas, uint64(0))
3047- assert.Error(t, err)
3048+ simRes := sdk.SimulationResponse{
3049+ GasInfo: sdk.GasInfo{GasUsed: 10, GasWanted: 20},
3050+ Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
3051+ }
3052+
3053+ bz := cdc.MustMarshalBinaryBare(simRes)
3054+ res, err := parseQueryResponse(cdc, bz)
3055+ require.NoError(t, err)
3056+ require.Equal(t, 10, int(res.GasInfo.GasUsed))
3057+ require.NotNil(t, res.Result)
3058+
3059+ res, err = parseQueryResponse(cdc, []byte("fuzzy"))
3060+ require.Error(t, err)
3061 }
3062
3063 func TestCalculateGas(t *testing.T) {
3064@@ -37,9 +43,14 @@ func TestCalculateGas(t *testing.T) {
3065 makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, []byte) ([]byte, int64, error) {
3066 return func(string, []byte) ([]byte, int64, error) {
3067 if wantErr {
3068- return nil, 0, errors.New("")
3069+ return nil, 0, errors.New("query failed")
3070+ }
3071+ simRes := sdk.SimulationResponse{
3072+ GasInfo: sdk.GasInfo{GasUsed: gasUsed, GasWanted: gasUsed},
3073+ Result: &sdk.Result{Data: []byte("tx data"), Log: "log"},
3074 }
3075- return cdc.MustMarshalBinaryLengthPrefixed(gasUsed), 0, nil
3076+
3077+ return cdc.MustMarshalBinaryBare(simRes), 0, nil
3078 }
3079 }
3080
3081@@ -54,20 +65,26 @@ func TestCalculateGas(t *testing.T) {
3082 args args
3083 wantEstimate uint64
3084 wantAdjusted uint64
3085- wantErr bool
3086+ expPass bool
3087 }{
3088- {"error", args{0, true, 1.2}, 0, 0, true},
3089- {"adjusted gas", args{10, false, 1.2}, 10, 12, false},
3090+ {"error", args{0, true, 1.2}, 0, 0, false},
3091+ {"adjusted gas", args{10, false, 1.2}, 10, 12, true},
3092 }
3093
3094 for _, tt := range tests {
3095 tt := tt
3096 t.Run(tt.name, func(t *testing.T) {
3097 queryFunc := makeQueryFunc(tt.args.queryFuncGasUsed, tt.args.queryFuncWantErr)
3098- gotEstimate, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment)
3099- assert.Equal(t, err != nil, tt.wantErr)
3100- assert.Equal(t, gotEstimate, tt.wantEstimate)
3101- assert.Equal(t, gotAdjusted, tt.wantAdjusted)
3102+ simRes, gotAdjusted, err := CalculateGas(queryFunc, cdc, []byte(""), tt.args.adjustment)
3103+ if tt.expPass {
3104+ require.NoError(t, err)
3105+ require.Equal(t, simRes.GasInfo.GasUsed, tt.wantEstimate)
3106+ require.Equal(t, gotAdjusted, tt.wantAdjusted)
3107+ require.NotNil(t, simRes.Result)
3108+ } else {
3109+ require.Error(t, err)
3110+ require.Nil(t, simRes.Result)
3111+ }
3112 })
3113 }
3114 }
3115diff --git a/x/auth/types/account.go b/x/auth/types/account.go
3116index a8d919a38..4aa575311 100644
3117--- a/x/auth/types/account.go
3118+++ b/x/auth/types/account.go
3119@@ -2,7 +2,6 @@ package types
3120
3121 import (
3122 "bytes"
3123- "encoding/json"
3124 "errors"
3125 "time"
3126
3127@@ -168,48 +167,3 @@ func (acc BaseAccount) MarshalYAML() (interface{}, error) {
3128
3129 return string(bz), err
3130 }
3131-
3132-// MarshalJSON returns the JSON representation of a BaseAccount.
3133-func (acc BaseAccount) MarshalJSON() ([]byte, error) {
3134- alias := baseAccountPretty{
3135- Address: acc.Address,
3136- Coins: acc.Coins,
3137- AccountNumber: acc.AccountNumber,
3138- Sequence: acc.Sequence,
3139- }
3140-
3141- if acc.PubKey != nil {
3142- pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey)
3143- if err != nil {
3144- return nil, err
3145- }
3146-
3147- alias.PubKey = pks
3148- }
3149-
3150- return json.Marshal(alias)
3151-}
3152-
3153-// UnmarshalJSON unmarshals raw JSON bytes into a BaseAccount.
3154-func (acc *BaseAccount) UnmarshalJSON(bz []byte) error {
3155- var alias baseAccountPretty
3156- if err := json.Unmarshal(bz, &alias); err != nil {
3157- return err
3158- }
3159-
3160- if alias.PubKey != "" {
3161- pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey)
3162- if err != nil {
3163- return err
3164- }
3165-
3166- acc.PubKey = pk
3167- }
3168-
3169- acc.Address = alias.Address
3170- acc.Coins = alias.Coins
3171- acc.AccountNumber = alias.AccountNumber
3172- acc.Sequence = alias.Sequence
3173-
3174- return nil
3175-}
3176diff --git a/x/auth/types/account_test.go b/x/auth/types/account_test.go
3177index 7be6a25e5..14236172a 100644
3178--- a/x/auth/types/account_test.go
3179+++ b/x/auth/types/account_test.go
3180@@ -1,7 +1,6 @@
3181 package types
3182
3183 import (
3184- "encoding/json"
3185 "errors"
3186 "testing"
3187
3188@@ -129,28 +128,3 @@ func TestGenesisAccountValidate(t *testing.T) {
3189 })
3190 }
3191 }
3192-
3193-func TestBaseAccountJSON(t *testing.T) {
3194- pubkey := secp256k1.GenPrivKey().PubKey()
3195- addr := sdk.AccAddress(pubkey.Address())
3196- coins := sdk.NewCoins(sdk.NewInt64Coin("test", 5))
3197- baseAcc := NewBaseAccount(addr, coins, pubkey, 10, 50)
3198-
3199- bz, err := json.Marshal(baseAcc)
3200- require.NoError(t, err)
3201-
3202- bz1, err := baseAcc.MarshalJSON()
3203- require.NoError(t, err)
3204- require.Equal(t, string(bz1), string(bz))
3205-
3206- var a BaseAccount
3207- require.NoError(t, json.Unmarshal(bz, &a))
3208- require.Equal(t, baseAcc.String(), a.String())
3209-
3210- bz, err = ModuleCdc.MarshalJSON(baseAcc)
3211- require.NoError(t, err)
3212-
3213- var b BaseAccount
3214- require.NoError(t, ModuleCdc.UnmarshalJSON(bz, &b))
3215- require.Equal(t, baseAcc.String(), b.String())
3216-}
3217diff --git a/x/bank/internal/keeper/keeper.go b/x/bank/internal/keeper/keeper.go
3218index 25e47b073..5f0dfed2b 100644
3219--- a/x/bank/internal/keeper/keeper.go
3220+++ b/x/bank/internal/keeper/keeper.go
3221@@ -211,6 +211,15 @@ func (keeper BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.In
3222 sdk.NewAttribute(sdk.AttributeKeyAmount, out.Coins.String()),
3223 ),
3224 )
3225+
3226+ // Create account if recipient does not exist.
3227+ //
3228+ // NOTE: This should ultimately be removed in favor a more flexible approach
3229+ // such as delegated fee messages.
3230+ acc := keeper.ak.GetAccount(ctx, out.Address)
3231+ if acc == nil {
3232+ keeper.ak.SetAccount(ctx, keeper.ak.NewAccountWithAddress(ctx, out.Address))
3233+ }
3234 }
3235
3236 return nil
3237@@ -219,9 +228,11 @@ func (keeper BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.In
3238 // SendCoins moves coins from one account to another
3239 func (keeper BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error {
3240 ctx.EventManager().EmitEvents(sdk.Events{
3241+ // This event should have all info (to, from, amount) without looking at other events
3242 sdk.NewEvent(
3243 types.EventTypeTransfer,
3244 sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()),
3245+ sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()),
3246 sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()),
3247 ),
3248 sdk.NewEvent(
3249diff --git a/x/bank/internal/keeper/keeper_test.go b/x/bank/internal/keeper/keeper_test.go
3250index 6b6f3cd40..e248df4b7 100644
3251--- a/x/bank/internal/keeper/keeper_test.go
3252+++ b/x/bank/internal/keeper/keeper_test.go
3253@@ -149,6 +149,38 @@ func TestSendKeeper(t *testing.T) {
3254 require.Error(t, err)
3255 }
3256
3257+func TestInputOutputNewAccount(t *testing.T) {
3258+ app, ctx := createTestApp(false)
3259+ balances := sdk.NewCoins(sdk.NewInt64Coin("foo", 100), sdk.NewInt64Coin("bar", 50))
3260+ addr1 := sdk.AccAddress([]byte("addr1"))
3261+ acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1)
3262+
3263+ app.AccountKeeper.SetAccount(ctx, acc1)
3264+ require.NoError(t, app.BankKeeper.SetCoins(ctx, addr1, balances))
3265+
3266+ acc1Balances := app.BankKeeper.GetCoins(ctx, addr1)
3267+ require.Equal(t, balances, acc1Balances)
3268+
3269+ addr2 := sdk.AccAddress([]byte("addr2"))
3270+
3271+ require.Nil(t, app.AccountKeeper.GetAccount(ctx, addr2))
3272+ require.Empty(t, app.BankKeeper.GetCoins(ctx, addr2))
3273+
3274+ inputs := []types.Input{
3275+ {Address: addr1, Coins: sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10))},
3276+ }
3277+ outputs := []types.Output{
3278+ {Address: addr2, Coins: sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10))},
3279+ }
3280+
3281+ require.NoError(t, app.BankKeeper.InputOutputCoins(ctx, inputs, outputs))
3282+
3283+ expected := sdk.NewCoins(sdk.NewInt64Coin("foo", 30), sdk.NewInt64Coin("bar", 10))
3284+ acc2Balances := app.BankKeeper.GetCoins(ctx, addr2)
3285+ require.Equal(t, expected, acc2Balances)
3286+ require.NotNil(t, app.AccountKeeper.GetAccount(ctx, addr2))
3287+}
3288+
3289 func TestMsgSendEvents(t *testing.T) {
3290 app, ctx := createTestApp(false)
3291
3292@@ -170,9 +202,8 @@ func TestMsgSendEvents(t *testing.T) {
3293 }
3294 event1.Attributes = append(
3295 event1.Attributes,
3296- tmkv.Pair{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr2.String())})
3297- event1.Attributes = append(
3298- event1.Attributes,
3299+ tmkv.Pair{Key: []byte(types.AttributeKeyRecipient), Value: []byte(addr2.String())},
3300+ tmkv.Pair{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())},
3301 tmkv.Pair{Key: []byte(sdk.AttributeKeyAmount), Value: []byte(newCoins.String())})
3302 event2 := sdk.Event{
3303 Type: sdk.EventTypeMessage,
3304diff --git a/x/distribution/types/codec.go b/x/distribution/types/codec.go
3305index 4ad4632d5..1cf6d9d6b 100644
3306--- a/x/distribution/types/codec.go
3307+++ b/x/distribution/types/codec.go
3308@@ -4,12 +4,13 @@ import (
3309 "github.com/cosmos/cosmos-sdk/codec"
3310 )
3311
3312-// Register concrete types on codec codec
3313+// RegisterCodec concrete distribution types on amino codec
3314 func RegisterCodec(cdc *codec.Codec) {
3315 cdc.RegisterConcrete(MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil)
3316 cdc.RegisterConcrete(MsgWithdrawValidatorCommission{}, "cosmos-sdk/MsgWithdrawValidatorCommission", nil)
3317 cdc.RegisterConcrete(MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil)
3318 cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil)
3319+ cdc.RegisterConcrete(MsgFundCommunityPool{}, "cosmos-sdk/MsgFundCommunityPool", nil)
3320 }
3321
3322 // ModuleCdc is a generic sealed codec to be used throughout module
3323diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go
3324index 219beef71..53d778c14 100644
3325--- a/x/staking/keeper/delegation.go
3326+++ b/x/staking/keeper/delegation.go
3327@@ -258,6 +258,8 @@ func (k Keeper) DequeueAllMatureUBDQueue(ctx sdk.Context,
3328 store := ctx.KVStore(k.storeKey)
3329 // gets an iterator for all timeslices from time 0 until the current Blockheader time
3330 unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time)
3331+ defer unbondingTimesliceIterator.Close()
3332+
3333 for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
3334 timeslice := []types.DVPair{}
3335 value := unbondingTimesliceIterator.Value()
3336@@ -445,6 +447,8 @@ func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time
3337 store := ctx.KVStore(k.storeKey)
3338 // gets an iterator for all timeslices from time 0 until the current Blockheader time
3339 redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time)
3340+ defer redelegationTimesliceIterator.Close()
3341+
3342 for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() {
3343 timeslice := []types.DVVTriplet{}
3344 value := redelegationTimesliceIterator.Value()