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/Gopkg.lock b/Gopkg.lock index bcc306edb9..b166713ee0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -643,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", 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 d5681832d5..6528922245 100644 --- a/PENDING.md +++ b/PENDING.md @@ -11,13 +11,18 @@ 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. + * [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. + * [\#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') * Tendermint @@ -32,6 +37,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`. @@ -39,9 +45,11 @@ 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 + * [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 @@ -50,16 +58,27 @@ IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) * [gaia-lite] [\#2819](https://github.com/cosmos/cosmos-sdk/pull/2819) Tx search now supports multiple tags as query parameters + * [\#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 * 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 + - \#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 + - #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 @@ -68,16 +87,20 @@ IMPROVEMENTS BUG FIXES * Gaia REST API (`gaiacli advanced rest-server`) + - [gaia-lite] #2868 Added handler for governance tally endpoit * Gaia CLI (`gaiacli`) * 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 - \#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 diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 827536d215..a83713d424 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -6,11 +6,11 @@ import ( "runtime/debug" "strings" + "github.com/gogo/protobuf/proto" "github.com/pkg/errors" 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" @@ -20,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 @@ -47,12 +44,13 @@ 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 + // 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 @@ -68,7 +66,11 @@ type BaseApp struct { deliverState *state // for DeliverTx voteInfos []abci.VoteInfo // absent validators from begin block - // minimum fees for spam prevention + // consensus params + // TODO move this in the future to baseapp param store on main store. + consensusParams *abci.ConsensusParams + + // spam prevention minimumFees sdk.Coins // flag for sealing @@ -79,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 @@ -94,13 +92,8 @@ 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 +111,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 { @@ -148,21 +136,23 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { } // load latest application version -func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error { +// 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 { return err } - return app.initFromStore(mainKey) + return app.initFromMainStore(mainKey) } // load application version -func (app *BaseApp) LoadVersion(version int64, mainKey sdk.StoreKey) error { +// 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 { return err } - return app.initFromStore(mainKey) + return app.initFromMainStore(mainKey) } // the last CommitID of the multistore @@ -176,13 +166,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 params 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{}) @@ -211,6 +222,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{ @@ -227,6 +242,29 @@ 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 + } + return uint64(app.consensusParams.BlockSize.MaxGas) +} + //______________________________________________________________________________ // ABCI @@ -249,8 +287,15 @@ 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) { + + // 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}) @@ -258,6 +303,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 @@ -329,8 +379,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 +390,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" @@ -392,6 +444,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) @@ -400,12 +453,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, } } @@ -427,9 +481,20 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg } 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) } + // add block gas meter + var gasMeter sdk.GasMeter + if maxGas := app.getMaximumBlockGas(); maxGas > 0 { + gasMeter = sdk.NewGasMeter(maxGas) + } else { + gasMeter = sdk.NewInfiniteGasMeter() + } + app.deliverState.ctx = app.deliverState.ctx.WithBlockGasMeter(gasMeter) + if app.beginBlocker != nil { res = app.beginBlocker(app.deliverState.ctx, req) } @@ -459,17 +524,18 @@ 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, } } // 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 { @@ -482,10 +548,11 @@ 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, - 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, } } @@ -501,7 +568,6 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { // Validate the Msg. err := msg.ValidateBasic() if err != nil { - err = err.WithDefaultCodespace(sdk.CodespaceRoot) return err } } @@ -509,13 +575,13 @@ 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) { - // Get the context - ctx = getState(app, 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 } @@ -526,7 +592,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 +620,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 +630,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, } @@ -575,7 +644,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 } @@ -583,24 +652,45 @@ func getState(app *BaseApp, mode runTxMode) *state { return app.deliverState } -func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { - if mode == runTxModeSimulate { - ctx = ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) +// 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) { + + ms := ctx.MultiStore() + // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 + msCache := ms.CacheMultiStore() + if msCache.TracingEnabled() { + msCache = msCache.WithTracingContext( + sdk.TraceContext( + map[string]interface{}{ + "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), + }, + ), + ).(sdk.CacheMultiStore) } - return ctx + + 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) + var gasWanted uint64 + + ctx := app.getContextForTx(mode, txBytes) + ms := ctx.MultiStore() + + // only run the tx if there is block gas remaining + if mode == runTxModeDeliver && ctx.BlockGasMeter().IsOutOfGas() { + result = sdk.ErrOutOfGas("no block gas left to run tx").Result() + return + } defer func() { if r := recover(); r != nil { @@ -618,43 +708,65 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk 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") + } + }() + var msgs = tx.GetMsgs() if err := validateBasicTxMsgs(msgs); err != nil { 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) + + 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 } - // 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) + 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() @@ -679,14 +791,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 9f6414214b..c09f050ede 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) @@ -282,12 +284,24 @@ 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 -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" @@ -297,7 +311,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 +330,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 +384,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 +401,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) } } @@ -491,6 +516,7 @@ func TestDeliverTx(t *testing.T) { } app := setupBaseApp(t, anteOpt, routerOpt) + app.InitChain(abci.RequestInitChain{}) // Create same codec used in txDecoder codec := codec.New() @@ -596,7 +622,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) { @@ -678,7 +704,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 +728,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)) } @@ -710,13 +738,15 @@ 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.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} + unknownRouteTx = txTest{[]sdk.Msg{msgCounter{}, msgNoRoute{}}, 0, false} 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,13 +762,14 @@ 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) } } // 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)) @@ -763,7 +794,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, } @@ -775,7 +806,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{} }) } @@ -786,7 +817,7 @@ func TestTxGasLimits(t *testing.T) { testCases := []struct { tx *txTest - gasUsed int64 + gasUsed uint64 fail bool }{ {newTxCounter(0, 0), 0, false}, @@ -819,7 +850,181 @@ 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) } } } + +// Test that transactions exceeding gas limits fail +func TestMaxBlockGasLimits(t *testing.T) { + 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)) + + // 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(uint64(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(uint64(count), "counter-handler") + return sdk.Result{} + }) + } + + app := setupBaseApp(t, anteOpt, routerOpt) + app.InitChain(abci.RequestInitChain{ + ConsensusParams: &abci.ConsensusParams{ + BlockSize: &abci.BlockSizeParams{ + MaxGas: 100, + }, + }, + }) + + testCases := []struct { + tx *txTest + numDelivers int + gasUsedPerDeliver uint64 + fail bool + failAfterDeliver int + }{ + {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(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}, + {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 + app.BeginBlock(abci.RequestBeginBlock{}) + + // execute the transaction multiple times + for j := 0; j < tc.numDelivers; j++ { + res := app.Deliver(tx) + + ctx := app.getState(runTxModeDeliver).ctx + blockGasUsed := ctx.BlockGasMeter().GasConsumed() + + // check for failed transactions + 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().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().IsPastLimit()) + } + } + } +} + +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/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/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 9d03939e02..6a3ccff060 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 @@ -67,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") @@ -92,12 +99,32 @@ 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. type GasSetting struct { Simulate bool - Gas int64 + Gas uint64 } // Type returns the flag's value type. @@ -113,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/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/lcd_test.go b/client/lcd/lcd_test.go index d790b952a4..7478f42d39 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -284,7 +284,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, "") @@ -390,8 +390,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) { @@ -696,7 +696,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 @@ -714,7 +714,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) @@ -722,7 +722,7 @@ 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) @@ -734,6 +734,20 @@ func TestVote(t *testing.T) { 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) { @@ -853,11 +867,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()) @@ -871,7 +885,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 @@ -1381,8 +1395,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) @@ -1408,6 +1422,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) @@ -1418,8 +1441,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 @@ -1438,8 +1461,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 @@ -1501,7 +1524,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/root.go b/client/lcd/root.go index 8366b6114c..7081b76c03 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -10,176 +10,169 @@ 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" "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" ) -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/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 9ea1b2b521..82e7c34293 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 @@ -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 @@ -1418,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: @@ -1433,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: @@ -1443,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: @@ -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: @@ -1920,8 +1934,23 @@ definitions: "$ref": "#/definitions/Coin" proposal_id: type: integer - depositer: + depositor: "$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/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/client/utils/rest.go b/client/utils/rest.go index 53c6186921..c0a8c3c77e 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))) } @@ -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/client/utils/utils.go b/client/utils/utils.go index fee7c7fe25..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" @@ -71,7 +72,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) @@ -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 } @@ -152,7 +153,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 +162,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/app.go b/cmd/gaia/app/app.go index fb8455b5ae..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 ( @@ -113,7 +111,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 +122,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 @@ -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/genesis.go b/cmd/gaia/app/genesis.go index 7d95bbfa7d..60e96307bc 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -41,8 +41,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, @@ -59,8 +61,8 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stak 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 { @@ -91,7 +93,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) ( @@ -261,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) } @@ -279,11 +280,13 @@ 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}, + coins := sdk.Coins{ + 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..66868f13b9 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -59,11 +59,13 @@ 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 { - 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, @@ -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 @@ -133,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) } @@ -206,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{}, @@ -249,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{}, @@ -291,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{}, @@ -311,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) } @@ -326,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 @@ -365,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/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index e52bbe6630..820c55f4ef 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -370,7 +370,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()) @@ -399,7 +399,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()) @@ -480,7 +480,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())) @@ -491,7 +491,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())) @@ -542,16 +542,19 @@ 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) + 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)) 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()) } @@ -608,10 +611,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)) @@ -625,10 +629,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) @@ -664,6 +668,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/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index de99b0fc8f..05c7bc4eca 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -1,64 +1,98 @@ package main import ( + "fmt" + "net/http" "os" "path" + "github.com/rakyll/statik/fs" "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" "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" + + 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() { + // 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 + // 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, - lcd.ServeCommand(cdc), + rs.ServeCommand(), client.LineBreak, keys.Commands(), client.LineBreak, @@ -74,9 +108,77 @@ func main() { err = executor.Execute() if err != nil { - // handle with #870 + 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 +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, 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) + 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 { 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/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index bea3ac9525..f39ddae8c7 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -62,7 +62,7 @@ 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, + 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 { @@ -71,5 +71,5 @@ func exportAppStateAndTMValidators( return nil, nil, err } } - return gApp.ExportAppStateAndValidators() + return gApp.ExportAppStateAndValidators(forZeroHeight) } 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/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/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 6d66f2e306..b8392d3bd2 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -1,25 +1,32 @@ package init import ( + "bytes" "fmt" + "io" + "io/ioutil" + "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" + "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" - "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" "github.com/tendermint/tendermint/libs/common" - "io/ioutil" - "os" - "path/filepath" ) const ( @@ -66,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 } @@ -79,27 +88,40 @@ following delegation and commission default parameters: } // Run gaiad tx create-validator prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) - createValidatorCmd := cli.GetCmdCreateValidator(cdc) - - w, err := ioutil.TempFile("", "gentx") + txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) + cliCtx := context.NewCLIContext().WithCodec(cdc) + cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { return err } - unsignedGenTxFilename := w.Name() - defer os.Remove(unsignedGenTxFilename) - os.Stdout = w - if err = createValidatorCmd.RunE(nil, args); err != nil { - return err - } - w.Close() - prepareFlagsForTxSign() - signCmd := authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)) - if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { + // 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 } - os.Stdout = w - return signCmd.RunE(nil, []string{unsignedGenTxFilename}) + + // 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 + } + if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { + return err + } + fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument) + return nil }, } @@ -140,15 +162,35 @@ 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 { + return "", err + } + return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil } -func prepareOutputFile(rootDir, nodeID string) (w *os.File, err error) { - writePath := filepath.Join(rootDir, "config", "gentx") - if err = common.EnsureDir(writePath, 0700); err != nil { - return +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 } - filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)) - return os.Create(filename) + 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/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: 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/_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/_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/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/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index 3a16c8a97b..8093dd3b10 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -8,17 +8,28 @@ 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" + 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" ) +const ( + storeAcc = "acc" + storeSlashing = "slashing" + storeStake = "stake" +) + // rootCmd is the entry point for this binary var ( rootCmd = &cobra.Command{ @@ -34,6 +45,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. @@ -50,40 +72,39 @@ 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), + stakecmd.GetCmdQueryValidatorDelegations(storeStake, 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("stake", cdc), - stakecmd.GetCmdRedelegate("stake", 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( client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), keys.Commands(), client.LineBreak, version.VersionCmd, @@ -97,3 +118,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, 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/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/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/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index adb6169c9e..a001ee8b5f 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -13,11 +13,11 @@ 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" 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" @@ -31,6 +31,7 @@ var ( Use: "democli", Short: "Democoin light-client", } + storeAcc = "acc" ) func main() { @@ -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 @@ -65,20 +70,13 @@ func main() { // add query/post commands (custom to binary) // start with commands common to basecoin rootCmd.AddCommand( - client.GetCommands( - authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), - )...) + authcmd.GetAccountCmd(storeAcc, cdc), + ) + rootCmd.AddCommand( + bankcmd.SendTxCmd(cdc), + ) rootCmd.AddCommand( client.PostCommands( - bankcmd.SendTxCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - ibccmd.IBCTransferCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - ibccmd.IBCRelayCmd(cdc), simplestakingcmd.BondTxCmd(cdc), )...) rootCmd.AddCommand( @@ -96,7 +94,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 +108,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, storeAcc) + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) +} diff --git a/docs/examples/democoin/cmd/democoind/main.go b/docs/examples/democoin/cmd/democoind/main.go index 29e2640fde..8f52340f47 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) { @@ -129,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/examples/democoin/x/cool/app_test.go b/docs/examples/democoin/x/cool/app_test.go index 01cb73ef17..3725f7b9be 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")) @@ -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.CheckBalance(t, mapp, addr1, sdk.Coins{{"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.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.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.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.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{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}, []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}, []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}, []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}, []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}, []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/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/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..1556996b37 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)) @@ -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.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}}) + 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.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) + 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.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) + 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/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/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/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/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/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/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/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/docs/reference/baseapp.md b/docs/reference/baseapp.md index 1828021c2b..ad3b567167 100644 --- a/docs/reference/baseapp.md +++ b/docs/reference/baseapp.md @@ -1,13 +1,124 @@ -# 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. + + +## 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/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/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/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) 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/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/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/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/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/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/coin.go b/types/coin.go index 31f0e98a42..9742545324 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(fmt.Sprintf("negative coin amount: %v\n", 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/context.go b/types/context.go index bfb4c58fed..add88bfc33 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) } //---------------------------------------- @@ -133,20 +133,17 @@ const ( contextKeyMultiStore contextKey = iota contextKeyBlockHeader contextKeyBlockHeight - contextKeyConsensusParams contextKeyChainID contextKeyIsCheckTx contextKeyTxBytes contextKeyLogger contextKeyVoteInfos contextKeyGasMeter + contextKeyBlockGasMeter 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) } @@ -154,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) } @@ -170,11 +163,15 @@ 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) } -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. @@ -199,14 +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 - } - return c.withValue(contextKeyConsensusParams, params). - WithGasMeter(NewGasMeter(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) } @@ -219,6 +208,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) } @@ -230,7 +223,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 } 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/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/gas.go b/types/gas.go index 7b03474670..100b004305 100644 --- a/types/gas.go +++ b/types/gas.go @@ -18,17 +18,27 @@ 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 + GasConsumedToLimit() Gas + Limit() Gas ConsumeGas(amount Gas, descriptor string) + IsPastLimit() bool + IsOutOfGas() bool } type basicGasMeter struct { @@ -48,13 +58,39 @@ func (g *basicGasMeter) GasConsumed() Gas { return g.consumed } +func (g *basicGasMeter) Limit() Gas { + return g.limit +} + +func (g *basicGasMeter) GasConsumedToLimit() Gas { + if g.IsPastLimit() { + return g.limit + } + return g.consumed +} + 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}) } } +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 } @@ -70,8 +106,30 @@ 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) { - 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}) + } +} + +func (g *infiniteGasMeter) IsPastLimit() bool { + return false +} + +func (g *infiniteGasMeter) IsOutOfGas() bool { + return false } // GasConfig defines gas cost for each operation on KVStores diff --git a/types/gas_test.go b/types/gas_test.go index cd2384d12d..5f862dccdb 100644 --- a/types/gas_test.go +++ b/types/gas_test.go @@ -21,15 +21,24 @@ 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 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 } diff --git a/types/int.go b/types/int.go index c2bef7a64f..417a1763d4 100644 --- a/types/int.go +++ b/types/int.go @@ -2,6 +2,7 @@ package types import ( "encoding/json" + "math" "testing" "math/big" @@ -321,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 @@ -352,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 @@ -407,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 @@ -422,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)) @@ -436,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 } @@ -529,6 +540,22 @@ 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) { + 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..9e189858c7 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -590,3 +590,53 @@ func TestEncodingTableUint(t *testing.T) { require.Equal(t, tc.i, i, "Unmarshaled value is different from expected. tc #%d", tcnum) } } + +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 + 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/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/types/result.go b/types/result.go index 63b8a6e18f..924b73b49f 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 @@ -13,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/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/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 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/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.go b/x/auth/ante.go index 8539be06a4..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,27 +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) @@ -128,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++ { @@ -260,13 +227,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) } @@ -277,23 +247,35 @@ 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. + // + // 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(), stdTx.Fee.Gas) // NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). @@ -306,10 +288,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)) } @@ -322,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/ante_test.go b/x/auth/ante_test.go index 98725a74bd..8d7118e746 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) @@ -65,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, "") @@ -79,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) @@ -94,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) @@ -132,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 @@ -144,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) @@ -192,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) } @@ -252,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) } @@ -316,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) @@ -324,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) @@ -333,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) @@ -343,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) } @@ -380,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} @@ -423,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 @@ -482,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) } @@ -531,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) @@ -541,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 @@ -555,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( @@ -567,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) @@ -608,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) @@ -619,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) @@ -628,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) @@ -676,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}, @@ -698,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 @@ -707,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) { @@ -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/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..73facdfd93 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -2,9 +2,8 @@ package cli import ( "fmt" - "github.com/pkg/errors" - "github.com/spf13/viper" "io/ioutil" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -12,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" ) @@ -21,10 +22,11 @@ const ( flagValidateSigs = "validate-signatures" flagOffline = "offline" flagSigOnly = "signature-only" + flagOutfile = "output-document" ) // 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 +43,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 +53,14 @@ 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 + 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] } -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 +78,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 @@ -104,7 +110,20 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra. 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/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/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 f516de4513..8c0ce232be 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -14,9 +14,9 @@ import ( // TxBuilder implements a transaction context created in SDK modules. type TxBuilder struct { Codec *codec.Codec - AccountNumber int64 - Sequence int64 - Gas int64 // TODO: should this turn into uint64? requires further discussion - see #2173 + AccountNumber uint64 + Sequence uint64 + Gas uint64 GasAdjustment float64 SimulateGas bool ChainID string @@ -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), @@ -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 } @@ -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 3ad2ad4123..4ff472fef4 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 ( @@ -21,9 +21,9 @@ var ( func TestTxBuilderBuild(t *testing.T) { type fields struct { Codec *codec.Codec - AccountNumber int64 - Sequence int64 - Gas int64 + AccountNumber uint64 + Sequence uint64 + Gas uint64 GasAdjustment float64 SimulateGas bool ChainID string 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 84bf88a4b7..2f8ae41583 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. @@ -70,10 +122,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, @@ -104,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())) @@ -136,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 26bd792455..ee65561012 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 ( @@ -30,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 @@ -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{}, []uint64{}, []uint64{} + 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}, []uint64{0, 1}, []uint64{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}, []uint64{0, 1}, []uint64{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 = []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) + + 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}, []uint64{0, 1}, []uint64{0, 0} + tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee) + + err = tx.ValidateBasic() + require.NoError(t, err) +} diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 906573eae2..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,14 +140,15 @@ 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) - 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) + mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []uint64{0}, []uint64{1}, true, true, priv1) } func TestMsgSendMultipleOut(t *testing.T) { @@ -167,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}, @@ -210,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}, @@ -246,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}, @@ -258,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/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..1a7c444afb 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -1,13 +1,14 @@ 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" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -28,7 +29,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,9 +65,9 @@ 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) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -78,5 +79,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/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/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/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/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.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..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 } @@ -223,48 +215,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/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index f33c5e0c99..78b1f19452 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)}, @@ -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/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/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/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..7cc68fcc42 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -158,24 +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++ - } -} diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index d2c755cfa6..7b174cd1c4 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -36,10 +36,45 @@ 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)) } +// 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) { @@ -67,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 75863bfdb4..ae4e747299 100644 --- a/x/distribution/simulation/invariants.go +++ b/x/distribution/simulation/invariants.go @@ -7,17 +7,27 @@ 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 { return err } + err = DelAccumInvariants(d, sk)(app) + if err != nil { + return err + } + err = CanWithdrawInvariant(d, stk)(app) + if err != nil { + return err + } return nil } } @@ -48,3 +58,127 @@ 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 + } +} + +// 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/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 83ca7f8cc9..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,42 @@ 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) + + // 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) 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/client/cli/query.go b/x/gov/client/cli/query.go new file mode 100644 index 0000000000..b301c3b1b5 --- /dev/null +++ b/x/gov/client/cli/query.go @@ -0,0 +1,311 @@ +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.NewQueryProposalParams(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 { + bechDepositorAddr := viper.GetString(flagDepositor) + bechVoterAddr := viper.GetString(flagVoter) + strProposalStatus := viper.GetString(flagStatus) + numLimit := uint64(viper.GetInt64(flagNumLimit)) + + var depositorAddr sdk.AccAddress + var voterAddr sdk.AccAddress + var proposalStatus gov.ProposalStatus + + params := gov.NewQueryProposalsParams(proposalStatus, numLimit, voterAddr, depositorAddr) + + if len(bechDepositorAddr) != 0 { + depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) + if err != nil { + return err + } + params.Depositor = depositorAddr + } + + 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(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") + + 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.NewQueryVoteParams(proposalID, voterAddr) + 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.NewQueryProposalParams(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)) + + depositorAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositor)) + if err != nil { + return err + } + + params := gov.NewQueryDepositParams(proposalID, depositorAddr) + 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(flagDepositor, "", "bech32 depositor 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.NewQueryProposalParams(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.NewQueryProposalParams(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..000aef68db 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -2,12 +2,12 @@ package cli import ( "fmt" + "os" "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/gov" @@ -15,7 +15,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" ) @@ -28,7 +28,7 @@ const ( flagDeposit = "deposit" flagVoter = "voter" flagOption = "option" - flagDepositer = "depositer" + flagDepositor = "depositor" flagStatus = "status" flagNumLimit = "limit" flagProposal = "proposal" @@ -80,7 +80,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 { @@ -104,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 @@ -130,7 +130,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,9 +163,9 @@ 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() + depositorAddr, err := cliCtx.GetFromAddress() if err != nil { return err } @@ -177,14 +177,14 @@ 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 } 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 @@ -208,7 +208,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 +218,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 } @@ -230,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]", @@ -248,314 +248,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..72abdb2148 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" ) @@ -20,7 +20,7 @@ import ( const ( RestParamsType = "type" RestProposalID = "proposal-id" - RestDepositer = "depositer" + RestDepositor = "depositor" RestVoter = "voter" RestProposalStatus = "status" RestNumLimit = "limit" @@ -41,7 +41,8 @@ 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") } @@ -57,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 } @@ -81,7 +82,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 @@ -127,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()) @@ -165,7 +166,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 @@ -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 { @@ -268,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") @@ -281,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 @@ -295,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) @@ -319,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 } @@ -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()) @@ -435,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) @@ -450,17 +448,17 @@ 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 { - 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 @@ -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/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/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/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/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 e488d2abab..36bc10a6a0 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}, } @@ -59,19 +57,18 @@ 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 }{ {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}, } 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 171ab469d5..cde85bf940 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 } @@ -102,21 +113,29 @@ 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, depositor sdk.AccAddress) QueryDepositParams { + return QueryDepositParams{ + ProposalID: proposalID, + Depositor: depositor, + } } // 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())) + 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())) } 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 + Depositor sdk.AccAddress + ProposalStatus ProposalStatus + Limit uint64 +} + +// creates a new instance of QueryProposalsParams +func NewQueryProposalsParams(status ProposalStatus, limit uint64, voter, depositor sdk.AccAddress) QueryProposalsParams { + return QueryProposalsParams{ + Voter: voter, + Depositor: depositor, + 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.Depositor, 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..9ee71323ed 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) @@ -71,15 +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(QueryProposalsParams{ - Voter: voter, - Depositer: depositer, - ProposalStatus: status, - Limit: limit, - }), + Data: cdc.MustMarshalJSON(NewQueryProposalsParams(status, limit, voter, depositor)), } bz, err := querier(ctx, []string{QueryProposal}, query) @@ -92,13 +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(QueryDepositParams{ - ProposalID: proposalID, - Depositer: depositer, - }), + Data: cdc.MustMarshalJSON(NewQueryDepositParams(proposalID, depositor)), } 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/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" ) 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..f59b37921b 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)) @@ -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/ibctx.go b/x/ibc/client/cli/ibctx.go index be6dfc940d..afe3824fcc 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -2,13 +2,13 @@ package cli import ( "encoding/hex" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "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 +30,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 { @@ -42,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/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 43ad783f49..4ada207b84 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", @@ -114,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 { @@ -128,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 { @@ -167,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) @@ -190,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/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/ibc/ibc_test.go b/x/ibc/ibc_test.go index 6cd89fded4..2fa24a6c7c 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) @@ -91,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, @@ -108,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, @@ -126,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/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..a393b3a048 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,58 @@ 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)) + + // run the Fib function b.N times + 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/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 9e12cac8ec..1af0e4a033 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -61,22 +61,24 @@ 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], ) - 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( 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], ) } @@ -90,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/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/mock/test_utils.go b/x/mock/test_utils.go index 4e60478fa4..4d5fe05f33 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -50,16 +50,16 @@ 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) 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 @@ -70,17 +70,17 @@ 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 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..279844a4ce 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)) @@ -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,6 +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) - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code) + 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/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index f70be18712..513710ce42 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -5,9 +5,9 @@ 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" + "os" "github.com/spf13/cobra" ) @@ -22,7 +22,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 { @@ -31,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/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/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/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 ef307547c6..bd19643ae3 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -20,13 +20,18 @@ 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 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) { @@ -55,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/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/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/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/app_test.go b/x/stake/app_test.go index b24018a6ce..f6c887c291 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)) @@ -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{}) 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..f730e017f2 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,13 +2,13 @@ package cli import ( "fmt" + "os" "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/stake" @@ -25,78 +25,15 @@ 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 == "" { - 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 @@ -113,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 } @@ -126,7 +66,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 { @@ -155,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 @@ -178,7 +118,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 { @@ -198,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}) @@ -220,7 +160,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 @@ -253,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}) @@ -275,7 +215,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 { @@ -301,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}) @@ -313,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 +} 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 +} 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 32f63f5ed4..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,6 +598,9 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } rounded := returnAmount.TruncateInt() + if rounded.IsZero() { //TODO design consideration + return types.Redelegation{}, types.ErrVerySmallRedelegation(k.Codespace()) + } returnCoin := sdk.NewCoin(k.BondDenom(ctx), rounded) change := returnAmount.Sub(sdk.NewDecFromInt(rounded)) @@ -612,6 +613,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/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/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/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) 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 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/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 } diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index 1a6ed6a641..57dc2fb000 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 @@ -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") } 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 {