Merge develop

This commit is contained in:
Federico Kunze 2018-11-26 13:41:00 +01:00
commit 60fc7c7919
188 changed files with 4200 additions and 2453 deletions

View File

@ -162,6 +162,24 @@ jobs:
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make test_sim_gaia_import_export 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: test_sim_gaia_multi_seed:
<<: *defaults <<: *defaults
parallelism: 1 parallelism: 1
@ -301,6 +319,9 @@ workflows:
- test_sim_gaia_import_export: - test_sim_gaia_import_export:
requires: requires:
- setup_dependencies - setup_dependencies
- test_sim_gaia_simulation_after_import:
requires:
- setup_dependencies
- test_sim_gaia_multi_seed: - test_sim_gaia_multi_seed:
requires: requires:
- setup_dependencies - setup_dependencies

1
Gopkg.lock generated
View File

@ -643,6 +643,7 @@
"github.com/bgentry/speakeasy", "github.com/bgentry/speakeasy",
"github.com/btcsuite/btcd/btcec", "github.com/btcsuite/btcd/btcec",
"github.com/cosmos/go-bip39", "github.com/cosmos/go-bip39",
"github.com/gogo/protobuf/proto",
"github.com/golang/protobuf/proto", "github.com/golang/protobuf/proto",
"github.com/gorilla/mux", "github.com/gorilla/mux",
"github.com/mattn/go-isatty", "github.com/mattn/go-isatty",

View File

@ -184,6 +184,10 @@ test_sim_gaia_import_export:
@echo "Running Gaia import/export simulation. This may take several minutes..." @echo "Running Gaia import/export simulation. This may take several minutes..."
@bash scripts/import-export-sim.sh 50 @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: test_sim_gaia_multi_seed:
@echo "Running multi-seed Gaia simulation. This may take awhile!" @echo "Running multi-seed Gaia simulation. This may take awhile!"
@bash scripts/multisim.sh 25 @bash scripts/multisim.sh 25

View File

@ -11,13 +11,18 @@ BREAKING CHANGES
* [cli] [\#2786](https://github.com/cosmos/cosmos-sdk/pull/2786) Fix redelegation command flow * [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] [\#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] [\#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 * Gaia
* [mint] [\#2825] minting now occurs every block, inflation parameter updates still hourly
* SDK * SDK
* [\#2752](https://github.com/cosmos/cosmos-sdk/pull/2752) Don't hardcode bondable denom. * [\#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. * [\#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. * [\#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 * Tendermint
@ -32,6 +37,7 @@ FEATURES
* [gov][cli] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance * [gov][cli] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance
parameter query commands. parameter query commands.
* [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator. * [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 * Gaia
* [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`. * [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`.
@ -39,9 +45,11 @@ FEATURES
for getting governance parameters. for getting governance parameters.
* [app] \#2663 - Runtime-assertable invariants * [app] \#2663 - Runtime-assertable invariants
* [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`. * [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 * 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 * Tendermint
@ -50,16 +58,27 @@ IMPROVEMENTS
* Gaia REST API (`gaiacli advanced rest-server`) * 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 * [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`) * Gaia CLI (`gaiacli`)
* [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet
* Gaia * Gaia
- #2773 Require moniker to be provided on `gaiad init`. - #2772 Update BaseApp to not persist state when the ante handler fails on DeliverTx.
- #2672 [Makefile] Updated for better Windows compatibility and ledger support logic, get_tools was rewritten as a cross-compatible Makefile. - #2773 Require moniker to be provided on `gaiad init`.
- [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated. - #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 * SDK
- [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization - [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 * Tendermint
- #2796 Update to go-amino 0.14.1 - #2796 Update to go-amino 0.14.1
@ -68,16 +87,20 @@ IMPROVEMENTS
BUG FIXES BUG FIXES
* Gaia REST API (`gaiacli advanced rest-server`) * Gaia REST API (`gaiacli advanced rest-server`)
- [gaia-lite] #2868 Added handler for governance tally endpoit
* Gaia CLI (`gaiacli`) * Gaia CLI (`gaiacli`)
* Gaia * Gaia
* [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address` * [\#2723] Use `cosmosvalcons` Bech32 prefix in `tendermint show-address`
* [\#2742](https://github.com/cosmos/cosmos-sdk/issues/2742) Fix time format of TimeoutCommit override * [\#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 * SDK
- \#2733 [x/gov, x/mock/simulation] Fix governance simulation, update x/gov import/export - \#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 * 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 * [\#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

View File

@ -6,11 +6,11 @@ import (
"runtime/debug" "runtime/debug"
"strings" "strings"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors" "github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
@ -20,11 +20,8 @@ import (
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
) )
// Key to store the header in the DB itself. // Key to store the consensus params in the main store.
// Use the db directly instead of a store to avoid var mainConsensusParamsKey = []byte("consensus_params")
// conflicts with handlers writing to the store
// and to avoid affecting the Merkle root.
var dbHeaderKey = []byte("header")
// Enum mode for app.runTx // Enum mode for app.runTx
type runTxMode uint8 type runTxMode uint8
@ -47,12 +44,13 @@ type BaseApp struct {
cms sdk.CommitMultiStore // Main (uncached) state cms sdk.CommitMultiStore // Main (uncached) state
router Router // handle any kind of message router Router // handle any kind of message
queryRouter QueryRouter // router for redirecting query calls queryRouter QueryRouter // router for redirecting query calls
codespacer *sdk.Codespacer // handle module codespacing
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx 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 // may be nil
anteHandler sdk.AnteHandler // ante handler for fee and auth
initChainer sdk.InitChainer // initialize state with validators and state blob initChainer sdk.InitChainer // initialize state with validators and state blob
beginBlocker sdk.BeginBlocker // logic to run before any txs beginBlocker sdk.BeginBlocker // logic to run before any txs
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes 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 deliverState *state // for DeliverTx
voteInfos []abci.VoteInfo // absent validators from begin block 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 minimumFees sdk.Coins
// flag for sealing // flag for sealing
@ -79,10 +81,6 @@ var _ abci.Application = (*BaseApp)(nil)
// NewBaseApp returns a reference to an initialized BaseApp. // 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. // NOTE: The db is used to store the version number for now.
// Accepts a user-defined txDecoder // Accepts a user-defined txDecoder
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices // 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), cms: store.NewCommitMultiStore(db),
router: NewRouter(), router: NewRouter(),
queryRouter: NewQueryRouter(), queryRouter: NewQueryRouter(),
codespacer: sdk.NewCodespacer(),
txDecoder: txDecoder, txDecoder: txDecoder,
} }
// Register the undefined & root codespaces, which should not be used by
// any modules.
app.codespacer.RegisterOrPanic(sdk.CodespaceRoot)
for _, option := range options { for _, option := range options {
option(app) option(app)
} }
@ -118,11 +111,6 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) {
app.cms.WithTracer(w) 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 // Mount IAVL stores to the provided keys in the BaseApp multistore
func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) { func (app *BaseApp) MountStoresIAVL(keys ...*sdk.KVStoreKey) {
for _, key := range keys { for _, key := range keys {
@ -148,21 +136,23 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) {
} }
// load latest application version // 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() err := app.cms.LoadLatestVersion()
if err != nil { if err != nil {
return err return err
} }
return app.initFromStore(mainKey) return app.initFromMainStore(mainKey)
} }
// load application version // 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) err := app.cms.LoadVersion(version)
if err != nil { if err != nil {
return err return err
} }
return app.initFromStore(mainKey) return app.initFromMainStore(mainKey)
} }
// the last CommitID of the multistore // the last CommitID of the multistore
@ -176,13 +166,34 @@ func (app *BaseApp) LastBlockHeight() int64 {
} }
// initializes the remaining logic from app.cms // 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. // main store should exist.
// TODO: we don't actually need the main store here mainStore := app.cms.GetKVStore(mainKey)
main := app.cms.GetKVStore(mainKey) if mainStore == nil {
if main == nil {
return errors.New("baseapp expects MultiStore with 'main' KVStore") 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 // Needed for `gaiad export`, which inits from store but never calls initchain
app.setCheckState(abci.Header{}) app.setCheckState(abci.Header{})
@ -211,6 +222,10 @@ func (st *state) CacheMultiStore() sdk.CacheMultiStore {
return st.ms.CacheMultiStore() return st.ms.CacheMultiStore()
} }
func (st *state) Context() sdk.Context {
return st.ctx
}
func (app *BaseApp) setCheckState(header abci.Header) { func (app *BaseApp) setCheckState(header abci.Header) {
ms := app.cms.CacheMultiStore() ms := app.cms.CacheMultiStore()
app.checkState = &state{ 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 // ABCI
@ -249,8 +287,15 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
} }
// Implements ABCI // 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) { 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 // Initialize the deliver state and check state with ChainID and run initChain
app.setDeliverState(abci.Header{ChainID: req.ChainId}) app.setDeliverState(abci.Header{ChainID: req.ChainId})
app.setCheckState(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 { if app.initChainer == nil {
return 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) res = app.initChainer(app.deliverState.ctx, req)
// NOTE: we don't commit, but BeginBlock for block 1 // 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": case "version":
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK), Code: uint32(sdk.CodeOK),
Value: []byte(version.GetVersion()), Codespace: string(sdk.CodespaceRoot),
Value: []byte(version.GetVersion()),
} }
default: default:
result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() 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 // Encode with json
value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result)
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK), Code: uint32(sdk.CodeOK),
Value: value, Codespace: string(sdk.CodespaceRoot),
Value: value,
} }
} }
msg := "Expected second parameter to be either simulate or version, neither was present" 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() 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). ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger).
WithMinimumFees(app.minimumFees) WithMinimumFees(app.minimumFees)
@ -400,12 +453,13 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
resBytes, err := querier(ctx, path[2:], req) resBytes, err := querier(ctx, path[2:], req)
if err != nil { if err != nil {
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(err.ABCICode()), Code: uint32(err.Code()),
Log: err.ABCILog(), Codespace: string(err.Codespace()),
Log: err.ABCILog(),
} }
} }
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(sdk.ABCICodeOK), Code: uint32(sdk.CodeOK),
Value: resBytes, Value: resBytes,
} }
} }
@ -427,9 +481,20 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
} else { } else {
// In the first block, app.deliverState.ctx will already be initialized // In the first block, app.deliverState.ctx will already be initialized
// by InitChain. Context is now updated with Header information. // 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 { if app.beginBlocker != nil {
res = app.beginBlocker(app.deliverState.ctx, req) res = app.beginBlocker(app.deliverState.ctx, req)
} }
@ -459,17 +524,18 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) {
Code: uint32(result.Code), Code: uint32(result.Code),
Data: result.Data, Data: result.Data,
Log: result.Log, Log: result.Log,
GasWanted: result.GasWanted, GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints?
GasUsed: result.GasUsed, GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints?
Tags: result.Tags, Tags: result.Tags,
} }
} }
// Implements ABCI // Implements ABCI
func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
// Decode the Tx. // Decode the Tx.
var result sdk.Result
var tx, err = app.txDecoder(txBytes) var tx, err = app.txDecoder(txBytes)
var result sdk.Result
if err != nil { if err != nil {
result = err.Result() result = err.Result()
} else { } else {
@ -482,10 +548,11 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
// Tell the blockchain engine (i.e. Tendermint). // Tell the blockchain engine (i.e. Tendermint).
return abci.ResponseDeliverTx{ return abci.ResponseDeliverTx{
Code: uint32(result.Code), Code: uint32(result.Code),
Codespace: string(result.Codespace),
Data: result.Data, Data: result.Data,
Log: result.Log, Log: result.Log,
GasWanted: result.GasWanted, GasWanted: int64(result.GasWanted), // TODO: Should type accept unsigned ints?
GasUsed: result.GasUsed, GasUsed: int64(result.GasUsed), // TODO: Should type accept unsigned ints?
Tags: result.Tags, Tags: result.Tags,
} }
} }
@ -501,7 +568,6 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error {
// Validate the Msg. // Validate the Msg.
err := msg.ValidateBasic() err := msg.ValidateBasic()
if err != nil { if err != nil {
err = err.WithDefaultCodespace(sdk.CodespaceRoot)
return err return err
} }
} }
@ -509,13 +575,13 @@ func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error {
return nil return nil
} }
// retrieve the context for the ante handler and store the tx bytes; store // retrieve the context for the tx w/ txBytes and other memoized values.
// the vote infos if the tx runs within the deliverTx() state. func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Context) {
func (app *BaseApp) getContextForAnte(mode runTxMode, txBytes []byte) (ctx sdk.Context) { ctx = app.getState(mode).ctx.
// Get the context WithTxBytes(txBytes).
ctx = getState(app, mode).ctx.WithTxBytes(txBytes) WithVoteInfos(app.voteInfos)
if mode == runTxModeDeliver { if mode == runTxModeSimulate {
ctx = ctx.WithVoteInfos(app.voteInfos) ctx, _ = ctx.CacheContext()
} }
return return
} }
@ -526,7 +592,8 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
logs := make([]string, 0, len(msgs)) logs := make([]string, 0, len(msgs))
var data []byte // NOTE: we just append them all (?!) var data []byte // NOTE: we just append them all (?!)
var tags sdk.Tags // also 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 { for msgIdx, msg := range msgs {
// Match route. // Match route.
msgRoute := msg.Route() msgRoute := msg.Route()
@ -553,6 +620,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
if !msgResult.IsOK() { if !msgResult.IsOK() {
logs = append(logs, fmt.Sprintf("Msg %d failed: %s", msgIdx, msgResult.Log)) logs = append(logs, fmt.Sprintf("Msg %d failed: %s", msgIdx, msgResult.Log))
code = msgResult.Code code = msgResult.Code
codespace = msgResult.Codespace
break break
} }
@ -562,10 +630,11 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
// Set the final gas values. // Set the final gas values.
result = sdk.Result{ result = sdk.Result{
Code: code, Code: code,
Data: data, Codespace: codespace,
Log: strings.Join(logs, "\n"), Data: data,
GasUsed: ctx.GasMeter().GasConsumed(), Log: strings.Join(logs, "\n"),
GasUsed: ctx.GasMeter().GasConsumed(),
// TODO: FeeAmount/FeeDenom // TODO: FeeAmount/FeeDenom
Tags: tags, 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, // Returns the applicantion's deliverState if app is in runTxModeDeliver,
// otherwise it returns the application's checkstate. // 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 { if mode == runTxModeCheck || mode == runTxModeSimulate {
return app.checkState return app.checkState
} }
@ -583,24 +652,45 @@ func getState(app *BaseApp, mode runTxMode) *state {
return app.deliverState return app.deliverState
} }
func (app *BaseApp) initializeContext(ctx sdk.Context, mode runTxMode) sdk.Context { // cacheTxContext returns a new context based off of the provided context with
if mode == runTxModeSimulate { // a cache wrapped multi-store.
ctx = ctx.WithMultiStore(getState(app, runTxModeSimulate).CacheMultiStore()) 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 // runTx processes a transaction. The transactions is proccessed via an
// anteHandler. txBytes may be nil in some cases, eg. in tests. Also, in the // anteHandler. The provided txBytes may be nil in some cases, eg. in tests. For
// future we may support "internal" transactions. // further details on transaction execution, reference the BaseApp SDK
// documentation.
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) { func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk.Result) {
// NOTE: GasWanted should be returned by the AnteHandler. GasUsed is // NOTE: GasWanted should be returned by the AnteHandler. GasUsed is
// determined by the GasMeter. We need access to the context to get the gas // determined by the GasMeter. We need access to the context to get the gas
// meter so we initialize upfront. // meter so we initialize upfront.
var gasWanted int64 var gasWanted uint64
var msCache sdk.CacheMultiStore
ctx := app.getContextForAnte(mode, txBytes) ctx := app.getContextForTx(mode, txBytes)
ctx = app.initializeContext(ctx, mode) 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() { defer func() {
if r := recover(); r != nil { 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() 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() var msgs = tx.GetMsgs()
if err := validateBasicTxMsgs(msgs); err != nil { if err := validateBasicTxMsgs(msgs); err != nil {
return err.Result() return err.Result()
} }
// run the ante handler // Execute the ante handler if one is defined.
if app.anteHandler != nil { 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 { if abort {
return result return result
} }
if !newCtx.IsZero() { 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 gasWanted = result.GasWanted
} }
if mode == runTxModeSimulate { if mode == runTxModeCheck {
result = app.runMsgs(ctx, msgs, mode)
result.GasWanted = gasWanted
return return
} }
// Keep the state in a transient CacheWrap in case processing the messages // Create a new context based off of the existing context with a cache wrapped
// fails. // multi-store in case message processing fails.
msCache = getState(app, mode).CacheMultiStore() runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes)
if msCache.TracingEnabled() { result = app.runMsgs(runMsgCtx, msgs, mode)
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)
result.GasWanted = gasWanted result.GasWanted = gasWanted
if mode == runTxModeSimulate {
return
}
// only update state if all messages pass // only update state if all messages pass
if result.IsOK() { if result.IsOK() {
msCache.Write() msCache.Write()
@ -679,14 +791,6 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
// Implements ABCI // Implements ABCI
func (app *BaseApp) Commit() (res abci.ResponseCommit) { func (app *BaseApp) Commit() (res abci.ResponseCommit) {
header := app.deliverState.ctx.BlockHeader() 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 // Write the Deliver state and commit the MultiStore
app.deliverState.ms.Write() app.deliverState.ms.Write()

View File

@ -56,7 +56,9 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp {
require.Equal(t, t.Name(), app.Name()) require.Equal(t, t.Name(), app.Name())
// no stores are mounted // no stores are mounted
require.Panics(t, func() { app.LoadLatestVersion(capKey1) }) require.Panics(t, func() {
app.LoadLatestVersion(capKey1)
})
app.MountStoresIAVL(capKey1, capKey2) app.MountStoresIAVL(capKey1, capKey2)
@ -282,12 +284,24 @@ func TestInitChainer(t *testing.T) {
// Simple tx with a list of Msgs. // Simple tx with a list of Msgs.
type txTest struct { type txTest struct {
Msgs []sdk.Msg Msgs []sdk.Msg
Counter int64 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 // 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 ( const (
routeMsgCounter = "msgCounter" routeMsgCounter = "msgCounter"
@ -297,7 +311,8 @@ const (
// ValidateBasic() fails on negative counters. // ValidateBasic() fails on negative counters.
// Otherwise it's up to the handlers // Otherwise it's up to the handlers
type msgCounter struct { type msgCounter struct {
Counter int64 Counter int64
FailOnHandler bool
} }
// Implements Msg // Implements Msg
@ -315,9 +330,9 @@ func (msg msgCounter) ValidateBasic() sdk.Error {
func newTxCounter(txInt int64, msgInts ...int64) *txTest { func newTxCounter(txInt int64, msgInts ...int64) *txTest {
var msgs []sdk.Msg var msgs []sdk.Msg
for _, msgInt := range msgInts { 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 // 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 { 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) { return func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) {
store := ctx.KVStore(capKey) store := ctx.KVStore(capKey)
msgCounter := tx.(txTest).Counter txTest := tx.(txTest)
res = incrementingCounter(t, store, storeKey, msgCounter)
if txTest.FailOnAnte {
return newCtx, sdk.ErrInternal("ante handler failure").Result(), true
}
res = incrementingCounter(t, store, storeKey, txTest.Counter)
return return
} }
} }
@ -381,10 +401,15 @@ func handlerMsgCounter(t *testing.T, capKey *sdk.KVStoreKey, deliverKey []byte)
var msgCount int64 var msgCount int64
switch m := msg.(type) { switch m := msg.(type) {
case *msgCounter: case *msgCounter:
if m.FailOnHandler {
return sdk.ErrInternal("message handler failure").Result()
}
msgCount = m.Counter msgCount = m.Counter
case *msgCounter2: case *msgCounter2:
msgCount = m.Counter msgCount = m.Counter
} }
return incrementingCounter(t, store, deliverKey, msgCount) return incrementingCounter(t, store, deliverKey, msgCount)
} }
} }
@ -491,6 +516,7 @@ func TestDeliverTx(t *testing.T) {
} }
app := setupBaseApp(t, anteOpt, routerOpt) app := setupBaseApp(t, anteOpt, routerOpt)
app.InitChain(abci.RequestInitChain{})
// Create same codec used in txDecoder // Create same codec used in txDecoder
codec := codec.New() codec := codec.New()
@ -596,7 +622,7 @@ func TestConcurrentCheckDeliver(t *testing.T) {
// Simulate() and Query("/app/simulate", txBytes) should give // Simulate() and Query("/app/simulate", txBytes) should give
// the same results. // the same results.
func TestSimulateTx(t *testing.T) { func TestSimulateTx(t *testing.T) {
gasConsumed := int64(5) gasConsumed := uint64(5)
anteOpt := func(bapp *BaseApp) { anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { 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{} emptyTx := &txTest{}
err := app.Deliver(emptyTx) 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 // Transaction where ValidateBasic fails
@ -701,7 +728,8 @@ func TestRunInvalidTransaction(t *testing.T) {
tx := testCase.tx tx := testCase.tx
res := app.Deliver(tx) res := app.Deliver(tx)
if testCase.fail { 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 { } else {
require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
} }
@ -710,13 +738,15 @@ func TestRunInvalidTransaction(t *testing.T) {
// Transaction with no known route // Transaction with no known route
{ {
unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0} unknownRouteTx := txTest{[]sdk.Msg{msgNoRoute{}}, 0, false}
err := app.Deliver(unknownRouteTx) 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) 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 // Transaction with an unregistered message
@ -732,13 +762,14 @@ func TestRunInvalidTransaction(t *testing.T) {
txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx) txBytes, err := newCdc.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err) require.NoError(t, err)
res := app.DeliverTx(txBytes) 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 // Test that transactions exceeding gas limits fail
func TestTxGasLimits(t *testing.T) { func TestTxGasLimits(t *testing.T) {
gasGranted := int64(10) gasGranted := uint64(10)
anteOpt := func(bapp *BaseApp) { anteOpt := func(bapp *BaseApp) {
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { 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)) newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted))
@ -763,7 +794,7 @@ func TestTxGasLimits(t *testing.T) {
}() }()
count := tx.(*txTest).Counter count := tx.(*txTest).Counter
newCtx.GasMeter().ConsumeGas(count, "counter-ante") newCtx.GasMeter().ConsumeGas(uint64(count), "counter-ante")
res = sdk.Result{ res = sdk.Result{
GasWanted: gasGranted, GasWanted: gasGranted,
} }
@ -775,7 +806,7 @@ func TestTxGasLimits(t *testing.T) {
routerOpt := func(bapp *BaseApp) { routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
count := msg.(msgCounter).Counter count := msg.(msgCounter).Counter
ctx.GasMeter().ConsumeGas(count, "counter-handler") ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler")
return sdk.Result{} return sdk.Result{}
}) })
} }
@ -786,7 +817,7 @@ func TestTxGasLimits(t *testing.T) {
testCases := []struct { testCases := []struct {
tx *txTest tx *txTest
gasUsed int64 gasUsed uint64
fail bool fail bool
}{ }{
{newTxCounter(0, 0), 0, false}, {newTxCounter(0, 0), 0, false},
@ -819,7 +850,181 @@ func TestTxGasLimits(t *testing.T) {
if !tc.fail { if !tc.fail {
require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res)) require.True(t, res.IsOK(), fmt.Sprintf("%d: %v, %v", i, tc, res))
} else { } 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()
}

View File

@ -175,10 +175,22 @@ func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext {
return ctx 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 // WithAccountDecoder returns a copy of the context with an updated account
// decoder. // decoder.
func (ctx CLIContext) WithAccountDecoder(decoder auth.AccountDecoder) CLIContext { func (ctx CLIContext) WithAccountDecoder(cdc *codec.Codec) CLIContext {
ctx.AccDecoder = decoder ctx.AccDecoder = GetAccountDecoder(cdc)
return ctx return ctx
} }

View File

@ -92,7 +92,7 @@ func (ctx CLIContext) GetFromName() (string, error) {
// GetAccountNumber returns the next account number for the given account // GetAccountNumber returns the next account number for the given account
// address. // address.
func (ctx CLIContext) GetAccountNumber(address []byte) (int64, error) { func (ctx CLIContext) GetAccountNumber(address []byte) (uint64, error) {
account, err := ctx.GetAccount(address) account, err := ctx.GetAccount(address)
if err != nil { if err != nil {
return 0, err return 0, err
@ -103,7 +103,7 @@ func (ctx CLIContext) GetAccountNumber(address []byte) (int64, error) {
// GetAccountSequence returns the sequence number for the given account // GetAccountSequence returns the sequence number for the given account
// address. // address.
func (ctx CLIContext) GetAccountSequence(address []byte) (int64, error) { func (ctx CLIContext) GetAccountSequence(address []byte) (uint64, error) {
account, err := ctx.GetAccount(address) account, err := ctx.GetAccount(address)
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -17,25 +17,32 @@ const (
DefaultGasLimit = 200000 DefaultGasLimit = 200000
GasFlagSimulate = "simulate" GasFlagSimulate = "simulate"
FlagUseLedger = "ledger" FlagUseLedger = "ledger"
FlagChainID = "chain-id" FlagChainID = "chain-id"
FlagNode = "node" FlagNode = "node"
FlagHeight = "height" FlagHeight = "height"
FlagGas = "gas" FlagGas = "gas"
FlagGasAdjustment = "gas-adjustment" FlagGasAdjustment = "gas-adjustment"
FlagTrustNode = "trust-node" FlagTrustNode = "trust-node"
FlagFrom = "from" FlagFrom = "from"
FlagName = "name" FlagName = "name"
FlagAccountNumber = "account-number" FlagAccountNumber = "account-number"
FlagSequence = "sequence" FlagSequence = "sequence"
FlagMemo = "memo" FlagMemo = "memo"
FlagFee = "fee" FlagFee = "fee"
FlagAsync = "async" FlagAsync = "async"
FlagJson = "json" FlagJson = "json"
FlagPrintResponse = "print-response" FlagPrintResponse = "print-response"
FlagDryRun = "dry-run" FlagDryRun = "dry-run"
FlagGenerateOnly = "generate-only" FlagGenerateOnly = "generate-only"
FlagIndentResponse = "indent" 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 // 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 { for _, c := range cmds {
c.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") 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().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().Uint64(FlagAccountNumber, 0, "AccountNumber number to sign the tx")
c.Flags().Int64(FlagSequence, 0, "Sequence 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(FlagMemo, "", "Memo to send along with transaction")
c.Flags().String(FlagFee, "", "Fee to pay along with transaction") c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
@ -92,12 +99,32 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
return cmds 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 // Gas flag parsing functions
// GasSetting encapsulates the possible values passed through the --gas flag. // GasSetting encapsulates the possible values passed through the --gas flag.
type GasSetting struct { type GasSetting struct {
Simulate bool Simulate bool
Gas int64 Gas uint64
} }
// Type returns the flag's value type. // Type returns the flag's value type.
@ -113,18 +140,18 @@ func (v *GasSetting) String() string {
if v.Simulate { if v.Simulate {
return GasFlagSimulate return GasFlagSimulate
} }
return strconv.FormatInt(v.Gas, 10) return strconv.FormatUint(v.Gas, 10)
} }
// ParseGasFlag parses the value of the --gas flag. // 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 { switch s {
case "": case "":
gas = DefaultGasLimit gas = DefaultGasLimit
case GasFlagSimulate: case GasFlagSimulate:
simulate = true simulate = true
default: default:
gas, err = strconv.ParseInt(s, 10, 64) gas, err = strconv.ParseUint(s, 10, 64)
if err != nil { if err != nil {
err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate) err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate)
return return

View File

@ -2,20 +2,17 @@ package keys
import ( import (
"fmt" "fmt"
"github.com/syndtr/goleveldb/leveldb/opt" "net/http"
"path/filepath" "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" "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" "github.com/tendermint/tendermint/libs/cli"
dbm "github.com/tendermint/tendermint/libs/db" 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 // KeyDBName is the directory under root where we store the keys

View File

@ -284,7 +284,7 @@ func TestCoinSend(t *testing.T) {
// test failure with negative gas // test failure with negative gas
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "") 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 // test failure with 0 gas
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "") 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.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
require.Equal(t, uint32(0), resultTx.CheckTx.Code) require.Equal(t, uint32(0), resultTx.CheckTx.Code)
require.Equal(t, uint32(0), resultTx.DeliverTx.Code) require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
require.Equal(t, gasEstimate, resultTx.DeliverTx.GasWanted) require.Equal(t, gasEstimate, uint64(resultTx.DeliverTx.GasWanted))
require.Equal(t, gasEstimate, resultTx.DeliverTx.GasUsed) require.Equal(t, gasEstimate, uint64(resultTx.DeliverTx.GasUsed))
} }
func TestTxs(t *testing.T) { func TestTxs(t *testing.T) {
@ -696,7 +696,7 @@ func TestDeposit(t *testing.T) {
func TestVote(t *testing.T) { func TestVote(t *testing.T) {
name, password := "test", "1234567890" name, password := "test", "1234567890"
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) 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() defer cleanup()
// create SubmitProposal TX // create SubmitProposal TX
@ -714,7 +714,7 @@ func TestVote(t *testing.T) {
proposal := getProposal(t, port, proposalID) proposal := getProposal(t, port, proposalID)
require.Equal(t, "Test", proposal.GetTitle()) require.Equal(t, "Test", proposal.GetTitle())
// create SubmitProposal TX // deposit
resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5) resultTx = doDeposit(t, port, seed, name, password, addr, proposalID, 5)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
@ -722,7 +722,7 @@ func TestVote(t *testing.T) {
proposal = getProposal(t, port, proposalID) proposal = getProposal(t, port, proposalID)
require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus()) require.Equal(t, gov.StatusVotingPeriod, proposal.GetStatus())
// create SubmitProposal TX // vote
resultTx = doVote(t, port, seed, name, password, addr, proposalID) resultTx = doVote(t, port, seed, name, password, addr, proposalID)
tests.WaitForHeight(resultTx.Height+1, port) tests.WaitForHeight(resultTx.Height+1, port)
@ -734,6 +734,20 @@ func TestVote(t *testing.T) {
vote := getVote(t, port, proposalID, addr) vote := getVote(t, port, proposalID, addr)
require.Equal(t, proposalID, vote.ProposalID) require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option) 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) { func TestUnjail(t *testing.T) {
@ -853,11 +867,11 @@ func TestProposalsQuery(t *testing.T) {
require.Equal(t, proposalID3, (proposals[2]).GetProposalID()) require.Equal(t, proposalID3, (proposals[2]).GetProposalID())
// Test query deposited by addr1 // Test query deposited by addr1
proposals = getProposalsFilterDepositer(t, port, addrs[0]) proposals = getProposalsFilterDepositor(t, port, addrs[0])
require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) require.Equal(t, proposalID1, (proposals[0]).GetProposalID())
// Test query deposited by addr2 // 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, proposalID2, (proposals[0]).GetProposalID())
require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) require.Equal(t, proposalID3, (proposals[1]).GetProposalID())
@ -871,7 +885,7 @@ func TestProposalsQuery(t *testing.T) {
require.Equal(t, proposalID3, (proposals[0]).GetProposalID()) require.Equal(t, proposalID3, (proposals[0]).GetProposalID())
// Test query voted and deposited by addr1 // 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()) require.Equal(t, proposalID2, (proposals[0]).GetProposalID())
// Test query votes on Proposal 2 // Test query votes on Proposal 2
@ -1381,8 +1395,8 @@ func getDeposits(t *testing.T, port string, proposalID uint64) []gov.Deposit {
return deposits return deposits
} }
func getDeposit(t *testing.T, port string, proposalID uint64, depositerAddr sdk.AccAddress) gov.Deposit { 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, depositerAddr), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d/deposits/%s", proposalID, depositorAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var deposit gov.Deposit var deposit gov.Deposit
err := cdc.UnmarshalJSON([]byte(body), &deposit) err := cdc.UnmarshalJSON([]byte(body), &deposit)
@ -1408,6 +1422,15 @@ func getVotes(t *testing.T, port string, proposalID uint64) []gov.Vote {
return votes 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 { func getProposalsAll(t *testing.T, port string) []gov.Proposal {
res, body := Request(t, port, "GET", "/gov/proposals", nil) res, body := Request(t, port, "GET", "/gov/proposals", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
@ -1418,8 +1441,8 @@ func getProposalsAll(t *testing.T, port string) []gov.Proposal {
return proposals return proposals
} }
func getProposalsFilterDepositer(t *testing.T, port string, depositerAddr sdk.AccAddress) []gov.Proposal { func getProposalsFilterDepositor(t *testing.T, port string, depositorAddr sdk.AccAddress) []gov.Proposal {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s", depositerAddr), nil) res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositor=%s", depositorAddr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body) require.Equal(t, http.StatusOK, res.StatusCode, body)
var proposals []gov.Proposal var proposals []gov.Proposal
@ -1438,8 +1461,8 @@ func getProposalsFilterVoter(t *testing.T, port string, voterAddr sdk.AccAddress
return proposals return proposals
} }
func getProposalsFilterVoterDepositer(t *testing.T, port string, voterAddr, depositerAddr sdk.AccAddress) []gov.Proposal { func getProposalsFilterVoterDepositor(t *testing.T, port string, voterAddr, depositorAddr sdk.AccAddress) []gov.Proposal {
res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals?depositer=%s&voter=%s", depositerAddr, voterAddr), nil) 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) require.Equal(t, http.StatusOK, res.StatusCode, body)
var proposals []gov.Proposal var proposals []gov.Proposal
@ -1501,7 +1524,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
// deposit on proposal // deposit on proposal
jsonStr := []byte(fmt.Sprintf(`{ jsonStr := []byte(fmt.Sprintf(`{
"depositer": "%s", "depositor": "%s",
"amount": [{ "denom": "%s", "amount": "%d" }], "amount": [{ "denom": "%s", "amount": "%d" }],
"base_req": { "base_req": {
"name": "%s", "name": "%s",

View File

@ -10,176 +10,169 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys" "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" "github.com/cosmos/cosmos-sdk/codec"
keybase "github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/server" "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/gorilla/mux"
"github.com/rakyll/statik/fs" "github.com/rakyll/statik/fs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmserver "github.com/tendermint/tendermint/rpc/lib/server" tmserver "github.com/tendermint/tendermint/rpc/lib/server"
// Import statik for light client stuff
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
) )
const ( // RestServer represents the Light Client Rest server
flagListenAddr = "laddr" type RestServer struct {
flagCORS = "cors" Mux *mux.Router
flagMaxOpenConnections = "max-open" CliCtx context.CLIContext
flagInsecure = "insecure" KeyBase keybase.Keybase
flagSSLHosts = "ssl-hosts" Cdc *codec.Codec
flagSSLCertFile = "ssl-certfile"
flagSSLKeyFile = "ssl-keyfile" 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 // ServeCommand will generate a long-running rest server
// (aka Light Client Daemon) that exposes functionality similar // (aka Light Client Daemon) that exposes functionality similar
// to the cli, but over rest // to the cli, but over rest
func ServeCommand(cdc *codec.Codec) *cobra.Command { func (rs *RestServer) ServeCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rest-server", Use: "rest-server",
Short: "Start LCD (light-client daemon), a local REST server", Short: "Start LCD (light-client daemon), a local REST server",
RunE: func(cmd *cobra.Command, args []string) (err error) { RunE: func(cmd *cobra.Command, args []string) (err error) {
listenAddr := viper.GetString(flagListenAddr) rs.setKeybase(nil)
handler := createHandler(cdc) // 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) return err
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
}, },
} }
cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") client.RegisterRestServerFlags(cmd)
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))
return cmd return cmd
} }
func createHandler(cdc *codec.Codec) *mux.Router { func (rs *RestServer) registerSwaggerUI() {
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) {
statikFS, err := fs.New() statikFS, err := fs.New()
if err != nil { if err != nil {
panic(err) panic(err)
} }
staticServer := http.FileServer(statikFS) 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 { func validateCertKeyFiles(certFile, keyFile string) error {

View File

@ -1226,8 +1226,8 @@ paths:
required: false required: false
type: string type: string
- in: query - in: query
name: depositer name: depositor
description: depositer address description: depositor address
required: false required: false
type: string type: string
- in: query - in: query
@ -1281,7 +1281,7 @@ paths:
properties: properties:
base_req: base_req:
"$ref": "#/definitions/BaseReq" "$ref": "#/definitions/BaseReq"
depositer: depositor:
"$ref": "#/definitions/Address" "$ref": "#/definitions/Address"
amount: amount:
type: array type: array
@ -1321,6 +1321,29 @@ paths:
description: Invalid proposal id description: Invalid proposal id
500: 500:
description: Internal Server Error 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: /gov/proposals/{proposalId}/votes:
post: post:
summary: Vote a proposal summary: Vote a proposal
@ -1418,10 +1441,10 @@ paths:
description: Invalid proposal id description: Invalid proposal id
500: 500:
description: Internal Server Error description: Internal Server Error
/gov/proposals/{proposalId}/deposits/{depositer}: /gov/proposals/{proposalId}/deposits/{depositor}:
get: get:
summary: Query deposit summary: Query deposit
description: Query deposit by proposalId and depositer address description: Query deposit by proposalId and depositor address
produces: produces:
- application/json - application/json
tags: tags:
@ -1433,8 +1456,8 @@ paths:
required: true required: true
in: path in: path
- type: string - type: string
description: Bech32 depositer address description: Bech32 depositor address
name: depositer name: depositor
required: true required: true
in: path in: path
responses: responses:
@ -1443,7 +1466,7 @@ paths:
schema: schema:
$ref: "#/definitions/Deposit" $ref: "#/definitions/Deposit"
400: 400:
description: Invalid proposal id or depositer address description: Invalid proposal id or depositor address
404: 404:
description: Found no deposit description: Found no deposit
500: 500:
@ -1893,16 +1916,7 @@ definitions:
proposal_status: proposal_status:
type: string type: string
tally_result: tally_result:
type: object $ref: "#/definitions/TallyResult"
properties:
yes:
type: string
abstain:
type: string
no:
type: string
no_with_veto:
type: string
submit_time: submit_time:
type: string type: string
total_deposit: total_deposit:
@ -1920,8 +1934,23 @@ definitions:
"$ref": "#/definitions/Coin" "$ref": "#/definitions/Coin"
proposal_id: proposal_id:
type: integer type: integer
depositer: depositor:
"$ref": "#/definitions/Address" "$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: Vote:
type: object type: object
properties: properties:

View File

@ -19,6 +19,8 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys" "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" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
@ -45,6 +47,12 @@ import (
"github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/proxy"
tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmrpc "github.com/tendermint/tendermint/rpc/lib/server"
tmtypes "github.com/tendermint/tendermint/types" 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 // 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 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. // CreateAddr adds an address to the key store and returns an address and seed.
// It also requires that the key could be created. // It also requires that the key could be created.
func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) { func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) {
@ -288,7 +303,7 @@ func InitializeTestLCD(
require.NoError(t, err) require.NoError(t, err)
tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress)) tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress))
lcd, err := startLCD(logger, listenAddr, cdc) lcd, err := startLCD(logger, listenAddr, cdc, t)
require.NoError(t, err) require.NoError(t, err)
tests.WaitForLCDStart(port) tests.WaitForLCDStart(port)
@ -347,8 +362,23 @@ func startTM(
// startLCD starts the LCD. // startLCD starts the LCD.
// //
// NOTE: This causes the thread to block. // NOTE: This causes the thread to block.
func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) { func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing.T) (net.Listener, error) {
return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) 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 // Request makes a test LCD test request. It returns a response object and a

View File

@ -34,7 +34,7 @@ func WriteErrorResponse(w http.ResponseWriter, status int, err string) {
// WriteSimulationResponse prepares and writes an HTTP // WriteSimulationResponse prepares and writes an HTTP
// response for transactions simulations. // response for transactions simulations.
func WriteSimulationResponse(w http.ResponseWriter, gas int64) { func WriteSimulationResponse(w http.ResponseWriter, gas uint64) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas))) w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas)))
} }
@ -124,8 +124,8 @@ type BaseReq struct {
Name string `json:"name"` Name string `json:"name"`
Password string `json:"password"` Password string `json:"password"`
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
Gas string `json:"gas"` Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"` GasAdjustment string `json:"gas_adjustment"`
} }

View File

@ -3,6 +3,7 @@ package utils
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"os" "os"
"github.com/cosmos/cosmos-sdk/client/context" "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 // CalculateGas simulates the execution of a transaction and returns
// both the estimate obtained by the query and the adjusted amount. // 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 // run a simulation (via /app/simulate query) to
// estimate gas and update TxBuilder accordingly // estimate gas and update TxBuilder accordingly
rawRes, err := queryFunc("/app/simulate", txBytes) 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. // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
// Don't perform online validation or lookups if offline is true. // 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 var stdTx auth.StdTx
if offline { if offline {
stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs) 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) json, err := txBldr.Codec.MarshalJSON(stdTx)
if err == nil { if err == nil {
fmt.Printf("%s\n", json) fmt.Fprintf(w, "%s\n", json)
} }
return return
} }
@ -152,7 +153,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string,
// nolint // nolint
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. // 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) txBytes, err := txBldr.BuildWithPubKey(name, msgs)
if err != nil { if err != nil {
return return
@ -161,11 +162,11 @@ func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name stri
return return
} }
func adjustGasEstimate(estimate int64, adjustment float64) int64 { func adjustGasEstimate(estimate uint64, adjustment float64) uint64 {
return int64(adjustment * float64(estimate)) 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 var simulationResult sdk.Result
if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil { if err := cdc.UnmarshalBinaryLengthPrefixed(rawRes, &simulationResult); err != nil {
return 0, err return 0, err

View File

@ -14,16 +14,16 @@ func TestParseQueryResponse(t *testing.T) {
cdc := app.MakeCodec() cdc := app.MakeCodec()
sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10}) sdkResBytes := cdc.MustMarshalBinaryLengthPrefixed(sdk.Result{GasUsed: 10})
gas, err := parseQueryResponse(cdc, sdkResBytes) gas, err := parseQueryResponse(cdc, sdkResBytes)
assert.Equal(t, gas, int64(10)) assert.Equal(t, gas, uint64(10))
assert.Nil(t, err) assert.Nil(t, err)
gas, err = parseQueryResponse(cdc, []byte("fuzzy")) gas, err = parseQueryResponse(cdc, []byte("fuzzy"))
assert.Equal(t, gas, int64(0)) assert.Equal(t, gas, uint64(0))
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestCalculateGas(t *testing.T) { func TestCalculateGas(t *testing.T) {
cdc := app.MakeCodec() 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) { return func(string, common.HexBytes) ([]byte, error) {
if wantErr { if wantErr {
return nil, errors.New("") return nil, errors.New("")
@ -32,15 +32,15 @@ func TestCalculateGas(t *testing.T) {
} }
} }
type args struct { type args struct {
queryFuncGasUsed int64 queryFuncGasUsed uint64
queryFuncWantErr bool queryFuncWantErr bool
adjustment float64 adjustment float64
} }
tests := []struct { tests := []struct {
name string name string
args args args args
wantEstimate int64 wantEstimate uint64
wantAdjusted int64 wantAdjusted uint64
wantErr bool wantErr bool
}{ }{
{"error", args{0, true, 1.2}, 0, 0, true}, {"error", args{0, true, 1.2}, 0, 0, true},

View File

@ -1,7 +1,6 @@
package app package app
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -22,7 +21,6 @@ import (
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
) )
const ( const (
@ -113,7 +111,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.cdc, app.cdc,
app.keyStake, app.tkeyStake, app.keyStake, app.tkeyStake,
app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace),
app.RegisterCodespace(stake.DefaultCodespace), stake.DefaultCodespace,
) )
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
app.paramsKeeper.Subspace(mint.DefaultParamspace), app.paramsKeeper.Subspace(mint.DefaultParamspace),
@ -124,19 +122,19 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
app.keyDistr, app.keyDistr,
app.paramsKeeper.Subspace(distr.DefaultParamspace), app.paramsKeeper.Subspace(distr.DefaultParamspace),
app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper, app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper,
app.RegisterCodespace(stake.DefaultCodespace), distr.DefaultCodespace,
) )
app.slashingKeeper = slashing.NewKeeper( app.slashingKeeper = slashing.NewKeeper(
app.cdc, app.cdc,
app.keySlashing, app.keySlashing,
&stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
app.RegisterCodespace(slashing.DefaultCodespace), slashing.DefaultCodespace,
) )
app.govKeeper = gov.NewKeeper( app.govKeeper = gov.NewKeeper(
app.cdc, app.cdc,
app.keyGov, app.keyGov,
app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper, app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper,
app.RegisterCodespace(gov.DefaultCodespace), gov.DefaultCodespace,
) )
// register the staking hooks // 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 // initialize store from a genesis state
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisState) []abci.ValidatorUpdate {
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, "")
}
// sort by account number to maintain consistency // sort by account number to maintain consistency
sort.Slice(genesisState.Accounts, func(i, j int) bool { sort.Slice(genesisState.Accounts, func(i, j int) bool {
return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber 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) 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 // sanity check
if len(req.Validators) > 0 { 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{ return abci.ResponseInitChain{
Validators: validators, 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 // load a particular height
func (app *GaiaApp) LoadHeight(height int64) error { func (app *GaiaApp) LoadHeight(height int64) error {
return app.LoadVersion(height, app.keyMain) return app.LoadVersion(height, app.keyMain)

View File

@ -49,6 +49,6 @@ func TestGaiadExport(t *testing.T) {
// Making a new app object with the db, so that initchain hasn't been called // 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) 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") require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
} }

156
cmd/gaia/app/export.go Normal file
View File

@ -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
})
}

View File

@ -41,8 +41,10 @@ type GenesisState struct {
GenTxs []json.RawMessage `json:"gentxs"` GenTxs []json.RawMessage `json:"gentxs"`
} }
func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stakeData stake.GenesisState, mintData mint.GenesisState, func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { stakeData stake.GenesisState, mintData mint.GenesisState,
distrData distr.GenesisState, govData gov.GenesisState,
slashingData slashing.GenesisState) GenesisState {
return GenesisState{ return GenesisState{
Accounts: accounts, Accounts: accounts,
@ -59,8 +61,8 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, stak
type GenesisAccount struct { type GenesisAccount struct {
Address sdk.AccAddress `json:"address"` Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence_number"` Sequence uint64 `json:"sequence_number"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
} }
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { 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 // Create the core parameters for genesis initialization for gaia
// note that the pubkey input is this machines pubkey // note that the pubkey input is this machines pubkey
func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( 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) "account %v not in genesis.json: %+v", addr, addrMap)
} }
if acc.Coins.AmountOf(msg.Delegation.Denom).LT(msg.Delegation.Amount) { 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) 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 { func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount {
accAuth := auth.NewBaseAccountWithAddress(addr) accAuth := auth.NewBaseAccountWithAddress(addr)
coins :=sdk.Coins{ coins := sdk.Coins{
{"fooToken", sdk.NewInt(1000)}, sdk.NewCoin("fooToken", sdk.NewInt(1000)),
{bondDenom, freeFermionsAcc}, sdk.NewCoin(bondDenom, freeFermionsAcc),
} }
coins.Sort() coins.Sort()
accAuth.Coins = coins accAuth.Coins = coins
return NewGenesisAccount(&accAuth) return NewGenesisAccount(&accAuth)
} }

View File

@ -59,11 +59,13 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
if numInitiallyBonded > numAccs { if numInitiallyBonded > numAccs {
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 // Randomly generate some genesis accounts
for _, acc := range accs { 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{ genesisAccounts = append(genesisAccounts, GenesisAccount{
Address: acc.Address, Address: acc.Address,
Coins: coins, Coins: coins,
@ -86,7 +88,8 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
GovernancePenalty: sdk.NewDecWithPrec(1, 2), 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{ stakeGenesis := stake.GenesisState{
Pool: stake.InitialPool(), Pool: stake.InitialPool(),
Params: stake.Params{ Params: stake.Params{
@ -95,7 +98,8 @@ func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
BondDenom: stakeTypes.DefaultBondDenom, 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{ slashingGenesis := slashing.GenesisState{
Params: slashing.Params{ Params: slashing.Params{
MaxEvidenceAge: stakeGenesis.Params.UnbondingTime, 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))), 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{ mintGenesis := mint.GenesisState{
Minter: mint.Minter{ Minter: mint.InitialMinter(
InflationLastTime: time.Unix(0, 0), sdk.NewDecWithPrec(int64(r.Intn(99)), 2)),
Inflation: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), Params: mint.NewParams(
}, stakeTypes.DefaultBondDenom,
Params: mint.Params{ sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
MintDenom: stakeTypes.DefaultBondDenom, sdk.NewDecWithPrec(20, 2),
InflationRateChange: sdk.NewDecWithPrec(int64(r.Intn(99)), 2), sdk.NewDecWithPrec(7, 2),
InflationMax: sdk.NewDecWithPrec(20, 2), sdk.NewDecWithPrec(67, 2),
InflationMin: sdk.NewDecWithPrec(7, 2), uint64(60*60*8766/5)),
GoalBonded: sdk.NewDecWithPrec(67, 2),
},
} }
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 validators []stake.Validator
var delegations []stake.Delegation 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 := stake.NewValidator(valAddr, accs[i].PubKey, stake.Description{})
validator.Tokens = sdk.NewDec(amount) validator.Tokens = sdk.NewDec(amount)
validator.DelegatorShares = 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) validators = append(validators, validator)
delegations = append(delegations, delegation) delegations = append(delegations, delegation)
} }
@ -206,7 +210,7 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
// Run randomized simulation // Run randomized simulation
// TODO parameterize numbers, save for a later PR // TODO parameterize numbers, save for a later PR
err := simulation.SimulateFromSeed( _, err := simulation.SimulateFromSeed(
b, app.BaseApp, appStateFn, seed, b, app.BaseApp, appStateFn, seed,
testAndRunTxs(app), testAndRunTxs(app),
[]simulation.RandSetup{}, []simulation.RandSetup{},
@ -249,7 +253,7 @@ func TestFullGaiaSimulation(t *testing.T) {
require.Equal(t, "GaiaApp", app.Name()) require.Equal(t, "GaiaApp", app.Name())
// Run randomized simulation // Run randomized simulation
err := simulation.SimulateFromSeed( _, err := simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed, t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app), testAndRunTxs(app),
[]simulation.RandSetup{}, []simulation.RandSetup{},
@ -291,7 +295,7 @@ func TestGaiaImportExport(t *testing.T) {
require.Equal(t, "GaiaApp", app.Name()) require.Equal(t, "GaiaApp", app.Name())
// Run randomized simulation // Run randomized simulation
err := simulation.SimulateFromSeed( _, err := simulation.SimulateFromSeed(
t, app.BaseApp, appStateFn, seed, t, app.BaseApp, appStateFn, seed,
testAndRunTxs(app), testAndRunTxs(app),
[]simulation.RandSetup{}, []simulation.RandSetup{},
@ -311,7 +315,7 @@ func TestGaiaImportExport(t *testing.T) {
fmt.Printf("Exporting genesis...\n") fmt.Printf("Exporting genesis...\n")
appState, _, err := app.ExportAppStateAndValidators() appState, _, err := app.ExportAppStateAndValidators(false)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -326,15 +330,16 @@ func TestGaiaImportExport(t *testing.T) {
}() }()
newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil) newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil)
require.Equal(t, "GaiaApp", newApp.Name()) require.Equal(t, "GaiaApp", newApp.Name())
request := abci.RequestInitChain{ var genesisState GenesisState
AppStateBytes: appState, err = app.cdc.UnmarshalJSON(appState, &genesisState)
if err != nil {
panic(err)
} }
newApp.InitChain(request) ctxB := newApp.NewContext(true, abci.Header{})
newApp.Commit() newApp.initFromGenesisState(ctxB, genesisState)
fmt.Printf("Comparing stores...\n") fmt.Printf("Comparing stores...\n")
ctxA := app.NewContext(true, abci.Header{}) ctxA := app.NewContext(true, abci.Header{})
ctxB := newApp.NewContext(true, abci.Header{})
type StoreKeysPrefixes struct { type StoreKeysPrefixes struct {
A sdk.StoreKey A sdk.StoreKey
B 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 // TODO: Make another test for the fuzzer itself, which just has noOp txs
// and doesn't depend on gaia // and doesn't depend on gaia
func TestAppStateDeterminism(t *testing.T) { func TestAppStateDeterminism(t *testing.T) {

View File

@ -370,7 +370,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, " 1 - Test", proposalsQuery) require.Equal(t, " 1 - Test", proposalsQuery)
deposit := executeGetDeposit(t, 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)) fooAddr, flags))
require.Equal(t, int64(5), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) 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()) require.Equal(t, int64(15), deposits[0].Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64())
deposit = executeGetDeposit(t, 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)) fooAddr, flags))
require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) 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.True(t, success)
require.Empty(t, stderr) require.Empty(t, stderr)
msg := unmarshalStdTx(t, stdout) 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, len(msg.Msgs), 1)
require.Equal(t, 0, len(msg.GetSignatures())) require.Equal(t, 0, len(msg.GetSignatures()))
@ -491,7 +491,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
require.True(t, success) require.True(t, success)
require.Empty(t, stderr) require.Empty(t, stderr)
msg = unmarshalStdTx(t, stdout) 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, len(msg.Msgs), 1)
require.Equal(t, 0, len(msg.GetSignatures())) require.Equal(t, 0, len(msg.GetSignatures()))
@ -542,16 +542,19 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf(
"gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name())) "gaiacli tx broadcast %v --json %v", flags, signedTxFile.Name()))
require.True(t, success) require.True(t, success)
var result struct { var result struct {
Response abci.ResponseDeliverTx Response abci.ResponseDeliverTx
} }
require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result))
require.Equal(t, msg.Fee.Gas, result.Response.GasUsed) require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasUsed))
require.Equal(t, msg.Fee.Gas, result.Response.GasWanted) require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasWanted))
tests.WaitForNextNBlocksTM(2, port) tests.WaitForNextNBlocksTM(2, port)
barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags))
require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64())
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) 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) { func initializeFixtures(t *testing.T) (chainID, servAddr, port string) {
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "") 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 foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", 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) executeWriteCheckErr(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 bar", gaiacliHome), app.DefaultKeyPass)
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf( fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf(
"gaiacli keys show foo --output=json --home=%s", gaiacliHome)) "gaiacli keys show foo --output=json --home=%s", gaiacliHome))
chainID = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome)) 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) require.NoError(t, err)
genDoc.AppState = appStateJSON genDoc.AppState = appStateJSON
genDoc.SaveAs(genFile) genDoc.SaveAs(genFile)
executeWrite(t, fmt.Sprintf( executeWriteCheckErr(t, fmt.Sprintf(
"gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome), "gaiad gentx --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome),
app.DefaultKeyPass) 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 // get a free port, also setup some common flags
servAddr, port, err = server.FreeTCPAddr() servAddr, port, err = server.FreeTCPAddr()
require.NoError(t, err) require.NoError(t, err)
@ -664,6 +668,10 @@ func readGenesisFile(t *testing.T, genFile string) types.GenesisDoc {
//___________________________________________________________________________________ //___________________________________________________________________________________
// executors // 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) { func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) {
exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...)
return return

View File

@ -1,64 +1,98 @@
package main package main
import ( import (
"fmt"
"net/http"
"os" "os"
"path" "path"
"github.com/rakyll/statik/fs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version" "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" _ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
) )
const ( const (
storeAcc = "acc" storeAcc = "acc"
storeGov = "gov" storeGov = "gov"
storeSlashing = "slashing" storeSlashing = "slashing"
storeStake = "stake" storeStake = "stake"
queryRouteStake = "stake" storeDist = "distr"
)
// rootCmd is the entry point for this binary
var (
rootCmd = &cobra.Command{
Use: "gaiacli",
Short: "Command line interface for interacting with gaiad",
}
) )
func main() { func main() {
// Configure cobra to sort commands
cobra.EnableCommandSorting = false cobra.EnableCommandSorting = false
// Instantiate the codec for the command line application
cdc := app.MakeCodec() cdc := app.MakeCodec()
// Read in the configuration file for the sdk
config := sdk.GetConfig() config := sdk.GetConfig()
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal() 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 // TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc // 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 // Construct Root Command
rootCmd.AddCommand( rootCmd.AddCommand(
rpc.InitClientCommand(), rpc.InitClientCommand(),
rpc.StatusCommand(), rpc.StatusCommand(),
client.ConfigCmd(), client.ConfigCmd(),
queryCmd(cdc), queryCmd(cdc, mc),
txCmd(cdc), txCmd(cdc, mc),
client.LineBreak, client.LineBreak,
lcd.ServeCommand(cdc), rs.ServeCommand(),
client.LineBreak, client.LineBreak,
keys.Commands(), keys.Commands(),
client.LineBreak, client.LineBreak,
@ -74,9 +108,77 @@ func main() {
err = executor.Execute() err = executor.Execute()
if err != nil { 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) panic(err)
} }
staticServer := http.FileServer(statikFS)
rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer))
} }
func initConfig(cmd *cobra.Command) error { func initConfig(cmd *cobra.Command) error {

View File

@ -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
}

View File

@ -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
}

View File

@ -62,7 +62,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application
} }
func exportAppStateAndTMValidators( 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) { ) (json.RawMessage, []tmtypes.GenesisValidator, error) {
gApp := app.NewGaiaApp(logger, db, traceStore) gApp := app.NewGaiaApp(logger, db, traceStore)
if height != -1 { if height != -1 {
@ -71,5 +71,5 @@ func exportAppStateAndTMValidators(
return nil, nil, err return nil, nil, err
} }
} }
return gApp.ExportAppStateAndValidators() return gApp.ExportAppStateAndValidators(forZeroHeight)
} }

View File

@ -175,8 +175,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
// add handlers // add handlers
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) 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.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), app.RegisterCodespace(slashing.DefaultCodespace)) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace)
// register message routes // register message routes
app.Router(). app.Router().

View File

@ -25,14 +25,14 @@ func TestAddGenesisAccount(t *testing.T) {
}{ }{
{ {
"valid account", "valid account",
args{ args{
app.GenesisState{}, app.GenesisState{},
addr1, addr1,
sdk.Coins{}, sdk.Coins{},
}, },
false}, false},
{"dup account", args{ {"dup account", args{
app.GenesisState{Accounts: []app.GenesisAccount{app.GenesisAccount{Address:addr1}}}, app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}},
addr1, addr1,
sdk.Coins{}}, true}, sdk.Coins{}}, true},
} }

View File

@ -1,25 +1,32 @@
package init package init
import ( import (
"bytes"
"fmt" "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"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys" "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/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types" 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" "github.com/cosmos/cosmos-sdk/x/stake/client/cli"
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config" cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
tmcli "github.com/tendermint/tendermint/libs/cli" tmcli "github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/common"
"io/ioutil"
"os"
"path/filepath"
) )
const ( const (
@ -66,7 +73,9 @@ following delegation and commission default parameters:
if err != nil { if err != nil {
return err 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 return err
} }
@ -79,27 +88,40 @@ following delegation and commission default parameters:
} }
// Run gaiad tx create-validator // Run gaiad tx create-validator
prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey)
createValidatorCmd := cli.GetCmdCreateValidator(cdc) txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().WithCodec(cdc)
w, err := ioutil.TempFile("", "gentx") cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr)
if err != nil { if err != nil {
return err 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() // write the unsigned transaction to the buffer
signCmd := authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)) w := bytes.NewBuffer([]byte{})
if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { if err := utils.PrintUnsignedStdTx(w, txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil {
return err 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() { func makeOutputFilepath(rootDir, nodeID string) (string, error) {
viper.Set("offline", true) 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) { func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) {
writePath := filepath.Join(rootDir, "config", "gentx") var stdTx auth.StdTx
if err = common.EnsureDir(writePath, 0700); err != nil { bytes, err := ioutil.ReadAll(r)
return if err != nil {
return stdTx, err
} }
filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)) err = cdc.UnmarshalJSON(bytes, &stdTx)
return os.Create(filename) 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
} }

View File

@ -8,7 +8,7 @@ services:
- "26656-26657:26656-26657" - "26656-26657:26656-26657"
environment: environment:
- ID=0 - ID=0
- LOG=$${LOG:-gaiad.log} - LOG=${LOG:-gaiad.log}
volumes: volumes:
- ./build:/gaiad:Z - ./build:/gaiad:Z
networks: networks:
@ -22,7 +22,7 @@ services:
- "26659-26660:26656-26657" - "26659-26660:26656-26657"
environment: environment:
- ID=1 - ID=1
- LOG=$${LOG:-gaiad.log} - LOG=${LOG:-gaiad.log}
volumes: volumes:
- ./build:/gaiad:Z - ./build:/gaiad:Z
networks: networks:
@ -34,7 +34,7 @@ services:
image: "tendermint/gaiadnode" image: "tendermint/gaiadnode"
environment: environment:
- ID=2 - ID=2
- LOG=$${LOG:-gaiad.log} - LOG=${LOG:-gaiad.log}
ports: ports:
- "26661-26662:26656-26657" - "26661-26662:26656-26657"
volumes: volumes:
@ -48,7 +48,7 @@ services:
image: "tendermint/gaiadnode" image: "tendermint/gaiadnode"
environment: environment:
- ID=3 - ID=3
- LOG=$${LOG:-gaiad.log} - LOG=${LOG:-gaiad.log}
ports: ports:
- "26663-26664:26656-26657" - "26663-26664:26656-26657"
volumes: volumes:

View File

@ -52,11 +52,11 @@ type Account interface {
GetPubKey() crypto.PubKey // can return nil. GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error SetPubKey(crypto.PubKey) error
GetAccountNumber() int64 GetAccountNumber() uint64
SetAccountNumber(int64) error SetAccountNumber(uint64) error
GetSequence() int64 GetSequence() uint64
SetSequence(int64) error SetSequence(uint64) error
GetCoins() sdk.Coins GetCoins() sdk.Coins
SetCoins(sdk.Coins) error SetCoins(sdk.Coins) error
@ -79,8 +79,8 @@ type BaseAccount struct {
Address sdk.AccAddress `json:"address"` Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"` PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
} }
``` ```
@ -161,8 +161,8 @@ The standard form for signatures is `StdSignature`:
type StdSignature struct { type StdSignature struct {
crypto.PubKey `json:"pub_key"` // optional crypto.PubKey `json:"pub_key"` // optional
[]byte `json:"signature"` []byte `json:"signature"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
} }
``` ```

View File

@ -12,7 +12,8 @@ import (
) )
const ( const (
app1Name = "App1" app1Name = "App1"
bankCodespace = "BANK"
) )
func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp { func NewApp1(logger log.Logger, db dbm.DB) *bapp.BaseApp {
@ -107,7 +108,7 @@ func handleMsgSend(key *sdk.KVStoreKey) sdk.Handler {
if !ok { if !ok {
// Create custom error message and return result // Create custom error message and return result
// Note: Using unreserved error codespace // 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. // Load the store.
@ -137,7 +138,7 @@ func handleFrom(store sdk.KVStore, from sdk.AccAddress, amt sdk.Coins) sdk.Resul
accBytes := store.Get(from) accBytes := store.Get(from)
if accBytes == nil { if accBytes == nil {
// Account was not added to store. Return the result of the error. // 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. // Unmarshal the JSON account bytes.

View File

@ -126,7 +126,7 @@ func handleMsgIssue(keyIssue *sdk.KVStoreKey, keyAcc *sdk.KVStoreKey) sdk.Handle
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
issueMsg, ok := msg.(MsgIssue) issueMsg, ok := msg.(MsgIssue)
if !ok { if !ok {
return sdk.NewError(2, 1, "MsgIssue is malformed").Result() return sdk.NewError(bankCodespace, 1, "MsgIssue is malformed").Result()
} }
// Retrieve stores // Retrieve stores

View File

@ -20,7 +20,7 @@ func TestEncoding(t *testing.T) {
sendMsg := MsgSend{ sendMsg := MsgSend{
From: addr1, From: addr1,
To: addr2, To: addr2,
Amount: sdk.Coins{{"testCoins", sdk.NewInt(100)}}, Amount: sdk.Coins{sdk.NewCoin("testCoins", sdk.NewInt(100))},
} }
// Construct transaction // Construct transaction

View File

@ -30,7 +30,7 @@ func InitTestChain(bc *bapp.BaseApp, chainID string, addrs ...sdk.AccAddress) {
for _, addr := range addrs { for _, addr := range addrs {
acc := GenesisAccount{ acc := GenesisAccount{
Address: addr, Address: addr,
Coins: sdk.Coins{{"testCoin", sdk.NewInt(100)}}, Coins: sdk.Coins{sdk.NewCoin("testCoin", sdk.NewInt(100))},
} }
accounts = append(accounts, &acc) accounts = append(accounts, &acc)
} }
@ -61,12 +61,12 @@ func TestBadMsg(t *testing.T) {
addr2 := priv2.PubKey().Address().Bytes() addr2 := priv2.PubKey().Address().Bytes()
// Attempt to spend non-existent funds // 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 // Construct transaction
fee := auth.StdFee{ fee := auth.StdFee{
Gas: 1000000000000000, 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}, "") signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "")
sig, err := priv1.Sign(signBytes) sig, err := priv1.Sign(signBytes)
@ -108,11 +108,11 @@ func TestMsgSend(t *testing.T) {
InitTestChain(bc, "test-chain", addr1) InitTestChain(bc, "test-chain", addr1)
// Send funds to addr2 // 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{ fee := auth.StdFee{
Gas: 1000000000000000, 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}, "") signBytes := auth.StdSignBytes("test-chain", 0, 0, fee, []sdk.Msg{msg}, "")
sig, err := priv1.Sign(signBytes) sig, err := priv1.Sign(signBytes)

View File

@ -75,7 +75,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.Ba
}, },
) )
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) 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 // register message routes
app.Router(). app.Router().

View File

@ -8,17 +8,28 @@ import (
"github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" "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" "github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" 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" 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" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli"
slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/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" 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/spf13/cobra"
"github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/cli"
) )
const (
storeAcc = "acc"
storeSlashing = "slashing"
storeStake = "stake"
)
// rootCmd is the entry point for this binary // rootCmd is the entry point for this binary
var ( var (
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
@ -34,6 +45,17 @@ func main() {
// get the codec // get the codec
cdc := app.MakeCodec() 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 // TODO: Setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc. // with the cdc.
@ -50,40 +72,39 @@ func main() {
// add query/post commands (custom to binary) // add query/post commands (custom to binary)
rootCmd.AddCommand( rootCmd.AddCommand(
client.GetCommands( stakecmd.GetCmdQueryValidator(storeStake, cdc),
stakecmd.GetCmdQueryValidator("stake", cdc), stakecmd.GetCmdQueryValidators(storeStake, cdc),
stakecmd.GetCmdQueryValidators("stake", cdc), stakecmd.GetCmdQueryValidatorUnbondingDelegations(storeStake, cdc),
stakecmd.GetCmdQueryValidatorUnbondingDelegations("stake", cdc), stakecmd.GetCmdQueryValidatorRedelegations(storeStake, cdc),
stakecmd.GetCmdQueryValidatorRedelegations("stake", cdc), stakecmd.GetCmdQueryDelegation(storeStake, cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc), stakecmd.GetCmdQueryDelegations(storeStake, cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc), stakecmd.GetCmdQueryPool(storeStake, cdc),
stakecmd.GetCmdQueryPool("stake", cdc), stakecmd.GetCmdQueryParams(storeStake, cdc),
stakecmd.GetCmdQueryParams("stake", cdc), stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc),
stakecmd.GetCmdQueryUnbondingDelegation("stake", cdc), stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc),
stakecmd.GetCmdQueryUnbondingDelegations("stake", cdc), stakecmd.GetCmdQueryRedelegation(storeStake, cdc),
stakecmd.GetCmdQueryRedelegation("stake", cdc), stakecmd.GetCmdQueryRedelegations(storeStake, cdc),
stakecmd.GetCmdQueryRedelegations("stake", cdc), slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc),
slashingcmd.GetCmdQuerySigningInfo("slashing", cdc), stakecmd.GetCmdQueryValidatorDelegations(storeStake, cdc),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), authcmd.GetAccountCmd(storeAcc, cdc),
)...) )
rootCmd.AddCommand( rootCmd.AddCommand(
client.PostCommands( bankcmd.SendTxCmd(cdc),
bankcmd.SendTxCmd(cdc), ibccmd.IBCTransferCmd(cdc),
ibccmd.IBCTransferCmd(cdc), ibccmd.IBCRelayCmd(cdc),
ibccmd.IBCRelayCmd(cdc), stakecmd.GetCmdCreateValidator(cdc),
stakecmd.GetCmdCreateValidator(cdc), stakecmd.GetCmdEditValidator(cdc),
stakecmd.GetCmdEditValidator(cdc), stakecmd.GetCmdDelegate(cdc),
stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond(storeStake, cdc),
stakecmd.GetCmdUnbond("stake", cdc), stakecmd.GetCmdRedelegate(storeStake, cdc),
stakecmd.GetCmdRedelegate("stake", cdc), slashingcmd.GetCmdUnjail(cdc),
slashingcmd.GetCmdUnjail(cdc), )
)...)
// add proxy, version and key info // add proxy, version and key info
rootCmd.AddCommand( rootCmd.AddCommand(
client.LineBreak, client.LineBreak,
lcd.ServeCommand(cdc), rs.ServeCommand(),
keys.Commands(), keys.Commands(),
client.LineBreak, client.LineBreak,
version.VersionCmd, version.VersionCmd,
@ -97,3 +118,13 @@ func main() {
panic(err) 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)
}

View File

@ -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"))) 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) { json.RawMessage, []tmtypes.GenesisValidator, error) {
bapp := app.NewBasecoinApp(logger, db) bapp := app.NewBasecoinApp(logger, db)
return bapp.ExportAppStateAndValidators() return bapp.ExportAppStateAndValidators()

View File

@ -83,10 +83,10 @@ func NewDemocoinApp(logger log.Logger, db dbm.DB) *DemocoinApp {
// Add handlers. // Add handlers.
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.bankKeeper, app.RegisterCodespace(cool.DefaultCodespace)) app.coolKeeper = cool.NewKeeper(app.capKeyMainStore, app.bankKeeper, cool.DefaultCodespace)
app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.bankKeeper, app.RegisterCodespace(pow.DefaultCodespace)) app.powKeeper = pow.NewKeeper(app.capKeyPowStore, pow.NewConfig("pow", int64(1)), app.bankKeeper, pow.DefaultCodespace)
app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, app.RegisterCodespace(ibc.DefaultCodespace)) app.ibcMapper = ibc.NewMapper(app.cdc, app.capKeyIBCStore, ibc.DefaultCodespace)
app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper, app.RegisterCodespace(simplestake.DefaultCodespace)) app.stakeKeeper = simplestake.NewKeeper(app.capKeyStakingStore, app.bankKeeper, simplestake.DefaultCodespace)
app.Router(). app.Router().
AddRoute("bank", bank.NewHandler(app.bankKeeper)). AddRoute("bank", bank.NewHandler(app.bankKeeper)).
AddRoute("cool", cool.NewHandler(app.coolKeeper)). AddRoute("cool", cool.NewHandler(app.coolKeeper)).

View File

@ -13,11 +13,11 @@ import (
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" 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" 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/app"
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/types"
coolcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool/client/cli" 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" 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" simplestakingcmd "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestake/client/cli"
@ -31,6 +31,7 @@ var (
Use: "democli", Use: "democli",
Short: "Democoin light-client", Short: "Democoin light-client",
} }
storeAcc = "acc"
) )
func main() { func main() {
@ -47,6 +48,10 @@ func main() {
config.SetBech32PrefixForConsensusNode("democons", "democonspub") config.SetBech32PrefixForConsensusNode("democons", "democonspub")
config.Seal() config.Seal()
rs := lcd.NewRestServer(cdc)
registerRoutes(rs)
// TODO: setup keybase, viper object, etc. to be passed into // TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do // the below functions and eliminate global vars, like we do
// with the cdc // with the cdc
@ -65,20 +70,13 @@ func main() {
// add query/post commands (custom to binary) // add query/post commands (custom to binary)
// start with commands common to basecoin // start with commands common to basecoin
rootCmd.AddCommand( rootCmd.AddCommand(
client.GetCommands( authcmd.GetAccountCmd(storeAcc, cdc),
authcmd.GetAccountCmd("acc", cdc, types.GetAccountDecoder(cdc)), )
)...) rootCmd.AddCommand(
bankcmd.SendTxCmd(cdc),
)
rootCmd.AddCommand( rootCmd.AddCommand(
client.PostCommands( client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCTransferCmd(cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
ibccmd.IBCRelayCmd(cdc),
simplestakingcmd.BondTxCmd(cdc), simplestakingcmd.BondTxCmd(cdc),
)...) )...)
rootCmd.AddCommand( rootCmd.AddCommand(
@ -96,7 +94,7 @@ func main() {
// add proxy, version and key info // add proxy, version and key info
rootCmd.AddCommand( rootCmd.AddCommand(
client.LineBreak, client.LineBreak,
lcd.ServeCommand(cdc), rs.ServeCommand(),
keys.Commands(), keys.Commands(),
client.LineBreak, client.LineBreak,
version.VersionCmd, version.VersionCmd,
@ -110,3 +108,11 @@ func main() {
panic(err) 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)
}

View File

@ -30,7 +30,6 @@ const (
flagClientHome = "home-client" flagClientHome = "home-client"
) )
// coolGenAppParams sets up the app_state and appends the cool app state // coolGenAppParams sets up the app_state and appends the cool app state
func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) ( func CoolAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
appState json.RawMessage, err error) { 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) 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) { json.RawMessage, []tmtypes.GenesisValidator, error) {
dapp := app.NewDemocoinApp(logger, db) dapp := app.NewDemocoinApp(logger, db)
return dapp.ExportAppStateAndValidators() return dapp.ExportAppStateAndValidators()

View File

@ -50,7 +50,7 @@ func getMockApp(t *testing.T) *mock.App {
RegisterCodec(mapp.Cdc) RegisterCodec(mapp.Cdc)
keyCool := sdk.NewKVStoreKey("cool") keyCool := sdk.NewKVStoreKey("cool")
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper)
keeper := NewKeeper(keyCool, bankKeeper, mapp.RegisterCodespace(DefaultCodespace)) keeper := NewKeeper(keyCool, bankKeeper, DefaultCodespace)
mapp.Router().AddRoute("cool", NewHandler(keeper)) mapp.Router().AddRoute("cool", NewHandler(keeper))
mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold")) mapp.SetInitChainer(getInitChainer(mapp, keeper, "ice-cold"))
@ -88,17 +88,17 @@ func TestMsgQuiz(t *testing.T) {
require.Equal(t, acc1, res1) require.Equal(t, acc1, res1)
// Set the trend, submit a really cool quiz and check for reward // 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{setTrendMsg1}, []uint64{0}, []uint64{0}, true, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []uint64{0}, []uint64{2}, false, false, priv1) // result without reward
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(69))})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{3}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []uint64{0}, []uint64{4}, true, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do! mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []uint64{0}, []uint64{5}, false, false, priv1) // the same answer will nolonger do!
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("icecold", sdk.NewInt(138))})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again mock.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{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("badvibesonly", sdk.NewInt(69)), sdk.NewCoin("icecold", sdk.NewInt(138))})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool 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
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types" 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" 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) txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
WithCodec(cdc). WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) WithAccountDecoder(cdc)
from, err := cliCtx.GetFromAddress() from, err := cliCtx.GetFromAddress()
if err != nil { if err != nil {
@ -46,7 +45,7 @@ func SetTrendTxCmd(cdc *codec.Codec) *cobra.Command {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
WithCodec(cdc). WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) WithAccountDecoder(cdc)
from, err := cliCtx.GetFromAddress() from, err := cliCtx.GetFromAddress()
if err != nil { if err != nil {

View File

@ -8,7 +8,7 @@ import (
// Cool errors reserve 400 ~ 499. // Cool errors reserve 400 ~ 499.
const ( const (
DefaultCodespace sdk.CodespaceType = 6 DefaultCodespace sdk.CodespaceType = "cool"
// Cool module reserves error 400-499 lawl // Cool module reserves error 400-499 lawl
CodeIncorrectCoolAnswer sdk.CodeType = 400 CodeIncorrectCoolAnswer sdk.CodeType = 400

View File

@ -94,7 +94,7 @@ func (keeper Keeper) Handle(h Handler, ctx sdk.Context, o Msg, codespace sdk.Cod
err := h(cctx, payload) err := h(cctx, payload)
if err != nil { if err != nil {
return sdk.Result{ return sdk.Result{
Code: sdk.ABCICodeOK, Code: sdk.CodeOK,
Log: err.ABCILog(), Log: err.ABCILog(),
} }
} }

View File

@ -27,7 +27,7 @@ func getMockApp(t *testing.T) *mock.App {
keyPOW := sdk.NewKVStoreKey("pow") keyPOW := sdk.NewKVStoreKey("pow")
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper)
config := Config{"pow", 1} 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.Router().AddRoute("pow", keeper.Handler)
mapp.SetInitChainer(getInitChainer(mapp, keeper)) mapp.SetInitChainer(getInitChainer(mapp, keeper))
@ -74,13 +74,13 @@ func TestMsgMine(t *testing.T) {
// Mine and check for reward // Mine and check for reward
mineMsg1 := GenerateMsgMine(addr1, 1, 2) mineMsg1 := GenerateMsgMine(addr1, 1, 2)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []uint64{0}, []uint64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(1))})
// Mine again and check for reward // Mine again and check for reward
mineMsg2 := GenerateMsgMine(addr1, 2, 3) mineMsg2 := GenerateMsgMine(addr1, 2, 3)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))})
// Mine again - should be invalid // Mine again - should be invalid
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1) mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []uint64{0}, []uint64{1}, false, false, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}}) mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewCoin("pow", sdk.NewInt(2))})
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/pow"
sdk "github.com/cosmos/cosmos-sdk/types" 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" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -24,7 +23,7 @@ func MineCmd(cdc *codec.Codec) *cobra.Command {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
WithCodec(cdc). WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) WithAccountDecoder(cdc)
from, err := cliCtx.GetFromAddress() from, err := cliCtx.GetFromAddress()
if err != nil { if err != nil {

View File

@ -9,7 +9,7 @@ type CodeType = sdk.CodeType
// POW errors reserve 200 ~ 299 // POW errors reserve 200 ~ 299
const ( const (
DefaultCodespace sdk.CodespaceType = 5 DefaultCodespace sdk.CodespaceType = "pow"
CodeInvalidDifficulty CodeType = 201 CodeInvalidDifficulty CodeType = 201
CodeNonexistentDifficulty CodeType = 202 CodeNonexistentDifficulty CodeType = 202
CodeNonexistentReward CodeType = 203 CodeNonexistentReward CodeType = 203

View File

@ -9,7 +9,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestake" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/x/simplestake"
sdk "github.com/cosmos/cosmos-sdk/types" 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" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -32,7 +31,7 @@ func BondTxCmd(cdc *codec.Codec) *cobra.Command {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc) txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
WithCodec(cdc). WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) WithAccountDecoder(cdc)
from, err := cliCtx.GetFromAddress() from, err := cliCtx.GetFromAddress()
if err != nil { if err != nil {

View File

@ -6,7 +6,7 @@ import (
// simple stake errors reserve 300 ~ 399. // simple stake errors reserve 300 ~ 399.
const ( const (
DefaultCodespace sdk.CodespaceType = 4 DefaultCodespace sdk.CodespaceType = moduleName
// simplestake errors reserve 300 - 399. // simplestake errors reserve 300 - 399.
CodeEmptyValidator sdk.CodeType = 300 CodeEmptyValidator sdk.CodeType = 300

View File

@ -22,12 +22,12 @@ func handleMsgBond() sdk.Result {
// Removed ValidatorSet from result because it does not get used. // Removed ValidatorSet from result because it does not get used.
// TODO: Implement correct bond/unbond handling // TODO: Implement correct bond/unbond handling
return sdk.Result{ return sdk.Result{
Code: sdk.ABCICodeOK, Code: sdk.CodeOK,
} }
} }
func handleMsgUnbond() sdk.Result { func handleMsgUnbond() sdk.Result {
return sdk.Result{ return sdk.Result{
Code: sdk.ABCICodeOK, Code: sdk.CodeOK,
} }
} }

View File

@ -24,10 +24,12 @@ There are three types of key representations that are used:
- Derived from account keys generated by `gaiacli keys add` - Derived from account keys generated by `gaiacli keys add`
- Used to receive funds - Used to receive funds
- e.g. `cosmos15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc` - e.g. `cosmos15h6vd5f0wqps26zjlwrc6chah08ryu4hzzdwhc`
* `cosmosvaloper` * `cosmosvaloper`
* Used to associate a validator to it's operator - Used to associate a validator to it's operator
* Used to invoke staking commands - Used to invoke staking commands
* e.g. `cosmosvaloper1carzvgq3e6y3z5kz5y6gxp3wpy3qdrv928vyah` - e.g. `cosmosvaloper1carzvgq3e6y3z5kz5y6gxp3wpy3qdrv928vyah`
- `cosmospub` - `cosmospub`
- Derived from account keys generated by `gaiacli keys add` - Derived from account keys generated by `gaiacli keys add`
- e.g. `cosmospub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm` - e.g. `cosmospub1zcjduc3q7fu03jnlu2xpl75s2nkt7krm6grh4cc5aqth73v0zwmea25wj2hsqhlqzm`
@ -72,7 +74,7 @@ View the validator pubkey for your node by typing:
gaiad tendermint show-validator 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 ::: 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. 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 <account_cosmosval>
#### Bond Tokens #### 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 ```bash
gaiacli tx stake delegate \ gaiacli tx stake delegate \
@ -304,7 +306,7 @@ gaiacli tx stake redelegate \
--chain-id=<chain_id> --chain-id=<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. 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 ##### Query Delegations To Validator
You can also query all of the delegations to a particular validator: You can also query all of the delegations to a particular validator:
```bash ```bash
gaiacli query delegations-to <account_cosmosval> gaiacli query delegations-to <account_cosmosval>
``` ```
@ -418,7 +421,7 @@ Or query all available proposals:
gaiacli query gov 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 #### Increase deposit
@ -447,7 +450,7 @@ You can also query a deposit submitted by a specific address:
```bash ```bash
gaiacli query gov deposit \ gaiacli query gov deposit \
--proposal-id=<proposal_id> \ --proposal-id=<proposal_id> \
--depositer=<account_cosmos> --depositor=<account_cosmos>
``` ```
#### Vote on a proposal #### Vote on a proposal

View File

@ -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): You can also export state from a particular height (at the end of processing the block of that height):
```bash ```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 ## Upgrade to Validator Node

View File

@ -63,7 +63,7 @@ principle:
type AppAccount struct {...} type AppAccount struct {...}
var account := &AppAccount{ var account := &AppAccount{
Address: pub.Address(), Address: pub.Address(),
Coins: sdk.Coins{{"ATM", 100}}, Coins: sdk.Coins{sdk.NewInt64Coin("ATM", 100)},
} }
var sumValue := externalModule.ComputeSumValue(account) var sumValue := externalModule.ComputeSumValue(account)
``` ```

View File

@ -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. The BaseApp is composed of many internal components. Some of the most important
During CheckTx the state transition function is only applied to the checkTxState and should return include the `CommitMultiStore` and its internal state. The internal state is
before any expensive state transitions are run (this is up to each developer). It also needs to return the estimated essentially two sub-states, both of which are used for transaction execution
gas cost. 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 The BaseApp requires stores to be mounted via capabilities keys - handlers can
need to be fully executed. 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. 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.

View File

@ -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: There is one instance where Atom holders that deposits can be refunded:
* If the proposal is accepted. * 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 ### Proposal types

View File

@ -68,8 +68,8 @@ const (
```go ```go
type Deposit struct { type Deposit struct {
Amount sdk.Coins // Amount of coins deposited by depositer Amount sdk.Coins // Amount of coins deposited by depositor
Depositer crypto.address // Address of depositer 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 // proposal was accepted at the end of the voting period
// refund deposits (non-voters already punished) // refund deposits (non-voters already punished)
proposal.CurrentStatus = ProposalStatusAccepted proposal.CurrentStatus = ProposalStatusAccepted
for each (amount, depositer) in proposal.Deposits for each (amount, depositor) in proposal.Deposits
depositer.AtomBalance += amount depositor.AtomBalance += amount
else else
// proposal was rejected // proposal was rejected

View File

@ -1,18 +1,18 @@
# Begin-Block # 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 NextInflationRate(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) {
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) {
inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange inflationRateChangePerYear = (1 - bondedRatio/params.GoalBonded) * params.InflationRateChange
inflationRateChange = inflationRateChangePerYear/hrsPerYr inflationRateChange = inflationRateChangePerYear/hrsPerYr
@ -26,3 +26,25 @@ NextInflation(params Params, bondedRatio sdk.Dec) (inflation sdk.Dec) {
} }
return inflation 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())
```

View File

@ -8,8 +8,9 @@ The minter is a space for holding current inflation information.
```golang ```golang
type Minter struct { type Minter struct {
InflationLastTime time.Time // block time which the last inflation was processed LastUpdate time.Time // time which the last update was made to the minter
Inflation sdk.Dec // current annual inflation rate 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 InflationMax sdk.Dec // maximum inflation rate
InflationMin sdk.Dec // minimum inflation rate InflationMin sdk.Dec // minimum inflation rate
GoalBonded sdk.Dec // goal of percent bonded atoms GoalBonded sdk.Dec // goal of percent bonded atoms
BlocksPerYear uint64 // expected blocks per year
} }
``` ```

View File

@ -3,4 +3,4 @@
Here contains the files required for automated deployment of either local or remote testnets. 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 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)

View File

@ -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

View File

@ -19,7 +19,7 @@ type (
// AppExporter is a function that dumps all app state to // AppExporter is a function that dumps all app state to
// JSON-serializable structure and returns the current validator set. // 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) { func openDB(rootDir string) (dbm.DB, error) {

View File

@ -14,7 +14,8 @@ import (
) )
const ( const (
flagHeight = "height" flagHeight = "height"
flagForZeroHeight = "for-zero-height"
) )
// ExportCmd dumps app state to JSON. // ExportCmd dumps app state to JSON.
@ -50,7 +51,8 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C
return err return err
} }
height := viper.GetInt64(flagHeight) 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 { if err != nil {
return errors.Errorf("error exporting state: %v\n", err) 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().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 return cmd
} }

View File

@ -15,7 +15,6 @@ import (
tmtypes "github.com/tendermint/tendermint/types" tmtypes "github.com/tendermint/tendermint/types"
) )
// SimpleGenTx is a simple genesis tx // SimpleGenTx is a simple genesis tx
type SimpleGenTx struct { type SimpleGenTx struct {
Addr sdk.AccAddress `json:"addr"` Addr sdk.AccAddress `json:"addr"`
@ -23,7 +22,6 @@ type SimpleGenTx struct {
//_____________________________________________________________________ //_____________________________________________________________________
// Generate a genesis transaction // Generate a genesis transaction
func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) ( func SimpleAppGenTx(cdc *codec.Codec, pk crypto.PubKey) (
appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) { appGenTx, cliPrint json.RawMessage, validator types.GenesisValidator, err error) {

View File

@ -11,6 +11,8 @@ import (
// cacheMultiStore holds many cache-wrapped stores. // cacheMultiStore holds many cache-wrapped stores.
// Implements MultiStore. // Implements MultiStore.
// NOTE: a cacheMultiStore (and MultiStores in general) should never expose the
// keys for the substores.
type cacheMultiStore struct { type cacheMultiStore struct {
db CacheKVStore db CacheKVStore
stores map[StoreKey]CacheWrap stores map[StoreKey]CacheWrap

View File

@ -73,15 +73,15 @@ func testGasKVStoreWrap(t *testing.T, store KVStore) {
meter := sdk.NewGasMeter(10000) meter := sdk.NewGasMeter(10000)
store = store.Gas(meter, sdk.GasConfig{HasCost: 10}) 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")) 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 = store.Gas(meter, sdk.GasConfig{HasCost: 20})
store.Has([]byte("key")) store.Has([]byte("key"))
require.Equal(t, int64(40), meter.GasConsumed()) require.Equal(t, uint64(40), meter.GasConsumed())
} }
func TestGasKVStoreWrap(t *testing.T) { func TestGasKVStoreWrap(t *testing.T) {

View File

@ -230,13 +230,19 @@ func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore {
} }
// Implements MultiStore. // Implements MultiStore.
// If the store does not exist, panics.
func (rs *rootMultiStore) GetStore(key StoreKey) Store { 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 // GetKVStore implements the MultiStore interface. If tracing is enabled on the
// rootMultiStore, a wrapped TraceKVStore will be returned with the given // rootMultiStore, a wrapped TraceKVStore will be returned with the given
// tracer, otherwise, the original KVStore will be returned. // tracer, otherwise, the original KVStore will be returned.
// If the store does not exist, panics.
func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore {
store := rs.stores[key].(KVStore) store := rs.stores[key].(KVStore)

View File

@ -156,34 +156,37 @@ func TestMultiStoreQuery(t *testing.T) {
// Test bad path. // Test bad path.
query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} query := abci.RequestQuery{Path: "/key", Data: k, Height: ver}
qres := multi.Query(query) 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" query.Path = "h897fy32890rf63296r92"
qres = multi.Query(query) 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. // Test invalid store name.
query.Path = "/garbage/key" query.Path = "/garbage/key"
qres = multi.Query(query) 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. // Test valid query with data.
query.Path = "/store1/key" query.Path = "/store1/key"
qres = multi.Query(query) 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) require.Equal(t, v, qres.Value)
// Test valid but empty query. // Test valid but empty query.
query.Path = "/store2/key" query.Path = "/store2/key"
query.Prove = true query.Prove = true
qres = multi.Query(query) 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) require.Nil(t, qres.Value)
// Test store2 data. // Test store2 data.
query.Data = k2 query.Data = k2
qres = multi.Query(query) 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) require.Equal(t, v2, qres.Value)
} }

View File

@ -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
}

View File

@ -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))
}

View File

@ -4,23 +4,41 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"sort" "sort"
"strconv"
"strings" "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 { type Coin struct {
Denom string `json:"denom"` Denom string `json:"denom"`
Amount Int `json:"amount"`
// 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 { func NewCoin(denom string, amount Int) Coin {
if amount.LT(ZeroInt()) {
panic(fmt.Sprintf("negative coin amount: %v\n", amount))
}
return Coin{ return Coin{
Denom: denom, Denom: denom,
Amount: amount, 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 { func NewInt64Coin(denom string, amount int64) Coin {
return NewCoin(denom, NewInt(amount)) 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)) 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 { func (coin Coin) IsPositive() bool {
return (coin.Amount.Sign() == 1) 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 { func (coin Coin) IsNotNegative() bool {
return (coin.Amount.Sign() != -1) 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
// Coins is a set of Coin, one per currency // Coins is a set of Coin, one per currency
@ -101,127 +132,157 @@ func (coins Coins) String() string {
return out[:len(out)-1] 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 { func (coins Coins) IsValid() bool {
switch len(coins) { switch len(coins) {
case 0: case 0:
return true return true
case 1: case 1:
return !coins[0].IsZero() return coins[0].IsPositive()
default: default:
lowDenom := coins[0].Denom lowDenom := coins[0].Denom
for _, coin := range coins[1:] { for _, coin := range coins[1:] {
if coin.Denom <= lowDenom { if coin.Denom <= lowDenom {
return false return false
} }
if coin.IsZero() { if !coin.IsPositive() {
return false return false
} }
// we compare each coin against the last denom // we compare each coin against the last denom
lowDenom = coin.Denom lowDenom = coin.Denom
} }
return true return true
} }
} }
// Plus combines two sets of coins // Plus adds two sets of coins.
// CONTRACT: Plus will never return Coins where one Coin has a 0 amount. //
// 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 { 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) sum := ([]Coin)(nil)
indexA, indexB := 0, 0 indexA, indexB := 0, 0
lenA, lenB := len(coins), len(coinsB) lenA, lenB := len(coins), len(coinsB)
for { for {
if indexA == lenA { if indexA == lenA {
if indexB == lenB { if indexB == lenB {
// return nil coins if both sets are empty
return sum 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 { } 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] coinA, coinB := coins[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) { switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1: case -1: // coin A denom < coin B denom
if coinA.IsZero() { if !coinA.IsZero() {
// ignore 0 sum coin type
} else {
sum = append(sum, coinA) sum = append(sum, coinA)
} }
indexA++ indexA++
case 0:
if coinA.Amount.Add(coinB.Amount).IsZero() { case 0: // coin A denom == coin B denom
// ignore 0 sum coin type res := coinA.Plus(coinB)
} else { if !res.IsZero() {
sum = append(sum, coinA.Plus(coinB)) sum = append(sum, res)
} }
indexA++ indexA++
indexB++ indexB++
case 1:
if coinB.IsZero() { case 1: // coin A denom > coin B denom
// ignore 0 sum coin type if !coinB.IsZero() {
} else {
sum = append(sum, coinB) sum = append(sum, coinB)
} }
indexB++ indexB++
} }
} }
} }
// Negative returns a set of coins with all amount negative // Minus subtracts a set of coins from another.
func (coins Coins) Negative() Coins { //
res := make([]Coin, 0, len(coins)) // e.g.
for _, coin := range coins { // {2A, 3B} - {A} = {A, 3B}
res = append(res, Coin{ // {2A} - {0B} = {2A}
Denom: coin.Denom, // {A, B} - {A} = {B}
Amount: coin.Amount.Neg(), //
}) // CONTRACT: Minus will never return Coins where one Coin has a non-positive
} // amount. In otherwords, IsValid will always return true.
return res
}
// Minus subtracts a set of coins from another (adds the inverse)
func (coins Coins) Minus(coinsB Coins) Coins { 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. // greater amount in coinsB.
func (coins Coins) IsAllGT(coinsB Coins) bool { func (coins Coins) IsAllGT(coinsB Coins) bool {
diff := coins.Minus(coinsB) diff, _ := coins.SafeMinus(coinsB)
if len(diff) == 0 { if len(diff) == 0 {
return false return false
} }
return diff.IsPositive() return diff.IsPositive()
} }
// IsAllGTE returns True iff for every denom in coins, the denom is present at an // IsAllGTE returns true iff for every denom in coins, the denom is present at
// equal or greater amount in coinsB. // an equal or greater amount in coinsB.
func (coins Coins) IsAllGTE(coinsB Coins) bool { func (coins Coins) IsAllGTE(coinsB Coins) bool {
diff := coins.Minus(coinsB) diff, _ := coins.SafeMinus(coinsB)
if len(diff) == 0 { if len(diff) == 0 {
return true return true
} }
return diff.IsNotNegative() return diff.IsNotNegative()
} }
// IsAllLT returns True iff for every denom in coins, the denom is present at // IsAllLT returns True iff for every denom in coins, the denom is present at
// a smaller amount in coinsB. // a smaller amount in coinsB.
func (coins Coins) IsAllLT(coinsB Coins) bool { func (coins Coins) IsAllLT(coinsB Coins) bool {
diff := coinsB.Minus(coins) return coinsB.IsAllGT(coins)
if len(diff) == 0 {
return false
}
return diff.IsPositive()
} }
// 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. // a smaller or equal amount in coinsB.
func (coins Coins) IsAllLTE(coinsB Coins) bool { func (coins Coins) IsAllLTE(coinsB Coins) bool {
diff := coinsB.Minus(coins) return coinsB.IsAllGTE(coins)
if len(diff) == 0 {
return true
}
return diff.IsNotNegative()
} }
// IsZero returns true if there are no coins or all coins are zero. // 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) { if len(coins) != len(coinsB) {
return false return false
} }
coins = coins.Sort()
coinsB = coinsB.Sort()
for i := 0; i < len(coins); i++ { for i := 0; i < len(coins); i++ {
if coins[i].Denom != coinsB[i].Denom || !coins[i].Amount.Equal(coinsB[i].Amount) { if coins[i].Denom != coinsB[i].Denom || !coins[i].Amount.Equal(coinsB[i].Amount) {
return false return false
} }
} }
return true return true
} }
// IsPositive returns true if there is at least one coin, and all // Empty returns true if there are no coins and false otherwise.
// currencies have a positive value func (coins Coins) Empty() bool {
func (coins Coins) IsPositive() bool { return len(coins) == 0
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
} }
// Returns the amount of a denom from coins // Returns the amount of a denom from coins
@ -280,15 +323,18 @@ func (coins Coins) AmountOf(denom string) Int {
switch len(coins) { switch len(coins) {
case 0: case 0:
return ZeroInt() return ZeroInt()
case 1: case 1:
coin := coins[0] coin := coins[0]
if coin.Denom == denom { if coin.Denom == denom {
return coin.Amount return coin.Amount
} }
return ZeroInt() return ZeroInt()
default: default:
midIdx := len(coins) / 2 // 2:1, 3:1, 4:2 midIdx := len(coins) / 2 // 2:1, 3:1, 4:2
coin := coins[midIdx] coin := coins[midIdx]
if denom < coin.Denom { if denom < coin.Denom {
return coins[:midIdx].AmountOf(denom) return coins[:midIdx].AmountOf(denom)
} else if denom == coin.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 // Sort interface
//nolint //nolint
@ -315,7 +429,7 @@ func (coins Coins) Sort() Coins {
return coins return coins
} }
//---------------------------------------- //-----------------------------------------------------------------------------
// Parsing // Parsing
var ( var (
@ -333,17 +447,17 @@ func ParseCoin(coinStr string) (coin Coin, err error) {
matches := reCoin.FindStringSubmatch(coinStr) matches := reCoin.FindStringSubmatch(coinStr)
if matches == nil { if matches == nil {
err = fmt.Errorf("invalid coin expression: %s", coinStr) return Coin{}, fmt.Errorf("invalid coin expression: %s", coinStr)
return
} }
denomStr, amountStr := matches[2], matches[1] denomStr, amountStr := matches[2], matches[1]
amount, err := strconv.Atoi(amountStr) amount, ok := NewIntFromString(amountStr)
if err != nil { if !ok {
return 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. // ParseCoins will parse out a list of coins separated by commas.

View File

@ -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))
}
}

View File

@ -1,43 +1,20 @@
package types package types
import ( import (
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestIsPositiveCoin(t *testing.T) { // ----------------------------------------------------------------------------
cases := []struct { // Coin tests
inputOne Coin
expected bool
}{
{NewInt64Coin("A", 1), true},
{NewInt64Coin("A", 0), false},
{NewInt64Coin("a", -1), false},
}
for tcIndex, tc := range cases { func TestCoin(t *testing.T) {
res := tc.inputOne.IsPositive() require.Panics(t, func() { NewInt64Coin("A", -1) })
require.Equal(t, tc.expected, res, "%s positivity is incorrect, tc #%d", tc.inputOne.String(), tcIndex) 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 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 TestSameDenomAsCoin(t *testing.T) { 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), true},
{NewInt64Coin("A", 1), NewInt64Coin("a", 1), false}, {NewInt64Coin("A", 1), NewInt64Coin("a", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false},
{NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), true}, {NewInt64Coin("steak", 1), NewInt64Coin("steak", 10), true},
{NewInt64Coin("stake", -11), NewInt64Coin("stake", 10), true},
} }
for tcIndex, tc := range cases { 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) { func TestIsGTECoin(t *testing.T) {
cases := []struct { cases := []struct {
inputOne Coin inputOne Coin
@ -67,8 +115,7 @@ func TestIsGTECoin(t *testing.T) {
}{ }{
{NewInt64Coin("A", 1), NewInt64Coin("A", 1), true}, {NewInt64Coin("A", 1), NewInt64Coin("A", 1), true},
{NewInt64Coin("A", 2), 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 { for tcIndex, tc := range cases {
@ -85,7 +132,6 @@ func TestIsLTCoin(t *testing.T) {
}{ }{
{NewInt64Coin("A", 1), NewInt64Coin("A", 1), false}, {NewInt64Coin("A", 1), NewInt64Coin("A", 1), false},
{NewInt64Coin("A", 2), 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", 0), NewInt64Coin("b", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("b", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("b", 1), false},
{NewInt64Coin("a", 1), NewInt64Coin("a", 1), false}, {NewInt64Coin("a", 1), NewInt64Coin("a", 1), false},
@ -98,76 +144,18 @@ func TestIsLTCoin(t *testing.T) {
} }
} }
func TestIsEqualCoin(t *testing.T) { func TestCoinIsZero(t *testing.T) {
cases := []struct { coin := NewInt64Coin("A", 0)
inputOne Coin res := coin.IsZero()
inputTwo Coin require.True(t, res)
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},
}
for tcIndex, tc := range cases { coin = NewInt64Coin("A", 1)
res := tc.inputOne.IsEqual(tc.inputTwo) res = coin.IsZero()
require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) require.False(t, res)
}
} }
func TestPlusCoin(t *testing.T) { // ----------------------------------------------------------------------------
cases := []struct { // Coins tests
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())
}
func TestIsZeroCoins(t *testing.T) { func TestIsZeroCoins(t *testing.T) {
cases := []struct { 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("B", 0)}, false},
{Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("A", 1)}, false}, {Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("A", 1)}, false},
{Coins{NewInt64Coin("A", 0)}, Coins{NewInt64Coin("A", 0), NewInt64Coin("B", 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)}, true},
{Coins{NewInt64Coin("A", 0), NewInt64Coin("B", 1)}, Coins{NewInt64Coin("B", 1), NewInt64Coin("A", 0)}, false},
} }
for tcnum, tc := range cases { 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{ good := Coins{
{"GAS", NewInt(1)}, {"GAS", NewInt(1)},
{"MINERAL", NewInt(1)}, {"MINERAL", NewInt(1)},
{"TREE", NewInt(1)}, {"TREE", NewInt(1)},
} }
neg := good.Negative()
sum := good.Plus(neg)
empty := Coins{ empty := Coins{
{"GOLD", NewInt(0)}, {"GOLD", NewInt(0)},
} }
@ -228,6 +264,7 @@ func TestCoins(t *testing.T) {
{"GAS", NewInt(1)}, {"GAS", NewInt(1)},
{"MINERAL", NewInt(1)}, {"MINERAL", NewInt(1)},
} }
// both are after the first one, but the second and third are in the wrong order // both are after the first one, but the second and third are in the wrong order
badSort2 := Coins{ badSort2 := Coins{
{"GAS", NewInt(1)}, {"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.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.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.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, badSort1.IsValid(), "Coins are not sorted")
assert.False(t, badSort2.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, badAmt.IsValid(), "Coins cannot include 0 amounts")
assert.False(t, dup.IsValid(), "Duplicate coin") assert.False(t, dup.IsValid(), "Duplicate coin")
} }
func TestCoinsGT(t *testing.T) { func TestCoinsGT(t *testing.T) {
@ -314,32 +348,6 @@ func TestCoinsLTE(t *testing.T) {
assert.True(t, Coins{}.IsAllLTE(Coins{{"A", one}})) 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) { func TestParse(t *testing.T) {
one := NewInt(1) 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) require.Equal(t, tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex)
} }
} }
} }
func TestSortCoins(t *testing.T) { func TestSortCoins(t *testing.T) {
good := Coins{ good := Coins{
NewInt64Coin("GAS", 1), NewInt64Coin("GAS", 1),
NewInt64Coin("MINERAL", 1), NewInt64Coin("MINERAL", 1),
@ -424,7 +430,6 @@ func TestSortCoins(t *testing.T) {
} }
func TestAmountOf(t *testing.T) { func TestAmountOf(t *testing.T) {
case0 := Coins{} case0 := Coins{}
case1 := Coins{ case1 := Coins{
NewInt64Coin("", 0), NewInt64Coin("", 0),
@ -481,55 +486,3 @@ func TestAmountOf(t *testing.T) {
assert.Equal(t, NewInt(tc.amountOfTREE), tc.coins.AmountOf("TREE")) 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))
}
}

View File

@ -73,12 +73,12 @@ func (c Context) Value(key interface{}) interface{} {
// KVStore fetches a KVStore from the MultiStore. // KVStore fetches a KVStore from the MultiStore.
func (c Context) KVStore(key StoreKey) KVStore { 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. // TransientStore fetches a TransientStore from the MultiStore.
func (c Context) TransientStore(key StoreKey) KVStore { 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 contextKeyMultiStore contextKey = iota
contextKeyBlockHeader contextKeyBlockHeader
contextKeyBlockHeight contextKeyBlockHeight
contextKeyConsensusParams
contextKeyChainID contextKeyChainID
contextKeyIsCheckTx contextKeyIsCheckTx
contextKeyTxBytes contextKeyTxBytes
contextKeyLogger contextKeyLogger
contextKeyVoteInfos contextKeyVoteInfos
contextKeyGasMeter contextKeyGasMeter
contextKeyBlockGasMeter
contextKeyMinimumFees contextKeyMinimumFees
) )
// NOTE: Do not expose MultiStore. func (c Context) MultiStore() MultiStore {
// MultiStore exposes all the keys.
// Instead, pass the context and the store key.
func (c Context) multiStore() MultiStore {
return c.Value(contextKeyMultiStore).(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) 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) ChainID() string { return c.Value(contextKeyChainID).(string) }
func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) } 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) 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) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) }
func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) } 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 { func (c Context) WithBlockHeader(header abci.Header) Context {
var _ proto.Message = &header // for cloning. 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) 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) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) }
func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contextKeyTxBytes, txBytes) } 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) 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 { func (c Context) WithIsCheckTx(isCheckTx bool) Context {
return c.withValue(contextKeyIsCheckTx, isCheckTx) 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 // Cache the multistore and return a new cached context. The cached context is
// written to the context when writeCache is called. // written to the context when writeCache is called.
func (c Context) CacheContext() (cc Context, writeCache func()) { func (c Context) CacheContext() (cc Context, writeCache func()) {
cms := c.multiStore().CacheMultiStore() cms := c.MultiStore().CacheMultiStore()
cc = c.WithMultiStore(cms) cc = c.WithMultiStore(cms)
return cc, cms.Write return cc, cms.Write
} }

View File

@ -172,10 +172,21 @@ func NewDecFromStr(str string) (d Dec, err Error) {
return Dec{combined}, nil 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 //nolint
func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil 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) 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) 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) 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 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 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 { func (d Dec) String() string {
bz, err := d.Int.MarshalText() bz, err := d.Int.MarshalText()
if err != nil { if err != nil {

View File

@ -10,37 +10,22 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
) )
// ABCICodeType - combined codetype / codespace // CodeType - ABCI code identifier within codespace
type ABCICodeType uint32 type CodeType uint32
// CodeType - code identifier within codespace
type CodeType uint16
// CodespaceType - codespace identifier // CodespaceType - codespace identifier
type CodespaceType uint16 type CodespaceType string
// IsOK - is everything okay? // IsOK - is everything okay?
func (code ABCICodeType) IsOK() bool { func (code CodeType) IsOK() bool {
if code == ABCICodeOK { if code == CodeOK {
return true return true
} }
return false 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 // SDK error codes
const ( const (
// ABCI error codes
ABCICodeOK ABCICodeType = 0
// Base error codes // Base error codes
CodeOK CodeType = 0 CodeOK CodeType = 0
CodeInternal CodeType = 1 CodeInternal CodeType = 1
@ -62,11 +47,8 @@ const (
// CodespaceRoot is a codespace for error codes in this file only. // CodespaceRoot is a codespace for error codes in this file only.
// Notice that 0 is an "unset" codespace, which can be overridden with // Notice that 0 is an "unset" codespace, which can be overridden with
// Error.WithDefaultCodespace(). // Error.WithDefaultCodespace().
CodespaceUndefined CodespaceType = 0 CodespaceUndefined CodespaceType = ""
CodespaceRoot CodespaceType = 1 CodespaceRoot CodespaceType = "sdk"
// Maximum reservable codespace (2^16 - 1)
MaximumCodespace CodespaceType = 65535
) )
func unknownCodeMsg(code CodeType) string { func unknownCodeMsg(code CodeType) string {
@ -185,7 +167,6 @@ type Error interface {
Code() CodeType Code() CodeType
Codespace() CodespaceType Codespace() CodespaceType
ABCILog() string ABCILog() string
ABCICode() ABCICodeType
Result() Result Result() Result
QueryResult() abci.ResponseQuery QueryResult() abci.ResponseQuery
} }
@ -239,17 +220,12 @@ func (err *sdkError) TraceSDK(format string, args ...interface{}) Error {
// Implements ABCIError. // Implements ABCIError.
func (err *sdkError) Error() string { func (err *sdkError) Error() string {
return fmt.Sprintf(`ERROR: return fmt.Sprintf(`ERROR:
Codespace: %d Codespace: %s
Code: %d Code: %d
Message: %#v Message: %#v
`, err.codespace, err.code, err.cmnError.Error()) `, err.codespace, err.code, err.cmnError.Error())
} }
// Implements ABCIError.
func (err *sdkError) ABCICode() ABCICodeType {
return ToABCICode(err.codespace, err.code)
}
// Implements Error. // Implements Error.
func (err *sdkError) Codespace() CodespaceType { func (err *sdkError) Codespace() CodespaceType {
return err.codespace return err.codespace
@ -267,7 +243,6 @@ func (err *sdkError) ABCILog() string {
jsonErr := humanReadableError{ jsonErr := humanReadableError{
Codespace: err.codespace, Codespace: err.codespace,
Code: err.code, Code: err.code,
ABCICode: err.ABCICode(),
Message: errMsg, Message: errMsg,
} }
bz, er := cdc.MarshalJSON(jsonErr) bz, er := cdc.MarshalJSON(jsonErr)
@ -280,16 +255,18 @@ func (err *sdkError) ABCILog() string {
func (err *sdkError) Result() Result { func (err *sdkError) Result() Result {
return Result{ return Result{
Code: err.ABCICode(), Code: err.Code(),
Log: err.ABCILog(), Codespace: err.Codespace(),
Log: err.ABCILog(),
} }
} }
// QueryResult allows us to return sdk.Error.QueryResult() in query responses // QueryResult allows us to return sdk.Error.QueryResult() in query responses
func (err *sdkError) QueryResult() abci.ResponseQuery { func (err *sdkError) QueryResult() abci.ResponseQuery {
return abci.ResponseQuery{ return abci.ResponseQuery{
Code: uint32(err.ABCICode()), Code: uint32(err.Code()),
Log: err.ABCILog(), Codespace: string(err.Codespace()),
Log: err.ABCILog(),
} }
} }
@ -324,6 +301,5 @@ func mustGetMsgIndex(abciLog string) int {
type humanReadableError struct { type humanReadableError struct {
Codespace CodespaceType `json:"codespace"` Codespace CodespaceType `json:"codespace"`
Code CodeType `json:"code"` Code CodeType `json:"code"`
ABCICode ABCICodeType `json:"abci_code"`
Message string `json:"message"` Message string `json:"message"`
} }

View File

@ -42,7 +42,7 @@ var errFns = []errFn{
} }
func TestCodeType(t *testing.T) { func TestCodeType(t *testing.T) {
require.True(t, ABCICodeOK.IsOK()) require.True(t, CodeOK.IsOK())
for tcnum, c := range codeTypes { for tcnum, c := range codeTypes {
msg := CodeToDefaultMsg(c) msg := CodeToDefaultMsg(c)
@ -59,12 +59,9 @@ func TestErrFn(t *testing.T) {
codeType := codeTypes[i] codeType := codeTypes[i]
require.Equal(t, err.Code(), codeType, "Err function expected to return proper code. tc #%d", 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.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.Code()), "Err function expected to return proper Code from QueryResult. 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().Log, err.ABCILog(), "Err function expected to return proper ABCILog 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) { func TestAppendMsgToErr(t *testing.T) {

View File

@ -18,17 +18,27 @@ var (
) )
// Gas measured by the SDK // Gas measured by the SDK
type Gas = int64 type Gas = uint64
// ErrorOutOfGas defines an error thrown when an action results in out of gas. // ErrorOutOfGas defines an error thrown when an action results in out of gas.
type ErrorOutOfGas struct { type ErrorOutOfGas struct {
Descriptor string 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 // GasMeter interface to track gas consumption
type GasMeter interface { type GasMeter interface {
GasConsumed() Gas GasConsumed() Gas
GasConsumedToLimit() Gas
Limit() Gas
ConsumeGas(amount Gas, descriptor string) ConsumeGas(amount Gas, descriptor string)
IsPastLimit() bool
IsOutOfGas() bool
} }
type basicGasMeter struct { type basicGasMeter struct {
@ -48,13 +58,39 @@ func (g *basicGasMeter) GasConsumed() Gas {
return g.consumed 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) { 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 { if g.consumed > g.limit {
panic(ErrorOutOfGas{descriptor}) 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 { type infiniteGasMeter struct {
consumed Gas consumed Gas
} }
@ -70,8 +106,30 @@ func (g *infiniteGasMeter) GasConsumed() Gas {
return g.consumed 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) { 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 // GasConfig defines gas cost for each operation on KVStores

View File

@ -21,15 +21,24 @@ func TestGasMeter(t *testing.T) {
for tcnum, tc := range cases { for tcnum, tc := range cases {
meter := NewGasMeter(tc.limit) meter := NewGasMeter(tc.limit)
used := int64(0) used := uint64(0)
for unum, usage := range tc.usage { for unum, usage := range tc.usage {
used += usage used += usage
require.NotPanics(t, func() { meter.ConsumeGas(usage, "") }, "Not exceeded limit but panicked. tc #%d, usage #%d", tcnum, unum) 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.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.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 break
} }

View File

@ -2,6 +2,7 @@ package types
import ( import (
"encoding/json" "encoding/json"
"math"
"testing" "testing"
"math/big" "math/big"
@ -321,11 +322,11 @@ func NewUint(n uint64) Uint {
// NewUintFromBigUint constructs Uint from big.Uint // NewUintFromBigUint constructs Uint from big.Uint
func NewUintFromBigInt(i *big.Int) Uint { func NewUintFromBigInt(i *big.Int) Uint {
// Check overflow res := Uint{i}
if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 { if UintOverflow(res) {
panic("Uint overflow") panic("Uint overflow")
} }
return Uint{i} return res
} }
// NewUintFromString constructs Uint from string // NewUintFromString constructs Uint from string
@ -352,11 +353,12 @@ func NewUintWithDecimal(n uint64, dec int) Uint {
i := new(big.Int) i := new(big.Int)
i.Mul(new(big.Int).SetUint64(n), exp) i.Mul(new(big.Int).SetUint64(n), exp)
// Check overflow res := Uint{i}
if i.Sign() == -1 || i.Sign() == 1 && i.BitLen() > 256 { if UintOverflow(res) {
panic("NewUintWithDecimal() out of bound") panic("NewUintWithDecimal() out of bound")
} }
return Uint{i}
return res
} }
// ZeroUint returns Uint value with zero // ZeroUint returns Uint value with zero
@ -407,8 +409,7 @@ func (i Uint) LT(i2 Uint) bool {
// Add adds Uint from another // Add adds Uint from another
func (i Uint) Add(i2 Uint) (res Uint) { func (i Uint) Add(i2 Uint) (res Uint) {
res = Uint{add(i.i, i2.i)} res = Uint{add(i.i, i2.i)}
// Check overflow if UintOverflow(res) {
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow") panic("Uint overflow")
} }
return return
@ -422,13 +423,23 @@ func (i Uint) AddRaw(i2 uint64) Uint {
// Sub subtracts Uint from another // Sub subtracts Uint from another
func (i Uint) Sub(i2 Uint) (res Uint) { func (i Uint) Sub(i2 Uint) (res Uint) {
res = Uint{sub(i.i, i2.i)} res = Uint{sub(i.i, i2.i)}
// Check overflow if UintOverflow(res) {
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow") panic("Uint overflow")
} }
return 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 // SubRaw subtracts uint64 from Uint
func (i Uint) SubRaw(i2 uint64) Uint { func (i Uint) SubRaw(i2 uint64) Uint {
return i.Sub(NewUint(i2)) return i.Sub(NewUint(i2))
@ -436,15 +447,15 @@ func (i Uint) SubRaw(i2 uint64) Uint {
// Mul multiples two Uints // Mul multiples two Uints
func (i Uint) Mul(i2 Uint) (res Uint) { func (i Uint) Mul(i2 Uint) (res Uint) {
// Check overflow
if i.i.BitLen()+i2.i.BitLen()-1 > 256 { if i.i.BitLen()+i2.i.BitLen()-1 > 256 {
panic("Uint overflow") panic("Uint overflow")
} }
res = Uint{mul(i.i, i2.i)} res = Uint{mul(i.i, i2.i)}
// Check overflow if UintOverflow(res) {
if res.Sign() == -1 || res.Sign() == 1 && res.i.BitLen() > 256 {
panic("Uint overflow") panic("Uint overflow")
} }
return 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(...)) // intended to be used with require/assert: require.True(IntEq(...))
func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) { 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() return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()

View File

@ -590,3 +590,53 @@ func TestEncodingTableUint(t *testing.T) {
require.Equal(t, tc.i, i, "Unmarshaled value is different from expected. tc #%d", tcnum) 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,
)
}
}

11
types/module_clients.go Normal file
View File

@ -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
}

View File

@ -4,7 +4,10 @@ package types
type Result struct { type Result struct {
// Code is the response code, is stored back on the chain. // 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 is any data returned from the app.
Data []byte Data []byte
@ -13,10 +16,10 @@ type Result struct {
Log string Log string
// GasWanted is the maximum units of work we allow this tx to perform. // 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 is the amount of gas actually consumed. NOTE: unimplemented
GasUsed int64 GasUsed uint64
// Tx fee amount and denom. // Tx fee amount and denom.
FeeAmount int64 FeeAmount int64

View File

@ -13,6 +13,6 @@ func TestResult(t *testing.T) {
res.Data = []byte("data") res.Data = []byte("data")
require.True(t, res.IsOK()) require.True(t, res.IsOK())
res.Code = ABCICodeType(1) res.Code = CodeType(1)
require.False(t, res.IsOK()) require.False(t, res.IsOK())
} }

View File

@ -64,6 +64,7 @@ type MultiStore interface { //nolint
CacheMultiStore() CacheMultiStore CacheMultiStore() CacheMultiStore
// Convenience for fetching substores. // Convenience for fetching substores.
// If the store does not exist, panics.
GetStore(StoreKey) Store GetStore(StoreKey) Store
GetKVStore(StoreKey) KVStore GetKVStore(StoreKey) KVStore

View File

@ -32,9 +32,12 @@ type Msg interface {
// Transactions objects must fulfill the Tx // Transactions objects must fulfill the Tx
type Tx interface { type Tx interface {
// Gets the all the transaction's messages.
// Gets the Msg.
GetMsgs() []Msg GetMsgs() []Msg
// ValidateBasic does a simple and lightweight validation check that doesn't
// require access to any other information.
ValidateBasic() Error
} }
//__________________________________________________________ //__________________________________________________________

View File

@ -21,11 +21,11 @@ type Account interface {
GetPubKey() crypto.PubKey // can return nil. GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error SetPubKey(crypto.PubKey) error
GetAccountNumber() int64 GetAccountNumber() uint64
SetAccountNumber(int64) error SetAccountNumber(uint64) error
GetSequence() int64 GetSequence() uint64
SetSequence(int64) error SetSequence(uint64) error
GetCoins() sdk.Coins GetCoins() sdk.Coins
SetCoins(sdk.Coins) error SetCoins(sdk.Coins) error
@ -48,8 +48,8 @@ type BaseAccount struct {
Address sdk.AccAddress `json:"address"` Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"` Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"` PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
} }
// Prototype function for BaseAccount // Prototype function for BaseAccount
@ -100,23 +100,23 @@ func (acc *BaseAccount) SetCoins(coins sdk.Coins) error {
} }
// Implements Account // Implements Account
func (acc *BaseAccount) GetAccountNumber() int64 { func (acc *BaseAccount) GetAccountNumber() uint64 {
return acc.AccountNumber return acc.AccountNumber
} }
// Implements Account // Implements Account
func (acc *BaseAccount) SetAccountNumber(accNumber int64) error { func (acc *BaseAccount) SetAccountNumber(accNumber uint64) error {
acc.AccountNumber = accNumber acc.AccountNumber = accNumber
return nil return nil
} }
// Implements sdk.Account. // Implements sdk.Account.
func (acc *BaseAccount) GetSequence() int64 { func (acc *BaseAccount) GetSequence() uint64 {
return acc.Sequence return acc.Sequence
} }
// Implements sdk.Account. // Implements sdk.Account.
func (acc *BaseAccount) SetSequence(seq int64) error { func (acc *BaseAccount) SetSequence(seq uint64) error {
acc.Sequence = seq acc.Sequence = seq
return nil return nil
} }

View File

@ -67,7 +67,7 @@ func TestBaseAccountSequence(t *testing.T) {
_, _, addr := keyPubAddr() _, _, addr := keyPubAddr()
acc := NewBaseAccountWithAddress(addr) acc := NewBaseAccountWithAddress(addr)
seq := int64(7) seq := uint64(7)
err := acc.SetSequence(seq) err := acc.SetSequence(seq)
require.Nil(t, err) require.Nil(t, err)
@ -79,7 +79,7 @@ func TestBaseAccountMarshal(t *testing.T) {
acc := NewBaseAccountWithAddress(addr) acc := NewBaseAccountWithAddress(addr)
someCoins := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 246)} someCoins := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 246)}
seq := int64(7) seq := uint64(7)
// set everything on the account // set everything on the account
err := acc.SetPubKey(pub) err := acc.SetPubKey(pub)

View File

@ -8,7 +8,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/crypto/secp256k1"
) )
@ -67,27 +66,18 @@ func NewAnteHandler(am AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler {
} }
}() }()
err := validateBasic(stdTx) if err := tx.ValidateBasic(); err != nil {
if err != nil {
return newCtx, err.Result(), true return newCtx, err.Result(), true
} }
// charge gas for the memo // charge gas for the memo
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo") newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
// stdSigs contains the sequence number, account number, and signatures // stdSigs contains the sequence number, account number, and signatures.
stdSigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice. // When simulating, this would just be a 0-length slice.
stdSigs := stdTx.GetSignatures()
signerAddrs := stdTx.GetSigners() 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 // create the list of all sign bytes
signBytesList := getSignBytesList(newCtx.ChainID(), stdTx, stdSigs) signBytesList := getSignBytesList(newCtx.ChainID(), stdTx, stdSigs)
signerAccs, res := getSignerAccs(newCtx, am, signerAddrs) 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) { func getSignerAccs(ctx sdk.Context, am AccountKeeper, addrs []sdk.AccAddress) (accs []Account, res sdk.Result) {
accs = make([]Account, len(addrs)) accs = make([]Account, len(addrs))
for i := 0; i < len(accs); i++ { 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 gasCost := gas / gasPerUnitCost
gasFees := make(sdk.Coins, len(fees)) gasFees := make(sdk.Coins, len(fees))
// TODO: Make this not price all coins in the same way // 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++ { 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) return fees.Plus(gasFees)
} }
@ -277,23 +247,35 @@ func deductFees(acc Account, fee StdFee) (Account, sdk.Result) {
coins := acc.GetCoins() coins := acc.GetCoins()
feeAmount := fee.Amount feeAmount := fee.Amount
newCoins := coins.Minus(feeAmount) if !feeAmount.IsValid() {
if !newCoins.IsNotNegative() { 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) errMsg := fmt.Sprintf("%s < %s", coins, feeAmount)
return nil, sdk.ErrInsufficientFunds(errMsg).Result() return nil, sdk.ErrInsufficientFunds(errMsg).Result()
} }
err := acc.SetCoins(newCoins) err := acc.SetCoins(newCoins)
if err != nil { if err != nil {
// Handle w/ #870 // Handle w/ #870
panic(err) panic(err)
} }
return acc, sdk.Result{} return acc, sdk.Result{}
} }
func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result { func ensureSufficientMempoolFees(ctx sdk.Context, stdTx StdTx) sdk.Result {
// currently we use a very primitive gas pricing model with a constant gasPrice. // 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. // 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) requiredFees := adjustFeesByGas(ctx.MinimumFees(), stdTx.Fee.Gas)
// NOTE: !A.IsAllGTE(B) is not the same as A.IsAllLT(B). // 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 { 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 { if simulate || ctx.BlockHeight() == 0 {
return ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) return ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
} }
return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas)) return ctx.WithGasMeter(sdk.NewGasMeter(stdTx.Fee.Gas))
} }
@ -322,15 +306,3 @@ func getSignBytesList(chainID string, stdTx StdTx, stdSigs []StdSignature) (sign
} }
return 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
}

View File

@ -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) { func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) {
_, result, abort := anteHandler(ctx, tx, simulate) _, result, abort := anteHandler(ctx, tx, simulate)
require.False(t, abort) require.False(t, abort)
require.Equal(t, sdk.ABCICodeOK, result.Code) require.Equal(t, sdk.CodeOK, result.Code)
require.True(t, result.IsOK()) 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) { 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) newCtx, result, abort := anteHandler(ctx, tx, simulate)
require.True(t, abort) 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 { if code == sdk.CodeOutOfGas {
stdTx, ok := tx.(StdTx) 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)) sigs := make([]StdSignature, len(privs))
for i, priv := range privs { for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") 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 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)) sigs := make([]StdSignature, len(privs))
for i, priv := range privs { for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo) 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 // 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)) sigs := make([]StdSignature, len(privs))
for i, priv := range privs { for i, priv := range privs {
sig, err := priv.Sign(signBytes) sig, err := priv.Sign(signBytes)
@ -132,7 +133,7 @@ func TestAnteHandlerSigErrors(t *testing.T) {
msgs := []sdk.Msg{msg1, msg2} msgs := []sdk.Msg{msg1, msg2}
// test no signatures // 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 = newTestTx(ctx, msgs, privs, accNums, seqs, fee)
// tx.GetSigners returns addresses in correct order: addr1, addr2, addr3 // 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) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized)
// test num sigs dont match GetSigners // 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) tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized)
// test an unrecognized account // 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) tx = newTestTx(ctx, msgs, privs, accNums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnknownAddress)
@ -192,30 +193,30 @@ func TestAnteHandlerAccountNumbers(t *testing.T) {
msgs := []sdk.Msg{msg} msgs := []sdk.Msg{msg}
// test good tx from one signer // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
// new tx from wrong account number // new tx from wrong account number
seqs = []int64{1} seqs = []uint64{1}
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.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// from correct account number // from correct account number
seqs = []int64{1} seqs = []uint64{1}
tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) tx = newTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
// new tx with another signer and incorrect account numbers // new tx with another signer and incorrect account numbers
msg1 := newTestMsg(addr1, addr2) msg1 := newTestMsg(addr1, addr2)
msg2 := newTestMsg(addr2, addr1) msg2 := newTestMsg(addr2, addr1)
msgs = []sdk.Msg{msg1, msg2} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// correct account numbers // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
} }
@ -252,30 +253,30 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) {
msgs := []sdk.Msg{msg} msgs := []sdk.Msg{msg}
// test good tx from one signer // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
// new tx from wrong account number // new tx from wrong account number
seqs = []int64{1} seqs = []uint64{1}
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.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// from correct account number // from correct account number
seqs = []int64{1} seqs = []uint64{1}
tx = newTestTx(ctx, msgs, privs, []int64{0}, seqs, fee) tx = newTestTx(ctx, msgs, privs, []uint64{0}, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
// new tx with another signer and incorrect account numbers // new tx with another signer and incorrect account numbers
msg1 := newTestMsg(addr1, addr2) msg1 := newTestMsg(addr1, addr2)
msg2 := newTestMsg(addr2, addr1) msg2 := newTestMsg(addr2, addr1)
msgs = []sdk.Msg{msg1, msg2} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// correct account numbers // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
} }
@ -316,7 +317,7 @@ func TestAnteHandlerSequences(t *testing.T) {
msgs := []sdk.Msg{msg} msgs := []sdk.Msg{msg}
// test good tx from one signer // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
@ -324,7 +325,7 @@ func TestAnteHandlerSequences(t *testing.T) {
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// fix sequence, should pass // fix sequence, should pass
seqs = []int64{1} seqs = []uint64{1}
tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
@ -333,7 +334,7 @@ func TestAnteHandlerSequences(t *testing.T) {
msg2 := newTestMsg(addr3, addr1) msg2 := newTestMsg(addr3, addr1)
msgs = []sdk.Msg{msg1, msg2} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
@ -343,18 +344,18 @@ func TestAnteHandlerSequences(t *testing.T) {
// tx from just second signer with incorrect sequence fails // tx from just second signer with incorrect sequence fails
msg = newTestMsg(addr2) msg = newTestMsg(addr2)
msgs = []sdk.Msg{msg} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidSequence)
// fix the sequence and it passes // 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) checkValidTx(t, anteHandler, ctx, tx, false)
// another tx from both of them that passes // another tx from both of them that passes
msg = newTestMsg(addr1, addr2) msg = newTestMsg(addr1, addr2)
msgs = []sdk.Msg{msg} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
} }
@ -380,7 +381,7 @@ func TestAnteHandlerFees(t *testing.T) {
// msg and signatures // msg and signatures
var tx sdk.Tx var tx sdk.Tx
msg := newTestMsg(addr1) 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() fee := newStdFee()
msgs := []sdk.Msg{msg} msgs := []sdk.Msg{msg}
@ -423,7 +424,7 @@ func TestAnteHandlerMemoGas(t *testing.T) {
// msg and signatures // msg and signatures
var tx sdk.Tx var tx sdk.Tx
msg := newTestMsg(addr1) 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)) fee := NewStdFee(0, sdk.NewInt64Coin("atom", 0))
// tx does not have enough gas // tx does not have enough gas
@ -482,19 +483,19 @@ func TestAnteHandlerMultiSigner(t *testing.T) {
fee := newStdFee() fee := newStdFee()
// signers in order // 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") 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) checkValidTx(t, anteHandler, ctx, tx, false)
// change sequence numbers // 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) 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) checkValidTx(t, anteHandler, ctx, tx, false)
// expected seqs = [3, 2, 2] // 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) 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) fee3.Amount[0].Amount = fee3.Amount[0].Amount.AddRaw(100)
// test good tx and signBytes // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
@ -541,8 +542,8 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
cases := []struct { cases := []struct {
chainID string chainID string
accnum int64 accnum uint64
seq int64 seq uint64
fee StdFee fee StdFee
msgs []sdk.Msg msgs []sdk.Msg
code sdk.CodeType code sdk.CodeType
@ -555,7 +556,7 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
{chainID, 0, 1, fee3, msgs, codeUnauth}, // test wrong fee {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 { for _, cs := range cases {
tx := newTestTxWithSignBytes( tx := newTestTxWithSignBytes(
@ -567,14 +568,14 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
} }
// test wrong signer if public key exist // 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeUnauthorized)
// test wrong signer if public doesn't exist // test wrong signer if public doesn't exist
msg = newTestMsg(addr2) msg = newTestMsg(addr2)
msgs = []sdk.Msg{msg} 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey)
@ -608,7 +609,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
// test good tx and set public key // test good tx and set public key
msg := newTestMsg(addr1) msg := newTestMsg(addr1)
msgs := []sdk.Msg{msg} 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() fee := newStdFee()
tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx, false) checkValidTx(t, anteHandler, ctx, tx, false)
@ -619,7 +620,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
// test public key not found // test public key not found
msg = newTestMsg(addr2) msg = newTestMsg(addr2)
msgs = []sdk.Msg{msg} 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 := tx.(StdTx).GetSignatures()
sigs[0].PubKey = nil sigs[0].PubKey = nil
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey)
@ -628,7 +629,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
require.Nil(t, acc2.GetPubKey()) require.Nil(t, acc2.GetPubKey())
// test invalid signature and public key // 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) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeInvalidPubKey)
acc2 = mapper.GetAccount(ctx, addr2) acc2 = mapper.GetAccount(ctx, addr2)
@ -676,7 +677,7 @@ func TestConsumeSignatureVerificationGas(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args args args args
gasConsumed int64 gasConsumed uint64
wantPanic bool wantPanic bool
}{ }{
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), ed25519.GenPrivKey().PubKey()}, ed25519VerifyCost, false}, {"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), ed25519.GenPrivKey().PubKey()}, ed25519VerifyCost, false},
@ -698,7 +699,7 @@ func TestConsumeSignatureVerificationGas(t *testing.T) {
func TestAdjustFeesByGas(t *testing.T) { func TestAdjustFeesByGas(t *testing.T) {
type args struct { type args struct {
fee sdk.Coins fee sdk.Coins
gas int64 gas uint64
} }
tests := []struct { tests := []struct {
name string name string
@ -707,7 +708,6 @@ func TestAdjustFeesByGas(t *testing.T) {
}{ }{
{"nil coins", args{sdk.Coins{}, 10000}, sdk.Coins{}}, {"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)}}, {"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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -785,7 +785,7 @@ func TestAnteHandlerSigLimitExceeded(t *testing.T) {
// test rejection logic // test rejection logic
privs, accnums, seqs := []crypto.PrivKey{priv1, priv2, priv3, priv4, priv5, priv6, priv7, priv8}, 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) tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeTooManySignatures) checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeTooManySignatures)
} }

View File

@ -5,34 +5,17 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" 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 // GetAccountCmd returns a query account that will display the state of the
// account at a given address. // account at a given address.
// nolint: unparam // nolint: unparam
func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecoder) *cobra.Command { func GetAccountCmd(storeName string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{ cmd := &cobra.Command{
Use: "account [address]", Use: "account [address]",
Short: "Query account balance", Short: "Query account balance",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
@ -47,9 +30,9 @@ func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecod
cliCtx := context.NewCLIContext(). cliCtx := context.NewCLIContext().
WithCodec(cdc). WithCodec(cdc).
WithAccountDecoder(decoder) WithAccountDecoder(cdc)
if err := cliCtx.EnsureAccountExistsFromAddr(key); err != nil { if err = cliCtx.EnsureAccountExistsFromAddr(key); err != nil {
return err return err
} }
@ -72,4 +55,7 @@ func GetAccountCmd(storeName string, cdc *codec.Codec, decoder auth.AccountDecod
return nil return nil
}, },
} }
// Add the flags here and return the command
return client.GetCommands(cmd)[0]
} }

View File

@ -2,9 +2,8 @@ package cli
import ( import (
"fmt" "fmt"
"github.com/pkg/errors"
"github.com/spf13/viper"
"io/ioutil" "io/ioutil"
"os"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
@ -12,7 +11,9 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/go-amino" "github.com/tendermint/go-amino"
) )
@ -21,10 +22,11 @@ const (
flagValidateSigs = "validate-signatures" flagValidateSigs = "validate-signatures"
flagOffline = "offline" flagOffline = "offline"
flagSigOnly = "signature-only" flagSigOnly = "signature-only"
flagOutfile = "output-document"
) )
// GetSignCommand returns the sign command // 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{ cmd := &cobra.Command{
Use: "sign <file>", Use: "sign <file>",
Short: "Sign transactions generated offline", 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. 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 Thus account number or sequence number lookups will not be performed and it is
recommended to set such parameters manually.`, recommended to set such parameters manually.`,
RunE: makeSignCmd(codec, decoder), RunE: makeSignCmd(codec),
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
} }
cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") 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, "+ 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.") "those who have already signed it, and make sure that signatures are in the correct order.")
cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") cmd.Flags().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) { return func(cmd *cobra.Command, args []string) (err error) {
stdTx, err := readAndUnmarshalStdTx(cdc, args[0]) stdTx, err := readAndUnmarshalStdTx(cdc, args[0])
if err != nil { if err != nil {
@ -72,7 +78,7 @@ func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.
if name == "" { if name == "" {
return errors.New("required flag \"name\" has not been set") 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() txBldr := authtxb.NewTxBuilderFromCLI()
// if --signature-only is on, then override --append // 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 { if err != nil {
return err 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 return
} }
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
@ -17,11 +16,11 @@ import (
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) { func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
r.HandleFunc( r.HandleFunc(
"/auth/accounts/{address}", "/auth/accounts/{address}",
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx), QueryAccountRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx),
).Methods("GET") ).Methods("GET")
r.HandleFunc( r.HandleFunc(
"/bank/balances/{address}", "/bank/balances/{address}",
QueryBalancesRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx), QueryBalancesRequestHandlerFn(storeName, cdc, context.GetAccountDecoder(cdc), cliCtx),
).Methods("GET") ).Methods("GET")
r.HandleFunc( r.HandleFunc(
"/tx/sign", "/tx/sign",

View File

@ -18,8 +18,8 @@ type SignBody struct {
LocalAccountName string `json:"name"` LocalAccountName string `json:"name"`
Password string `json:"password"` Password string `json:"password"`
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
AppendSig bool `json:"append_sig"` AppendSig bool `json:"append_sig"`
} }

View File

@ -10,8 +10,8 @@ import (
// it is signed. For use in the CLI. // it is signed. For use in the CLI.
type StdSignMsg struct { type StdSignMsg struct {
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"` AccountNumber uint64 `json:"account_number"`
Sequence int64 `json:"sequence"` Sequence uint64 `json:"sequence"`
Fee auth.StdFee `json:"fee"` Fee auth.StdFee `json:"fee"`
Msgs []sdk.Msg `json:"msgs"` Msgs []sdk.Msg `json:"msgs"`
Memo string `json:"memo"` Memo string `json:"memo"`

View File

@ -14,9 +14,9 @@ import (
// TxBuilder implements a transaction context created in SDK modules. // TxBuilder implements a transaction context created in SDK modules.
type TxBuilder struct { type TxBuilder struct {
Codec *codec.Codec Codec *codec.Codec
AccountNumber int64 AccountNumber uint64
Sequence int64 Sequence uint64
Gas int64 // TODO: should this turn into uint64? requires further discussion - see #2173 Gas uint64
GasAdjustment float64 GasAdjustment float64
SimulateGas bool SimulateGas bool
ChainID string ChainID string
@ -38,10 +38,10 @@ func NewTxBuilderFromCLI() TxBuilder {
return TxBuilder{ return TxBuilder{
ChainID: chainID, ChainID: chainID,
AccountNumber: viper.GetInt64(client.FlagAccountNumber), AccountNumber: uint64(viper.GetInt64(client.FlagAccountNumber)),
Gas: client.GasFlagVar.Gas, Gas: client.GasFlagVar.Gas,
GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment), GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment),
Sequence: viper.GetInt64(client.FlagSequence), Sequence: uint64(viper.GetInt64(client.FlagSequence)),
SimulateGas: client.GasFlagVar.Simulate, SimulateGas: client.GasFlagVar.Simulate,
Fee: viper.GetString(client.FlagFee), Fee: viper.GetString(client.FlagFee),
Memo: viper.GetString(client.FlagMemo), 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. // 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 bldr.Gas = gas
return bldr 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. // 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 bldr.Sequence = sequence
return bldr return bldr
} }
@ -85,7 +85,7 @@ func (bldr TxBuilder) WithMemo(memo string) TxBuilder {
} }
// WithAccountNumber returns a copy of the context with an account number. // 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 bldr.AccountNumber = accnum
return bldr return bldr
} }

View File

@ -9,8 +9,8 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/tendermint/tendermint/crypto/ed25519"
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/tendermint/tendermint/crypto/ed25519"
) )
var ( var (
@ -21,9 +21,9 @@ var (
func TestTxBuilderBuild(t *testing.T) { func TestTxBuilderBuild(t *testing.T) {
type fields struct { type fields struct {
Codec *codec.Codec Codec *codec.Codec
AccountNumber int64 AccountNumber uint64
Sequence int64 Sequence uint64
Gas int64 Gas uint64
GasAdjustment float64 GasAdjustment float64
SimulateGas bool SimulateGas bool
ChainID string ChainID string

View File

@ -118,7 +118,7 @@ func (am AccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (crypto.
} }
// Returns the Sequence of the account at address // 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) acc := am.GetAccount(ctx, addr)
if acc == nil { if acc == nil {
return 0, sdk.ErrUnknownAddress(addr.String()) 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 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) acc := am.GetAccount(ctx, addr)
if acc == nil { if acc == nil {
return sdk.ErrUnknownAddress(addr.String()) 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 // Returns and increments the global account number counter
func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) int64 { func (am AccountKeeper) GetNextAccountNumber(ctx sdk.Context) uint64 {
var accNumber int64 var accNumber uint64
store := ctx.KVStore(am.key) store := ctx.KVStore(am.key)
bz := store.Get(globalAccountNumberKey) bz := store.Get(globalAccountNumberKey)
if bz == nil { if bz == nil {

View File

@ -49,7 +49,7 @@ func TestAccountMapperGetSet(t *testing.T) {
require.Nil(t, mapper.GetAccount(ctx, addr)) require.Nil(t, mapper.GetAccount(ctx, addr))
// set some values on the account and save it // set some values on the account and save it
newSequence := int64(20) newSequence := uint64(20)
acc.SetSequence(newSequence) acc.SetSequence(newSequence)
mapper.SetAccount(ctx, acc) mapper.SetAccount(ctx, acc)
@ -75,8 +75,8 @@ func TestAccountMapperRemoveAccount(t *testing.T) {
acc1 := mapper.NewAccountWithAddress(ctx, addr1) acc1 := mapper.NewAccountWithAddress(ctx, addr1)
acc2 := mapper.NewAccountWithAddress(ctx, addr2) acc2 := mapper.NewAccountWithAddress(ctx, addr2)
accSeq1 := int64(20) accSeq1 := uint64(20)
accSeq2 := int64(40) accSeq2 := uint64(40)
acc1.SetSequence(accSeq1) acc1.SetSequence(accSeq1)
acc2.SetSequence(accSeq2) acc2.SetSequence(accSeq2)

Some files were not shown because too many files have changed in this diff Show More