From 2f73cf4193faab13fead96614405947d7e3e037c Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 12 Nov 2018 23:12:09 -0500 Subject: [PATCH 01/51] block gas meter working --- baseapp/baseapp.go | 25 +++++++++++++++++++++---- types/context.go | 7 +++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 827536d215..419f88eb61 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -68,8 +68,10 @@ type BaseApp struct { deliverState *state // for DeliverTx voteInfos []abci.VoteInfo // absent validators from begin block - // minimum fees for spam prevention - minimumFees sdk.Coins + // spam prevention + minimumFees sdk.Coins + maximumBlockGas int64 + deliverGas // flag for sealing sealed bool @@ -194,6 +196,9 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // SetMinimumFees sets the minimum fees. func (app *BaseApp) SetMinimumFees(fees sdk.Coins) { app.minimumFees = fees } +// SetMaximumBlockGas sets the maximum gas allowable per block. +func (app *BaseApp) SetMaximumBlockGas(gas int64) { app.maximumBlockGas = gas } + // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { @@ -422,12 +427,19 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // Initialize the DeliverTx state. If this is the first block, it should // already be initialized in InitChain. Otherwise app.deliverState will be // nil, since it is reset on Commit. + blockGasMeter := sdk.NewGasMeter(app.maximumBlockGas) if app.deliverState == nil { app.setDeliverState(req.Header) + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(blockGasMeter) + } else { // In the first block, app.deliverState.ctx will already be initialized // by InitChain. Context is now updated with Header information. - app.deliverState.ctx = app.deliverState.ctx.WithBlockHeader(req.Header).WithBlockHeight(req.Header.Height) + app.deliverState.ctx = app.deliverState.ctx. + WithBlockHeader(req.Header). + WithBlockHeight(req.Header.Height). + WithBlockGasMeter(blockGasMeter) } if app.beginBlocker != nil { @@ -467,9 +479,10 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { // Implements ABCI func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { + // Decode the Tx. - var result sdk.Result var tx, err = app.txDecoder(txBytes) + var result sdk.Result if err != nil { result = err.Result() } else { @@ -655,6 +668,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result = app.runMsgs(ctx, msgs, mode) result.GasWanted = gasWanted + // consume block gas + ctx.BlockGasMeter.ConsumeGas( + ctx.GasMeter().GasConsumed(), "block gas meter") + // only update state if all messages pass if result.IsOK() { msCache.Write() diff --git a/types/context.go b/types/context.go index bfb4c58fed..ed65e57d92 100644 --- a/types/context.go +++ b/types/context.go @@ -140,6 +140,7 @@ const ( contextKeyLogger contextKeyVoteInfos contextKeyGasMeter + contextKeyBlockGasMeter contextKeyMinimumFees ) @@ -170,6 +171,8 @@ func (c Context) VoteInfos() []abci.VoteInfo { func (c Context) GasMeter() GasMeter { return c.Value(contextKeyGasMeter).(GasMeter) } +func (c Context) BlockGasMeter() GasMeter { return c.Value(contextKeyBlockGasMeter).(GasMeter) } + func (c Context) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) } func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) } @@ -219,6 +222,10 @@ func (c Context) WithVoteInfos(VoteInfos []abci.VoteInfo) Context { func (c Context) WithGasMeter(meter GasMeter) Context { return c.withValue(contextKeyGasMeter, meter) } +func (c Context) WithBlockGasMeter(meter GasMeter) Context { + return c.withValue(contextKeyBlockGasMeter, meter) +} + func (c Context) WithIsCheckTx(isCheckTx bool) Context { return c.withValue(contextKeyIsCheckTx, isCheckTx) } From 956d351f68705e073e6ba1bd412eb5e6ff8d2f7d Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 11:30:06 -0500 Subject: [PATCH 02/51] basic structure in place --- baseapp/baseapp.go | 16 ++++++++++------ types/gas.go | 9 +++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 419f88eb61..9f9d98f7e9 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -427,20 +427,17 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // Initialize the DeliverTx state. If this is the first block, it should // already be initialized in InitChain. Otherwise app.deliverState will be // nil, since it is reset on Commit. - blockGasMeter := sdk.NewGasMeter(app.maximumBlockGas) if app.deliverState == nil { app.setDeliverState(req.Header) - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(blockGasMeter) - } else { // In the first block, app.deliverState.ctx will already be initialized // by InitChain. Context is now updated with Header information. app.deliverState.ctx = app.deliverState.ctx. WithBlockHeader(req.Header). - WithBlockHeight(req.Header.Height). - WithBlockGasMeter(blockGasMeter) + WithBlockHeight(req.Header.Height) } + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) if app.beginBlocker != nil { res = app.beginBlocker(app.deliverState.ctx, req) @@ -607,6 +604,13 @@ func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Conte // anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the // future we may support "internal" transactions. func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { + + // only run the tx if there is block gas remaining + if ctx.BlockGasMeter.PastLimit() { + result = sdk.ErrOutOfGas("no block gas left to run tx").Result() + return + } + // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. diff --git a/types/gas.go b/types/gas.go index 7b03474670..6ec29dd139 100644 --- a/types/gas.go +++ b/types/gas.go @@ -29,6 +29,7 @@ type ErrorOutOfGas struct { type GasMeter interface { GasConsumed() Gas ConsumeGas(amount Gas, descriptor string) + PastLimit() bool } type basicGasMeter struct { @@ -55,6 +56,10 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { } } +func (g *basicGasMeter) PastLimit() bool { + return g.consumed > g.limit +} + type infiniteGasMeter struct { consumed Gas } @@ -74,6 +79,10 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { g.consumed += amount } +func (g *infiniteGasMeter) PastLimit() bool { + return false +} + // GasConfig defines gas cost for each operation on KVStores type GasConfig struct { HasCost Gas From ebaa39468ac491063959b1d9884bd3c899ab333a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 13:01:18 -0500 Subject: [PATCH 03/51] modified app provider to pass genesis --- baseapp/options.go | 5 +++++ cmd/gaia/app/genesis.go | 8 +++++--- cmd/gaia/cmd/gaiad/main.go | 18 +++++++++++++++--- server/constructors.go | 4 +++- server/start.go | 9 ++++++--- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/baseapp/options.go b/baseapp/options.go index a6460248df..8d61313143 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -39,6 +39,11 @@ func SetMinimumFees(minFees string) func(*BaseApp) { return func(bap *BaseApp) { bap.SetMinimumFees(fees) } } +// SetMinimumFees returns an option that sets the minimum fees on the app. +func SetMaximumBlockGas(gas int64) func(*BaseApp) { + return func(bap *BaseApp) { bap.SetMaximumBlockGas(gas) } +} + func (app *BaseApp) SetName(name string) { if app.sealed { panic("SetName() on sealed BaseApp") diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index e3c869ada1..c0929205be 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -42,8 +42,10 @@ type GenesisState struct { GenTxs []json.RawMessage `json:"gentxs"` } -func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState, - distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { +func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, + stakeData stake.GenesisState, mintData mint.GenesisState, + distrData distr.GenesisState, govData gov.GenesisState, + slashingData slashing.GenesisState) GenesisState { return GenesisState{ Accounts: accounts, @@ -287,7 +289,7 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { accAuth := auth.NewBaseAccountWithAddress(addr) - coins :=sdk.Coins{ + coins := sdk.Coins{ {"fooToken", sdk.NewInt(1000)}, {bondDenom, freeFermionsAcc}, } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index ef7b39d111..4154b85be1 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -13,6 +13,7 @@ import ( "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/node" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -56,16 +57,27 @@ func main() { } } -func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { +func newApp(logger log.Logger, db dbm.DB, + traceStore io.Writer, genDocProvider node.GenesisDocProvider) abci.Application { + + // get the maximum gas from tendermint genesis parameters + genDoc, err := genDocProvider() + if err != nil { + panic(err) + } + maxBlockGas := genDoc.ConsensusParams.BlockSize.MaxGas + return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")), baseapp.SetMinimumFees(viper.GetString("minimum_fees")), + baseapp.SetMaximumBlockGas(maxBlockGas), ) } func exportAppStateAndTMValidators( - logger log.Logger, db dbm.DB, traceStore io.Writer, -) (json.RawMessage, []tmtypes.GenesisValidator, error) { + logger log.Logger, db dbm.DB, traceStore io.Writer) ( + json.RawMessage, []tmtypes.GenesisValidator, error) { + gApp := app.NewGaiaApp(logger, db, traceStore) return gApp.ExportAppStateAndValidators() } diff --git a/server/constructors.go b/server/constructors.go index 57282e43b1..63cb5f62bf 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -9,13 +9,15 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/node" tmtypes "github.com/tendermint/tendermint/types" ) type ( // AppCreator is a function that allows us to lazily initialize an // application using various configurations. - AppCreator func(log.Logger, dbm.DB, io.Writer) abci.Application + AppCreator func(log.Logger, dbm.DB, + io.Writer, node.GenesisDocProvider) abci.Application // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. diff --git a/server/start.go b/server/start.go index cf39ff71b6..d84169ab44 100644 --- a/server/start.go +++ b/server/start.go @@ -68,7 +68,9 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { return err } - app := appCreator(ctx.Logger, db, traceWriter) + cfg := ctx.Config + genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) + app := appCreator(ctx.Logger, db, traceWriter, genDocProvider) svr, err := server.NewServer(addr, "socket", app) if err != nil { @@ -107,7 +109,8 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } - app := appCreator(ctx.Logger, db, traceWriter) + genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) + app := appCreator(ctx.Logger, db, traceWriter, genDocProvider) nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) if err != nil { @@ -120,7 +123,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()), nodeKey, proxy.NewLocalClientCreator(app), - node.DefaultGenesisDocProviderFunc(cfg), + genDocProvider, node.DefaultDBProvider, node.DefaultMetricsProvider(cfg.Instrumentation), ctx.Logger.With("module", "node"), From 3bf67b63e11560d67ad87e376df44b21a7c4e538 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 14:27:15 -0500 Subject: [PATCH 04/51] compiling --- baseapp/baseapp.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 9f9d98f7e9..be7177fb24 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -71,7 +71,6 @@ type BaseApp struct { // spam prevention minimumFees sdk.Coins maximumBlockGas int64 - deliverGas // flag for sealing sealed bool @@ -604,13 +603,6 @@ func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Conte // anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the // future we may support "internal" transactions. func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { - - // only run the tx if there is block gas remaining - if ctx.BlockGasMeter.PastLimit() { - result = sdk.ErrOutOfGas("no block gas left to run tx").Result() - return - } - // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. @@ -619,6 +611,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk ctx := app.getContextForAnte(mode, txBytes) ctx = app.initializeContext(ctx, mode) + // only run the tx if there is block gas remaining + if ctx.BlockGasMeter().PastLimit() { + result = sdk.ErrOutOfGas("no block gas left to run tx").Result() + return + } + defer func() { if r := recover(); r != nil { switch rType := r.(type) { @@ -673,7 +671,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result.GasWanted = gasWanted // consume block gas - ctx.BlockGasMeter.ConsumeGas( + ctx.BlockGasMeter().ConsumeGas( ctx.GasMeter().GasConsumed(), "block gas meter") // only update state if all messages pass From 8069b2b7e6a61c6f9519b943e123e3fae35d0bb8 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 14:30:24 -0500 Subject: [PATCH 05/51] default infinite block gas meter --- baseapp/baseapp.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index be7177fb24..e661c904fe 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -435,8 +435,15 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg WithBlockHeader(req.Header). WithBlockHeight(req.Header.Height) } - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + + // add block gas meter + if app.maximumBlockGas > 0 { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + } else { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + } if app.beginBlocker != nil { res = app.beginBlocker(app.deliverState.ctx, req) From 24468306b48eb91631c040f262322bab49aba397 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 14:35:53 -0500 Subject: [PATCH 06/51] examples installing --- examples/basecoin/cmd/basecoind/main.go | 3 ++- examples/democoin/cmd/democoind/main.go | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index f07fbd3ff2..e56654f516 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -6,6 +6,7 @@ import ( "io" "os" + "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/cosmos/cosmos-sdk/baseapp" @@ -122,7 +123,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return cmd } -func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application { +func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer, _ node.GenesisDocProvider) abci.Application { return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } diff --git a/examples/democoin/cmd/democoind/main.go b/examples/democoin/cmd/democoind/main.go index d095b4c790..5acb459dbb 100644 --- a/examples/democoin/cmd/democoind/main.go +++ b/examples/democoin/cmd/democoind/main.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/spf13/cobra" @@ -129,7 +130,9 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cob return cmd } -func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { +func newApp(logger log.Logger, db dbm.DB, _ io.Writer, + _ node.GenesisDocProvider) abci.Application { + return app.NewDemocoinApp(logger, db) } From eead27872fd577175fab9625be0539e61b3c7d0c Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 15:12:04 -0500 Subject: [PATCH 07/51] initial test case --- baseapp/baseapp_test.go | 93 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 9f6414214b..53c34334d6 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -823,3 +823,96 @@ func TestTxGasLimits(t *testing.T) { } } } + +// Test that transactions exceeding gas limits fail +func TestMaxBlockGasLimits(t *testing.T) { + gasGranted := int64(10) + anteOpt := func(bapp *BaseApp) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { + newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) + + // NOTE/TODO/XXX: + // AnteHandlers must have their own defer/recover in order + // for the BaseApp to know how much gas was used used! + // This is because the GasMeter is created in the AnteHandler, + // but if it panics the context won't be set properly in runTx's recover ... + defer func() { + if r := recover(); r != nil { + switch rType := r.(type) { + case sdk.ErrorOutOfGas: + log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) + res = sdk.ErrOutOfGas(log).Result() + res.GasWanted = gasGranted + res.GasUsed = newCtx.GasMeter().GasConsumed() + default: + panic(r) + } + } + }() + + count := tx.(*txTest).Counter + newCtx.GasMeter().ConsumeGas(count, "counter-ante") + res = sdk.Result{ + GasWanted: gasGranted, + } + return + }) + + } + + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + count := msg.(msgCounter).Counter + ctx.GasMeter().ConsumeGas(count, "counter-handler") + return sdk.Result{} + }) + } + + app := setupBaseApp(t, anteOpt, routerOpt) + app.SetMaximumBlockGas(100) + + testCases := []struct { + tx *txTest + numDelivers int + blockGasUsed int64 + fail bool + }{ + {newTxCounter(0, 0), 0, 0, false}, + {newTxCounter(9, 1), 2, 20, false}, + {newTxCounter(10, 0), 3, 30, false}, + {newTxCounter(10, 0), 10, 100, false}, + {newTxCounter(2, 7), 11, 99, false}, + + {newTxCounter(2, 7), 12, 108, true}, + {newTxCounter(10, 0), 11, 110, true}, + } + + for i, tc := range testCases { + tx := tc.tx + + // reset the block gas + app.BeginBlock(abci.RequestBeginBlock{}) + + // execute the transaction multiple times + for j := 0; j < numDelivers; j++ { + res := app.Deliver(tx) + } + + ctx := app.getContextForAnte(runTxModeDeliver, tx) + ctx = app.initializeContext(ctx, runTxModeDeliver) + blockGasUsed := ctx.BlockGasMeter().ConsumedGas() + + // check gas used and wanted + require.Equal(t, tc.blockGasUsed, blockGasUsed, + fmt.Sprintf("%d: %v, %v, %v", i, tc, blockGasUsed, res)) + + // check for out of gas + if !tc.fail { + require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.False(t, ctx.BlockGasMeter().PastLimit()) + } else { + require.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.True(t, ctx.BlockGasMeter().PastLimit()) + } + } +} From 7e6fcc0161a549752ff9501480a9a5b01dbfc283 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 13 Nov 2018 16:01:00 -0500 Subject: [PATCH 08/51] passing test --- baseapp/baseapp_test.go | 55 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 53c34334d6..56349ebaa5 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -872,19 +872,20 @@ func TestMaxBlockGasLimits(t *testing.T) { app.SetMaximumBlockGas(100) testCases := []struct { - tx *txTest - numDelivers int - blockGasUsed int64 - fail bool + tx *txTest + numDelivers int + gasUsedPerDeliver int64 + fail bool + failAfterDeliver int }{ - {newTxCounter(0, 0), 0, 0, false}, - {newTxCounter(9, 1), 2, 20, false}, - {newTxCounter(10, 0), 3, 30, false}, - {newTxCounter(10, 0), 10, 100, false}, - {newTxCounter(2, 7), 11, 99, false}, + {newTxCounter(0, 0), 0, 0, false, 0}, + {newTxCounter(9, 1), 2, 10, false, 0}, + {newTxCounter(10, 0), 3, 10, false, 0}, + {newTxCounter(10, 0), 10, 10, false, 0}, + {newTxCounter(2, 7), 11, 9, false, 0}, - {newTxCounter(2, 7), 12, 108, true}, - {newTxCounter(10, 0), 11, 110, true}, + {newTxCounter(10, 0), 11, 10, true, 10}, + {newTxCounter(10, 0), 15, 10, true, 10}, } for i, tc := range testCases { @@ -894,25 +895,27 @@ func TestMaxBlockGasLimits(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{}) // execute the transaction multiple times - for j := 0; j < numDelivers; j++ { + for j := 0; j < tc.numDelivers; j++ { res := app.Deliver(tx) - } - ctx := app.getContextForAnte(runTxModeDeliver, tx) - ctx = app.initializeContext(ctx, runTxModeDeliver) - blockGasUsed := ctx.BlockGasMeter().ConsumedGas() + ctx := app.getContextForAnte(runTxModeDeliver, nil) + ctx = app.initializeContext(ctx, runTxModeDeliver) + blockGasUsed := ctx.BlockGasMeter().GasConsumed() - // check gas used and wanted - require.Equal(t, tc.blockGasUsed, blockGasUsed, - fmt.Sprintf("%d: %v, %v, %v", i, tc, blockGasUsed, res)) + // check for failed transactions + if tc.fail && (j+1) > tc.failAfterDeliver { + require.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.True(t, ctx.BlockGasMeter().PastLimit()) + } else { - // check for out of gas - if !tc.fail { - require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res)) - require.False(t, ctx.BlockGasMeter().PastLimit()) - } else { - require.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), fmt.Sprintf("%d: %v, %v", i, tc, res)) - require.True(t, ctx.BlockGasMeter().PastLimit()) + // check gas used and wanted + expBlockGasUsed := tc.gasUsedPerDeliver * int64(j+1) + require.Equal(t, expBlockGasUsed, blockGasUsed, + fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res)) + + require.True(t, res.IsOK(), fmt.Sprintf("%d,%d: %v, %v", i, j, tc, res)) + require.False(t, ctx.BlockGasMeter().PastLimit()) + } } } } From 68e3b9a55908a4ee9e5b3ad4e7d6dbaf89afe6aa Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 14 Nov 2018 00:57:27 -0500 Subject: [PATCH 09/51] only use block gas for deliver --- baseapp/baseapp.go | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e661c904fe..17ed869c42 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -264,6 +264,15 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC } res = app.initChainer(app.deliverState.ctx, req) + // add block gas meter + if app.maximumBlockGas > 0 { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + } else { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + } + // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState return @@ -434,15 +443,15 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg app.deliverState.ctx = app.deliverState.ctx. WithBlockHeader(req.Header). WithBlockHeight(req.Header.Height) - } - // add block gas meter - if app.maximumBlockGas > 0 { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) - } else { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + // add block gas meter + if app.maximumBlockGas > 0 { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + } else { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + } } if app.beginBlocker != nil { @@ -619,7 +628,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.initializeContext(ctx, mode) // only run the tx if there is block gas remaining - if ctx.BlockGasMeter().PastLimit() { + if mode == runTxModeDeliver && ctx.BlockGasMeter().PastLimit() { result = sdk.ErrOutOfGas("no block gas left to run tx").Result() return } @@ -678,8 +687,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result.GasWanted = gasWanted // consume block gas - ctx.BlockGasMeter().ConsumeGas( - ctx.GasMeter().GasConsumed(), "block gas meter") + if mode == runTxModeDeliver { + ctx.BlockGasMeter().ConsumeGas( + ctx.GasMeter().GasConsumed(), "block gas meter") + } // only update state if all messages pass if result.IsOK() { From 0d4dd8762bb8bba044aaec99684b38263d8f3cb9 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 14 Nov 2018 14:07:46 -0500 Subject: [PATCH 10/51] fix baseapp tests --- baseapp/baseapp.go | 25 ++++++++----------------- baseapp/baseapp_test.go | 1 + 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 17ed869c42..5e3624bb7d 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -264,15 +264,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC } res = app.initChainer(app.deliverState.ctx, req) - // add block gas meter - if app.maximumBlockGas > 0 { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) - } else { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewInfiniteGasMeter()) - } - // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState return @@ -443,15 +434,15 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg app.deliverState.ctx = app.deliverState.ctx. WithBlockHeader(req.Header). WithBlockHeight(req.Header.Height) + } - // add block gas meter - if app.maximumBlockGas > 0 { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) - } else { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewInfiniteGasMeter()) - } + // add block gas meter + if app.maximumBlockGas > 0 { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + } else { + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) } if app.beginBlocker != nil { diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 56349ebaa5..6a4bfefcf5 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -491,6 +491,7 @@ func TestDeliverTx(t *testing.T) { } app := setupBaseApp(t, anteOpt, routerOpt) + app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder codec := codec.New() From 524906478ab5f906b772218a4b3954cb35f551ff Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 14 Nov 2018 14:16:52 -0500 Subject: [PATCH 11/51] add init chain block gas for gen-txs (all unit tests fixed) --- baseapp/baseapp.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 5e3624bb7d..7e35fb5f6e 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -262,6 +262,11 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC if app.initChainer == nil { return } + + // add block gas meter for any genesis transactions (allow infinite gas) + app.deliverState.ctx = app.deliverState.ctx. + WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + res = app.initChainer(app.deliverState.ctx, req) // NOTE: we don't commit, but BeginBlock for block 1 From 90217e23134f588ed0bd7c5308ff209346a05d2b Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 14 Nov 2018 15:14:34 -0500 Subject: [PATCH 12/51] docs and pending --- PENDING.md | 3 +- docs/spec/baseapp/WIP_abci_application.md | 46 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 docs/spec/baseapp/WIP_abci_application.md diff --git a/PENDING.md b/PENDING.md index 52bf8a7eeb..49a66c79d6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -33,7 +33,8 @@ FEATURES for getting governance parameters. * SDK - * [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time + * [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time + * [core] \#2775 Add deliverTx maximum block gas limit * Tendermint diff --git a/docs/spec/baseapp/WIP_abci_application.md b/docs/spec/baseapp/WIP_abci_application.md new file mode 100644 index 0000000000..52f0ab18dd --- /dev/null +++ b/docs/spec/baseapp/WIP_abci_application.md @@ -0,0 +1,46 @@ +# ABCI application + +The `BaseApp` struct fulfills the tendermint-abci `Application` interface. + +## Info + +## SetOption + +## Query + +## CheckTx + +## InitChain +TODO pseudo code + +During chain initialization InitChain runs the initialization logic directly on +the CommitMultiStore and commits it. The deliver and check states are +initialized with the ChainID. Additionally the Block gas meter is initialized +with an infinite amount of gas to run any genesis transactions. + +Note that we do not `Commit` during `InitChain` however BeginBlock for block 1 +starts from this deliverState. + + +## BeginBlock +TODO complete description & pseudo code + +The block gas meter is reset within BeginBlock for the deliver state. +If no maximum block gas is set within baseapp then an infinite +gas meter is set, otherwise a gas meter with the baseapp `maximumBlockGas` +is initialized + +## DeliverTx +TODO complete description & pseudo code + +Before transaction logic is run, the `BlockGasMeter` is first checked for +remaining gas. If no gas remains, then `DeliverTx` immediately returns an error. + +After the transaction has been processed the used gas is deducted from the +BlockGasMeter. If the remaining gas exceeds the meter's limits, then DeliverTx +returns an error and the transaction is not committed. + +## EndBlock + +## Commit + From 2a594fe3381b3b990c08da91fa2848f7f71a5845 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 15 Nov 2018 11:13:18 -0500 Subject: [PATCH 13/51] basic cwgoes comments --- baseapp/baseapp.go | 10 +++++----- docs/spec/baseapp/WIP_abci_application.md | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 7e35fb5f6e..2cd7f562b3 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -253,7 +253,7 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp } // Implements ABCI -// InitChain runs the initialization logic directly on the CommitMultiStore and commits it. +// InitChain runs the initialization logic directly on the CommitMultiStore. func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { // Initialize the deliver state and check state with ChainID and run initChain app.setDeliverState(abci.Header{ChainID: req.ChainId}) @@ -442,13 +442,13 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg } // add block gas meter + var gasMeter sdk.GasMeter if app.maximumBlockGas > 0 { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewGasMeter(app.maximumBlockGas)) + gasMeter = sdk.NewGasMeter(app.maximumBlockGas) } else { - app.deliverState.ctx = app.deliverState.ctx. - WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + gasMeter = sdk.NewInfiniteGasMeter() } + app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter) if app.beginBlocker != nil { res = app.beginBlocker(app.deliverState.ctx, req) diff --git a/docs/spec/baseapp/WIP_abci_application.md b/docs/spec/baseapp/WIP_abci_application.md index 52f0ab18dd..7baadccee4 100644 --- a/docs/spec/baseapp/WIP_abci_application.md +++ b/docs/spec/baseapp/WIP_abci_application.md @@ -14,9 +14,9 @@ The `BaseApp` struct fulfills the tendermint-abci `Application` interface. TODO pseudo code During chain initialization InitChain runs the initialization logic directly on -the CommitMultiStore and commits it. The deliver and check states are -initialized with the ChainID. Additionally the Block gas meter is initialized -with an infinite amount of gas to run any genesis transactions. +the CommitMultiStore. The deliver and check states are initialized with the +ChainID. Additionally the block gas meter is initialized with an infinite +amount of gas to run any genesis transactions. Note that we do not `Commit` during `InitChain` however BeginBlock for block 1 starts from this deliverState. From 8d6b0929fb5fb170d4a8543e517b23f793af2fcb Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 16 Nov 2018 09:12:24 -0800 Subject: [PATCH 14/51] Codespaces as Strings (#2821) --- Gopkg.lock | 3 +- PENDING.md | 2 + baseapp/baseapp.go | 42 +++++++-------- baseapp/baseapp_test.go | 18 ++++--- cmd/gaia/app/app.go | 8 +-- cmd/gaia/cmd/gaiadebug/hack.go | 4 +- docs/_attic/sdk/core/examples/app1.go | 7 +-- docs/_attic/sdk/core/examples/app2.go | 2 +- docs/examples/basecoin/app/app.go | 2 +- docs/examples/democoin/app/app.go | 8 +-- docs/examples/democoin/x/cool/app_test.go | 2 +- docs/examples/democoin/x/cool/errors.go | 2 +- docs/examples/democoin/x/oracle/handler.go | 2 +- docs/examples/democoin/x/pow/app_test.go | 2 +- docs/examples/democoin/x/pow/errors.go | 2 +- .../examples/democoin/x/simplestake/errors.go | 2 +- .../democoin/x/simplestake/handler.go | 4 +- store/rootmultistore_test.go | 15 +++--- types/codespacer.go | 35 ------------- types/codespacer_test.go | 47 ----------------- types/errors.go | 52 +++++-------------- types/errors_test.go | 7 +-- types/result.go | 5 +- types/result_test.go | 2 +- x/auth/ante_test.go | 7 +-- x/bank/app_test.go | 3 +- x/bank/errors.go | 2 +- x/distribution/types/errors.go | 2 +- x/gov/errors.go | 2 +- x/gov/test_common.go | 2 +- x/ibc/app_test.go | 2 +- x/ibc/errors.go | 2 +- x/mock/app_test.go | 4 +- x/mock/test_utils.go | 12 ++--- x/slashing/app_test.go | 7 +-- x/slashing/errors.go | 2 +- x/slashing/handler_test.go | 3 +- x/stake/app_test.go | 2 +- x/stake/types/errors.go | 2 +- 39 files changed, 119 insertions(+), 210 deletions(-) delete mode 100644 types/codespacer.go delete mode 100644 types/codespacer_test.go diff --git a/Gopkg.lock b/Gopkg.lock index bcc306edb9..40192b2afd 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -165,12 +165,13 @@ version = "v1.2.0" [[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", + "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", diff --git a/PENDING.md b/PENDING.md index 050646593f..a3034c01eb 100644 --- a/PENDING.md +++ b/PENDING.md @@ -56,8 +56,10 @@ IMPROVEMENTS - #2773 Require moniker to be provided on `gaiad init`. - #2672 [Makefile] Updated for better Windows compatibility and ledger support logic, get_tools was rewritten as a cross-compatible Makefile. - [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated. + * SDK - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization + - \#2821 Codespaces are now strings * Tendermint - #2796 Update to go-amino 0.14.1 diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 827536d215..34041dff20 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -47,7 +47,6 @@ type BaseApp struct { cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message queryRouter QueryRouter // router for redirecting query calls - codespacer *sdk.Codespacer // handle module codespacing txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth @@ -94,13 +93,9 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod cms: store.NewCommitMultiStore(db), router: NewRouter(), queryRouter: NewQueryRouter(), - codespacer: sdk.NewCodespacer(), txDecoder: txDecoder, } - // Register the undefined & root codespaces, which should not be used by - // any modules. - app.codespacer.RegisterOrPanic(sdk.CodespaceRoot) for _, option := range options { option(app) } @@ -118,11 +113,6 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { app.cms.WithTracer(w) } -// Register the next available codespace through the baseapp's codespacer, starting from a default -func (app *BaseApp) RegisterCodespace(codespace sdk.CodespaceType) sdk.CodespaceType { - return app.codespacer.RegisterNext(codespace) -} - // Mount IAVL stores to the provided keys in the BaseApp multistore func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) { for _, key := range keys { @@ -329,8 +319,9 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc } case "version": return abci.ResponseQuery{ - Code: uint32(sdk.ABCICodeOK), - Value: []byte(version.GetVersion()), + Code: uint32(sdk.CodeOK), + Codespace: string(sdk.CodespaceRoot), + Value: []byte(version.GetVersion()), } default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() @@ -339,8 +330,9 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc // Encode with json value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) return abci.ResponseQuery{ - Code: uint32(sdk.ABCICodeOK), - Value: value, + Code: uint32(sdk.CodeOK), + Codespace: string(sdk.CodespaceRoot), + Value: value, } } msg := "Expected second parameter to be either simulate or version, neither was present" @@ -400,12 +392,13 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res resBytes, err := querier(ctx, path[2:], req) if err != nil { return abci.ResponseQuery{ - Code: uint32(err.ABCICode()), - Log: err.ABCILog(), + Code: uint32(err.Code()), + Codespace: string(err.Codespace()), + Log: err.ABCILog(), } } return abci.ResponseQuery{ - Code: uint32(sdk.ABCICodeOK), + Code: uint32(sdk.CodeOK), Value: resBytes, } } @@ -482,6 +475,7 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { // Tell the blockchain engine (i.e. Tendermint). return abci.ResponseDeliverTx{ Code: uint32(result.Code), + Codespace: string(result.Codespace), Data: result.Data, Log: result.Log, GasWanted: result.GasWanted, @@ -501,7 +495,6 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { // Validate the Msg. err := msg.ValidateBasic() if err != nil { - err = err.WithDefaultCodespace(sdk.CodespaceRoot) return err } } @@ -526,7 +519,8 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re logs := make([]string, 0, len(msgs)) var data []byte // NOTE: we just append them all (?!) var tags sdk.Tags // also just append them all - var code sdk.ABCICodeType + var code sdk.CodeType + var codespace sdk.CodespaceType for msgIdx, msg := range msgs { // Match route. msgRoute := msg.Route() @@ -553,6 +547,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re if !msgResult.IsOK() { logs = append(logs, fmt.Sprintf("Msg %d failed: %s", msgIdx, msgResult.Log)) code = msgResult.Code + codespace = msgResult.Codespace break } @@ -562,10 +557,11 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re // Set the final gas values. result = sdk.Result{ - Code: code, - Data: data, - Log: strings.Join(logs, "\n"), - GasUsed: ctx.GasMeter().GasConsumed(), + Code: code, + Codespace: codespace, + Data: data, + Log: strings.Join(logs, "\n"), + GasUsed: ctx.GasMeter().GasConsumed(), // TODO: FeeAmount/FeeDenom Tags: tags, } diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 9f6414214b..35f9fa4246 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -678,7 +678,8 @@ func TestRunInvalidTransaction(t *testing.T) { { emptyTx := &txTest{} err := app.Deliver(emptyTx) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInternal), err.Code) + require.EqualValues(t, sdk.CodeInternal, err.Code) + require.EqualValues(t, sdk.CodespaceRoot, err.Codespace) } // Transaction where ValidateBasic fails @@ -701,7 +702,8 @@ func TestRunInvalidTransaction(t *testing.T) { tx := testCase.tx res := app.Deliver(tx) if testCase.fail { - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeInvalidSequence), res.Code) + require.EqualValues(t, sdk.CodeInvalidSequence, res.Code) + require.EqualValues(t, sdk.CodespaceRoot, res.Codespace) } else { require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) } @@ -712,11 +714,13 @@ func TestRunInvalidTransaction(t *testing.T) { { unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0} err := app.Deliver(unknownRouteTx) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), err.Code) + require.EqualValues(t, sdk.CodeUnknownRequest, err.Code) + require.EqualValues(t, sdk.CodespaceRoot, err.Codespace) unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0} err = app.Deliver(unknownRouteTx) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), err.Code) + require.EqualValues(t, sdk.CodeUnknownRequest, err.Code) + require.EqualValues(t, sdk.CodespaceRoot, err.Codespace) } // Transaction with an unregistered message @@ -732,7 +736,8 @@ func TestRunInvalidTransaction(t *testing.T) { txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx) require.NoError(t, err) res := app.DeliverTx(txBytes) - require.EqualValues(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeTxDecode), res.Code) + require.EqualValues(t, sdk.CodeTxDecode, res.Code) + require.EqualValues(t, sdk.CodespaceRoot, res.Codespace) } } @@ -819,7 +824,8 @@ func TestTxGasLimits(t *testing.T) { if !tc.fail { require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res)) } else { - require.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.Equal(t, sdk.CodeOutOfGas, res.Code, fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.Equal(t, sdk.CodespaceRoot, res.Codespace) } } } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index fb8455b5ae..c06d82bb17 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -113,7 +113,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), - app.RegisterCodespace(stake.DefaultCodespace), + stake.DefaultCodespace, ) app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.paramsKeeper.Subspace(mint.DefaultParamspace), @@ -124,19 +124,19 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio app.keyDistr, app.paramsKeeper.Subspace(distr.DefaultParamspace), app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper, - app.RegisterCodespace(stake.DefaultCodespace), + distr.DefaultCodespace, ) app.slashingKeeper = slashing.NewKeeper( app.cdc, app.keySlashing, &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), - app.RegisterCodespace(slashing.DefaultCodespace), + slashing.DefaultCodespace, ) app.govKeeper = gov.NewKeeper( app.cdc, app.keyGov, app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper, - app.RegisterCodespace(gov.DefaultCodespace), + gov.DefaultCodespace, ) // register the staking hooks diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 734a83df30..e9f3b56589 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -175,8 +175,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) - app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), app.RegisterCodespace(stake.DefaultCodespace)) - app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), app.RegisterCodespace(slashing.DefaultCodespace)) + app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), stake.DefaultCodespace) + app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace) // register message routes app.Router(). diff --git a/docs/_attic/sdk/core/examples/app1.go b/docs/_attic/sdk/core/examples/app1.go index 8ee34fca59..25e8b40def 100644 --- a/docs/_attic/sdk/core/examples/app1.go +++ b/docs/_attic/sdk/core/examples/app1.go @@ -12,7 +12,8 @@ import ( ) const ( - app1Name = "App1" + app1Name = "App1" + bankCodespace = "BANK" ) func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp { @@ -107,7 +108,7 @@ func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler { if !ok { // Create custom error message and return result // Note: Using unreserved error codespace - return sdk.NewError(2, 1, "MsgSend is malformed").Result() + return sdk.NewError(bankCodespace, 1, "MsgSend is malformed").Result() } // Load the store. @@ -137,7 +138,7 @@ func handleFrom(store sdk.KVStore, from sdk.AccAddress, amt sdk.Coins) sdk.Resul accBytes := store.Get(from) if accBytes == nil { // Account was not added to store. Return the result of the error. - return sdk.NewError(2, 101, "Account not added to store").Result() + return sdk.NewError(bankCodespace, 101, "Account not added to store").Result() } // Unmarshal the JSON account bytes. diff --git a/docs/_attic/sdk/core/examples/app2.go b/docs/_attic/sdk/core/examples/app2.go index 24458384c0..620a191135 100644 --- a/docs/_attic/sdk/core/examples/app2.go +++ b/docs/_attic/sdk/core/examples/app2.go @@ -126,7 +126,7 @@ func handleMsgIssue(keyIssue *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey) sdk.Handle return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { issueMsg, ok := msg.(MsgIssue) if !ok { - return sdk.NewError(2, 1, "MsgIssue is malformed").Result() + return sdk.NewError(bankCodespace, 1, "MsgIssue is malformed").Result() } // Retrieve stores diff --git a/docs/examples/basecoin/app/app.go b/docs/examples/basecoin/app/app.go index 9515feaadc..3e58e71f92 100644 --- a/docs/examples/basecoin/app/app.go +++ b/docs/examples/basecoin/app/app.go @@ -75,7 +75,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba }, ) app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) - app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace)) + app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, ibc.DefaultCodespace) // register message routes app.Router(). diff --git a/docs/examples/democoin/app/app.go b/docs/examples/democoin/app/app.go index bc618e3606..ec9db1ae87 100644 --- a/docs/examples/democoin/app/app.go +++ b/docs/examples/democoin/app/app.go @@ -83,10 +83,10 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp { // Add handlers. app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) - app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.bankKeeper, app.RegisterCodespace(cool.DefaultCodespace)) - app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.bankKeeper, app.RegisterCodespace(pow.DefaultCodespace)) - app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) - app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper, app.RegisterCodespace(simplestake.DefaultCodespace)) + app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.bankKeeper, cool.DefaultCodespace) + app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.bankKeeper, pow.DefaultCodespace) + app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, ibc.DefaultCodespace) + app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper, simplestake.DefaultCodespace) app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). AddRoute("cool", cool.NewHandler(app.coolKeeper)). diff --git a/docs/examples/democoin/x/cool/app_test.go b/docs/examples/democoin/x/cool/app_test.go index 01cb73ef17..3f1dd6734c 100644 --- a/docs/examples/democoin/x/cool/app_test.go +++ b/docs/examples/democoin/x/cool/app_test.go @@ -50,7 +50,7 @@ func getMockApp(t *testing.T) *mock.App { RegisterCodec(mapp.Cdc) keyCool := sdk.NewKVStoreKey("cool") bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) - keeper := NewKeeper(keyCool, bankKeeper, mapp.RegisterCodespace(DefaultCodespace)) + keeper := NewKeeper(keyCool, bankKeeper, DefaultCodespace) mapp.Router().AddRoute("cool", NewHandler(keeper)) mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold")) diff --git a/docs/examples/democoin/x/cool/errors.go b/docs/examples/democoin/x/cool/errors.go index 73df2386ef..e31d664708 100644 --- a/docs/examples/democoin/x/cool/errors.go +++ b/docs/examples/democoin/x/cool/errors.go @@ -8,7 +8,7 @@ import ( // Cool errors reserve 400 ~ 499. const ( - DefaultCodespace sdk.CodespaceType = 6 + DefaultCodespace sdk.CodespaceType = "cool" // Cool module reserves error 400-499 lawl CodeIncorrectCoolAnswer sdk.CodeType = 400 diff --git a/docs/examples/democoin/x/oracle/handler.go b/docs/examples/democoin/x/oracle/handler.go index c525222c4c..fb7587a126 100644 --- a/docs/examples/democoin/x/oracle/handler.go +++ b/docs/examples/democoin/x/oracle/handler.go @@ -94,7 +94,7 @@ func (keeper Keeper) Handle(h Handler, ctx sdk.Context, o Msg, codespace sdk.Cod err := h(cctx, payload) if err != nil { return sdk.Result{ - Code: sdk.ABCICodeOK, + Code: sdk.CodeOK, Log: err.ABCILog(), } } diff --git a/docs/examples/democoin/x/pow/app_test.go b/docs/examples/democoin/x/pow/app_test.go index 5009b7ec54..41901f0979 100644 --- a/docs/examples/democoin/x/pow/app_test.go +++ b/docs/examples/democoin/x/pow/app_test.go @@ -27,7 +27,7 @@ func getMockApp(t *testing.T) *mock.App { keyPOW := sdk.NewKVStoreKey("pow") bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) config := Config{"pow", 1} - keeper := NewKeeper(keyPOW, config, bankKeeper, mapp.RegisterCodespace(DefaultCodespace)) + keeper := NewKeeper(keyPOW, config, bankKeeper, DefaultCodespace) mapp.Router().AddRoute("pow", keeper.Handler) mapp.SetInitChainer(getInitChainer(mapp, keeper)) diff --git a/docs/examples/democoin/x/pow/errors.go b/docs/examples/democoin/x/pow/errors.go index e25964da73..8368c2359d 100644 --- a/docs/examples/democoin/x/pow/errors.go +++ b/docs/examples/democoin/x/pow/errors.go @@ -9,7 +9,7 @@ type CodeType = sdk.CodeType // POW errors reserve 200 ~ 299 const ( - DefaultCodespace sdk.CodespaceType = 5 + DefaultCodespace sdk.CodespaceType = "pow" CodeInvalidDifficulty CodeType = 201 CodeNonexistentDifficulty CodeType = 202 CodeNonexistentReward CodeType = 203 diff --git a/docs/examples/democoin/x/simplestake/errors.go b/docs/examples/democoin/x/simplestake/errors.go index 8125e57aab..a0fc6e1acc 100644 --- a/docs/examples/democoin/x/simplestake/errors.go +++ b/docs/examples/democoin/x/simplestake/errors.go @@ -6,7 +6,7 @@ import ( // simple stake errors reserve 300 ~ 399. const ( - DefaultCodespace sdk.CodespaceType = 4 + DefaultCodespace sdk.CodespaceType = moduleName // simplestake errors reserve 300 - 399. CodeEmptyValidator sdk.CodeType = 300 diff --git a/docs/examples/democoin/x/simplestake/handler.go b/docs/examples/democoin/x/simplestake/handler.go index 114f066436..104058eec2 100644 --- a/docs/examples/democoin/x/simplestake/handler.go +++ b/docs/examples/democoin/x/simplestake/handler.go @@ -22,12 +22,12 @@ func handleMsgBond() sdk.Result { // Removed ValidatorSet from result because it does not get used. // TODO: Implement correct bond/unbond handling return sdk.Result{ - Code: sdk.ABCICodeOK, + Code: sdk.CodeOK, } } func handleMsgUnbond() sdk.Result { return sdk.Result{ - Code: sdk.ABCICodeOK, + Code: sdk.CodeOK, } } diff --git a/store/rootmultistore_test.go b/store/rootmultistore_test.go index 501c5b730c..cd555d6f2d 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmultistore_test.go @@ -156,34 +156,37 @@ func TestMultiStoreQuery(t *testing.T) { // Test bad path. query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} qres := multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) + require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) query.Path = "h897fy32890rf63296r92" qres = multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) + require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) // Test invalid store name. query.Path = "/garbage/key" qres = multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnknownRequest), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) + require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) // Test valid query with data. query.Path = "/store1/key" qres = multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeOK, qres.Code) require.Equal(t, v, qres.Value) // Test valid but empty query. query.Path = "/store2/key" query.Prove = true qres = multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeOK, qres.Code) require.Nil(t, qres.Value) // Test store2 data. query.Data = k2 qres = multi.Query(query) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOK), sdk.ABCICodeType(qres.Code)) + require.EqualValues(t, sdk.CodeOK, qres.Code) require.Equal(t, v2, qres.Value) } diff --git a/types/codespacer.go b/types/codespacer.go deleted file mode 100644 index 92025c1a63..0000000000 --- a/types/codespacer.go +++ /dev/null @@ -1,35 +0,0 @@ -package types - -// Codespacer is a simple struct to track reserved codespaces -type Codespacer struct { - reserved map[CodespaceType]bool -} - -// NewCodespacer generates a new Codespacer with the starting codespace -func NewCodespacer() *Codespacer { - return &Codespacer{ - reserved: make(map[CodespaceType]bool), - } -} - -// RegisterNext reserves and returns the next available codespace, starting from a default, and panics if the maximum codespace is reached -func (c *Codespacer) RegisterNext(codespace CodespaceType) CodespaceType { - for { - if !c.reserved[codespace] { - c.reserved[codespace] = true - return codespace - } - codespace++ - if codespace == MaximumCodespace { - panic("Maximum codespace reached!") - } - } -} - -// RegisterOrPanic reserved a codespace or panics if it is unavailable -func (c *Codespacer) RegisterOrPanic(codespace CodespaceType) { - if c.reserved[codespace] { - panic("Cannot register codespace, already reserved") - } - c.reserved[codespace] = true -} diff --git a/types/codespacer_test.go b/types/codespacer_test.go deleted file mode 100644 index 7052aa92a3..0000000000 --- a/types/codespacer_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestRegisterNext(t *testing.T) { - codespacer := NewCodespacer() - // unregistered, allow - code1 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code1, CodespaceType(2)) - // registered, pick next - code2 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code2, CodespaceType(3)) - // pick next - code3 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code3, CodespaceType(4)) - // skip 1 - code4 := codespacer.RegisterNext(CodespaceType(6)) - require.Equal(t, code4, CodespaceType(6)) - code5 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code5, CodespaceType(5)) - code6 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code6, CodespaceType(7)) - // panic on maximum - defer func() { - r := recover() - require.NotNil(t, r, "Did not panic on maximum codespace") - }() - codespacer.RegisterNext(MaximumCodespace - 1) - codespacer.RegisterNext(MaximumCodespace - 1) -} - -func TestRegisterOrPanic(t *testing.T) { - codespacer := NewCodespacer() - // unregistered, allow - code1 := codespacer.RegisterNext(CodespaceType(2)) - require.Equal(t, code1, CodespaceType(2)) - // panic on duplicate - defer func() { - r := recover() - require.NotNil(t, r, "Did not panic on duplicate codespace") - }() - codespacer.RegisterOrPanic(CodespaceType(2)) -} diff --git a/types/errors.go b/types/errors.go index 965a97c872..46436531ef 100644 --- a/types/errors.go +++ b/types/errors.go @@ -10,37 +10,22 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) -// ABCICodeType - combined codetype / codespace -type ABCICodeType uint32 - -// CodeType - code identifier within codespace -type CodeType uint16 +// CodeType - ABCI code identifier within codespace +type CodeType uint32 // CodespaceType - codespace identifier -type CodespaceType uint16 +type CodespaceType string // IsOK - is everything okay? -func (code ABCICodeType) IsOK() bool { - if code == ABCICodeOK { +func (code CodeType) IsOK() bool { + if code == CodeOK { return true } return false } -// get the abci code from the local code and codespace -func ToABCICode(space CodespaceType, code CodeType) ABCICodeType { - // TODO: Make Tendermint more aware of codespaces. - if space == CodespaceRoot && code == CodeOK { - return ABCICodeOK - } - return ABCICodeType((uint32(space) << 16) | uint32(code)) -} - // SDK error codes const ( - // ABCI error codes - ABCICodeOK ABCICodeType = 0 - // Base error codes CodeOK CodeType = 0 CodeInternal CodeType = 1 @@ -62,11 +47,8 @@ const ( // CodespaceRoot is a codespace for error codes in this file only. // Notice that 0 is an "unset" codespace, which can be overridden with // Error.WithDefaultCodespace(). - CodespaceUndefined CodespaceType = 0 - CodespaceRoot CodespaceType = 1 - - // Maximum reservable codespace (2^16 - 1) - MaximumCodespace CodespaceType = 65535 + CodespaceUndefined CodespaceType = "" + CodespaceRoot CodespaceType = "sdk" ) func unknownCodeMsg(code CodeType) string { @@ -185,7 +167,6 @@ type Error interface { Code() CodeType Codespace() CodespaceType ABCILog() string - ABCICode() ABCICodeType Result() Result QueryResult() abci.ResponseQuery } @@ -239,17 +220,12 @@ func (err *sdkError) TraceSDK(format string, args ...interface{}) Error { // Implements ABCIError. func (err *sdkError) Error() string { return fmt.Sprintf(`ERROR: -Codespace: %d +Codespace: %s Code: %d Message: %#v `, err.codespace, err.code, err.cmnError.Error()) } -// Implements ABCIError. -func (err *sdkError) ABCICode() ABCICodeType { - return ToABCICode(err.codespace, err.code) -} - // Implements Error. func (err *sdkError) Codespace() CodespaceType { return err.codespace @@ -267,7 +243,6 @@ func (err *sdkError) ABCILog() string { jsonErr := humanReadableError{ Codespace: err.codespace, Code: err.code, - ABCICode: err.ABCICode(), Message: errMsg, } bz, er := cdc.MarshalJSON(jsonErr) @@ -280,16 +255,18 @@ func (err *sdkError) ABCILog() string { func (err *sdkError) Result() Result { return Result{ - Code: err.ABCICode(), - Log: err.ABCILog(), + Code: err.Code(), + Codespace: err.Codespace(), + Log: err.ABCILog(), } } // QueryResult allows us to return sdk.Error.QueryResult() in query responses func (err *sdkError) QueryResult() abci.ResponseQuery { return abci.ResponseQuery{ - Code: uint32(err.ABCICode()), - Log: err.ABCILog(), + Code: uint32(err.Code()), + Codespace: string(err.Codespace()), + Log: err.ABCILog(), } } @@ -324,6 +301,5 @@ func mustGetMsgIndex(abciLog string) int { type humanReadableError struct { Codespace CodespaceType `json:"codespace"` Code CodeType `json:"code"` - ABCICode ABCICodeType `json:"abci_code"` Message string `json:"message"` } diff --git a/types/errors_test.go b/types/errors_test.go index 1d63e09908..8d4f882263 100644 --- a/types/errors_test.go +++ b/types/errors_test.go @@ -42,7 +42,7 @@ var errFns = []errFn{ } func TestCodeType(t *testing.T) { - require.True(t, ABCICodeOK.IsOK()) + require.True(t, CodeOK.IsOK()) for tcnum, c := range codeTypes { msg := CodeToDefaultMsg(c) @@ -59,12 +59,9 @@ func TestErrFn(t *testing.T) { codeType := codeTypes[i] require.Equal(t, err.Code(), codeType, "Err function expected to return proper code. tc #%d", i) require.Equal(t, err.Codespace(), CodespaceRoot, "Err function expected to return proper codespace. tc #%d", i) - require.Equal(t, err.Result().Code, ToABCICode(CodespaceRoot, codeType), "Err function expected to return proper ABCICode. tc #%d") - require.Equal(t, err.QueryResult().Code, uint32(err.ABCICode()), "Err function expected to return proper ABCICode from QueryResult. tc #%d") + require.Equal(t, err.QueryResult().Code, uint32(err.Code()), "Err function expected to return proper Code from QueryResult. tc #%d") require.Equal(t, err.QueryResult().Log, err.ABCILog(), "Err function expected to return proper ABCILog from QueryResult. tc #%d") } - - require.Equal(t, ABCICodeOK, ToABCICode(CodespaceRoot, CodeOK)) } func TestAppendMsgToErr(t *testing.T) { diff --git a/types/result.go b/types/result.go index 63b8a6e18f..20893a076f 100644 --- a/types/result.go +++ b/types/result.go @@ -4,7 +4,10 @@ package types type Result struct { // Code is the response code, is stored back on the chain. - Code ABCICodeType + Code CodeType + + // Codespace is the string referring to the domain of an error + Codespace CodespaceType // Data is any data returned from the app. Data []byte diff --git a/types/result_test.go b/types/result_test.go index e0305932cf..9765933d98 100644 --- a/types/result_test.go +++ b/types/result_test.go @@ -13,6 +13,6 @@ func TestResult(t *testing.T) { res.Data = []byte("data") require.True(t, res.IsOK()) - res.Code = ABCICodeType(1) + res.Code = CodeType(1) require.False(t, res.IsOK()) } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 98725a74bd..c376d29961 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -43,7 +43,7 @@ func privAndAddr() (crypto.PrivKey, sdk.AccAddress) { func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) { _, result, abort := anteHandler(ctx, tx, simulate) require.False(t, abort) - require.Equal(t, sdk.ABCICodeOK, result.Code) + require.Equal(t, sdk.CodeOK, result.Code) require.True(t, result.IsOK()) } @@ -51,8 +51,9 @@ func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool, code sdk.CodeType) { newCtx, result, abort := anteHandler(ctx, tx, simulate) require.True(t, abort) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code, - fmt.Sprintf("Expected %v, got %v", sdk.ToABCICode(sdk.CodespaceRoot, code), result)) + + require.Equal(t, code, result.Code, fmt.Sprintf("Expected %v, got %v", code, result)) + require.Equal(t, sdk.CodespaceRoot, result.Codespace) if code == sdk.CodeOutOfGas { stdTx, ok := tx.(StdTx) diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 906573eae2..c71a9b392b 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -144,7 +144,8 @@ func TestMsgSendWithAccounts(t *testing.T) { tx.Signatures[0].Sequence = 1 res := mapp.Deliver(tx) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log) + require.EqualValues(t, sdk.CodeUnauthorized, res.Code, res.Log) + require.EqualValues(t, sdk.CodespaceRoot, res.Codespace) // resigning the tx with the bumped sequence should work mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, true, priv1) diff --git a/x/bank/errors.go b/x/bank/errors.go index cf11ddc22f..5df5cfdce5 100644 --- a/x/bank/errors.go +++ b/x/bank/errors.go @@ -7,7 +7,7 @@ import ( // Bank errors reserve 100 ~ 199. const ( - DefaultCodespace sdk.CodespaceType = 2 + DefaultCodespace sdk.CodespaceType = "bank" CodeInvalidInput sdk.CodeType = 101 CodeInvalidOutput sdk.CodeType = 102 diff --git a/x/distribution/types/errors.go b/x/distribution/types/errors.go index 605c1b38db..36de11f740 100644 --- a/x/distribution/types/errors.go +++ b/x/distribution/types/errors.go @@ -8,7 +8,7 @@ import ( type CodeType = sdk.CodeType const ( - DefaultCodespace sdk.CodespaceType = 6 + DefaultCodespace sdk.CodespaceType = "DISTR" CodeInvalidInput CodeType = 103 CodeNoDistributionInfo CodeType = 104 ) diff --git a/x/gov/errors.go b/x/gov/errors.go index e803d080ea..9b9aa25dbd 100644 --- a/x/gov/errors.go +++ b/x/gov/errors.go @@ -8,7 +8,7 @@ import ( ) const ( - DefaultCodespace sdk.CodespaceType = 5 + DefaultCodespace sdk.CodespaceType = "GOV" CodeUnknownProposal sdk.CodeType = 1 CodeInactiveProposal sdk.CodeType = 2 diff --git a/x/gov/test_common.go b/x/gov/test_common.go index a0bc80fc2e..67b9fd902e 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -35,7 +35,7 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, pk := params.NewKeeper(mapp.Cdc, keyGlobalParams, tkeyGlobalParams) ck := bank.NewBaseKeeper(mapp.AccountKeeper) - sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, pk.Subspace(stake.DefaultParamspace), mapp.RegisterCodespace(stake.DefaultCodespace)) + sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, pk.Subspace(stake.DefaultParamspace), stake.DefaultCodespace) keeper := NewKeeper(mapp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace) mapp.Router().AddRoute("gov", NewHandler(keeper)) diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go index 9b360c7c24..e59588a5ac 100644 --- a/x/ibc/app_test.go +++ b/x/ibc/app_test.go @@ -20,7 +20,7 @@ func getMockApp(t *testing.T) *mock.App { RegisterCodec(mapp.Cdc) keyIBC := sdk.NewKVStoreKey("ibc") - ibcMapper := NewMapper(mapp.Cdc, keyIBC, mapp.RegisterCodespace(DefaultCodespace)) + ibcMapper := NewMapper(mapp.Cdc, keyIBC, DefaultCodespace) bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, bankKeeper)) diff --git a/x/ibc/errors.go b/x/ibc/errors.go index 7a3194baf1..96ae58066a 100644 --- a/x/ibc/errors.go +++ b/x/ibc/errors.go @@ -6,7 +6,7 @@ import ( // IBC errors reserve 200 ~ 299. const ( - DefaultCodespace sdk.CodespaceType = 3 + DefaultCodespace sdk.CodespaceType = "ibc" // IBC errors reserve 200 - 299. CodeInvalidSequence sdk.CodeType = 200 diff --git a/x/mock/app_test.go b/x/mock/app_test.go index 9e12cac8ec..c47ddb7179 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -71,7 +71,9 @@ func TestCheckAndDeliverGenTx(t *testing.T) { []int64{accs[1].GetAccountNumber()}, []int64{accs[1].GetSequence() + 1}, true, false, privKeys[1], ) - require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log) + + require.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log) + require.Equal(t, sdk.CodespaceRoot, res.Codespace) // Resigning the tx with the correct privKey should result in an OK result SignCheckDeliver( diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go index 4e60478fa4..130339d0e7 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -57,9 +57,9 @@ func CheckGenTx( res := app.Check(tx) if expPass { - require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + require.Equal(t, sdk.CodeOK, res.Code, res.Log) } else { - require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) } return res @@ -78,9 +78,9 @@ func SignCheckDeliver( res := app.Simulate(tx) if expSimPass { - require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + require.Equal(t, sdk.CodeOK, res.Code, res.Log) } else { - require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) } // Simulate a sending a transaction and committing a block @@ -88,9 +88,9 @@ func SignCheckDeliver( res = app.Deliver(tx) if expPass { - require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log) + require.Equal(t, sdk.CodeOK, res.Code, res.Log) } else { - require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log) + require.NotEqual(t, sdk.CodeOK, res.Code, res.Log) } app.EndBlock(abci.RequestEndBlock{}) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index dd96ff51e2..bfcbcba0b2 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -36,8 +36,8 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) { bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams, tkeyParams) - stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, paramsKeeper.Subspace(stake.DefaultParamspace), mapp.RegisterCodespace(stake.DefaultCodespace)) - keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Subspace(DefaultParamspace), mapp.RegisterCodespace(DefaultCodespace)) + stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, paramsKeeper.Subspace(stake.DefaultParamspace), stake.DefaultCodespace) + keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Subspace(DefaultParamspace), DefaultCodespace) mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper)) mapp.Router().AddRoute("slashing", NewHandler(keeper)) @@ -126,5 +126,6 @@ func TestSlashingMsgs(t *testing.T) { // unjail should fail with unknown validator res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, false, priv1) - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code) + require.EqualValues(t, CodeValidatorNotJailed, res.Code) + require.EqualValues(t, DefaultCodespace, res.Codespace) } diff --git a/x/slashing/errors.go b/x/slashing/errors.go index 77cb2d28e3..0fa523c256 100644 --- a/x/slashing/errors.go +++ b/x/slashing/errors.go @@ -10,7 +10,7 @@ type CodeType = sdk.CodeType const ( // Default slashing codespace - DefaultCodespace sdk.CodespaceType = 10 + DefaultCodespace sdk.CodespaceType = "SLASH" CodeInvalidValidator CodeType = 101 CodeValidatorJailed CodeType = 102 diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index ef307547c6..9041bfe56c 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -26,7 +26,8 @@ func TestCannotUnjailUnlessJailed(t *testing.T) { // assert non-jailed validator can't be unjailed got = slh(ctx, NewMsgUnjail(addr)) require.False(t, got.IsOK(), "allowed unjail of non-jailed validator") - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), got.Code) + require.EqualValues(t, CodeValidatorNotJailed, got.Code) + require.EqualValues(t, DefaultCodespace, got.Codespace) } func TestJailedValidatorDelegations(t *testing.T) { diff --git a/x/stake/app_test.go b/x/stake/app_test.go index b24018a6ce..2866acf1d1 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -29,7 +29,7 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) { bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper) pk := params.NewKeeper(mApp.Cdc, keyParams, tkeyParams) - keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, bankKeeper, pk.Subspace(DefaultParamspace), mApp.RegisterCodespace(DefaultCodespace)) + keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, bankKeeper, pk.Subspace(DefaultParamspace), DefaultCodespace) mApp.Router().AddRoute("stake", NewHandler(keeper)) mApp.SetEndBlocker(getEndBlocker(keeper)) diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 1a6ed6a641..bbef528d50 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -11,7 +11,7 @@ import ( type CodeType = sdk.CodeType const ( - DefaultCodespace sdk.CodespaceType = 4 + DefaultCodespace sdk.CodespaceType = "STAKE" CodeInvalidValidator CodeType = 101 CodeInvalidDelegation CodeType = 102 From 15b6fa0959206cb5d61cca80b0b567bc771bf024 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Fri, 16 Nov 2018 13:33:47 -0500 Subject: [PATCH 15/51] Cache-wrap context during ante handler exec (#2781) * Use cache-wrapped multi-store in ante * Implement TestBaseAppAnteHandler * Add reference documentation for BaseApp/CheckTx/DeliverTx --- PENDING.md | 7 ++- baseapp/baseapp.go | 69 ++++++++++++++------- baseapp/baseapp_test.go | 109 +++++++++++++++++++++++++++++++--- cmd/gaia/cli_test/cli_test.go | 4 ++ docs/reference/baseapp.md | 68 ++++++++++++++++++--- 5 files changed, 214 insertions(+), 43 deletions(-) diff --git a/PENDING.md b/PENDING.md index a3034c01eb..6295d55726 100644 --- a/PENDING.md +++ b/PENDING.md @@ -53,9 +53,10 @@ IMPROVEMENTS * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet * Gaia - - #2773 Require moniker to be provided on `gaiad init`. - - #2672 [Makefile] Updated for better Windows compatibility and ledger support logic, get_tools was rewritten as a cross-compatible Makefile. - - [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated. + - #2772 Update BaseApp to not persist state when the ante handler fails on DeliverTx. + - #2773 Require moniker to be provided on `gaiad init`. + - #2672 [Makefile] Updated for better Windows compatibility and ledger support logic, get_tools was rewritten as a cross-compatible Makefile. + - [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated. * SDK - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 34041dff20..0d55710241 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -10,7 +10,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" - cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -505,11 +504,11 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { // retrieve the context for the ante handler and store the tx bytes; store // the vote infos if the tx runs within the deliverTx() state. func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { - // Get the context - ctx = getState(app, mode).ctx.WithTxBytes(txBytes) + ctx = app.getState(mode).ctx.WithTxBytes(txBytes) if mode == runTxModeDeliver { ctx = ctx.WithVoteInfos(app.voteInfos) } + return } @@ -571,7 +570,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re // Returns the applicantion's deliverState if app is in runTxModeDeliver, // otherwise it returns the application's checkstate. -func getState(app *BaseApp, mode runTxMode) *state { +func (app *BaseApp) getState(mode runTxMode) *state { if mode == runTxModeCheck || mode == runTxModeSimulate { return app.checkState } @@ -581,20 +580,42 @@ func getState(app *BaseApp, mode runTxMode) *state { func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { if mode == runTxModeSimulate { - ctx = ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) + ctx = ctx.WithMultiStore(app.getState(runTxModeSimulate).CacheMultiStore()) } return ctx } +// cacheTxContext returns a new context based off of the provided context with a +// cache wrapped multi-store and the store itself to allow the caller to write +// changes from the cached multi-store. +func (app *BaseApp) cacheTxContext( + ctx sdk.Context, txBytes []byte, mode runTxMode, +) (sdk.Context, sdk.CacheMultiStore) { + + msCache := app.getState(mode).CacheMultiStore() + if msCache.TracingEnabled() { + msCache = msCache.WithTracingContext( + sdk.TraceContext( + map[string]interface{}{ + "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), + }, + ), + ).(sdk.CacheMultiStore) + } + + return ctx.WithMultiStore(msCache), msCache +} + // runTx processes a transaction. The transactions is proccessed via an -// anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the -// future we may support "internal" transactions. +// anteHandler. The provided txBytes may be nil in some cases, eg. in tests. For +// further details on transaction execution, reference the BaseApp SDK +// documentation. func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. var gasWanted int64 - var msCache sdk.CacheMultiStore + ctx := app.getContextForAnte(mode, txBytes) ctx = app.initializeContext(ctx, mode) @@ -619,16 +640,27 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return err.Result() } - // run the ante handler + // Execute the ante handler if one is defined. if app.anteHandler != nil { - newCtx, result, abort := app.anteHandler(ctx, tx, (mode == runTxModeSimulate)) + var anteCtx sdk.Context + var msCache sdk.CacheMultiStore + + // Cache wrap context before anteHandler call in case it aborts. + // This is required for both CheckTx and DeliverTx. + // https://github.com/cosmos/cosmos-sdk/issues/2772 + // NOTE: Alternatively, we could require that anteHandler ensures that + // writes do not happen if aborted/failed. This may have some + // performance benefits, but it'll be more difficult to get right. + anteCtx, msCache = app.cacheTxContext(ctx, txBytes, mode) + + newCtx, result, abort := app.anteHandler(anteCtx, tx, (mode == runTxModeSimulate)) if abort { return result } if !newCtx.IsZero() { ctx = newCtx } - + msCache.Write() gasWanted = result.GasWanted } @@ -638,17 +670,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return } - // Keep the state in a transient CacheWrap in case processing the messages - // fails. - msCache = getState(app, mode).CacheMultiStore() - if msCache.TracingEnabled() { - msCache = msCache.WithTracingContext(sdk.TraceContext( - map[string]interface{}{"txHash": cmn.HexBytes(tmhash.Sum(txBytes)).String()}, - )).(sdk.CacheMultiStore) - } - - ctx = ctx.WithMultiStore(msCache) - result = app.runMsgs(ctx, msgs, mode) + // Create a new context based off of the existing context with a cache wrapped + // multi-store in case message processing fails. + runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes, mode) + result = app.runMsgs(runMsgCtx, msgs, mode) result.GasWanted = gasWanted // only update state if all messages pass diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 35f9fa4246..9c2e7e1a0d 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -282,8 +282,19 @@ func TestInitChainer(t *testing.T) { // Simple tx with a list of Msgs. type txTest struct { - Msgs []sdk.Msg - Counter int64 + Msgs []sdk.Msg + Counter int64 + FailOnAnte bool +} + +func (tx *txTest) setFailOnAnte(fail bool) { + tx.FailOnAnte = fail +} + +func (tx *txTest) setFailOnHandler(fail bool) { + for i, msg := range tx.Msgs { + tx.Msgs[i] = msgCounter{msg.(msgCounter).Counter, fail} + } } // Implements Tx @@ -297,7 +308,8 @@ const ( // ValidateBasic() fails on negative counters. // Otherwise it's up to the handlers type msgCounter struct { - Counter int64 + Counter int64 + FailOnHandler bool } // Implements Msg @@ -315,9 +327,9 @@ func (msg msgCounter) ValidateBasic() sdk.Error { func newTxCounter(txInt int64, msgInts ...int64) *txTest { var msgs []sdk.Msg for _, msgInt := range msgInts { - msgs = append(msgs, msgCounter{msgInt}) + msgs = append(msgs, msgCounter{msgInt, false}) } - return &txTest{msgs, txInt} + return &txTest{msgs, txInt, false} } // a msg we dont know how to route @@ -369,8 +381,13 @@ func testTxDecoder(cdc *codec.Codec) sdk.TxDecoder { func anteHandlerTxTest(t *testing.T, capKey *sdk.KVStoreKey, storeKey []byte) sdk.AnteHandler { return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { store := ctx.KVStore(capKey) - msgCounter := tx.(txTest).Counter - res = incrementingCounter(t, store, storeKey, msgCounter) + txTest := tx.(txTest) + + if txTest.FailOnAnte { + return newCtx, sdk.ErrInternal("ante handler failure").Result(), true + } + + res = incrementingCounter(t, store, storeKey, txTest.Counter) return } } @@ -381,10 +398,15 @@ func handlerMsgCounter(t *testing.T, capKey *sdk.KVStoreKey, deliverKey []byte) var msgCount int64 switch m := msg.(type) { case *msgCounter: + if m.FailOnHandler { + return sdk.ErrInternal("message handler failure").Result() + } + msgCount = m.Counter case *msgCounter2: msgCount = m.Counter } + return incrementingCounter(t, store, deliverKey, msgCount) } } @@ -712,12 +734,12 @@ func TestRunInvalidTransaction(t *testing.T) { // Transaction with no known route { - unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0} + unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false} err := app.Deliver(unknownRouteTx) require.EqualValues(t, sdk.CodeUnknownRequest, err.Code) require.EqualValues(t, sdk.CodespaceRoot, err.Codespace) - unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0} + unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false} err = app.Deliver(unknownRouteTx) require.EqualValues(t, sdk.CodeUnknownRequest, err.Code) require.EqualValues(t, sdk.CodespaceRoot, err.Codespace) @@ -829,3 +851,72 @@ func TestTxGasLimits(t *testing.T) { } } } + +func TestBaseAppAnteHandler(t *testing.T) { + anteKey := []byte("ante-key") + anteOpt := func(bapp *BaseApp) { + bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) + } + + deliverKey := []byte("deliver-key") + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(routeMsgCounter, handlerMsgCounter(t, capKey1, deliverKey)) + } + + cdc := codec.New() + app := setupBaseApp(t, anteOpt, routerOpt) + + app.InitChain(abci.RequestInitChain{}) + registerTestCodec(cdc) + app.BeginBlock(abci.RequestBeginBlock{}) + + // execute a tx that will fail ante handler execution + // + // NOTE: State should not be mutated here. This will be implicitly checked by + // the next txs ante handler execution (anteHandlerTxTest). + tx := newTxCounter(0, 0) + tx.setFailOnAnte(true) + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + res := app.DeliverTx(txBytes) + require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) + + ctx := app.getState(runTxModeDeliver).ctx + store := ctx.KVStore(capKey1) + require.Equal(t, int64(0), getIntFromStore(store, anteKey)) + + // execute at tx that will pass the ante handler (the checkTx state should + // mutate) but will fail the message handler + tx = newTxCounter(0, 0) + tx.setFailOnHandler(true) + + txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + + res = app.DeliverTx(txBytes) + require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) + + ctx = app.getState(runTxModeDeliver).ctx + store = ctx.KVStore(capKey1) + require.Equal(t, int64(1), getIntFromStore(store, anteKey)) + require.Equal(t, int64(0), getIntFromStore(store, deliverKey)) + + // execute a successful ante handler and message execution where state is + // implicitly checked by previous tx executions + tx = newTxCounter(1, 0) + + txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + + res = app.DeliverTx(txBytes) + require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) + + ctx = app.getState(runTxModeDeliver).ctx + store = ctx.KVStore(capKey1) + require.Equal(t, int64(2), getIntFromStore(store, anteKey)) + require.Equal(t, int64(1), getIntFromStore(store, deliverKey)) + + // commit + app.EndBlock(abci.RequestEndBlock{}) + app.Commit() +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 874c2a29a4..62f4d0d64f 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -537,16 +537,20 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( "gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) require.True(t, success) + var result struct { Response abci.ResponseDeliverTx } + require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) require.Equal(t, msg.Fee.Gas, result.Response.GasUsed) require.Equal(t, msg.Fee.Gas, result.Response.GasWanted) + tests.WaitForNextNBlocksTM(2, port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) + fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) } diff --git a/docs/reference/baseapp.md b/docs/reference/baseapp.md index 1828021c2b..e1a80e2933 100644 --- a/docs/reference/baseapp.md +++ b/docs/reference/baseapp.md @@ -1,13 +1,63 @@ -# baseApp +# BaseApp -`baseApp` requires stores to be mounted via capabilities keys - handlers can only access stores they're given the key to. The `baseApp` ensures all stores are properly loaded, cached, and committed. One mounted store is considered the "main" - it holds the latest block header, from which we can find and load the most recent state. +The BaseApp defines the foundational implementation for a basic ABCI application +so that your Cosmos-SDK application can communicate with an underlying +Tendermint node. -`baseApp` distinguishes between two handler types: `AnteHandler` and `MsgHandler`. Whilst the former is a global validity check that applies to all transactions from all modules, i.e. it checks nonces and whether balances are sufficient to pay fees, validates signatures and ensures that transactions don't carry too many signatures, the latter is the full state transition function. -During CheckTx the state transition function is only applied to the checkTxState and should return -before any expensive state transitions are run (this is up to each developer). It also needs to return the estimated -gas cost. +The BaseApp is composed of many internal components. Some of the most important +include the `CommitMultiStore` and its internal state. The internal state is +essentially two sub-states, both of which are used for transaction execution +during different phases, `CheckTx` and `DeliverTx` respectively. During block +commitment, only the `DeliverTx` is persisted. -During DeliverTx the state transition function is applied to the blockchain state and the transactions -need to be fully executed. +The BaseApp requires stores to be mounted via capabilities keys - handlers can +only access stores they're given the key to. The `baseApp` ensures all stores are +properly loaded, cached, and committed. One mounted store is considered the +"main" - it holds the latest block header, from which we can find and load the +most recent state. -BaseApp is responsible for managing the context passed into handlers - it makes the block header available and provides the right stores for CheckTx and DeliverTx. BaseApp is completely agnostic to serialization formats. \ No newline at end of file +The BaseApp distinguishes between two handler types - the `AnteHandler` and the +`MsgHandler`. The former is a global validity check (checking nonces, sigs and +sufficient balances to pay fees, e.g. things that apply to all transaction from +all modules), the later is the full state transition function. + +During `CheckTx` the state transition function is only applied to the `checkTxState` +and should return before any expensive state transitions are run +(this is up to each developer). It also needs to return the estimated gas cost. + +During `DeliverTx` the state transition function is applied to the blockchain +state and the transactions need to be fully executed. + +The BaseApp is responsible for managing the context passed into handlers - +it makes the block header available and provides the right stores for `CheckTx` +and `DeliverTx`. BaseApp is completely agnostic to serialization formats. + +## Transaction Life Cycle + +During the execution of a transaction, it may pass through both `CheckTx` and +`DeliverTx` as defined in the ABCI specification. `CheckTx` is executed by the +proposing validator and is used for the Tendermint mempool for all full nodes. + +Both `CheckTx` and `DeliverTx` execute the application's AnteHandler (if +defined), where the AnteHandler is responsible for pre-message validation +checks such as account and signature validation, fee deduction and collection, +and incrementing sequence numbers. + +### CheckTx + +During the execution of `CheckTx`, only the AnteHandler is executed. + +State transitions due to the AnteHandler are persisted between subsequent calls +of `CheckTx` in the check-tx state, unless the AnteHandler fails and aborts. + +### DeliverTx + +During the execution of `DeliverTx`, the AnteHandler and Handler is executed. + +The transaction execution during `DeliverTx` operates in a similar fashion to +`CheckTx`. However, state transitions that occur during the AnteHandler are +persisted even when the following Handler processing logic fails. + +It is possible that a malicious proposer may include a transaction in a block +that fails the AnteHandler. In this case, all state transitions for the +offending transaction are discarded. From 9676ce7d48e61c20b4371e113c53e27d1b8d0cf9 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 16 Nov 2018 14:21:36 -0800 Subject: [PATCH 16/51] Expose LCD router, allowing devs to register custom routes from their modules (#2836) * Fixes #1081 --- PENDING.md | 1 + client/flags.go | 65 +++-- client/keys/utils.go | 15 +- client/lcd/root.go | 268 ++++++++++----------- client/lcd/test_helpers.go | 36 ++- cmd/gaia/cmd/gaiacli/main.go | 44 +++- docs/examples/basecoin/cmd/basecli/main.go | 28 ++- docs/examples/democoin/cmd/democli/main.go | 22 +- 8 files changed, 300 insertions(+), 179 deletions(-) diff --git a/PENDING.md b/PENDING.md index 6295d55726..a8f278ebb4 100644 --- a/PENDING.md +++ b/PENDING.md @@ -48,6 +48,7 @@ FEATURES IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) + * [\#2836](https://github.com/cosmos/cosmos-sdk/pull/2836) Expose LCD router to allow users to register routes there. * Gaia CLI (`gaiacli`) * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet diff --git a/client/flags.go b/client/flags.go index 9d03939e02..dfadab07e4 100644 --- a/client/flags.go +++ b/client/flags.go @@ -17,25 +17,32 @@ const ( DefaultGasLimit = 200000 GasFlagSimulate = "simulate" - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagHeight = "height" - FlagGas = "gas" - FlagGasAdjustment = "gas-adjustment" - FlagTrustNode = "trust-node" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagMemo = "memo" - FlagFee = "fee" - FlagAsync = "async" - FlagJson = "json" - FlagPrintResponse = "print-response" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" - FlagIndentResponse = "indent" + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagGas = "gas" + FlagGasAdjustment = "gas-adjustment" + FlagTrustNode = "trust-node" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagMemo = "memo" + FlagFee = "fee" + FlagAsync = "async" + FlagJson = "json" + FlagPrintResponse = "print-response" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagIndentResponse = "indent" + FlagListenAddr = "laddr" + FlagCORS = "cors" + FlagMaxOpenConnections = "max-open" + FlagInsecure = "insecure" + FlagSSLHosts = "ssl-hosts" + FlagSSLCertFile = "ssl-certfile" + FlagSSLKeyFile = "ssl-keyfile" ) // LineBreak can be included in a command list to provide a blank line @@ -92,6 +99,26 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { return cmds } +// RegisterRestServerFlags registers the flags required for rest server +func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { + cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") + cmd.Flags().Bool(FlagInsecure, false, "Do not set up SSL/TLS layer") + cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") + cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") + cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") + cmd.Flags().String(FlagCORS, "", "Set the domains that can make CORS requests (* for all)") + cmd.Flags().String(FlagChainID, "", "Chain ID of Tendermint node") + cmd.Flags().String(FlagNode, "tcp://localhost:26657", "Address of the node to connect to") + cmd.Flags().Int(FlagMaxOpenConnections, 1000, "The number of maximum open connections") + cmd.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") + + viper.BindPFlag(FlagTrustNode, cmd.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagChainID, cmd.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, cmd.Flags().Lookup(FlagNode)) + return cmd +} + // Gas flag parsing functions // GasSetting encapsulates the possible values passed through the --gas flag. diff --git a/client/keys/utils.go b/client/keys/utils.go index 742b512d3d..0ac4a9c907 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -2,20 +2,17 @@ package keys import ( "fmt" - "github.com/syndtr/goleveldb/leveldb/opt" + "net/http" "path/filepath" - "github.com/spf13/viper" - + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/viper" + "github.com/syndtr/goleveldb/leveldb/opt" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" - - "github.com/cosmos/cosmos-sdk/client" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "net/http" ) // KeyDBName is the directory under root where we store the keys diff --git a/client/lcd/root.go b/client/lcd/root.go index 8366b6114c..3d61816467 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -10,15 +10,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + keybase "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" - auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" - stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/spf13/cobra" @@ -27,159 +21,155 @@ import ( tmserver "github.com/tendermint/tendermint/rpc/lib/server" ) -const ( - flagListenAddr = "laddr" - flagCORS = "cors" - flagMaxOpenConnections = "max-open" - flagInsecure = "insecure" - flagSSLHosts = "ssl-hosts" - flagSSLCertFile = "ssl-certfile" - flagSSLKeyFile = "ssl-keyfile" -) +// RestServer represents the Light Client Rest server +type RestServer struct { + Mux *mux.Router + CliCtx context.CLIContext + KeyBase keybase.Keybase + Cdc *codec.Codec + + log log.Logger + listener net.Listener + fingerprint string +} + +// NewRestServer creates a new rest server instance +func NewRestServer(cdc *codec.Codec) *RestServer { + r := mux.NewRouter() + cliCtx := context.NewCLIContext().WithCodec(cdc) + + // Register version methods on the router + r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") + r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") + + return &RestServer{ + Mux: r, + CliCtx: cliCtx, + Cdc: cdc, + + log: logger, + } +} + +func (rs *RestServer) setKeybase(kb keybase.Keybase) { + // If a keybase is passed in, set it and return + if kb != nil { + rs.KeyBase = kb + return + } + + // Otherwise get the keybase and set it + kb, err := keys.GetKeyBase() //XXX + if err != nil { + fmt.Printf("Failed to open Keybase: %s, exiting...", err) + os.Exit(1) + } + rs.KeyBase = kb +} + +// Start starts the rest server +func (rs *RestServer) Start(listenAddr string, sslHosts string, + certFile string, keyFile string, maxOpen int, insecure bool) (err error) { + + server.TrapSignal(func() { + err := rs.listener.Close() + rs.log.Error("error closing listener", "err", err) + }) + + // TODO: re-enable insecure mode once #2715 has been addressed + if insecure { + fmt.Println( + "Insecure mode is temporarily disabled, please locally generate an " + + "SSL certificate to test. Support will be re-enabled soon!", + ) + // listener, err = tmserver.StartHTTPServer( + // listenAddr, handler, logger, + // tmserver.Config{MaxOpenConnections: maxOpen}, + // ) + // if err != nil { + // return + // } + } else { + if certFile != "" { + // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 + err = validateCertKeyFiles(certFile, keyFile) + if err != nil { + return err + } + + // cert/key pair is provided, read the fingerprint + rs.fingerprint, err = fingerprintFromFile(certFile) + if err != nil { + return err + } + } else { + // if certificate is not supplied, generate a self-signed one + certFile, keyFile, rs.fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) + if err != nil { + return err + } + + defer func() { + os.Remove(certFile) + os.Remove(keyFile) + }() + } + + rs.listener, err = tmserver.StartHTTPAndTLSServer( + listenAddr, rs.Mux, + certFile, keyFile, + rs.log, + tmserver.Config{MaxOpenConnections: maxOpen}, + ) + if err != nil { + return + } + + rs.log.Info(rs.fingerprint) + rs.log.Info("REST server started") + } + + // logger.Info("REST server started") + + return nil +} // ServeCommand will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest -func ServeCommand(cdc *codec.Codec) *cobra.Command { - +func (rs *RestServer) ServeCommand() *cobra.Command { cmd := &cobra.Command{ Use: "rest-server", Short: "Start LCD (light-client daemon), a local REST server", RunE: func(cmd *cobra.Command, args []string) (err error) { - listenAddr := viper.GetString(flagListenAddr) - handler := createHandler(cdc) + rs.setKeybase(nil) + // Start the rest server and return error if one exists + err = rs.Start( + viper.GetString(client.FlagListenAddr), + viper.GetString(client.FlagSSLHosts), + viper.GetString(client.FlagSSLCertFile), + viper.GetString(client.FlagSSLKeyFile), + viper.GetInt(client.FlagMaxOpenConnections), + viper.GetBool(client.FlagInsecure)) - registerSwaggerUI(handler) - - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - maxOpen := viper.GetInt(flagMaxOpenConnections) - sslHosts := viper.GetString(flagSSLHosts) - certFile := viper.GetString(flagSSLCertFile) - keyFile := viper.GetString(flagSSLKeyFile) - - var listener net.Listener - var fingerprint string - - server.TrapSignal(func() { - err := listener.Close() - logger.Error("error closing listener", "err", err) - }) - - var cleanupFunc func() - - // TODO: re-enable insecure mode once #2715 has been addressed - if viper.GetBool(flagInsecure) { - fmt.Println( - "Insecure mode is temporarily disabled, please locally generate an " + - "SSL certificate to test. Support will be re-enabled soon!", - ) - // listener, err = tmserver.StartHTTPServer( - // listenAddr, handler, logger, - // tmserver.Config{MaxOpenConnections: maxOpen}, - // ) - // if err != nil { - // return - // } - } else { - if certFile != "" { - // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 - err = validateCertKeyFiles(certFile, keyFile) - if err != nil { - return err - } - - // cert/key pair is provided, read the fingerprint - fingerprint, err = fingerprintFromFile(certFile) - if err != nil { - return err - } - } else { - // if certificate is not supplied, generate a self-signed one - certFile, keyFile, fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) - if err != nil { - return err - } - - cleanupFunc = func() { - os.Remove(certFile) - os.Remove(keyFile) - } - - defer cleanupFunc() - } - - listener, err = tmserver.StartHTTPAndTLSServer( - listenAddr, handler, - certFile, keyFile, - logger, - tmserver.Config{MaxOpenConnections: maxOpen}, - ) - if err != nil { - return - } - - logger.Info(fingerprint) - logger.Info("REST server started") - } - - // logger.Info("REST server started") - - return nil + return err }, } - cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Bool(flagInsecure, false, "Do not set up SSL/TLS layer") - cmd.Flags().String(flagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") - cmd.Flags().String(flagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") - cmd.Flags().String(flagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") - cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") - cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") - cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") - cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") - - viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) - viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) - viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) + client.RegisterRestServerFlags(cmd) return cmd } -func createHandler(cdc *codec.Codec) *mux.Router { - r := mux.NewRouter() - - kb, err := keys.GetKeyBase() //XXX - if err != nil { - panic(err) - } - - cliCtx := context.NewCLIContext().WithCodec(cdc) - - // TODO: make more functional? aka r = keys.RegisterRoutes(r) - r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") - r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") - - keys.RegisterRoutes(r, cliCtx.Indent) - rpc.RegisterRoutes(cliCtx, r) - tx.RegisterRoutes(cliCtx, r, cdc) - auth.RegisterRoutes(cliCtx, r, cdc, "acc") - bank.RegisterRoutes(cliCtx, r, cdc, kb) - stake.RegisterRoutes(cliCtx, r, cdc, kb) - slashing.RegisterRoutes(cliCtx, r, cdc, kb) - gov.RegisterRoutes(cliCtx, r, cdc) - - return r -} - -func registerSwaggerUI(r *mux.Router) { +func (rs *RestServer) registerSwaggerUI() { statikFS, err := fs.New() if err != nil { panic(err) } staticServer := http.FileServer(statikFS) - r.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) + rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) } func validateCertKeyFiles(certFile, keyFile string) error { diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 774fd4d6ad..4fbacf3d58 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -19,6 +19,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -45,6 +47,12 @@ import ( "github.com/tendermint/tendermint/proxy" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" + + authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + stakeRest "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) // makePathname creates a unique pathname for each test. It will panic if it @@ -103,6 +111,13 @@ func GetKeyBase(t *testing.T) crkeys.Keybase { return keybase } +// GetTestKeyBase fetches the current testing keybase +func GetTestKeyBase(t *testing.T) crkeys.Keybase { + keybase, err := keys.GetKeyBaseWithWritePerm() + require.NoError(t, err) + return keybase +} + // CreateAddr adds an address to the key store and returns an address and seed. // It also requires that the key could be created. func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) { @@ -288,7 +303,7 @@ func InitializeTestLCD( require.NoError(t, err) tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress)) - lcd, err := startLCD(logger, listenAddr, cdc) + lcd, err := startLCD(logger, listenAddr, cdc, t) require.NoError(t, err) tests.WaitForLCDStart(port) @@ -347,8 +362,23 @@ func startTM( // startLCD starts the LCD. // // NOTE: This causes the thread to block. -func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) { - return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) +func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing.T) (net.Listener, error) { + rs := NewRestServer(cdc) + rs.setKeybase(GetTestKeyBase(t)) + registerRoutes(rs) + return tmrpc.StartHTTPServer(listenAddr, rs.Mux, logger, tmrpc.Config{}) +} + +// NOTE: If making updates here also update cmd/gaia/cmd/gaiacli/main.go +func registerRoutes(rs *RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + authRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bankRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stakeRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + govRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } // Request makes a test LCD test request. It returns a response object and a diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index de99b0fc8f..3dd936f863 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -1,9 +1,11 @@ package main import ( + "net/http" "os" "path" + "github.com/rakyll/statik/fs" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -13,9 +15,15 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) @@ -37,15 +45,25 @@ var ( ) func main() { + // Configure cobra to sort commands cobra.EnableCommandSorting = false + + // Instantiate the codec for the command line application cdc := app.MakeCodec() + // Read in the configuration file for the sdk config := sdk.GetConfig() config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) config.Seal() + // Create a new RestServer instance to serve the light client routes + rs := lcd.NewRestServer(cdc) + + // registerRoutes registers the routes on the rest server + registerRoutes(rs) + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc @@ -58,7 +76,7 @@ func main() { queryCmd(cdc), txCmd(cdc), client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), client.LineBreak, keys.Commands(), client.LineBreak, @@ -79,6 +97,30 @@ func main() { } } +// registerRoutes registers the routes from the different modules for the LCD. +// NOTE: details on the routes added for each module are in the module documentation +// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go +func registerRoutes(rs *lcd.RestServer) { + registerSwaggerUI(rs) + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) +} + +func registerSwaggerUI(rs *lcd.RestServer) { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + staticServer := http.FileServer(statikFS) + rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) +} + func initConfig(cmd *cobra.Command) error { home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) if err != nil { diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index 3a16c8a97b..ad32bf5169 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -9,12 +9,17 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" + slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" ) @@ -34,6 +39,17 @@ func main() { // get the codec cdc := app.MakeCodec() + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("baseacc", "basepub") + config.SetBech32PrefixForValidator("baseval", "basevalpub") + config.SetBech32PrefixForConsensusNode("basecons", "baseconspub") + config.Seal() + + rs := lcd.NewRestServer(cdc) + + registerRoutes(rs) + // TODO: Setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc. @@ -83,7 +99,7 @@ func main() { // add proxy, version and key info rootCmd.AddCommand( client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), keys.Commands(), client.LineBreak, version.VersionCmd, @@ -97,3 +113,13 @@ func main() { panic(err) } } + +func registerRoutes(rs *lcd.RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) +} diff --git a/docs/examples/democoin/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index adb6169c9e..d101da2d9c 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -13,8 +13,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/app" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/types" @@ -47,6 +48,10 @@ func main() { config.SetBech32PrefixForConsensusNode("democons", "democonspub") config.Seal() + rs := lcd.NewRestServer(cdc) + + registerRoutes(rs) + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc @@ -74,11 +79,6 @@ func main() { )...) rootCmd.AddCommand( client.PostCommands( - ibccmd.IBCTransferCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - ibccmd.IBCRelayCmd(cdc), simplestakingcmd.BondTxCmd(cdc), )...) rootCmd.AddCommand( @@ -96,7 +96,7 @@ func main() { // add proxy, version and key info rootCmd.AddCommand( client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), keys.Commands(), client.LineBreak, version.VersionCmd, @@ -110,3 +110,11 @@ func main() { panic(err) } } + +func registerRoutes(rs *lcd.RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) +} From fd968f7d8fb748bd5e2046dec358b6a85a8c73bf Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 19 Nov 2018 16:42:53 +0100 Subject: [PATCH 17/51] R4R: Remove unused bank.MsgIssue (and prevent possible panic) (#2855) * Remove all bank.MsgIssue code --- PENDING.md | 1 + x/bank/codec.go | 1 - x/bank/handler.go | 7 ----- x/bank/msgs.go | 59 ----------------------------------- x/bank/msgs_test.go | 45 -------------------------- x/ibc/ibc_test.go | 1 - x/stake/keeper/test_common.go | 1 - 7 files changed, 1 insertion(+), 114 deletions(-) diff --git a/PENDING.md b/PENDING.md index a8f278ebb4..4720ec774c 100644 --- a/PENDING.md +++ b/PENDING.md @@ -80,6 +80,7 @@ BUG FIXES * SDK - \#2733 [x/gov, x/mock/simulation] Fix governance simulation, update x/gov import/export + - \#2854 [x/bank] Remove unused bank.MsgIssue, prevent possible panic * Tendermint * [\#2797](https://github.com/tendermint/tendermint/pull/2797) AddressBook requires addresses to have IDs; Do not crap out immediately after sending pex addrs in seed mode diff --git a/x/bank/codec.go b/x/bank/codec.go index bcc2cbddaa..2195e4853a 100644 --- a/x/bank/codec.go +++ b/x/bank/codec.go @@ -7,7 +7,6 @@ import ( // Register concrete types on codec codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil) - cdc.RegisterConcrete(MsgIssue{}, "cosmos-sdk/Issue", nil) } var msgCdc = codec.New() diff --git a/x/bank/handler.go b/x/bank/handler.go index e02043d7b4..ea3ee4398c 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -10,8 +10,6 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgSend: return handleMsgSend(ctx, k, msg) - case MsgIssue: - return handleMsgIssue(ctx, k, msg) default: errMsg := "Unrecognized bank Msg type: %s" + msg.Type() return sdk.ErrUnknownRequest(errMsg).Result() @@ -32,8 +30,3 @@ func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { Tags: tags, } } - -// Handle MsgIssue. -func handleMsgIssue(ctx sdk.Context, k Keeper, msg MsgIssue) sdk.Result { - panic("not implemented yet") -} diff --git a/x/bank/msgs.go b/x/bank/msgs.go index a1c346a88d..1af7acfe73 100644 --- a/x/bank/msgs.go +++ b/x/bank/msgs.go @@ -86,65 +86,6 @@ func (msg MsgSend) GetSigners() []sdk.AccAddress { return addrs } -//---------------------------------------- -// MsgIssue - -// MsgIssue - high level transaction of the coin module -type MsgIssue struct { - Banker sdk.AccAddress `json:"banker"` - Outputs []Output `json:"outputs"` -} - -var _ sdk.Msg = MsgIssue{} - -// NewMsgIssue - construct arbitrary multi-in, multi-out send msg. -func NewMsgIssue(banker sdk.AccAddress, out []Output) MsgIssue { - return MsgIssue{Banker: banker, Outputs: out} -} - -// Implements Msg. -// nolint -func (msg MsgIssue) Route() string { return "bank" } // TODO: "bank/issue" -func (msg MsgIssue) Type() string { return "issue" } - -// Implements Msg. -func (msg MsgIssue) ValidateBasic() sdk.Error { - // XXX - if len(msg.Outputs) == 0 { - return ErrNoOutputs(DefaultCodespace).TraceSDK("") - } - for _, out := range msg.Outputs { - if err := out.ValidateBasic(); err != nil { - return err.TraceSDK("") - } - } - return nil -} - -// Implements Msg. -func (msg MsgIssue) GetSignBytes() []byte { - var outputs []json.RawMessage - for _, output := range msg.Outputs { - outputs = append(outputs, output.GetSignBytes()) - } - b, err := msgCdc.MarshalJSON(struct { - Banker sdk.AccAddress `json:"banker"` - Outputs []json.RawMessage `json:"outputs"` - }{ - Banker: msg.Banker, - Outputs: outputs, - }) - if err != nil { - panic(err) - } - return sdk.MustSortJSON(b) -} - -// Implements Msg. -func (msg MsgIssue) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Banker} -} - //---------------------------------------- // Input diff --git a/x/bank/msgs_test.go b/x/bank/msgs_test.go index 157cc9b5b9..14e9a4c709 100644 --- a/x/bank/msgs_test.go +++ b/x/bank/msgs_test.go @@ -223,48 +223,3 @@ func TestMsgSendSigners(t *testing.T) { require.Equal(t, signers, tx.Signers()) } */ - -// ---------------------------------------- -// MsgIssue Tests - -func TestNewMsgIssue(t *testing.T) { - // TODO -} - -func TestMsgIssueRoute(t *testing.T) { - // Construct an MsgIssue - addr := sdk.AccAddress([]byte("loan-from-bank")) - coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} - var msg = MsgIssue{ - Banker: sdk.AccAddress([]byte("input")), - Outputs: []Output{NewOutput(addr, coins)}, - } - - // TODO some failures for bad result - require.Equal(t, msg.Route(), "bank") -} - -func TestMsgIssueValidation(t *testing.T) { - // TODO -} - -func TestMsgIssueGetSignBytes(t *testing.T) { - addr := sdk.AccAddress([]byte("loan-from-bank")) - coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} - var msg = MsgIssue{ - Banker: sdk.AccAddress([]byte("input")), - Outputs: []Output{NewOutput(addr, coins)}, - } - res := msg.GetSignBytes() - - expected := `{"banker":"cosmos1d9h8qat57ljhcm","outputs":[{"address":"cosmos1d3hkzm3dveex7mfdvfsku6cjngpcj","coins":[{"amount":"10","denom":"atom"}]}]}` - require.Equal(t, expected, string(res)) -} - -func TestMsgIssueGetSigners(t *testing.T) { - var msg = MsgIssue{ - Banker: sdk.AccAddress([]byte("onlyone")), - } - res := msg.GetSigners() - require.Equal(t, fmt.Sprintf("%v", res), "[6F6E6C796F6E65]") -} diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 6cd89fded4..94c3c7a2ec 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -44,7 +44,6 @@ func makeCodec() *codec.Codec { // Register Msgs cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(bank.MsgSend{}, "test/ibc/Send", nil) - cdc.RegisterConcrete(bank.MsgIssue{}, "test/ibc/Issue", nil) cdc.RegisterConcrete(IBCTransferMsg{}, "test/ibc/IBCTransferMsg", nil) cdc.RegisterConcrete(IBCReceiveMsg{}, "test/ibc/IBCReceiveMsg", nil) diff --git a/x/stake/keeper/test_common.go b/x/stake/keeper/test_common.go index af7e688f32..65445cbb1f 100644 --- a/x/stake/keeper/test_common.go +++ b/x/stake/keeper/test_common.go @@ -60,7 +60,6 @@ func MakeTestCodec() *codec.Codec { // Register Msgs cdc.RegisterInterface((*sdk.Msg)(nil), nil) cdc.RegisterConcrete(bank.MsgSend{}, "test/stake/Send", nil) - cdc.RegisterConcrete(bank.MsgIssue{}, "test/stake/Issue", nil) cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil) cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil) cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil) From f525717054b9f3d09212ba7523ac98a2489d95ad Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 19 Nov 2018 09:02:34 -0800 Subject: [PATCH 18/51] Standardize CLI Exports from Modules (#2840) * Move query and tx commands to modules * Move GetAccountDecoder to prevent import cycle and replace calls to it with one call in WithAccountDecoder * Add moduleClients interface and implement in all applicable modules * Use module clients in cli initialization --- PENDING.md | 1 + client/context/context.go | 16 +- client/lcd/root.go | 3 + cmd/gaia/cmd/gaiacli/main.go | 96 +++++- cmd/gaia/cmd/gaiacli/query.go | 83 ----- cmd/gaia/cmd/gaiacli/tx.go | 83 ----- cmd/gaia/init/gentx.go | 9 +- docs/examples/basecoin/cmd/basecli/main.go | 42 ++- docs/examples/democoin/cmd/democli/main.go | 6 +- .../examples/democoin/x/cool/client/cli/tx.go | 5 +- docs/examples/democoin/x/pow/client/cli/tx.go | 3 +- .../x/simplestake/client/cli/commands.go | 3 +- types/module_clients.go | 11 + x/auth/client/cli/account.go | 30 +- x/auth/client/cli/sign.go | 15 +- x/auth/client/rest/query.go | 5 +- x/bank/client/cli/broadcast.go | 3 +- x/bank/client/cli/sendtx.go | 10 +- x/distribution/client/cli/tx.go | 22 +- x/distribution/client/module_client.go | 38 ++ x/gov/client/cli/query.go | 324 ++++++++++++++++++ x/gov/client/cli/tx.go | 324 +----------------- x/gov/client/module_client.go | 55 +++ x/gov/client/rest/rest.go | 8 +- x/gov/client/{ => utils}/utils.go | 2 +- x/ibc/client/cli/ibctx.go | 3 +- x/ibc/client/cli/relay.go | 3 +- x/slashing/client/cli/tx.go | 3 +- x/slashing/client/module_client.go | 47 +++ x/stake/client/cli/query.go | 12 +- x/stake/client/cli/tx.go | 11 +- x/stake/client/cli/utils.go | 3 +- x/stake/client/module_client.go | 61 ++++ 33 files changed, 739 insertions(+), 601 deletions(-) delete mode 100644 cmd/gaia/cmd/gaiacli/query.go delete mode 100644 cmd/gaia/cmd/gaiacli/tx.go create mode 100644 types/module_clients.go create mode 100644 x/distribution/client/module_client.go create mode 100644 x/gov/client/cli/query.go create mode 100644 x/gov/client/module_client.go rename x/gov/client/{ => utils}/utils.go (98%) create mode 100644 x/slashing/client/module_client.go create mode 100644 x/stake/client/module_client.go diff --git a/PENDING.md b/PENDING.md index 4720ec774c..1c17731e2b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -31,6 +31,7 @@ FEATURES * [gov][cli] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance parameter query commands. * [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator. + * [\#2840](https://github.com/cosmos/cosmos-sdk/pull/2840) Standardize CLI exports from modules * Gaia * [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`. diff --git a/client/context/context.go b/client/context/context.go index 9108b3d0bb..4b44073681 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -175,10 +175,22 @@ func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { return ctx } +// GetAccountDecoder gets the account decoder for auth.DefaultAccount. +func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder { + return func(accBytes []byte) (acct auth.Account, err error) { + err = cdc.UnmarshalBinaryBare(accBytes, &acct) + if err != nil { + panic(err) + } + + return acct, err + } +} + // WithAccountDecoder returns a copy of the context with an updated account // decoder. -func (ctx CLIContext) WithAccountDecoder(decoder auth.AccountDecoder) CLIContext { - ctx.AccDecoder = decoder +func (ctx CLIContext) WithAccountDecoder(cdc *codec.Codec) CLIContext { + ctx.AccDecoder = GetAccountDecoder(cdc) return ctx } diff --git a/client/lcd/root.go b/client/lcd/root.go index 3d61816467..7081b76c03 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -19,6 +19,9 @@ import ( "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/log" tmserver "github.com/tendermint/tendermint/rpc/lib/server" + + // Import statik for light client stuff + _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) // RestServer represents the Light Client Rest server diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 3dd936f863..05c7bc4eca 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net/http" "os" "path" @@ -9,6 +10,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" @@ -19,29 +21,29 @@ import ( "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + distClient "github.com/cosmos/cosmos-sdk/x/distribution/client" + govClient "github.com/cosmos/cosmos-sdk/x/gov/client" + slashingClient "github.com/cosmos/cosmos-sdk/x/slashing/client" + stakeClient "github.com/cosmos/cosmos-sdk/x/stake/client" + _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) const ( - storeAcc = "acc" - storeGov = "gov" - storeSlashing = "slashing" - storeStake = "stake" - queryRouteStake = "stake" -) - -// rootCmd is the entry point for this binary -var ( - rootCmd = &cobra.Command{ - Use: "gaiacli", - Short: "Command line interface for interacting with gaiad", - } + storeAcc = "acc" + storeGov = "gov" + storeSlashing = "slashing" + storeStake = "stake" + storeDist = "distr" ) func main() { @@ -68,13 +70,27 @@ func main() { // the below functions and eliminate global vars, like we do // with the cdc + // Module clients hold cli commnads (tx,query) and lcd routes + // TODO: Make the lcd command take a list of ModuleClient + mc := []sdk.ModuleClients{ + govClient.NewModuleClient(storeGov, cdc), + distClient.NewModuleClient(storeDist, cdc), + stakeClient.NewModuleClient(storeStake, cdc), + slashingClient.NewModuleClient(storeSlashing, cdc), + } + + rootCmd := &cobra.Command{ + Use: "gaiacli", + Short: "Command line interface for interacting with gaiad", + } + // Construct Root Command rootCmd.AddCommand( rpc.InitClientCommand(), rpc.StatusCommand(), client.ConfigCmd(), - queryCmd(cdc), - txCmd(cdc), + queryCmd(cdc, mc), + txCmd(cdc, mc), client.LineBreak, rs.ServeCommand(), client.LineBreak, @@ -92,11 +108,55 @@ func main() { err = executor.Execute() if err != nil { - // handle with #870 - panic(err) + fmt.Printf("Failed executing CLI command: %s, exiting...\n", err) + os.Exit(1) } } +func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { + queryCmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + } + + queryCmd.AddCommand( + rpc.ValidatorCommand(), + rpc.BlockCommand(), + tx.SearchTxCmd(cdc), + tx.QueryTxCmd(cdc), + client.LineBreak, + authcmd.GetAccountCmd(storeAcc, cdc), + ) + + for _, m := range mc { + queryCmd.AddCommand(m.GetQueryCmd()) + } + + return queryCmd +} + +func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { + txCmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + } + + txCmd.AddCommand( + bankcmd.SendTxCmd(cdc), + client.LineBreak, + authcmd.GetSignCommand(cdc), + bankcmd.GetBroadcastCommand(cdc), + client.LineBreak, + ) + + for _, m := range mc { + txCmd.AddCommand(m.GetTxCmd()) + } + + return txCmd +} + // registerRoutes registers the routes from the different modules for the LCD. // NOTE: details on the routes added for each module are in the module documentation // NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go @@ -105,7 +165,7 @@ func registerRoutes(rs *lcd.RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, storeAcc) bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) diff --git a/cmd/gaia/cmd/gaiacli/query.go b/cmd/gaia/cmd/gaiacli/query.go deleted file mode 100644 index 3a806d36c2..0000000000 --- a/cmd/gaia/cmd/gaiacli/query.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/spf13/cobra" - - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" - stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" - amino "github.com/tendermint/go-amino" -) - -func queryCmd(cdc *amino.Codec) *cobra.Command { - //Add query commands - queryCmd := &cobra.Command{ - Use: "query", - Aliases: []string{"q"}, - Short: "Querying subcommands", - } - - // Group staking queries under a subcommand - stakeQueryCmd := &cobra.Command{ - Use: "stake", - Short: "Querying commands for the staking module", - } - - stakeQueryCmd.AddCommand(client.GetCommands( - stakecmd.GetCmdQueryDelegation(storeStake, cdc), - stakecmd.GetCmdQueryDelegations(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryRedelegation(storeStake, cdc), - stakecmd.GetCmdQueryRedelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidator(storeStake, cdc), - stakecmd.GetCmdQueryValidators(storeStake, cdc), - stakecmd.GetCmdQueryValidatorDelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc), - stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc), - stakecmd.GetCmdQueryParams(storeStake, cdc), - stakecmd.GetCmdQueryPool(storeStake, cdc))...) - - // Group gov queries under a subcommand - govQueryCmd := &cobra.Command{ - Use: "gov", - Short: "Querying commands for the governance module", - } - - govQueryCmd.AddCommand(client.GetCommands( - govcmd.GetCmdQueryProposal(storeGov, cdc), - govcmd.GetCmdQueryProposals(storeGov, cdc), - govcmd.GetCmdQueryVote(storeGov, cdc), - govcmd.GetCmdQueryVotes(storeGov, cdc), - govcmd.GetCmdQueryParams(storeGov, cdc), - govcmd.GetCmdQueryDeposit(storeGov, cdc), - govcmd.GetCmdQueryDeposits(storeGov, cdc))...) - - // Group slashing queries under a subcommand - slashingQueryCmd := &cobra.Command{ - Use: "slashing", - Short: "Querying commands for the slashing module", - } - - slashingQueryCmd.AddCommand(client.GetCommands( - slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc))...) - - // Query commcmmand structure - queryCmd.AddCommand( - rpc.BlockCommand(), - rpc.ValidatorCommand(), - tx.SearchTxCmd(cdc), - tx.QueryTxCmd(cdc), - client.LineBreak, - client.GetCommands(authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)))[0], - stakeQueryCmd, - govQueryCmd, - slashingQueryCmd, - ) - - return queryCmd -} diff --git a/cmd/gaia/cmd/gaiacli/tx.go b/cmd/gaia/cmd/gaiacli/tx.go deleted file mode 100644 index fa0abc4ad8..0000000000 --- a/cmd/gaia/cmd/gaiacli/tx.go +++ /dev/null @@ -1,83 +0,0 @@ -package main - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - distrcmd "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" - govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" - stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" - amino "github.com/tendermint/go-amino" -) - -func txCmd(cdc *amino.Codec) *cobra.Command { - //Add transaction generation commands - txCmd := &cobra.Command{ - Use: "tx", - Short: "Transactions subcommands", - } - - stakeTxCmd := &cobra.Command{ - Use: "stake", - Short: "Staking transaction subcommands", - } - - stakeTxCmd.AddCommand(client.PostCommands( - stakecmd.GetCmdCreateValidator(cdc), - stakecmd.GetCmdEditValidator(cdc), - stakecmd.GetCmdDelegate(cdc), - stakecmd.GetCmdRedelegate(storeStake, cdc), - stakecmd.GetCmdUnbond(storeStake, cdc), - )...) - - distTxCmd := &cobra.Command{ - Use: "dist", - Short: "Distribution transactions subcommands", - } - - distTxCmd.AddCommand(client.PostCommands( - distrcmd.GetCmdWithdrawRewards(cdc), - distrcmd.GetCmdSetWithdrawAddr(cdc), - )...) - - govTxCmd := &cobra.Command{ - Use: "gov", - Short: "Governance transactions subcommands", - } - - govTxCmd.AddCommand(client.PostCommands( - govcmd.GetCmdDeposit(cdc), - govcmd.GetCmdVote(cdc), - govcmd.GetCmdSubmitProposal(cdc), - )...) - - slashingTxCmd := &cobra.Command{ - Use: "slashing", - Short: "Slashing transactions subcommands", - } - - slashingTxCmd.AddCommand(client.PostCommands( - slashingcmd.GetCmdUnjail(cdc), - )...) - - txCmd.AddCommand( - //Add auth and bank commands - client.PostCommands( - bankcmd.SendTxCmd(cdc), - bankcmd.GetBroadcastCommand(cdc), - authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)), - )...) - - txCmd.AddCommand( - client.LineBreak, - stakeTxCmd, - distTxCmd, - govTxCmd, - slashingTxCmd, - ) - - return txCmd -} diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 6d66f2e306..096097e188 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -2,6 +2,10 @@ package init import ( "fmt" + "io/ioutil" + "os" + "path/filepath" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -17,9 +21,6 @@ import ( "github.com/tendermint/tendermint/crypto" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/common" - "io/ioutil" - "os" - "path/filepath" ) const ( @@ -94,7 +95,7 @@ following delegation and commission default parameters: w.Close() prepareFlagsForTxSign() - signCmd := authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)) + signCmd := authcmd.GetSignCommand(cdc) if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { return err } diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index ad32bf5169..cb0eeba5b9 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" @@ -24,6 +24,12 @@ import ( "github.com/tendermint/tendermint/libs/cli" ) +const ( + storeAcc = "acc" + storeSlashing = "slashing" + storeStake = "stake" +) + // rootCmd is the entry point for this binary var ( rootCmd = &cobra.Command{ @@ -67,20 +73,20 @@ func main() { // add query/post commands (custom to binary) rootCmd.AddCommand( client.GetCommands( - stakecmd.GetCmdQueryValidator("stake", cdc), - stakecmd.GetCmdQueryValidators("stake", cdc), - stakecmd.GetCmdQueryValidatorUnbondingDelegations("stake", cdc), - stakecmd.GetCmdQueryValidatorRedelegations("stake", cdc), - stakecmd.GetCmdQueryDelegation("stake", cdc), - stakecmd.GetCmdQueryDelegations("stake", cdc), - stakecmd.GetCmdQueryPool("stake", cdc), - stakecmd.GetCmdQueryParams("stake", cdc), - stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc), - stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc), - stakecmd.GetCmdQueryRedelegation("stake", cdc), - stakecmd.GetCmdQueryRedelegations("stake", cdc), - slashingcmd.GetCmdQuerySigningInfo("slashing", cdc), - authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), + stakecmd.GetCmdQueryValidator(storeStake, cdc), + stakecmd.GetCmdQueryValidators(storeStake, cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidatorRedelegations(storeStake, cdc), + stakecmd.GetCmdQueryDelegation(storeStake, cdc), + stakecmd.GetCmdQueryDelegations(storeStake, cdc), + stakecmd.GetCmdQueryPool(storeStake, cdc), + stakecmd.GetCmdQueryParams(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryRedelegation(storeStake, cdc), + stakecmd.GetCmdQueryRedelegations(storeStake, cdc), + slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), + authcmd.GetAccountCmd(storeAcc, cdc), )...) rootCmd.AddCommand( @@ -91,8 +97,8 @@ func main() { stakecmd.GetCmdCreateValidator(cdc), stakecmd.GetCmdEditValidator(cdc), stakecmd.GetCmdDelegate(cdc), - stakecmd.GetCmdUnbond("stake", cdc), - stakecmd.GetCmdRedelegate("stake", cdc), + stakecmd.GetCmdUnbond(storeStake, cdc), + stakecmd.GetCmdRedelegate(storeStake, cdc), slashingcmd.GetCmdUnjail(cdc), )...) @@ -118,7 +124,7 @@ func registerRoutes(rs *lcd.RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, storeAcc) bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) diff --git a/docs/examples/democoin/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index d101da2d9c..0c37d9bd0b 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -18,7 +18,6 @@ import ( bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/app" - "github.com/cosmos/cosmos-sdk/docs/examples/democoin/types" coolcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool/client/cli" powcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow/client/cli" simplestakingcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestake/client/cli" @@ -32,6 +31,7 @@ var ( Use: "democli", Short: "Democoin light-client", } + storeAcc = "acc" ) func main() { @@ -71,7 +71,7 @@ func main() { // start with commands common to basecoin rootCmd.AddCommand( client.GetCommands( - authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), + authcmd.GetAccountCmd(storeAcc, cdc), )...) rootCmd.AddCommand( client.PostCommands( @@ -115,6 +115,6 @@ func registerRoutes(rs *lcd.RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, storeAcc) bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) } diff --git a/docs/examples/democoin/x/cool/client/cli/tx.go b/docs/examples/democoin/x/cool/client/cli/tx.go index a21685a249..aedbd37114 100644 --- a/docs/examples/democoin/x/cool/client/cli/tx.go +++ b/docs/examples/democoin/x/cool/client/cli/tx.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) @@ -22,7 +21,7 @@ func QuizTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) from, err := cliCtx.GetFromAddress() if err != nil { @@ -46,7 +45,7 @@ func SetTrendTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) from, err := cliCtx.GetFromAddress() if err != nil { diff --git a/docs/examples/democoin/x/pow/client/cli/tx.go b/docs/examples/democoin/x/pow/client/cli/tx.go index 548aa99107..a6827c3aba 100644 --- a/docs/examples/democoin/x/pow/client/cli/tx.go +++ b/docs/examples/democoin/x/pow/client/cli/tx.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/spf13/cobra" @@ -24,7 +23,7 @@ func MineCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) from, err := cliCtx.GetFromAddress() if err != nil { diff --git a/docs/examples/democoin/x/simplestake/client/cli/commands.go b/docs/examples/democoin/x/simplestake/client/cli/commands.go index 3fe9c20c4a..582ea33e39 100644 --- a/docs/examples/democoin/x/simplestake/client/cli/commands.go +++ b/docs/examples/democoin/x/simplestake/client/cli/commands.go @@ -9,7 +9,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestake" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/spf13/cobra" @@ -32,7 +31,7 @@ func BondTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) from, err := cliCtx.GetFromAddress() if err != nil { diff --git a/types/module_clients.go b/types/module_clients.go new file mode 100644 index 0000000000..3b3a9d9a5d --- /dev/null +++ b/types/module_clients.go @@ -0,0 +1,11 @@ +package types + +import ( + "github.com/spf13/cobra" +) + +// ModuleClients helps modules provide a standard interface for exporting client functionality +type ModuleClients interface { + GetQueryCmd() *cobra.Command + GetTxCmd() *cobra.Command +} diff --git a/x/auth/client/cli/account.go b/x/auth/client/cli/account.go index f78b5252f2..922b3e2db2 100644 --- a/x/auth/client/cli/account.go +++ b/x/auth/client/cli/account.go @@ -5,34 +5,17 @@ import ( "github.com/spf13/cobra" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" ) -// GetAccountCmdDefault invokes the GetAccountCmd for the auth.BaseAccount type. -func GetAccountCmdDefault(storeName string, cdc *codec.Codec) *cobra.Command { - return GetAccountCmd(storeName, cdc, GetAccountDecoder(cdc)) -} - -// GetAccountDecoder gets the account decoder for auth.DefaultAccount. -func GetAccountDecoder(cdc *codec.Codec) auth.AccountDecoder { - return func(accBytes []byte) (acct auth.Account, err error) { - err = cdc.UnmarshalBinaryBare(accBytes, &acct) - if err != nil { - panic(err) - } - - return acct, err - } -} - // GetAccountCmd returns a query account that will display the state of the // account at a given address. // nolint: unparam -func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecoder) *cobra.Command { - return &cobra.Command{ +func GetAccountCmd(storeName string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ Use: "account [address]", Short: "Query account balance", Args: cobra.ExactArgs(1), @@ -47,9 +30,9 @@ func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecod cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(decoder) + WithAccountDecoder(cdc) - if err := cliCtx.EnsureAccountExistsFromAddr(key); err != nil { + if err = cliCtx.EnsureAccountExistsFromAddr(key); err != nil { return err } @@ -72,4 +55,7 @@ func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecod return nil }, } + + // Add the flags here and return the command + return client.GetCommands(cmd)[0] } diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index f4a4548d41..9075308e1d 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -2,9 +2,10 @@ package cli import ( "fmt" + "io/ioutil" + "github.com/pkg/errors" "github.com/spf13/viper" - "io/ioutil" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -24,7 +25,7 @@ const ( ) // GetSignCommand returns the sign command -func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Command { +func GetSignCommand(codec *amino.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "sign ", Short: "Sign transactions generated offline", @@ -41,7 +42,7 @@ order. The --offline flag makes sure that the client will not reach out to the local cache. Thus account number or sequence number lookups will not be performed and it is recommended to set such parameters manually.`, - RunE: makeSignCmd(codec, decoder), + RunE: makeSignCmd(codec), Args: cobra.ExactArgs(1), } cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") @@ -51,10 +52,12 @@ recommended to set such parameters manually.`, cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+ "those who have already signed it, and make sure that signatures are in the correct order.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") - return cmd + + // Add the flags here and return the command + return client.PostCommands(cmd)[0] } -func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.Command, args []string) error { +func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) (err error) { stdTx, err := readAndUnmarshalStdTx(cdc, args[0]) if err != nil { @@ -72,7 +75,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. if name == "" { return errors.New("required flag \"name\" has not been set") } - cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) + cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(cdc) txBldr := authtxb.NewTxBuilderFromCLI() // if --signature-only is on, then override --append diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index 0629cc939e..e60e0fdc1d 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/gorilla/mux" ) @@ -17,11 +16,11 @@ import ( func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) { r.HandleFunc( "/auth/accounts/{address}", - QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx), + QueryAccountRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx), ).Methods("GET") r.HandleFunc( "/bank/balances/{address}", - QueryBalancesRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx), + QueryBalancesRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx), ).Methods("GET") r.HandleFunc( "/tx/sign", diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index dd045439e0..1266683640 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/cobra" @@ -36,7 +37,7 @@ in place of an input filename, the command reads from standard input.`, }, } - return cmd + return client.PostCommands(cmd)[0] } func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) { diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index bae2179766..29a101cf73 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -1,13 +1,13 @@ package cli import ( + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - "github.com/cosmos/cosmos-sdk/x/bank/client" + bankClient "github.com/cosmos/cosmos-sdk/x/bank/client" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -28,7 +28,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) if err := cliCtx.EnsureAccountExists(); err != nil { return err @@ -64,7 +64,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { } // build and sign the transaction, then broadcast to Tendermint - msg := client.CreateMsg(from, to, coins) + msg := bankClient.CreateMsg(from, to, coins) if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) } @@ -78,5 +78,5 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { cmd.MarkFlagRequired(flagTo) cmd.MarkFlagRequired(flagAmount) - return cmd + return client.PostCommands(cmd)[0] } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index b88968cda5..ee82498e82 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -6,12 +6,13 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + amino "github.com/tendermint/go-amino" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -22,6 +23,21 @@ var ( flagIsValidator = "is-validator" ) +// GetTxCmd returns the transaction commands for this module +func GetTxCmd(storeKey string, cdc *amino.Codec) *cobra.Command { + distTxCmd := &cobra.Command{ + Use: "dist", + Short: "Distribution transactions subcommands", + } + + distTxCmd.AddCommand(client.PostCommands( + GetCmdWithdrawRewards(cdc), + GetCmdSetWithdrawAddr(cdc), + )...) + + return distTxCmd +} + // command to withdraw rewards func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ @@ -41,7 +57,7 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) var msg sdk.Msg switch { @@ -92,7 +108,7 @@ func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) delAddr, err := cliCtx.GetFromAddress() if err != nil { diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go new file mode 100644 index 0000000000..ba725e1f87 --- /dev/null +++ b/x/distribution/client/module_client.go @@ -0,0 +1,38 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/client" + distCmds "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" +) + +// ModuleClient exports all client functionality from this module +type ModuleClient struct { + storeKey string + cdc *amino.Codec +} + +func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { + return ModuleClient{storeKey, cdc} +} + +// GetQueryCmd returns the cli query commands for this module +func (mc ModuleClient) GetQueryCmd() *cobra.Command { + return &cobra.Command{Hidden: true} +} + +// GetTxCmd returns the transaction commands for this module +func (mc ModuleClient) GetTxCmd() *cobra.Command { + distTxCmd := &cobra.Command{ + Use: "dist", + Short: "Distribution transactions subcommands", + } + + distTxCmd.AddCommand(client.PostCommands( + distCmds.GetCmdWithdrawRewards(mc.cdc), + distCmds.GetCmdSetWithdrawAddr(mc.cdc), + )...) + + return distTxCmd +} diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go new file mode 100644 index 0000000000..227f278a91 --- /dev/null +++ b/x/gov/client/cli/query.go @@ -0,0 +1,324 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// GetCmdQueryProposal implements the query proposal command. +func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "proposal", + Short: "Query details of a single proposal", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + params := gov.QueryProposalParams{ + ProposalID: proposalID, + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of proposal being queried") + + return cmd +} + +// GetCmdQueryProposals implements a query proposals command. +func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "proposals", + Short: "Query proposals with optional filters", + RunE: func(cmd *cobra.Command, args []string) error { + bechDepositerAddr := viper.GetString(flagDepositer) + bechVoterAddr := viper.GetString(flagVoter) + strProposalStatus := viper.GetString(flagStatus) + numLimit := uint64(viper.GetInt64(flagNumLimit)) + + params := gov.QueryProposalsParams{ + Limit: numLimit, + } + + if len(bechDepositerAddr) != 0 { + depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) + if err != nil { + return err + } + params.Depositer = depositerAddr + } + + if len(bechVoterAddr) != 0 { + voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) + if err != nil { + return err + } + params.Voter = voterAddr + } + + if len(strProposalStatus) != 0 { + proposalStatus, err := gov.ProposalStatusFromString(govClientUtils.NormalizeProposalStatus(strProposalStatus)) + if err != nil { + return err + } + params.ProposalStatus = proposalStatus + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposals", queryRoute), bz) + if err != nil { + return err + } + + var matchingProposals []gov.Proposal + err = cdc.UnmarshalJSON(res, &matchingProposals) + if err != nil { + return err + } + + if len(matchingProposals) == 0 { + fmt.Println("No matching proposals found") + return nil + } + + for _, proposal := range matchingProposals { + fmt.Printf(" %d - %s\n", proposal.GetProposalID(), proposal.GetTitle()) + } + + return nil + }, + } + + cmd.Flags().String(flagNumLimit, "", "(optional) limit to latest [number] proposals. Defaults to all proposals") + cmd.Flags().String(flagDepositer, "", "(optional) filter by proposals deposited on by depositer") + cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") + cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") + + return cmd +} + +// Command to Get a Proposal Information +// GetCmdQueryVote implements the query proposal vote command. +func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "vote", + Short: "Query details of a single vote", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + voterAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagVoter)) + if err != nil { + return err + } + + params := gov.QueryVoteParams{ + Voter: voterAddr, + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/vote", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on") + cmd.Flags().String(flagVoter, "", "bech32 voter address") + + return cmd +} + +// GetCmdQueryVotes implements the command to query for proposal votes. +func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "votes", + Short: "Query votes on a proposal", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + params := gov.QueryVotesParams{ + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's votes are being queried") + + return cmd +} + +// Command to Get a specific Deposit Information +// GetCmdQueryDeposit implements the query proposal deposit command. +func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "deposit", + Short: "Query details of a deposit", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + depositerAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositer)) + if err != nil { + return err + } + + params := gov.QueryDepositParams{ + Depositer: depositerAddr, + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposit", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of proposal deposited on") + cmd.Flags().String(flagDepositer, "", "bech32 depositer address") + + return cmd +} + +// GetCmdQueryDeposits implements the command to query for proposal deposits. +func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "deposits", + Short: "Query deposits on a proposal", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + params := gov.QueryDepositsParams{ + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's deposits are being queried") + + return cmd +} + +// GetCmdQueryTally implements the command to query for proposal tally result. +func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "tally", + Short: "Get the tally of a proposal vote", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + proposalID := uint64(viper.GetInt64(flagProposalID)) + + params := gov.QueryTallyParams{ + ProposalID: proposalID, + } + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/tally", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + cmd.Flags().String(flagProposalID, "", "proposalID of which proposal is being tallied") + + return cmd +} + +// GetCmdQueryProposal implements the query proposal command. +func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "param [param-type]", + Short: "Query the parameters (voting|tallying|deposit) of the governance process", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + paramType := args[0] + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, paramType), nil) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 31780f68e7..e804863d11 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -7,7 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/gov" @@ -15,7 +14,7 @@ import ( "io/ioutil" "strings" - "github.com/cosmos/cosmos-sdk/x/gov/client" + govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -80,7 +79,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) fromAddr, err := cliCtx.GetFromAddress() if err != nil { @@ -130,7 +129,7 @@ func parseSubmitProposalFlags() (*proposal, error) { if proposalFile == "" { proposal.Title = viper.GetString(flagTitle) proposal.Description = viper.GetString(flagDescription) - proposal.Type = client.NormalizeProposalType(viper.GetString(flagProposalType)) + proposal.Type = govClientUtils.NormalizeProposalType(viper.GetString(flagProposalType)) proposal.Deposit = viper.GetString(flagDeposit) return proposal, nil } @@ -163,7 +162,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) depositerAddr, err := cliCtx.GetFromAddress() if err != nil { @@ -208,7 +207,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) voterAddr, err := cliCtx.GetFromAddress() if err != nil { @@ -218,7 +217,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { proposalID := uint64(viper.GetInt64(flagProposalID)) option := viper.GetString(flagOption) - byteVoteOption, err := gov.VoteOptionFromString(client.NormalizeVoteOption(option)) + byteVoteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(option)) if err != nil { return err } @@ -248,314 +247,3 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { return cmd } - -// GetCmdQueryProposal implements the query proposal command. -func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "param [param-type]", - Short: "Query the parameters (voting|tallying|deposit) of the governance process", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - paramType := args[0] - - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, paramType), nil) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - return cmd -} - -// GetCmdQueryProposal implements the query proposal command. -func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "proposal", - Short: "Query details of a single proposal", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - params := gov.QueryProposalParams{ - ProposalID: proposalID, - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of proposal being queried") - - return cmd -} - -// GetCmdQueryProposals implements a query proposals command. -func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "proposals", - Short: "Query proposals with optional filters", - RunE: func(cmd *cobra.Command, args []string) error { - bechDepositerAddr := viper.GetString(flagDepositer) - bechVoterAddr := viper.GetString(flagVoter) - strProposalStatus := viper.GetString(flagStatus) - numLimit := uint64(viper.GetInt64(flagNumLimit)) - - params := gov.QueryProposalsParams{ - Limit: numLimit, - } - - if len(bechDepositerAddr) != 0 { - depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) - if err != nil { - return err - } - params.Depositer = depositerAddr - } - - if len(bechVoterAddr) != 0 { - voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) - if err != nil { - return err - } - params.Voter = voterAddr - } - - if len(strProposalStatus) != 0 { - proposalStatus, err := gov.ProposalStatusFromString(client.NormalizeProposalStatus(strProposalStatus)) - if err != nil { - return err - } - params.ProposalStatus = proposalStatus - } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - cliCtx := context.NewCLIContext().WithCodec(cdc) - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposals", queryRoute), bz) - if err != nil { - return err - } - - var matchingProposals []gov.Proposal - err = cdc.UnmarshalJSON(res, &matchingProposals) - if err != nil { - return err - } - - if len(matchingProposals) == 0 { - fmt.Println("No matching proposals found") - return nil - } - - for _, proposal := range matchingProposals { - fmt.Printf(" %d - %s\n", proposal.GetProposalID(), proposal.GetTitle()) - } - - return nil - }, - } - - cmd.Flags().String(flagNumLimit, "", "(optional) limit to latest [number] proposals. Defaults to all proposals") - cmd.Flags().String(flagDepositer, "", "(optional) filter by proposals deposited on by depositer") - cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") - cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") - - return cmd -} - -// Command to Get a Proposal Information -// GetCmdQueryVote implements the query proposal vote command. -func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "vote", - Short: "Query details of a single vote", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - voterAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagVoter)) - if err != nil { - return err - } - - params := gov.QueryVoteParams{ - Voter: voterAddr, - ProposalID: proposalID, - } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/vote", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on") - cmd.Flags().String(flagVoter, "", "bech32 voter address") - - return cmd -} - -// GetCmdQueryVotes implements the command to query for proposal votes. -func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "votes", - Short: "Query votes on a proposal", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - params := gov.QueryVotesParams{ - ProposalID: proposalID, - } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's votes are being queried") - - return cmd -} - -// Command to Get a specific Deposit Information -// GetCmdQueryDeposit implements the query proposal deposit command. -func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "deposit", - Short: "Query details of a deposit", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - depositerAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositer)) - if err != nil { - return err - } - - params := gov.QueryDepositParams{ - Depositer: depositerAddr, - ProposalID: proposalID, - } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposit", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of proposal deposited on") - cmd.Flags().String(flagDepositer, "", "bech32 depositer address") - - return cmd -} - -// GetCmdQueryDeposits implements the command to query for proposal deposits. -func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "deposits", - Short: "Query deposits on a proposal", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - params := gov.QueryDepositsParams{ - ProposalID: proposalID, - } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's deposits are being queried") - - return cmd -} - -// GetCmdQueryTally implements the command to query for proposal tally result. -func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command { - cmd := &cobra.Command{ - Use: "tally", - Short: "Get the tally of a proposal vote", - RunE: func(cmd *cobra.Command, args []string) error { - cliCtx := context.NewCLIContext().WithCodec(cdc) - proposalID := uint64(viper.GetInt64(flagProposalID)) - - params := gov.QueryTallyParams{ - ProposalID: proposalID, - } - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/tally", queryRoute), bz) - if err != nil { - return err - } - - fmt.Println(string(res)) - return nil - }, - } - - cmd.Flags().String(flagProposalID, "", "proposalID of which proposal is being tallied") - - return cmd -} diff --git a/x/gov/client/module_client.go b/x/gov/client/module_client.go new file mode 100644 index 0000000000..7cabc6884b --- /dev/null +++ b/x/gov/client/module_client.go @@ -0,0 +1,55 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/client" + govCli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" +) + +// ModuleClient exports all client functionality from this module +type ModuleClient struct { + storeKey string + cdc *amino.Codec +} + +func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { + return ModuleClient{storeKey, cdc} +} + +// GetQueryCmd returns the cli query commands for this module +func (mc ModuleClient) GetQueryCmd() *cobra.Command { + // Group gov queries under a subcommand + govQueryCmd := &cobra.Command{ + Use: "gov", + Short: "Querying commands for the governance module", + } + + govQueryCmd.AddCommand(client.GetCommands( + govCli.GetCmdQueryProposal(mc.storeKey, mc.cdc), + govCli.GetCmdQueryProposals(mc.storeKey, mc.cdc), + govCli.GetCmdQueryVote(mc.storeKey, mc.cdc), + govCli.GetCmdQueryVotes(mc.storeKey, mc.cdc), + govCli.GetCmdQueryParams(mc.storeKey, mc.cdc), + govCli.GetCmdQueryDeposit(mc.storeKey, mc.cdc), + govCli.GetCmdQueryDeposits(mc.storeKey, mc.cdc), + govCli.GetCmdQueryTally(mc.storeKey, mc.cdc))...) + + return govQueryCmd +} + +// GetTxCmd returns the transaction commands for this module +func (mc ModuleClient) GetTxCmd() *cobra.Command { + govTxCmd := &cobra.Command{ + Use: "gov", + Short: "Governance transactions subcommands", + } + + govTxCmd.AddCommand(client.PostCommands( + govCli.GetCmdDeposit(mc.cdc), + govCli.GetCmdVote(mc.cdc), + govCli.GetCmdSubmitProposal(mc.cdc), + )...) + + return govTxCmd +} diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 930a39b025..c000a34d87 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -10,7 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/gov/client" + govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/gorilla/mux" "github.com/pkg/errors" ) @@ -81,7 +81,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } - proposalType, err := gov.ProposalTypeFromString(client.NormalizeProposalType(req.ProposalType)) + proposalType, err := gov.ProposalTypeFromString(govClientUtils.NormalizeProposalType(req.ProposalType)) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -165,7 +165,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc return } - voteOption, err := gov.VoteOptionFromString(client.NormalizeVoteOption(req.Option)) + voteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(req.Option)) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -460,7 +460,7 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) } if len(strProposalStatus) != 0 { - proposalStatus, err := gov.ProposalStatusFromString(client.NormalizeProposalStatus(strProposalStatus)) + proposalStatus, err := gov.ProposalStatusFromString(govClientUtils.NormalizeProposalStatus(strProposalStatus)) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return diff --git a/x/gov/client/utils.go b/x/gov/client/utils/utils.go similarity index 98% rename from x/gov/client/utils.go rename to x/gov/client/utils/utils.go index 013f3944a3..e91d7005ce 100644 --- a/x/gov/client/utils.go +++ b/x/gov/client/utils/utils.go @@ -1,4 +1,4 @@ -package client +package utils // NormalizeVoteOption - normalize user specified vote option func NormalizeVoteOption(option string) string { diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go index be6dfc940d..e8b107d9af 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/utils" codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/ibc" @@ -30,7 +29,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) from, err := cliCtx.GetFromAddress() if err != nil { diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 43ad783f49..217d7cdee8 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -9,7 +9,6 @@ import ( codec "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/ibc" @@ -42,7 +41,7 @@ type relayCommander struct { func IBCRelayCmd(cdc *codec.Codec) *cobra.Command { cmdr := relayCommander{ cdc: cdc, - decoder: authcmd.GetAccountDecoder(cdc), + decoder: context.GetAccountDecoder(cdc), ibcStore: "ibc", mainStore: "main", accStore: "acc", diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index f70be18712..7124b3544e 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -5,7 +5,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/slashing" @@ -22,7 +21,7 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) valAddr, err := cliCtx.GetFromAddress() if err != nil { diff --git a/x/slashing/client/module_client.go b/x/slashing/client/module_client.go new file mode 100644 index 0000000000..82efb5afe0 --- /dev/null +++ b/x/slashing/client/module_client.go @@ -0,0 +1,47 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" +) + +// ModuleClient exports all client functionality from this module +type ModuleClient struct { + storeKey string + cdc *amino.Codec +} + +func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { + return ModuleClient{storeKey, cdc} +} + +// GetQueryCmd returns the cli query commands for this module +func (mc ModuleClient) GetQueryCmd() *cobra.Command { + // Group slashing queries under a subcommand + slashingQueryCmd := &cobra.Command{ + Use: "slashing", + Short: "Querying commands for the slashing module", + } + + slashingQueryCmd.AddCommand(client.GetCommands( + cli.GetCmdQuerySigningInfo(mc.storeKey, mc.cdc))...) + + return slashingQueryCmd + +} + +// GetTxCmd returns the transaction commands for this module +func (mc ModuleClient) GetTxCmd() *cobra.Command { + slashingTxCmd := &cobra.Command{ + Use: "slashing", + Short: "Slashing transactions subcommands", + } + + slashingTxCmd.AddCommand(client.PostCommands( + cli.GetCmdUnjail(mc.cdc), + )...) + + return slashingTxCmd +} diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index 24e4499960..40598fcf80 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -115,7 +115,7 @@ func GetCmdQueryValidators(storeName string, cdc *codec.Codec) *cobra.Command { } // GetCmdQueryValidatorUnbondingDelegations implements the query all unbonding delegatations from a validator command. -func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryValidatorUnbondingDelegations(storeKey string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "unbonding-delegations-from [operator-addr]", Short: "Query all unbonding delegatations from a validator", @@ -135,7 +135,7 @@ func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Code } res, err := cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/validatorUnbondingDelegations", queryRoute), + fmt.Sprintf("custom/%s/validatorUnbondingDelegations", storeKey), bz) if err != nil { return err @@ -150,7 +150,7 @@ func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Code } // GetCmdQueryValidatorRedelegations implements the query all redelegatations from a validator command. -func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryValidatorRedelegations(storeKey string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "redelegations-from [operator-addr]", Short: "Query all outgoing redelegatations from a validator", @@ -170,7 +170,7 @@ func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cob } res, err := cliCtx.QueryWithData( - fmt.Sprintf("custom/%s/validatorRedelegations", queryRoute), + fmt.Sprintf("custom/%s/validatorRedelegations", storeKey), bz) if err != nil { return err @@ -288,7 +288,7 @@ func GetCmdQueryDelegations(storeName string, cdc *codec.Codec) *cobra.Command { // GetCmdQueryValidatorDelegations implements the command to query all the // delegations to a specific validator. -func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { +func GetCmdQueryValidatorDelegations(storeKey string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "delegations-to [validator-addr]", Short: "Query all delegations made to one validator", @@ -308,7 +308,7 @@ func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validatorDelegations", queryRoute), bz) + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validatorDelegations", storeKey), bz) if err != nil { return err } diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index a42fcf6d02..09b235abb1 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake" @@ -25,7 +24,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) amounstStr := viper.GetString(FlagAmount) if amounstStr == "" { @@ -126,7 +125,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) valAddr, err := cliCtx.GetFromAddress() if err != nil { @@ -178,7 +177,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) amount, err := sdk.ParseCoin(viper.GetString(FlagAmount)) if err != nil { @@ -220,7 +219,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) var err error @@ -275,7 +274,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) delAddr, err := cliCtx.GetFromAddress() if err != nil { diff --git a/x/stake/client/cli/utils.go b/x/stake/client/cli/utils.go index 9aca2d8995..502cb11ec6 100644 --- a/x/stake/client/cli/utils.go +++ b/x/stake/client/cli/utils.go @@ -4,7 +4,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/pkg/errors" @@ -45,7 +44,7 @@ func getShares( key := stake.GetDelegationKey(delAddr, valAddr) cliCtx := context.NewCLIContext(). WithCodec(cdc). - WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) + WithAccountDecoder(cdc) resQuery, err := cliCtx.QueryStore(key, storeName) if err != nil { diff --git a/x/stake/client/module_client.go b/x/stake/client/module_client.go new file mode 100644 index 0000000000..5a08668dc0 --- /dev/null +++ b/x/stake/client/module_client.go @@ -0,0 +1,61 @@ +package client + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" +) + +// ModuleClient exports all client functionality from this module +type ModuleClient struct { + storeKey string + cdc *amino.Codec +} + +func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient { + return ModuleClient{storeKey, cdc} +} + +// GetQueryCmd returns the cli query commands for this module +func (mc ModuleClient) GetQueryCmd() *cobra.Command { + stakeQueryCmd := &cobra.Command{ + Use: "stake", + Short: "Querying commands for the staking module", + } + stakeQueryCmd.AddCommand(client.GetCommands( + cli.GetCmdQueryDelegation(mc.storeKey, mc.cdc), + cli.GetCmdQueryDelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryUnbondingDelegation(mc.storeKey, mc.cdc), + cli.GetCmdQueryUnbondingDelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryRedelegation(mc.storeKey, mc.cdc), + cli.GetCmdQueryRedelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryValidator(mc.storeKey, mc.cdc), + cli.GetCmdQueryValidators(mc.storeKey, mc.cdc), + cli.GetCmdQueryValidatorDelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryValidatorUnbondingDelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryValidatorRedelegations(mc.storeKey, mc.cdc), + cli.GetCmdQueryParams(mc.storeKey, mc.cdc), + cli.GetCmdQueryPool(mc.storeKey, mc.cdc))...) + + return stakeQueryCmd + +} + +// GetTxCmd returns the transaction commands for this module +func (mc ModuleClient) GetTxCmd() *cobra.Command { + stakeTxCmd := &cobra.Command{ + Use: "stake", + Short: "Staking transaction subcommands", + } + + stakeTxCmd.AddCommand(client.PostCommands( + cli.GetCmdCreateValidator(mc.cdc), + cli.GetCmdEditValidator(mc.cdc), + cli.GetCmdDelegate(mc.cdc), + cli.GetCmdRedelegate(mc.storeKey, mc.cdc), + cli.GetCmdUnbond(mc.storeKey, mc.cdc), + )...) + + return stakeTxCmd +} From 6e813ab3a82d3834c87a668dfeff26dafae4a3b4 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 19 Nov 2018 12:13:45 -0500 Subject: [PATCH 19/51] Change gas & related fields to unsigned integer type (#2839) * Change gas & related fields to unsigned integer type * Implement AddUint64Overflow --- PENDING.md | 3 ++- baseapp/baseapp.go | 10 ++++---- baseapp/baseapp_test.go | 10 ++++---- client/flags.go | 8 +++---- client/lcd/lcd_test.go | 6 ++--- client/utils/rest.go | 2 +- client/utils/utils.go | 10 ++++---- client/utils/utils_test.go | 12 +++++----- cmd/gaia/app/genesis.go | 3 +-- cmd/gaia/cli_test/cli_test.go | 9 ++++--- cmd/gaia/init/genesis_accts_test.go | 14 +++++------ docs/examples/democoin/cmd/democoind/main.go | 1 - server/init.go | 2 -- store/gaskvstore_test.go | 6 ++--- types/context.go | 4 +++- types/gas.go | 25 +++++++++++++++++--- types/gas_test.go | 2 +- types/int.go | 11 +++++++++ types/int_test.go | 25 ++++++++++++++++++++ types/result.go | 4 ++-- x/auth/ante.go | 12 +++++++--- x/auth/ante_test.go | 4 ++-- x/auth/client/txbuilder/txbuilder.go | 4 ++-- x/auth/client/txbuilder/txbuilder_test.go | 4 ++-- x/auth/stdtx.go | 4 ++-- 25 files changed, 127 insertions(+), 68 deletions(-) diff --git a/PENDING.md b/PENDING.md index 1c17731e2b..a0985593e8 100644 --- a/PENDING.md +++ b/PENDING.md @@ -62,7 +62,8 @@ IMPROVEMENTS * SDK - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization - - \#2821 Codespaces are now strings + - #2815 Gas unit fields changed from `int64` to `uint64`. + - #2821 Codespaces are now strings * Tendermint - #2796 Update to go-amino 0.14.1 diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0d55710241..34f9d817de 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -451,8 +451,8 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { Code: uint32(result.Code), Data: result.Data, Log: result.Log, - GasWanted: result.GasWanted, - GasUsed: result.GasUsed, + GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? + GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? Tags: result.Tags, } } @@ -477,8 +477,8 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { Codespace: string(result.Codespace), Data: result.Data, Log: result.Log, - GasWanted: result.GasWanted, - GasUsed: result.GasUsed, + GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints? + GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints? Tags: result.Tags, } } @@ -614,7 +614,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // determined by the GasMeter. We need access to the context to get the gas // meter so we initialize upfront. - var gasWanted int64 + var gasWanted uint64 ctx := app.getContextForAnte(mode, txBytes) ctx = app.initializeContext(ctx, mode) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 9c2e7e1a0d..ba3830e920 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -618,7 +618,7 @@ func TestConcurrentCheckDeliver(t *testing.T) { // Simulate() and Query("/app/simulate", txBytes) should give // the same results. func TestSimulateTx(t *testing.T) { - gasConsumed := int64(5) + gasConsumed := uint64(5) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { @@ -765,7 +765,7 @@ func TestRunInvalidTransaction(t *testing.T) { // Test that transactions exceeding gas limits fail func TestTxGasLimits(t *testing.T) { - gasGranted := int64(10) + gasGranted := uint64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) @@ -790,7 +790,7 @@ func TestTxGasLimits(t *testing.T) { }() count := tx.(*txTest).Counter - newCtx.GasMeter().ConsumeGas(count, "counter-ante") + newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") res = sdk.Result{ GasWanted: gasGranted, } @@ -802,7 +802,7 @@ func TestTxGasLimits(t *testing.T) { routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { count := msg.(msgCounter).Counter - ctx.GasMeter().ConsumeGas(count, "counter-handler") + ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return sdk.Result{} }) } @@ -813,7 +813,7 @@ func TestTxGasLimits(t *testing.T) { testCases := []struct { tx *txTest - gasUsed int64 + gasUsed uint64 fail bool }{ {newTxCounter(0, 0), 0, false}, diff --git a/client/flags.go b/client/flags.go index dfadab07e4..ffb2a0c2f0 100644 --- a/client/flags.go +++ b/client/flags.go @@ -124,7 +124,7 @@ func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { // GasSetting encapsulates the possible values passed through the --gas flag. type GasSetting struct { Simulate bool - Gas int64 + Gas uint64 } // Type returns the flag's value type. @@ -140,18 +140,18 @@ func (v *GasSetting) String() string { if v.Simulate { return GasFlagSimulate } - return strconv.FormatInt(v.Gas, 10) + return strconv.FormatUint(v.Gas, 10) } // ParseGasFlag parses the value of the --gas flag. -func ReadGasFlag(s string) (simulate bool, gas int64, err error) { +func ReadGasFlag(s string) (simulate bool, gas uint64, err error) { switch s { case "": gas = DefaultGasLimit case GasFlagSimulate: simulate = true default: - gas, err = strconv.ParseInt(s, 10, 64) + gas, err = strconv.ParseUint(s, 10, 64) if err != nil { err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate) return diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 1be908e98b..885e4b914f 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -283,7 +283,7 @@ func TestCoinSend(t *testing.T) { // test failure with negative gas res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "") - require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) + require.Equal(t, http.StatusBadRequest, res.StatusCode, body) // test failure with 0 gas res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "") @@ -389,8 +389,8 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx)) require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - require.Equal(t, gasEstimate, resultTx.DeliverTx.GasWanted) - require.Equal(t, gasEstimate, resultTx.DeliverTx.GasUsed) + require.Equal(t, gasEstimate, uint64(resultTx.DeliverTx.GasWanted)) + require.Equal(t, gasEstimate, uint64(resultTx.DeliverTx.GasUsed)) } func TestTxs(t *testing.T) { diff --git a/client/utils/rest.go b/client/utils/rest.go index 53c6186921..13347098b3 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -34,7 +34,7 @@ func WriteErrorResponse(w http.ResponseWriter, status int, err string) { // WriteSimulationResponse prepares and writes an HTTP // response for transactions simulations. -func WriteSimulationResponse(w http.ResponseWriter, gas int64) { +func WriteSimulationResponse(w http.ResponseWriter, gas uint64) { w.WriteHeader(http.StatusOK) w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas))) } diff --git a/client/utils/utils.go b/client/utils/utils.go index fee7c7fe25..46bb9799c1 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -71,7 +71,7 @@ func EnrichCtxWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name // CalculateGas simulates the execution of a transaction and returns // both the estimate obtained by the query and the adjusted amount. -func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *amino.Codec, txBytes []byte, adjustment float64) (estimate, adjusted int64, err error) { +func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *amino.Codec, txBytes []byte, adjustment float64) (estimate, adjusted uint64, err error) { // run a simulation (via /app/simulate query) to // estimate gas and update TxBuilder accordingly rawRes, err := queryFunc("/app/simulate", txBytes) @@ -152,7 +152,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, // nolint // SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted int64, err error) { +func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { txBytes, err := txBldr.BuildWithPubKey(name, msgs) if err != nil { return @@ -161,11 +161,11 @@ func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name stri return } -func adjustGasEstimate(estimate int64, adjustment float64) int64 { - return int64(adjustment * float64(estimate)) +func adjustGasEstimate(estimate uint64, adjustment float64) uint64 { + return uint64(adjustment * float64(estimate)) } -func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) { +func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (uint64, error) { var simulationResult sdk.Result if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil { return 0, err diff --git a/client/utils/utils_test.go b/client/utils/utils_test.go index c3bf315699..b22a50806c 100644 --- a/client/utils/utils_test.go +++ b/client/utils/utils_test.go @@ -14,16 +14,16 @@ func TestParseQueryResponse(t *testing.T) { cdc := app.MakeCodec() sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10}) gas, err := parseQueryResponse(cdc, sdkResBytes) - assert.Equal(t, gas, int64(10)) + assert.Equal(t, gas, uint64(10)) assert.Nil(t, err) gas, err = parseQueryResponse(cdc, []byte("fuzzy")) - assert.Equal(t, gas, int64(0)) + assert.Equal(t, gas, uint64(0)) assert.NotNil(t, err) } func TestCalculateGas(t *testing.T) { cdc := app.MakeCodec() - makeQueryFunc := func(gasUsed int64, wantErr bool) func(string, common.HexBytes) ([]byte, error) { + makeQueryFunc := func(gasUsed uint64, wantErr bool) func(string, common.HexBytes) ([]byte, error) { return func(string, common.HexBytes) ([]byte, error) { if wantErr { return nil, errors.New("") @@ -32,15 +32,15 @@ func TestCalculateGas(t *testing.T) { } } type args struct { - queryFuncGasUsed int64 + queryFuncGasUsed uint64 queryFuncWantErr bool adjustment float64 } tests := []struct { name string args args - wantEstimate int64 - wantAdjusted int64 + wantEstimate uint64 + wantAdjusted uint64 wantErr bool }{ {"error", args{0, true, 1.2}, 0, 0, true}, diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 7d95bbfa7d..5ce9ab8b95 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -91,7 +91,6 @@ func (ga *GenesisAccount) ToAccount() (acc *auth.BaseAccount) { } } - // Create the core parameters for genesis initialization for gaia // note that the pubkey input is this machines pubkey func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( @@ -279,7 +278,7 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { accAuth := auth.NewBaseAccountWithAddress(addr) - coins :=sdk.Coins{ + coins := sdk.Coins{ {"fooToken", sdk.NewInt(1000)}, {bondDenom, freeFermionsAcc}, } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 62f4d0d64f..26e6fcaa36 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -475,7 +475,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) - require.Equal(t, msg.Fee.Gas, int64(client.DefaultGasLimit)) + require.Equal(t, msg.Fee.Gas, uint64(client.DefaultGasLimit)) require.Equal(t, len(msg.Msgs), 1) require.Equal(t, 0, len(msg.GetSignatures())) @@ -486,7 +486,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) - require.Equal(t, msg.Fee.Gas, int64(100)) + require.Equal(t, msg.Fee.Gas, uint64(100)) require.Equal(t, len(msg.Msgs), 1) require.Equal(t, 0, len(msg.GetSignatures())) @@ -543,9 +543,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { } require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) - require.Equal(t, msg.Fee.Gas, result.Response.GasUsed) - require.Equal(t, msg.Fee.Gas, result.Response.GasWanted) - + require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasUsed)) + require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasWanted)) tests.WaitForNextNBlocksTM(2, port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) diff --git a/cmd/gaia/init/genesis_accts_test.go b/cmd/gaia/init/genesis_accts_test.go index 8825a8cd34..49634a90c4 100644 --- a/cmd/gaia/init/genesis_accts_test.go +++ b/cmd/gaia/init/genesis_accts_test.go @@ -25,14 +25,14 @@ func TestAddGenesisAccount(t *testing.T) { }{ { "valid account", - args{ - app.GenesisState{}, - addr1, - sdk.Coins{}, - }, - false}, + args{ + app.GenesisState{}, + addr1, + sdk.Coins{}, + }, + false}, {"dup account", args{ - app.GenesisState{Accounts: []app.GenesisAccount{app.GenesisAccount{Address:addr1}}}, + app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}}, addr1, sdk.Coins{}}, true}, } diff --git a/docs/examples/democoin/cmd/democoind/main.go b/docs/examples/democoin/cmd/democoind/main.go index 29e2640fde..730109798c 100644 --- a/docs/examples/democoin/cmd/democoind/main.go +++ b/docs/examples/democoin/cmd/democoind/main.go @@ -30,7 +30,6 @@ const ( flagClientHome = "home-client" ) - // coolGenAppParams sets up the app_state and appends the cool app state func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( appState json.RawMessage, err error) { diff --git a/server/init.go b/server/init.go index 3a6c6adae5..e1655c27ea 100644 --- a/server/init.go +++ b/server/init.go @@ -15,7 +15,6 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) - // SimpleGenTx is a simple genesis tx type SimpleGenTx struct { Addr sdk.AccAddress `json:"addr"` @@ -23,7 +22,6 @@ type SimpleGenTx struct { //_____________________________________________________________________ - // Generate a genesis transaction func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) ( appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go index ba77bae1f8..69f4ae204e 100644 --- a/store/gaskvstore_test.go +++ b/store/gaskvstore_test.go @@ -73,15 +73,15 @@ func testGasKVStoreWrap(t *testing.T, store KVStore) { meter := sdk.NewGasMeter(10000) store = store.Gas(meter, sdk.GasConfig{HasCost: 10}) - require.Equal(t, int64(0), meter.GasConsumed()) + require.Equal(t, uint64(0), meter.GasConsumed()) store.Has([]byte("key")) - require.Equal(t, int64(10), meter.GasConsumed()) + require.Equal(t, uint64(10), meter.GasConsumed()) store = store.Gas(meter, sdk.GasConfig{HasCost: 20}) store.Has([]byte("key")) - require.Equal(t, int64(40), meter.GasConsumed()) + require.Equal(t, uint64(40), meter.GasConsumed()) } func TestGasKVStoreWrap(t *testing.T) { diff --git a/types/context.go b/types/context.go index bfb4c58fed..267e666815 100644 --- a/types/context.go +++ b/types/context.go @@ -203,8 +203,10 @@ func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context { if params == nil { return c } + + // TODO: Do we need to handle invalid MaxGas values? return c.withValue(contextKeyConsensusParams, params). - WithGasMeter(NewGasMeter(params.BlockSize.MaxGas)) + WithGasMeter(NewGasMeter(uint64(params.BlockSize.MaxGas))) } func (c Context) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) } diff --git a/types/gas.go b/types/gas.go index 7b03474670..656bd1d04c 100644 --- a/types/gas.go +++ b/types/gas.go @@ -18,13 +18,19 @@ var ( ) // Gas measured by the SDK -type Gas = int64 +type Gas = uint64 // ErrorOutOfGas defines an error thrown when an action results in out of gas. type ErrorOutOfGas struct { Descriptor string } +// ErrorGasOverflow defines an error thrown when an action results gas consumption +// unsigned integer overflow. +type ErrorGasOverflow struct { + Descriptor string +} + // GasMeter interface to track gas consumption type GasMeter interface { GasConsumed() Gas @@ -49,7 +55,14 @@ func (g *basicGasMeter) GasConsumed() Gas { } func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { - g.consumed += amount + var overflow bool + + // TODO: Should we set the consumed field after overflow checking? + g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + if overflow { + panic(ErrorGasOverflow{descriptor}) + } + if g.consumed > g.limit { panic(ErrorOutOfGas{descriptor}) } @@ -71,7 +84,13 @@ func (g *infiniteGasMeter) GasConsumed() Gas { } func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { - g.consumed += amount + var overflow bool + + // TODO: Should we set the consumed field after overflow checking? + g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + if overflow { + panic(ErrorGasOverflow{descriptor}) + } } // GasConfig defines gas cost for each operation on KVStores diff --git a/types/gas_test.go b/types/gas_test.go index cd2384d12d..f4452053fb 100644 --- a/types/gas_test.go +++ b/types/gas_test.go @@ -21,7 +21,7 @@ func TestGasMeter(t *testing.T) { for tcnum, tc := range cases { meter := NewGasMeter(tc.limit) - used := int64(0) + used := uint64(0) for unum, usage := range tc.usage { used += usage diff --git a/types/int.go b/types/int.go index c2bef7a64f..2083168e9c 100644 --- a/types/int.go +++ b/types/int.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "math" "testing" "math/big" @@ -529,6 +530,16 @@ func (i *Uint) UnmarshalJSON(bz []byte) error { //__________________________________________________________________________ +// AddUint64Overflow performs the addition operation on two uint64 integers and +// returns a boolean on whether or not the result overflows. +func AddUint64Overflow(a, b uint64) (uint64, bool) { + if math.MaxUint64-a < b { + return 0, true + } + + return a + b, false +} + // intended to be used with require/assert: require.True(IntEq(...)) func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() diff --git a/types/int_test.go b/types/int_test.go index cd357c4f77..78bee66bfa 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -590,3 +590,28 @@ func TestEncodingTableUint(t *testing.T) { require.Equal(t, tc.i, i, "Unmarshaled value is different from expected. tc #%d", tcnum) } } + +func TestAddUint64Overflow(t *testing.T) { + testCases := []struct { + a, b uint64 + result uint64 + overflow bool + }{ + {0, 0, 0, false}, + {100, 100, 200, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 1, math.MaxUint64, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 2, 0, true}, + } + + for i, tc := range testCases { + res, overflow := AddUint64Overflow(tc.a, tc.b) + require.Equal( + t, tc.overflow, overflow, + "invalid overflow result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + require.Equal( + t, tc.result, res, + "invalid uint64 result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + } +} diff --git a/types/result.go b/types/result.go index 20893a076f..924b73b49f 100644 --- a/types/result.go +++ b/types/result.go @@ -16,10 +16,10 @@ type Result struct { Log string // GasWanted is the maximum units of work we allow this tx to perform. - GasWanted int64 + GasWanted uint64 // GasUsed is the amount of gas actually consumed. NOTE: unimplemented - GasUsed int64 + GasUsed uint64 // Tx fee amount and denom. FeeAmount int64 diff --git a/x/auth/ante.go b/x/auth/ante.go index 8539be06a4..7beb643c56 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -71,6 +71,7 @@ func NewAnteHandler(am AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler { if err != nil { return newCtx, err.Result(), true } + // charge gas for the memo newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo") @@ -260,13 +261,16 @@ func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) { } } -func adjustFeesByGas(fees sdk.Coins, gas int64) sdk.Coins { +func adjustFeesByGas(fees sdk.Coins, gas uint64) sdk.Coins { gasCost := gas / gasPerUnitCost gasFees := make(sdk.Coins, len(fees)) + // TODO: Make this not price all coins in the same way + // TODO: Undo int64 casting once unsigned integers are supported for coins for i := 0; i < len(fees); i++ { - gasFees[i] = sdk.NewInt64Coin(fees[i].Denom, gasCost) + gasFees[i] = sdk.NewInt64Coin(fees[i].Denom, int64(gasCost)) } + return fees.Plus(gasFees) } @@ -306,10 +310,12 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { } func setGasMeter(simulate bool, ctx sdk.Context, stdTx StdTx) sdk.Context { - // set the gas meter + // In various cases such as simulation and during the genesis block, we do not + // meter any gas utilization. if simulate || ctx.BlockHeight() == 0 { return ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) } + return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) } diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index c376d29961..0e8d13957a 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -677,7 +677,7 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { tests := []struct { name string args args - gasConsumed int64 + gasConsumed uint64 wantPanic bool }{ {"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), ed25519.GenPrivKey().PubKey()}, ed25519VerifyCost, false}, @@ -699,7 +699,7 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { func TestAdjustFeesByGas(t *testing.T) { type args struct { fee sdk.Coins - gas int64 + gas uint64 } tests := []struct { name string diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index f516de4513..593d745abf 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -16,7 +16,7 @@ type TxBuilder struct { Codec *codec.Codec AccountNumber int64 Sequence int64 - Gas int64 // TODO: should this turn into uint64? requires further discussion - see #2173 + Gas uint64 GasAdjustment float64 SimulateGas bool ChainID string @@ -61,7 +61,7 @@ func (bldr TxBuilder) WithChainID(chainID string) TxBuilder { } // WithGas returns a copy of the context with an updated gas. -func (bldr TxBuilder) WithGas(gas int64) TxBuilder { +func (bldr TxBuilder) WithGas(gas uint64) TxBuilder { bldr.Gas = gas return bldr } diff --git a/x/auth/client/txbuilder/txbuilder_test.go b/x/auth/client/txbuilder/txbuilder_test.go index 3ad2ad4123..f4f9163a03 100644 --- a/x/auth/client/txbuilder/txbuilder_test.go +++ b/x/auth/client/txbuilder/txbuilder_test.go @@ -9,8 +9,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/tendermint/tendermint/crypto/ed25519" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/tendermint/tendermint/crypto/ed25519" ) var ( @@ -23,7 +23,7 @@ func TestTxBuilderBuild(t *testing.T) { Codec *codec.Codec AccountNumber int64 Sequence int64 - Gas int64 + Gas uint64 GasAdjustment float64 SimulateGas bool ChainID string diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index 84bf88a4b7..ba1c65b845 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -70,10 +70,10 @@ func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures } // which must be above some miminum to be accepted into the mempool. type StdFee struct { Amount sdk.Coins `json:"amount"` - Gas int64 `json:"gas"` + Gas uint64 `json:"gas"` } -func NewStdFee(gas int64, amount ...sdk.Coin) StdFee { +func NewStdFee(gas uint64, amount ...sdk.Coin) StdFee { return StdFee{ Amount: amount, Gas: gas, From 47eed3958b22cf1a842a7167f069469e2c78446b Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 01:06:14 -0800 Subject: [PATCH 20/51] Clean up Context/MultiStore usage in BaseApp (#2847) --- baseapp/baseapp.go | 62 +++++++++++++++++++++------------------- store/cachemultistore.go | 2 ++ types/context.go | 15 +++++----- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 34f9d817de..286d58bbaa 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -200,6 +200,10 @@ func (st *state) CacheMultiStore() sdk.CacheMultiStore { return st.ms.CacheMultiStore() } +func (st *state) Context() sdk.Context { + return st.ctx +} + func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() app.checkState = &state{ @@ -383,6 +387,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res return sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() } + // Cache wrap the commit-multistore for safety. ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger). WithMinimumFees(app.minimumFees) @@ -501,14 +506,14 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { return nil } -// retrieve the context for the ante handler and store the tx bytes; store -// the vote infos if the tx runs within the deliverTx() state. -func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { - ctx = app.getState(mode).ctx.WithTxBytes(txBytes) - if mode == runTxModeDeliver { - ctx = ctx.WithVoteInfos(app.voteInfos) +// retrieve the context for the tx w/ txBytes and other memoized values. +func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Context) { + ctx = app.getState(mode).ctx. + WithTxBytes(txBytes). + WithVoteInfos(app.voteInfos) + if mode == runTxModeSimulate { + ctx, _ = ctx.CacheContext() } - return } @@ -578,21 +583,14 @@ func (app *BaseApp) getState(mode runTxMode) *state { return app.deliverState } -func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { - if mode == runTxModeSimulate { - ctx = ctx.WithMultiStore(app.getState(runTxModeSimulate).CacheMultiStore()) - } - return ctx -} +// cacheTxContext returns a new context based off of the provided context with +// a cache wrapped multi-store. +func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) ( + sdk.Context, sdk.CacheMultiStore) { -// cacheTxContext returns a new context based off of the provided context with a -// cache wrapped multi-store and the store itself to allow the caller to write -// changes from the cached multi-store. -func (app *BaseApp) cacheTxContext( - ctx sdk.Context, txBytes []byte, mode runTxMode, -) (sdk.Context, sdk.CacheMultiStore) { - - msCache := app.getState(mode).CacheMultiStore() + ms := ctx.MultiStore() + // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 + msCache := ms.CacheMultiStore() if msCache.TracingEnabled() { msCache = msCache.WithTracingContext( sdk.TraceContext( @@ -616,8 +614,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // meter so we initialize upfront. var gasWanted uint64 - ctx := app.getContextForAnte(mode, txBytes) - ctx = app.initializeContext(ctx, mode) + ctx := app.getContextForTx(mode, txBytes) + ms := ctx.MultiStore() defer func() { if r := recover(); r != nil { @@ -651,31 +649,37 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // NOTE: Alternatively, we could require that anteHandler ensures that // writes do not happen if aborted/failed. This may have some // performance benefits, but it'll be more difficult to get right. - anteCtx, msCache = app.cacheTxContext(ctx, txBytes, mode) + anteCtx, msCache = app.cacheTxContext(ctx, txBytes) newCtx, result, abort := app.anteHandler(anteCtx, tx, (mode == runTxModeSimulate)) if abort { return result } if !newCtx.IsZero() { - ctx = newCtx + // At this point, newCtx.MultiStore() is cache wrapped, + // or something else replaced by anteHandler. + // We want the original ms, not one which was cache-wrapped + // for the ante handler. + ctx = newCtx.WithMultiStore(ms) } msCache.Write() gasWanted = result.GasWanted } - if mode == runTxModeSimulate { - result = app.runMsgs(ctx, msgs, mode) - result.GasWanted = gasWanted + if mode == runTxModeCheck { return } // Create a new context based off of the existing context with a cache wrapped // multi-store in case message processing fails. - runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes, mode) + runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) result = app.runMsgs(runMsgCtx, msgs, mode) result.GasWanted = gasWanted + if mode == runTxModeSimulate { + return + } + // only update state if all messages pass if result.IsOK() { msCache.Write() diff --git a/store/cachemultistore.go b/store/cachemultistore.go index f69ad42aac..ee19778559 100644 --- a/store/cachemultistore.go +++ b/store/cachemultistore.go @@ -11,6 +11,8 @@ import ( // cacheMultiStore holds many cache-wrapped stores. // Implements MultiStore. +// NOTE: a cacheMultiStore (and MultiStores in general) should never expose the +// keys for the substores. type cacheMultiStore struct { db CacheKVStore stores map[StoreKey]CacheWrap diff --git a/types/context.go b/types/context.go index 267e666815..aac05fdfdf 100644 --- a/types/context.go +++ b/types/context.go @@ -73,12 +73,12 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), cachedKVGasConfig) + return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedKVGasConfig) } // TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key StoreKey) KVStore { - return c.multiStore().GetKVStore(key).Gas(c.GasMeter(), cachedTransientGasConfig) + return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedTransientGasConfig) } //---------------------------------------- @@ -143,10 +143,7 @@ const ( contextKeyMinimumFees ) -// NOTE: Do not expose MultiStore. -// MultiStore exposes all the keys. -// Instead, pass the context and the store key. -func (c Context) multiStore() MultiStore { +func (c Context) MultiStore() MultiStore { return c.Value(contextKeyMultiStore).(MultiStore) } @@ -174,7 +171,9 @@ func (c Context) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) } func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) } -func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) } +func (c Context) WithMultiStore(ms MultiStore) Context { + return c.withValue(contextKeyMultiStore, ms) +} func (c Context) WithBlockHeader(header abci.Header) Context { var _ proto.Message = &header // for cloning. @@ -232,7 +231,7 @@ func (c Context) WithMinimumFees(minFees Coins) Context { // Cache the multistore and return a new cached context. The cached context is // written to the context when writeCache is called. func (c Context) CacheContext() (cc Context, writeCache func()) { - cms := c.multiStore().CacheMultiStore() + cms := c.MultiStore().CacheMultiStore() cc = c.WithMultiStore(cms) return cc, cms.Write } From 41fc538ac7d1d8587e6c983d677ca5f4309b129f Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 20 Nov 2018 04:22:35 -0500 Subject: [PATCH 21/51] Add Safety Measures to Coin/Coins (#2797) --- PENDING.md | 4 +- cmd/gaia/app/genesis.go | 6 +- cmd/gaia/app/sim_test.go | 2 +- docs/_attic/sdk/core/examples/app2_test.go | 2 +- docs/_attic/sdk/core/examples/app4_test.go | 10 +- docs/examples/democoin/x/cool/app_test.go | 10 +- docs/examples/democoin/x/pow/app_test.go | 6 +- docs/intro/ocap.md | 2 +- types/coin.go | 328 +++++++++++++------- types/coin_benchmark_test.go | 64 ++++ types/coin_test.go | 337 +++++++++------------ types/int.go | 42 ++- types/int_test.go | 25 ++ x/auth/ante.go | 20 +- x/auth/ante_test.go | 1 - x/bank/keeper.go | 6 +- x/bank/msgs_test.go | 8 - x/bank/simulation/msgs.go | 2 +- x/gov/msgs_test.go | 3 - x/slashing/handler_test.go | 6 +- x/slashing/keeper_test.go | 23 +- x/slashing/tick_test.go | 5 +- x/stake/types/msg_test.go | 6 - 23 files changed, 556 insertions(+), 362 deletions(-) create mode 100644 types/coin_benchmark_test.go diff --git a/PENDING.md b/PENDING.md index a0985593e8..41e5c39c60 100644 --- a/PENDING.md +++ b/PENDING.md @@ -62,8 +62,10 @@ IMPROVEMENTS * SDK - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization + - \#2821 Codespaces are now strings + - [types] #2776 Improve safety of `Coin` and `Coins` types. Various functions + and methods will panic when a negative amount is discovered. - #2815 Gas unit fields changed from `int64` to `uint64`. - - #2821 Codespaces are now strings * Tendermint - #2796 Update to go-amino 0.14.1 diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 5ce9ab8b95..ab958e9331 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -279,10 +279,12 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount { accAuth := auth.NewBaseAccountWithAddress(addr) coins := sdk.Coins{ - {"fooToken", sdk.NewInt(1000)}, - {bondDenom, freeFermionsAcc}, + sdk.NewCoin("fooToken", sdk.NewInt(1000)), + sdk.NewCoin(bondDenom, freeFermionsAcc), } + coins.Sort() + accAuth.Coins = coins return NewGenesisAccount(&accAuth) } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index e56344554a..70ae8e12af 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -63,7 +63,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { // Randomly generate some genesis accounts for _, acc := range accs { - coins := sdk.Coins{sdk.Coin{stakeTypes.DefaultBondDenom, sdk.NewInt(amount)}} + coins := sdk.Coins{sdk.NewCoin(stakeTypes.DefaultBondDenom, sdk.NewInt(amount))} genesisAccounts = append(genesisAccounts, GenesisAccount{ Address: acc.Address, Coins: coins, diff --git a/docs/_attic/sdk/core/examples/app2_test.go b/docs/_attic/sdk/core/examples/app2_test.go index c59dec2647..b7c94bbcf7 100644 --- a/docs/_attic/sdk/core/examples/app2_test.go +++ b/docs/_attic/sdk/core/examples/app2_test.go @@ -20,7 +20,7 @@ func TestEncoding(t *testing.T) { sendMsg := MsgSend{ From: addr1, To: addr2, - Amount: sdk.Coins{{"testCoins", sdk.NewInt(100)}}, + Amount: sdk.Coins{sdk.NewCoin("testCoins", sdk.NewInt(100))}, } // Construct transaction diff --git a/docs/_attic/sdk/core/examples/app4_test.go b/docs/_attic/sdk/core/examples/app4_test.go index 7d95d7b57d..86be8ea125 100644 --- a/docs/_attic/sdk/core/examples/app4_test.go +++ b/docs/_attic/sdk/core/examples/app4_test.go @@ -30,7 +30,7 @@ func InitTestChain(bc *bapp.BaseApp, chainID string, addrs ...sdk.AccAddress) { for _, addr := range addrs { acc := GenesisAccount{ Address: addr, - Coins: sdk.Coins{{"testCoin", sdk.NewInt(100)}}, + Coins: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))}, } accounts = append(accounts, &acc) } @@ -61,12 +61,12 @@ func TestBadMsg(t *testing.T) { addr2 := priv2.PubKey().Address().Bytes() // Attempt to spend non-existent funds - msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{{"testCoin", sdk.NewInt(100)}}) + msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))}) // Construct transaction fee := auth.StdFee{ Gas: 1000000000000000, - Amount: sdk.Coins{{"testCoin", sdk.NewInt(0)}}, + Amount: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(0))}, } signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "") sig, err := priv1.Sign(signBytes) @@ -108,11 +108,11 @@ func TestMsgSend(t *testing.T) { InitTestChain(bc, "test-chain", addr1) // Send funds to addr2 - msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{{"testCoin", sdk.NewInt(100)}}) + msg := GenerateSpendMsg(addr1, addr2, sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))}) fee := auth.StdFee{ Gas: 1000000000000000, - Amount: sdk.Coins{{"testCoin", sdk.NewInt(0)}}, + Amount: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(0))}, } signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "") sig, err := priv1.Sign(signBytes) diff --git a/docs/examples/democoin/x/cool/app_test.go b/docs/examples/democoin/x/cool/app_test.go index 3f1dd6734c..7bfc8b9cc0 100644 --- a/docs/examples/democoin/x/cool/app_test.go +++ b/docs/examples/democoin/x/cool/app_test.go @@ -90,15 +90,15 @@ func TestMsgQuiz(t *testing.T) { // Set the trend, submit a really cool quiz and check for reward mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, true, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))}) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))}) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))}) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do! - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))}) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("badvibesonly", sdk.NewInt(69)), sdk.NewCoin("icecold", sdk.NewInt(138))}) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool } diff --git a/docs/examples/democoin/x/pow/app_test.go b/docs/examples/democoin/x/pow/app_test.go index 41901f0979..58a7d35386 100644 --- a/docs/examples/democoin/x/pow/app_test.go +++ b/docs/examples/democoin/x/pow/app_test.go @@ -75,12 +75,12 @@ func TestMsgMine(t *testing.T) { // Mine and check for reward mineMsg1 := GenerateMsgMine(addr1, 1, 2) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(1))}) // Mine again and check for reward mineMsg2 := GenerateMsgMine(addr1, 2, 3) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))}) // Mine again - should be invalid mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1) - mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) + mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))}) } diff --git a/docs/intro/ocap.md b/docs/intro/ocap.md index 7d57d0c34e..cea7c108dc 100644 --- a/docs/intro/ocap.md +++ b/docs/intro/ocap.md @@ -63,7 +63,7 @@ principle: type AppAccount struct {...} var account := &AppAccount{ Address: pub.Address(), - Coins: sdk.Coins{{"ATM", 100}}, + Coins: sdk.Coins{sdk.NewInt64Coin("ATM", 100)}, } var sumValue := externalModule.ComputeSumValue(account) ``` diff --git a/types/coin.go b/types/coin.go index 31f0e98a42..5f9020e84e 100644 --- a/types/coin.go +++ b/types/coin.go @@ -4,23 +4,41 @@ import ( "fmt" "regexp" "sort" - "strconv" "strings" ) -// Coin hold some amount of one currency +//----------------------------------------------------------------------------- +// Coin + +// Coin hold some amount of one currency. +// +// CONTRACT: A coin will never hold a negative amount of any denomination. +// +// TODO: Make field members private for further safety. type Coin struct { - Denom string `json:"denom"` - Amount Int `json:"amount"` + Denom string `json:"denom"` + + // To allow the use of unsigned integers (see: #1273) a larger refactor will + // need to be made. So we use signed integers for now with safety measures in + // place preventing negative values being used. + Amount Int `json:"amount"` } +// NewCoin returns a new coin with a denomination and amount. It will panic if +// the amount is negative. func NewCoin(denom string, amount Int) Coin { + if amount.LT(ZeroInt()) { + panic("negative coin amount") + } + return Coin{ Denom: denom, Amount: amount, } } +// NewInt64Coin returns a new coin with a denomination and amount. It will panic +// if the amount is negative. func NewInt64Coin(denom string, amount int64) Coin { return NewCoin(denom, NewInt(amount)) } @@ -57,33 +75,46 @@ func (coin Coin) IsEqual(other Coin) bool { return coin.SameDenomAs(other) && (coin.Amount.Equal(other.Amount)) } -// IsPositive returns true if coin amount is positive +// Adds amounts of two coins with same denom. If the coins differ in denom then +// it panics. +func (coin Coin) Plus(coinB Coin) Coin { + if !coin.SameDenomAs(coinB) { + panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom)) + } + + return Coin{coin.Denom, coin.Amount.Add(coinB.Amount)} +} + +// Subtracts amounts of two coins with same denom. If the coins differ in denom +// then it panics. +func (coin Coin) Minus(coinB Coin) Coin { + if !coin.SameDenomAs(coinB) { + panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom)) + } + + res := Coin{coin.Denom, coin.Amount.Sub(coinB.Amount)} + if !res.IsNotNegative() { + panic("negative count amount") + } + + return res +} + +// IsPositive returns true if coin amount is positive. +// +// TODO: Remove once unsigned integers are used. func (coin Coin) IsPositive() bool { return (coin.Amount.Sign() == 1) } -// IsNotNegative returns true if coin amount is not negative +// IsNotNegative returns true if coin amount is not negative and false otherwise. +// +// TODO: Remove once unsigned integers are used. func (coin Coin) IsNotNegative() bool { return (coin.Amount.Sign() != -1) } -// Adds amounts of two coins with same denom -func (coin Coin) Plus(coinB Coin) Coin { - if !coin.SameDenomAs(coinB) { - return coin - } - return Coin{coin.Denom, coin.Amount.Add(coinB.Amount)} -} - -// Subtracts amounts of two coins with same denom -func (coin Coin) Minus(coinB Coin) Coin { - if !coin.SameDenomAs(coinB) { - return coin - } - return Coin{coin.Denom, coin.Amount.Sub(coinB.Amount)} -} - -//---------------------------------------- +//----------------------------------------------------------------------------- // Coins // Coins is a set of Coin, one per currency @@ -101,127 +132,157 @@ func (coins Coins) String() string { return out[:len(out)-1] } -// IsValid asserts the Coins are sorted, and don't have 0 amounts +// IsValid asserts the Coins are sorted and have positive amounts. func (coins Coins) IsValid() bool { switch len(coins) { case 0: return true case 1: - return !coins[0].IsZero() + return coins[0].IsPositive() default: lowDenom := coins[0].Denom + for _, coin := range coins[1:] { if coin.Denom <= lowDenom { return false } - if coin.IsZero() { + if !coin.IsPositive() { return false } + // we compare each coin against the last denom lowDenom = coin.Denom } + return true } } -// Plus combines two sets of coins -// CONTRACT: Plus will never return Coins where one Coin has a 0 amount. +// Plus adds two sets of coins. +// +// e.g. +// {2A} + {A, 2B} = {3A, 2B} +// {2A} + {0B} = {2A} +// +// NOTE: Plus operates under the invariant that coins are sorted by +// denominations. +// +// CONTRACT: Plus will never return Coins where one Coin has a non-positive +// amount. In otherwords, IsValid will always return true. func (coins Coins) Plus(coinsB Coins) Coins { + return coins.safePlus(coinsB) +} + +// safePlus will perform addition of two coins sets. If both coin sets are +// empty, then an empty set is returned. If only a single set is empty, the +// other set is returned. Otherwise, the coins are compared in order of their +// denomination and addition only occurs when the denominations match, otherwise +// the coin is simply added to the sum assuming it's not zero. +func (coins Coins) safePlus(coinsB Coins) Coins { sum := ([]Coin)(nil) indexA, indexB := 0, 0 lenA, lenB := len(coins), len(coinsB) + for { if indexA == lenA { if indexB == lenB { + // return nil coins if both sets are empty return sum } - return append(sum, coinsB[indexB:]...) + + // return set B (excluding zero coins) if set A is empty + return append(sum, removeZeroCoins(coinsB[indexB:])...) } else if indexB == lenB { - return append(sum, coins[indexA:]...) + // return set A (excluding zero coins) if set B is empty + return append(sum, removeZeroCoins(coins[indexA:])...) } + coinA, coinB := coins[indexA], coinsB[indexB] + switch strings.Compare(coinA.Denom, coinB.Denom) { - case -1: - if coinA.IsZero() { - // ignore 0 sum coin type - } else { + case -1: // coin A denom < coin B denom + if !coinA.IsZero() { sum = append(sum, coinA) } + indexA++ - case 0: - if coinA.Amount.Add(coinB.Amount).IsZero() { - // ignore 0 sum coin type - } else { - sum = append(sum, coinA.Plus(coinB)) + + case 0: // coin A denom == coin B denom + res := coinA.Plus(coinB) + if !res.IsZero() { + sum = append(sum, res) } + indexA++ indexB++ - case 1: - if coinB.IsZero() { - // ignore 0 sum coin type - } else { + + case 1: // coin A denom > coin B denom + if !coinB.IsZero() { sum = append(sum, coinB) } + indexB++ } } } -// Negative returns a set of coins with all amount negative -func (coins Coins) Negative() Coins { - res := make([]Coin, 0, len(coins)) - for _, coin := range coins { - res = append(res, Coin{ - Denom: coin.Denom, - Amount: coin.Amount.Neg(), - }) - } - return res -} - -// Minus subtracts a set of coins from another (adds the inverse) +// Minus subtracts a set of coins from another. +// +// e.g. +// {2A, 3B} - {A} = {A, 3B} +// {2A} - {0B} = {2A} +// {A, B} - {A} = {B} +// +// CONTRACT: Minus will never return Coins where one Coin has a non-positive +// amount. In otherwords, IsValid will always return true. func (coins Coins) Minus(coinsB Coins) Coins { - return coins.Plus(coinsB.Negative()) + diff, hasNeg := coins.SafeMinus(coinsB) + if hasNeg { + panic("negative coin amount") + } + + return diff } -// IsAllGT returns True iff for every denom in coins, the denom is present at a +// SafeMinus performs the same arithmetic as Minus but returns a boolean if any +// negative coin amount was returned. +func (coins Coins) SafeMinus(coinsB Coins) (Coins, bool) { + diff := coins.safePlus(coinsB.negative()) + return diff, !diff.IsNotNegative() +} + +// IsAllGT returns true iff for every denom in coins, the denom is present at a // greater amount in coinsB. func (coins Coins) IsAllGT(coinsB Coins) bool { - diff := coins.Minus(coinsB) + diff, _ := coins.SafeMinus(coinsB) if len(diff) == 0 { return false } + return diff.IsPositive() } -// IsAllGTE returns True iff for every denom in coins, the denom is present at an -// equal or greater amount in coinsB. +// IsAllGTE returns true iff for every denom in coins, the denom is present at +// an equal or greater amount in coinsB. func (coins Coins) IsAllGTE(coinsB Coins) bool { - diff := coins.Minus(coinsB) + diff, _ := coins.SafeMinus(coinsB) if len(diff) == 0 { return true } + return diff.IsNotNegative() } // IsAllLT returns True iff for every denom in coins, the denom is present at // a smaller amount in coinsB. func (coins Coins) IsAllLT(coinsB Coins) bool { - diff := coinsB.Minus(coins) - if len(diff) == 0 { - return false - } - return diff.IsPositive() + return coinsB.IsAllGT(coins) } -// IsAllLTE returns True iff for every denom in coins, the denom is present at +// IsAllLTE returns true iff for every denom in coins, the denom is present at // a smaller or equal amount in coinsB. func (coins Coins) IsAllLTE(coinsB Coins) bool { - diff := coinsB.Minus(coins) - if len(diff) == 0 { - return true - } - return diff.IsNotNegative() + return coinsB.IsAllGTE(coins) } // IsZero returns true if there are no coins or all coins are zero. @@ -239,40 +300,22 @@ func (coins Coins) IsEqual(coinsB Coins) bool { if len(coins) != len(coinsB) { return false } + + coins = coins.Sort() + coinsB = coinsB.Sort() + for i := 0; i < len(coins); i++ { if coins[i].Denom != coinsB[i].Denom || !coins[i].Amount.Equal(coinsB[i].Amount) { return false } } + return true } -// IsPositive returns true if there is at least one coin, and all -// currencies have a positive value -func (coins Coins) IsPositive() bool { - if len(coins) == 0 { - return false - } - for _, coin := range coins { - if !coin.IsPositive() { - return false - } - } - return true -} - -// IsNotNegative returns true if there is no currency with a negative value -// (even no coins is true here) -func (coins Coins) IsNotNegative() bool { - if len(coins) == 0 { - return true - } - for _, coin := range coins { - if !coin.IsNotNegative() { - return false - } - } - return true +// Empty returns true if there are no coins and false otherwise. +func (coins Coins) Empty() bool { + return len(coins) == 0 } // Returns the amount of a denom from coins @@ -280,15 +323,18 @@ func (coins Coins) AmountOf(denom string) Int { switch len(coins) { case 0: return ZeroInt() + case 1: coin := coins[0] if coin.Denom == denom { return coin.Amount } return ZeroInt() + default: midIdx := len(coins) / 2 // 2:1, 3:1, 4:2 coin := coins[midIdx] + if denom < coin.Denom { return coins[:midIdx].AmountOf(denom) } else if denom == coin.Denom { @@ -299,7 +345,75 @@ func (coins Coins) AmountOf(denom string) Int { } } -//---------------------------------------- +// IsPositive returns true if there is at least one coin and all currencies +// have a positive value. +// +// TODO: Remove once unsigned integers are used. +func (coins Coins) IsPositive() bool { + if len(coins) == 0 { + return false + } + + for _, coin := range coins { + if !coin.IsPositive() { + return false + } + } + + return true +} + +// IsNotNegative returns true if there is no coin amount with a negative value +// (even no coins is true here). +// +// TODO: Remove once unsigned integers are used. +func (coins Coins) IsNotNegative() bool { + if len(coins) == 0 { + return true + } + + for _, coin := range coins { + if !coin.IsNotNegative() { + return false + } + } + + return true +} + +// negative returns a set of coins with all amount negative. +// +// TODO: Remove once unsigned integers are used. +func (coins Coins) negative() Coins { + res := make([]Coin, 0, len(coins)) + + for _, coin := range coins { + res = append(res, Coin{ + Denom: coin.Denom, + Amount: coin.Amount.Neg(), + }) + } + + return res +} + +// removeZeroCoins removes all zero coins from the given coin set in-place. +func removeZeroCoins(coins Coins) Coins { + i, l := 0, len(coins) + for i < l { + if coins[i].IsZero() { + // remove coin + coins = append(coins[:i], coins[i+1:]...) + l-- + } else { + i++ + } + } + + return coins[:i] +} + +//----------------------------------------------------------------------------- // Sort interface //nolint @@ -315,7 +429,7 @@ func (coins Coins) Sort() Coins { return coins } -//---------------------------------------- +//----------------------------------------------------------------------------- // Parsing var ( @@ -333,17 +447,17 @@ func ParseCoin(coinStr string) (coin Coin, err error) { matches := reCoin.FindStringSubmatch(coinStr) if matches == nil { - err = fmt.Errorf("invalid coin expression: %s", coinStr) - return + return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr) } + denomStr, amountStr := matches[2], matches[1] - amount, err := strconv.Atoi(amountStr) - if err != nil { - return + amount, ok := NewIntFromString(amountStr) + if !ok { + return Coin{}, fmt.Errorf("failed to parse coin amount: %s", amountStr) } - return Coin{denomStr, NewInt(int64(amount))}, nil + return Coin{denomStr, amount}, nil } // ParseCoins will parse out a list of coins separated by commas. diff --git a/types/coin_benchmark_test.go b/types/coin_benchmark_test.go new file mode 100644 index 0000000000..9c13bf7724 --- /dev/null +++ b/types/coin_benchmark_test.go @@ -0,0 +1,64 @@ +package types + +import ( + "fmt" + "testing" +) + +func BenchmarkCoinsAdditionIntersect(b *testing.B) { + benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { + return func(b *testing.B) { + coinsA := Coins(make([]Coin, numCoinsA)) + coinsB := Coins(make([]Coin, numCoinsB)) + + for i := 0; i < numCoinsA; i++ { + coinsA[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) + } + for i := 0; i < numCoinsB; i++ { + coinsB[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + coinsA.Plus(coinsB) + } + } + } + + benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}} + for i := 0; i < len(benchmarkSizes); i++ { + sizeA := benchmarkSizes[i][0] + sizeB := benchmarkSizes[i][1] + b.Run(fmt.Sprintf("sizes: A_%d, B_%d", sizeA, sizeB), benchmarkingFunc(sizeA, sizeB)) + } +} + +func BenchmarkCoinsAdditionNoIntersect(b *testing.B) { + benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { + return func(b *testing.B) { + coinsA := Coins(make([]Coin, numCoinsA)) + coinsB := Coins(make([]Coin, numCoinsB)) + + for i := 0; i < numCoinsA; i++ { + coinsA[i] = NewCoin("COINZ_"+string(numCoinsB+i), NewInt(int64(i))) + } + for i := 0; i < numCoinsB; i++ { + coinsB[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + coinsA.Plus(coinsB) + } + } + } + + benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}, {1000, 2}} + for i := 0; i < len(benchmarkSizes); i++ { + sizeA := benchmarkSizes[i][0] + sizeB := benchmarkSizes[i][1] + b.Run(fmt.Sprintf("sizes: A_%d, B_%d", sizeA, sizeB), benchmarkingFunc(sizeA, sizeB)) + } +} diff --git a/types/coin_test.go b/types/coin_test.go index 77307f22a5..774534860d 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -1,43 +1,20 @@ package types import ( - "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestIsPositiveCoin(t *testing.T) { - cases := []struct { - inputOne Coin - expected bool - }{ - {NewInt64Coin("A", 1), true}, - {NewInt64Coin("A", 0), false}, - {NewInt64Coin("a", -1), false}, - } +// ---------------------------------------------------------------------------- +// Coin tests - for tcIndex, tc := range cases { - res := tc.inputOne.IsPositive() - require.Equal(t, tc.expected, res, "%s positivity is incorrect, tc #%d", tc.inputOne.String(), tcIndex) - } -} - -func TestIsNotNegativeCoin(t *testing.T) { - cases := []struct { - inputOne Coin - expected bool - }{ - {NewInt64Coin("A", 1), true}, - {NewInt64Coin("A", 0), true}, - {NewInt64Coin("a", -1), false}, - } - - for tcIndex, tc := range cases { - res := tc.inputOne.IsNotNegative() - require.Equal(t, tc.expected, res, "%s not-negativity is incorrect, tc #%d", tc.inputOne.String(), tcIndex) - } +func TestCoin(t *testing.T) { + require.Panics(t, func() { NewInt64Coin("A", -1) }) + require.Panics(t, func() { NewCoin("A", NewInt(-1)) }) + require.Equal(t, NewInt(5), NewInt64Coin("A", 5).Amount) + require.Equal(t, NewInt(5), NewCoin("A", NewInt(5)).Amount) } func TestSameDenomAsCoin(t *testing.T) { @@ -49,8 +26,7 @@ func TestSameDenomAsCoin(t *testing.T) { {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, - {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), true}, - {NewInt64Coin("stake", -11), NewInt64Coin("stake", 10), true}, + {NewInt64Coin("steak", 1), NewInt64Coin("steak", 10), true}, } for tcIndex, tc := range cases { @@ -59,6 +35,78 @@ func TestSameDenomAsCoin(t *testing.T) { } } +func TestIsEqualCoin(t *testing.T) { + cases := []struct { + inputOne Coin + inputTwo Coin + expected bool + }{ + {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, + {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, + {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, + {NewInt64Coin("steak", 1), NewInt64Coin("steak", 10), false}, + } + + for tcIndex, tc := range cases { + res := tc.inputOne.IsEqual(tc.inputTwo) + require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) + } +} + +func TestPlusCoin(t *testing.T) { + cases := []struct { + inputOne Coin + inputTwo Coin + expected Coin + shouldPanic bool + }{ + {NewInt64Coin("A", 1), NewInt64Coin("A", 1), NewInt64Coin("A", 2), false}, + {NewInt64Coin("A", 1), NewInt64Coin("A", 0), NewInt64Coin("A", 1), false}, + {NewInt64Coin("A", 1), NewInt64Coin("B", 1), NewInt64Coin("A", 1), true}, + } + + for tcIndex, tc := range cases { + if tc.shouldPanic { + require.Panics(t, func() { tc.inputOne.Plus(tc.inputTwo) }) + } else { + res := tc.inputOne.Plus(tc.inputTwo) + require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) + } + } +} + +func TestMinusCoin(t *testing.T) { + cases := []struct { + inputOne Coin + inputTwo Coin + expected Coin + shouldPanic bool + }{ + {NewInt64Coin("A", 1), NewInt64Coin("B", 1), NewInt64Coin("A", 1), true}, + {NewInt64Coin("A", 10), NewInt64Coin("A", 1), NewInt64Coin("A", 9), false}, + {NewInt64Coin("A", 5), NewInt64Coin("A", 3), NewInt64Coin("A", 2), false}, + {NewInt64Coin("A", 5), NewInt64Coin("A", 0), NewInt64Coin("A", 5), false}, + {NewInt64Coin("A", 1), NewInt64Coin("A", 5), Coin{}, true}, + } + + for tcIndex, tc := range cases { + if tc.shouldPanic { + require.Panics(t, func() { tc.inputOne.Minus(tc.inputTwo) }) + } else { + res := tc.inputOne.Minus(tc.inputTwo) + require.Equal(t, tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) + } + } + + tc := struct { + inputOne Coin + inputTwo Coin + expected int64 + }{NewInt64Coin("A", 1), NewInt64Coin("A", 1), 0} + res := tc.inputOne.Minus(tc.inputTwo) + require.Equal(t, tc.expected, res.Amount.Int64()) +} + func TestIsGTECoin(t *testing.T) { cases := []struct { inputOne Coin @@ -67,8 +115,7 @@ func TestIsGTECoin(t *testing.T) { }{ {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, {NewInt64Coin("A", 2), NewInt64Coin("A", 1), true}, - {NewInt64Coin("A", -1), NewInt64Coin("A", 5), false}, - {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, + {NewInt64Coin("A", 1), NewInt64Coin("B", 1), false}, } for tcIndex, tc := range cases { @@ -85,7 +132,6 @@ func TestIsLTCoin(t *testing.T) { }{ {NewInt64Coin("A", 1), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", 2), NewInt64Coin("A", 1), false}, - {NewInt64Coin("A", -1), NewInt64Coin("A", 5), true}, {NewInt64Coin("a", 0), NewInt64Coin("b", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("a", 1), false}, @@ -98,76 +144,18 @@ func TestIsLTCoin(t *testing.T) { } } -func TestIsEqualCoin(t *testing.T) { - cases := []struct { - inputOne Coin - inputTwo Coin - expected bool - }{ - {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, - {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, - {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, - {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), false}, - {NewInt64Coin("stake", -11), NewInt64Coin("stake", 10), false}, - } +func TestCoinIsZero(t *testing.T) { + coin := NewInt64Coin("A", 0) + res := coin.IsZero() + require.True(t, res) - for tcIndex, tc := range cases { - res := tc.inputOne.IsEqual(tc.inputTwo) - require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) - } + coin = NewInt64Coin("A", 1) + res = coin.IsZero() + require.False(t, res) } -func TestPlusCoin(t *testing.T) { - cases := []struct { - inputOne Coin - inputTwo Coin - expected Coin - }{ - {NewInt64Coin("A", 1), NewInt64Coin("A", 1), NewInt64Coin("A", 2)}, - {NewInt64Coin("A", 1), NewInt64Coin("B", 1), NewInt64Coin("A", 1)}, - {NewInt64Coin("asdf", -4), NewInt64Coin("asdf", 5), NewInt64Coin("asdf", 1)}, - } - - for tcIndex, tc := range cases { - res := tc.inputOne.Plus(tc.inputTwo) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) - } - - tc := struct { - inputOne Coin - inputTwo Coin - expected int64 - }{NewInt64Coin("asdf", -1), NewInt64Coin("asdf", 1), 0} - res := tc.inputOne.Plus(tc.inputTwo) - require.Equal(t, tc.expected, res.Amount.Int64()) -} - -func TestMinusCoin(t *testing.T) { - cases := []struct { - inputOne Coin - inputTwo Coin - expected Coin - }{ - - {NewInt64Coin("A", 1), NewInt64Coin("B", 1), NewInt64Coin("A", 1)}, - {NewInt64Coin("asdf", -4), NewInt64Coin("asdf", 5), NewInt64Coin("asdf", -9)}, - {NewInt64Coin("asdf", 10), NewInt64Coin("asdf", 1), NewInt64Coin("asdf", 9)}, - } - - for tcIndex, tc := range cases { - res := tc.inputOne.Minus(tc.inputTwo) - require.Equal(t, tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) - } - - tc := struct { - inputOne Coin - inputTwo Coin - expected int64 - }{NewInt64Coin("A", 1), NewInt64Coin("A", 1), 0} - res := tc.inputOne.Minus(tc.inputTwo) - require.Equal(t, tc.expected, res.Amount.Int64()) - -} +// ---------------------------------------------------------------------------- +// Coins tests func TestIsZeroCoins(t *testing.T) { cases := []struct { @@ -199,8 +187,7 @@ func TestEqualCoins(t *testing.T) { {Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("B", 0)}, false}, {Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("A", 1)}, false}, {Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("A", 0), NewInt64Coin("B", 1)}, false}, - // TODO: is it expected behaviour? shouldn't we sort the coins before comparing them? - {Coins{NewInt64Coin("A", 0), NewInt64Coin("B", 1)}, Coins{NewInt64Coin("B", 1), NewInt64Coin("A", 0)}, false}, + {Coins{NewInt64Coin("A", 0), NewInt64Coin("B", 1)}, Coins{NewInt64Coin("B", 1), NewInt64Coin("A", 0)}, true}, } for tcnum, tc := range cases { @@ -209,16 +196,65 @@ func TestEqualCoins(t *testing.T) { } } -func TestCoins(t *testing.T) { +func TestPlusCoins(t *testing.T) { + zero := NewInt(0) + one := NewInt(1) + two := NewInt(2) - //Define the coins to be used in tests + cases := []struct { + inputOne Coins + inputTwo Coins + expected Coins + }{ + {Coins{{"A", one}, {"B", one}}, Coins{{"A", one}, {"B", one}}, Coins{{"A", two}, {"B", two}}}, + {Coins{{"A", zero}, {"B", one}}, Coins{{"A", zero}, {"B", zero}}, Coins{{"B", one}}}, + {Coins{{"A", two}}, Coins{{"B", zero}}, Coins{{"A", two}}}, + {Coins{{"A", one}}, Coins{{"A", one}, {"B", two}}, Coins{{"A", two}, {"B", two}}}, + {Coins{{"A", zero}, {"B", zero}}, Coins{{"A", zero}, {"B", zero}}, Coins(nil)}, + } + + for tcIndex, tc := range cases { + res := tc.inputOne.Plus(tc.inputTwo) + assert.True(t, res.IsValid()) + require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) + } +} + +func TestMinusCoins(t *testing.T) { + zero := NewInt(0) + one := NewInt(1) + two := NewInt(2) + + testCases := []struct { + inputOne Coins + inputTwo Coins + expected Coins + shouldPanic bool + }{ + {Coins{{"A", two}}, Coins{{"A", one}, {"B", two}}, Coins{{"A", one}, {"B", two}}, true}, + {Coins{{"A", two}}, Coins{{"B", zero}}, Coins{{"A", two}}, false}, + {Coins{{"A", one}}, Coins{{"B", zero}}, Coins{{"A", one}}, false}, + {Coins{{"A", one}, {"B", one}}, Coins{{"A", one}}, Coins{{"B", one}}, false}, + {Coins{{"A", one}, {"B", one}}, Coins{{"A", two}}, Coins{}, true}, + } + + for i, tc := range testCases { + if tc.shouldPanic { + require.Panics(t, func() { tc.inputOne.Minus(tc.inputTwo) }) + } else { + res := tc.inputOne.Minus(tc.inputTwo) + assert.True(t, res.IsValid()) + require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", i) + } + } +} + +func TestCoins(t *testing.T) { good := Coins{ {"GAS", NewInt(1)}, {"MINERAL", NewInt(1)}, {"TREE", NewInt(1)}, } - neg := good.Negative() - sum := good.Plus(neg) empty := Coins{ {"GOLD", NewInt(0)}, } @@ -228,6 +264,7 @@ func TestCoins(t *testing.T) { {"GAS", NewInt(1)}, {"MINERAL", NewInt(1)}, } + // both are after the first one, but the second and third are in the wrong order badSort2 := Coins{ {"GAS", NewInt(1)}, @@ -251,13 +288,10 @@ func TestCoins(t *testing.T) { assert.True(t, good.IsAllGTE(empty), "Expected %v to be >= %v", good, empty) assert.False(t, good.IsAllLT(empty), "Expected %v to be < %v", good, empty) assert.True(t, empty.IsAllLT(good), "Expected %v to be < %v", empty, good) - assert.False(t, neg.IsPositive(), "Expected neg coins to not be positive: %v", neg) - assert.Zero(t, len(sum), "Expected 0 coins") assert.False(t, badSort1.IsValid(), "Coins are not sorted") assert.False(t, badSort2.IsValid(), "Coins are not sorted") assert.False(t, badAmt.IsValid(), "Coins cannot include 0 amounts") assert.False(t, dup.IsValid(), "Duplicate coin") - } func TestCoinsGT(t *testing.T) { @@ -314,32 +348,6 @@ func TestCoinsLTE(t *testing.T) { assert.True(t, Coins{}.IsAllLTE(Coins{{"A", one}})) } -func TestPlusCoins(t *testing.T) { - one := NewInt(1) - zero := NewInt(0) - negone := NewInt(-1) - two := NewInt(2) - - cases := []struct { - inputOne Coins - inputTwo Coins - expected Coins - }{ - {Coins{{"A", one}, {"B", one}}, Coins{{"A", one}, {"B", one}}, Coins{{"A", two}, {"B", two}}}, - {Coins{{"A", zero}, {"B", one}}, Coins{{"A", zero}, {"B", zero}}, Coins{{"B", one}}}, - {Coins{{"A", zero}, {"B", zero}}, Coins{{"A", zero}, {"B", zero}}, Coins(nil)}, - {Coins{{"A", one}, {"B", zero}}, Coins{{"A", negone}, {"B", zero}}, Coins(nil)}, - {Coins{{"A", negone}, {"B", zero}}, Coins{{"A", zero}, {"B", zero}}, Coins{{"A", negone}}}, - } - - for tcIndex, tc := range cases { - res := tc.inputOne.Plus(tc.inputTwo) - assert.True(t, res.IsValid()) - require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) - } -} - -//Test the parsing of Coin and Coins func TestParse(t *testing.T) { one := NewInt(1) @@ -370,11 +378,9 @@ func TestParse(t *testing.T) { require.Equal(t, tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) } } - } func TestSortCoins(t *testing.T) { - good := Coins{ NewInt64Coin("GAS", 1), NewInt64Coin("MINERAL", 1), @@ -424,7 +430,6 @@ func TestSortCoins(t *testing.T) { } func TestAmountOf(t *testing.T) { - case0 := Coins{} case1 := Coins{ NewInt64Coin("", 0), @@ -481,55 +486,3 @@ func TestAmountOf(t *testing.T) { assert.Equal(t, NewInt(tc.amountOfTREE), tc.coins.AmountOf("TREE")) } } - -func BenchmarkCoinsAdditionIntersect(b *testing.B) { - benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { - return func(b *testing.B) { - coinsA := Coins(make([]Coin, numCoinsA)) - coinsB := Coins(make([]Coin, numCoinsB)) - for i := 0; i < numCoinsA; i++ { - coinsA[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) - } - for i := 0; i < numCoinsB; i++ { - coinsB[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - coinsA.Plus(coinsB) - } - } - } - - benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}} - for i := 0; i < len(benchmarkSizes); i++ { - sizeA := benchmarkSizes[i][0] - sizeB := benchmarkSizes[i][1] - b.Run(fmt.Sprintf("sizes: A_%d, B_%d", sizeA, sizeB), benchmarkingFunc(sizeA, sizeB)) - } -} - -func BenchmarkCoinsAdditionNoIntersect(b *testing.B) { - benchmarkingFunc := func(numCoinsA int, numCoinsB int) func(b *testing.B) { - return func(b *testing.B) { - coinsA := Coins(make([]Coin, numCoinsA)) - coinsB := Coins(make([]Coin, numCoinsB)) - for i := 0; i < numCoinsA; i++ { - coinsA[i] = NewCoin("COINZ_"+string(numCoinsB+i), NewInt(int64(i))) - } - for i := 0; i < numCoinsB; i++ { - coinsB[i] = NewCoin("COINZ_"+string(i), NewInt(int64(i))) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - coinsA.Plus(coinsB) - } - } - } - - benchmarkSizes := [][]int{{1, 1}, {5, 5}, {5, 20}, {1, 1000}, {2, 1000}, {1000, 2}} - for i := 0; i < len(benchmarkSizes); i++ { - sizeA := benchmarkSizes[i][0] - sizeB := benchmarkSizes[i][1] - b.Run(fmt.Sprintf("sizes: A_%d, B_%d", sizeA, sizeB), benchmarkingFunc(sizeA, sizeB)) - } -} diff --git a/types/int.go b/types/int.go index 2083168e9c..417a1763d4 100644 --- a/types/int.go +++ b/types/int.go @@ -322,11 +322,11 @@ func NewUint(n uint64) Uint { // NewUintFromBigUint constructs Uint from big.Uint func NewUintFromBigInt(i *big.Int) Uint { - // Check overflow - if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 { + res := Uint{i} + if UintOverflow(res) { panic("Uint overflow") } - return Uint{i} + return res } // NewUintFromString constructs Uint from string @@ -353,11 +353,12 @@ func NewUintWithDecimal(n uint64, dec int) Uint { i := new(big.Int) i.Mul(new(big.Int).SetUint64(n), exp) - // Check overflow - if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 { + res := Uint{i} + if UintOverflow(res) { panic("NewUintWithDecimal() out of bound") } - return Uint{i} + + return res } // ZeroUint returns Uint value with zero @@ -408,8 +409,7 @@ func (i Uint) LT(i2 Uint) bool { // Add adds Uint from another func (i Uint) Add(i2 Uint) (res Uint) { res = Uint{add(i.i, i2.i)} - // Check overflow - if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 { + if UintOverflow(res) { panic("Uint overflow") } return @@ -423,13 +423,23 @@ func (i Uint) AddRaw(i2 uint64) Uint { // Sub subtracts Uint from another func (i Uint) Sub(i2 Uint) (res Uint) { res = Uint{sub(i.i, i2.i)} - // Check overflow - if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 { + if UintOverflow(res) { panic("Uint overflow") } return } +// SafeSub attempts to subtract one Uint from another. A boolean is also returned +// indicating if the result contains integer overflow. +func (i Uint) SafeSub(i2 Uint) (Uint, bool) { + res := Uint{sub(i.i, i2.i)} + if UintOverflow(res) { + return res, true + } + + return res, false +} + // SubRaw subtracts uint64 from Uint func (i Uint) SubRaw(i2 uint64) Uint { return i.Sub(NewUint(i2)) @@ -437,15 +447,15 @@ func (i Uint) SubRaw(i2 uint64) Uint { // Mul multiples two Uints func (i Uint) Mul(i2 Uint) (res Uint) { - // Check overflow if i.i.BitLen()+i2.i.BitLen()-1 > 256 { panic("Uint overflow") } + res = Uint{mul(i.i, i2.i)} - // Check overflow - if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 { + if UintOverflow(res) { panic("Uint overflow") } + return } @@ -530,6 +540,12 @@ func (i *Uint) UnmarshalJSON(bz []byte) error { //__________________________________________________________________________ +// UintOverflow returns true if a given unsigned integer overflows and false +// otherwise. +func UintOverflow(x Uint) bool { + return x.i.Sign() == -1 || x.i.Sign() == 1 && x.i.BitLen() > 256 +} + // AddUint64Overflow performs the addition operation on two uint64 integers and // returns a boolean on whether or not the result overflows. func AddUint64Overflow(a, b uint64) (uint64, bool) { diff --git a/types/int_test.go b/types/int_test.go index 78bee66bfa..9e189858c7 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -591,6 +591,31 @@ func TestEncodingTableUint(t *testing.T) { } } +func TestSafeSub(t *testing.T) { + testCases := []struct { + x, y Uint + expected uint64 + overflow bool + }{ + {NewUint(0), NewUint(0), 0, false}, + {NewUint(10), NewUint(5), 5, false}, + {NewUint(5), NewUint(10), 5, true}, + {NewUint(math.MaxUint64), NewUint(0), math.MaxUint64, false}, + } + + for i, tc := range testCases { + res, overflow := tc.x.SafeSub(tc.y) + require.Equal( + t, tc.overflow, overflow, + "invalid overflow result; x: %s, y: %s, tc: #%d", tc.x, tc.y, i, + ) + require.Equal( + t, tc.expected, res.BigInt().Uint64(), + "invalid subtraction result; x: %s, y: %s, tc: #%d", tc.x, tc.y, i, + ) + } +} + func TestAddUint64Overflow(t *testing.T) { testCases := []struct { a, b uint64 diff --git a/x/auth/ante.go b/x/auth/ante.go index 7beb643c56..9a7a15e3e9 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -281,24 +281,36 @@ func deductFees(acc Account, fee StdFee) (Account, sdk.Result) { coins := acc.GetCoins() feeAmount := fee.Amount - newCoins := coins.Minus(feeAmount) - if !newCoins.IsNotNegative() { + if !feeAmount.IsValid() { + return nil, sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", feeAmount)).Result() + } + + newCoins, ok := coins.SafeMinus(feeAmount) + if ok { errMsg := fmt.Sprintf("%s < %s", coins, feeAmount) return nil, sdk.ErrInsufficientFunds(errMsg).Result() } + err := acc.SetCoins(newCoins) if err != nil { // Handle w/ #870 panic(err) } + return acc, sdk.Result{} } func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { // currently we use a very primitive gas pricing model with a constant gasPrice. // adjustFeesByGas handles calculating the amount of fees required based on the provided gas. - // TODO: Make the gasPrice not a constant, and account for tx size. - requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas) + // + // TODO: + // - Make the gasPrice not a constant, and account for tx size. + // - Make Gas an unsigned integer and use tx basic validation + if stdTx.Fee.Gas <= 0 { + return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied: %d", stdTx.Fee.Gas)).Result() + } + requiredFees := adjustFeesByGas(ctx.MinimumFees(), uint64(stdTx.Fee.Gas)) // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) { diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 0e8d13957a..566892e07e 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -708,7 +708,6 @@ func TestAdjustFeesByGas(t *testing.T) { }{ {"nil coins", args{sdk.Coins{}, 10000}, sdk.Coins{}}, {"nil coins", args{sdk.Coins{sdk.NewInt64Coin("A", 10), sdk.NewInt64Coin("B", 0)}, 10000}, sdk.Coins{sdk.NewInt64Coin("A", 20), sdk.NewInt64Coin("B", 10)}}, - {"negative coins", args{sdk.Coins{sdk.NewInt64Coin("A", -10), sdk.NewInt64Coin("B", 10)}, 10000}, sdk.Coins{sdk.NewInt64Coin("B", 20)}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 3a33870e14..af930953f4 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -182,11 +182,13 @@ func hasCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt s // SubtractCoins subtracts amt from the coins at the addr. func subtractCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error) { ctx.GasMeter().ConsumeGas(costSubtractCoins, "subtractCoins") + oldCoins := getCoins(ctx, am, addr) - newCoins := oldCoins.Minus(amt) - if !newCoins.IsNotNegative() { + newCoins, hasNeg := oldCoins.SafeMinus(amt) + if hasNeg { return amt, nil, sdk.ErrInsufficientCoins(fmt.Sprintf("%s < %s", oldCoins, amt)) } + err := setCoins(ctx, am, addr, newCoins) tags := sdk.NewTags("sender", []byte(addr.String())) return newCoins, tags, err diff --git a/x/bank/msgs_test.go b/x/bank/msgs_test.go index 14e9a4c709..040d12bda4 100644 --- a/x/bank/msgs_test.go +++ b/x/bank/msgs_test.go @@ -35,8 +35,6 @@ func TestInputValidation(t *testing.T) { emptyCoins := sdk.Coins{} emptyCoins2 := sdk.Coins{sdk.NewInt64Coin("eth", 0)} someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} - minusCoins := sdk.Coins{sdk.NewInt64Coin("eth", -34)} - someMinusCoins := sdk.Coins{sdk.NewInt64Coin("atom", 20), sdk.NewInt64Coin("eth", -34)} unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} cases := []struct { @@ -52,8 +50,6 @@ func TestInputValidation(t *testing.T) { {false, NewInput(addr1, emptyCoins)}, // invalid coins {false, NewInput(addr1, emptyCoins2)}, // invalid coins {false, NewInput(addr1, someEmptyCoins)}, // invalid coins - {false, NewInput(addr1, minusCoins)}, // negative coins - {false, NewInput(addr1, someMinusCoins)}, // negative coins {false, NewInput(addr1, unsortedCoins)}, // unsorted coins } @@ -77,8 +73,6 @@ func TestOutputValidation(t *testing.T) { emptyCoins := sdk.Coins{} emptyCoins2 := sdk.Coins{sdk.NewInt64Coin("eth", 0)} someEmptyCoins := sdk.Coins{sdk.NewInt64Coin("eth", 10), sdk.NewInt64Coin("atom", 0)} - minusCoins := sdk.Coins{sdk.NewInt64Coin("eth", -34)} - someMinusCoins := sdk.Coins{sdk.NewInt64Coin("atom", 20), sdk.NewInt64Coin("eth", -34)} unsortedCoins := sdk.Coins{sdk.NewInt64Coin("eth", 1), sdk.NewInt64Coin("atom", 1)} cases := []struct { @@ -94,8 +88,6 @@ func TestOutputValidation(t *testing.T) { {false, NewOutput(addr1, emptyCoins)}, // invalid coins {false, NewOutput(addr1, emptyCoins2)}, // invalid coins {false, NewOutput(addr1, someEmptyCoins)}, // invalid coins - {false, NewOutput(addr1, minusCoins)}, // negative coins - {false, NewOutput(addr1, someMinusCoins)}, // negative coins {false, NewOutput(addr1, unsortedCoins)}, // unsorted coins } diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index f33c5e0c99..f1fd866c1f 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -82,7 +82,7 @@ func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.A toAddr.String(), ) - coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} + coins := sdk.Coins{sdk.NewCoin(initFromCoins[denomIndex].Denom, amt)} msg = bank.MsgSend{ Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index e488d2abab..4a661985c9 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -13,7 +13,6 @@ import ( var ( coinsPos = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1000)} coinsZero = sdk.Coins{} - coinsNeg = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, -10000)} coinsPosNotAtoms = sdk.Coins{sdk.NewInt64Coin("foo", 10000)} coinsMulti = sdk.Coins{sdk.NewInt64Coin(stakeTypes.DefaultBondDenom, 1000), sdk.NewInt64Coin("foo", 10000)} ) @@ -40,7 +39,6 @@ func TestMsgSubmitProposal(t *testing.T) { {"Test Proposal", "the purpose of this proposal is to test", 0x05, addrs[0], coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, sdk.AccAddress{}, coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsZero, true}, - {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsNeg, false}, {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsMulti, true}, } @@ -66,7 +64,6 @@ func TestMsgDeposit(t *testing.T) { {0, addrs[0], coinsPos, true}, {1, sdk.AccAddress{}, coinsPos, false}, {1, addrs[0], coinsZero, true}, - {1, addrs[0], coinsNeg, false}, {1, addrs[0], coinsMulti, true}, } diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 9041bfe56c..c77150535e 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -20,7 +20,11 @@ func TestCannotUnjailUnlessJailed(t *testing.T) { got := stake.NewHandler(sk)(ctx, msg) require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) + + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, + ) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // assert non-jailed validator can't be unjailed diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 94251ded31..e5318f292f 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -35,7 +35,10 @@ func TestHandleDoubleSign(t *testing.T) { got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt)) require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, + ) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) // handle a signature to set signing info @@ -76,7 +79,10 @@ func TestSlashingPeriodCap(t *testing.T) { require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, + ) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, operatorAddr).GetPower())) // handle a signature to set signing info @@ -140,8 +146,13 @@ func TestHandleAbsentValidator(t *testing.T) { got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) + + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, + ) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) + // will exist since the validator has been bonded info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) @@ -296,7 +307,11 @@ func TestHandleNewValidator(t *testing.T) { got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}}) + + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt))}, + ) require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower()) // Now a validator, for two blocks diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index c6590c94e7..932ac51a93 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -20,7 +20,10 @@ func TestBeginBlocker(t *testing.T) { got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt)) require.True(t, got.IsOK()) stake.EndBlocker(ctx, sk) - require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) + require.Equal( + t, ck.GetCoins(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, + ) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) val := abci.Validator{ diff --git a/x/stake/types/msg_test.go b/x/stake/types/msg_test.go index 7e719f3eef..4b4055fca2 100644 --- a/x/stake/types/msg_test.go +++ b/x/stake/types/msg_test.go @@ -12,7 +12,6 @@ import ( var ( coinPos = sdk.NewInt64Coin(DefaultBondDenom, 1000) coinZero = sdk.NewInt64Coin(DefaultBondDenom, 0) - coinNeg = sdk.NewInt64Coin(DefaultBondDenom, -10000) ) // test ValidateBasic for MsgCreateValidator @@ -34,8 +33,6 @@ func TestMsgCreateValidator(t *testing.T) { {"empty address", "a", "b", "c", "d", commission2, emptyAddr, pk1, coinPos, false}, {"empty pubkey", "a", "b", "c", "d", commission1, addr1, emptyPubkey, coinPos, true}, {"empty bond", "a", "b", "c", "d", commission2, addr1, pk1, coinZero, false}, - {"negative bond", "a", "b", "c", "d", commission2, addr1, pk1, coinNeg, false}, - {"negative bond", "a", "b", "c", "d", commission1, addr1, pk1, coinNeg, false}, } for _, tc := range tests { @@ -96,8 +93,6 @@ func TestMsgCreateValidatorOnBehalfOf(t *testing.T) { {"empty validator address", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), emptyAddr, pk2, coinPos, false}, {"empty pubkey", "a", "b", "c", "d", commission1, sdk.AccAddress(addr1), addr2, emptyPubkey, coinPos, true}, {"empty bond", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), addr2, pk2, coinZero, false}, - {"negative bond", "a", "b", "c", "d", commission1, sdk.AccAddress(addr1), addr2, pk2, coinNeg, false}, - {"negative bond", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), addr2, pk2, coinNeg, false}, } for _, tc := range tests { @@ -136,7 +131,6 @@ func TestMsgDelegate(t *testing.T) { {"empty delegator", sdk.AccAddress(emptyAddr), addr1, coinPos, false}, {"empty validator", sdk.AccAddress(addr1), emptyAddr, coinPos, false}, {"empty bond", sdk.AccAddress(addr1), addr2, coinZero, false}, - {"negative bond", sdk.AccAddress(addr1), addr2, coinNeg, false}, } for _, tc := range tests { From d8bbf85efaadd22dfb2dce7245ffa7db62f7daa0 Mon Sep 17 00:00:00 2001 From: Max Levy <35595512+maxim-levy@users.noreply.github.com> Date: Tue, 20 Nov 2018 19:03:11 +0900 Subject: [PATCH 22/51] Merge PR #2866: URL fixed Follow up on Documentation Structure Change and Cleanup (#2808) --- networks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networks/README.md b/networks/README.md index 221f6e3858..9e948d4c27 100644 --- a/networks/README.md +++ b/networks/README.md @@ -3,4 +3,4 @@ Here contains the files required for automated deployment of either local or remote testnets. Doing so is best accomplished using the `make` targets. For more information, see the -[networks documentation](/docs/getting-started/networks.md) +[networks documentation](/docs/gaia/networks.md) From d911565d0bb89a5996c0adf0dda21e74dca3e7fc Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 13:16:44 -0800 Subject: [PATCH 23/51] Fix compile --- baseapp/baseapp.go | 4 ++-- baseapp/baseapp_test.go | 13 +++++++------ baseapp/options.go | 2 +- cmd/gaia/cmd/gaiad/main.go | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 7cd4d48571..34780cfbbe 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -68,7 +68,7 @@ type BaseApp struct { // spam prevention minimumFees sdk.Coins - maximumBlockGas int64 + maximumBlockGas uint64 // flag for sealing sealed bool @@ -185,7 +185,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { func (app *BaseApp) SetMinimumFees(fees sdk.Coins) { app.minimumFees = fees } // SetMaximumBlockGas sets the maximum gas allowable per block. -func (app *BaseApp) SetMaximumBlockGas(gas int64) { app.maximumBlockGas = gas } +func (app *BaseApp) SetMaximumBlockGas(gas uint64) { app.maximumBlockGas = gas } // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 27e5f8280e..8ea5cbb89b 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -855,7 +855,7 @@ func TestTxGasLimits(t *testing.T) { // Test that transactions exceeding gas limits fail func TestMaxBlockGasLimits(t *testing.T) { - gasGranted := int64(10) + gasGranted := uint64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) @@ -880,7 +880,7 @@ func TestMaxBlockGasLimits(t *testing.T) { }() count := tx.(*txTest).Counter - newCtx.GasMeter().ConsumeGas(count, "counter-ante") + newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante") res = sdk.Result{ GasWanted: gasGranted, } @@ -892,7 +892,7 @@ func TestMaxBlockGasLimits(t *testing.T) { routerOpt := func(bapp *BaseApp) { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { count := msg.(msgCounter).Counter - ctx.GasMeter().ConsumeGas(count, "counter-handler") + ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") return sdk.Result{} }) } @@ -903,7 +903,7 @@ func TestMaxBlockGasLimits(t *testing.T) { testCases := []struct { tx *txTest numDelivers int - gasUsedPerDeliver int64 + gasUsedPerDeliver uint64 fail bool failAfterDeliver int }{ @@ -933,12 +933,13 @@ func TestMaxBlockGasLimits(t *testing.T) { // check for failed transactions if tc.fail && (j+1) > tc.failAfterDeliver { - require.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.Equal(t, res.Code, sdk.CodeOutOfGas, fmt.Sprintf("%d: %v, %v", i, tc, res)) + require.Equal(t, res.Codespace, sdk.CodespaceRoot, fmt.Sprintf("%d: %v, %v", i, tc, res)) require.True(t, ctx.BlockGasMeter().PastLimit()) } else { // check gas used and wanted - expBlockGasUsed := tc.gasUsedPerDeliver * int64(j+1) + expBlockGasUsed := tc.gasUsedPerDeliver * uint64(j+1) require.Equal(t, expBlockGasUsed, blockGasUsed, fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res)) diff --git a/baseapp/options.go b/baseapp/options.go index 8d61313143..7fd95aec8f 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -40,7 +40,7 @@ func SetMinimumFees(minFees string) func(*BaseApp) { } // SetMinimumFees returns an option that sets the minimum fees on the app. -func SetMaximumBlockGas(gas int64) func(*BaseApp) { +func SetMaximumBlockGas(gas uint64) func(*BaseApp) { return func(bap *BaseApp) { bap.SetMaximumBlockGas(gas) } } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 4ae6f57d5a..ecb906bc71 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -63,7 +63,7 @@ func newApp(logger log.Logger, db dbm.DB, if err != nil { panic(err) } - maxBlockGas := genDoc.ConsensusParams.BlockSize.MaxGas + maxBlockGas := uint64(genDoc.ConsensusParams.BlockSize.MaxGas) return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")), From 10bdf8fa037439006189f1ea917c1f8f754197a4 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 16:44:49 -0800 Subject: [PATCH 24/51] Store ConsensusParams to main store --- baseapp/baseapp.go | 107 +++++++++++++------ baseapp/baseapp_test.go | 12 ++- baseapp/options.go | 5 - cmd/gaia/cmd/gaiad/main.go | 13 +-- docs/examples/basecoin/cmd/basecoind/main.go | 3 +- docs/examples/democoin/cmd/democoind/main.go | 5 +- server/constructors.go | 4 +- server/start.go | 9 +- store/rootmultistore.go | 8 +- types/store.go | 1 + 10 files changed, 98 insertions(+), 69 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 34780cfbbe..19a5d6dab7 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -6,6 +6,7 @@ import ( "runtime/debug" "strings" + "github.com/gogo/protobuf/proto" "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" @@ -19,11 +20,8 @@ import ( "github.com/cosmos/cosmos-sdk/version" ) -// Key to store the header in the DB itself. -// Use the db directly instead of a store to avoid -// conflicts with handlers writing to the store -// and to avoid affecting the Merkle root. -var dbHeaderKey = []byte("header") +// Key to store the consensus params in the main store. +var mainConsensusParamsKey = []byte("consensus_params") // Enum mode for app.runTx type runTxMode uint8 @@ -48,9 +46,11 @@ type BaseApp struct { queryRouter QueryRouter // router for redirecting query calls txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx - anteHandler sdk.AnteHandler // ante handler for fee and auth + // set upon LoadVersion or LoadLatestVersion. + mainKey *sdk.KVStoreKey // Main KVStore in cms // may be nil + anteHandler sdk.AnteHandler // ante handler for fee and auth initChainer sdk.InitChainer // initialize state with validators and state blob beginBlocker sdk.BeginBlocker // logic to run before any txs endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes @@ -66,9 +66,12 @@ type BaseApp struct { deliverState *state // for DeliverTx voteInfos []abci.VoteInfo // absent validators from begin block + // consensus params + // TODO move this in the future to baseapp param store on main store. + consensusParams *abci.ConsensusParams + // spam prevention - minimumFees sdk.Coins - maximumBlockGas uint64 + minimumFees sdk.Coins // flag for sealing sealed bool @@ -78,10 +81,6 @@ var _ abci.Application = (*BaseApp)(nil) // NewBaseApp returns a reference to an initialized BaseApp. // -// TODO: Determine how to use a flexible and robust configuration paradigm that -// allows for sensible defaults while being highly configurable -// (e.g. functional options). -// // NOTE: The db is used to store the version number for now. // Accepts a user-defined txDecoder // Accepts variable number of option functions, which act on the BaseApp to set configuration choices @@ -95,7 +94,6 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod queryRouter: NewQueryRouter(), txDecoder: txDecoder, } - for _, option := range options { option(app) } @@ -138,21 +136,21 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { } // load latest application version -func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error { +func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error { err := app.cms.LoadLatestVersion() if err != nil { return err } - return app.initFromStore(mainKey) + return app.initFromMainStore(mainKey) } // load application version -func (app *BaseApp) LoadVersion(version int64, mainKey sdk.StoreKey) error { +func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error { err := app.cms.LoadVersion(version) if err != nil { return err } - return app.initFromStore(mainKey) + return app.initFromMainStore(mainKey) } // the last CommitID of the multistore @@ -166,13 +164,34 @@ func (app *BaseApp) LastBlockHeight() int64 { } // initializes the remaining logic from app.cms -func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { +func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { + // main store should exist. - // TODO: we don't actually need the main store here - main := app.cms.GetKVStore(mainKey) - if main == nil { + mainStore := app.cms.GetKVStore(mainKey) + if mainStore == nil { return errors.New("baseapp expects MultiStore with 'main' KVStore") } + + // memoize mainKey. + if app.mainKey != nil { + panic("app.mainKey expected to be nil; duplicate init?") + } + app.mainKey = mainKey + + // load consensus param from the main store + consensusParamsBz := mainStore.Get(mainConsensusParamsKey) + if consensusParamsBz != nil { + var consensusParams = &abci.ConsensusParams{} + err := proto.Unmarshal(consensusParamsBz, consensusParams) + if err != nil { + panic(err) + } + app.setConsensusParams(consensusParams) + } else { + // It will get saved later during InitChain. + // TODO assert that InitChain hasn't yet been called. + } + // Needed for `gaiad export`, which inits from store but never calls initchain app.setCheckState(abci.Header{}) @@ -184,9 +203,6 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // SetMinimumFees sets the minimum fees. func (app *BaseApp) SetMinimumFees(fees sdk.Coins) { app.minimumFees = fees } -// SetMaximumBlockGas sets the maximum gas allowable per block. -func (app *BaseApp) SetMaximumBlockGas(gas uint64) { app.maximumBlockGas = gas } - // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { @@ -220,6 +236,30 @@ func (app *BaseApp) setDeliverState(header abci.Header) { } } +// setConsensusParams memoizes the consensus params. +func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) { + app.consensusParams = consensusParams +} + +// setConsensusParams stores the consensus params to the main store. +func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) { + consensusParamsBz, err := proto.Marshal(consensusParams) + if err != nil { + panic(err) + } + mainStore := app.cms.GetKVStore(app.mainKey) + mainStore.Set(mainConsensusParamsKey, consensusParamsBz) +} + +// getMaximumBlockGas gets the maximum gas from the consensus params. +func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) { + if app.consensusParams == nil || app.consensusParams.BlockSize == nil { + return 0 + } else { + return uint64(app.consensusParams.BlockSize.MaxGas) + } +} + //______________________________________________________________________________ // ABCI @@ -244,6 +284,13 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp // Implements ABCI // InitChain runs the initialization logic directly on the CommitMultiStore. func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { + + // Stash the consensus params in the cms main store and memoize. + if req.ConsensusParams != nil { + app.setConsensusParams(req.ConsensusParams) + app.storeConsensusParams(req.ConsensusParams) + } + // Initialize the deliver state and check state with ChainID and run initChain app.setDeliverState(abci.Header{ChainID: req.ChainId}) app.setCheckState(abci.Header{ChainID: req.ChainId}) @@ -435,8 +482,8 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // add block gas meter var gasMeter sdk.GasMeter - if app.maximumBlockGas > 0 { - gasMeter = sdk.NewGasMeter(app.maximumBlockGas) + if maxGas := app.getMaximumBlockGas(); maxGas > 0 { + gasMeter = sdk.NewGasMeter(maxGas) } else { gasMeter = sdk.NewInfiniteGasMeter() } @@ -733,14 +780,6 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // Implements ABCI func (app *BaseApp) Commit() (res abci.ResponseCommit) { header := app.deliverState.ctx.BlockHeader() - /* - // Write the latest Header to the store - headerBytes, err := proto.Marshal(&header) - if err != nil { - panic(err) - } - app.db.SetSync(dbHeaderKey, headerBytes) - */ // Write the Deliver state and commit the MultiStore app.deliverState.ms.Write() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 8ea5cbb89b..83cecee083 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -56,7 +56,9 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { require.Equal(t, t.Name(), app.Name()) // no stores are mounted - require.Panics(t, func() { app.LoadLatestVersion(capKey1) }) + require.Panics(t, func() { + app.LoadLatestVersion(capKey1) + }) app.MountStoresIAVL(capKey1, capKey2) @@ -898,7 +900,13 @@ func TestMaxBlockGasLimits(t *testing.T) { } app := setupBaseApp(t, anteOpt, routerOpt) - app.SetMaximumBlockGas(100) + app.InitChain(abci.RequestInitChain{ + ConsensusParams: &abci.ConsensusParams{ + BlockSize: &abci.BlockSizeParams{ + MaxGas: 100, + }, + }, + }) testCases := []struct { tx *txTest diff --git a/baseapp/options.go b/baseapp/options.go index 7fd95aec8f..a6460248df 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -39,11 +39,6 @@ func SetMinimumFees(minFees string) func(*BaseApp) { return func(bap *BaseApp) { bap.SetMinimumFees(fees) } } -// SetMinimumFees returns an option that sets the minimum fees on the app. -func SetMaximumBlockGas(gas uint64) func(*BaseApp) { - return func(bap *BaseApp) { bap.SetMaximumBlockGas(gas) } -} - func (app *BaseApp) SetName(name string) { if app.sealed { panic("SetName() on sealed BaseApp") diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index ecb906bc71..f3a8309e06 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -13,7 +13,6 @@ import ( "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/node" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -55,20 +54,10 @@ func main() { } } -func newApp(logger log.Logger, db dbm.DB, - traceStore io.Writer, genDocProvider node.GenesisDocProvider) abci.Application { - - // get the maximum gas from tendermint genesis parameters - genDoc, err := genDocProvider() - if err != nil { - panic(err) - } - maxBlockGas := uint64(genDoc.ConsensusParams.BlockSize.MaxGas) - +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { return app.NewGaiaApp(logger, db, traceStore, baseapp.SetPruning(viper.GetString("pruning")), baseapp.SetMinimumFees(viper.GetString("minimum_fees")), - baseapp.SetMaximumBlockGas(maxBlockGas), ) } diff --git a/docs/examples/basecoin/cmd/basecoind/main.go b/docs/examples/basecoin/cmd/basecoind/main.go index 3f257c495c..318b36a8f5 100644 --- a/docs/examples/basecoin/cmd/basecoind/main.go +++ b/docs/examples/basecoin/cmd/basecoind/main.go @@ -6,7 +6,6 @@ import ( "io" "os" - "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/cosmos/cosmos-sdk/baseapp" @@ -121,7 +120,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return cmd } -func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer, _ node.GenesisDocProvider) abci.Application { +func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Application { return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } diff --git a/docs/examples/democoin/cmd/democoind/main.go b/docs/examples/democoin/cmd/democoind/main.go index a4feef77c6..730109798c 100644 --- a/docs/examples/democoin/cmd/democoind/main.go +++ b/docs/examples/democoin/cmd/democoind/main.go @@ -9,7 +9,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/viper" "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/spf13/cobra" @@ -125,9 +124,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return cmd } -func newApp(logger log.Logger, db dbm.DB, _ io.Writer, - _ node.GenesisDocProvider) abci.Application { - +func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { return app.NewDemocoinApp(logger, db) } diff --git a/server/constructors.go b/server/constructors.go index 4e62239b62..9039d8a81d 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -9,15 +9,13 @@ import ( abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/node" tmtypes "github.com/tendermint/tendermint/types" ) type ( // AppCreator is a function that allows us to lazily initialize an // application using various configurations. - AppCreator func(log.Logger, dbm.DB, - io.Writer, node.GenesisDocProvider) abci.Application + AppCreator func(log.Logger, dbm.DB, io.Writer) abci.Application // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. diff --git a/server/start.go b/server/start.go index d84169ab44..cf39ff71b6 100644 --- a/server/start.go +++ b/server/start.go @@ -68,9 +68,7 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { return err } - cfg := ctx.Config - genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) - app := appCreator(ctx.Logger, db, traceWriter, genDocProvider) + app := appCreator(ctx.Logger, db, traceWriter) svr, err := server.NewServer(addr, "socket", app) if err != nil { @@ -109,8 +107,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { return nil, err } - genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) - app := appCreator(ctx.Logger, db, traceWriter, genDocProvider) + app := appCreator(ctx.Logger, db, traceWriter) nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) if err != nil { @@ -123,7 +120,7 @@ func startInProcess(ctx *Context, appCreator AppCreator) (*node.Node, error) { pvm.LoadOrGenFilePV(cfg.PrivValidatorFile()), nodeKey, proxy.NewLocalClientCreator(app), - genDocProvider, + node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, node.DefaultMetricsProvider(cfg.Instrumentation), ctx.Logger.With("module", "node"), diff --git a/store/rootmultistore.go b/store/rootmultistore.go index cd2d0135f1..3faf67a5e5 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -230,13 +230,19 @@ func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore { } // Implements MultiStore. +// If the store does not exist, panics. func (rs *rootMultiStore) GetStore(key StoreKey) Store { - return rs.stores[key] + store := rs.stores[key] + if store == nil { + panic("Could not load store " + key.String()) + } + return store } // GetKVStore implements the MultiStore interface. If tracing is enabled on the // rootMultiStore, a wrapped TraceKVStore will be returned with the given // tracer, otherwise, the original KVStore will be returned. +// If the store does not exist, panics. func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { store := rs.stores[key].(KVStore) diff --git a/types/store.go b/types/store.go index 8fe0321f5c..c2e57a3428 100644 --- a/types/store.go +++ b/types/store.go @@ -64,6 +64,7 @@ type MultiStore interface { //nolint CacheMultiStore() CacheMultiStore // Convenience for fetching substores. + // If the store does not exist, panics. GetStore(StoreKey) Store GetKVStore(StoreKey) KVStore From 4afd53d81b515f4c3b0ac4faf6a2590fe1fcf3e6 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 20:07:30 -0800 Subject: [PATCH 25/51] Consume block gas to tx gas limit even upon overconsumption --- baseapp/baseapp.go | 14 +++++++------- baseapp/baseapp_test.go | 6 +++--- types/gas.go | 37 ++++++++++++++++++++++++++++++++++--- types/gas_test.go | 9 +++++++++ 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 19a5d6dab7..136099d0d6 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -688,7 +688,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.initializeContext(ctx, mode) // only run the tx if there is block gas remaining - if mode == runTxModeDeliver && ctx.BlockGasMeter().PastLimit() { + if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() { result = sdk.ErrOutOfGas("no block gas left to run tx").Result() return } @@ -705,6 +705,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk } } + // consume block gas whether panic or not. + if mode == runTxModeDeliver { + ctx.BlockGasMeter().ConsumeGas( + ctx.GasMeter().GasConsumedToLimit(), "block gas meter") + } + result.GasWanted = gasWanted result.GasUsed = ctx.GasMeter().GasConsumed() }() @@ -750,12 +756,6 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result = app.runMsgs(runMsgCtx, msgs, mode) result.GasWanted = gasWanted - // consume block gas - if mode == runTxModeDeliver { - ctx.BlockGasMeter().ConsumeGas( - ctx.GasMeter().GasConsumed(), "block gas meter") - } - // only update state if all messages pass if result.IsOK() { msCache.Write() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 83cecee083..3dd9971507 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -943,16 +943,16 @@ func TestMaxBlockGasLimits(t *testing.T) { if tc.fail && (j+1) > tc.failAfterDeliver { require.Equal(t, res.Code, sdk.CodeOutOfGas, fmt.Sprintf("%d: %v, %v", i, tc, res)) require.Equal(t, res.Codespace, sdk.CodespaceRoot, fmt.Sprintf("%d: %v, %v", i, tc, res)) - require.True(t, ctx.BlockGasMeter().PastLimit()) + //require.True(t, ctx.BlockGasMeter().IsPastLimit()) NOTE: not necessarily true. + require.True(t, ctx.BlockGasMeter().IsOutOfGas()) } else { - // check gas used and wanted expBlockGasUsed := tc.gasUsedPerDeliver * uint64(j+1) require.Equal(t, expBlockGasUsed, blockGasUsed, fmt.Sprintf("%d,%d: %v, %v, %v, %v", i, j, tc, expBlockGasUsed, blockGasUsed, res)) require.True(t, res.IsOK(), fmt.Sprintf("%d,%d: %v, %v", i, j, tc, res)) - require.False(t, ctx.BlockGasMeter().PastLimit()) + require.False(t, ctx.BlockGasMeter().IsPastLimit()) } } } diff --git a/types/gas.go b/types/gas.go index d9bd7de407..be9a55b779 100644 --- a/types/gas.go +++ b/types/gas.go @@ -34,8 +34,11 @@ type ErrorGasOverflow struct { // GasMeter interface to track gas consumption type GasMeter interface { GasConsumed() Gas + GasConsumedToLimit() Gas + Limit() Gas ConsumeGas(amount Gas, descriptor string) - PastLimit() bool + IsPastLimit() bool + IsOutOfGas() bool } type basicGasMeter struct { @@ -55,6 +58,18 @@ func (g *basicGasMeter) GasConsumed() Gas { return g.consumed } +func (g *basicGasMeter) Limit() Gas { + return g.limit +} + +func (g *basicGasMeter) GasConsumedToLimit() Gas { + if g.consumed > g.limit { + return g.limit + } else { + return g.consumed + } +} + func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool @@ -69,10 +84,14 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { } } -func (g *basicGasMeter) PastLimit() bool { +func (g *basicGasMeter) IsPastLimit() bool { return g.consumed > g.limit } +func (g *basicGasMeter) IsOutOfGas() bool { + return g.consumed >= g.limit +} + type infiniteGasMeter struct { consumed Gas } @@ -88,6 +107,14 @@ func (g *infiniteGasMeter) GasConsumed() Gas { return g.consumed } +func (g *infiniteGasMeter) GasConsumedToLimit() Gas { + return g.consumed +} + +func (g *infiniteGasMeter) Limit() Gas { + return 0 +} + func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool @@ -98,7 +125,11 @@ func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { } } -func (g *infiniteGasMeter) PastLimit() bool { +func (g *infiniteGasMeter) IsPastLimit() bool { + return false +} + +func (g *infiniteGasMeter) IsOutOfGas() bool { return false } diff --git a/types/gas_test.go b/types/gas_test.go index f4452053fb..5f862dccdb 100644 --- a/types/gas_test.go +++ b/types/gas_test.go @@ -27,9 +27,18 @@ func TestGasMeter(t *testing.T) { used += usage require.NotPanics(t, func() { meter.ConsumeGas(usage, "") }, "Not exceeded limit but panicked. tc #%d, usage #%d", tcnum, unum) require.Equal(t, used, meter.GasConsumed(), "Gas consumption not match. tc #%d, usage #%d", tcnum, unum) + require.Equal(t, used, meter.GasConsumedToLimit(), "Gas consumption (to limit) not match. tc #%d, usage #%d", tcnum, unum) + require.False(t, meter.IsPastLimit(), "Not exceeded limit but got IsPastLimit() true") + if unum < len(tc.usage)-1 { + require.False(t, meter.IsOutOfGas(), "Not yet at limit but got IsOutOfGas() true") + } else { + require.True(t, meter.IsOutOfGas(), "At limit but got IsOutOfGas() false") + } } require.Panics(t, func() { meter.ConsumeGas(1, "") }, "Exceeded but not panicked. tc #%d", tcnum) + require.Equal(t, meter.GasConsumedToLimit(), meter.Limit(), "Gas consumption (to limit) not match limit") + require.Equal(t, meter.GasConsumed(), meter.Limit()+1, "Gas consumption not match limit+1") break } From bd982b1423aca2c82aee542a777856809c7d841f Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 20 Nov 2018 20:23:09 -0800 Subject: [PATCH 26/51] Merge reference/baseapp and spec/baseapp/WIP_abci_application.md --- docs/reference/baseapp.md | 61 +++++++++++++++++++++++ docs/spec/baseapp/WIP_abci_application.md | 46 ----------------- 2 files changed, 61 insertions(+), 46 deletions(-) delete mode 100644 docs/spec/baseapp/WIP_abci_application.md diff --git a/docs/reference/baseapp.md b/docs/reference/baseapp.md index e1a80e2933..ad3b567167 100644 --- a/docs/reference/baseapp.md +++ b/docs/reference/baseapp.md @@ -61,3 +61,64 @@ persisted even when the following Handler processing logic fails. It is possible that a malicious proposer may include a transaction in a block that fails the AnteHandler. In this case, all state transitions for the offending transaction are discarded. + + +## Other ABCI Messages + +Besides `CheckTx` and `DeliverTx`, BaseApp handles the following ABCI messages. + +### Info +TODO complete description + +### SetOption +TODO complete description + +### Query +TODO complete description + +### InitChain +TODO complete description + +During chain initialization InitChain runs the initialization logic directly on +the CommitMultiStore. The deliver and check states are initialized with the +ChainID. + +Note that we do not commit after InitChain, so BeginBlock for block 1 starts +from the deliver state as initialized by InitChain. + +### BeginBlock +TODO complete description + +### EndBlock +TODO complete description + +### Commit +TODO complete description + + +## Gas Management + +### Gas: InitChain + +During InitChain, the block gas meter is initialized with an infinite amount of +gas to run any genesis transactions. + +Additionally, the InitChain request message includes ConsensusParams as +declared in the genesis.json file. + +### Gas: BeginBlock + +The block gas meter is reset during BeginBlock for the deliver state. If no +maximum block gas is set within baseapp then an infinite gas meter is set, +otherwise a gas meter with `ConsensusParam.BlockSize.MaxGas` is initialized. + +### Gas: DeliverTx + +Before the transaction logic is run, the `BlockGasMeter` is first checked to +see if any gas remains. If no gas remains, then `DeliverTx` immediately returns +an error. + +After the transaction has been processed, the used gas (up to the transaction +gas limit) is deducted from the BlockGasMeter. If the remaining gas exceeds the +meter's limits, then DeliverTx returns an error and the transaction is not +committed. diff --git a/docs/spec/baseapp/WIP_abci_application.md b/docs/spec/baseapp/WIP_abci_application.md deleted file mode 100644 index 7baadccee4..0000000000 --- a/docs/spec/baseapp/WIP_abci_application.md +++ /dev/null @@ -1,46 +0,0 @@ -# ABCI application - -The `BaseApp` struct fulfills the tendermint-abci `Application` interface. - -## Info - -## SetOption - -## Query - -## CheckTx - -## InitChain -TODO pseudo code - -During chain initialization InitChain runs the initialization logic directly on -the CommitMultiStore. The deliver and check states are initialized with the -ChainID. Additionally the block gas meter is initialized with an infinite -amount of gas to run any genesis transactions. - -Note that we do not `Commit` during `InitChain` however BeginBlock for block 1 -starts from this deliverState. - - -## BeginBlock -TODO complete description & pseudo code - -The block gas meter is reset within BeginBlock for the deliver state. -If no maximum block gas is set within baseapp then an infinite -gas meter is set, otherwise a gas meter with the baseapp `maximumBlockGas` -is initialized - -## DeliverTx -TODO complete description & pseudo code - -Before transaction logic is run, the `BlockGasMeter` is first checked for -remaining gas. If no gas remains, then `DeliverTx` immediately returns an error. - -After the transaction has been processed the used gas is deducted from the -BlockGasMeter. If the remaining gas exceeds the meter's limits, then DeliverTx -returns an error and the transaction is not committed. - -## EndBlock - -## Commit - From 6fd3132e7122e9db0727fde771653a4c0aa26e0c Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 21 Nov 2018 02:02:15 -0500 Subject: [PATCH 27/51] lint fix, merge fix --- baseapp/baseapp.go | 3 +-- baseapp/baseapp_test.go | 3 +-- cmd/gaia/app/genesis.go | 2 +- types/gas.go | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 95f7fbc4b4..de7e36d72a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -259,9 +259,8 @@ func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) { if app.consensusParams == nil || app.consensusParams.BlockSize == nil { return 0 - } else { - return uint64(app.consensusParams.BlockSize.MaxGas) } + return uint64(app.consensusParams.BlockSize.MaxGas) } //______________________________________________________________________________ diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3dd9971507..0994dd3613 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -935,8 +935,7 @@ func TestMaxBlockGasLimits(t *testing.T) { for j := 0; j < tc.numDelivers; j++ { res := app.Deliver(tx) - ctx := app.getContextForAnte(runTxModeDeliver, nil) - ctx = app.initializeContext(ctx, runTxModeDeliver) + ctx := app.getState(runTxModeDeliver).ctx blockGasUsed := ctx.BlockGasMeter().GasConsumed() // check for failed transactions diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 5bc9b577bb..15010af98f 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -262,7 +262,7 @@ func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tm "account %v not in genesis.json: %+v", addr, addrMap) } if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) { - err = fmt.Errorf("insufficient fund for the delegation: %s < %s", + err = fmt.Errorf("insufficient fund for the delegation: %v < %v", acc.Coins.AmountOf(msg.Delegation.Denom), msg.Delegation.Amount) } diff --git a/types/gas.go b/types/gas.go index be9a55b779..90c9da3ec8 100644 --- a/types/gas.go +++ b/types/gas.go @@ -65,9 +65,8 @@ func (g *basicGasMeter) Limit() Gas { func (g *basicGasMeter) GasConsumedToLimit() Gas { if g.consumed > g.limit { return g.limit - } else { - return g.consumed } + return g.consumed } func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { From d227e2a29e584210aae6d0894770170ee62d671e Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 21 Nov 2018 10:54:51 +0100 Subject: [PATCH 28/51] Merge PR #2869: Gov tally endpoint * Added tally endpoint * Update querier.go * rename queryable * Address @alexanderbez comments --- PENDING.md | 1 + client/lcd/lcd_test.go | 29 +- client/lcd/swagger-ui/swagger.yaml | 49 +++- x/gov/client/cli/query.go | 35 +-- x/gov/client/rest/rest.go | 25 +- x/gov/querier.go | 253 ++++++++++-------- x/gov/querier_test.go | 33 +-- x/stake/querier/{queryable.go => querier.go} | 0 .../{queryable_test.go => querier_test.go} | 0 9 files changed, 230 insertions(+), 195 deletions(-) rename x/stake/querier/{queryable.go => querier.go} (100%) rename x/stake/querier/{queryable_test.go => querier_test.go} (100%) diff --git a/PENDING.md b/PENDING.md index 41e5c39c60..a3e505c515 100644 --- a/PENDING.md +++ b/PENDING.md @@ -74,6 +74,7 @@ IMPROVEMENTS BUG FIXES * Gaia REST API (`gaiacli advanced rest-server`) + - [gaia-lite] #2868 Added handler for governance tally endpoit * Gaia CLI (`gaiacli`) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 885e4b914f..5a6368dac4 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -678,7 +678,7 @@ func TestDeposit(t *testing.T) { func TestVote(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() // create SubmitProposal TX @@ -696,7 +696,7 @@ func TestVote(t *testing.T) { proposal := getProposal(t, port, proposalID) require.Equal(t, "Test", proposal.GetTitle()) - // create SubmitProposal TX + // deposit resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5) tests.WaitForHeight(resultTx.Height+1, port) @@ -704,13 +704,27 @@ func TestVote(t *testing.T) { proposal = getProposal(t, port, proposalID) require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus()) - // create SubmitProposal TX + // vote resultTx = doVote(t, port, seed, name, password, addr, proposalID) tests.WaitForHeight(resultTx.Height+1, port) vote := getVote(t, port, proposalID, addr) require.Equal(t, proposalID, vote.ProposalID) require.Equal(t, gov.OptionYes, vote.Option) + + tally := getTally(t, port, proposalID) + require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 as the address is not bonded") + + // create bond TX + resultTx = doDelegate(t, port, seed, name, password, addr, operAddrs[0], 60) + tests.WaitForHeight(resultTx.Height+1, port) + + // vote + resultTx = doVote(t, port, seed, name, password, addr, proposalID) + tests.WaitForHeight(resultTx.Height+1, port) + + tally = getTally(t, port, proposalID) + require.Equal(t, sdk.NewDec(60), tally.Yes, "tally should be equal to the amount delegated") } func TestUnjail(t *testing.T) { @@ -1328,6 +1342,15 @@ func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote { return votes } +func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/tally", proposalID), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var tally gov.TallyResult + err := cdc.UnmarshalJSON([]byte(body), &tally) + require.Nil(t, err) + return tally +} + func getProposalsAll(t *testing.T, port string) []gov.Proposal { res, body := Request(t, port, "GET", "/gov/proposals", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 920194ffa4..e5de2fe04e 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -1321,6 +1321,29 @@ paths: description: Invalid proposal id 500: description: Internal Server Error + /gov/proposals/{proposalId}/tally: + get: + summary: Get a proposal's tally result at the current time + description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. + produces: + - application/json + tags: + - ICS22 + parameters: + - type: string + description: proposal id + name: proposalId + required: true + in: path + responses: + 200: + description: OK + schema: + $ref: "#/definitions/TallyResult" + 400: + description: Invalid proposal id + 500: + description: Internal Server Error /gov/proposals/{proposalId}/votes: post: summary: Vote a proposal @@ -1893,16 +1916,7 @@ definitions: proposal_status: type: string tally_result: - type: object - properties: - yes: - type: string - abstain: - type: string - no: - type: string - no_with_veto: - type: string + $ref: "#/definitions/TallyResult" submit_time: type: string total_deposit: @@ -1922,6 +1936,21 @@ definitions: type: integer depositer: "$ref": "#/definitions/Address" + TallyResult: + type: object + properties: + yes: + type: string + example: "0.0000000000" + abstain: + type: string + example: "0.0000000000" + no: + type: string + example: "0.0000000000" + no_with_veto: + type: string + example: "0.0000000000" Vote: type: object properties: diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index 227f278a91..ef8840527c 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -21,10 +21,7 @@ func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) proposalID := uint64(viper.GetInt64(flagProposalID)) - params := gov.QueryProposalParams{ - ProposalID: proposalID, - } - + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -56,9 +53,11 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { strProposalStatus := viper.GetString(flagStatus) numLimit := uint64(viper.GetInt64(flagNumLimit)) - params := gov.QueryProposalsParams{ - Limit: numLimit, - } + var depositerAddr sdk.AccAddress + var voterAddr sdk.AccAddress + var proposalStatus gov.ProposalStatus + + params := gov.NewQueryProposalsParams(proposalStatus, numLimit, voterAddr, depositerAddr) if len(bechDepositerAddr) != 0 { depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) @@ -138,10 +137,7 @@ func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { return err } - params := gov.QueryVoteParams{ - Voter: voterAddr, - ProposalID: proposalID, - } + params := gov.NewQueryVoteParams(proposalID, voterAddr) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -172,9 +168,7 @@ func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) proposalID := uint64(viper.GetInt64(flagProposalID)) - params := gov.QueryVotesParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -210,10 +204,7 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { return err } - params := gov.QueryDepositParams{ - Depositer: depositerAddr, - ProposalID: proposalID, - } + params := gov.NewQueryDepositParams(proposalID, depositerAddr) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -244,9 +235,7 @@ func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) proposalID := uint64(viper.GetInt64(flagProposalID)) - params := gov.QueryDepositsParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -276,9 +265,7 @@ func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) proposalID := uint64(viper.GetInt64(flagProposalID)) - params := gov.QueryTallyParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { return err diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index c000a34d87..06416e5c84 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -42,6 +42,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositer), queryDepositHandlerFn(cdc, cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cdc, cliCtx)).Methods("GET") } @@ -244,9 +245,7 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return } - params := gov.QueryDepositsParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -412,9 +411,8 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - params := gov.QueryVotesParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) + bz, err := cdc.MarshalJSON(params) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -498,10 +496,8 @@ func queryTallyOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) strProposalID := vars[RestProposalID] if len(strProposalID) == 0 { - w.WriteHeader(http.StatusBadRequest) err := errors.New("proposalId required but not specified") - w.Write([]byte(err.Error())) - + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -510,20 +506,17 @@ func queryTallyOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - params := gov.QueryTallyParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/tally", bz) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } diff --git a/x/gov/querier.go b/x/gov/querier.go index 171ab469d5..37335b9a1e 100644 --- a/x/gov/querier.go +++ b/x/gov/querier.go @@ -25,7 +25,7 @@ const ( ) func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { switch path[0] { case QueryParams: return queryParams(ctx, path[1:], req, keeper) @@ -49,42 +49,53 @@ func NewQuerier(keeper Keeper) sdk.Querier { } } -func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { +func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { switch path[0] { case ParamDeposit: - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx)) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil case ParamVoting: - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx)) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil case ParamTallying: - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx)) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx)) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil default: - return res, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path)) + return nil, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path)) } } -// Params for query 'custom/gov/proposal' +// Params for queries: +// - 'custom/gov/proposal' +// - 'custom/gov/deposits' +// - 'custom/gov/tally' +// - 'custom/gov/votes' type QueryProposalParams struct { ProposalID uint64 } +// creates a new instance of QueryProposalParams +func NewQueryProposalParams(proposalID uint64) QueryProposalParams { + return QueryProposalParams{ + ProposalID: proposalID, + } +} + // nolint: unparam -func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { +func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { var params QueryProposalParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } proposal := keeper.GetProposal(ctx, params.ProposalID) @@ -92,9 +103,9 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper return nil, ErrUnknownProposal(DefaultCodespace, params.ProposalID) } - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, proposal) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, proposal) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil } @@ -105,18 +116,26 @@ type QueryDepositParams struct { Depositer sdk.AccAddress } +// creates a new instance of QueryDepositParams +func NewQueryDepositParams(proposalID uint64, depositer sdk.AccAddress) QueryDepositParams { + return QueryDepositParams{ + ProposalID: proposalID, + Depositer: depositer, + } +} + // nolint: unparam -func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { +func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, deposit) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, deposit) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil } @@ -127,33 +146,36 @@ type QueryVoteParams struct { Voter sdk.AccAddress } +// creates a new instance of QueryVoteParams +func NewQueryVoteParams(proposalID uint64, voter sdk.AccAddress) QueryVoteParams { + return QueryVoteParams{ + ProposalID: proposalID, + Voter: voter, + } +} + // nolint: unparam -func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { +func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { var params QueryVoteParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, vote) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, vote) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil } -// Params for query 'custom/gov/deposits' -type QueryDepositsParams struct { - ProposalID uint64 -} - // nolint: unparam -func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - var params QueryDepositsParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) +func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params QueryProposalParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } var deposits []Deposit @@ -164,80 +186,19 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper deposits = append(deposits, deposit) } - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, deposits) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, deposits) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil } -// Params for query 'custom/gov/votes' -type QueryVotesParams struct { - ProposalID uint64 -} - // nolint: unparam -func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - var params QueryVotesParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) - } - - var votes []Vote - votesIterator := keeper.GetVotes(ctx, params.ProposalID) - for ; votesIterator.Valid(); votesIterator.Next() { - vote := Vote{} - keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) - votes = append(votes, vote) - } - - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, votes) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) - } - return bz, nil -} - -// Params for query 'custom/gov/proposals' -type QueryProposalsParams struct { - Voter sdk.AccAddress - Depositer sdk.AccAddress - ProposalStatus ProposalStatus - Limit uint64 -} - -// nolint: unparam -func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - var params QueryProposalsParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) - } - - proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.Limit) - - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, proposals) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) - } - return bz, nil -} - -// Params for query 'custom/gov/tally' -type QueryTallyParams struct { - ProposalID uint64 -} - -// nolint: unparam -func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - // TODO: Dependant on #1914 - - var params QueryTallyParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err2.Error())) +func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params QueryProposalParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } proposalID := params.ProposalID @@ -254,12 +215,72 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { tallyResult = proposal.GetTallyResult() } else { + // proposal is in voting period _, tallyResult = tally(ctx, keeper, proposal) } - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, tallyResult) - if err2 != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + bz, err := codec.MarshalJSONIndent(keeper.cdc, tallyResult) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// nolint: unparam +func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params QueryProposalParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + var votes []Vote + votesIterator := keeper.GetVotes(ctx, params.ProposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + vote := Vote{} + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) + votes = append(votes, vote) + } + + bz, err := codec.MarshalJSONIndent(keeper.cdc, votes) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// Params for query 'custom/gov/proposals' +type QueryProposalsParams struct { + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + Limit uint64 +} + +// creates a new instance of QueryProposalsParams +func NewQueryProposalsParams(status ProposalStatus, limit uint64, voter, depositer sdk.AccAddress) QueryProposalsParams { + return QueryProposalsParams{ + Voter: voter, + Depositer: depositer, + ProposalStatus: status, + Limit: limit, + } +} + +// nolint: unparam +func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params QueryProposalsParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.Limit) + + bz, err := codec.MarshalJSONIndent(keeper.cdc, proposals) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } return bz, nil } diff --git a/x/gov/querier_test.go b/x/gov/querier_test.go index eee58d2ac1..c649f2b21a 100644 --- a/x/gov/querier_test.go +++ b/x/gov/querier_test.go @@ -56,9 +56,7 @@ func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier s func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) Proposal { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryProposal}, "/"), - Data: cdc.MustMarshalJSON(QueryProposalParams{ - ProposalID: proposalID, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)), } bz, err := querier(ctx, []string{QueryProposal}, query) @@ -74,12 +72,7 @@ func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositer, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryProposals}, "/"), - Data: cdc.MustMarshalJSON(QueryProposalsParams{ - Voter: voter, - Depositer: depositer, - ProposalStatus: status, - Limit: limit, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalsParams(status, limit, voter, depositer)), } bz, err := querier(ctx, []string{QueryProposal}, query) @@ -95,10 +88,7 @@ func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querie func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositer sdk.AccAddress) Deposit { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryDeposit}, "/"), - Data: cdc.MustMarshalJSON(QueryDepositParams{ - ProposalID: proposalID, - Depositer: depositer, - }), + Data: cdc.MustMarshalJSON(NewQueryDepositParams(proposalID, depositer)), } bz, err := querier(ctx, []string{QueryDeposits}, query) @@ -114,9 +104,7 @@ func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Deposit { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryDeposits}, "/"), - Data: cdc.MustMarshalJSON(QueryDepositsParams{ - ProposalID: proposalID, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)), } bz, err := querier(ctx, []string{QueryDeposits}, query) @@ -132,10 +120,7 @@ func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) Vote { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"), - Data: cdc.MustMarshalJSON(QueryVoteParams{ - ProposalID: proposalID, - Voter: voter, - }), + Data: cdc.MustMarshalJSON(NewQueryVoteParams(proposalID, voter)), } bz, err := querier(ctx, []string{QueryVote}, query) @@ -151,9 +136,7 @@ func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Vote { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"), - Data: cdc.MustMarshalJSON(QueryVotesParams{ - ProposalID: proposalID, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)), } bz, err := querier(ctx, []string{QueryVotes}, query) @@ -169,9 +152,7 @@ func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sd func getQueriedTally(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) TallyResult { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryTally}, "/"), - Data: cdc.MustMarshalJSON(QueryTallyParams{ - ProposalID: proposalID, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)), } bz, err := querier(ctx, []string{QueryTally}, query) diff --git a/x/stake/querier/queryable.go b/x/stake/querier/querier.go similarity index 100% rename from x/stake/querier/queryable.go rename to x/stake/querier/querier.go diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/querier_test.go similarity index 100% rename from x/stake/querier/queryable_test.go rename to x/stake/querier/querier_test.go From 1ea0e4c457fc105b48131a60e3d28c6c1bb32cc0 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 21 Nov 2018 05:16:56 -0500 Subject: [PATCH 29/51] Merge PR #2863: Transaction ValidateBasic * Add ValidateBasic to Tx interface * Update BaseApp unit tests * Add missing return in ValidateBasic * Update ValidateBasic to use IsNotNegative * Add pending log entry * Add unit test TestTxValidateBasic * Fix broken lint regression * Add sig count check to validation * Add test case to TestTxValidateBasic --- PENDING.md | 5 ++- baseapp/baseapp_test.go | 3 +- types/tx_msg.go | 7 ++-- x/auth/ante.go | 56 +++--------------------------- x/auth/stdtx.go | 56 ++++++++++++++++++++++++++++-- x/auth/stdtx_test.go | 76 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 57 deletions(-) diff --git a/PENDING.md b/PENDING.md index a3e505c515..b6273d186a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -66,7 +66,10 @@ IMPROVEMENTS - [types] #2776 Improve safety of `Coin` and `Coins` types. Various functions and methods will panic when a negative amount is discovered. - #2815 Gas unit fields changed from `int64` to `uint64`. - + - #2821 Codespaces are now strings + - #2779 Introduce `ValidateBasic` to the `Tx` interface and call it in the ante + handler. + * Tendermint - #2796 Update to go-amino 0.14.1 diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index ba3830e920..ab6916f82a 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -298,7 +298,8 @@ func (tx *txTest) setFailOnHandler(fail bool) { } // Implements Tx -func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs } +func (tx txTest) GetMsgs() []sdk.Msg { return tx.Msgs } +func (tx txTest) ValidateBasic() sdk.Error { return nil } const ( routeMsgCounter = "msgCounter" diff --git a/types/tx_msg.go b/types/tx_msg.go index 7882b25d31..791ab9c353 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -32,9 +32,12 @@ type Msg interface { // Transactions objects must fulfill the Tx type Tx interface { - - // Gets the Msg. + // Gets the all the transaction's messages. GetMsgs() []Msg + + // ValidateBasic does a simple and lightweight validation check that doesn't + // require access to any other information. + ValidateBasic() Error } //__________________________________________________________ diff --git a/x/auth/ante.go b/x/auth/ante.go index 9a7a15e3e9..685d60949f 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -8,7 +8,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/crypto/secp256k1" ) @@ -67,28 +66,18 @@ func NewAnteHandler(am AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler { } }() - err := validateBasic(stdTx) - if err != nil { + if err := tx.ValidateBasic(); err != nil { return newCtx, err.Result(), true } // charge gas for the memo newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo") - // stdSigs contains the sequence number, account number, and signatures - stdSigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice. + // stdSigs contains the sequence number, account number, and signatures. + // When simulating, this would just be a 0-length slice. + stdSigs := stdTx.GetSignatures() signerAddrs := stdTx.GetSigners() - sigCount := 0 - for i := 0; i < len(stdSigs); i++ { - sigCount += countSubKeys(stdSigs[i].PubKey) - if sigCount > txSigLimit { - return newCtx, sdk.ErrTooManySignatures(fmt.Sprintf( - "signatures: %d, limit: %d", sigCount, txSigLimit), - ).Result(), true - } - } - // create the list of all sign bytes signBytesList := getSignBytesList(newCtx.ChainID(), stdTx, stdSigs) signerAccs, res := getSignerAccs(newCtx, am, signerAddrs) @@ -129,29 +118,6 @@ func NewAnteHandler(am AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler { } } -// Validate the transaction based on things that don't depend on the context -func validateBasic(tx StdTx) (err sdk.Error) { - // Assert that there are signatures. - sigs := tx.GetSignatures() - if len(sigs) == 0 { - return sdk.ErrUnauthorized("no signers") - } - - // Assert that number of signatures is correct. - var signerAddrs = tx.GetSigners() - if len(sigs) != len(signerAddrs) { - return sdk.ErrUnauthorized("wrong number of signers") - } - - memo := tx.GetMemo() - if len(memo) > maxMemoCharacters { - return sdk.ErrMemoTooLarge( - fmt.Sprintf("maximum number of characters is %d but received %d characters", - maxMemoCharacters, len(memo))) - } - return nil -} - func getSignerAccs(ctx sdk.Context, am AccountKeeper, addrs []sdk.AccAddress) (accs []Account, res sdk.Result) { accs = make([]Account, len(addrs)) for i := 0; i < len(accs); i++ { @@ -310,7 +276,7 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { if stdTx.Fee.Gas <= 0 { return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied: %d", stdTx.Fee.Gas)).Result() } - requiredFees := adjustFeesByGas(ctx.MinimumFees(), uint64(stdTx.Fee.Gas)) + requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas) // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) { @@ -340,15 +306,3 @@ func getSignBytesList(chainID string, stdTx StdTx, stdSigs []StdSignature) (sign } return } - -func countSubKeys(pub crypto.PubKey) int { - v, ok := pub.(*multisig.PubKeyMultisigThreshold) - if !ok { - return 1 - } - nkeys := 0 - for _, subkey := range v.PubKeys { - nkeys += countSubKeys(subkey) - } - return nkeys -} diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index ba1c65b845..e8b9461fd9 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -2,10 +2,12 @@ package auth import ( "encoding/json" + "fmt" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/multisig" ) var _ sdk.Tx = (*StdTx)(nil) @@ -28,11 +30,61 @@ func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string) StdT } } -//nolint +// GetMsgs returns the all the transaction's messages. func (tx StdTx) GetMsgs() []sdk.Msg { return tx.Msgs } +// ValidateBasic does a simple and lightweight validation check that doesn't +// require access to any other information. +func (tx StdTx) ValidateBasic() sdk.Error { + stdSigs := tx.GetSignatures() + + if !tx.Fee.Amount.IsNotNegative() { + return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee %s amount provided", tx.Fee.Amount)) + } + if len(stdSigs) == 0 { + return sdk.ErrUnauthorized("no signers") + } + if len(stdSigs) != len(tx.GetSigners()) { + return sdk.ErrUnauthorized("wrong number of signers") + } + if len(tx.GetMemo()) > maxMemoCharacters { + return sdk.ErrMemoTooLarge( + fmt.Sprintf( + "maximum number of characters is %d but received %d characters", + maxMemoCharacters, len(tx.GetMemo()), + ), + ) + } + + sigCount := 0 + for i := 0; i < len(stdSigs); i++ { + sigCount += countSubKeys(stdSigs[i].PubKey) + if sigCount > txSigLimit { + return sdk.ErrTooManySignatures( + fmt.Sprintf("signatures: %d, limit: %d", sigCount, txSigLimit), + ) + } + } + + return nil +} + +func countSubKeys(pub crypto.PubKey) int { + v, ok := pub.(*multisig.PubKeyMultisigThreshold) + if !ok { + return 1 + } + + numKeys := 0 + for _, subkey := range v.PubKeys { + numKeys += countSubKeys(subkey) + } + + return numKeys +} + // GetSigners returns the addresses that must sign the transaction. -// Addresses are returned in a determistic order. +// Addresses are returned in a deterministic order. // They are accumulated from the GetSigners method for each Msg // in the order they appear in tx.GetMsgs(). // Duplicate addresses will be omitted. diff --git a/x/auth/stdtx_test.go b/x/auth/stdtx_test.go index 26bd792455..a3267ac192 100644 --- a/x/auth/stdtx_test.go +++ b/x/auth/stdtx_test.go @@ -2,11 +2,15 @@ package auth import ( "fmt" + "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" ) var ( @@ -51,3 +55,75 @@ func TestStdSignBytes(t *testing.T) { require.Equal(t, tc.want, got, "Got unexpected result on test case i: %d", i) } } + +func TestTxValidateBasic(t *testing.T) { + ctx := sdk.NewContext(nil, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger()) + + // keys and addresses + priv1, addr1 := privAndAddr() + priv2, addr2 := privAndAddr() + priv3, addr3 := privAndAddr() + priv4, addr4 := privAndAddr() + priv5, addr5 := privAndAddr() + priv6, addr6 := privAndAddr() + priv7, addr7 := privAndAddr() + priv8, addr8 := privAndAddr() + + // msg and signatures + msg1 := newTestMsg(addr1, addr2) + fee := newStdFee() + + msgs := []sdk.Msg{msg1} + + // require to fail validation upon invalid fee + badFee := newStdFee() + badFee.Amount[0].Amount = sdk.NewInt(-5) + tx := newTestTx(ctx, nil, nil, nil, nil, badFee) + + err := tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeInsufficientFee, err.Result().Code) + + // require to fail validation when no signatures exist + privs, accNums, seqs := []crypto.PrivKey{}, []int64{}, []int64{} + tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) + + err = tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeUnauthorized, err.Result().Code) + + // require to fail validation when signatures do not match expected signers + privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0, 1}, []int64{0, 0} + tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) + + err = tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeUnauthorized, err.Result().Code) + + // require to fail validation when memo is too large + badMemo := strings.Repeat("bad memo", 50) + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0} + tx = newTestTxWithMemo(ctx, msgs, privs, accNums, seqs, fee, badMemo) + + err = tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeMemoTooLarge, err.Result().Code) + + // require to fail validation when there are too many signatures + privs = []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8} + accNums, seqs = []int64{0, 0, 0, 0, 0, 0, 0, 0}, []int64{0, 0, 0, 0, 0, 0, 0, 0} + badMsg := newTestMsg(addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8) + badMsgs := []sdk.Msg{badMsg} + tx = newTestTx(ctx, badMsgs, privs, accNums, seqs, fee) + + err = tx.ValidateBasic() + require.Error(t, err) + require.Equal(t, sdk.CodeTooManySignatures, err.Result().Code) + + // require to pass when above criteria are matched + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0} + tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) + + err = tx.ValidateBasic() + require.NoError(t, err) +} From 3e68e440634ec5e3faee53776961cd059e8dba34 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 21 Nov 2018 23:44:13 +0000 Subject: [PATCH 30/51] Merge PR #2874: gaiad gentx subcommands refactoring * gaiad gentx subcommands refactoring - Replace STDIN/STDOUT redirection in `gaiad gentx` with subcommands command line options to redirect streams to file since viper does not handle redirection well. - Use `BuildCreateValidatorMsg` to build a `MsgCreateValidator` rather than redirecting to `gaiacli tx stake create-validator`. - `PrintUnsignedStdTx` now takes an `io.Writer` object. - Mark `--pubkey`, `--amount` and `--moniker` as required flags instead of validating them manually. - Use stake.NewDescription() to make a new Description - ref #2835 * Refresh PENDING.md --- PENDING.md | 1 + client/utils/utils.go | 5 +- cmd/gaia/init/gentx.go | 36 ++++++---- x/auth/client/cli/sign.go | 24 +++++-- x/bank/client/cli/sendtx.go | 3 +- x/gov/client/cli/tx.go | 7 +- x/ibc/client/cli/ibctx.go | 3 +- x/slashing/client/cli/tx.go | 3 +- x/stake/client/cli/tx.go | 140 ++++++++++++++++++------------------ 9 files changed, 128 insertions(+), 94 deletions(-) diff --git a/PENDING.md b/PENDING.md index b6273d186a..f462a7e88d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -10,6 +10,7 @@ BREAKING CHANGES * [cli] [\#2786](https://github.com/cosmos/cosmos-sdk/pull/2786) Fix redelegation command flow * [cli] [\#2829](https://github.com/cosmos/cosmos-sdk/pull/2829) add-genesis-account command now validates state when adding accounts * [cli] [\#2804](https://github.com/cosmos/cosmos-sdk/issues/2804) Check whether key exists before passing it on to `tx create-validator`. + * [cli] [\#2874](https://github.com/cosmos/cosmos-sdk/pull/2874) `gaiacli tx sign` takes an optional `--output-document` flag to support output redirection. * Gaia diff --git a/client/utils/utils.go b/client/utils/utils.go index 46bb9799c1..08b8d87f27 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "bytes" "fmt" + "io" "os" "github.com/cosmos/cosmos-sdk/client/context" @@ -88,7 +89,7 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc * // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. // Don't perform online validation or lookups if offline is true. -func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) { +func PrintUnsignedStdTx(w io.Writer, txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) { var stdTx auth.StdTx if offline { stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs) @@ -100,7 +101,7 @@ func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg } json, err := txBldr.Codec.MarshalJSON(stdTx) if err == nil { - fmt.Printf("%s\n", json) + fmt.Fprintf(w, "%s\n", json) } return } diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 096097e188..91dfbd2e16 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -6,17 +6,21 @@ import ( "os" "path/filepath" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/spf13/cobra" - "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" tmcli "github.com/tendermint/tendermint/libs/cli" @@ -80,7 +84,13 @@ following delegation and commission default parameters: } // Run gaiad tx create-validator prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) - createValidatorCmd := cli.GetCmdCreateValidator(cdc) + cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg( + context.NewCLIContext().WithCodec(cdc), + authtxb.NewTxBuilderFromCLI().WithCodec(cdc), + ) + if err != nil { + return err + } w, err := ioutil.TempFile("", "gentx") if err != nil { @@ -88,18 +98,19 @@ following delegation and commission default parameters: } unsignedGenTxFilename := w.Name() defer os.Remove(unsignedGenTxFilename) - os.Stdout = w - if err = createValidatorCmd.RunE(nil, args); err != nil { + + if err := utils.PrintUnsignedStdTx(w, txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil { return err } - w.Close() prepareFlagsForTxSign() signCmd := authcmd.GetSignCommand(cdc) - if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { + + outputDocument, err := makeOutputFilepath(config.RootDir, nodeID) + if err != nil { return err } - os.Stdout = w + viper.Set("output-document", outputDocument) return signCmd.RunE(nil, []string{unsignedGenTxFilename}) }, } @@ -145,11 +156,10 @@ func prepareFlagsForTxSign() { viper.Set("offline", true) } -func prepareOutputFile(rootDir, nodeID string) (w *os.File, err error) { +func makeOutputFilepath(rootDir, nodeID string) (string, error) { writePath := filepath.Join(rootDir, "config", "gentx") - if err = common.EnsureDir(writePath, 0700); err != nil { - return + if err := common.EnsureDir(writePath, 0700); err != nil { + return "", err } - filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)) - return os.Create(filename) + return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil } diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 9075308e1d..6fc57ac166 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -3,9 +3,7 @@ package cli import ( "fmt" "io/ioutil" - - "github.com/pkg/errors" - "github.com/spf13/viper" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -13,7 +11,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/tendermint/go-amino" ) @@ -22,6 +22,7 @@ const ( flagValidateSigs = "validate-signatures" flagOffline = "offline" flagSigOnly = "signature-only" + flagOutfile = "output-document" ) // GetSignCommand returns the sign command @@ -52,6 +53,8 @@ recommended to set such parameters manually.`, cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+ "those who have already signed it, and make sure that signatures are in the correct order.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") + cmd.Flags().String(flagOutfile, "", + "The document will be written to the given file instead of STDOUT") // Add the flags here and return the command return client.PostCommands(cmd)[0] @@ -107,7 +110,20 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error if err != nil { return err } - fmt.Printf("%s\n", json) + + if viper.GetString(flagOutfile) == "" { + fmt.Printf("%s\n", json) + return + } + + fp, err := os.OpenFile( + viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644, + ) + if err != nil { + return err + } + defer fp.Close() + fmt.Fprintf(fp, "%s\n", json) return } } diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 29a101cf73..1a7c444afb 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" bankClient "github.com/cosmos/cosmos-sdk/x/bank/client" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -66,7 +67,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { // build and sign the transaction, then broadcast to Tendermint msg := bankClient.CreateMsg(from, to, coins) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index e804863d11..461732cf59 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -103,7 +104,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to Tendermint @@ -183,7 +184,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to a Tendermint @@ -229,7 +230,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]", diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go index e8b107d9af..afe3824fcc 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -2,6 +2,7 @@ package cli import ( "encoding/hex" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -41,7 +42,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { return err } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 7124b3544e..513710ce42 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/slashing" + "os" "github.com/spf13/cobra" ) @@ -30,7 +31,7 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr)) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 09b235abb1..f730e017f2 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -26,76 +27,13 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - amounstStr := viper.GetString(FlagAmount) - if amounstStr == "" { - return fmt.Errorf("Must specify amount to stake using --amount") - } - amount, err := sdk.ParseCoin(amounstStr) + cliCtx, txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { return err } - valAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - - pkStr := viper.GetString(FlagPubKey) - if len(pkStr) == 0 { - return fmt.Errorf("must use --pubkey flag") - } - - pk, err := sdk.GetConsPubKeyBech32(pkStr) - if err != nil { - return err - } - - if viper.GetString(FlagMoniker) == "" { - return fmt.Errorf("please enter a moniker for the validator using --moniker") - } - - description := stake.Description{ - Moniker: viper.GetString(FlagMoniker), - Identity: viper.GetString(FlagIdentity), - Website: viper.GetString(FlagWebsite), - Details: viper.GetString(FlagDetails), - } - - // get the initial validator commission parameters - rateStr := viper.GetString(FlagCommissionRate) - maxRateStr := viper.GetString(FlagCommissionMaxRate) - maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) - commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr) - if err != nil { - return err - } - - var msg sdk.Msg - if viper.GetString(FlagAddressDelegator) != "" { - delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) - if err != nil { - return err - } - - msg = stake.NewMsgCreateValidatorOnBehalfOf( - delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, - ) - } else { - msg = stake.NewMsgCreateValidator( - sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, - ) - } - - if viper.GetBool(FlagGenesisFormat) { - ip := viper.GetString(FlagIP) - nodeID := viper.GetString(FlagNodeID) - if nodeID != "" && ip != "" { - txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) - } - } - if viper.GetBool(FlagGenesisFormat) || cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, true) } // build and sign the transaction, then broadcast to Tendermint @@ -112,6 +50,9 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(FlagIP, "", fmt.Sprintf("Node's public IP. It takes effect only when used in combination with --%s", FlagGenesisFormat)) cmd.Flags().String(FlagNodeID, "", "Node's ID") cmd.MarkFlagRequired(client.FlagFrom) + cmd.MarkFlagRequired(FlagAmount) + cmd.MarkFlagRequired(FlagPubKey) + cmd.MarkFlagRequired(FlagMoniker) return cmd } @@ -154,7 +95,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint @@ -197,7 +138,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgDelegate(delAddr, valAddr, amount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -252,7 +193,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, sharesAmount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -300,7 +241,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgBeginUnbonding(delAddr, valAddr, sharesAmount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -312,3 +253,64 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } + +// BuildCreateValidatorMsg makes a new MsgCreateValidator. +func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder) (context.CLIContext, authtxb.TxBuilder, sdk.Msg, error) { + amounstStr := viper.GetString(FlagAmount) + amount, err := sdk.ParseCoin(amounstStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + valAddr, err := cliCtx.GetFromAddress() + if err != nil { + return cliCtx, txBldr, nil, err + } + + pkStr := viper.GetString(FlagPubKey) + pk, err := sdk.GetConsPubKeyBech32(pkStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + description := stake.NewDescription( + viper.GetString(FlagMoniker), + viper.GetString(FlagIdentity), + viper.GetString(FlagWebsite), + viper.GetString(FlagDetails), + ) + + // get the initial validator commission parameters + rateStr := viper.GetString(FlagCommissionRate) + maxRateStr := viper.GetString(FlagCommissionMaxRate) + maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) + commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + var msg sdk.Msg + if viper.GetString(FlagAddressDelegator) != "" { + delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) + if err != nil { + return cliCtx, txBldr, nil, err + } + + msg = stake.NewMsgCreateValidatorOnBehalfOf( + delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, + ) + } else { + msg = stake.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, + ) + } + + if viper.GetBool(FlagGenesisFormat) { + ip := viper.GetString(FlagIP) + nodeID := viper.GetString(FlagNodeID) + if nodeID != "" && ip != "" { + txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + return cliCtx, txBldr, msg, nil +} From f09fa33cfda1ac016ff0a40643b296c4807336ee Mon Sep 17 00:00:00 2001 From: John McDowall Date: Wed, 21 Nov 2018 15:53:33 -0800 Subject: [PATCH 31/51] Merge PR #2802: Correct the usage of misspelling of English word 'depositor'. Throughout the codebase the misspelling 'depositer' of the English word 'depositor' has been used. This commit applies a global search and replace to correct the misspelling. --- PENDING.md | 1 + client/lcd/lcd_test.go | 20 +++++++++--------- client/lcd/swagger-ui/swagger.yaml | 18 ++++++++-------- cmd/gaia/cli_test/cli_test.go | 4 ++-- docs/gaia/gaiacli.md | 19 ++++++++++------- docs/spec/governance/overview.md | 2 +- docs/spec/governance/state.md | 8 +++---- x/gov/client/cli/query.go | 20 +++++++++--------- x/gov/client/cli/tx.go | 6 +++--- x/gov/client/rest/rest.go | 28 ++++++++++++------------ x/gov/depositsvotes.go | 4 ++-- x/gov/genesis.go | 2 +- x/gov/handler.go | 4 ++-- x/gov/keeper.go | 34 +++++++++++++++--------------- x/gov/keeper_keys.go | 4 ++-- x/gov/keeper_test.go | 10 ++++----- x/gov/msgs.go | 14 ++++++------ x/gov/msgs_test.go | 4 ++-- x/gov/querier.go | 16 +++++++------- x/gov/querier_test.go | 8 +++---- x/gov/tags/tags.go | 2 +- 21 files changed, 116 insertions(+), 112 deletions(-) diff --git a/PENDING.md b/PENDING.md index f462a7e88d..47c6ab60e5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,6 +18,7 @@ BREAKING CHANGES * [\#2752](https://github.com/cosmos/cosmos-sdk/pull/2752) Don't hardcode bondable denom. * [\#2019](https://github.com/cosmos/cosmos-sdk/issues/2019) Cap total number of signatures. Current per-transaction limit is 7, and if that is exceeded transaction is rejected. * [\#2801](https://github.com/cosmos/cosmos-sdk/pull/2801) Remove AppInit structure. + * [\#2798](https://github.com/cosmos/cosmos-sdk/issues/2798) Governance API has miss-spelled English word in JSON response ('depositer' -> 'depositor') * Tendermint diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 5a6368dac4..001cf3d929 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -830,11 +830,11 @@ func TestProposalsQuery(t *testing.T) { require.Equal(t, proposalID3, (proposals[2]).GetProposalID()) // Test query deposited by addr1 - proposals = getProposalsFilterDepositer(t, port, addrs[0]) + proposals = getProposalsFilterDepositor(t, port, addrs[0]) require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) // Test query deposited by addr2 - proposals = getProposalsFilterDepositer(t, port, addrs[1]) + proposals = getProposalsFilterDepositor(t, port, addrs[1]) require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) @@ -848,7 +848,7 @@ func TestProposalsQuery(t *testing.T) { require.Equal(t, proposalID3, (proposals[0]).GetProposalID()) // Test query voted and deposited by addr1 - proposals = getProposalsFilterVoterDepositer(t, port, addrs[0], addrs[0]) + proposals = getProposalsFilterVoterDepositor(t, port, addrs[0], addrs[0]) require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) // Test query votes on Proposal 2 @@ -1315,8 +1315,8 @@ func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit { return deposits } -func getDeposit(t *testing.T, port string, proposalID uint64, depositerAddr sdk.AccAddress) gov.Deposit { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositerAddr), nil) +func getDeposit(t *testing.T, port string, proposalID uint64, depositorAddr sdk.AccAddress) gov.Deposit { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var deposit gov.Deposit err := cdc.UnmarshalJSON([]byte(body), &deposit) @@ -1361,8 +1361,8 @@ func getProposalsAll(t *testing.T, port string) []gov.Proposal { return proposals } -func getProposalsFilterDepositer(t *testing.T, port string, depositerAddr sdk.AccAddress) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s", depositerAddr), nil) +func getProposalsFilterDepositor(t *testing.T, port string, depositorAddr sdk.AccAddress) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s", depositorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var proposals []gov.Proposal @@ -1381,8 +1381,8 @@ func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress return proposals } -func getProposalsFilterVoterDepositer(t *testing.T, port string, voterAddr, depositerAddr sdk.AccAddress) []gov.Proposal { - res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s&voter=%s", depositerAddr, voterAddr), nil) +func getProposalsFilterVoterDepositor(t *testing.T, port string, voterAddr, depositorAddr sdk.AccAddress) []gov.Proposal { + res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s&voter=%s", depositorAddr, voterAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var proposals []gov.Proposal @@ -1444,7 +1444,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk // deposit on proposal jsonStr := []byte(fmt.Sprintf(`{ - "depositer": "%s", + "depositor": "%s", "amount": [{ "denom": "%s", "amount": "%d" }], "base_req": { "name": "%s", diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index e5de2fe04e..33dac880e3 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -1226,8 +1226,8 @@ paths: required: false type: string - in: query - name: depositer - description: depositer address + name: depositor + description: depositor address required: false type: string - in: query @@ -1281,7 +1281,7 @@ paths: properties: base_req: "$ref": "#/definitions/BaseReq" - depositer: + depositor: "$ref": "#/definitions/Address" amount: type: array @@ -1441,10 +1441,10 @@ paths: description: Invalid proposal id 500: description: Internal Server Error - /gov/proposals/{proposalId}/deposits/{depositer}: + /gov/proposals/{proposalId}/deposits/{depositor}: get: summary: Query deposit - description: Query deposit by proposalId and depositer address + description: Query deposit by proposalId and depositor address produces: - application/json tags: @@ -1456,8 +1456,8 @@ paths: required: true in: path - type: string - description: Bech32 depositer address - name: depositer + description: Bech32 depositor address + name: depositor required: true in: path responses: @@ -1466,7 +1466,7 @@ paths: schema: $ref: "#/definitions/Deposit" 400: - description: Invalid proposal id or depositer address + description: Invalid proposal id or depositor address 404: description: Found no deposit 500: @@ -1934,7 +1934,7 @@ definitions: "$ref": "#/definitions/Coin" proposal_id: type: integer - depositer: + depositor: "$ref": "#/definitions/Address" TallyResult: type: object diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 26e6fcaa36..8ad32870f4 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -365,7 +365,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, " 1 - Test", proposalsQuery) deposit := executeGetDeposit(t, - fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositer=%s --output=json %v", + fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositor=%s --output=json %v", fooAddr, flags)) require.Equal(t, int64(5), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) @@ -394,7 +394,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, int64(15), deposits[0].Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) deposit = executeGetDeposit(t, - fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositer=%s --output=json %v", + fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositor=%s --output=json %v", fooAddr, flags)) require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 5749e1ca77..38eaa0b8b1 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -24,10 +24,12 @@ There are three types of key representations that are used: - Derived from account keys generated by `gaiacli keys add` - Used to receive funds - e.g. `cosmos15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc` + * `cosmosvaloper` - * Used to associate a validator to it's operator - * Used to invoke staking commands - * e.g. `cosmosvaloper1carzvgq3e6y3z5kz5y6gxp3wpy3qdrv928vyah` + - Used to associate a validator to it's operator + - Used to invoke staking commands + - e.g. `cosmosvaloper1carzvgq3e6y3z5kz5y6gxp3wpy3qdrv928vyah` + - `cosmospub` - Derived from account keys generated by `gaiacli keys add` - e.g. `cosmospub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm` @@ -72,7 +74,7 @@ View the validator pubkey for your node by typing: gaiad tendermint show-validator ``` -Note that this is the Tendermint signing key, *not* the operator key you will use in delegation transactions. +Note that this is the Tendermint signing key, _not_ the operator key you will use in delegation transactions. ::: danger Warning We strongly recommend _NOT_ using the same passphrase for multiple keys. The Tendermint team and the Interchain Foundation will not be responsible for the loss of funds. @@ -211,7 +213,7 @@ gaiacli query stake validator #### Bond Tokens -On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond tokens to a testnet validator (*i.e.* delegate): +On the testnet, we delegate `steak` instead of `atom`. Here's how you can bond tokens to a testnet validator (_i.e._ delegate): ```bash gaiacli tx stake delegate \ @@ -304,7 +306,7 @@ gaiacli tx stake redelegate \ --chain-id= ``` -Here you can also redelegate a specific `shares-amount` or a `shares-fraction` with the corresponding flags. +Here you can also redelegate a specific `shares-amount` or a `shares-fraction` with the corresponding flags. The redelegation will be automatically completed when the unbonding period has passed. @@ -367,6 +369,7 @@ With the `pool` command you will get the values for: ##### Query Delegations To Validator You can also query all of the delegations to a particular validator: + ```bash gaiacli query delegations-to ``` @@ -418,7 +421,7 @@ Or query all available proposals: gaiacli query gov proposals ``` -You can also query proposals filtered by `voter` or `depositer` by using the corresponding flags. +You can also query proposals filtered by `voter` or `depositor` by using the corresponding flags. #### Increase deposit @@ -447,7 +450,7 @@ You can also query a deposit submitted by a specific address: ```bash gaiacli query gov deposit \ --proposal-id= \ - --depositer= + --depositor= ``` #### Vote on a proposal diff --git a/docs/spec/governance/overview.md b/docs/spec/governance/overview.md index 53f7d7b081..a79b951904 100644 --- a/docs/spec/governance/overview.md +++ b/docs/spec/governance/overview.md @@ -42,7 +42,7 @@ If proposal's deposit does not reach `MinDeposit` before `MaxDepositPeriod`, pro There is one instance where Atom holders that deposits can be refunded: * If the proposal is accepted. -Then, deposits will automatically be refunded to their respective depositer. +Then, deposits will automatically be refunded to their respective depositor. ### Proposal types diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 3858ba06b6..6c2799fd04 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -68,8 +68,8 @@ const ( ```go type Deposit struct { - Amount sdk.Coins // Amount of coins deposited by depositer - Depositer crypto.address // Address of depositer + Amount sdk.Coins // Amount of coins deposited by depositor + Depositor crypto.address // Address of depositor } ``` @@ -185,8 +185,8 @@ And the pseudocode for the `ProposalProcessingQueue`: // proposal was accepted at the end of the voting period // refund deposits (non-voters already punished) proposal.CurrentStatus = ProposalStatusAccepted - for each (amount, depositer) in proposal.Deposits - depositer.AtomBalance += amount + for each (amount, depositor) in proposal.Deposits + depositor.AtomBalance += amount else // proposal was rejected diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index ef8840527c..b301c3b1b5 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -48,23 +48,23 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { Use: "proposals", Short: "Query proposals with optional filters", RunE: func(cmd *cobra.Command, args []string) error { - bechDepositerAddr := viper.GetString(flagDepositer) + bechDepositorAddr := viper.GetString(flagDepositor) bechVoterAddr := viper.GetString(flagVoter) strProposalStatus := viper.GetString(flagStatus) numLimit := uint64(viper.GetInt64(flagNumLimit)) - var depositerAddr sdk.AccAddress + var depositorAddr sdk.AccAddress var voterAddr sdk.AccAddress var proposalStatus gov.ProposalStatus - params := gov.NewQueryProposalsParams(proposalStatus, numLimit, voterAddr, depositerAddr) + params := gov.NewQueryProposalsParams(proposalStatus, numLimit, voterAddr, depositorAddr) - if len(bechDepositerAddr) != 0 { - depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) + if len(bechDepositorAddr) != 0 { + depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { return err } - params.Depositer = depositerAddr + params.Depositor = depositorAddr } if len(bechVoterAddr) != 0 { @@ -115,7 +115,7 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { } cmd.Flags().String(flagNumLimit, "", "(optional) limit to latest [number] proposals. Defaults to all proposals") - cmd.Flags().String(flagDepositer, "", "(optional) filter by proposals deposited on by depositer") + cmd.Flags().String(flagDepositor, "", "(optional) filter by proposals deposited on by depositor") cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted") cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected") @@ -199,12 +199,12 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) proposalID := uint64(viper.GetInt64(flagProposalID)) - depositerAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositer)) + depositorAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositor)) if err != nil { return err } - params := gov.NewQueryDepositParams(proposalID, depositerAddr) + params := gov.NewQueryDepositParams(proposalID, depositorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -221,7 +221,7 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { } cmd.Flags().String(flagProposalID, "", "proposalID of proposal deposited on") - cmd.Flags().String(flagDepositer, "", "bech32 depositer address") + cmd.Flags().String(flagDepositor, "", "bech32 depositor address") return cmd } diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 461732cf59..000aef68db 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -28,7 +28,7 @@ const ( flagDeposit = "deposit" flagVoter = "voter" flagOption = "option" - flagDepositer = "depositer" + flagDepositor = "depositor" flagStatus = "status" flagNumLimit = "limit" flagProposal = "proposal" @@ -165,7 +165,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - depositerAddr, err := cliCtx.GetFromAddress() + depositorAddr, err := cliCtx.GetFromAddress() if err != nil { return err } @@ -177,7 +177,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { return err } - msg := gov.NewMsgDeposit(depositerAddr, proposalID, amount) + msg := gov.NewMsgDeposit(depositorAddr, proposalID, amount) err = msg.ValidateBasic() if err != nil { return err diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 06416e5c84..72abdb2148 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -20,7 +20,7 @@ import ( const ( RestParamsType = "type" RestProposalID = "proposal-id" - RestDepositer = "depositer" + RestDepositor = "depositor" RestVoter = "voter" RestProposalStatus = "status" RestNumLimit = "limit" @@ -41,7 +41,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cdc, cliCtx)).Methods("GET") - r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositer), queryDepositHandlerFn(cdc, cliCtx)).Methods("GET") + r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cdc, cliCtx)).Methods("GET") @@ -58,7 +58,7 @@ type postProposalReq struct { type depositReq struct { BaseReq utils.BaseReq `json:"base_req"` - Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer + Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } @@ -128,7 +128,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF } // create the message - msg := gov.NewMsgDeposit(req.Depositer, proposalID, req.Amount) + msg := gov.NewMsgDeposit(req.Depositor, proposalID, req.Amount) err = msg.ValidateBasic() if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -267,7 +267,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strProposalID := vars[RestProposalID] - bechDepositerAddr := vars[RestDepositer] + bechDepositorAddr := vars[RestDepositor] if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") @@ -280,13 +280,13 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } - if len(bechDepositerAddr) == 0 { - err := errors.New("depositer address required but not specified") + if len(bechDepositorAddr) == 0 { + err := errors.New("depositor address required but not specified") utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) + depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -294,7 +294,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han params := gov.QueryDepositParams{ ProposalID: proposalID, - Depositer: depositerAddr, + Depositor: depositorAddr, } bz, err := cdc.MarshalJSON(params) @@ -318,7 +318,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) + err = errors.Errorf("depositor [%s] did not deposit on proposalID [%d]", bechDepositorAddr, proposalID) utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } @@ -433,7 +433,7 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { bechVoterAddr := r.URL.Query().Get(RestVoter) - bechDepositerAddr := r.URL.Query().Get(RestDepositer) + bechDepositorAddr := r.URL.Query().Get(RestDepositor) strProposalStatus := r.URL.Query().Get(RestProposalStatus) strNumLimit := r.URL.Query().Get(RestNumLimit) @@ -448,13 +448,13 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) params.Voter = voterAddr } - if len(bechDepositerAddr) != 0 { - depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) + if len(bechDepositorAddr) != 0 { + depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - params.Depositer = depositerAddr + params.Depositor = depositorAddr } if len(strProposalStatus) != 0 { diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index 7a6b043e63..8e22f245d1 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -28,14 +28,14 @@ func (voteA Vote) Empty() bool { // Deposit type Deposit struct { - Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer + Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor ProposalID uint64 `json:"proposal_id"` // proposalID of the proposal Amount sdk.Coins `json:"amount"` // Deposit amount } // Returns whether 2 deposits are equal func (depositA Deposit) Equals(depositB Deposit) bool { - return depositA.Depositer.Equals(depositB.Depositer) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount) + return depositA.Depositor.Equals(depositB.Depositor) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount) } // Returns whether a deposit is empty diff --git a/x/gov/genesis.go b/x/gov/genesis.go index e134a4a78c..2096fc2ec2 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -69,7 +69,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { k.setVotingParams(ctx, data.VotingParams) k.setTallyParams(ctx, data.TallyParams) for _, deposit := range data.Deposits { - k.setDeposit(ctx, deposit.ProposalID, deposit.Deposit.Depositer, deposit.Deposit) + k.setDeposit(ctx, deposit.ProposalID, deposit.Deposit.Depositor, deposit.Deposit) } for _, vote := range data.Votes { k.setVote(ctx, vote.ProposalID, vote.Vote.Voter, vote.Vote) diff --git a/x/gov/handler.go b/x/gov/handler.go index 180f7a21a2..9516710a96 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -53,7 +53,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result { - err, votingStarted := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositer, msg.Amount) + err, votingStarted := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositor, msg.Amount) if err != nil { return err.Result() } @@ -63,7 +63,7 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result // TODO: Add tag for if voting period started resTags := sdk.NewTags( tags.Action, tags.ActionDeposit, - tags.Depositer, []byte(msg.Depositer.String()), + tags.Depositor, []byte(msg.Depositor.String()), tags.ProposalID, proposalIDBytes, ) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 4f370ef531..0a3c8e1064 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -139,7 +139,7 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { } // Get Proposal from store by ProposalID -func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal { +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal { maxProposalID, err := keeper.peekCurrentProposalID(ctx) if err != nil { @@ -160,8 +160,8 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr } } - if depositerAddr != nil && len(depositerAddr) != 0 { - _, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) + if depositorAddr != nil && len(depositorAddr) != 0 { + _, found := keeper.GetDeposit(ctx, proposalID, depositorAddr) if !found { continue } @@ -340,10 +340,10 @@ func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sd // ===================================================== // Deposits -// Gets the deposit of a specific depositer on a specific proposal -func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress) (Deposit, bool) { +// Gets the deposit of a specific depositor on a specific proposal +func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (Deposit, bool) { store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyDeposit(proposalID, depositerAddr)) + bz := store.Get(KeyDeposit(proposalID, depositorAddr)) if bz == nil { return Deposit{}, false } @@ -352,15 +352,15 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositerAdd return deposit, true } -func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress, deposit Deposit) { +func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, deposit Deposit) { store := ctx.KVStore(keeper.storeKey) bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit) - store.Set(KeyDeposit(proposalID, depositerAddr), bz) + store.Set(KeyDeposit(proposalID, depositorAddr), bz) } -// Adds or updates a deposit of a specific depositer on a specific proposal +// Adds or updates a deposit of a specific depositor on a specific proposal // Activates voting period when appropriate -func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositerAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) { +func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) { // Checks to see if proposal exists proposal := keeper.GetProposal(ctx, proposalID) if proposal == nil { @@ -372,8 +372,8 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositerAdd return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false } - // Send coins from depositer's account to DepositedCoinsAccAddr account - _, err := keeper.ck.SendCoins(ctx, depositerAddr, DepositedCoinsAccAddr, depositAmount) + // Send coins from depositor's account to DepositedCoinsAccAddr account + _, err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount) if err != nil { return err, false } @@ -391,13 +391,13 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositerAdd } // Add or update deposit object - currDeposit, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) + currDeposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr) if !found { - newDeposit := Deposit{depositerAddr, proposalID, depositAmount} - keeper.setDeposit(ctx, proposalID, depositerAddr, newDeposit) + newDeposit := Deposit{depositorAddr, proposalID, depositAmount} + keeper.setDeposit(ctx, proposalID, depositorAddr, newDeposit) } else { currDeposit.Amount = currDeposit.Amount.Plus(depositAmount) - keeper.setDeposit(ctx, proposalID, depositerAddr, currDeposit) + keeper.setDeposit(ctx, proposalID, depositorAddr, currDeposit) } return nil, activatedVotingPeriod @@ -418,7 +418,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) - _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositer, deposit.Amount) + _, err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount) if err != nil { panic("should not happen") } diff --git a/x/gov/keeper_keys.go b/x/gov/keeper_keys.go index 8a3324fd18..b1a5d7761e 100644 --- a/x/gov/keeper_keys.go +++ b/x/gov/keeper_keys.go @@ -25,8 +25,8 @@ func KeyProposal(proposalID uint64) []byte { } // Key for getting a specific deposit from the store -func KeyDeposit(proposalID uint64, depositerAddr sdk.AccAddress) []byte { - return []byte(fmt.Sprintf("deposits:%d:%d", proposalID, depositerAddr)) +func KeyDeposit(proposalID uint64, depositorAddr sdk.AccAddress) []byte { + return []byte(fmt.Sprintf("deposits:%d:%d", proposalID, depositorAddr)) } // Key for getting a specific vote from the store diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 23199472ba..83702920e1 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -93,7 +93,7 @@ func TestDeposits(t *testing.T) { deposit, found = keeper.GetDeposit(ctx, proposalID, addrs[0]) require.True(t, found) require.Equal(t, fourSteak, deposit.Amount) - require.Equal(t, addrs[0], deposit.Depositer) + require.Equal(t, addrs[0], deposit.Depositor) require.Equal(t, fourSteak, keeper.GetProposal(ctx, proposalID).GetTotalDeposit()) require.Equal(t, addr0Initial.Minus(fourSteak), keeper.ck.GetCoins(ctx, addrs[0])) @@ -104,7 +104,7 @@ func TestDeposits(t *testing.T) { deposit, found = keeper.GetDeposit(ctx, proposalID, addrs[0]) require.True(t, found) require.Equal(t, fourSteak.Plus(fiveSteak), deposit.Amount) - require.Equal(t, addrs[0], deposit.Depositer) + require.Equal(t, addrs[0], deposit.Depositor) require.Equal(t, fourSteak.Plus(fiveSteak), keeper.GetProposal(ctx, proposalID).GetTotalDeposit()) require.Equal(t, addr0Initial.Minus(fourSteak).Minus(fiveSteak), keeper.ck.GetCoins(ctx, addrs[0])) @@ -114,7 +114,7 @@ func TestDeposits(t *testing.T) { require.True(t, votingStarted) deposit, found = keeper.GetDeposit(ctx, proposalID, addrs[1]) require.True(t, found) - require.Equal(t, addrs[1], deposit.Depositer) + require.Equal(t, addrs[1], deposit.Depositor) require.Equal(t, fourSteak, deposit.Amount) require.Equal(t, fourSteak.Plus(fiveSteak).Plus(fourSteak), keeper.GetProposal(ctx, proposalID).GetTotalDeposit()) require.Equal(t, addr1Initial.Minus(fourSteak), keeper.ck.GetCoins(ctx, addrs[1])) @@ -126,11 +126,11 @@ func TestDeposits(t *testing.T) { depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) - require.Equal(t, addrs[0], deposit.Depositer) + require.Equal(t, addrs[0], deposit.Depositor) require.Equal(t, fourSteak.Plus(fiveSteak), deposit.Amount) depositsIterator.Next() keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) - require.Equal(t, addrs[1], deposit.Depositer) + require.Equal(t, addrs[1], deposit.Depositor) require.Equal(t, fourSteak, deposit.Amount) depositsIterator.Next() require.False(t, depositsIterator.Valid()) diff --git a/x/gov/msgs.go b/x/gov/msgs.go index b8325d81ca..6465b2a2d5 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -85,14 +85,14 @@ func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress { // MsgDeposit type MsgDeposit struct { ProposalID uint64 `json:"proposal_id"` // ID of the proposal - Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer + Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } -func NewMsgDeposit(depositer sdk.AccAddress, proposalID uint64, amount sdk.Coins) MsgDeposit { +func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins) MsgDeposit { return MsgDeposit{ ProposalID: proposalID, - Depositer: depositer, + Depositor: depositor, Amount: amount, } } @@ -104,8 +104,8 @@ func (msg MsgDeposit) Type() string { return "deposit" } // Implements Msg. func (msg MsgDeposit) ValidateBasic() sdk.Error { - if len(msg.Depositer) == 0 { - return sdk.ErrInvalidAddress(msg.Depositer.String()) + if len(msg.Depositor) == 0 { + return sdk.ErrInvalidAddress(msg.Depositor.String()) } if !msg.Amount.IsValid() { return sdk.ErrInvalidCoins(msg.Amount.String()) @@ -120,7 +120,7 @@ func (msg MsgDeposit) ValidateBasic() sdk.Error { } func (msg MsgDeposit) String() string { - return fmt.Sprintf("MsgDeposit{%s=>%v: %v}", msg.Depositer, msg.ProposalID, msg.Amount) + return fmt.Sprintf("MsgDeposit{%s=>%v: %v}", msg.Depositor, msg.ProposalID, msg.Amount) } // Implements Msg. @@ -139,7 +139,7 @@ func (msg MsgDeposit) GetSignBytes() []byte { // Implements Msg. func (msg MsgDeposit) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Depositer} + return []sdk.AccAddress{msg.Depositor} } //----------------------------------------------------------- diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index 4a661985c9..36bc10a6a0 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -57,7 +57,7 @@ func TestMsgDeposit(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) tests := []struct { proposalID uint64 - depositerAddr sdk.AccAddress + depositorAddr sdk.AccAddress depositAmount sdk.Coins expectPass bool }{ @@ -68,7 +68,7 @@ func TestMsgDeposit(t *testing.T) { } for i, tc := range tests { - msg := NewMsgDeposit(tc.depositerAddr, tc.proposalID, tc.depositAmount) + msg := NewMsgDeposit(tc.depositorAddr, tc.proposalID, tc.depositAmount) if tc.expectPass { require.NoError(t, msg.ValidateBasic(), "test: %v", i) } else { diff --git a/x/gov/querier.go b/x/gov/querier.go index 37335b9a1e..cde85bf940 100644 --- a/x/gov/querier.go +++ b/x/gov/querier.go @@ -113,14 +113,14 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper // Params for query 'custom/gov/deposit' type QueryDepositParams struct { ProposalID uint64 - Depositer sdk.AccAddress + Depositor sdk.AccAddress } // creates a new instance of QueryDepositParams -func NewQueryDepositParams(proposalID uint64, depositer sdk.AccAddress) QueryDepositParams { +func NewQueryDepositParams(proposalID uint64, depositor sdk.AccAddress) QueryDepositParams { return QueryDepositParams{ ProposalID: proposalID, - Depositer: depositer, + Depositor: depositor, } } @@ -132,7 +132,7 @@ func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } - deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) + deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositor) bz, err := codec.MarshalJSONIndent(keeper.cdc, deposit) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) @@ -253,16 +253,16 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke // Params for query 'custom/gov/proposals' type QueryProposalsParams struct { Voter sdk.AccAddress - Depositer sdk.AccAddress + Depositor sdk.AccAddress ProposalStatus ProposalStatus Limit uint64 } // creates a new instance of QueryProposalsParams -func NewQueryProposalsParams(status ProposalStatus, limit uint64, voter, depositer sdk.AccAddress) QueryProposalsParams { +func NewQueryProposalsParams(status ProposalStatus, limit uint64, voter, depositor sdk.AccAddress) QueryProposalsParams { return QueryProposalsParams{ Voter: voter, - Depositer: depositer, + Depositor: depositor, ProposalStatus: status, Limit: limit, } @@ -276,7 +276,7 @@ func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keepe return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } - proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.Limit) + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositor, params.ProposalStatus, params.Limit) bz, err := codec.MarshalJSONIndent(keeper.cdc, proposals) if err != nil { diff --git a/x/gov/querier_test.go b/x/gov/querier_test.go index c649f2b21a..9ee71323ed 100644 --- a/x/gov/querier_test.go +++ b/x/gov/querier_test.go @@ -69,10 +69,10 @@ func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier return proposal } -func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositer, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal { +func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositor, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryProposals}, "/"), - Data: cdc.MustMarshalJSON(NewQueryProposalsParams(status, limit, voter, depositer)), + Data: cdc.MustMarshalJSON(NewQueryProposalsParams(status, limit, voter, depositor)), } bz, err := querier(ctx, []string{QueryProposal}, query) @@ -85,10 +85,10 @@ func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querie return proposals } -func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositer sdk.AccAddress) Deposit { +func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) Deposit { query := abci.RequestQuery{ Path: strings.Join([]string{"custom", "gov", QueryDeposit}, "/"), - Data: cdc.MustMarshalJSON(NewQueryDepositParams(proposalID, depositer)), + Data: cdc.MustMarshalJSON(NewQueryDepositParams(proposalID, depositor)), } bz, err := querier(ctx, []string{QueryDeposits}, query) diff --git a/x/gov/tags/tags.go b/x/gov/tags/tags.go index 2eded1901a..954acc5eda 100644 --- a/x/gov/tags/tags.go +++ b/x/gov/tags/tags.go @@ -17,6 +17,6 @@ var ( Proposer = "proposer" ProposalID = "proposal-id" VotingPeriodStart = "voting-period-start" - Depositer = "depositer" + Depositor = "depositor" Voter = "voter" ) From 972377c2874148881cf0fecfe984d4619362d4f6 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 21 Nov 2018 23:49:05 -0500 Subject: [PATCH 32/51] lint --- x/auth/ante.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/auth/ante.go b/x/auth/ante.go index 9a7a15e3e9..7ac245cf08 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -310,7 +310,7 @@ func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { if stdTx.Fee.Gas <= 0 { return sdk.ErrInternal(fmt.Sprintf("invalid gas supplied: %d", stdTx.Fee.Gas)).Result() } - requiredFees := adjustFeesByGas(ctx.MinimumFees(), uint64(stdTx.Fee.Gas)) + requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas) // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). if !ctx.MinimumFees().IsZero() && !stdTx.Fee.Amount.IsAllGTE(requiredFees) { From b4b61b890c4f031562a3830351c270561c64cab9 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 22 Nov 2018 00:30:04 -0500 Subject: [PATCH 33/51] address some comments while reviewing Jaes work --- baseapp/baseapp.go | 6 +++++- types/gas.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index de7e36d72a..b0e7645d48 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -136,6 +136,7 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { } // load latest application version +// panics if called more than once on a running baseapp func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error { err := app.cms.LoadLatestVersion() if err != nil { @@ -145,6 +146,7 @@ func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error { } // load application version +// panics if called more than once on a running baseapp func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error { err := app.cms.LoadVersion(version) if err != nil { @@ -702,7 +704,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk } } - // consume block gas whether panic or not. + // If BlockGasMeter() panics it will be caught by the above recover and + // return an error - in any case BlockGasMeter will consume gas past + // the limit. if mode == runTxModeDeliver { ctx.BlockGasMeter().ConsumeGas( ctx.GasMeter().GasConsumedToLimit(), "block gas meter") diff --git a/types/gas.go b/types/gas.go index 90c9da3ec8..100b004305 100644 --- a/types/gas.go +++ b/types/gas.go @@ -63,7 +63,7 @@ func (g *basicGasMeter) Limit() Gas { } func (g *basicGasMeter) GasConsumedToLimit() Gas { - if g.consumed > g.limit { + if g.IsPastLimit() { return g.limit } return g.consumed From abed373d5c1525e5f1d882c2c885b83dbad464c2 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 22 Nov 2018 00:36:12 -0500 Subject: [PATCH 34/51] extra max block gas test at limit --- baseapp/baseapp_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 0994dd3613..f4b3b12861 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -920,6 +920,7 @@ func TestMaxBlockGasLimits(t *testing.T) { {newTxCounter(10, 0), 3, 10, false, 0}, {newTxCounter(10, 0), 10, 10, false, 0}, {newTxCounter(2, 7), 11, 9, false, 0}, + {newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass {newTxCounter(10, 0), 11, 10, true, 10}, {newTxCounter(10, 0), 15, 10, true, 10}, From 2d3e1afea8dce759f9a0e0cf0a83943563840fde Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 22 Nov 2018 11:21:11 +0100 Subject: [PATCH 35/51] Add demonstrative failing testcase --- baseapp/baseapp_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index f4b3b12861..c2346499d5 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -922,6 +922,7 @@ func TestMaxBlockGasLimits(t *testing.T) { {newTxCounter(2, 7), 11, 9, false, 0}, {newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass + {newTxCounter(9, 0), 12, 9, true, 11}, // fail after 11 {newTxCounter(10, 0), 11, 10, true, 10}, {newTxCounter(10, 0), 15, 10, true, 10}, } From c245a3deb1b76251b8ea2de21c84e3aae5ce4de8 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 22 Nov 2018 13:51:51 +0000 Subject: [PATCH 36/51] Don't get command through client.{Get,Post}Commands() Logic has changed recently and commands are now enriched with flags by default. Closes: #2884 --- docs/examples/basecoin/cmd/basecli/main.go | 52 +++++++++++----------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index cb0eeba5b9..36952be877 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -72,35 +72,33 @@ func main() { // add query/post commands (custom to binary) rootCmd.AddCommand( - client.GetCommands( - stakecmd.GetCmdQueryValidator(storeStake, cdc), - stakecmd.GetCmdQueryValidators(storeStake, cdc), - stakecmd.GetCmdQueryValidatorUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidatorRedelegations(storeStake, cdc), - stakecmd.GetCmdQueryDelegation(storeStake, cdc), - stakecmd.GetCmdQueryDelegations(storeStake, cdc), - stakecmd.GetCmdQueryPool(storeStake, cdc), - stakecmd.GetCmdQueryParams(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryRedelegation(storeStake, cdc), - stakecmd.GetCmdQueryRedelegations(storeStake, cdc), - slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), - authcmd.GetAccountCmd(storeAcc, cdc), - )...) + stakecmd.GetCmdQueryValidator(storeStake, cdc), + stakecmd.GetCmdQueryValidators(storeStake, cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidatorRedelegations(storeStake, cdc), + stakecmd.GetCmdQueryDelegation(storeStake, cdc), + stakecmd.GetCmdQueryDelegations(storeStake, cdc), + stakecmd.GetCmdQueryPool(storeStake, cdc), + stakecmd.GetCmdQueryParams(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryRedelegation(storeStake, cdc), + stakecmd.GetCmdQueryRedelegations(storeStake, cdc), + slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), + authcmd.GetAccountCmd(storeAcc, cdc), + ) rootCmd.AddCommand( - client.PostCommands( - bankcmd.SendTxCmd(cdc), - ibccmd.IBCTransferCmd(cdc), - ibccmd.IBCRelayCmd(cdc), - stakecmd.GetCmdCreateValidator(cdc), - stakecmd.GetCmdEditValidator(cdc), - stakecmd.GetCmdDelegate(cdc), - stakecmd.GetCmdUnbond(storeStake, cdc), - stakecmd.GetCmdRedelegate(storeStake, cdc), - slashingcmd.GetCmdUnjail(cdc), - )...) + bankcmd.SendTxCmd(cdc), + ibccmd.IBCTransferCmd(cdc), + ibccmd.IBCRelayCmd(cdc), + stakecmd.GetCmdCreateValidator(cdc), + stakecmd.GetCmdEditValidator(cdc), + stakecmd.GetCmdDelegate(cdc), + stakecmd.GetCmdUnbond(storeStake, cdc), + stakecmd.GetCmdRedelegate(storeStake, cdc), + slashingcmd.GetCmdUnjail(cdc), + ) // add proxy, version and key info rootCmd.AddCommand( From 83793a8974a6750208cb027c0e36353acd39799d Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 22 Nov 2018 14:36:28 +0000 Subject: [PATCH 37/51] Update PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 47c6ab60e5..757447358a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -91,6 +91,7 @@ BUG FIXES - \#2733 [x/gov, x/mock/simulation] Fix governance simulation, update x/gov import/export - \#2854 [x/bank] Remove unused bank.MsgIssue, prevent possible panic + - \#2884 [docs/examples] Fix `basecli version` panic * Tendermint * [\#2797](https://github.com/tendermint/tendermint/pull/2797) AddressBook requires addresses to have IDs; Do not crap out immediately after sending pex addrs in seed mode From b8b502c43a8ae3e181a716274b2636936bf12feb Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 22 Nov 2018 14:42:35 +0000 Subject: [PATCH 38/51] Same for democli --- docs/examples/democoin/cmd/democli/main.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/examples/democoin/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index 0c37d9bd0b..a001ee8b5f 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -70,13 +70,11 @@ func main() { // add query/post commands (custom to binary) // start with commands common to basecoin rootCmd.AddCommand( - client.GetCommands( - authcmd.GetAccountCmd(storeAcc, cdc), - )...) + authcmd.GetAccountCmd(storeAcc, cdc), + ) rootCmd.AddCommand( - client.PostCommands( - bankcmd.SendTxCmd(cdc), - )...) + bankcmd.SendTxCmd(cdc), + ) rootCmd.AddCommand( client.PostCommands( simplestakingcmd.BondTxCmd(cdc), From c22b400ba84a3dd4cc5b860939609786ae803767 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 22 Nov 2018 15:49:56 +0000 Subject: [PATCH 39/51] Add missing cmd --- docs/examples/basecoin/cmd/basecli/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index 36952be877..8093dd3b10 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -85,6 +85,7 @@ func main() { stakecmd.GetCmdQueryRedelegation(storeStake, cdc), stakecmd.GetCmdQueryRedelegations(storeStake, cdc), slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), + stakecmd.GetCmdQueryValidatorDelegations(storeStake, cdc), authcmd.GetAccountCmd(storeAcc, cdc), ) From 56fa7dc4ef7aa07757925875e193624f00ce33f1 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 22 Nov 2018 12:34:13 -0500 Subject: [PATCH 40/51] fix BlockGasRecovery --- baseapp/baseapp.go | 16 ++++++++++------ baseapp/baseapp_test.go | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index b0e7645d48..02f482415c 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -704,16 +704,20 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk } } - // If BlockGasMeter() panics it will be caught by the above recover and - // return an error - in any case BlockGasMeter will consume gas past - // the limit. + result.GasWanted = gasWanted + result.GasUsed = ctx.GasMeter().GasConsumed() + }() + + // If BlockGasMeter() panics it will be caught by the above recover and + // return an error - in any case BlockGasMeter will consume gas past + // the limit. + // NOTE: this must exist in a separate defer function for the + // above recovery to recover from this one + defer func() { if mode == runTxModeDeliver { ctx.BlockGasMeter().ConsumeGas( ctx.GasMeter().GasConsumedToLimit(), "block gas meter") } - - result.GasWanted = gasWanted - result.GasUsed = ctx.GasMeter().GasConsumed() }() var msgs = tx.GetMsgs() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index c2346499d5..d629ec5808 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -922,12 +922,13 @@ func TestMaxBlockGasLimits(t *testing.T) { {newTxCounter(2, 7), 11, 9, false, 0}, {newTxCounter(10, 0), 10, 10, false, 0}, // hit the limit but pass - {newTxCounter(9, 0), 12, 9, true, 11}, // fail after 11 {newTxCounter(10, 0), 11, 10, true, 10}, {newTxCounter(10, 0), 15, 10, true, 10}, + {newTxCounter(9, 0), 12, 9, true, 11}, // fly past the limit } for i, tc := range testCases { + fmt.Printf("debug i: %v\n", i) tx := tc.tx // reset the block gas @@ -944,7 +945,6 @@ func TestMaxBlockGasLimits(t *testing.T) { if tc.fail && (j+1) > tc.failAfterDeliver { require.Equal(t, res.Code, sdk.CodeOutOfGas, fmt.Sprintf("%d: %v, %v", i, tc, res)) require.Equal(t, res.Codespace, sdk.CodespaceRoot, fmt.Sprintf("%d: %v, %v", i, tc, res)) - //require.True(t, ctx.BlockGasMeter().IsPastLimit()) NOTE: not necessarily true. require.True(t, ctx.BlockGasMeter().IsOutOfGas()) } else { // check gas used and wanted From ce10ef2b274faf2bf4b4bbf292238f7a3b2c7911 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 22 Nov 2018 12:41:20 -0500 Subject: [PATCH 41/51] replaced proto with codec in baseapp --- baseapp/baseapp.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 02f482415c..f4c20ca905 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -6,7 +6,6 @@ import ( "runtime/debug" "strings" - "github.com/gogo/protobuf/proto" "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" @@ -184,7 +183,7 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { consensusParamsBz := mainStore.Get(mainConsensusParamsKey) if consensusParamsBz != nil { var consensusParams = &abci.ConsensusParams{} - err := proto.Unmarshal(consensusParamsBz, consensusParams) + err := codec.Cdc.UnmarshalBinaryLengthPrefixed(consensusParamsBz, consensusParams) if err != nil { panic(err) } @@ -249,7 +248,7 @@ func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) { // setConsensusParams stores the consensus params to the main store. func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) { - consensusParamsBz, err := proto.Marshal(consensusParams) + consensusParamsBz, err := codec.Cdc.MarshalBinaryLengthPrefixed(consensusParams) if err != nil { panic(err) } From 33d64b6163bbb713faaf57c441b278178f697e12 Mon Sep 17 00:00:00 2001 From: dongsamb Date: Sat, 24 Nov 2018 01:28:29 +0900 Subject: [PATCH 42/51] Merge PR #2891: Remove redundant temporary code remDelPool when calc DelPool --- x/distribution/types/delegator_info.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/distribution/types/delegator_info.go b/x/distribution/types/delegator_info.go index 83ca7f8cc9..decf321298 100644 --- a/x/distribution/types/delegator_info.go +++ b/x/distribution/types/delegator_info.go @@ -50,9 +50,8 @@ func (di DelegationDistInfo) WithdrawRewards(wc WithdrawContext, vi ValidatorDis accum := di.GetDelAccum(wc.Height, delegatorShares) di.DelPoolWithdrawalHeight = wc.Height withdrawalTokens := vi.DelPool.MulDec(accum).QuoDec(vi.DelAccum.Accum) - remDelPool := vi.DelPool.Minus(withdrawalTokens) - vi.DelPool = remDelPool + vi.DelPool = vi.DelPool.Minus(withdrawalTokens) vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum) return di, vi, fp, withdrawalTokens From 5792e1d5c44a01e9b02347225d980d93b15baf3d Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sat, 24 Nov 2018 18:10:39 -0800 Subject: [PATCH 43/51] Apply suggestions from code review Co-Authored-By: jaekwon --- baseapp/baseapp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index f4c20ca905..b2e07c5881 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -173,13 +173,13 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { return errors.New("baseapp expects MultiStore with 'main' KVStore") } - // memoize mainKey. + // memoize mainKey if app.mainKey != nil { panic("app.mainKey expected to be nil; duplicate init?") } app.mainKey = mainKey - // load consensus param from the main store + // load consensus params from the main store consensusParamsBz := mainStore.Get(mainConsensusParamsKey) if consensusParamsBz != nil { var consensusParams = &abci.ConsensusParams{} From 819af35962ac9990b89ca92b20104116eaaf0d8d Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sat, 24 Nov 2018 18:10:59 -0800 Subject: [PATCH 44/51] Final fixes from review --- baseapp/baseapp.go | 5 +++-- types/context.go | 15 --------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index b2e07c5881..a83713d424 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -6,6 +6,7 @@ import ( "runtime/debug" "strings" + "github.com/gogo/protobuf/proto" "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" @@ -183,7 +184,7 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { consensusParamsBz := mainStore.Get(mainConsensusParamsKey) if consensusParamsBz != nil { var consensusParams = &abci.ConsensusParams{} - err := codec.Cdc.UnmarshalBinaryLengthPrefixed(consensusParamsBz, consensusParams) + err := proto.Unmarshal(consensusParamsBz, consensusParams) if err != nil { panic(err) } @@ -248,7 +249,7 @@ func (app *BaseApp) setConsensusParams(consensusParams *abci.ConsensusParams) { // setConsensusParams stores the consensus params to the main store. func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams) { - consensusParamsBz, err := codec.Cdc.MarshalBinaryLengthPrefixed(consensusParams) + consensusParamsBz, err := proto.Marshal(consensusParams) if err != nil { panic(err) } diff --git a/types/context.go b/types/context.go index 905748d513..add88bfc33 100644 --- a/types/context.go +++ b/types/context.go @@ -133,7 +133,6 @@ const ( contextKeyMultiStore contextKey = iota contextKeyBlockHeader contextKeyBlockHeight - contextKeyConsensusParams contextKeyChainID contextKeyIsCheckTx contextKeyTxBytes @@ -152,10 +151,6 @@ func (c Context) BlockHeader() abci.Header { return c.Value(contextKeyBlockHeade func (c Context) BlockHeight() int64 { return c.Value(contextKeyBlockHeight).(int64) } -func (c Context) ConsensusParams() abci.ConsensusParams { - return c.Value(contextKeyConsensusParams).(abci.ConsensusParams) -} - func (c Context) ChainID() string { return c.Value(contextKeyChainID).(string) } func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) } @@ -201,16 +196,6 @@ func (c Context) WithBlockHeight(height int64) Context { return c.withValue(contextKeyBlockHeight, height).withValue(contextKeyBlockHeader, newHeader) } -func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context { - if params == nil { - return c - } - - // TODO: Do we need to handle invalid MaxGas values? - return c.withValue(contextKeyConsensusParams, params). - WithGasMeter(NewGasMeter(uint64(params.BlockSize.MaxGas))) -} - func (c Context) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) } func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contextKeyTxBytes, txBytes) } From f12ac439f7c62e398da516ffa2bb9572278cfc55 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sun, 25 Nov 2018 23:44:51 -0500 Subject: [PATCH 45/51] dep --- Gopkg.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 40192b2afd..b166713ee0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -165,13 +165,12 @@ version = "v1.2.0" [[projects]] - digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" + digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", - "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", @@ -644,6 +643,7 @@ "github.com/bgentry/speakeasy", "github.com/btcsuite/btcd/btcec", "github.com/cosmos/go-bip39", + "github.com/gogo/protobuf/proto", "github.com/golang/protobuf/proto", "github.com/gorilla/mux", "github.com/mattn/go-isatty", From 2d071763d18832867436b718f5a3a845696e8704 Mon Sep 17 00:00:00 2001 From: yutianwu Date: Mon, 26 Nov 2018 18:26:05 +0800 Subject: [PATCH 46/51] Merge PR #2899: Remove redundant $ --- PENDING.md | 1 + docker-compose.yml | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PENDING.md b/PENDING.md index 6f99f5705c..c4c2c73f4d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -87,6 +87,7 @@ BUG FIXES * Gaia * [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address` * [\#2742](https://github.com/cosmos/cosmos-sdk/issues/2742) Fix time format of TimeoutCommit override + * [\#2898](https://github.com/cosmos/cosmos-sdk/issues/2898) Remove redundant '$' in docker-compose.yml * SDK diff --git a/docker-compose.yml b/docker-compose.yml index fca518db4e..a0001416e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: - "26656-26657:26656-26657" environment: - ID=0 - - LOG=$${LOG:-gaiad.log} + - LOG=${LOG:-gaiad.log} volumes: - ./build:/gaiad:Z networks: @@ -22,7 +22,7 @@ services: - "26659-26660:26656-26657" environment: - ID=1 - - LOG=$${LOG:-gaiad.log} + - LOG=${LOG:-gaiad.log} volumes: - ./build:/gaiad:Z networks: @@ -34,7 +34,7 @@ services: image: "tendermint/gaiadnode" environment: - ID=2 - - LOG=$${LOG:-gaiad.log} + - LOG=${LOG:-gaiad.log} ports: - "26661-26662:26656-26657" volumes: @@ -48,7 +48,7 @@ services: image: "tendermint/gaiadnode" environment: - ID=3 - - LOG=$${LOG:-gaiad.log} + - LOG=${LOG:-gaiad.log} ports: - "26663-26664:26656-26657" volumes: From b7da2eaa3374c3603a713793ba3f7b82bd81cad9 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 26 Nov 2018 06:29:21 -0500 Subject: [PATCH 47/51] Merge PR #2799: Account numbers and sequences to uint64 --- PENDING.md | 1 + client/context/query.go | 4 +- client/flags.go | 4 +- client/utils/rest.go | 4 +- cmd/gaia/app/genesis.go | 4 +- docs/_attic/sdk/core/app3.md | 16 ++--- docs/examples/democoin/x/cool/app_test.go | 16 ++--- docs/examples/democoin/x/pow/app_test.go | 6 +- x/auth/account.go | 20 +++--- x/auth/account_test.go | 4 +- x/auth/ante_test.go | 84 +++++++++++------------ x/auth/client/rest/sign.go | 4 +- x/auth/client/txbuilder/stdsignmsg.go | 4 +- x/auth/client/txbuilder/txbuilder.go | 12 ++-- x/auth/client/txbuilder/txbuilder_test.go | 4 +- x/auth/keeper.go | 8 +-- x/auth/keeper_test.go | 6 +- x/auth/stdtx.go | 10 +-- x/auth/stdtx_test.go | 14 ++-- x/bank/app_test.go | 32 ++++----- x/bank/bench_test.go | 2 +- x/bank/simulation/msgs.go | 4 +- x/ibc/app_test.go | 8 +-- x/ibc/client/cli/relay.go | 10 +-- x/ibc/ibc_test.go | 14 ++-- x/ibc/mapper.go | 12 ++-- x/ibc/types.go | 4 +- x/mock/app.go | 6 +- x/mock/app_test.go | 10 +-- x/mock/test_utils.go | 8 +-- x/slashing/app_test.go | 4 +- x/stake/app_test.go | 10 +-- 32 files changed, 175 insertions(+), 174 deletions(-) diff --git a/PENDING.md b/PENDING.md index c4c2c73f4d..85f740b964 100644 --- a/PENDING.md +++ b/PENDING.md @@ -16,6 +16,7 @@ BREAKING CHANGES * SDK * [\#2752](https://github.com/cosmos/cosmos-sdk/pull/2752) Don't hardcode bondable denom. + * [\#2701](https://github.com/cosmos/cosmos-sdk/issues/2701) Account numbers and sequence numbers in `auth` are now `uint64` instead of `int64` * [\#2019](https://github.com/cosmos/cosmos-sdk/issues/2019) Cap total number of signatures. Current per-transaction limit is 7, and if that is exceeded transaction is rejected. * [\#2801](https://github.com/cosmos/cosmos-sdk/pull/2801) Remove AppInit structure. * [\#2798](https://github.com/cosmos/cosmos-sdk/issues/2798) Governance API has miss-spelled English word in JSON response ('depositer' -> 'depositor') diff --git a/client/context/query.go b/client/context/query.go index 572a137787..486bd5186e 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -92,7 +92,7 @@ func (ctx CLIContext) GetFromName() (string, error) { // GetAccountNumber returns the next account number for the given account // address. -func (ctx CLIContext) GetAccountNumber(address []byte) (int64, error) { +func (ctx CLIContext) GetAccountNumber(address []byte) (uint64, error) { account, err := ctx.GetAccount(address) if err != nil { return 0, err @@ -103,7 +103,7 @@ func (ctx CLIContext) GetAccountNumber(address []byte) (int64, error) { // GetAccountSequence returns the sequence number for the given account // address. -func (ctx CLIContext) GetAccountSequence(address []byte) (int64, error) { +func (ctx CLIContext) GetAccountSequence(address []byte) (uint64, error) { account, err := ctx.GetAccount(address) if err != nil { return 0, err diff --git a/client/flags.go b/client/flags.go index ffb2a0c2f0..6a3ccff060 100644 --- a/client/flags.go +++ b/client/flags.go @@ -74,8 +74,8 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") - c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") - c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") + c.Flags().Uint64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") + c.Flags().Uint64(FlagSequence, 0, "Sequence number to sign the tx") c.Flags().String(FlagMemo, "", "Memo to send along with transaction") c.Flags().String(FlagFee, "", "Fee to pay along with transaction") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") diff --git a/client/utils/rest.go b/client/utils/rest.go index 13347098b3..c0a8c3c77e 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -124,8 +124,8 @@ type BaseReq struct { Name string `json:"name"` Password string `json:"password"` ChainID string `json:"chain_id"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` Gas string `json:"gas"` GasAdjustment string `json:"gas_adjustment"` } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 15010af98f..60e96307bc 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -61,8 +61,8 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, type GenesisAccount struct { Address sdk.AccAddress `json:"address"` Coins sdk.Coins `json:"coins"` - Sequence int64 `json:"sequence_number"` - AccountNumber int64 `json:"account_number"` + Sequence uint64 `json:"sequence_number"` + AccountNumber uint64 `json:"account_number"` } func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { diff --git a/docs/_attic/sdk/core/app3.md b/docs/_attic/sdk/core/app3.md index 2462e3e66a..a84bb6873d 100644 --- a/docs/_attic/sdk/core/app3.md +++ b/docs/_attic/sdk/core/app3.md @@ -52,11 +52,11 @@ type Account interface { GetPubKey() crypto.PubKey // can return nil. SetPubKey(crypto.PubKey) error - GetAccountNumber() int64 - SetAccountNumber(int64) error + GetAccountNumber() uint64 + SetAccountNumber(uint64) error - GetSequence() int64 - SetSequence(int64) error + GetSequence() uint64 + SetSequence(uint64) error GetCoins() sdk.Coins SetCoins(sdk.Coins) error @@ -79,8 +79,8 @@ type BaseAccount struct { Address sdk.AccAddress `json:"address"` Coins sdk.Coins `json:"coins"` PubKey crypto.PubKey `json:"public_key"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` } ``` @@ -161,8 +161,8 @@ The standard form for signatures is `StdSignature`: type StdSignature struct { crypto.PubKey `json:"pub_key"` // optional []byte `json:"signature"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` } ``` diff --git a/docs/examples/democoin/x/cool/app_test.go b/docs/examples/democoin/x/cool/app_test.go index 7bfc8b9cc0..3725f7b9be 100644 --- a/docs/examples/democoin/x/cool/app_test.go +++ b/docs/examples/democoin/x/cool/app_test.go @@ -88,17 +88,17 @@ func TestMsgQuiz(t *testing.T) { require.Equal(t, acc1, res1) // Set the trend, submit a really cool quiz and check for reward - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, true, priv1) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []uint64{0}, []uint64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{1}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))}) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []uint64{0}, []uint64{2}, false, false, priv1) // result without reward mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))}) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{3}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))}) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do! + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []uint64{0}, []uint64{4}, true, true, priv1) // reset the trend + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{5}, false, false, priv1) // the same answer will nolonger do! mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))}) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []uint64{0}, []uint64{6}, true, true, priv1) // earlier answer now relevant again mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("badvibesonly", sdk.NewInt(69)), sdk.NewCoin("icecold", sdk.NewInt(138))}) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []uint64{0}, []uint64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool } diff --git a/docs/examples/democoin/x/pow/app_test.go b/docs/examples/democoin/x/pow/app_test.go index 58a7d35386..1556996b37 100644 --- a/docs/examples/democoin/x/pow/app_test.go +++ b/docs/examples/democoin/x/pow/app_test.go @@ -74,13 +74,13 @@ func TestMsgMine(t *testing.T) { // Mine and check for reward mineMsg1 := GenerateMsgMine(addr1, 1, 2) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(1))}) // Mine again and check for reward mineMsg2 := GenerateMsgMine(addr1, 2, 3) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))}) // Mine again - should be invalid - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, false, false, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))}) } diff --git a/x/auth/account.go b/x/auth/account.go index 4a55a48ea3..0fa601acc3 100644 --- a/x/auth/account.go +++ b/x/auth/account.go @@ -21,11 +21,11 @@ type Account interface { GetPubKey() crypto.PubKey // can return nil. SetPubKey(crypto.PubKey) error - GetAccountNumber() int64 - SetAccountNumber(int64) error + GetAccountNumber() uint64 + SetAccountNumber(uint64) error - GetSequence() int64 - SetSequence(int64) error + GetSequence() uint64 + SetSequence(uint64) error GetCoins() sdk.Coins SetCoins(sdk.Coins) error @@ -48,8 +48,8 @@ type BaseAccount struct { Address sdk.AccAddress `json:"address"` Coins sdk.Coins `json:"coins"` PubKey crypto.PubKey `json:"public_key"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` } // Prototype function for BaseAccount @@ -100,23 +100,23 @@ func (acc *BaseAccount) SetCoins(coins sdk.Coins) error { } // Implements Account -func (acc *BaseAccount) GetAccountNumber() int64 { +func (acc *BaseAccount) GetAccountNumber() uint64 { return acc.AccountNumber } // Implements Account -func (acc *BaseAccount) SetAccountNumber(accNumber int64) error { +func (acc *BaseAccount) SetAccountNumber(accNumber uint64) error { acc.AccountNumber = accNumber return nil } // Implements sdk.Account. -func (acc *BaseAccount) GetSequence() int64 { +func (acc *BaseAccount) GetSequence() uint64 { return acc.Sequence } // Implements sdk.Account. -func (acc *BaseAccount) SetSequence(seq int64) error { +func (acc *BaseAccount) SetSequence(seq uint64) error { acc.Sequence = seq return nil } diff --git a/x/auth/account_test.go b/x/auth/account_test.go index e48060fbef..8b75e8ce5c 100644 --- a/x/auth/account_test.go +++ b/x/auth/account_test.go @@ -67,7 +67,7 @@ func TestBaseAccountSequence(t *testing.T) { _, _, addr := keyPubAddr() acc := NewBaseAccountWithAddress(addr) - seq := int64(7) + seq := uint64(7) err := acc.SetSequence(seq) require.Nil(t, err) @@ -79,7 +79,7 @@ func TestBaseAccountMarshal(t *testing.T) { acc := NewBaseAccountWithAddress(addr) someCoins := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 246)} - seq := int64(7) + seq := uint64(7) // set everything on the account err := acc.SetPubKey(pub) diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index 566892e07e..8d7118e746 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -66,7 +66,7 @@ func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, } } -func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee) sdk.Tx { +func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") @@ -80,7 +80,7 @@ func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums return tx } -func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee, memo string) sdk.Tx { +func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, memo string) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo) @@ -95,7 +95,7 @@ func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, } // All signers sign over the same StdSignDoc. Should always create invalid signatures -func newTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee, signBytes []byte, memo string) sdk.Tx { +func newTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, signBytes []byte, memo string) sdk.Tx { sigs := make([]StdSignature, len(privs)) for i, priv := range privs { sig, err := priv.Sign(signBytes) @@ -133,7 +133,7 @@ func TestAnteHandlerSigErrors(t *testing.T) { msgs := []sdk.Msg{msg1, msg2} // test no signatures - privs, accNums, seqs := []crypto.PrivKey{}, []int64{}, []int64{} + privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) // tx.GetSigners returns addresses in correct order: addr1, addr2, addr3 @@ -145,12 +145,12 @@ func TestAnteHandlerSigErrors(t *testing.T) { checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test num sigs dont match GetSigners - privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test an unrecognized account - privs, accNums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{0, 0, 0} + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) @@ -193,30 +193,30 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { msgs := []sdk.Msg{msg} // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) // new tx from wrong account number - seqs = []int64{1} - tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) + seqs = []uint64{1} + tx = newTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // from correct account number - seqs = []int64{1} - tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) + seqs = []uint64{1} + tx = newTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) // new tx with another signer and incorrect account numbers msg1 := newTestMsg(addr1, addr2) msg2 := newTestMsg(addr2, addr1) msgs = []sdk.Msg{msg1, msg2} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // correct account numbers - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) } @@ -253,30 +253,30 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { msgs := []sdk.Msg{msg} // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) // new tx from wrong account number - seqs = []int64{1} - tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) + seqs = []uint64{1} + tx = newTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // from correct account number - seqs = []int64{1} - tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) + seqs = []uint64{1} + tx = newTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) // new tx with another signer and incorrect account numbers msg1 := newTestMsg(addr1, addr2) msg2 := newTestMsg(addr2, addr1) msgs = []sdk.Msg{msg1, msg2} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{1, 0}, []uint64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // correct account numbers - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 0}, []int64{2, 0} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 0}, []uint64{2, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) } @@ -317,7 +317,7 @@ func TestAnteHandlerSequences(t *testing.T) { msgs := []sdk.Msg{msg} // test good tx from one signer - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) @@ -325,7 +325,7 @@ func TestAnteHandlerSequences(t *testing.T) { checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // fix sequence, should pass - seqs = []int64{1} + seqs = []uint64{1} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) @@ -334,7 +334,7 @@ func TestAnteHandlerSequences(t *testing.T) { msg2 := newTestMsg(addr3, addr1) msgs = []sdk.Msg{msg1, msg2} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{2, 0, 0} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{2, 0, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) @@ -344,18 +344,18 @@ func TestAnteHandlerSequences(t *testing.T) { // tx from just second signer with incorrect sequence fails msg = newTestMsg(addr2) msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{1}, []int64{0} + privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{1}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) // fix the sequence and it passes - tx = newTestTx(ctx, msgs, []crypto.PrivKey{priv2}, []int64{1}, []int64{1}, fee) + tx = newTestTx(ctx, msgs, []crypto.PrivKey{priv2}, []uint64{1}, []uint64{1}, fee) checkValidTx(t, anteHandler, ctx, tx, false) // another tx from both of them that passes msg = newTestMsg(addr1, addr2) msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{3, 2} + privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{3, 2} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) } @@ -381,7 +381,7 @@ func TestAnteHandlerFees(t *testing.T) { // msg and signatures var tx sdk.Tx msg := newTestMsg(addr1) - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} fee := newStdFee() msgs := []sdk.Msg{msg} @@ -424,7 +424,7 @@ func TestAnteHandlerMemoGas(t *testing.T) { // msg and signatures var tx sdk.Tx msg := newTestMsg(addr1) - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} fee := NewStdFee(0, sdk.NewInt64Coin("atom", 0)) // tx does not have enough gas @@ -483,19 +483,19 @@ func TestAnteHandlerMultiSigner(t *testing.T) { fee := newStdFee() // signers in order - privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []int64{0, 1, 2}, []int64{0, 0, 0} + privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3}, []uint64{0, 1, 2}, []uint64{0, 0, 0} tx = newTestTxWithMemo(ctx, msgs, privs, accnums, seqs, fee, "Check signers are in expected order and different account numbers works") checkValidTx(t, anteHandler, ctx, tx, false) // change sequence numbers - tx = newTestTx(ctx, []sdk.Msg{msg1}, []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{1, 1}, fee) + tx = newTestTx(ctx, []sdk.Msg{msg1}, []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{1, 1}, fee) checkValidTx(t, anteHandler, ctx, tx, false) - tx = newTestTx(ctx, []sdk.Msg{msg2}, []crypto.PrivKey{priv3, priv1}, []int64{2, 0}, []int64{1, 2}, fee) + tx = newTestTx(ctx, []sdk.Msg{msg2}, []crypto.PrivKey{priv3, priv1}, []uint64{2, 0}, []uint64{1, 2}, fee) checkValidTx(t, anteHandler, ctx, tx, false) // expected seqs = [3, 2, 2] - tx = newTestTxWithMemo(ctx, msgs, privs, accnums, []int64{3, 2, 2}, fee, "Check signers are in expected order and different account numbers and sequence numbers works") + tx = newTestTxWithMemo(ctx, msgs, privs, accnums, []uint64{3, 2, 2}, fee, "Check signers are in expected order and different account numbers and sequence numbers works") checkValidTx(t, anteHandler, ctx, tx, false) } @@ -532,7 +532,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { fee3.Amount[0].Amount = fee3.Amount[0].Amount.AddRaw(100) // test good tx and signBytes - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) @@ -542,8 +542,8 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { cases := []struct { chainID string - accnum int64 - seq int64 + accnum uint64 + seq uint64 fee StdFee msgs []sdk.Msg code sdk.CodeType @@ -556,7 +556,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { {chainID, 0, 1, fee3, msgs, codeUnauth}, // test wrong fee } - privs, seqs = []crypto.PrivKey{priv1}, []int64{1} + privs, seqs = []crypto.PrivKey{priv1}, []uint64{1} for _, cs := range cases { tx := newTestTxWithSignBytes( @@ -568,14 +568,14 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { } // test wrong signer if public key exist - privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{0}, []int64{1} + privs, accnums, seqs = []crypto.PrivKey{priv2}, []uint64{0}, []uint64{1} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) // test wrong signer if public doesn't exist msg = newTestMsg(addr2) msgs = []sdk.Msg{msg} - privs, accnums, seqs = []crypto.PrivKey{priv1}, []int64{1}, []int64{0} + privs, accnums, seqs = []crypto.PrivKey{priv1}, []uint64{1}, []uint64{0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) @@ -609,7 +609,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { // test good tx and set public key msg := newTestMsg(addr1) msgs := []sdk.Msg{msg} - privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0} + privs, accnums, seqs := []crypto.PrivKey{priv1}, []uint64{0}, []uint64{0} fee := newStdFee() tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkValidTx(t, anteHandler, ctx, tx, false) @@ -620,7 +620,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { // test public key not found msg = newTestMsg(addr2) msgs = []sdk.Msg{msg} - tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) + tx = newTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) sigs := tx.(StdTx).GetSignatures() sigs[0].PubKey = nil checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) @@ -629,7 +629,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) { require.Nil(t, acc2.GetPubKey()) // test invalid signature and public key - tx = newTestTx(ctx, msgs, privs, []int64{1}, seqs, fee) + tx = newTestTx(ctx, msgs, privs, []uint64{1}, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) acc2 = mapper.GetAccount(ctx, addr2) @@ -785,7 +785,7 @@ func TestAnteHandlerSigLimitExceeded(t *testing.T) { // test rejection logic privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8}, - []int64{0, 0, 0, 0, 0, 0, 0, 0}, []int64{0, 0, 0, 0, 0, 0, 0, 0} + []uint64{0, 0, 0, 0, 0, 0, 0, 0}, []uint64{0, 0, 0, 0, 0, 0, 0, 0} tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeTooManySignatures) } diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 13dd8d20c3..0307db6c1a 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -18,8 +18,8 @@ type SignBody struct { LocalAccountName string `json:"name"` Password string `json:"password"` ChainID string `json:"chain_id"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` AppendSig bool `json:"append_sig"` } diff --git a/x/auth/client/txbuilder/stdsignmsg.go b/x/auth/client/txbuilder/stdsignmsg.go index 050a370fff..f5c78cc3f9 100644 --- a/x/auth/client/txbuilder/stdsignmsg.go +++ b/x/auth/client/txbuilder/stdsignmsg.go @@ -10,8 +10,8 @@ import ( // it is signed. For use in the CLI. type StdSignMsg struct { ChainID string `json:"chain_id"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` Fee auth.StdFee `json:"fee"` Msgs []sdk.Msg `json:"msgs"` Memo string `json:"memo"` diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 593d745abf..8c0ce232be 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -14,8 +14,8 @@ import ( // TxBuilder implements a transaction context created in SDK modules. type TxBuilder struct { Codec *codec.Codec - AccountNumber int64 - Sequence int64 + AccountNumber uint64 + Sequence uint64 Gas uint64 GasAdjustment float64 SimulateGas bool @@ -38,10 +38,10 @@ func NewTxBuilderFromCLI() TxBuilder { return TxBuilder{ ChainID: chainID, - AccountNumber: viper.GetInt64(client.FlagAccountNumber), + AccountNumber: uint64(viper.GetInt64(client.FlagAccountNumber)), Gas: client.GasFlagVar.Gas, GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment), - Sequence: viper.GetInt64(client.FlagSequence), + Sequence: uint64(viper.GetInt64(client.FlagSequence)), SimulateGas: client.GasFlagVar.Simulate, Fee: viper.GetString(client.FlagFee), Memo: viper.GetString(client.FlagMemo), @@ -73,7 +73,7 @@ func (bldr TxBuilder) WithFee(fee string) TxBuilder { } // WithSequence returns a copy of the context with an updated sequence number. -func (bldr TxBuilder) WithSequence(sequence int64) TxBuilder { +func (bldr TxBuilder) WithSequence(sequence uint64) TxBuilder { bldr.Sequence = sequence return bldr } @@ -85,7 +85,7 @@ func (bldr TxBuilder) WithMemo(memo string) TxBuilder { } // WithAccountNumber returns a copy of the context with an account number. -func (bldr TxBuilder) WithAccountNumber(accnum int64) TxBuilder { +func (bldr TxBuilder) WithAccountNumber(accnum uint64) TxBuilder { bldr.AccountNumber = accnum return bldr } diff --git a/x/auth/client/txbuilder/txbuilder_test.go b/x/auth/client/txbuilder/txbuilder_test.go index f4f9163a03..4ff472fef4 100644 --- a/x/auth/client/txbuilder/txbuilder_test.go +++ b/x/auth/client/txbuilder/txbuilder_test.go @@ -21,8 +21,8 @@ var ( func TestTxBuilderBuild(t *testing.T) { type fields struct { Codec *codec.Codec - AccountNumber int64 - Sequence int64 + AccountNumber uint64 + Sequence uint64 Gas uint64 GasAdjustment float64 SimulateGas bool diff --git a/x/auth/keeper.go b/x/auth/keeper.go index da5481749f..deecaf2b24 100644 --- a/x/auth/keeper.go +++ b/x/auth/keeper.go @@ -118,7 +118,7 @@ func (am AccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (crypto. } // Returns the Sequence of the account at address -func (am AccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (int64, sdk.Error) { +func (am AccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (uint64, sdk.Error) { acc := am.GetAccount(ctx, addr) if acc == nil { return 0, sdk.ErrUnknownAddress(addr.String()) @@ -126,7 +126,7 @@ func (am AccountKeeper) GetSequence(ctx sdk.Context, addr sdk.AccAddress) (int64 return acc.GetSequence(), nil } -func (am AccountKeeper) setSequence(ctx sdk.Context, addr sdk.AccAddress, newSequence int64) sdk.Error { +func (am AccountKeeper) setSequence(ctx sdk.Context, addr sdk.AccAddress, newSequence uint64) sdk.Error { acc := am.GetAccount(ctx, addr) if acc == nil { return sdk.ErrUnknownAddress(addr.String()) @@ -141,8 +141,8 @@ func (am AccountKeeper) setSequence(ctx sdk.Context, addr sdk.AccAddress, newSeq } // Returns and increments the global account number counter -func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) int64 { - var accNumber int64 +func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) uint64 { + var accNumber uint64 store := ctx.KVStore(am.key) bz := store.Get(globalAccountNumberKey) if bz == nil { diff --git a/x/auth/keeper_test.go b/x/auth/keeper_test.go index f1f4c7eaf9..8409a7d593 100644 --- a/x/auth/keeper_test.go +++ b/x/auth/keeper_test.go @@ -49,7 +49,7 @@ func TestAccountMapperGetSet(t *testing.T) { require.Nil(t, mapper.GetAccount(ctx, addr)) // set some values on the account and save it - newSequence := int64(20) + newSequence := uint64(20) acc.SetSequence(newSequence) mapper.SetAccount(ctx, acc) @@ -75,8 +75,8 @@ func TestAccountMapperRemoveAccount(t *testing.T) { acc1 := mapper.NewAccountWithAddress(ctx, addr1) acc2 := mapper.NewAccountWithAddress(ctx, addr2) - accSeq1 := int64(20) - accSeq2 := int64(40) + accSeq1 := uint64(20) + accSeq2 := uint64(40) acc1.SetSequence(accSeq1) acc2.SetSequence(accSeq2) diff --git a/x/auth/stdtx.go b/x/auth/stdtx.go index e8b9461fd9..2f8ae41583 100644 --- a/x/auth/stdtx.go +++ b/x/auth/stdtx.go @@ -156,16 +156,16 @@ func (fee StdFee) Bytes() []byte { // and the Sequence numbers for each signature (prevent // inchain replay and enforce tx ordering per account). type StdSignDoc struct { - AccountNumber int64 `json:"account_number"` + AccountNumber uint64 `json:"account_number"` ChainID string `json:"chain_id"` Fee json.RawMessage `json:"fee"` Memo string `json:"memo"` Msgs []json.RawMessage `json:"msgs"` - Sequence int64 `json:"sequence"` + Sequence uint64 `json:"sequence"` } // StdSignBytes returns the bytes to sign for a transaction. -func StdSignBytes(chainID string, accnum int64, sequence int64, fee StdFee, msgs []sdk.Msg, memo string) []byte { +func StdSignBytes(chainID string, accnum uint64, sequence uint64, fee StdFee, msgs []sdk.Msg, memo string) []byte { var msgsBytes []json.RawMessage for _, msg := range msgs { msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) @@ -188,8 +188,8 @@ func StdSignBytes(chainID string, accnum int64, sequence int64, fee StdFee, msgs type StdSignature struct { crypto.PubKey `json:"pub_key"` // optional Signature []byte `json:"signature"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` } // logic for standard transaction decoding diff --git a/x/auth/stdtx_test.go b/x/auth/stdtx_test.go index a3267ac192..ee65561012 100644 --- a/x/auth/stdtx_test.go +++ b/x/auth/stdtx_test.go @@ -34,8 +34,8 @@ func TestStdTx(t *testing.T) { func TestStdSignBytes(t *testing.T) { type args struct { chainID string - accnum int64 - sequence int64 + accnum uint64 + sequence uint64 fee StdFee msgs []sdk.Msg memo string @@ -85,7 +85,7 @@ func TestTxValidateBasic(t *testing.T) { require.Equal(t, sdk.CodeInsufficientFee, err.Result().Code) // require to fail validation when no signatures exist - privs, accNums, seqs := []crypto.PrivKey{}, []int64{}, []int64{} + privs, accNums, seqs := []crypto.PrivKey{}, []uint64{}, []uint64{} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) err = tx.ValidateBasic() @@ -93,7 +93,7 @@ func TestTxValidateBasic(t *testing.T) { require.Equal(t, sdk.CodeUnauthorized, err.Result().Code) // require to fail validation when signatures do not match expected signers - privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0, 1}, []int64{0, 0} + privs, accNums, seqs = []crypto.PrivKey{priv1}, []uint64{0, 1}, []uint64{0, 0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) err = tx.ValidateBasic() @@ -102,7 +102,7 @@ func TestTxValidateBasic(t *testing.T) { // require to fail validation when memo is too large badMemo := strings.Repeat("bad memo", 50) - privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0} + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} tx = newTestTxWithMemo(ctx, msgs, privs, accNums, seqs, fee, badMemo) err = tx.ValidateBasic() @@ -111,7 +111,7 @@ func TestTxValidateBasic(t *testing.T) { // require to fail validation when there are too many signatures privs = []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8} - accNums, seqs = []int64{0, 0, 0, 0, 0, 0, 0, 0}, []int64{0, 0, 0, 0, 0, 0, 0, 0} + accNums, seqs = []uint64{0, 0, 0, 0, 0, 0, 0, 0}, []uint64{0, 0, 0, 0, 0, 0, 0, 0} badMsg := newTestMsg(addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8) badMsgs := []sdk.Msg{badMsg} tx = newTestTx(ctx, badMsgs, privs, accNums, seqs, fee) @@ -121,7 +121,7 @@ func TestTxValidateBasic(t *testing.T) { require.Equal(t, sdk.CodeTooManySignatures, err.Result().Code) // require to pass when above criteria are matched - privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0} + privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []uint64{0, 1}, []uint64{0, 0} tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) err = tx.ValidateBasic() diff --git a/x/bank/app_test.go b/x/bank/app_test.go index c71a9b392b..128705b583 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -24,8 +24,8 @@ type ( expSimPass bool expPass bool msgs []sdk.Msg - accNums []int64 - accSeqs []int64 + accNums []uint64 + accSeqs []uint64 privKeys []crypto.PrivKey expectedBalances []expectedBalance } @@ -109,8 +109,8 @@ func TestMsgSendWithAccounts(t *testing.T) { testCases := []appTestCase{ { msgs: []sdk.Msg{sendMsg1}, - accNums: []int64{0}, - accSeqs: []int64{0}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, expSimPass: true, expPass: true, privKeys: []crypto.PrivKey{priv1}, @@ -121,8 +121,8 @@ func TestMsgSendWithAccounts(t *testing.T) { }, { msgs: []sdk.Msg{sendMsg1, sendMsg2}, - accNums: []int64{0}, - accSeqs: []int64{0}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, expSimPass: false, expPass: false, privKeys: []crypto.PrivKey{priv1}, @@ -140,7 +140,7 @@ func TestMsgSendWithAccounts(t *testing.T) { // bumping the tx nonce number without resigning should be an auth error mapp.BeginBlock(abci.RequestBeginBlock{}) - tx := mock.GenTx([]sdk.Msg{sendMsg1}, []int64{0}, []int64{0}, priv1) + tx := mock.GenTx([]sdk.Msg{sendMsg1}, []uint64{0}, []uint64{0}, priv1) tx.Signatures[0].Sequence = 1 res := mapp.Deliver(tx) @@ -148,7 +148,7 @@ func TestMsgSendWithAccounts(t *testing.T) { require.EqualValues(t, sdk.CodespaceRoot, res.Codespace) // resigning the tx with the bumped sequence should work - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []uint64{0}, []uint64{1}, true, true, priv1) } func TestMsgSendMultipleOut(t *testing.T) { @@ -168,8 +168,8 @@ func TestMsgSendMultipleOut(t *testing.T) { testCases := []appTestCase{ { msgs: []sdk.Msg{sendMsg2}, - accNums: []int64{0}, - accSeqs: []int64{0}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, expSimPass: true, expPass: true, privKeys: []crypto.PrivKey{priv1}, @@ -211,8 +211,8 @@ func TestSengMsgMultipleInOut(t *testing.T) { testCases := []appTestCase{ { msgs: []sdk.Msg{sendMsg3}, - accNums: []int64{0, 0}, - accSeqs: []int64{0, 0}, + accNums: []uint64{0, 0}, + accSeqs: []uint64{0, 0}, expSimPass: true, expPass: true, privKeys: []crypto.PrivKey{priv1, priv4}, @@ -247,8 +247,8 @@ func TestMsgSendDependent(t *testing.T) { testCases := []appTestCase{ { msgs: []sdk.Msg{sendMsg1}, - accNums: []int64{0}, - accSeqs: []int64{0}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, expSimPass: true, expPass: true, privKeys: []crypto.PrivKey{priv1}, @@ -259,8 +259,8 @@ func TestMsgSendDependent(t *testing.T) { }, { msgs: []sdk.Msg{sendMsg4}, - accNums: []int64{0}, - accSeqs: []int64{0}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, expSimPass: true, expPass: true, privKeys: []crypto.PrivKey{priv2}, diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index bff99da996..a3f69dcdd4 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -37,7 +37,7 @@ func BenchmarkOneBankSendTxPerBlock(b *testing.B) { // Construct genesis state mock.SetGenesis(benchmarkApp, accs) // Precompute all txs - txs := mock.GenSequenceOfTxs([]sdk.Msg{sendMsg1}, []int64{0}, []int64{int64(0)}, b.N, priv1) + txs := mock.GenSequenceOfTxs([]sdk.Msg{sendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) b.ResetTimer() // Run this with a profiler, so its easy to distinguish what time comes from // Committing, and what time comes from Check/Deliver Tx. diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index f1fd866c1f..78b1f19452 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -95,8 +95,8 @@ func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.A func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountKeeper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error { initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs)) initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs)) - AccountNumbers := make([]int64, len(msg.Inputs)) - SequenceNumbers := make([]int64, len(msg.Inputs)) + AccountNumbers := make([]uint64, len(msg.Inputs)) + SequenceNumbers := make([]uint64, len(msg.Inputs)) for i := 0; i < len(msg.Inputs); i++ { acc := mapper.GetAccount(ctx, msg.Inputs[i].Address) diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go index e59588a5ac..f59b37921b 100644 --- a/x/ibc/app_test.go +++ b/x/ibc/app_test.go @@ -70,10 +70,10 @@ func TestIBCMsgs(t *testing.T) { Sequence: 0, } - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, emptyCoins) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{1}, false, false, priv1) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{1}, false, false, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, coins) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, false, false, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, false, false, priv1) } diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 217d7cdee8..4ada207b84 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -113,7 +113,7 @@ OUTER: panic(err) } - var processed int64 + var processed uint64 if processedbz == nil { processed = 0 } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(processedbz, &processed); err != nil { @@ -127,7 +127,7 @@ OUTER: continue OUTER //TODO replace with continue (I think it should just to the correct place where OUTER is now) } - var egressLength int64 + var egressLength uint64 if egressLengthbz == nil { egressLength = 0 } else if err = c.cdc.UnmarshalBinaryLengthPrefixed(egressLengthbz, &egressLength); err != nil { @@ -166,12 +166,12 @@ func query(node string, key []byte, storeName string) (res []byte, err error) { } // nolint: unparam -func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error { +func (c relayCommander) broadcastTx(seq uint64, node string, tx []byte) error { _, err := context.NewCLIContext().WithNodeURI(node).BroadcastTx(tx) return err } -func (c relayCommander) getSequence(node string) int64 { +func (c relayCommander) getSequence(node string) uint64 { res, err := query(node, c.address, c.accStore) if err != nil { panic(err) @@ -189,7 +189,7 @@ func (c relayCommander) getSequence(node string) int64 { return 0 } -func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []byte { +func (c relayCommander) refine(bz []byte, sequence uint64, passphrase string) []byte { var packet ibc.IBCPacket if err := c.cdc.UnmarshalBinaryLengthPrefixed(bz, &packet); err != nil { panic(err) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 94c3c7a2ec..2fa24a6c7c 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -90,11 +90,11 @@ func TestIBC(t *testing.T) { var msg sdk.Msg var res sdk.Result - var egl int64 - var igs int64 + var egl uint64 + var igs uint64 egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, int64(0)) + require.Equal(t, egl, uint64(0)) msg = IBCTransferMsg{ IBCPacket: packet, @@ -107,10 +107,10 @@ func TestIBC(t *testing.T) { require.Equal(t, zero, coins) egl = ibcm.getEgressLength(store, chainid) - require.Equal(t, egl, int64(1)) + require.Equal(t, egl, uint64(1)) igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(0)) + require.Equal(t, igs, uint64(0)) msg = IBCReceiveMsg{ IBCPacket: packet, @@ -125,11 +125,11 @@ func TestIBC(t *testing.T) { require.Equal(t, mycoins, coins) igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(1)) + require.Equal(t, igs, uint64(1)) res = h(ctx, msg) require.False(t, res.IsOK()) igs = ibcm.GetIngressSequence(ctx, chainid) - require.Equal(t, igs, int64(1)) + require.Equal(t, igs, uint64(1)) } diff --git a/x/ibc/mapper.go b/x/ibc/mapper.go index 957ab191a8..101fac0339 100644 --- a/x/ibc/mapper.go +++ b/x/ibc/mapper.go @@ -76,7 +76,7 @@ func unmarshalBinaryPanic(cdc *codec.Codec, bz []byte, ptr interface{}) { } // TODO add description -func (ibcm Mapper) GetIngressSequence(ctx sdk.Context, srcChain string) int64 { +func (ibcm Mapper) GetIngressSequence(ctx sdk.Context, srcChain string) uint64 { store := ctx.KVStore(ibcm.key) key := IngressSequenceKey(srcChain) @@ -87,13 +87,13 @@ func (ibcm Mapper) GetIngressSequence(ctx sdk.Context, srcChain string) int64 { return 0 } - var res int64 + var res uint64 unmarshalBinaryPanic(ibcm.cdc, bz, &res) return res } // TODO add description -func (ibcm Mapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence int64) { +func (ibcm Mapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence uint64) { store := ctx.KVStore(ibcm.key) key := IngressSequenceKey(srcChain) @@ -102,20 +102,20 @@ func (ibcm Mapper) SetIngressSequence(ctx sdk.Context, srcChain string, sequence } // Retrieves the index of the currently stored outgoing IBC packets. -func (ibcm Mapper) getEgressLength(store sdk.KVStore, destChain string) int64 { +func (ibcm Mapper) getEgressLength(store sdk.KVStore, destChain string) uint64 { bz := store.Get(EgressLengthKey(destChain)) if bz == nil { zero := marshalBinaryPanic(ibcm.cdc, int64(0)) store.Set(EgressLengthKey(destChain), zero) return 0 } - var res int64 + var res uint64 unmarshalBinaryPanic(ibcm.cdc, bz, &res) return res } // Stores an outgoing IBC packet under "egress/chain_id/index". -func EgressKey(destChain string, index int64) []byte { +func EgressKey(destChain string, index uint64) []byte { return []byte(fmt.Sprintf("egress/%s/%d", destChain, index)) } diff --git a/x/ibc/types.go b/x/ibc/types.go index 72dae0d389..0f596bb035 100644 --- a/x/ibc/types.go +++ b/x/ibc/types.go @@ -96,7 +96,7 @@ func (msg IBCTransferMsg) ValidateBasic() sdk.Error { type IBCReceiveMsg struct { IBCPacket Relayer sdk.AccAddress - Sequence int64 + Sequence uint64 } // nolint @@ -112,7 +112,7 @@ func (msg IBCReceiveMsg) GetSignBytes() []byte { b, err := msgCdc.MarshalJSON(struct { IBCPacket json.RawMessage Relayer sdk.AccAddress - Sequence int64 + Sequence uint64 }{ IBCPacket: json.RawMessage(msg.IBCPacket.GetSignBytes()), Relayer: msg.Relayer, diff --git a/x/mock/app.go b/x/mock/app.go index 0b7b0ae16b..6382ba05bf 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -185,7 +185,7 @@ func SetGenesis(app *App, accs []auth.Account) { } // GenTx generates a signed mock transaction. -func GenTx(msgs []sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKey) auth.StdTx { +func GenTx(msgs []sdk.Msg, accnums []uint64, seq []uint64, priv ...crypto.PrivKey) auth.StdTx { // Make the transaction free fee := auth.StdFee{ Amount: sdk.Coins{sdk.NewInt64Coin("foocoin", 0)}, @@ -304,7 +304,7 @@ func GetAllAccounts(mapper auth.AccountKeeper, ctx sdk.Context) []auth.Account { // GenSequenceOfTxs generates a set of signed transactions of messages, such // that they differ only by having the sequence numbers incremented between // every transaction. -func GenSequenceOfTxs(msgs []sdk.Msg, accnums []int64, initSeqNums []int64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx { +func GenSequenceOfTxs(msgs []sdk.Msg, accnums []uint64, initSeqNums []uint64, numToGenerate int, priv ...crypto.PrivKey) []auth.StdTx { txs := make([]auth.StdTx, numToGenerate, numToGenerate) for i := 0; i < numToGenerate; i++ { txs[i] = GenTx(msgs, accnums, initSeqNums, priv...) @@ -314,7 +314,7 @@ func GenSequenceOfTxs(msgs []sdk.Msg, accnums []int64, initSeqNums []int64, numT return txs } -func incrementAllSequenceNumbers(initSeqNums []int64) { +func incrementAllSequenceNumbers(initSeqNums []uint64) { for i := 0; i < len(initSeqNums); i++ { initSeqNums[i]++ } diff --git a/x/mock/app_test.go b/x/mock/app_test.go index c47ddb7179..1af0e4a033 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -61,14 +61,14 @@ func TestCheckAndDeliverGenTx(t *testing.T) { SignCheckDeliver( t, mApp.BaseApp, []sdk.Msg{msg}, - []int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()}, + []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, true, true, privKeys[0], ) // Signing a tx with the wrong privKey should result in an auth error res := SignCheckDeliver( t, mApp.BaseApp, []sdk.Msg{msg}, - []int64{accs[1].GetAccountNumber()}, []int64{accs[1].GetSequence() + 1}, + []uint64{accs[1].GetAccountNumber()}, []uint64{accs[1].GetSequence() + 1}, true, false, privKeys[1], ) @@ -78,7 +78,7 @@ func TestCheckAndDeliverGenTx(t *testing.T) { // Resigning the tx with the correct privKey should result in an OK result SignCheckDeliver( t, mApp.BaseApp, []sdk.Msg{msg}, - []int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence() + 1}, + []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence() + 1}, true, true, privKeys[0], ) } @@ -92,14 +92,14 @@ func TestCheckGenTx(t *testing.T) { msg1 := testMsg{signers: []sdk.AccAddress{addrs[0]}, positiveNum: 1} CheckGenTx( t, mApp.BaseApp, []sdk.Msg{msg1}, - []int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()}, + []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, true, privKeys[0], ) msg2 := testMsg{signers: []sdk.AccAddress{addrs[0]}, positiveNum: -1} CheckGenTx( t, mApp.BaseApp, []sdk.Msg{msg2}, - []int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()}, + []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, false, privKeys[0], ) } diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go index 130339d0e7..4d5fe05f33 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -50,8 +50,8 @@ func CheckBalance(t *testing.T, app *App, addr sdk.AccAddress, exp sdk.Coins) { // compared against the parameter 'expPass'. A test assertion is made using the // parameter 'expPass' against the result. A corresponding result is returned. func CheckGenTx( - t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64, - seq []int64, expPass bool, priv ...crypto.PrivKey, + t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []uint64, + seq []uint64, expPass bool, priv ...crypto.PrivKey, ) sdk.Result { tx := GenTx(msgs, accNums, seq, priv...) res := app.Check(tx) @@ -70,8 +70,8 @@ func CheckGenTx( // the parameter 'expPass' against the result. A corresponding result is // returned. func SignCheckDeliver( - t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64, - seq []int64, expSimPass, expPass bool, priv ...crypto.PrivKey, + t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []uint64, + seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, ) sdk.Result { tx := GenTx(msgs, accNums, seq, priv...) // Must simulate now as CheckTx doesn't run Msgs anymore diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index bfcbcba0b2..279844a4ce 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -111,7 +111,7 @@ func TestSlashingMsgs(t *testing.T) { createValidatorMsg := stake.NewMsgCreateValidator( sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission, ) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)}) mapp.BeginBlock(abci.RequestBeginBlock{}) @@ -125,7 +125,7 @@ func TestSlashingMsgs(t *testing.T) { checkValidatorSigningInfo(t, mapp, keeper, sdk.ConsAddress(addr1), false) // unjail should fail with unknown validator - res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, false, priv1) + res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []uint64{0}, []uint64{1}, false, false, priv1) require.EqualValues(t, CodeValidatorNotJailed, res.Code) require.EqualValues(t, DefaultCodespace, res.Codespace) } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index 2866acf1d1..f6c887c291 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -125,7 +125,7 @@ func TestStakeMsgs(t *testing.T) { sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionMsg, ) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin)}) mApp.BeginBlock(abci.RequestBeginBlock{}) @@ -139,7 +139,7 @@ func TestStakeMsgs(t *testing.T) { addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg, ) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 0}, []int64{1, 0}, true, true, priv1, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []uint64{0, 0}, []uint64{1, 0}, true, true, priv1, priv2) mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)}) mApp.BeginBlock(abci.RequestBeginBlock{}) @@ -155,7 +155,7 @@ func TestStakeMsgs(t *testing.T) { description = NewDescription("bar_moniker", "", "", "") editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, true, priv1) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{2}, true, true, priv1) validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) require.Equal(t, description, validator.Description) @@ -163,13 +163,13 @@ func TestStakeMsgs(t *testing.T) { mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{0}, []int64{1}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{1}, true, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10)) // begin unbonding beginUnbondingMsg := NewMsgBeginUnbonding(addr2, sdk.ValAddress(addr1), sdk.NewDec(10)) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{0}, []int64{2}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{2}, true, true, priv2) // delegation should exist anymore checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) From 4c36b0fe05f1c649d591e110cf9a302fc588f9f5 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 26 Nov 2018 11:50:33 +0000 Subject: [PATCH 48/51] Merge PR #2881: Don't call gaiacli tx sign. Use utils.SignStdTx() instead. This is to avoid command redirection and reduce the use of viper's global variables. Closes: #2875 --- PENDING.md | 1 + cmd/gaia/cli_test/cli_test.go | 13 +++++-- cmd/gaia/init/gentx.go | 73 +++++++++++++++++++++++++---------- x/auth/client/cli/sign.go | 2 +- 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/PENDING.md b/PENDING.md index 05a4988eb0..a40406d3ab 100644 --- a/PENDING.md +++ b/PENDING.md @@ -11,6 +11,7 @@ BREAKING CHANGES * [cli] [\#2829](https://github.com/cosmos/cosmos-sdk/pull/2829) add-genesis-account command now validates state when adding accounts * [cli] [\#2804](https://github.com/cosmos/cosmos-sdk/issues/2804) Check whether key exists before passing it on to `tx create-validator`. * [cli] [\#2874](https://github.com/cosmos/cosmos-sdk/pull/2874) `gaiacli tx sign` takes an optional `--output-document` flag to support output redirection. + * [cli] [\#2875](https://github.com/cosmos/cosmos-sdk/pull/2875) Refactor `gaiad gentx` and avoid redirection to `gaiacli tx sign` for tx signing. * Gaia diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 8ad32870f4..f2c2e4ba1f 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -606,10 +606,11 @@ func getTestingHomeDirs() (string, string) { func initializeFixtures(t *testing.T) (chainID, servAddr, port string) { tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") + os.RemoveAll(filepath.Join(gaiadHome, "config", "gentx")) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass) executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass) fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf( "gaiacli keys show foo --output=json --home=%s", gaiacliHome)) chainID = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) @@ -623,10 +624,10 @@ func initializeFixtures(t *testing.T) (chainID, servAddr, port string) { require.NoError(t, err) genDoc.AppState = appStateJSON genDoc.SaveAs(genFile) - executeWrite(t, fmt.Sprintf( + executeWriteCheckErr(t, fmt.Sprintf( "gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome), app.DefaultKeyPass) - executeWrite(t, fmt.Sprintf("gaiad collect-gentxs --home=%s", gaiadHome), app.DefaultKeyPass) + executeWriteCheckErr(t, fmt.Sprintf("gaiad collect-gentxs --home=%s", gaiadHome), app.DefaultKeyPass) // get a free port, also setup some common flags servAddr, port, err = server.FreeTCPAddr() require.NoError(t, err) @@ -662,6 +663,10 @@ func readGenesisFile(t *testing.T, genFile string) types.GenesisDoc { //___________________________________________________________________________________ // executors +func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) { + require.True(t, executeWrite(t, cmdStr, writes...)) +} + func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) { exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) return diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 91dfbd2e16..b8392d3bd2 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -1,7 +1,9 @@ package init import ( + "bytes" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -17,7 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -71,7 +73,9 @@ following delegation and commission default parameters: if err != nil { return err } - if _, err = kb.Get(viper.GetString(client.FlagName)); err != nil { + + name := viper.GetString(client.FlagName) + if _, err := kb.Get(name); err != nil { return err } @@ -84,34 +88,40 @@ following delegation and commission default parameters: } // Run gaiad tx create-validator prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) - cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg( - context.NewCLIContext().WithCodec(cdc), - authtxb.NewTxBuilderFromCLI().WithCodec(cdc), - ) + txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) + cliCtx := context.NewCLIContext().WithCodec(cdc) + cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { return err } - w, err := ioutil.TempFile("", "gentx") - if err != nil { - return err - } - unsignedGenTxFilename := w.Name() - defer os.Remove(unsignedGenTxFilename) - + // write the unsigned transaction to the buffer + w := bytes.NewBuffer([]byte{}) if err := utils.PrintUnsignedStdTx(w, txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil { return err } - prepareFlagsForTxSign() - signCmd := authcmd.GetSignCommand(cdc) + // read the transaction + stdTx, err := readUnsignedGenTxFile(cdc, w) + if err != nil { + return err + } + + // sign the transaction and write it to the output file + signedTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, false, true) + if err != nil { + return err + } outputDocument, err := makeOutputFilepath(config.RootDir, nodeID) if err != nil { return err } - viper.Set("output-document", outputDocument) - return signCmd.RunE(nil, []string{unsignedGenTxFilename}) + if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument) + return nil }, } @@ -152,10 +162,6 @@ func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID st } } -func prepareFlagsForTxSign() { - viper.Set("offline", true) -} - func makeOutputFilepath(rootDir, nodeID string) (string, error) { writePath := filepath.Join(rootDir, "config", "gentx") if err := common.EnsureDir(writePath, 0700); err != nil { @@ -163,3 +169,28 @@ func makeOutputFilepath(rootDir, nodeID string) (string, error) { } return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil } + +func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) { + var stdTx auth.StdTx + bytes, err := ioutil.ReadAll(r) + if err != nil { + return stdTx, err + } + err = cdc.UnmarshalJSON(bytes, &stdTx) + return stdTx, err +} + +// nolint: errcheck +func writeSignedGenTx(cdc *codec.Codec, outputDocument string, tx auth.StdTx) error { + outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer outputFile.Close() + json, err := cdc.MarshalJSON(tx) + if err != nil { + return err + } + _, err = fmt.Fprintf(outputFile, "%s\n", json) + return err +} diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 6fc57ac166..73facdfd93 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -118,7 +118,7 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error fp, err := os.OpenFile( viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644, - ) + ) if err != nil { return err } From 7cb1ba625e7479d3b9382505e60cb33a1fe87e1f Mon Sep 17 00:00:00 2001 From: frog power 4000 Date: Mon, 26 Nov 2018 07:13:47 -0500 Subject: [PATCH 49/51] blockly minting (#2825) * update mechanism to use average block time * correctly sets accum height for zero-delegations * update Decimal Format() * clip withdrawal tokens * PositiveDelegationInvariant * DelegatorSharesInvariant * DelAccumInvariants --- PENDING.md | 2 + cmd/gaia/app/sim_test.go | 36 ++++++----- docs/spec/mint/begin_block.md | 42 ++++++++++--- docs/spec/mint/state.md | 6 +- types/coin.go | 2 +- types/decimal.go | 19 ++++++ x/distribution/keeper/hooks.go | 5 ++ x/distribution/keeper/test_common.go | 22 ++++++- x/distribution/keeper/validator.go | 7 +++ x/distribution/simulation/invariants.go | 82 +++++++++++++++++++++++++ x/distribution/types/dec_coin.go | 19 +++++- x/distribution/types/delegator_info.go | 52 +++++++++++++++- x/mint/abci_app.go | 19 +++--- x/mint/genesis.go | 2 +- x/mint/minter.go | 70 +++++++++++++-------- x/mint/minter_test.go | 52 +++++++++++++++- x/mint/params.go | 16 +++++ x/stake/keeper/delegation.go | 4 ++ x/stake/keeper/sdk_types.go | 4 +- x/stake/simulation/invariants.go | 57 +++++++++++++++++ x/stake/types/errors.go | 4 ++ 21 files changed, 453 insertions(+), 69 deletions(-) diff --git a/PENDING.md b/PENDING.md index a40406d3ab..59a8420b55 100644 --- a/PENDING.md +++ b/PENDING.md @@ -14,6 +14,7 @@ BREAKING CHANGES * [cli] [\#2875](https://github.com/cosmos/cosmos-sdk/pull/2875) Refactor `gaiad gentx` and avoid redirection to `gaiacli tx sign` for tx signing. * Gaia + * [mint] [\#2825] minting now occurs every block, inflation parameter updates still hourly * SDK * [\#2752](https://github.com/cosmos/cosmos-sdk/pull/2752) Don't hardcode bondable denom. @@ -74,6 +75,7 @@ IMPROVEMENTS - #2821 Codespaces are now strings - #2779 Introduce `ValidateBasic` to the `Tx` interface and call it in the ante handler. + - #2825 More staking and distribution invariants * Tendermint - #2796 Update to go-amino 0.14.1 diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 70ae8e12af..bd800b81d7 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -59,7 +59,9 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { if numInitiallyBonded > numAccs { numInitiallyBonded = numAccs } - fmt.Printf("Selected randomly generated parameters for simulated genesis: {amount of steak per account: %v, initially bonded validators: %v}\n", amount, numInitiallyBonded) + fmt.Printf("Selected randomly generated parameters for simulated genesis:\n"+ + "\t{amount of steak per account: %v, initially bonded validators: %v}\n", + amount, numInitiallyBonded) // Randomly generate some genesis accounts for _, acc := range accs { @@ -86,7 +88,8 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { GovernancePenalty: sdk.NewDecWithPrec(1, 2), }, } - fmt.Printf("Selected randomly generated governance parameters: %+v\n", govGenesis) + fmt.Printf("Selected randomly generated governance parameters:\n\t%+v\n", govGenesis) + stakeGenesis := stake.GenesisState{ Pool: stake.InitialPool(), Params: stake.Params{ @@ -95,7 +98,8 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { BondDenom: stakeTypes.DefaultBondDenom, }, } - fmt.Printf("Selected randomly generated staking parameters: %+v\n", stakeGenesis) + fmt.Printf("Selected randomly generated staking parameters:\n\t%+v\n", stakeGenesis) + slashingGenesis := slashing.GenesisState{ Params: slashing.Params{ MaxEvidenceAge: stakeGenesis.Params.UnbondingTime, @@ -107,21 +111,21 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))), }, } - fmt.Printf("Selected randomly generated slashing parameters: %+v\n", slashingGenesis) + fmt.Printf("Selected randomly generated slashing parameters:\n\t%+v\n", slashingGenesis) + mintGenesis := mint.GenesisState{ - Minter: mint.Minter{ - InflationLastTime: time.Unix(0, 0), - Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), - }, - Params: mint.Params{ - MintDenom: stakeTypes.DefaultBondDenom, - InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), - InflationMax: sdk.NewDecWithPrec(20, 2), - InflationMin: sdk.NewDecWithPrec(7, 2), - GoalBonded: sdk.NewDecWithPrec(67, 2), - }, + Minter: mint.InitialMinter( + sdk.NewDecWithPrec(int64(r.Intn(99)), 2)), + Params: mint.NewParams( + stakeTypes.DefaultBondDenom, + sdk.NewDecWithPrec(int64(r.Intn(99)), 2), + sdk.NewDecWithPrec(20, 2), + sdk.NewDecWithPrec(7, 2), + sdk.NewDecWithPrec(67, 2), + uint64(60*60*8766/5)), } - fmt.Printf("Selected randomly generated minting parameters: %v\n", mintGenesis) + fmt.Printf("Selected randomly generated minting parameters:\n\t%+v\n", mintGenesis) + var validators []stake.Validator var delegations []stake.Delegation diff --git a/docs/spec/mint/begin_block.md b/docs/spec/mint/begin_block.md index 7588db38b1..9207141472 100644 --- a/docs/spec/mint/begin_block.md +++ b/docs/spec/mint/begin_block.md @@ -1,18 +1,18 @@ # Begin-Block -## Inflation +Inflation occurs at the beginning of each block, however minting parameters +are only calculated once per hour. -Inflation occurs at the beginning of each block. +## NextInflationRate -### NextInflation +The target annual inflation rate is recalculated at the first block of each new +hour. The inflation is also subject to a rate change (positive or negative) +depending on the distance from the desired ratio (67%). The maximum rate change +possible is defined to be 13% per year, however the annual inflation is capped +as between 7% and 20%. -The target annual inflation rate is recalculated for each provisions cycle. The -inflation is also subject to a rate change (positive or negative) depending on -the distance from the desired ratio (67%). The maximum rate change possible is -defined to be 13% per year, however the annual inflation is capped as between -7% and 20%. - -NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) { +``` +NextInflationRate(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) { inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange inflationRateChange = inflationRateChangePerYear/hrsPerYr @@ -26,3 +26,25 @@ NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) { } return inflation +``` + +## NextAnnualProvisions + +Calculate the annual provisions based on current total supply and inflation +rate. This parameter is calculated once per block. + +``` +NextAnnualProvisions(params Params, totalSupply sdk.Dec) (provisions sdk.Dec) { + return Inflation * totalSupply +``` + +## BlockProvision + +Calculate the provisions generated for each block based on current +annual provisions + +``` +BlockProvision(params Params) sdk.Coin { + provisionAmt = AnnualProvisions/ params.BlocksPerYear + return sdk.NewCoin(params.MintDenom, provisionAmt.Truncate()) +``` diff --git a/docs/spec/mint/state.md b/docs/spec/mint/state.md index 98e8e63dd2..c3133296ea 100644 --- a/docs/spec/mint/state.md +++ b/docs/spec/mint/state.md @@ -8,8 +8,9 @@ The minter is a space for holding current inflation information. ```golang type Minter struct { - InflationLastTime time.Time // block time which the last inflation was processed - Inflation sdk.Dec // current annual inflation rate + LastUpdate time.Time // time which the last update was made to the minter + Inflation sdk.Dec // current annual inflation rate + AnnualProvisions sdk.Dec // current annual exptected provisions } ``` @@ -26,6 +27,7 @@ type Params struct { InflationMax sdk.Dec // maximum inflation rate InflationMin sdk.Dec // minimum inflation rate GoalBonded sdk.Dec // goal of percent bonded atoms + BlocksPerYear uint64 // expected blocks per year } ``` diff --git a/types/coin.go b/types/coin.go index 5f9020e84e..9742545324 100644 --- a/types/coin.go +++ b/types/coin.go @@ -28,7 +28,7 @@ type Coin struct { // the amount is negative. func NewCoin(denom string, amount Int) Coin { if amount.LT(ZeroInt()) { - panic("negative coin amount") + panic(fmt.Sprintf("negative coin amount: %v\n", amount)) } return Coin{ diff --git a/types/decimal.go b/types/decimal.go index 3c0e89f60b..0d843f6cb7 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -172,10 +172,21 @@ func NewDecFromStr(str string) (d Dec, err Error) { return Dec{combined}, nil } +// Decimal from string, panic on error +func MustNewDecFromStr(s string) Dec { + dec, err := NewDecFromStr(s) + if err != nil { + panic(err) + } + return dec +} + //______________________________________________________________________________________________ //nolint func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero +func (d Dec) IsNegative() bool { return (d.Int).Sign() == -1 } // is negative +func (d Dec) IsPositive() bool { return (d.Int).Sign() == 1 } // is positive func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal @@ -252,6 +263,14 @@ func (d Dec) IsInteger() bool { return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 } +// format decimal state +func (d Dec) Format(s fmt.State, verb rune) { + _, err := s.Write([]byte(d.String())) + if err != nil { + panic(err) + } +} + func (d Dec) String() string { bz, err := d.Int.MarshalText() if err != nil { diff --git a/x/distribution/keeper/hooks.go b/x/distribution/keeper/hooks.go index a4f4353fa7..b531a40429 100644 --- a/x/distribution/keeper/hooks.go +++ b/x/distribution/keeper/hooks.go @@ -10,6 +10,11 @@ import ( // Create a new validator distribution record func (k Keeper) onValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { + // defensive check for existence + if k.HasValidatorDistInfo(ctx, valAddr) { + panic("validator dist info already exists (not cleaned up properly)") + } + height := ctx.BlockHeight() vdi := types.ValidatorDistInfo{ OperatorAddr: valAddr, diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 660abbd0e0..26033432fc 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -162,7 +162,8 @@ func (fck DummyFeeCollectionKeeper) ClearCollectedFees(_ sdk.Context) { //__________________________________________________________________________________ // used in simulation -// iterate over all the validator distribution infos (inefficient, just used to check invariants) +// iterate over all the validator distribution infos (inefficient, just used to +// check invariants) func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) { @@ -179,3 +180,22 @@ func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, index++ } } + +// iterate over all the delegation distribution infos (inefficient, just used +// to check invariants) +func (k Keeper) IterateDelegationDistInfos(ctx sdk.Context, + fn func(index int64, distInfo types.DelegationDistInfo) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) + defer iter.Close() + index := int64(0) + for ; iter.Valid(); iter.Next() { + var ddi types.DelegationDistInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &ddi) + if fn(index, ddi) { + return + } + index++ + } +} diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index d2c755cfa6..119622edd2 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -36,6 +36,13 @@ func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInf // remove a validator distribution info func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) { + + // defensive check + vdi := k.GetValidatorDistInfo(ctx, valAddr) + if vdi.DelAccum.Accum.IsPositive() { + panic("Should not delete validator with unwithdrawn delegator accum") + } + store := ctx.KVStore(k.storeKey) store.Delete(GetValidatorDistInfoKey(valAddr)) } diff --git a/x/distribution/simulation/invariants.go b/x/distribution/simulation/invariants.go index 75863bfdb4..128e7faa9a 100644 --- a/x/distribution/simulation/invariants.go +++ b/x/distribution/simulation/invariants.go @@ -18,6 +18,10 @@ func AllInvariants(d distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { if err != nil { return err } + err = DelAccumInvariants(d, sk)(app) + if err != nil { + return err + } return nil } } @@ -48,3 +52,81 @@ func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invaria return nil } } + +// DelAccumInvariants checks that each validator del accum == sum all delegators' accum +func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { + + return func(app *baseapp.BaseApp) error { + mockHeader := abci.Header{Height: app.LastBlockHeight() + 1} + ctx := app.NewContext(false, mockHeader) + height := ctx.BlockHeight() + + totalDelAccumFromVal := make(map[string]sdk.Dec) // key is the valOpAddr string + totalDelAccum := make(map[string]sdk.Dec) + + // iterate the validators + iterVal := func(_ int64, vdi distr.ValidatorDistInfo) bool { + key := vdi.OperatorAddr.String() + validator := sk.Validator(ctx, vdi.OperatorAddr) + totalDelAccumFromVal[key] = vdi.GetTotalDelAccum(height, + validator.GetDelegatorShares()) + + // also initialize the delegation map + totalDelAccum[key] = sdk.ZeroDec() + + return false + } + k.IterateValidatorDistInfos(ctx, iterVal) + + // iterate the delegations + iterDel := func(_ int64, ddi distr.DelegationDistInfo) bool { + key := ddi.ValOperatorAddr.String() + delegation := sk.Delegation(ctx, ddi.DelegatorAddr, ddi.ValOperatorAddr) + totalDelAccum[key] = totalDelAccum[key].Add( + ddi.GetDelAccum(height, delegation.GetShares())) + return false + } + k.IterateDelegationDistInfos(ctx, iterDel) + + // compare + for key, delAccumFromVal := range totalDelAccumFromVal { + sumDelAccum := totalDelAccum[key] + + if !sumDelAccum.Equal(delAccumFromVal) { + + logDelAccums := "" + iterDel := func(_ int64, ddi distr.DelegationDistInfo) bool { + keyLog := ddi.ValOperatorAddr.String() + if keyLog == key { + delegation := sk.Delegation(ctx, ddi.DelegatorAddr, ddi.ValOperatorAddr) + accum := ddi.GetDelAccum(height, delegation.GetShares()) + if accum.IsPositive() { + logDelAccums += fmt.Sprintf("\n\t\tdel: %v, accum: %v", + ddi.DelegatorAddr.String(), + accum.String()) + } + } + return false + } + k.IterateDelegationDistInfos(ctx, iterDel) + + operAddr, err := sdk.ValAddressFromBech32(key) + if err != nil { + panic(err) + } + validator := sk.Validator(ctx, operAddr) + + return fmt.Errorf("delegator accum invariance: \n"+ + "\tvalidator key: %v\n"+ + "\tvalidator: %+v\n"+ + "\tsum delegator accum: %v\n"+ + "\tvalidator's total delegator accum: %v\n"+ + "\tlog of delegations with accum: %v\n", + key, validator, sumDelAccum.String(), + delAccumFromVal.String(), logDelAccums) + } + } + + return nil + } +} diff --git a/x/distribution/types/dec_coin.go b/x/distribution/types/dec_coin.go index 5eedad7e36..c5d9f360ad 100644 --- a/x/distribution/types/dec_coin.go +++ b/x/distribution/types/dec_coin.go @@ -20,6 +20,13 @@ func NewDecCoin(denom string, amount int64) DecCoin { } } +func NewDecCoinFromDec(denom string, amount sdk.Dec) DecCoin { + return DecCoin{ + Denom: denom, + Amount: amount, + } +} + func NewDecCoinFromCoin(coin sdk.Coin) DecCoin { return DecCoin{ Denom: coin.Denom, @@ -140,7 +147,7 @@ func (coins DecCoins) MulDec(d sdk.Dec) DecCoins { return res } -// divide all the coins by a multiple +// divide all the coins by a decimal func (coins DecCoins) QuoDec(d sdk.Dec) DecCoins { res := make([]DecCoin, len(coins)) for i, coin := range coins { @@ -176,3 +183,13 @@ func (coins DecCoins) AmountOf(denom string) sdk.Dec { } } } + +// returns the amount of a denom from deccoins +func (coins DecCoins) HasNegative() bool { + for _, coin := range coins { + if coin.Amount.IsNegative() { + return true + } + } + return false +} diff --git a/x/distribution/types/delegator_info.go b/x/distribution/types/delegator_info.go index decf321298..6068752f21 100644 --- a/x/distribution/types/delegator_info.go +++ b/x/distribution/types/delegator_info.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -24,7 +26,17 @@ func NewDelegationDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.Val // Get the calculated accum of this delegator at the provided height func (di DelegationDistInfo) GetDelAccum(height int64, delegatorShares sdk.Dec) sdk.Dec { blocks := height - di.DelPoolWithdrawalHeight - return delegatorShares.MulInt(sdk.NewInt(blocks)) + accum := delegatorShares.MulInt(sdk.NewInt(blocks)) + + // defensive check + if accum.IsNegative() { + panic(fmt.Sprintf("negative accum: %v\n"+ + "\theight: %v\n"+ + "\tdelegation_dist_info: %v\n"+ + "\tdelegator_shares: %v\n", + accum.String(), height, di, delegatorShares)) + } + return accum } // Withdraw rewards from delegator. @@ -41,7 +53,9 @@ func (di DelegationDistInfo) WithdrawRewards(wc WithdrawContext, vi ValidatorDis fp := wc.FeePool vi = vi.UpdateTotalDelAccum(wc.Height, totalDelShares) + // Break out to prevent a divide by zero. if vi.DelAccum.Accum.IsZero() { + di.DelPoolWithdrawalHeight = wc.Height return di, vi, fp, DecCoins{} } @@ -49,9 +63,43 @@ func (di DelegationDistInfo) WithdrawRewards(wc WithdrawContext, vi ValidatorDis accum := di.GetDelAccum(wc.Height, delegatorShares) di.DelPoolWithdrawalHeight = wc.Height + withdrawalTokens := vi.DelPool.MulDec(accum).QuoDec(vi.DelAccum.Accum) - vi.DelPool = vi.DelPool.Minus(withdrawalTokens) + // Clip withdrawal tokens by pool, due to possible rounding errors. + // This rounding error may be introduced upon multiplication since + // we're clipping decimal digits, and then when we divide by a number ~1 or + // < 1, the error doesn't get "buried", and if << 1 it'll get amplified. + // more: https://github.com/cosmos/cosmos-sdk/issues/2888#issuecomment-441387987 + for i, decCoin := range withdrawalTokens { + poolDenomAmount := vi.DelPool.AmountOf(decCoin.Denom) + if decCoin.Amount.GT(poolDenomAmount) { + withdrawalTokens[i] = NewDecCoinFromDec(decCoin.Denom, poolDenomAmount) + } + } + + // defensive check for impossible accum ratios + if accum.GT(vi.DelAccum.Accum) { + panic(fmt.Sprintf("accum > vi.DelAccum.Accum:\n"+ + "\taccum\t\t\t%v\n"+ + "\tvi.DelAccum.Accum\t%v\n", + accum, vi.DelAccum.Accum)) + } + + remDelPool := vi.DelPool.Minus(withdrawalTokens) + + // defensive check + if remDelPool.HasNegative() { + panic(fmt.Sprintf("negative remDelPool: %v\n"+ + "\tvi.DelPool\t\t%v\n"+ + "\taccum\t\t\t%v\n"+ + "\tvi.DelAccum.Accum\t%v\n"+ + "\twithdrawalTokens\t%v\n", + remDelPool, vi.DelPool, accum, + vi.DelAccum.Accum, withdrawalTokens)) + } + + vi.DelPool = remDelPool vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum) return di, vi, fp, withdrawalTokens diff --git a/x/mint/abci_app.go b/x/mint/abci_app.go index 73491f8081..963039961a 100644 --- a/x/mint/abci_app.go +++ b/x/mint/abci_app.go @@ -6,21 +6,26 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Called every block, process inflation on the first block of every hour +// Inflate every block, update inflation parameters once per hour func BeginBlocker(ctx sdk.Context, k Keeper) { blockTime := ctx.BlockHeader().Time minter := k.GetMinter(ctx) - if blockTime.Sub(minter.InflationLastTime) < time.Hour { // only mint on the hour! + params := k.GetParams(ctx) + + mintedCoin := minter.BlockProvision(params) + k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin}) + k.sk.InflateSupply(ctx, sdk.NewDecFromInt(mintedCoin.Amount)) + + if blockTime.Sub(minter.LastUpdate) < time.Hour { return } - params := k.GetParams(ctx) + // adjust the inflation, hourly-provision rate every hour totalSupply := k.sk.TotalPower(ctx) bondedRatio := k.sk.BondedRatio(ctx) - minter.InflationLastTime = blockTime - minter, mintedCoin := minter.ProcessProvisions(params, totalSupply, bondedRatio) - k.fck.AddCollectedFees(ctx, sdk.Coins{mintedCoin}) - k.sk.InflateSupply(ctx, sdk.NewDecFromInt(mintedCoin.Amount)) + minter.Inflation = minter.NextInflationRate(params, bondedRatio) + minter.AnnualProvisions = minter.NextAnnualProvisions(params, totalSupply) + minter.LastUpdate = blockTime k.SetMinter(ctx, minter) } diff --git a/x/mint/genesis.go b/x/mint/genesis.go index ce375d71e5..27615ed7ee 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -20,7 +20,7 @@ func NewGenesisState(minter Minter, params Params) GenesisState { // get raw genesis raw message for testing func DefaultGenesisState() GenesisState { return GenesisState{ - Minter: InitialMinter(), + Minter: DefaultInitialMinter(), Params: DefaultParams(), } } diff --git a/x/mint/minter.go b/x/mint/minter.go index 135675887b..2edf1dabea 100644 --- a/x/mint/minter.go +++ b/x/mint/minter.go @@ -7,45 +7,54 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// current inflation state +// Minter represents the minting state type Minter struct { - InflationLastTime time.Time `json:"inflation_last_time"` // block time which the last inflation was processed - Inflation sdk.Dec `json:"inflation"` // current annual inflation rate + LastUpdate time.Time `json:"last_update"` // time which the last update was made to the minter + Inflation sdk.Dec `json:"inflation"` // current annual inflation rate + AnnualProvisions sdk.Dec `json:"annual_provisions"` // current annual expected provisions } -// minter object for a new minter -func InitialMinter() Minter { +// Create a new minter object +func NewMinter(lastUpdate time.Time, inflation, + annualProvisions sdk.Dec) Minter { + return Minter{ - InflationLastTime: time.Unix(0, 0), - Inflation: sdk.NewDecWithPrec(13, 2), + LastUpdate: lastUpdate, + Inflation: inflation, + AnnualProvisions: annualProvisions, } } +// minter object for a new chain +func InitialMinter(inflation sdk.Dec) Minter { + return NewMinter( + time.Unix(0, 0), + inflation, + sdk.NewDec(0), + ) +} + +// default initial minter object for a new chain +// which uses an inflation rate of 13% +func DefaultInitialMinter() Minter { + return InitialMinter( + sdk.NewDecWithPrec(13, 2), + ) +} + func validateMinter(minter Minter) error { if minter.Inflation.LT(sdk.ZeroDec()) { - return fmt.Errorf("mint parameter Inflation should be positive, is %s ", minter.Inflation.String()) - } - if minter.Inflation.GT(sdk.OneDec()) { - return fmt.Errorf("mint parameter Inflation must be <= 1, is %s", minter.Inflation.String()) + return fmt.Errorf("mint parameter Inflation should be positive, is %s", + minter.Inflation.String()) } return nil } var hrsPerYr = sdk.NewDec(8766) // as defined by a julian year of 365.25 days -// process provisions for an hour period -func (m Minter) ProcessProvisions(params Params, totalSupply, bondedRatio sdk.Dec) ( - minter Minter, provisions sdk.Coin) { - - m.Inflation = m.NextInflation(params, bondedRatio) - provisionsDec := m.Inflation.Mul(totalSupply).Quo(hrsPerYr) - provisions = sdk.NewCoin(params.MintDenom, provisionsDec.TruncateInt()) - - return m, provisions -} - -// get the next inflation rate for the hour -func (m Minter) NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) { +// get the new inflation rate for the next hour +func (m Minter) NextInflationRate(params Params, bondedRatio sdk.Dec) ( + inflation sdk.Dec) { // The target annual inflation rate is recalculated for each previsions cycle. The // inflation is also subject to a rate change (positive or negative) depending on @@ -70,3 +79,16 @@ func (m Minter) NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk return inflation } + +// calculate the annual provisions based on current total supply and inflation rate +func (m Minter) NextAnnualProvisions(params Params, totalSupply sdk.Dec) ( + provisions sdk.Dec) { + + return m.Inflation.Mul(totalSupply) +} + +// get the provisions for a block based on the annual provisions rate +func (m Minter) BlockProvision(params Params) sdk.Coin { + provisionAmt := m.AnnualProvisions.QuoInt(sdk.NewInt(int64(params.BlocksPerYear))) + return sdk.NewCoin(params.MintDenom, provisionAmt.TruncateInt()) +} diff --git a/x/mint/minter_test.go b/x/mint/minter_test.go index b022b0ec80..63cd253d76 100644 --- a/x/mint/minter_test.go +++ b/x/mint/minter_test.go @@ -1,6 +1,7 @@ package mint import ( + "math/rand" "testing" "github.com/stretchr/testify/require" @@ -9,7 +10,7 @@ import ( ) func TestNextInflation(t *testing.T) { - minter := InitialMinter() + minter := DefaultInitialMinter() params := DefaultParams() // Governing Mechanism: @@ -44,10 +45,57 @@ func TestNextInflation(t *testing.T) { for i, tc := range tests { minter.Inflation = tc.setInflation - inflation := minter.NextInflation(params, tc.bondedRatio) + inflation := minter.NextInflationRate(params, tc.bondedRatio) diffInflation := inflation.Sub(tc.setInflation) require.True(t, diffInflation.Equal(tc.expChange), "Test Index: %v\nDiff: %v\nExpected: %v\n", i, diffInflation, tc.expChange) } } + +func TestBlockProvision(t *testing.T) { + minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) + params := DefaultParams() + + secondsPerYear := int64(60 * 60 * 8766) + + tests := []struct { + annualProvisions int64 + expProvisions int64 + }{ + {secondsPerYear / 5, 1}, + {secondsPerYear/5 + 1, 1}, + {(secondsPerYear / 5) * 2, 2}, + {(secondsPerYear / 5) / 2, 0}, + } + for i, tc := range tests { + minter.AnnualProvisions = sdk.NewDec(tc.annualProvisions) + provisions := minter.BlockProvision(params) + + expProvisions := sdk.NewCoin(params.MintDenom, + sdk.NewInt(tc.expProvisions)) + + require.True(t, expProvisions.IsEqual(provisions), + "test: %v\n\tExp: %v\n\tGot: %v\n", + i, tc.expProvisions, provisions) + } +} + +// Benchmarking :) +// previously using sdk.Int operations: +// BenchmarkBlockProvision-4 5000000 220 ns/op +// +// using sdk.Dec operations: (current implementation) +// BenchmarkBlockProvision-4 3000000 429 ns/op +func BenchmarkBlockProvision(b *testing.B) { + minter := InitialMinter(sdk.NewDecWithPrec(1, 1)) + params := DefaultParams() + + s1 := rand.NewSource(100) + r1 := rand.New(s1) + minter.AnnualProvisions = sdk.NewDec(r1.Int63n(1000000)) + + for n := 0; n < b.N; n++ { + minter.BlockProvision(params) + } +} diff --git a/x/mint/params.go b/x/mint/params.go index 47c9c85480..e1acd3a630 100644 --- a/x/mint/params.go +++ b/x/mint/params.go @@ -2,6 +2,7 @@ package mint import ( "fmt" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,6 +15,20 @@ type Params struct { InflationMax sdk.Dec `json:"inflation_max"` // maximum inflation rate InflationMin sdk.Dec `json:"inflation_min"` // minimum inflation rate GoalBonded sdk.Dec `json:"goal_bonded"` // goal of percent bonded atoms + BlocksPerYear uint64 `json:"blocks_per_year"` // expected blocks per year +} + +func NewParams(mintDenom string, inflationRateChange, inflationMax, + inflationMin, goalBonded sdk.Dec, blocksPerYear uint64) Params { + + return Params{ + MintDenom: mintDenom, + InflationRateChange: inflationRateChange, + InflationMax: inflationMax, + InflationMin: inflationMin, + GoalBonded: goalBonded, + BlocksPerYear: blocksPerYear, + } } // default minting module parameters @@ -24,6 +39,7 @@ func DefaultParams() Params { InflationMax: sdk.NewDecWithPrec(20, 2), InflationMin: sdk.NewDecWithPrec(7, 2), GoalBonded: sdk.NewDecWithPrec(67, 2), + BlocksPerYear: uint64(60 * 60 * 8766 / 5), // assuming 5 second block times } } diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 32f63f5ed4..b68b20ee8c 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -600,6 +600,9 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } rounded := returnAmount.TruncateInt() + if rounded.IsZero() { + return types.Redelegation{}, types.ErrVerySmallRedelegation(k.Codespace()) + } returnCoin := sdk.NewCoin(k.BondDenom(ctx), rounded) change := returnAmount.Sub(sdk.NewDecFromInt(rounded)) @@ -612,6 +615,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, if !found { return types.Redelegation{}, types.ErrBadRedelegationDst(k.Codespace()) } + sharesCreated, err := k.Delegate(ctx, delAddr, returnCoin, dstValidator, false) if err != nil { return types.Redelegation{}, err diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index 1dea473f89..f777077d7d 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -10,7 +10,7 @@ import ( // Implements ValidatorSet var _ sdk.ValidatorSet = Keeper{} -// iterate through the active validator set and perform the provided function +// iterate through the validator set and perform the provided function func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) @@ -27,7 +27,7 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato iterator.Close() } -// iterate through the active validator set and perform the provided function +// iterate through the bonded validator set and perform the provided function func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) maxValidators := k.MaxValidators(ctx) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 439f40de3b..44348c673f 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -33,6 +33,16 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper, return err } + err = PositiveDelegationInvariant(k)(app) + if err != nil { + return err + } + + err = DelegatorSharesInvariant(k)(app) + if err != nil { + return err + } + err = ValidatorSetInvariant(k)(app) return err } @@ -131,6 +141,53 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { } } +// PositiveDelegationInvariant checks that all stored delegations have > 0 shares. +func PositiveDelegationInvariant(k stake.Keeper) simulation.Invariant { + return func(app *baseapp.BaseApp) error { + ctx := app.NewContext(false, abci.Header{}) + + delegations := k.GetAllDelegations(ctx) + for _, delegation := range delegations { + if delegation.Shares.IsNegative() { + return fmt.Errorf("delegation with negative shares: %+v", delegation) + } + if delegation.Shares.IsZero() { + return fmt.Errorf("delegation with zero shares: %+v", delegation) + } + } + + return nil + } +} + +// DelegatorSharesInvariant checks whether all the delegator shares which persist +// in the delegator object add up to the correct total delegator shares +// amount stored in each validator +func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant { + return func(app *baseapp.BaseApp) error { + ctx := app.NewContext(false, abci.Header{}) + + validators := k.GetAllValidators(ctx) + for _, validator := range validators { + + valTotalDelShares := validator.GetDelegatorShares() + + totalDelShares := sdk.ZeroDec() + delegations := k.GetValidatorDelegations(ctx, validator.GetOperator()) + for _, delegation := range delegations { + totalDelShares = totalDelShares.Add(delegation.Shares) + } + + if !valTotalDelShares.Equal(totalDelShares) { + return fmt.Errorf("broken delegator shares invariance:\n"+ + "\tvalidator.DelegatorShares: %v\n"+ + "\tsum of Delegator.Shares: %v", valTotalDelShares, totalDelShares) + } + } + return nil + } +} + // ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set func ValidatorSetInvariant(k stake.Keeper) simulation.Invariant { return func(app *baseapp.BaseApp) error { diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index bbef528d50..57dc2fb000 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -159,6 +159,10 @@ func ErrSelfRedelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "cannot redelegate to the same validator") } +func ErrVerySmallRedelegation(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, "too few tokens to redelegate, truncates to zero tokens") +} + func ErrBadRedelegationDst(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "redelegation validator not found") } From ad121f14984c18659764629b52b15fe8fc762a5c Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 26 Nov 2018 13:21:23 +0100 Subject: [PATCH 50/51] Add a flag to export for zero-height start (#2827) Closes #2812 This PR adds the flag --for-zero-height to gaiad export, which runs several alterations to the application state to prepare for restarting a new chain in a consistent fashion. It also: * Moves Gaia's export code to cmd/gaia/app/export.go for cleaner separation. * Fixes an inconsistency where we treated the initChainer as happening at height -1 - it should now happen at height 0, since the first header sent by Tendermint has height 1. * Runs the runtime invariant checks on start (in initChainer) * Adds a few auxiliary functions to clear slashing periods * Removes the Height field from Delegation objects in x/stake, which was not used anywhere --- .circleci/config.yml | 21 +++ Makefile | 4 + PENDING.md | 1 + cmd/gaia/app/app.go | 64 +++----- cmd/gaia/app/app_test.go | 2 +- cmd/gaia/app/export.go | 156 +++++++++++++++++++ cmd/gaia/app/sim_test.go | 102 ++++++++++-- cmd/gaia/cmd/gaiad/main.go | 7 +- docs/examples/basecoin/cmd/basecoind/main.go | 2 +- docs/examples/democoin/cmd/democoind/main.go | 2 +- docs/gaia/join-testnet.md | 8 +- scripts/simulation-after-import.sh | 54 +++++++ server/constructors.go | 2 +- server/export.go | 7 +- x/distribution/keeper/delegation.go | 28 ++++ x/distribution/keeper/validator.go | 32 ++++ x/distribution/simulation/invariants.go | 54 ++++++- x/mint/minter_test.go | 1 + x/mock/simulation/mock_tendermint.go | 9 +- x/mock/simulation/simulate.go | 9 +- x/slashing/genesis.go | 16 +- x/slashing/handler_test.go | 2 +- x/slashing/hooks.go | 2 +- x/slashing/keeper.go | 4 +- x/slashing/signing_info.go | 6 +- x/slashing/signing_info_test.go | 2 +- x/slashing/slashing_period.go | 12 +- x/stake/genesis.go | 11 +- x/stake/handler_test.go | 2 - x/stake/keeper/delegation.go | 6 +- x/stake/keeper/delegation_test.go | 10 +- x/stake/types/delegation.go | 6 - 32 files changed, 534 insertions(+), 110 deletions(-) create mode 100644 cmd/gaia/app/export.go create mode 100755 scripts/simulation-after-import.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 6be110cf27..a0bab3e46e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -162,6 +162,24 @@ jobs: export PATH="$GOBIN:$PATH" make test_sim_gaia_import_export + test_sim_gaia_simulation_after_import: + <<: *defaults + parallelism: 1 + steps: + - attach_workspace: + at: /tmp/workspace + - checkout + - run: + name: dependencies + command: | + export PATH="$GOBIN:$PATH" + make get_vendor_deps + - run: + name: Test Gaia import/export simulation + command: | + export PATH="$GOBIN:$PATH" + make test_sim_gaia_simulation_after_import + test_sim_gaia_multi_seed: <<: *defaults parallelism: 1 @@ -301,6 +319,9 @@ workflows: - test_sim_gaia_import_export: requires: - setup_dependencies + - test_sim_gaia_simulation_after_import: + requires: + - setup_dependencies - test_sim_gaia_multi_seed: requires: - setup_dependencies diff --git a/Makefile b/Makefile index 35e5b50eff..d9034db9bc 100644 --- a/Makefile +++ b/Makefile @@ -184,6 +184,10 @@ test_sim_gaia_import_export: @echo "Running Gaia import/export simulation. This may take several minutes..." @bash scripts/import-export-sim.sh 50 +test_sim_gaia_simulation_after_import: + @echo "Running Gaia simulation-after-import. This may take several minutes..." + @bash scripts/simulation-after-import.sh 50 + test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" @bash scripts/multisim.sh 25 diff --git a/PENDING.md b/PENDING.md index 59a8420b55..02bb9eab36 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,6 +44,7 @@ FEATURES for getting governance parameters. * [app] \#2663 - Runtime-assertable invariants * [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`. + * [app] \#2812 Support export alterations to prepare for restarting at zero-height * SDK * [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index c06d82bb17..10248b4263 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -1,7 +1,6 @@ package app import ( - "encoding/json" "fmt" "io" "os" @@ -22,7 +21,6 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - tmtypes "github.com/tendermint/tendermint/types" ) const ( @@ -218,18 +216,8 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R } } -// custom logic for gaia initialization -func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - stateJSON := req.AppStateBytes - // TODO is this now the whole genesis file? - - var genesisState GenesisState - err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) - if err != nil { - panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 - // return sdk.ErrGenesisParse("").TraceCause(err, "") - } - +// initialize store from a genesis state +func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisState) []abci.ValidatorUpdate { // sort by account number to maintain consistency sort.Slice(genesisState.Accounts, func(i, j int) bool { return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber @@ -276,6 +264,22 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci validators = app.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } + return validators +} + +// custom logic for gaia initialization +func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + stateJSON := req.AppStateBytes + // TODO is this now the whole genesis file? + + var genesisState GenesisState + err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) + if err != nil { + panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 + // return sdk.ErrGenesisParse("").TraceCause(err, "") + } + + validators := app.initFromGenesisState(ctx, genesisState) // sanity check if len(req.Validators) > 0 { @@ -292,40 +296,14 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci } } + // assert runtime invariants + app.assertRuntimeInvariants() + return abci.ResponseInitChain{ Validators: validators, } } -// export the state of gaia for a genesis file -func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { - ctx := app.NewContext(true, abci.Header{}) - - // iterate to get the accounts - accounts := []GenesisAccount{} - appendAccount := func(acc auth.Account) (stop bool) { - account := NewGenesisAccountI(acc) - accounts = append(accounts, account) - return false - } - app.accountKeeper.IterateAccounts(ctx, appendAccount) - genState := NewGenesisState( - accounts, - auth.ExportGenesis(ctx, app.feeCollectionKeeper), - stake.ExportGenesis(ctx, app.stakeKeeper), - mint.ExportGenesis(ctx, app.mintKeeper), - distr.ExportGenesis(ctx, app.distrKeeper), - gov.ExportGenesis(ctx, app.govKeeper), - slashing.ExportGenesis(ctx, app.slashingKeeper), - ) - appState, err = codec.MarshalJSONIndent(app.cdc, genState) - if err != nil { - return nil, nil, err - } - validators = stake.WriteValidators(ctx, app.stakeKeeper) - return appState, validators, nil -} - // load a particular height func (app *GaiaApp) LoadHeight(height int64) error { return app.LoadVersion(height, app.keyMain) diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 7023eb09c1..358c3395c7 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -49,6 +49,6 @@ func TestGaiadExport(t *testing.T) { // Making a new app object with the db, so that initchain hasn't been called newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil) - _, _, err := newGapp.ExportAppStateAndValidators() + _, _, err := newGapp.ExportAppStateAndValidators(false) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } diff --git a/cmd/gaia/app/export.go b/cmd/gaia/app/export.go new file mode 100644 index 0000000000..041f4560e4 --- /dev/null +++ b/cmd/gaia/app/export.go @@ -0,0 +1,156 @@ +package app + +import ( + "encoding/json" + "fmt" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/slashing" + stake "github.com/cosmos/cosmos-sdk/x/stake" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" +) + +// export the state of gaia for a genesis file +func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool) ( + appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { + + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + + if forZeroHeight { + app.prepForZeroHeightGenesis(ctx) + } + + // iterate to get the accounts + accounts := []GenesisAccount{} + appendAccount := func(acc auth.Account) (stop bool) { + account := NewGenesisAccountI(acc) + accounts = append(accounts, account) + return false + } + app.accountKeeper.IterateAccounts(ctx, appendAccount) + + genState := NewGenesisState( + accounts, + auth.ExportGenesis(ctx, app.feeCollectionKeeper), + stake.ExportGenesis(ctx, app.stakeKeeper), + mint.ExportGenesis(ctx, app.mintKeeper), + distr.ExportGenesis(ctx, app.distrKeeper), + gov.ExportGenesis(ctx, app.govKeeper), + slashing.ExportGenesis(ctx, app.slashingKeeper), + ) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) + if err != nil { + return nil, nil, err + } + validators = stake.WriteValidators(ctx, app.stakeKeeper) + return appState, validators, nil +} + +// prepare for fresh start at zero height +func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context) { + + /* TODO XXX check some invariants */ + + height := ctx.BlockHeight() + + valAccum := sdk.ZeroDec() + vdiIter := func(_ int64, vdi distr.ValidatorDistInfo) bool { + lastValPower := app.stakeKeeper.GetLastValidatorPower(ctx, vdi.OperatorAddr) + valAccum = valAccum.Add(vdi.GetValAccum(height, sdk.NewDecFromInt(lastValPower))) + return false + } + app.distrKeeper.IterateValidatorDistInfos(ctx, vdiIter) + + lastTotalPower := sdk.NewDecFromInt(app.stakeKeeper.GetLastTotalPower(ctx)) + totalAccum := app.distrKeeper.GetFeePool(ctx).GetTotalValAccum(height, lastTotalPower) + + if !totalAccum.Equal(valAccum) { + panic(fmt.Errorf("validator accum invariance: \n\tfee pool totalAccum: %v"+ + "\n\tvalidator accum \t%v\n", totalAccum.String(), valAccum.String())) + } + + fmt.Printf("accum invariant ok!\n") + + /* END TODO XXX */ + + /* Handle fee distribution state. */ + + // withdraw all delegator & validator rewards + vdiIter = func(_ int64, valInfo distr.ValidatorDistInfo) (stop bool) { + err := app.distrKeeper.WithdrawValidatorRewardsAll(ctx, valInfo.OperatorAddr) + if err != nil { + panic(err) + } + return false + } + app.distrKeeper.IterateValidatorDistInfos(ctx, vdiIter) + + ddiIter := func(_ int64, distInfo distr.DelegationDistInfo) (stop bool) { + err := app.distrKeeper.WithdrawDelegationReward( + ctx, distInfo.DelegatorAddr, distInfo.ValOperatorAddr) + if err != nil { + panic(err) + } + return false + } + app.distrKeeper.IterateDelegationDistInfos(ctx, ddiIter) + + // delete all distribution infos + // these will be recreated in InitGenesis + app.distrKeeper.RemoveValidatorDistInfos(ctx) + app.distrKeeper.RemoveDelegationDistInfos(ctx) + + // assert that the fee pool is empty + feePool := app.distrKeeper.GetFeePool(ctx) + if !feePool.TotalValAccum.Accum.IsZero() { + panic("unexpected leftover validator accum") + } + bondDenom := app.stakeKeeper.GetParams(ctx).BondDenom + if !feePool.ValPool.AmountOf(bondDenom).IsZero() { + panic(fmt.Sprintf("unexpected leftover validator pool coins: %v", + feePool.ValPool.AmountOf(bondDenom).String())) + } + + // reset fee pool height, save fee pool + feePool.TotalValAccum.UpdateHeight = 0 + app.distrKeeper.SetFeePool(ctx, feePool) + + /* Handle stake state. */ + + // iterate through validators by power descending, reset bond height, update bond intra-tx counter + store := ctx.KVStore(app.keyStake) + iter := sdk.KVStoreReversePrefixIterator(store, stake.ValidatorsByPowerIndexKey) + counter := int16(0) + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(iter.Value()) + validator, found := app.stakeKeeper.GetValidator(ctx, addr) + if !found { + panic("expected validator, not found") + } + validator.BondHeight = 0 + validator.BondIntraTxCounter = counter + validator.UnbondingHeight = 0 + app.stakeKeeper.SetValidator(ctx, validator) + counter++ + } + iter.Close() + + /* Handle slashing state. */ + + // we have to clear the slashing periods, since they reference heights + app.slashingKeeper.DeleteValidatorSlashingPeriods(ctx) + + // reset start height on signing infos + app.slashingKeeper.IterateValidatorSigningInfos(ctx, func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + return false + }) +} diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index bd800b81d7..66868f13b9 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -137,7 +137,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { validator := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{}) validator.Tokens = sdk.NewDec(amount) validator.DelegatorShares = sdk.NewDec(amount) - delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount), 0} + delegation := stake.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount)} validators = append(validators, validator) delegations = append(delegations, delegation) } @@ -210,7 +210,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) { // Run randomized simulation // TODO parameterize numbers, save for a later PR - err := simulation.SimulateFromSeed( + _, err := simulation.SimulateFromSeed( b, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []simulation.RandSetup{}, @@ -253,7 +253,7 @@ func TestFullGaiaSimulation(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - err := simulation.SimulateFromSeed( + _, err := simulation.SimulateFromSeed( t, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []simulation.RandSetup{}, @@ -295,7 +295,7 @@ func TestGaiaImportExport(t *testing.T) { require.Equal(t, "GaiaApp", app.Name()) // Run randomized simulation - err := simulation.SimulateFromSeed( + _, err := simulation.SimulateFromSeed( t, app.BaseApp, appStateFn, seed, testAndRunTxs(app), []simulation.RandSetup{}, @@ -315,7 +315,7 @@ func TestGaiaImportExport(t *testing.T) { fmt.Printf("Exporting genesis...\n") - appState, _, err := app.ExportAppStateAndValidators() + appState, _, err := app.ExportAppStateAndValidators(false) if err != nil { panic(err) } @@ -330,15 +330,16 @@ func TestGaiaImportExport(t *testing.T) { }() newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil) require.Equal(t, "GaiaApp", newApp.Name()) - request := abci.RequestInitChain{ - AppStateBytes: appState, + var genesisState GenesisState + err = app.cdc.UnmarshalJSON(appState, &genesisState) + if err != nil { + panic(err) } - newApp.InitChain(request) - newApp.Commit() + ctxB := newApp.NewContext(true, abci.Header{}) + newApp.initFromGenesisState(ctxB, genesisState) fmt.Printf("Comparing stores...\n") ctxA := app.NewContext(true, abci.Header{}) - ctxB := newApp.NewContext(true, abci.Header{}) type StoreKeysPrefixes struct { A sdk.StoreKey B sdk.StoreKey @@ -369,6 +370,87 @@ func TestGaiaImportExport(t *testing.T) { } +func TestGaiaSimulationAfterImport(t *testing.T) { + if !enabled { + t.Skip("Skipping Gaia simulation after import") + } + + // Setup Gaia application + var logger log.Logger + if verbose { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim") + db, _ := dbm.NewGoLevelDB("Simulation", dir) + defer func() { + db.Close() + os.RemoveAll(dir) + }() + app := NewGaiaApp(logger, db, nil) + require.Equal(t, "GaiaApp", app.Name()) + + // Run randomized simulation + stopEarly, err := simulation.SimulateFromSeed( + t, app.BaseApp, appStateFn, seed, + testAndRunTxs(app), + []simulation.RandSetup{}, + invariants(app), + numBlocks, + blockSize, + commit, + ) + if commit { + // for memdb: + // fmt.Println("Database Size", db.Stats()["database.size"]) + fmt.Println("GoLevelDB Stats") + fmt.Println(db.Stats()["leveldb.stats"]) + fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + } + require.Nil(t, err) + + if stopEarly { + // we can't export or import a zero-validator genesis + fmt.Printf("We can't export or import a zero-validator genesis, exiting test...\n") + return + } + + fmt.Printf("Exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators(true) + if err != nil { + panic(err) + } + + fmt.Printf("Importing genesis...\n") + + newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2") + newDB, _ := dbm.NewGoLevelDB("Simulation-2", dir) + defer func() { + newDB.Close() + os.RemoveAll(newDir) + }() + newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil) + require.Equal(t, "GaiaApp", newApp.Name()) + newApp.InitChain(abci.RequestInitChain{ + AppStateBytes: appState, + }) + + // Run randomized simulation on imported app + _, err = simulation.SimulateFromSeed( + t, newApp.BaseApp, appStateFn, seed, + testAndRunTxs(newApp), + []simulation.RandSetup{}, + invariants(newApp), + numBlocks, + blockSize, + commit, + ) + require.Nil(t, err) + +} + // TODO: Make another test for the fuzzer itself, which just has noOp txs // and doesn't depend on gaia func TestAppStateDeterminism(t *testing.T) { diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index f3a8309e06..f39ddae8c7 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -62,9 +62,8 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application } func exportAppStateAndTMValidators( - logger log.Logger, db dbm.DB, traceStore io.Writer, height int64) ( - json.RawMessage, []tmtypes.GenesisValidator, error) { - + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, +) (json.RawMessage, []tmtypes.GenesisValidator, error) { gApp := app.NewGaiaApp(logger, db, traceStore) if height != -1 { err := gApp.LoadHeight(height) @@ -72,5 +71,5 @@ func exportAppStateAndTMValidators( return nil, nil, err } } - return gApp.ExportAppStateAndValidators() + return gApp.ExportAppStateAndValidators(forZeroHeight) } diff --git a/docs/examples/basecoin/cmd/basecoind/main.go b/docs/examples/basecoin/cmd/basecoind/main.go index 318b36a8f5..383a843b29 100644 --- a/docs/examples/basecoin/cmd/basecoind/main.go +++ b/docs/examples/basecoin/cmd/basecoind/main.go @@ -124,7 +124,7 @@ func newApp(logger log.Logger, db dbm.DB, storeTracer io.Writer) abci.Applicatio return app.NewBasecoinApp(logger, db, baseapp.SetPruning(viper.GetString("pruning"))) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer, _ int64) ( +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, storeTracer io.Writer, _ int64, _ bool) ( json.RawMessage, []tmtypes.GenesisValidator, error) { bapp := app.NewBasecoinApp(logger, db) return bapp.ExportAppStateAndValidators() diff --git a/docs/examples/democoin/cmd/democoind/main.go b/docs/examples/democoin/cmd/democoind/main.go index 730109798c..8f52340f47 100644 --- a/docs/examples/democoin/cmd/democoind/main.go +++ b/docs/examples/democoin/cmd/democoind/main.go @@ -128,7 +128,7 @@ func newApp(logger log.Logger, db dbm.DB, _ io.Writer) abci.Application { return app.NewDemocoinApp(logger, db) } -func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer, _ int64) ( +func exportAppStateAndTMValidators(logger log.Logger, db dbm.DB, _ io.Writer, _ int64, _ bool) ( json.RawMessage, []tmtypes.GenesisValidator, error) { dapp := app.NewDemocoinApp(logger, db) return dapp.ExportAppStateAndValidators() diff --git a/docs/gaia/join-testnet.md b/docs/gaia/join-testnet.md index e1c9163cd1..756d38ce6d 100644 --- a/docs/gaia/join-testnet.md +++ b/docs/gaia/join-testnet.md @@ -142,7 +142,13 @@ gaiad export > [filename].json You can also export state from a particular height (at the end of processing the block of that height): ```bash -gaiad export --height=[height] > [filename].json +gaiad export --height [height] > [filename].json +``` + +If you plan to start a new network from the exported state, export with the `--for-zero-height` flag: + +```bash +gaiad export --height [height] --for-zero-height > [filename].json ``` ## Upgrade to Validator Node diff --git a/scripts/simulation-after-import.sh b/scripts/simulation-after-import.sh new file mode 100755 index 0000000000..2e90d12349 --- /dev/null +++ b/scripts/simulation-after-import.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \ +11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827) +blocks=$1 + +echo "Running multi-seed import-export simulation with seeds ${seeds[@]}" +echo "Running $blocks blocks per seed" +echo "Edit scripts/simulation-after-import.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce." +echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)." + +trap 'kill $(jobs -pr)' SIGINT SIGTERM + +tmpdir=$(mktemp -d) +echo "Using temporary log directory: $tmpdir" + +sim() { + seed=$1 + echo "Running simulation after import with seed $seed. This may take awhile!" + file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout" + echo "Writing stdout to $file..." + go test ./cmd/gaia/app -run TestGaiaSimulationAfterImport -SimulationEnabled=true -SimulationNumBlocks=$blocks \ + -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file +} + +i=0 +pids=() +for seed in ${seeds[@]}; do + sim $seed & + pids[${i}]=$! + i=$(($i+1)) + sleep 10 # start in order, nicer logs +done + +echo "Simulation processes spawned, waiting for completion..." + +code=0 + +i=0 +for pid in ${pids[*]}; do + wait $pid + last=$? + seed=${seeds[${i}]} + if [ $last -ne 0 ] + then + echo "Import/export simulation with seed $seed failed!" + code=1 + else + echo "Import/export simulation with seed $seed OK" + fi + i=$(($i+1)) +done + +exit $code diff --git a/server/constructors.go b/server/constructors.go index 9039d8a81d..909bda8e25 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -19,7 +19,7 @@ type ( // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. - AppExporter func(log.Logger, dbm.DB, io.Writer, int64) (json.RawMessage, []tmtypes.GenesisValidator, error) + AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool) (json.RawMessage, []tmtypes.GenesisValidator, error) ) func openDB(rootDir string) (dbm.DB, error) { diff --git a/server/export.go b/server/export.go index fbe52eef6d..7b5ba4a69e 100644 --- a/server/export.go +++ b/server/export.go @@ -14,7 +14,8 @@ import ( ) const ( - flagHeight = "height" + flagHeight = "height" + flagForZeroHeight = "for-zero-height" ) // ExportCmd dumps app state to JSON. @@ -50,7 +51,8 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C return err } height := viper.GetInt64(flagHeight) - appState, validators, err := appExporter(ctx.Logger, db, traceWriter, height) + forZeroHeight := viper.GetBool(flagForZeroHeight) + appState, validators, err := appExporter(ctx.Logger, db, traceWriter, height, forZeroHeight) if err != nil { return errors.Errorf("error exporting state: %v\n", err) } @@ -73,6 +75,7 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C }, } cmd.Flags().Int64(flagHeight, -1, "Export state from a particular height (-1 means latest height)") + cmd.Flags().Bool(flagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") return cmd } diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index ac519107da..728dfb269f 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -42,6 +42,34 @@ func (k Keeper) RemoveDelegationDistInfo(ctx sdk.Context, delAddr sdk.AccAddress store.Delete(GetDelegationDistInfoKey(delAddr, valOperatorAddr)) } +// remove all delegation distribution infos +func (k Keeper) RemoveDelegationDistInfos(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } +} + +// iterate over all the validator distribution infos +func (k Keeper) IterateDelegationDistInfos(ctx sdk.Context, + fn func(index int64, distInfo types.DelegationDistInfo) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) + defer iter.Close() + index := int64(0) + for ; iter.Valid(); iter.Next() { + var ddi types.DelegationDistInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &ddi) + if fn(index, ddi) { + return + } + index++ + } +} + //___________________________________________________________________________________________ // get the delegator withdraw address, return the delegator address if not set diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index 119622edd2..7b174cd1c4 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -47,6 +47,34 @@ func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) store.Delete(GetValidatorDistInfoKey(valAddr)) } +// remove all validator distribution infos +func (k Keeper) RemoveValidatorDistInfos(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } +} + +// iterate over all the validator distribution infos +func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, + fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) { + + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey) + defer iter.Close() + index := int64(0) + for ; iter.Valid(); iter.Next() { + var vdi types.ValidatorDistInfo + k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vdi) + if fn(index, vdi) { + return + } + index++ + } +} + // Get the calculated accum of a validator at the current block // without affecting the state. func (k Keeper) GetValidatorAccum(ctx sdk.Context, operatorAddr sdk.ValAddress) (sdk.Dec, sdk.Error) { @@ -74,6 +102,10 @@ func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.Va accAddr := sdk.AccAddress(operatorAddr.Bytes()) withdraw := k.withdrawDelegationRewardsAll(ctx, accAddr) + //if withdraw.AmountOf { + //return types.ErrNoValidatorDistInfo(k.codespace) + //} + // withdrawal validator commission rewards valInfo := k.GetValidatorDistInfo(ctx, operatorAddr) wc := k.GetWithdrawContext(ctx, operatorAddr) diff --git a/x/distribution/simulation/invariants.go b/x/distribution/simulation/invariants.go index 128e7faa9a..ae4e747299 100644 --- a/x/distribution/simulation/invariants.go +++ b/x/distribution/simulation/invariants.go @@ -7,12 +7,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/mock/simulation" + "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" ) // AllInvariants runs all invariants of the distribution module // Currently: total supply, positive power -func AllInvariants(d distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { +func AllInvariants(d distr.Keeper, stk stake.Keeper) simulation.Invariant { + sk := distr.StakeKeeper(stk) return func(app *baseapp.BaseApp) error { err := ValAccumInvariants(d, sk)(app) if err != nil { @@ -22,6 +24,10 @@ func AllInvariants(d distr.Keeper, sk distr.StakeKeeper) simulation.Invariant { if err != nil { return err } + err = CanWithdrawInvariant(d, stk)(app) + if err != nil { + return err + } return nil } } @@ -130,3 +136,49 @@ func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invaria return nil } } + +// CanWithdrawInvariant checks that current rewards can be completely withdrawn +func CanWithdrawInvariant(k distr.Keeper, sk stake.Keeper) simulation.Invariant { + return func(app *baseapp.BaseApp) error { + mockHeader := abci.Header{Height: app.LastBlockHeight() + 1} + ctx := app.NewContext(false, mockHeader) + + // we don't want to write the changes + ctx, _ = ctx.CacheContext() + + // withdraw all delegator & validator rewards + vdiIter := func(_ int64, valInfo distr.ValidatorDistInfo) (stop bool) { + err := k.WithdrawValidatorRewardsAll(ctx, valInfo.OperatorAddr) + if err != nil { + panic(err) + } + return false + } + k.IterateValidatorDistInfos(ctx, vdiIter) + + ddiIter := func(_ int64, distInfo distr.DelegationDistInfo) (stop bool) { + err := k.WithdrawDelegationReward( + ctx, distInfo.DelegatorAddr, distInfo.ValOperatorAddr) + if err != nil { + panic(err) + } + return false + } + k.IterateDelegationDistInfos(ctx, ddiIter) + + // assert that the fee pool is empty + feePool := k.GetFeePool(ctx) + if !feePool.TotalValAccum.Accum.IsZero() { + return fmt.Errorf("unexpected leftover validator accum") + } + bondDenom := sk.GetParams(ctx).BondDenom + if !feePool.ValPool.AmountOf(bondDenom).IsZero() { + return fmt.Errorf("unexpected leftover validator pool coins: %v", + feePool.ValPool.AmountOf(bondDenom).String()) + } + + // all ok + return nil + + } +} diff --git a/x/mint/minter_test.go b/x/mint/minter_test.go index 63cd253d76..a393b3a048 100644 --- a/x/mint/minter_test.go +++ b/x/mint/minter_test.go @@ -95,6 +95,7 @@ func BenchmarkBlockProvision(b *testing.B) { r1 := rand.New(s1) minter.AnnualProvisions = sdk.NewDec(r1.Int63n(1000000)) + // run the Fib function b.N times for n := 0; n < b.N; n++ { minter.BlockProvision(params) } diff --git a/x/mock/simulation/mock_tendermint.go b/x/mock/simulation/mock_tendermint.go index 2ddac2e794..54e38d5c78 100644 --- a/x/mock/simulation/mock_tendermint.go +++ b/x/mock/simulation/mock_tendermint.go @@ -167,10 +167,11 @@ func RandomRequestBeginBlock(r *rand.Rand, params Params, time := header.Time vals := voteInfos - if r.Float64() < params.PastEvidenceFraction { - height = int64(r.Intn(int(header.Height) - 1)) - time = pastTimes[height] - vals = pastVoteInfos[height] + if r.Float64() < params.PastEvidenceFraction && header.Height > 1 { + height = int64(r.Intn(int(header.Height)-1)) + 1 // Tendermint starts at height 1 + // array indices offset by one + time = pastTimes[height-1] + vals = pastVoteInfos[height-1] } validator := vals[r.Intn(len(vals))].Validator diff --git a/x/mock/simulation/simulate.go b/x/mock/simulation/simulate.go index ac0e5d3f7f..74446e290a 100644 --- a/x/mock/simulation/simulate.go +++ b/x/mock/simulation/simulate.go @@ -27,7 +27,7 @@ type AppStateFn func(r *rand.Rand, accs []Account) json.RawMessage // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, appStateFn AppStateFn, ops WeightedOperations, setups []RandSetup, - invariants Invariants, numBlocks int, blockSize int, commit bool) error { + invariants Invariants, numBlocks int, blockSize int, commit bool) (bool, error) { time := time.Now().UnixNano() return SimulateFromSeed(t, app, appStateFn, time, ops, @@ -57,10 +57,9 @@ func initChain(r *rand.Rand, params Params, accounts []Account, func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, appStateFn AppStateFn, seed int64, ops WeightedOperations, setups []RandSetup, invariants Invariants, - numBlocks int, blockSize int, commit bool) (simError error) { + numBlocks int, blockSize int, commit bool) (stopEarly bool, simError error) { // in case we have to end early, don't os.Exit so that we can run cleanup code. - stopEarly := false testingMode, t, b := getTestingMode(tb) fmt.Printf("Starting SimulateFromSeed with randomness "+ "created with seed %d\n", int(seed)) @@ -217,14 +216,14 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, if stopEarly { eventStats.Print() - return simError + return true, simError } fmt.Printf("\nSimulation complete. Final height (blocks): %d, "+ "final time (seconds), : %v, operations ran %d\n", header.Height, header.Time, opCount) eventStats.Print() - return nil + return false, nil } //______________________________________________________________________________ diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 1d4a443691..2a921af49a 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -7,10 +7,10 @@ import ( // GenesisState - all slashing state that must be provided at genesis type GenesisState struct { - Params Params - SigningInfos map[string]ValidatorSigningInfo - MissedBlocks map[string][]MissedBlock - SlashingPeriods []ValidatorSlashingPeriod + Params Params `json:"params"` + SigningInfos map[string]ValidatorSigningInfo `json:"signing_infos"` + MissedBlocks map[string][]MissedBlock `json:"missed_blocks"` + SlashingPeriods []ValidatorSlashingPeriod `json:"slashing_periods"` } // MissedBlock @@ -41,7 +41,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState, sdata types. if err != nil { panic(err) } - keeper.setValidatorSigningInfo(ctx, address, info) + keeper.SetValidatorSigningInfo(ctx, address, info) } for addr, array := range data.MissedBlocks { @@ -70,12 +70,12 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { signingInfos := make(map[string]ValidatorSigningInfo) missedBlocks := make(map[string][]MissedBlock) - keeper.iterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) { + keeper.IterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) { bechAddr := address.String() signingInfos[bechAddr] = info localMissedBlocks := []MissedBlock{} - keeper.iterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { + keeper.IterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) { localMissedBlocks = append(localMissedBlocks, MissedBlock{index, missed}) return false }) @@ -85,7 +85,7 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) (data GenesisState) { }) slashingPeriods := []ValidatorSlashingPeriod{} - keeper.iterateValidatorSlashingPeriods(ctx, func(slashingPeriod ValidatorSlashingPeriod) (stop bool) { + keeper.IterateValidatorSlashingPeriods(ctx, func(slashingPeriod ValidatorSlashingPeriod) (stop bool) { slashingPeriods = append(slashingPeriods, slashingPeriod) return false }) diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index c77150535e..bd19643ae3 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -60,7 +60,7 @@ func TestJailedValidatorDelegations(t *testing.T) { JailedUntil: time.Unix(0, 0), MissedBlocksCounter: int64(0), } - slashingKeeper.setValidatorSigningInfo(ctx, consAddr, newInfo) + slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) // delegate tokens to the validator delAddr := sdk.AccAddress(addrs[2]) diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index e09f6c566e..bb7e413242 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -18,7 +18,7 @@ func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sd JailedUntil: time.Unix(0, 0), MissedBlocksCounter: 0, } - k.setValidatorSigningInfo(ctx, address, signingInfo) + k.SetValidatorSigningInfo(ctx, address, signingInfo) } // Create a new slashing period when a validator is bonded diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index fe1531f228..61d3661720 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -96,7 +96,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) } signInfo.JailedUntil = time.Add(k.DoubleSignUnbondDuration(ctx)) - k.setValidatorSigningInfo(ctx, consAddr, signInfo) + k.SetValidatorSigningInfo(ctx, consAddr, signInfo) } // handle a validator signature, must be called once per validator per block @@ -168,7 +168,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p } // Set the updated signing info - k.setValidatorSigningInfo(ctx, consAddr, signInfo) + k.SetValidatorSigningInfo(ctx, consAddr, signInfo) } func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) { diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 291351742f..f4f2d2fde8 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -21,7 +21,7 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress } // Stored by *validator* address (not operator address) -func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool)) { +func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context, handler func(address sdk.ConsAddress, info ValidatorSigningInfo) (stop bool)) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, ValidatorSigningInfoKey) defer iter.Close() @@ -36,7 +36,7 @@ func (k Keeper) iterateValidatorSigningInfos(ctx sdk.Context, handler func(addre } // Stored by *validator* address (not operator address) -func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { +func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info ValidatorSigningInfo) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(info) store.Set(GetValidatorSigningInfoKey(address), bz) @@ -56,7 +56,7 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con } // Stored by *validator* address (not operator address) -func (k Keeper) iterateValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { +func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { store := ctx.KVStore(k.storeKey) index := int64(0) // Array may be sparse diff --git a/x/slashing/signing_info_test.go b/x/slashing/signing_info_test.go index 15863ebc70..d340eaf7a5 100644 --- a/x/slashing/signing_info_test.go +++ b/x/slashing/signing_info_test.go @@ -19,7 +19,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { JailedUntil: time.Unix(2, 0), MissedBlocksCounter: int64(10), } - keeper.setValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo) + keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo) info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0])) require.True(t, found) require.Equal(t, info.StartHeight, int64(4)) diff --git a/x/slashing/slashing_period.go b/x/slashing/slashing_period.go index 4caf5d7c9d..e726453dc2 100644 --- a/x/slashing/slashing_period.go +++ b/x/slashing/slashing_period.go @@ -54,7 +54,7 @@ func (k Keeper) getValidatorSlashingPeriodForHeight(ctx sdk.Context, address sdk // Iterate over all slashing periods in the store, calling on each // decode slashing period a provided handler function // Stop if the provided handler function returns true -func (k Keeper) iterateValidatorSlashingPeriods(ctx sdk.Context, handler func(slashingPeriod ValidatorSlashingPeriod) (stop bool)) { +func (k Keeper) IterateValidatorSlashingPeriods(ctx sdk.Context, handler func(slashingPeriod ValidatorSlashingPeriod) (stop bool)) { store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey) defer iter.Close() @@ -66,6 +66,16 @@ func (k Keeper) iterateValidatorSlashingPeriods(ctx sdk.Context, handler func(sl } } +// Delete all slashing periods in the store. +func (k Keeper) DeleteValidatorSlashingPeriods(ctx sdk.Context) { + store := ctx.KVStore(k.storeKey) + iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey) + for ; iter.Valid(); iter.Next() { + store.Delete(iter.Key()) + } + iter.Close() +} + // Stored by validator Tendermint address (not operator address) // This function sets a validator slashing period for a particular validator, // start height, end height, and current slashed-so-far total, or updates diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 7be8eff3a4..bdd62d3769 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -21,8 +21,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ // We need to pretend to be "n blocks before genesis", where "n" is the validator update delay, // so that e.g. slashing periods are correctly initialized for the validator set - // e.g. with a one-block offset - the first TM block is at height 0, so state updates applied from genesis.json are in block -1. - ctx = ctx.WithBlockHeight(-types.ValidatorUpdateDelay) + // e.g. with a one-block offset - the first TM block is at height 1, so state updates applied from genesis.json are in block 0. + ctx = ctx.WithBlockHeight(1 - types.ValidatorUpdateDelay) keeper.SetPool(ctx, data.Pool) keeper.SetParams(ctx, data.Params) @@ -72,6 +72,13 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ if data.Exported { for _, lv := range data.LastValidatorPowers { keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) + validator, found := keeper.GetValidator(ctx, lv.Address) + if !found { + panic("expected validator, not found") + } + update := validator.ABCIValidatorUpdate() + update.Power = lv.Power.Int64() // keep the next-val-set offset, use the last power for the first block + res = append(res, update) } } else { res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index fcc268f558..81d03de058 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -341,8 +341,6 @@ func TestIncrementsMsgDelegate(t *testing.T) { expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation) expDelegatorAcc := sdk.NewInt(initBond - expBond) - require.Equal(t, bond.Height, int64(i), "Incorrect bond height") - gotBond := bond.Shares.RoundInt64() gotDelegatorShares := validator.DelegatorShares.RoundInt64() gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index b68b20ee8c..811b156293 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -415,7 +415,6 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co // Update delegation delegation.Shares = delegation.Shares.Add(newShares) - delegation.Height = ctx.BlockHeight() k.SetDelegation(ctx, delegation) return newShares, nil @@ -462,8 +461,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA k.RemoveDelegation(ctx, delegation) } else { - // Update height - delegation.Height = ctx.BlockHeight() + // update the delegation k.SetDelegation(ctx, delegation) } @@ -600,7 +598,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } rounded := returnAmount.TruncateInt() - if rounded.IsZero() { + if rounded.IsZero() { //TODO design consideration return types.Redelegation{}, types.ErrVerySmallRedelegation(k.Codespace()) } returnCoin := sdk.NewCoin(k.BondDenom(ctx), rounded) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 3fa641fd2a..2310f849fd 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -56,11 +56,11 @@ func TestDelegation(t *testing.T) { require.True(t, bond1to1.Equal(resBond)) // add some more records - bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewDec(9), 0} - bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewDec(9), 1} - bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewDec(9), 2} - bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewDec(9), 3} - bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewDec(9), 4} + bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewDec(9)} + bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewDec(9)} + bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewDec(9)} + bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewDec(9)} + bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewDec(9)} keeper.SetDelegation(ctx, bond1to2) keeper.SetDelegation(ctx, bond1to3) keeper.SetDelegation(ctx, bond2to1) diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index e731554274..0d49b1db58 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -33,19 +33,16 @@ type Delegation struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` ValidatorAddr sdk.ValAddress `json:"validator_addr"` Shares sdk.Dec `json:"shares"` - Height int64 `json:"height"` // Last height bond updated } type delegationValue struct { Shares sdk.Dec - Height int64 } // return the delegation without fields contained within the key for the store func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte { val := delegationValue{ delegation.Shares, - delegation.Height, } return cdc.MustMarshalBinaryLengthPrefixed(val) } @@ -81,7 +78,6 @@ func UnmarshalDelegation(cdc *codec.Codec, key, value []byte) (delegation Delega DelegatorAddr: delAddr, ValidatorAddr: valAddr, Shares: storeValue.Shares, - Height: storeValue.Height, }, nil } @@ -89,7 +85,6 @@ func UnmarshalDelegation(cdc *codec.Codec, key, value []byte) (delegation Delega func (d Delegation) Equal(d2 Delegation) bool { return bytes.Equal(d.DelegatorAddr, d2.DelegatorAddr) && bytes.Equal(d.ValidatorAddr, d2.ValidatorAddr) && - d.Height == d2.Height && d.Shares.Equal(d2.Shares) } @@ -109,7 +104,6 @@ func (d Delegation) HumanReadableString() (string, error) { resp += fmt.Sprintf("Delegator: %s\n", d.DelegatorAddr) resp += fmt.Sprintf("Validator: %s\n", d.ValidatorAddr) resp += fmt.Sprintf("Shares: %s\n", d.Shares.String()) - resp += fmt.Sprintf("Height: %d", d.Height) return resp, nil } From 7ec2b9a72b1cb39e3026e96688596827201e7510 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 26 Nov 2018 04:27:23 -0800 Subject: [PATCH 51/51] Fix merge conflict issues from previous squash commit --- x/distribution/keeper/test_common.go | 41 ---------------------------- 1 file changed, 41 deletions(-) diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 26033432fc..7cc68fcc42 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -158,44 +158,3 @@ func (fck DummyFeeCollectionKeeper) SetCollectedFees(in sdk.Coins) { func (fck DummyFeeCollectionKeeper) ClearCollectedFees(_ sdk.Context) { heldFees = sdk.Coins{} } - -//__________________________________________________________________________________ -// used in simulation - -// iterate over all the validator distribution infos (inefficient, just used to -// check invariants) -func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context, - fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) { - - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey) - defer iter.Close() - index := int64(0) - for ; iter.Valid(); iter.Next() { - var vdi types.ValidatorDistInfo - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vdi) - if fn(index, vdi) { - return - } - index++ - } -} - -// iterate over all the delegation distribution infos (inefficient, just used -// to check invariants) -func (k Keeper) IterateDelegationDistInfos(ctx sdk.Context, - fn func(index int64, distInfo types.DelegationDistInfo) (stop bool)) { - - store := ctx.KVStore(k.storeKey) - iter := sdk.KVStorePrefixIterator(store, DelegationDistInfoKey) - defer iter.Close() - index := int64(0) - for ; iter.Valid(); iter.Next() { - var ddi types.DelegationDistInfo - k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &ddi) - if fn(index, ddi) { - return - } - index++ - } -}