From a573625df8a7bba774f4e317205e5ee76ef83cec Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Thu, 23 Apr 2020 12:35:58 -0400 Subject: [PATCH] [R4R] bump SDK version to v0.38.3 (#421) * bump SDK version to v0.38.3 Co-authored-by: Denali Marsh Co-authored-by: Kevin Davis Co-authored-by: Kevin Davis Co-authored-by: denalimarsh Co-authored-by: rhuairahrighairigh --- app/app.go | 109 ++- app/export.go | 28 +- app/params/doc.go | 19 + app/params/params.go | 15 + app/sim_test.go | 651 +++--------------- app/test_common.go | 4 + cli_test/test_helpers.go | 35 +- cmd/kvcli/main.go | 23 +- cmd/kvd/main.go | 4 +- go.mod | 17 +- go.sum | 409 ++++++++--- rest_test/setup/setuptest.go | 5 +- x/auction/alias.go | 26 +- x/auction/client/cli/query.go | 4 +- x/auction/client/cli/tx.go | 8 +- x/auction/genesis.go | 2 +- x/auction/handler.go | 15 +- x/auction/keeper/auctions.go | 103 +-- x/auction/keeper/auctions_test.go | 35 +- x/auction/keeper/bidding_test.go | 123 ++-- x/auction/keeper/invariants.go | 4 +- x/auction/keeper/keeper.go | 16 +- x/auction/keeper/querier.go | 26 +- x/auction/module.go | 59 +- x/auction/simulation/decoder.go | 5 +- x/auction/simulation/decoder_test.go | 13 +- x/auction/simulation/genesis.go | 6 +- x/auction/simulation/operations.go | 251 +++++++ x/auction/simulation/operations/msg.go | 177 ----- x/auction/simulation/params.go | 20 +- x/auction/types/auctions.go | 11 +- x/auction/types/errors.go | 126 +--- x/auction/types/expected_keepers.go | 10 +- x/auction/types/msg.go | 17 +- x/auction/types/params.go | 116 +++- x/bep3/abci_test.go | 14 +- x/bep3/alias.go | 19 - x/bep3/client/cli/query.go | 4 +- x/bep3/client/cli/tx.go | 14 +- x/bep3/client/rest/rest.go | 39 +- x/bep3/genesis.go | 8 +- x/bep3/handler.go | 32 +- x/bep3/handler_test.go | 50 +- x/bep3/keeper/asset.go | 38 +- x/bep3/keeper/keeper.go | 11 +- x/bep3/keeper/params.go | 8 +- x/bep3/keeper/params_test.go | 15 +- x/bep3/keeper/querier.go | 45 +- x/bep3/keeper/querier_test.go | 17 +- x/bep3/keeper/swap.go | 117 ++-- x/bep3/keeper/swap_test.go | 21 +- x/bep3/module.go | 59 +- x/bep3/simulation/decoder.go | 10 +- x/bep3/simulation/decoder_test.go | 17 +- x/bep3/simulation/genesis.go | 19 +- x/bep3/simulation/operations.go | 210 ++++++ x/bep3/simulation/operations/msg.go | 146 ---- x/bep3/simulation/params.go | 10 +- x/bep3/types/errors.go | 144 +--- x/bep3/types/expected_keepers.go | 10 +- x/bep3/types/msg.go | 107 +-- x/bep3/types/msg_test.go | 17 +- x/bep3/types/params.go | 110 ++- x/bep3/types/params_test.go | 6 +- x/bep3/types/querier.go | 12 +- x/bep3/types/swap.go | 33 +- x/bep3/types/swap_test.go | 15 +- x/cdp/alias.go | 36 +- x/cdp/client/cli/query.go | 10 +- x/cdp/client/cli/tx.go | 20 +- x/cdp/genesis.go | 2 +- x/cdp/handler.go | 40 +- x/cdp/handler_test.go | 13 +- x/cdp/keeper/auctions.go | 10 +- x/cdp/keeper/cdp.go | 55 +- x/cdp/keeper/cdp_test.go | 32 +- x/cdp/keeper/deposit.go | 37 +- x/cdp/keeper/deposit_test.go | 15 +- x/cdp/keeper/draw.go | 55 +- x/cdp/keeper/draw_test.go | 21 +- x/cdp/keeper/fees.go | 10 +- x/cdp/keeper/fees_test.go | 8 +- x/cdp/keeper/keeper.go | 9 +- x/cdp/keeper/keeper_bench_test.go | 2 +- x/cdp/keeper/querier.go | 70 +- x/cdp/keeper/savings.go | 7 +- x/cdp/keeper/seize.go | 14 +- x/cdp/keeper/seize_test.go | 5 +- x/cdp/module.go | 55 +- x/cdp/simulation/decoder.go | 5 +- x/cdp/simulation/decoder_test.go | 25 +- x/cdp/simulation/genesis.go | 10 +- .../{operations/msgs.go => operations.go} | 259 ++++--- x/cdp/simulation/{operations => }/utils.go | 2 +- .../simulation/{operations => }/utils_test.go | 6 +- x/cdp/types/errors.go | 145 +--- x/cdp/types/expected_keepers.go | 22 +- x/cdp/types/msg.go | 83 +-- x/cdp/types/msg_test.go | 30 +- x/cdp/types/params.go | 219 ++++-- x/kavadist/alias.go | 14 +- x/kavadist/handler.go | 10 +- x/kavadist/keeper/keeper.go | 9 +- x/kavadist/keeper/mint.go | 4 +- x/kavadist/module.go | 57 +- x/kavadist/simulation/decoder.go | 5 +- x/kavadist/simulation/decoder_test.go | 8 +- x/kavadist/simulation/params.go | 8 +- x/kavadist/types/errors.go | 11 - x/kavadist/types/expected_keepers.go | 4 +- x/kavadist/types/params.go | 38 +- x/pricefeed/alias.go | 19 +- x/pricefeed/client/cli/query.go | 3 +- x/pricefeed/client/cli/tx.go | 7 +- x/pricefeed/handler.go | 16 +- x/pricefeed/keeper/keeper.go | 121 ++-- x/pricefeed/keeper/params.go | 12 +- x/pricefeed/keeper/querier.go | 63 +- x/pricefeed/module.go | 59 +- x/pricefeed/simulation/decoder.go | 4 +- x/pricefeed/simulation/decoder_test.go | 11 +- x/pricefeed/simulation/genesis.go | 3 +- .../{operations/msg.go => operations.go} | 108 ++- x/pricefeed/simulation/params.go | 1 - x/pricefeed/types/errors.go | 56 +- x/pricefeed/types/msgs.go | 20 +- x/pricefeed/types/params.go | 19 +- x/validator-vesting/client/cli/query.go | 7 +- x/validator-vesting/keeper/keeper.go | 2 +- x/validator-vesting/keeper/keeper_test.go | 2 +- x/validator-vesting/keeper/querier.go | 19 +- x/validator-vesting/keeper/test_common.go | 8 +- x/validator-vesting/module.go | 58 +- x/validator-vesting/simulation/decoder.go | 5 +- .../simulation/decoder_test.go | 11 +- x/validator-vesting/simulation/genesis.go | 6 +- x/validator-vesting/test_common.go | 4 +- x/validator-vesting/types/expected_keepers.go | 8 +- .../types/validator_vesting_account.go | 99 ++- .../types/validator_vesting_account_test.go | 2 +- 140 files changed, 3247 insertions(+), 2875 deletions(-) create mode 100644 app/params/doc.go create mode 100644 app/params/params.go create mode 100644 x/auction/simulation/operations.go delete mode 100644 x/auction/simulation/operations/msg.go create mode 100644 x/bep3/simulation/operations.go delete mode 100644 x/bep3/simulation/operations/msg.go rename x/cdp/simulation/{operations/msgs.go => operations.go} (50%) rename x/cdp/simulation/{operations => }/utils.go (95%) rename x/cdp/simulation/{operations => }/utils_test.go (81%) delete mode 100644 x/kavadist/types/errors.go rename x/pricefeed/simulation/{operations/msg.go => operations.go} (61%) diff --git a/app/app.go b/app/app.go index 7d1a4bec..151deeaa 100644 --- a/app/app.go +++ b/app/app.go @@ -12,12 +12,13 @@ import ( validatorvesting "github.com/kava-labs/kava/x/validator-vesting" abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" + tmos "github.com/tendermint/tendermint/libs/os" dbm "github.com/tendermint/tm-db" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" @@ -26,6 +27,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/crisis" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -61,6 +63,7 @@ var ( crisis.AppModuleBasic{}, slashing.AppModuleBasic{}, supply.AppModuleBasic{}, + evidence.AppModuleBasic{}, auction.AppModuleBasic{}, cdp.AppModuleBasic{}, pricefeed.AppModuleBasic{}, @@ -86,7 +89,10 @@ var ( } ) -// Extended ABCI application +// Verify app interface at compile time +var _ simapp.App = (*App)(nil) + +// App represents an extended ABCI application type App struct { *bam.BaseApp cdc *codec.Codec @@ -108,6 +114,7 @@ type App struct { govKeeper gov.Keeper crisisKeeper crisis.Keeper paramsKeeper params.Keeper + evidenceKeeper evidence.Keeper vvKeeper validatorvesting.Keeper auctionKeeper auction.Keeper cdpKeeper cdp.Keeper @@ -136,7 +143,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, keys := sdk.NewKVStoreKeys( bam.MainStoreKey, auth.StoreKey, staking.StoreKey, supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, - gov.StoreKey, params.StoreKey, validatorvesting.StoreKey, + gov.StoreKey, params.StoreKey, evidence.StoreKey, validatorvesting.StoreKey, auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, bep3.StoreKey, kavadist.StoreKey, ) @@ -151,7 +158,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, } // init params keeper and subspaces - app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) + app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey]) authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace) bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace) stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace) @@ -159,6 +166,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace) slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace) govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()) + evidenceSubspace := app.paramsKeeper.Subspace(evidence.DefaultParamspace) crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace) auctionSubspace := app.paramsKeeper.Subspace(auction.DefaultParamspace) cdpSubspace := app.paramsKeeper.Subspace(cdp.DefaultParamspace) @@ -171,51 +179,68 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, app.cdc, keys[auth.StoreKey], authSubspace, - auth.ProtoBaseAccount) + auth.ProtoBaseAccount, + ) app.bankKeeper = bank.NewBaseKeeper( app.accountKeeper, bankSubspace, - bank.DefaultCodespace, - app.ModuleAccountAddrs()) + app.ModuleAccountAddrs(), + ) app.supplyKeeper = supply.NewKeeper( app.cdc, keys[supply.StoreKey], app.accountKeeper, app.bankKeeper, - mAccPerms) + mAccPerms, + ) stakingKeeper := staking.NewKeeper( app.cdc, keys[staking.StoreKey], app.supplyKeeper, stakingSubspace, - staking.DefaultCodespace) + ) app.mintKeeper = mint.NewKeeper( app.cdc, keys[mint.StoreKey], mintSubspace, &stakingKeeper, app.supplyKeeper, - auth.FeeCollectorName) + auth.FeeCollectorName, + ) app.distrKeeper = distr.NewKeeper( app.cdc, keys[distr.StoreKey], distrSubspace, &stakingKeeper, app.supplyKeeper, - distr.DefaultCodespace, auth.FeeCollectorName, - app.ModuleAccountAddrs()) + app.ModuleAccountAddrs(), + ) app.slashingKeeper = slashing.NewKeeper( app.cdc, keys[slashing.StoreKey], &stakingKeeper, slashingSubspace, - slashing.DefaultCodespace) + ) app.crisisKeeper = crisis.NewKeeper( crisisSubspace, invCheckPeriod, app.supplyKeeper, - auth.FeeCollectorName) + auth.FeeCollectorName, + ) + + // create evidence keeper with router + evidenceKeeper := evidence.NewKeeper( + app.cdc, + keys[evidence.StoreKey], + evidenceSubspace, + &app.stakingKeeper, + app.slashingKeeper, + ) + evidenceRouter := evidence.NewRouter() + evidenceKeeper.SetRouter(evidenceRouter) + app.evidenceKeeper = *evidenceKeeper + govRouter := gov.NewRouter() govRouter. AddRoute(gov.RouterKey, gov.ProposalHandler). @@ -227,25 +252,28 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, govSubspace, app.supplyKeeper, &stakingKeeper, - gov.DefaultCodespace, - govRouter) + govRouter, + ) + app.vvKeeper = validatorvesting.NewKeeper( app.cdc, keys[validatorvesting.StoreKey], app.accountKeeper, app.bankKeeper, app.supplyKeeper, - &stakingKeeper) + &stakingKeeper, + ) app.pricefeedKeeper = pricefeed.NewKeeper( app.cdc, keys[pricefeed.StoreKey], pricefeedSubspace, - pricefeed.DefaultCodespace) + ) app.auctionKeeper = auction.NewKeeper( app.cdc, keys[auction.StoreKey], app.supplyKeeper, - auctionSubspace) + auctionSubspace, + ) app.cdpKeeper = cdp.NewKeeper( app.cdc, keys[cdp.StoreKey], @@ -254,19 +282,18 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, app.auctionKeeper, app.supplyKeeper, app.accountKeeper, - cdp.DefaultCodespace) + ) app.bep3Keeper = bep3.NewKeeper( app.cdc, keys[bep3.StoreKey], app.supplyKeeper, bep3Subspace, - bep3.DefaultCodespace) + ) app.kavadistKeeper = kavadist.NewKeeper( app.cdc, keys[kavadist.StoreKey], kavadistSubspace, app.supplyKeeper, - kavadist.DefaultCodespace, ) // register the staking hooks @@ -282,16 +309,17 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, bank.NewAppModule(app.bankKeeper, app.accountKeeper), crisis.NewAppModule(&app.crisisKeeper), supply.NewAppModule(app.supplyKeeper, app.accountKeeper), - distr.NewAppModule(app.distrKeeper, app.supplyKeeper), - gov.NewAppModule(app.govKeeper, app.supplyKeeper), + gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper), mint.NewAppModule(app.mintKeeper), - slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), + slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper), + distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper), staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper), + evidence.NewAppModule(app.evidenceKeeper), validatorvesting.NewAppModule(app.vvKeeper, app.accountKeeper), - auction.NewAppModule(app.auctionKeeper, app.supplyKeeper), - cdp.NewAppModule(app.cdpKeeper, app.pricefeedKeeper, app.supplyKeeper), - pricefeed.NewAppModule(app.pricefeedKeeper), - bep3.NewAppModule(app.bep3Keeper, app.supplyKeeper), + auction.NewAppModule(app.auctionKeeper, app.accountKeeper, app.supplyKeeper), + cdp.NewAppModule(app.cdpKeeper, app.accountKeeper, app.pricefeedKeeper, app.supplyKeeper), + pricefeed.NewAppModule(app.pricefeedKeeper, app.accountKeeper), + bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), ) @@ -306,7 +334,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, auth.ModuleName, // loads all accounts - should run before any module with a module account validatorvesting.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, slashing.ModuleName, - gov.ModuleName, mint.ModuleName, + gov.ModuleName, mint.ModuleName, evidence.ModuleName, pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName, bep3.ModuleName, kavadist.ModuleName, // TODO is this order ok? supply.ModuleName, // calculates the total supply from account - should run after modules that modify accounts in genesis crisis.ModuleName, // runs the invariants at genesis - should run after other modules @@ -325,15 +353,15 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, validatorvesting.NewAppModule(app.vvKeeper, app.accountKeeper), bank.NewAppModule(app.bankKeeper, app.accountKeeper), supply.NewAppModule(app.supplyKeeper, app.accountKeeper), - gov.NewAppModule(app.govKeeper, app.supplyKeeper), + gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper), mint.NewAppModule(app.mintKeeper), - distr.NewAppModule(app.distrKeeper, app.supplyKeeper), + distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper), staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper), - slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), - pricefeed.NewAppModule(app.pricefeedKeeper), - cdp.NewAppModule(app.cdpKeeper, app.pricefeedKeeper, app.supplyKeeper), - auction.NewAppModule(app.auctionKeeper, app.supplyKeeper), - bep3.NewAppModule(app.bep3Keeper, app.supplyKeeper), + slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper), + pricefeed.NewAppModule(app.pricefeedKeeper, app.accountKeeper), + cdp.NewAppModule(app.cdpKeeper, app.accountKeeper, app.pricefeedKeeper, app.supplyKeeper), + auction.NewAppModule(app.auctionKeeper, app.accountKeeper, app.supplyKeeper), + bep3.NewAppModule(app.bep3Keeper, app.accountKeeper, app.supplyKeeper), kavadist.NewAppModule(app.kavadistKeeper, app.supplyKeeper), ) @@ -353,7 +381,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, if loadLatest { err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) if err != nil { - cmn.Exit(err.Error()) + tmos.Exit(err.Error()) } } @@ -422,6 +450,11 @@ func (app *App) Codec() *codec.Codec { return app.cdc } +// SimulationManager implements the SimulationApp interface +func (app *App) SimulationManager() *module.SimulationManager { + return app.sm +} + // GetMaccPerms returns a mapping of the application's module account permissions. func GetMaccPerms() map[string][]string { perms := make(map[string][]string) diff --git a/app/export.go b/app/export.go index 0b6c150c..e4a5725a 100644 --- a/app/export.go +++ b/app/export.go @@ -13,10 +13,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" ) -// export the state of the app for a genesis file -func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string) ( - appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { - +// ExportAppStateAndValidators export the state of the app for a genesis file +func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string, +) (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()}) @@ -25,7 +24,6 @@ func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList [] } genState := app.mm.ExportGenesis(ctx) - appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err @@ -35,6 +33,8 @@ func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList [] } // prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) { applyWhiteList := false @@ -60,14 +60,24 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string // withdraw all validator commission app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) { - _, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + accumCommission := app.distrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) + if accumCommission.IsZero() { + return false + } + _, err := app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + if err != nil { + log.Fatal(err) + } return false }) // withdraw all delegator rewards dels := app.stakingKeeper.GetAllDelegations(ctx) for _, delegation := range dels { - _, _ = app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + _, err := app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + if err != nil { + log.Fatal(err) + } } // clear validator slash events @@ -86,7 +96,7 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string // donate any unwithdrawn outstanding reward fraction tokens to the community pool scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator()) feePool := app.distrKeeper.GetFeePool(ctx) - feePool.CommunityPool = feePool.CommunityPool.Add(scraps) + feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) app.distrKeeper.SetFeePool(ctx, feePool) app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) @@ -128,7 +138,6 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey) counter := int16(0) - var valConsAddrs []sdk.ConsAddress for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(iter.Key()[1:]) validator, found := app.stakingKeeper.GetValidator(ctx, addr) @@ -137,7 +146,6 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string } validator.UnbondingHeight = 0 - valConsAddrs = append(valConsAddrs, validator.ConsAddress()) if applyWhiteList && !whiteListMap[addr.String()] { validator.Jailed = true } diff --git a/app/params/doc.go b/app/params/doc.go new file mode 100644 index 00000000..c8efce8f --- /dev/null +++ b/app/params/doc.go @@ -0,0 +1,19 @@ +/* +Package params defines the simulation parameters for the Kava app. + +It contains the default weights used for each transaction used on the module's +simulation. These weights define the chance for a transaction to be simulated at +any gived operation. + +You can repace the default values for the weights by providing a params.json +file with the weights defined for each of the transaction operations: + + { + "op_weight_msg_send": 60, + "op_weight_msg_delegate": 100, + } + +In the example above, the `MsgSend` has 60% chance to be simulated, while the +`MsgDelegate` will always be simulated. +*/ +package params diff --git a/app/params/params.go b/app/params/params.go new file mode 100644 index 00000000..d74f7623 --- /dev/null +++ b/app/params/params.go @@ -0,0 +1,15 @@ +package params + +// Simulation parameter constants +const ( + StakePerAccount = "stake_per_account" + InitiallyBondedValidators = "initially_bonded_validators" +) + +// Default simulation operation weights for messages and gov proposals +const ( + DefaultWeightMsgPlaceBid int = 100 + DefaultWeightMsgCreateAtomicSwap int = 100 + DefaultWeightMsgUpdatePrices int = 100 + DefaultWeightMsgCdp int = 100 +) diff --git a/app/sim_test.go b/app/sim_test.go index c4fb6bcd..d7a48802 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -3,69 +3,35 @@ package app import ( "encoding/json" "fmt" - "io/ioutil" - "math/rand" "os" "testing" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/helpers" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - authsimops "github.com/cosmos/cosmos-sdk/x/auth/simulation/operations" - banksimops "github.com/cosmos/cosmos-sdk/x/bank/simulation/operations" distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrsimops "github.com/cosmos/cosmos-sdk/x/distribution/simulation/operations" "github.com/cosmos/cosmos-sdk/x/gov" - govsimops "github.com/cosmos/cosmos-sdk/x/gov/simulation/operations" "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/params" - paramsimops "github.com/cosmos/cosmos-sdk/x/params/simulation/operations" "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/slashing" - slashingsimops "github.com/cosmos/cosmos-sdk/x/slashing/simulation/operations" "github.com/cosmos/cosmos-sdk/x/staking" - stakingsimops "github.com/cosmos/cosmos-sdk/x/staking/simulation/operations" "github.com/cosmos/cosmos-sdk/x/supply" - - auctionsimops "github.com/kava-labs/kava/x/auction/simulation/operations" - bep3simops "github.com/kava-labs/kava/x/bep3/simulation/operations" - cdpsimops "github.com/kava-labs/kava/x/cdp/simulation/operations" - pricefeedsimops "github.com/kava-labs/kava/x/pricefeed/simulation/operations" ) -// Simulation parameter constants -const ( - StakePerAccount = "stake_per_account" - InitiallyBondedValidators = "initially_bonded_validators" - OpWeightDeductFee = "op_weight_deduct_fee" - OpWeightMsgSend = "op_weight_msg_send" - OpWeightSingleInputMsgMultiSend = "op_weight_single_input_msg_multisend" - OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address" - OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward" - OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission" - OpWeightSubmitVotingSlashingTextProposal = "op_weight_submit_voting_slashing_text_proposal" - OpWeightSubmitVotingSlashingCommunitySpendProposal = "op_weight_submit_voting_slashing_community_spend_proposal" - OpWeightSubmitVotingSlashingParamChangeProposal = "op_weight_submit_voting_slashing_param_change_proposal" - OpWeightMsgDeposit = "op_weight_msg_deposit" - OpWeightMsgCreateValidator = "op_weight_msg_create_validator" - OpWeightMsgEditValidator = "op_weight_msg_edit_validator" - OpWeightMsgDelegate = "op_weight_msg_delegate" - OpWeightMsgUndelegate = "op_weight_msg_undelegate" - OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate" - OpWeightMsgUnjail = "op_weight_msg_unjail" - OpWeightMsgPlaceBid = "op_weight_msg_place_bid" - OpWeightMsgPricefeed = "op_weight_msg_pricefeed" - OpWeightMsgCreateAtomicSwap = "op_weight_msg_create_atomic_Swap" - OpWeightMsgCdp = "op_weight_msg_cdp" -) +type StoreKeysPrefixes struct { + A sdk.StoreKey + B sdk.StoreKey + Prefixes [][]byte +} // TestMain runs setup and teardown code before all tests. func TestMain(m *testing.M) { @@ -75,251 +41,11 @@ func TestMain(m *testing.M) { config.Seal() // load the values from simulation specific flags simapp.GetSimulatorFlags() - // run tests exitCode := m.Run() os.Exit(exitCode) } -func testAndRunTxs(app *App, config simulation.Config) []simulation.WeightedOperation { - ap := make(simulation.AppParams) - - paramChanges := app.sm.GenerateParamChanges(config.Seed) - - if config.ParamsFile != "" { - bz, err := ioutil.ReadFile(config.ParamsFile) - if err != nil { - panic(err) - } - - app.cdc.MustUnmarshalJSON(bz, &ap) - } - - // nolint: govet - return []simulation.WeightedOperation{ - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightDeductFee, &v, nil, - func(_ *rand.Rand) { - v = 5 - }) - return v - }(nil), - authsimops.SimulateDeductFee(app.accountKeeper, app.supplyKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgSend, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - banksimops.SimulateMsgSend(app.accountKeeper, app.bankKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightSingleInputMsgMultiSend, &v, nil, - func(_ *rand.Rand) { - v = 10 - }) - return v - }(nil), - banksimops.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgSetWithdrawAddress, &v, nil, - func(_ *rand.Rand) { - v = 50 - }) - return v - }(nil), - distrsimops.SimulateMsgSetWithdrawAddress(app.distrKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgWithdrawDelegationReward, &v, nil, - func(_ *rand.Rand) { - v = 50 - }) - return v - }(nil), - distrsimops.SimulateMsgWithdrawDelegatorReward(app.distrKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgWithdrawValidatorCommission, &v, nil, - func(_ *rand.Rand) { - v = 50 - }) - return v - }(nil), - distrsimops.SimulateMsgWithdrawValidatorCommission(app.distrKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingTextProposal, &v, nil, - func(_ *rand.Rand) { - v = 5 - }) - return v - }(nil), - govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, govsimops.SimulateTextProposalContent), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingCommunitySpendProposal, &v, nil, - func(_ *rand.Rand) { - v = 5 - }) - return v - }(nil), - govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, distrsimops.SimulateCommunityPoolSpendProposalContent(app.distrKeeper)), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightSubmitVotingSlashingParamChangeProposal, &v, nil, - func(_ *rand.Rand) { - v = 5 - }) - return v - }(nil), - govsimops.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, paramsimops.SimulateParamChangeProposalContent(paramChanges)), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgDeposit, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - govsimops.SimulateMsgDeposit(app.govKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgCreateValidator, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - stakingsimops.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgEditValidator, &v, nil, - func(_ *rand.Rand) { - v = 5 - }) - return v - }(nil), - stakingsimops.SimulateMsgEditValidator(app.stakingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgDelegate, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - stakingsimops.SimulateMsgDelegate(app.accountKeeper, app.stakingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgUndelegate, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - stakingsimops.SimulateMsgUndelegate(app.accountKeeper, app.stakingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgBeginRedelegate, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - stakingsimops.SimulateMsgBeginRedelegate(app.accountKeeper, app.stakingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgUnjail, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - slashingsimops.SimulateMsgUnjail(app.slashingKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgPlaceBid, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - auctionsimops.SimulateMsgPlaceBid(app.accountKeeper, app.auctionKeeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgCreateAtomicSwap, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - bep3simops.SimulateMsgCreateAtomicSwap(app.accountKeeper, app.bep3Keeper), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgPricefeed, &v, nil, - func(_ *rand.Rand) { - v = 100 - }) - return v - }(nil), - pricefeedsimops.SimulateMsgUpdatePrices(app.pricefeedKeeper, config.NumBlocks), - }, - { - func(_ *rand.Rand) int { - var v int - ap.GetOrGenerate(app.cdc, OpWeightMsgCdp, &v, nil, - func(_ *rand.Rand) { - v = 100 // TODO - }) - return v - }(nil), - cdpsimops.SimulateMsgCdp(app.accountKeeper, app.cdpKeeper, app.pricefeedKeeper), - }, - } -} - // fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of // an IAVLStore for faster simulation speed. func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { @@ -332,197 +58,96 @@ func interBlockCacheOpt() func(*baseapp.BaseApp) { return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) } -// Profile with: -// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/GaiaApp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out -func BenchmarkFullAppSimulation(b *testing.B) { - logger := log.NewNopLogger() - config := simapp.NewConfigFromFlags() - - var db dbm.DB - dir, _ := ioutil.TempDir("", "goleveldb-app-sim") - db, _ = sdk.NewLevelDB("Simulation", dir) - defer func() { - db.Close() - _ = os.RemoveAll(dir) - }() - - app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt()) - - // Run randomized simulation - // TODO: parameterize numbers, save for a later PR - _, simParams, simErr := simulation.SimulateFromSeed( - b, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, - ) - - // export state and params before the simulation error is checked - if config.ExportStatePath != "" { - if err := ExportStateToJSON(app, config.ExportStatePath); err != nil { - fmt.Println(err) - b.Fail() - } - } - - if config.ExportParamsPath != "" { - if err := simapp.ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil { - fmt.Println(err) - b.Fail() - } - } - - if simErr != nil { - fmt.Println(simErr) - b.FailNow() - } - - if config.Commit { - fmt.Println("\nGoLevelDB Stats") - fmt.Println(db.Stats()["leveldb.stats"]) - fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) - } -} - -// TestFullAppSimulation runs a standard simulation of the app, modified by cmd line flag values. func TestFullAppSimulation(t *testing.T) { - if !simapp.FlagEnabledValue { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { t.Skip("skipping application simulation") } - - var logger log.Logger - config := simapp.NewConfigFromFlags() - - if simapp.FlagVerboseValue { - logger = log.TestingLogger() - } else { - logger = log.NewNopLogger() - } - - var db dbm.DB - dir, _ := ioutil.TempDir("", "goleveldb-app-sim") - db, _ = sdk.NewLevelDB("Simulation", dir) + require.NoError(t, err, "simulation setup failed") defer func() { db.Close() - _ = os.RemoveAll(dir) + require.NoError(t, os.RemoveAll(dir)) }() app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) - require.Equal(t, "kava", app.Name()) + require.Equal(t, appName, app.Name()) - // Run randomized simulation + // run randomized simulation _, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, - ) - - // export state and params before the simulation error is checked - if config.ExportStatePath != "" { - err := ExportStateToJSON(app, config.ExportStatePath) - require.NoError(t, err) - } - - if config.ExportParamsPath != "" { - err := simapp.ExportParamsToJSON(simParams, config.ExportParamsPath) - require.NoError(t, err) - } - - require.NoError(t, simErr) - - if config.Commit { - // for memdb: - // fmt.Println("Database Size", db.Stats()["database.size"]) - fmt.Println("\nGoLevelDB Stats") - fmt.Println(db.Stats()["leveldb.stats"]) - fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) - } -} - -// TestAppImportExport runs a simulation, exports the state, imports it, then checks the db state is same after import as it was before export. -func TestAppImportExport(t *testing.T) { - if !simapp.FlagEnabledValue { - t.Skip("skipping application import/export simulation") - } - - var logger log.Logger - config := simapp.NewConfigFromFlags() - - if simapp.FlagVerboseValue { - logger = log.TestingLogger() - } else { - logger = log.NewNopLogger() - } - - var db dbm.DB - dir, _ := ioutil.TempDir("", "goleveldb-app-sim") - db, _ = sdk.NewLevelDB("Simulation", dir) - - defer func() { - db.Close() - _ = os.RemoveAll(dir) - }() - - app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) - require.Equal(t, "kava", app.Name()) - - // Run randomized simulation - _, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, ) // export state and simParams before the simulation error is checked - if config.ExportStatePath != "" { - err := ExportStateToJSON(app, config.ExportStatePath) - require.NoError(t, err) - } - - if config.ExportParamsPath != "" { - err := simapp.ExportParamsToJSON(simParams, config.ExportParamsPath) - require.NoError(t, err) - } - + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) require.NoError(t, simErr) if config.Commit { - // for memdb: - // fmt.Println("Database Size", db.Stats()["database.size"]) - fmt.Println("\nGoLevelDB Stats") - fmt.Println(db.Stats()["leveldb.stats"]) - fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + simapp.PrintStats(db) + } +} + +func TestAppImportExport(t *testing.T) { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { + t.Skip("skipping application import/export simulation") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, app.Name()) + + // Run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, + ) + + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simapp.PrintStats(db) } fmt.Printf("exporting genesis...\n") appState, _, err := app.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err) + fmt.Printf("importing genesis...\n") - newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") - newDB, _ := sdk.NewLevelDB("Simulation-2", dir) + _, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2") + require.NoError(t, err, "simulation setup failed") defer func() { newDB.Close() - _ = os.RemoveAll(newDir) + require.NoError(t, os.RemoveAll(newDir)) }() newApp := NewApp(log.NewNopLogger(), newDB, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) - require.Equal(t, "kava", newApp.Name()) + require.Equal(t, appName, newApp.Name()) - var genesisState simapp.GenesisState - err = app.cdc.UnmarshalJSON(appState, &genesisState) + var genesisState GenesisState + err = app.Codec().UnmarshalJSON(appState, &genesisState) require.NoError(t, err) + ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) ctxB := newApp.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) newApp.mm.InitGenesis(ctxB, genesisState) fmt.Printf("comparing stores...\n") - ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) - - type StoreKeysPrefixes struct { - A sdk.StoreKey - B sdk.StoreKey - Prefixes [][]byte - } storeKeysPrefixes := []StoreKeysPrefixes{ {app.keys[baseapp.MainStoreKey], newApp.keys[baseapp.MainStoreKey], [][]byte{}}, @@ -539,118 +164,84 @@ func TestAppImportExport(t *testing.T) { {app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}}, } - for _, storeKeysPrefix := range storeKeysPrefixes { - storeKeyA := storeKeysPrefix.A - storeKeyB := storeKeysPrefix.B - prefixes := storeKeysPrefix.Prefixes + for _, skp := range storeKeysPrefixes { + storeA := ctxA.KVStore(skp.A) + storeB := ctxB.KVStore(skp.B) - storeA := ctxA.KVStore(storeKeyA) - storeB := ctxB.KVStore(storeKeyB) - - failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, prefixes) + failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") - fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), storeKeyA, storeKeyB) - require.Len(t, failedKVAs, 0, simapp.GetSimulationLog(storeKeyA.Name(), app.sm.StoreDecoders, app.cdc, failedKVAs, failedKVBs)) + fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) + require.Equal(t, len(failedKVAs), 0, simapp.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, app.Codec(), failedKVAs, failedKVBs)) } } -// TestAppSimulationAfterImport runs a simulation, exports it, imports it and runs another simulation. func TestAppSimulationAfterImport(t *testing.T) { - if !simapp.FlagEnabledValue { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { t.Skip("skipping application simulation after import") } - - var logger log.Logger - config := simapp.NewConfigFromFlags() - - if simapp.FlagVerboseValue { - logger = log.TestingLogger() - } else { - logger = log.NewNopLogger() - } - - dir, _ := ioutil.TempDir("", "goleveldb-app-sim") - db, _ := sdk.NewLevelDB("Simulation", dir) + require.NoError(t, err, "simulation setup failed") defer func() { db.Close() - _ = os.RemoveAll(dir) + require.NoError(t, os.RemoveAll(dir)) }() app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) - require.Equal(t, "kava", app.Name()) + require.Equal(t, appName, app.Name()) - // Run randomized simulation // Run randomized simulation stopEarly, simParams, simErr := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, ) - // export state and params before the simulation error is checked - if config.ExportStatePath != "" { - err := ExportStateToJSON(app, config.ExportStatePath) - require.NoError(t, err) - } - - if config.ExportParamsPath != "" { - err := simapp.ExportParamsToJSON(simParams, config.ExportParamsPath) - require.NoError(t, err) - } - + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) require.NoError(t, simErr) if config.Commit { - // for memdb: - // fmt.Println("Database Size", db.Stats()["database.size"]) - fmt.Println("\nGoLevelDB Stats") - fmt.Println(db.Stats()["leveldb.stats"]) - fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"]) + simapp.PrintStats(db) } 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") + fmt.Println("can't export or import a zero-validator genesis, exiting test...") return } - fmt.Printf("Exporting genesis...\n") + fmt.Printf("exporting genesis...\n") appState, _, err := app.ExportAppStateAndValidators(true, []string{}) - if err != nil { - panic(err) - } + require.NoError(t, err) - fmt.Printf("Importing genesis...\n") + fmt.Printf("importing genesis...\n") - newDir, _ := ioutil.TempDir("", "goleveldb-app-sim-2") - newDB, _ := sdk.NewLevelDB("Simulation-2", dir) + _, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2") + require.NoError(t, err, "simulation setup failed") defer func() { newDB.Close() - _ = os.RemoveAll(newDir) + require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewApp(log.NewNopLogger(), newDB, nil, true, 0, fauxMerkleModeOpt) - require.Equal(t, "kava", newApp.Name()) + newApp := NewApp(log.NewNopLogger(), newDB, nil, true, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, newApp.Name()) newApp.InitChain(abci.RequestInitChain{ AppStateBytes: appState, }) - // Run randomized simulation on imported app _, _, err = simulation.SimulateFromSeed( - t, os.Stdout, newApp.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(newApp, config), newApp.ModuleAccountAddrs(), config, + t, os.Stdout, newApp.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(newApp, newApp.Codec(), config), + newApp.ModuleAccountAddrs(), config, ) - require.NoError(t, err) } -// TODO: Make another test for the fuzzer itself, which just has noOp txs -// and doesn't depend on the application. -// TestAppStateDeterminism runs several sims with the same seed and checks the states are equal. func TestAppStateDeterminism(t *testing.T) { if !simapp.FlagEnabledValue { t.Skip("skipping application simulation") @@ -661,6 +252,7 @@ func TestAppStateDeterminism(t *testing.T) { config.ExportParamsPath = "" config.OnOperation = false config.AllInvariants = false + config.ChainID = helpers.SimAppChainID numTimesToRunPerSeed := 2 appHashList := make([]json.RawMessage, numTimesToRunPerSeed) @@ -676,8 +268,9 @@ func TestAppStateDeterminism(t *testing.T) { ) _, _, err := simulation.SimulateFromSeed( - t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, ) require.NoError(t, err) @@ -692,63 +285,3 @@ func TestAppStateDeterminism(t *testing.T) { } } } - -func BenchmarkInvariants(b *testing.B) { - logger := log.NewNopLogger() - - config := simapp.NewConfigFromFlags() - config.AllInvariants = false - - dir, _ := ioutil.TempDir("", "goleveldb-app-invariant-bench") - db, _ := sdk.NewLevelDB("simulation", dir) - - defer func() { - db.Close() - os.RemoveAll(dir) - }() - - app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt()) - - // 2. Run parameterized simulation (w/o invariants) - _, simParams, simErr := simulation.SimulateFromSeed( - b, ioutil.Discard, app.BaseApp, simapp.AppStateFn(app.Codec(), app.sm), - testAndRunTxs(app, config), app.ModuleAccountAddrs(), config, - ) - - // export state and params before the simulation error is checked - if config.ExportStatePath != "" { - if err := ExportStateToJSON(app, config.ExportStatePath); err != nil { - fmt.Println(err) - b.Fail() - } - } - - if config.ExportParamsPath != "" { - if err := simapp.ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil { - fmt.Println(err) - b.Fail() - } - } - - if simErr != nil { - fmt.Println(simErr) - b.FailNow() - } - - ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1}) - - // 3. Benchmark each invariant separately - // - // NOTE: We use the crisis keeper as it has all the invariants registered with - // their respective metadata which makes it useful for testing/benchmarking. - for _, cr := range app.crisisKeeper.Routes() { - b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) { - for n := 0; n < b.N; n++ { - if res, stop := cr.Invar(ctx); stop { - fmt.Printf("broken invariant at block %d of %d\n%s", ctx.BlockHeight()-1, config.NumBlocks, res) - b.FailNow() - } - } - }) - } -} diff --git a/app/test_common.go b/app/test_common.go index be6032dd..42bc00a5 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -50,6 +50,10 @@ type TestApp struct { } func NewTestApp() TestApp { + config := sdk.GetConfig() + SetBech32AddressPrefixes(config) + SetBip44CoinType(config) + db := tmdb.NewMemDB() app := NewApp(log.NewNopLogger(), db, nil, true, 0) return TestApp{App: *app} diff --git a/cli_test/test_helpers.go b/cli_test/test_helpers.go index 05470c99..1d82792f 100644 --- a/cli_test/test_helpers.go +++ b/cli_test/test_helpers.go @@ -14,7 +14,6 @@ import ( tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/client" clientkeys "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -208,7 +207,7 @@ func (f *Fixtures) UnsafeResetAll(flags ...string) { // NOTE: GDInit sets the ChainID for the Fixtures instance func (f *Fixtures) GDInit(moniker string, flags ...string) { cmd := fmt.Sprintf("%s init -o --home=%s %s", f.GaiadBinary, f.GaiadHome, moniker) - _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) var chainID string var initRes map[string]json.RawMessage @@ -231,13 +230,13 @@ func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, fl // GenTx is gaiad gentx func (f *Fixtures) GenTx(name string, flags ...string) { cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s", f.GaiadBinary, name, f.GaiadHome, f.GaiacliHome) - executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // CollectGenTxs is gaiad collect-gentxs func (f *Fixtures) CollectGenTxs(flags ...string) { cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.GaiadBinary, f.GaiadHome) - executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // GDStart runs gaiad start with the appropriate flags and returns a process @@ -276,19 +275,19 @@ func (f *Fixtures) KeysDelete(name string, flags ...string) { // KeysAdd is gaiacli keys add func (f *Fixtures) KeysAdd(name string, flags ...string) { cmd := fmt.Sprintf("%s keys add --home=%s %s", f.GaiacliBinary, f.GaiacliHome, name) - executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // KeysAddRecover prepares gaiacli keys add --recover func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) { cmd := fmt.Sprintf("%s keys add --home=%s --recover %s", f.GaiacliBinary, f.GaiacliHome, name) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass, mnemonic) } // KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { cmd := fmt.Sprintf("%s keys add --home=%s --recover %s --account %d --index %d", f.GaiacliBinary, f.GaiacliHome, name, account, index) - executeWriteCheckErr(f.T, addFlags(cmd, flags), client.DefaultKeyPass, mnemonic) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass, mnemonic) } // KeysShow is gaiacli keys show @@ -324,25 +323,25 @@ func (f *Fixtures) CLIConfig(key, value string, flags ...string) { // TxSend is gaiacli tx send func (f *Fixtures) TxSend(from string, to sdk.AccAddress, amount sdk.Coin, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx send %s %s %s %v", f.GaiacliBinary, from, to, amount, f.Flags()) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxSign is gaiacli tx sign func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx sign %v --from=%s %v", f.GaiacliBinary, f.Flags(), signer, fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxBroadcast is gaiacli tx broadcast func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx broadcast %v %v", f.GaiacliBinary, f.Flags(), fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxEncode is gaiacli tx encode func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx encode %v %v", f.GaiacliBinary, f.Flags(), fileName) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxMultisign is gaiacli tx multisign @@ -364,13 +363,13 @@ func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk. cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05") cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10") cmd += fmt.Sprintf(" --min-self-delegation=%v", "1") - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxStakingUnbond is gaiacli tx staking unbond func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool { cmd := fmt.Sprintf("%s tx staking unbond %s %v --from=%s %v", f.GaiacliBinary, validator, shares, from, f.Flags()) - return executeWrite(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWrite(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } //___________________________________________________________________________________ @@ -380,19 +379,19 @@ func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx gov submit-proposal %v --from=%s --type=%s", f.GaiacliBinary, f.Flags(), from, typ) cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxGovDeposit is gaiacli tx gov deposit func (f *Fixtures) TxGovDeposit(proposalID int, from string, amount sdk.Coin, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx gov deposit %d %s --from=%s %v", f.GaiacliBinary, proposalID, amount, from, f.Flags()) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxGovVote is gaiacli tx gov vote func (f *Fixtures) TxGovVote(proposalID int, option gov.VoteOption, from string, flags ...string) (bool, string, string) { cmd := fmt.Sprintf("%s tx gov vote %d %s --from=%s %v", f.GaiacliBinary, proposalID, option, from, f.Flags()) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxGovSubmitParamChangeProposal executes a CLI parameter change proposal @@ -406,7 +405,7 @@ func (f *Fixtures) TxGovSubmitParamChangeProposal( f.GaiacliBinary, proposalPath, from, f.Flags(), ) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } // TxGovSubmitCommunityPoolSpendProposal executes a CLI community pool spend proposal @@ -420,7 +419,7 @@ func (f *Fixtures) TxGovSubmitCommunityPoolSpendProposal( f.GaiacliBinary, proposalPath, from, f.Flags(), ) - return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), client.DefaultKeyPass) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) } //___________________________________________________________________________________ diff --git a/cmd/kvcli/main.go b/cmd/kvcli/main.go index 9873421f..8700f0ac 100644 --- a/cmd/kvcli/main.go +++ b/cmd/kvcli/main.go @@ -12,6 +12,7 @@ import ( "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" sdk "github.com/cosmos/cosmos-sdk/types" @@ -48,7 +49,7 @@ func main() { } // Add --chain-id to persistent flags and mark it required - rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node") + rootCmd.PersistentFlags().String(flags.FlagChainID, "", "Chain ID of tendermint node") rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { return initConfig(rootCmd) } @@ -59,13 +60,13 @@ func main() { client.ConfigCmd(app.DefaultCLIHome), queryCmd(cdc), txCmd(cdc), - client.LineBreak, + flags.LineBreak, lcd.ServeCommand(cdc, registerRoutes), - client.LineBreak, + flags.LineBreak, getModifiedKeysCmd(), - client.LineBreak, + flags.LineBreak, version.Cmd, - client.NewCompletionCmd(rootCmd, true), + flags.NewCompletionCmd(rootCmd, true), ) // Add flags and prefix all env exposed with KA @@ -87,12 +88,12 @@ func queryCmd(cdc *amino.Codec) *cobra.Command { queryCmd.AddCommand( authcmd.GetAccountCmd(cdc), - client.LineBreak, + flags.LineBreak, rpc.ValidatorCommand(cdc), rpc.BlockCommand(), authcmd.QueryTxsByEventsCmd(cdc), authcmd.QueryTxCmd(cdc), - client.LineBreak, + flags.LineBreak, ) // add modules' query commands @@ -109,14 +110,14 @@ func txCmd(cdc *amino.Codec) *cobra.Command { txCmd.AddCommand( bankcmd.SendTxCmd(cdc), - client.LineBreak, + flags.LineBreak, authcmd.GetSignCommand(cdc), authcmd.GetMultiSignCommand(cdc), - client.LineBreak, + flags.LineBreak, authcmd.GetBroadcastCommand(cdc), authcmd.GetEncodeCommand(cdc), authcmd.GetDecodeCommand(cdc), - client.LineBreak, + flags.LineBreak, ) // add modules' tx commands @@ -156,7 +157,7 @@ func initConfig(cmd *cobra.Command) error { return err } } - if err := viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)); err != nil { + if err := viper.BindPFlag(flags.FlagChainID, cmd.PersistentFlags().Lookup(flags.FlagChainID)); err != nil { return err } if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { diff --git a/cmd/kvd/main.go b/cmd/kvd/main.go index bef94402..148f3ba8 100644 --- a/cmd/kvd/main.go +++ b/cmd/kvd/main.go @@ -14,7 +14,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" @@ -59,7 +59,7 @@ func main() { app.DefaultCLIHome)) rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics)) rootCmd.AddCommand(AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome)) - rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true)) + rootCmd.AddCommand(flags.NewCompletionCmd(rootCmd, true)) server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) diff --git a/go.mod b/go.mod index aa784d25..2b057cfa 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,14 @@ go 1.13 require ( github.com/btcsuite/btcd v0.20.1-beta // indirect - github.com/cosmos/cosmos-sdk v0.34.4-0.20191010193331-18de630d0ae1 - github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect + github.com/cosmos/cosmos-sdk v0.38.3 github.com/gorilla/mux v1.7.3 - github.com/spf13/cobra v0.0.5 - github.com/spf13/viper v1.4.0 - github.com/stretchr/testify v1.4.0 - github.com/tendermint/go-amino v0.15.0 - github.com/tendermint/tendermint v0.32.7 - github.com/tendermint/tm-db v0.2.0 + github.com/spf13/cobra v0.0.6 + github.com/spf13/viper v1.6.2 + github.com/stretchr/testify v1.5.1 + github.com/tendermint/go-amino v0.15.1 + github.com/tendermint/tendermint v0.33.3 + github.com/tendermint/tm-db v0.5.0 golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 // indirect - gopkg.in/yaml.v2 v2.2.4 + gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 4a1f0603..27038216 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,40 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/99designs/keyring v1.1.2 h1:JJauROcU6x6Nh9uZb+8JgXFvyo0GUESLo1ixhpA0Kmw= -github.com/99designs/keyring v1.1.2/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2BylwQY= +github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+UVU+Hfcihr1timk8YNXHxzZWgCo7ofnrZRApw= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= @@ -30,24 +49,36 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/cosmos-sdk v0.34.4-0.20191010193331-18de630d0ae1 h1:yb+E8HGzFnO0YwLS6OCBIAVWtN8KfCYoKeO9mgAmQN0= -github.com/cosmos/cosmos-sdk v0.34.4-0.20191010193331-18de630d0ae1/go.mod h1:IGBhkbOK1ebLqMWjtgo99zUxWHsA5IOb6N9CI8nHs0Y= -github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/cosmos-sdk v0.38.3 h1:qIBTiw+2T9POaSUJ5rvbBbXeq8C8btBlJxnSegPBd3Y= +github.com/cosmos/cosmos-sdk v0.38.3/go.mod h1:rzWOofbKfRt3wxiylmYWEFHnxxGj0coyqgWl2I9obAw= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= -github.com/cosmos/ledger-cosmos-go v0.10.3 h1:Qhi5yTR5Pg1CaTpd00pxlGwNl4sFRdtK1J96OTjeFFc= -github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= +github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -56,9 +87,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= -github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= @@ -67,69 +107,126 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 h1:tT8iWCYw4uOem71yYA3htfH+LNopJvcqZQshm56G5L4= github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f h1:8N8XWLZelZNibkhM1FuF+3Ad3YIbgirjdMiVA0eUkaM= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= @@ -138,7 +235,6 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -147,108 +243,187 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.5.0 h1:5BakdOZdtKJ1FFk6QdL8iSGrMWsXgchNJcrnarjbmJQ= -github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= +github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= -github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= -github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= +github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stumble/gorocksdb v0.0.3 h1:9UU+QA1pqFYJuf9+5p7z1IqdE5k0mma4UAeu2wmX8kA= -github.com/stumble/gorocksdb v0.0.3/go.mod h1:v6IHdFBXk5DJ1K4FZ0xi+eY737quiiBxYtSWXadLybY= -github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= -github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= -github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae h1:AOXNM7c2Vvo45SjAgeWF8Wy+NS7/NCqzRNpUc+HPAec= -github.com/tendermint/crypto v0.0.0-20190823183015-45b1026d81ae/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= +github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk= -github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= -github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8= -github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o= -github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= -github.com/tendermint/tendermint v0.32.6/go.mod h1:D2+A3pNjY+Po72X0mTfaXorFhiVI8dh/Zg640FGyGtE= -github.com/tendermint/tendermint v0.32.7 h1:Szu5Fm1L3pvn3t4uQxPAcP+7ndZEQKgLie/yokM56rU= -github.com/tendermint/tendermint v0.32.7/go.mod h1:D2+A3pNjY+Po72X0mTfaXorFhiVI8dh/Zg640FGyGtE= -github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= -github.com/tendermint/tm-db v0.2.0 h1:rJxgdqn6fIiVJZy4zLpY1qVlyD0TU6vhkT4kEf71TQQ= -github.com/tendermint/tm-db v0.2.0/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= +github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.13.2 h1:O1m08/Ciy53l9IYmf75uIRVvrNsfjEbre8u/yCu/oqk= +github.com/tendermint/iavl v0.13.2/go.mod h1:vE1u0XAGXYjHykd4BLp8p/yivrw2PF1TuoljBcsQoGA= +github.com/tendermint/tendermint v0.33.2/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= +github.com/tendermint/tendermint v0.33.3 h1:6lMqjEoCGejCzAghbvfQgmw87snGSqEhDTo/jw+W8CI= +github.com/tendermint/tendermint v0.33.3/go.mod h1:25DqB7YvV1tN3tHsjWoc2vFtlwICfrub9XO6UBO+4xk= +github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= +github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= +github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= @@ -256,79 +431,157 @@ github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWp go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA= golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/rest_test/setup/setuptest.go b/rest_test/setup/setuptest.go index dc52be9f..49267821 100644 --- a/rest_test/setup/setuptest.go +++ b/rest_test/setup/setuptest.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "bytes" "fmt" "io/ioutil" @@ -398,8 +399,8 @@ func sendMsgToBlockchain(cdc *amino.Codec, address string, keyname string, // get the account number and sequence number accountNumber, sequenceNumber := getAccountNumberAndSequenceNumber(cdc, address) - - txBldr := auth.NewTxBuilderFromCLI(). + inBuf := bufio.NewReader(os.Stdin) + txBldr := auth.NewTxBuilderFromCLI(inBuf). WithTxEncoder(authclient.GetTxEncoder(cdc)).WithChainID("testing"). WithKeybase(keybase).WithAccountNumber(accountNumber). WithSequence(sequenceNumber).WithGas(500000) diff --git a/x/auction/alias.go b/x/auction/alias.go index 3a9537ca..0ac1a7dd 100644 --- a/x/auction/alias.go +++ b/x/auction/alias.go @@ -1,8 +1,3 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/kava-labs/kava/x/auction/keeper -// ALIASGEN: github.com/kava-labs/kava/x/auction/types package auction import ( @@ -10,22 +5,13 @@ import ( "github.com/kava-labs/kava/x/auction/types" ) +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/kava-labs/kava/x/auction/keeper +// ALIASGEN: github.com/kava-labs/kava/x/auction/types + const ( - DefaultCodespace = types.DefaultCodespace - CodeInvalidInitialAuctionID = types.CodeInvalidInitialAuctionID - CodeInvalidModulePermissions = types.CodeInvalidModulePermissions - CodeUnrecognizedAuctionType = types.CodeUnrecognizedAuctionType - CodeAuctionNotFound = types.CodeAuctionNotFound - CodeAuctionHasNotExpired = types.CodeAuctionHasNotExpired - CodeAuctionHasExpired = types.CodeAuctionHasExpired - CodeInvalidBidDenom = types.CodeInvalidBidDenom - CodeInvalidLotDenom = types.CodeInvalidLotDenom - CodeBidTooSmall = types.CodeBidTooSmall - CodeBidTooLarge = types.CodeBidTooLarge - CodeLotTooSmall = types.CodeLotTooSmall - CodeLotTooLarge = types.CodeLotTooLarge - CodeCollateralAuctionIsInReversePhase = types.CodeCollateralAuctionIsInReversePhase - CodeCollateralAuctionIsInForwardPhase = types.CodeCollateralAuctionIsInForwardPhase EventTypeAuctionStart = types.EventTypeAuctionStart EventTypeAuctionBid = types.EventTypeAuctionBid EventTypeAuctionClose = types.EventTypeAuctionClose diff --git a/x/auction/client/cli/query.go b/x/auction/client/cli/query.go index 6d41ef3f..59ee6620 100644 --- a/x/auction/client/cli/query.go +++ b/x/auction/client/cli/query.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" "github.com/kava-labs/kava/x/auction/types" @@ -21,7 +21,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Querying commands for the auction module", } - auctionQueryCmd.AddCommand(client.GetCommands( + auctionQueryCmd.AddCommand(flags.GetCommands( QueryGetAuctionCmd(queryRoute, cdc), QueryGetAuctionsCmd(queryRoute, cdc), QueryParamsCmd(queryRoute, cdc), diff --git a/x/auction/client/cli/tx.go b/x/auction/client/cli/tx.go index 4f143d7a..c70c3d90 100644 --- a/x/auction/client/cli/tx.go +++ b/x/auction/client/cli/tx.go @@ -1,14 +1,15 @@ package cli import ( + "bufio" "fmt" "strconv" "strings" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -25,7 +26,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { Short: "auction transactions subcommands", } - auctionTxCmd.AddCommand(client.PostCommands( + auctionTxCmd.AddCommand(flags.PostCommands( GetCmdPlaceBid(cdc), )...) @@ -45,8 +46,9 @@ $ %s tx %s bid 34 1000usdx --from myKeyName `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) id, err := strconv.ParseUint(args[0], 10, 64) if err != nil { diff --git a/x/auction/genesis.go b/x/auction/genesis.go index ac6544b9..10862b5d 100644 --- a/x/auction/genesis.go +++ b/x/auction/genesis.go @@ -22,7 +22,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper types.SupplyKeeper for _, a := range gs.Auctions { keeper.SetAuction(ctx, a) // find the total coins that should be present in the module account - totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()) + totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...) } // check if the module account exists diff --git a/x/auction/handler.go b/x/auction/handler.go index 2ac42e36..26056f38 100644 --- a/x/auction/handler.go +++ b/x/auction/handler.go @@ -1,32 +1,31 @@ package auction import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/auction/types" ) // NewHandler returns a function to handle all "auction" type messages. func NewHandler(keeper Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case MsgPlaceBid: return handleMsgPlaceBid(ctx, keeper, msg) default: - return sdk.ErrUnknownRequest(fmt.Sprintf("Unrecognized auction msg type: %T", msg)).Result() + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } } -func handleMsgPlaceBid(ctx sdk.Context, keeper Keeper, msg MsgPlaceBid) sdk.Result { +func handleMsgPlaceBid(ctx sdk.Context, keeper Keeper, msg MsgPlaceBid) (*sdk.Result, error) { err := keeper.PlaceBid(ctx, msg.AuctionID, msg.Bidder, msg.Amount) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -37,7 +36,7 @@ func handleMsgPlaceBid(ctx sdk.Context, keeper Keeper, msg MsgPlaceBid) sdk.Resu ), ) - return sdk.Result{ + return &sdk.Result{ Events: ctx.EventManager().Events(), - } + }, nil } diff --git a/x/auction/keeper/auctions.go b/x/auction/keeper/auctions.go index da257d35..c1722dee 100644 --- a/x/auction/keeper/auctions.go +++ b/x/auction/keeper/auctions.go @@ -5,18 +5,19 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/supply" "github.com/kava-labs/kava/x/auction/types" ) // StartSurplusAuction starts a new surplus (forward) auction. -func (k Keeper) StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin, bidDenom string) (uint64, sdk.Error) { - +func (k Keeper) StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin, bidDenom string) (uint64, error) { auction := types.NewSurplusAuction( seller, lot, bidDenom, - types.DistantFuture) + types.DistantFuture, + ) err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot)) if err != nil { @@ -41,7 +42,7 @@ func (k Keeper) StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin } // StartDebtAuction starts a new debt (reverse) auction. -func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin, debt sdk.Coin) (uint64, sdk.Error) { +func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin, debt sdk.Coin) (uint64, error) { auction := types.NewDebtAuction( buyer, @@ -53,7 +54,7 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, in // This auction type mints coins at close. Need to check module account has minting privileges to avoid potential err in endblocker. macc := k.supplyKeeper.GetModuleAccount(ctx, buyer) if !macc.HasPermission(supply.Minter) { - return 0, types.ErrInvalidModulePermissions(k.codespace, supply.Minter) + return 0, sdkerrors.Wrap(types.ErrInvalidModulePermissions, supply.Minter) } err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, buyer, types.ModuleName, sdk.NewCoins(debt)) @@ -79,8 +80,10 @@ func (k Keeper) StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, in } // StartCollateralAuction starts a new collateral (2-phase) auction. -func (k Keeper) StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin) (uint64, sdk.Error) { - +func (k Keeper) StartCollateralAuction( + ctx sdk.Context, seller string, lot, maxBid sdk.Coin, + lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin, +) (uint64, error) { weightedAddresses, err := types.NewWeightedAddresses(lotReturnAddrs, lotReturnWeights) if err != nil { return 0, err @@ -91,7 +94,8 @@ func (k Keeper) StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.C types.DistantFuture, maxBid, weightedAddresses, - debt) + debt, + ) err = k.supplyKeeper.SendCoinsFromModuleToModule(ctx, seller, types.ModuleName, sdk.NewCoins(lot)) if err != nil { @@ -120,41 +124,40 @@ func (k Keeper) StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.C } // PlaceBid places a bid on any auction. -func (k Keeper) PlaceBid(ctx sdk.Context, auctionID uint64, bidder sdk.AccAddress, newAmount sdk.Coin) sdk.Error { +func (k Keeper) PlaceBid(ctx sdk.Context, auctionID uint64, bidder sdk.AccAddress, newAmount sdk.Coin) error { auction, found := k.GetAuction(ctx, auctionID) if !found { - return types.ErrAuctionNotFound(k.codespace, auctionID) + return sdkerrors.Wrapf(types.ErrAuctionNotFound, "%d", auctionID) } // validation common to all auctions if ctx.BlockTime().After(auction.GetEndTime()) { - return types.ErrAuctionHasExpired(k.codespace, auctionID) + return sdkerrors.Wrapf(types.ErrAuctionHasExpired, "%d", auctionID) } // move coins and return updated auction - var err sdk.Error - var updatedAuction types.Auction + var ( + err error + updatedAuction types.Auction + ) switch a := auction.(type) { case types.SurplusAuction: - if updatedAuction, err = k.PlaceBidSurplus(ctx, a, bidder, newAmount); err != nil { - return err - } + updatedAuction, err = k.PlaceBidSurplus(ctx, a, bidder, newAmount) case types.DebtAuction: - if updatedAuction, err = k.PlaceBidDebt(ctx, a, bidder, newAmount); err != nil { - return err - } + updatedAuction, err = k.PlaceBidDebt(ctx, a, bidder, newAmount) case types.CollateralAuction: if !a.IsReversePhase() { updatedAuction, err = k.PlaceForwardBidCollateral(ctx, a, bidder, newAmount) } else { updatedAuction, err = k.PlaceReverseBidCollateral(ctx, a, bidder, newAmount) } - if err != nil { - return err - } default: - return types.ErrUnrecognizedAuctionType(k.codespace) + err = sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auction.GetType()) + } + + if err != nil { + return err } k.SetAuction(ctx, updatedAuction) @@ -163,10 +166,10 @@ func (k Keeper) PlaceBid(ctx sdk.Context, auctionID uint64, bidder sdk.AccAddres } // PlaceBidSurplus places a forward bid on a surplus auction, moving coins and returning the updated auction. -func (k Keeper) PlaceBidSurplus(ctx sdk.Context, a types.SurplusAuction, bidder sdk.AccAddress, bid sdk.Coin) (types.SurplusAuction, sdk.Error) { +func (k Keeper) PlaceBidSurplus(ctx sdk.Context, a types.SurplusAuction, bidder sdk.AccAddress, bid sdk.Coin) (types.SurplusAuction, error) { // Validate new bid if bid.Denom != a.Bid.Denom { - return a, types.ErrInvalidBidDenom(k.codespace, bid.Denom, a.Bid.Denom) + return a, sdkerrors.Wrapf(types.ErrInvalidBidDenom, "%s ≠ %s)", bid.Denom, a.Bid.Denom) } minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost sdk.MaxInt( @@ -175,7 +178,7 @@ func (k Keeper) PlaceBidSurplus(ctx sdk.Context, a types.SurplusAuction, bidder ), ) if bid.Amount.LT(minNewBidAmt) { - return a, types.ErrBidTooSmall(k.codespace, bid, sdk.Coin{Denom: a.Bid.Denom, Amount: minNewBidAmt}) // not using NewCoin as it can panic + return a, sdkerrors.Wrapf(types.ErrBidTooSmall, "%s ≤ %s%s", bid, minNewBidAmt, a.Bid.Denom) } // New bidder pays back old bidder @@ -223,13 +226,13 @@ func (k Keeper) PlaceBidSurplus(ctx sdk.Context, a types.SurplusAuction, bidder } // PlaceForwardBidCollateral places a forward bid on a collateral auction, moving coins and returning the updated auction. -func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuction, bidder sdk.AccAddress, bid sdk.Coin) (types.CollateralAuction, sdk.Error) { +func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuction, bidder sdk.AccAddress, bid sdk.Coin) (types.CollateralAuction, error) { // Validate new bid if bid.Denom != a.Bid.Denom { - return a, types.ErrInvalidBidDenom(k.codespace, bid.Denom, a.Bid.Denom) + return a, sdkerrors.Wrapf(types.ErrInvalidBidDenom, "%s ≠ %s", bid.Denom, a.Bid.Denom) } if a.IsReversePhase() { - return a, types.ErrCollateralAuctionIsInReversePhase(k.codespace, a.ID) + return a, sdkerrors.Wrapf(types.ErrCollateralAuctionIsInReversePhase, "%d", a.ID) } minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost sdk.MaxInt( @@ -239,10 +242,10 @@ func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuc ) minNewBidAmt = sdk.MinInt(minNewBidAmt, a.MaxBid.Amount) // allow new bids to hit MaxBid even though it may be less than the increment % if bid.Amount.LT(minNewBidAmt) { - return a, types.ErrBidTooSmall(k.codespace, bid, sdk.Coin{Denom: a.Bid.Denom, Amount: minNewBidAmt}) // not using NewCoin as it can panic + return a, sdkerrors.Wrapf(types.ErrBidTooSmall, "%s ≤ %s%s", bid, minNewBidAmt, a.Bid.Denom) } if a.MaxBid.IsLT(bid) { - return a, types.ErrBidTooLarge(k.codespace, bid, a.MaxBid) + return a, sdkerrors.Wrapf(types.ErrBidTooLarge, "%s > %s", bid, a.MaxBid) } // New bidder pays back old bidder @@ -299,13 +302,13 @@ func (k Keeper) PlaceForwardBidCollateral(ctx sdk.Context, a types.CollateralAuc } // PlaceReverseBidCollateral places a reverse bid on a collateral auction, moving coins and returning the updated auction. -func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuction, bidder sdk.AccAddress, lot sdk.Coin) (types.CollateralAuction, sdk.Error) { +func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuction, bidder sdk.AccAddress, lot sdk.Coin) (types.CollateralAuction, error) { // Validate new bid if lot.Denom != a.Lot.Denom { - return a, types.ErrInvalidLotDenom(k.codespace, lot.Denom, a.Lot.Denom) + return a, sdkerrors.Wrapf(types.ErrInvalidLotDenom, lot.Denom, a.Lot.Denom) } if !a.IsReversePhase() { - return a, types.ErrCollateralAuctionIsInForwardPhase(k.codespace, a.ID) + return a, sdkerrors.Wrapf(types.ErrCollateralAuctionIsInForwardPhase, "%d", a.ID) } maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost sdk.MaxInt( @@ -314,10 +317,10 @@ func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuc ), ) if lot.Amount.GT(maxNewLotAmt) { - return a, types.ErrLotTooLarge(k.codespace, lot, sdk.Coin{Denom: a.Lot.Denom, Amount: maxNewLotAmt}) // not using NewCoin as it can panic + return a, sdkerrors.Wrapf(types.ErrLotTooLarge, "%s > %s%s", lot, maxNewLotAmt, a.Lot.Denom) } if lot.IsNegative() { - return a, types.ErrLotTooSmall(k.codespace, lot, sdk.Coin{Denom: a.Lot.Denom, Amount: sdk.ZeroInt()}) + return a, sdkerrors.Wrapf(types.ErrLotTooSmall, "%s ≤ %s%s", lot, sdk.ZeroInt(), a.Lot.Denom) } // New bidder pays back old bidder @@ -368,10 +371,10 @@ func (k Keeper) PlaceReverseBidCollateral(ctx sdk.Context, a types.CollateralAuc } // PlaceBidDebt places a reverse bid on a debt auction, moving coins and returning the updated auction. -func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.AccAddress, lot sdk.Coin) (types.DebtAuction, sdk.Error) { +func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.AccAddress, lot sdk.Coin) (types.DebtAuction, error) { // Validate new bid if lot.Denom != a.Lot.Denom { - return a, types.ErrInvalidLotDenom(k.codespace, lot.Denom, a.Lot.Denom) + return a, sdkerrors.Wrapf(types.ErrInvalidLotDenom, lot.Denom, a.Lot.Denom) } maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost sdk.MaxInt( @@ -380,10 +383,10 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.Ac ), ) if lot.Amount.GT(maxNewLotAmt) { - return a, types.ErrLotTooLarge(k.codespace, lot, sdk.Coin{Denom: a.Lot.Denom, Amount: maxNewLotAmt}) // not using NewCoin as it can panic + return a, sdkerrors.Wrapf(types.ErrLotTooLarge, "%s > %s%s", lot, maxNewLotAmt, a.Lot.Denom) } if lot.IsNegative() { - return a, types.ErrLotTooSmall(k.codespace, lot, sdk.Coin{Denom: a.Lot.Denom, Amount: sdk.ZeroInt()}) + return a, sdkerrors.Wrapf(types.ErrLotTooSmall, "%s ≤ %s%s", lot, sdk.ZeroInt(), a.Lot.Denom) } // New bidder pays back old bidder @@ -434,15 +437,15 @@ func (k Keeper) PlaceBidDebt(ctx sdk.Context, a types.DebtAuction, bidder sdk.Ac } // CloseAuction closes an auction and distributes funds to the highest bidder. -func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) sdk.Error { +func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) error { auction, found := k.GetAuction(ctx, auctionID) if !found { - return types.ErrAuctionNotFound(k.codespace, auctionID) + return sdkerrors.Wrapf(types.ErrAuctionNotFound, "%d", auctionID) } if ctx.BlockTime().Before(auction.GetEndTime()) { - return types.ErrAuctionHasNotExpired(k.codespace, ctx.BlockTime(), auction.GetEndTime()) + return sdkerrors.Wrapf(types.ErrAuctionHasNotExpired, "block time %s, auction end time %s", ctx.BlockTime().UTC(), auction.GetEndTime().UTC()) } // payout to the last bidder @@ -460,7 +463,7 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) sdk.Error { return err } default: - return types.ErrUnrecognizedAuctionType(k.codespace) + return sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auc.GetType()) } k.DeleteAuction(ctx, auctionID) @@ -475,7 +478,7 @@ func (k Keeper) CloseAuction(ctx sdk.Context, auctionID uint64) sdk.Error { } // PayoutDebtAuction pays out the proceeds for a debt auction, first minting the coins. -func (k Keeper) PayoutDebtAuction(ctx sdk.Context, a types.DebtAuction) sdk.Error { +func (k Keeper) PayoutDebtAuction(ctx sdk.Context, a types.DebtAuction) error { err := k.supplyKeeper.MintCoins(ctx, a.Initiator, sdk.NewCoins(a.Lot)) if err != nil { return err @@ -494,7 +497,7 @@ func (k Keeper) PayoutDebtAuction(ctx sdk.Context, a types.DebtAuction) sdk.Erro } // PayoutSurplusAuction pays out the proceeds for a surplus auction. -func (k Keeper) PayoutSurplusAuction(ctx sdk.Context, a types.SurplusAuction) sdk.Error { +func (k Keeper) PayoutSurplusAuction(ctx sdk.Context, a types.SurplusAuction) error { err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.Bidder, sdk.NewCoins(a.Lot)) if err != nil { return err @@ -503,7 +506,7 @@ func (k Keeper) PayoutSurplusAuction(ctx sdk.Context, a types.SurplusAuction) sd } // PayoutCollateralAuction pays out the proceeds for a collateral auction. -func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, a types.CollateralAuction) sdk.Error { +func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, a types.CollateralAuction) error { err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, a.Bidder, sdk.NewCoins(a.Lot)) if err != nil { return err @@ -518,7 +521,7 @@ func (k Keeper) PayoutCollateralAuction(ctx sdk.Context, a types.CollateralAucti } // CloseExpiredAuctions finds all auctions that are past (or at) their ending times and closes them, paying out to the highest bidder. -func (k Keeper) CloseExpiredAuctions(ctx sdk.Context) sdk.Error { +func (k Keeper) CloseExpiredAuctions(ctx sdk.Context) error { var expiredAuctions []uint64 k.IterateAuctionsByTime(ctx, ctx.BlockTime(), func(id uint64) bool { expiredAuctions = append(expiredAuctions, id) @@ -542,10 +545,10 @@ func earliestTime(t1, t2 time.Time) time.Time { } // splitCoinIntoWeightedBuckets divides up some amount of coins according to some weights. -func splitCoinIntoWeightedBuckets(coin sdk.Coin, buckets []sdk.Int) ([]sdk.Coin, sdk.Error) { +func splitCoinIntoWeightedBuckets(coin sdk.Coin, buckets []sdk.Int) ([]sdk.Coin, error) { for _, bucket := range buckets { if bucket.IsNegative() { - return nil, sdk.ErrInternal("cannot split coin into bucket with negative weight") + return nil, fmt.Errorf("cannot split %s into bucket with negative weight (%s)", coin.String(), bucket.String()) } } amounts := splitIntIntoWeightedBuckets(coin.Amount, buckets) diff --git a/x/auction/keeper/auctions_test.go b/x/auction/keeper/auctions_test.go index 3813b6df..c8c19c1b 100644 --- a/x/auction/keeper/auctions_test.go +++ b/x/auction/keeper/auctions_test.go @@ -267,30 +267,31 @@ func TestStartSurplusAuction(t *testing.T) { blockTime time.Time args args expectPass bool + expPanic bool }{ { "normal", someTime, args{cdp.LiquidatorMacc, c("stable", 10), "gov"}, - true, + true, false, }, { "no module account", someTime, args{"nonExistentModule", c("stable", 10), "gov"}, - false, + false, true, }, { "not enough coins", someTime, args{cdp.LiquidatorMacc, c("stable", 101), "gov"}, - false, + false, false, }, { "incorrect denom", someTime, args{cdp.LiquidatorMacc, c("notacoin", 10), "gov"}, - false, + false, false, }, } for _, tc := range testCases { @@ -308,7 +309,15 @@ func TestStartSurplusAuction(t *testing.T) { keeper := tApp.GetAuctionKeeper() // run function under test - id, err := keeper.StartSurplusAuction(ctx, tc.args.seller, tc.args.lot, tc.args.bidDenom) + var ( + id uint64 + err error + ) + if tc.expPanic { + require.Panics(t, func() { _, _ = keeper.StartSurplusAuction(ctx, tc.args.seller, tc.args.lot, tc.args.bidDenom) }, tc.name) + } else { + id, err = keeper.StartSurplusAuction(ctx, tc.args.seller, tc.args.lot, tc.args.bidDenom) + } // check sk := tApp.GetSupplyKeeper() @@ -316,11 +325,11 @@ func TestStartSurplusAuction(t *testing.T) { actualAuc, found := keeper.GetAuction(ctx, id) if tc.expectPass { - require.NoError(t, err) + require.NoError(t, err, tc.name) // check coins moved - require.Equal(t, initialLiquidatorCoins.Sub(cs(tc.args.lot)), liquidatorCoins) + require.Equal(t, initialLiquidatorCoins.Sub(cs(tc.args.lot)), liquidatorCoins, tc.name) // check auction in store and is correct - require.True(t, found) + require.True(t, found, tc.name) expectedAuction := types.Auction(types.SurplusAuction{BaseAuction: types.BaseAuction{ ID: id, Initiator: tc.args.seller, @@ -331,13 +340,13 @@ func TestStartSurplusAuction(t *testing.T) { EndTime: types.DistantFuture, MaxEndTime: types.DistantFuture, }}) - require.Equal(t, expectedAuction, actualAuc) - } else { - require.Error(t, err) + require.Equal(t, expectedAuction, actualAuc, tc.name) + } else if !tc.expPanic && !tc.expectPass { + require.Error(t, err, tc.name) // check coins not moved - require.Equal(t, initialLiquidatorCoins, liquidatorCoins) + require.Equal(t, initialLiquidatorCoins, liquidatorCoins, tc.name) // check auction not in store - require.False(t, found) + require.False(t, found, tc.name) } }) } diff --git a/x/auction/keeper/bidding_test.go b/x/auction/keeper/bidding_test.go index 40d2d164..9a34cabc 100644 --- a/x/auction/keeper/bidding_test.go +++ b/x/auction/keeper/bidding_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "strings" "testing" "time" @@ -55,319 +56,347 @@ func TestAuctionBidding(t *testing.T) { auctionArgs auctionArgs setupBids []bidArgs bidArgs bidArgs - expectedError sdk.CodeType + expectedError error expectedEndTime time.Time expectedBidder sdk.AccAddress expectedBid sdk.Coin - expectpass bool + expectPass bool + expectPanic bool }{ { "basic: auction doesn't exist", auctionArgs{Surplus, "", c("token1", 1), c("token2", 1), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token2", 10)}, - types.CodeAuctionNotFound, + types.ErrAuctionNotFound, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 10), false, + true, }, { "basic: closed auction", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token2", 10)}, - types.CodeAuctionHasExpired, + types.ErrAuctionHasExpired, types.DistantFuture, nil, c("token2", 0), false, + false, }, { "surplus: normal", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token2", 10)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 10), true, + false, }, { "surplus: second bidder", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, []bidArgs{{buyer, c("token2", 10)}}, bidArgs{secondBuyer, c("token2", 11)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), secondBuyer, c("token2", 11), true, + false, }, { "surplus: invalid bid denom", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 10), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("badtoken", 10)}, - types.CodeInvalidBidDenom, + types.ErrInvalidBidDenom, types.DistantFuture, nil, // surplus auctions are created with initial bidder as a nil address c("token2", 0), false, + false, }, { "surplus: invalid bid (less than)", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, []bidArgs{{buyer, c("token2", 100)}}, bidArgs{buyer, c("token2", 99)}, - types.CodeBidTooSmall, + types.ErrBidTooSmall, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 100), false, + false, }, { "surplus: invalid bid (equal)", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token2", 0)}, // min bid is technically 0 at default 5%, but it's capped at 1 - types.CodeBidTooSmall, + types.ErrBidTooSmall, types.DistantFuture, nil, // surplus auctions are created with initial bidder as a nil address c("token2", 0), false, + false, }, { "surplus: invalid bid (less than min increment)", auctionArgs{Surplus, modName, c("token1", 100), c("token2", 0), sdk.Coin{}, []sdk.AccAddress{}, []sdk.Int{}}, []bidArgs{{buyer, c("token2", 100)}}, bidArgs{buyer, c("token2", 104)}, // min bid is 105 at default 5% - types.CodeBidTooSmall, + types.ErrBidTooSmall, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 100), false, + false, }, { "debt: normal", auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot nil, bidArgs{buyer, c("token1", 10)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 100), true, + false, }, { "debt: second bidder", auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot []bidArgs{{buyer, c("token1", 10)}}, bidArgs{secondBuyer, c("token1", 9)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), secondBuyer, c("token2", 100), true, + false, }, { "debt: invalid lot denom", auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, // initial bid, lot nil, bidArgs{buyer, c("badtoken", 10)}, - types.CodeInvalidLotDenom, + types.ErrInvalidLotDenom, types.DistantFuture, supply.NewModuleAddress(modName), c("token2", 100), false, + false, }, { "debt: invalid lot size (larger)", auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token1", 21)}, - types.CodeLotTooLarge, + types.ErrLotTooLarge, types.DistantFuture, supply.NewModuleAddress(modName), c("token2", 100), false, + false, }, { "debt: invalid lot size (equal)", auctionArgs{Debt, modName, c("token1", 20), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token1", 20)}, - types.CodeLotTooLarge, + types.ErrLotTooLarge, types.DistantFuture, supply.NewModuleAddress(modName), c("token2", 100), false, + false, }, { "debt: invalid lot size (larger than min increment)", auctionArgs{Debt, modName, c("token1", 60), c("token2", 100), c("debt", 100), []sdk.AccAddress{}, []sdk.Int{}}, nil, bidArgs{buyer, c("token1", 58)}, // max lot at default 5% is 57 - types.CodeLotTooLarge, + types.ErrLotTooLarge, types.DistantFuture, supply.NewModuleAddress(modName), c("token2", 100), - false, + false, false, }, { "collateral [forward]: normal", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid nil, bidArgs{buyer, c("token2", 10)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 10), true, + false, }, { "collateral [forward]: second bidder", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 10)}}, bidArgs{secondBuyer, c("token2", 11)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), secondBuyer, c("token2", 11), true, + false, }, { "collateral [forward]: invalid bid denom", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid nil, bidArgs{buyer, c("badtoken", 10)}, - types.CodeInvalidBidDenom, + types.ErrInvalidBidDenom, types.DistantFuture, nil, c("token2", 0), false, + false, }, { "collateral [forward]: invalid bid size (smaller)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 10)}}, bidArgs{buyer, c("token2", 9)}, - types.CodeBidTooSmall, + types.ErrBidTooSmall, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 10), false, + false, }, { "collateral [forward]: invalid bid size (equal)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid nil, bidArgs{buyer, c("token2", 0)}, - types.CodeBidTooSmall, + types.ErrBidTooSmall, types.DistantFuture, nil, c("token2", 0), false, + false, }, { "collateral [forward]: invalid bid size (less than min increment)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, bidArgs{buyer, c("token2", 51)}, - types.CodeBidTooSmall, + types.ErrBidTooSmall, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), false, + false, }, { "collateral [forward]: less than min increment but equal to maxBid", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 99)}}, bidArgs{buyer, c("token2", 100)}, // min bid at default 5% is 104 - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 100), true, + false, }, { "collateral [forward]: invalid bid size (greater than max)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 100), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid nil, bidArgs{buyer, c("token2", 101)}, - types.CodeBidTooLarge, + types.ErrBidTooLarge, types.DistantFuture, nil, c("token2", 0), false, + false, }, { "collateral [reverse]: normal", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase bidArgs{buyer, c("token1", 15)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), true, + false, }, { "collateral [reverse]: second bidder", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}, {buyer, c("token1", 15)}}, // put auction into reverse phase, and add a reverse phase bid bidArgs{secondBuyer, c("token1", 14)}, - sdk.CodeType(0), + nil, someTime.Add(types.DefaultBidDuration), secondBuyer, c("token2", 50), true, + false, }, { "collateral [reverse]: invalid lot denom", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase bidArgs{buyer, c("badtoken", 15)}, - types.CodeInvalidLotDenom, + types.ErrInvalidLotDenom, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), false, + false, }, { "collateral [reverse]: invalid lot size (greater)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase bidArgs{buyer, c("token1", 21)}, - types.CodeLotTooLarge, + types.ErrLotTooLarge, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), false, + false, }, { "collateral [reverse]: invalid lot size (equal)", auctionArgs{Collateral, modName, c("token1", 20), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase bidArgs{buyer, c("token1", 20)}, - types.CodeLotTooLarge, + types.ErrLotTooLarge, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), false, + false, }, { "collateral [reverse]: invalid lot size (larger than min increment)", auctionArgs{Collateral, modName, c("token1", 60), c("token2", 50), c("debt", 50), collateralAddrs, collateralWeights}, // lot, max bid []bidArgs{{buyer, c("token2", 50)}}, // put auction into reverse phase bidArgs{buyer, c("token1", 58)}, // max lot at default 5% is 57 - types.CodeLotTooLarge, + types.ErrLotTooLarge, someTime.Add(types.DefaultBidDuration), buyer, c("token2", 50), false, + false, }, } for _, tc := range tests { @@ -394,17 +423,26 @@ func TestAuctionBidding(t *testing.T) { // Start Auction var id uint64 - var err sdk.Error + var err error switch tc.auctionArgs.auctionType { case Surplus: - id, _ = keeper.StartSurplusAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid.Denom) + if tc.expectPanic { + require.Panics(t, func() { + id, err = keeper.StartSurplusAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid.Denom) + }) + } else { + id, err = keeper.StartSurplusAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid.Denom) + } case Debt: - id, _ = keeper.StartDebtAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.bid, tc.auctionArgs.lot, tc.auctionArgs.debt) + id, err = keeper.StartDebtAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.bid, tc.auctionArgs.lot, tc.auctionArgs.debt) case Collateral: - id, _ = keeper.StartCollateralAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid, tc.auctionArgs.addresses, tc.auctionArgs.weights, tc.auctionArgs.debt) // seller, lot, maxBid, otherPerson + id, err = keeper.StartCollateralAuction(ctx, tc.auctionArgs.seller, tc.auctionArgs.lot, tc.auctionArgs.bid, tc.auctionArgs.addresses, tc.auctionArgs.weights, tc.auctionArgs.debt) // seller, lot, maxBid, otherPerson default: t.Fail() } + + require.NoError(t, err) + // Place setup bids for _, b := range tc.setupBids { require.NoError(t, keeper.PlaceBid(ctx, id, b.bidder, b.amount)) @@ -428,7 +466,7 @@ func TestAuctionBidding(t *testing.T) { err = keeper.PlaceBid(ctx, id, tc.bidArgs.bidder, tc.bidArgs.amount) // Check success/failure - if tc.expectpass { + if tc.expectPass { require.Nil(t, err) // Check auction was found newAuction, found := keeper.GetAuction(ctx, id) @@ -445,7 +483,8 @@ func TestAuctionBidding(t *testing.T) { case Debt: bidAmt = oldAuction.GetBid() case Collateral: - collatAuction, _ := oldAuction.(types.CollateralAuction) + collatAuction, ok := oldAuction.(types.CollateralAuction) + require.True(t, ok, tc.name) if collatAuction.IsReversePhase() { bidAmt = oldAuction.GetBid() } @@ -453,18 +492,18 @@ func TestAuctionBidding(t *testing.T) { if oldBidder.Equals(tc.bidArgs.bidder) { // same bidder require.Equal(t, newBidderOldCoins.Sub(cs(bidAmt.Sub(oldAuction.GetBid()))), bank.GetCoins(ctx, tc.bidArgs.bidder)) } else { - require.Equal(t, cs(newBidderOldCoins.Sub(cs(bidAmt))...), bank.GetCoins(ctx, tc.bidArgs.bidder)) // wrapping in cs() to avoid comparing nil and empty coins - if oldBidder.Equals(supply.NewModuleAddress(oldAuction.GetInitiator())) { // handle checking debt coins for case debt auction has had no bids placed yet TODO make this less confusing - require.Equal(t, cs(oldBidderOldCoins.Add(cs(oldAuction.GetBid()))...).Add(cs(c("debt", oldAuction.GetBid().Amount.Int64()))), bank.GetCoins(ctx, oldBidder)) + require.Equal(t, newBidderOldCoins.Sub(cs(bidAmt)), bank.GetCoins(ctx, tc.bidArgs.bidder)) // wrapping in cs() to avoid comparing nil and empty coins + if oldBidder.Equals(supply.NewModuleAddress(oldAuction.GetInitiator())) { // handle checking debt coins for case debt auction has had no bids placed yet TODO make this less confusing + require.Equal(t, oldBidderOldCoins.Add(oldAuction.GetBid()).Add(c("debt", oldAuction.GetBid().Amount.Int64())), bank.GetCoins(ctx, oldBidder)) } else { - require.Equal(t, cs(oldBidderOldCoins.Add(cs(oldAuction.GetBid()))...), bank.GetCoins(ctx, oldBidder)) + require.Equal(t, cs(oldBidderOldCoins.Add(oldAuction.GetBid())...), bank.GetCoins(ctx, oldBidder)) } } } else { // Check expected error code type require.NotNil(t, err, "PlaceBid did not return an error") // catch nil values before they cause a panic below - require.Equal(t, tc.expectedError, err.Result().Code) + require.True(t, errors.Is(err, tc.expectedError)) // Check auction values newAuction, found := keeper.GetAuction(ctx, id) diff --git a/x/auction/keeper/invariants.go b/x/auction/keeper/invariants.go index 769b9904..05eee629 100644 --- a/x/auction/keeper/invariants.go +++ b/x/auction/keeper/invariants.go @@ -21,7 +21,7 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { ValidIndexInvariant(k)) } -// ModuleAccountInvariant checks that the module account's coins matches those stored in auctions +// ModuleAccountInvariants checks that the module account's coins matches those stored in auctions func ModuleAccountInvariants(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { @@ -31,7 +31,7 @@ func ModuleAccountInvariants(k Keeper) sdk.Invariant { if !ok { panic("stored auction type does not fulfill GenesisAuction interface") } - totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()) + totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...) return false }) diff --git a/x/auction/keeper/keeper.go b/x/auction/keeper/keeper.go index 7a51302b..da8caf89 100644 --- a/x/auction/keeper/keeper.go +++ b/x/auction/keeper/keeper.go @@ -18,7 +18,6 @@ type Keeper struct { storeKey sdk.StoreKey cdc *codec.Codec paramSubspace subspace.Subspace - codespace sdk.CodespaceType } // NewKeeper returns a new auction keeper. @@ -26,11 +25,16 @@ func NewKeeper(cdc *codec.Codec, storeKey sdk.StoreKey, supplyKeeper types.Suppl if addr := supplyKeeper.GetModuleAddress(types.ModuleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } + + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } + return Keeper{ supplyKeeper: supplyKeeper, storeKey: storeKey, cdc: cdc, - paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()), + paramSubspace: paramstore, } } @@ -46,17 +50,17 @@ func (k Keeper) SetNextAuctionID(ctx sdk.Context, id uint64) { } // GetNextAuctionID reads the next available global ID from store -func (k Keeper) GetNextAuctionID(ctx sdk.Context) (uint64, sdk.Error) { +func (k Keeper) GetNextAuctionID(ctx sdk.Context) (uint64, error) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.NextAuctionIDKey) if bz == nil { - return 0, types.ErrInvalidInitialAuctionID(k.codespace) + return 0, types.ErrInvalidInitialAuctionID } return types.Uint64FromBytes(bz), nil } // IncrementNextAuctionID increments the next auction ID in the store by 1. -func (k Keeper) IncrementNextAuctionID(ctx sdk.Context) sdk.Error { +func (k Keeper) IncrementNextAuctionID(ctx sdk.Context) error { id, err := k.GetNextAuctionID(ctx) if err != nil { return err @@ -66,7 +70,7 @@ func (k Keeper) IncrementNextAuctionID(ctx sdk.Context) sdk.Error { } // StoreNewAuction stores an auction, adding a new ID -func (k Keeper) StoreNewAuction(ctx sdk.Context, auction types.Auction) (uint64, sdk.Error) { +func (k Keeper) StoreNewAuction(ctx sdk.Context, auction types.Auction) (uint64, error) { newAuctionID, err := k.GetNextAuctionID(ctx) if err != nil { return 0, err diff --git a/x/auction/keeper/querier.go b/x/auction/keeper/querier.go index 08d337b3..e7d6460a 100644 --- a/x/auction/keeper/querier.go +++ b/x/auction/keeper/querier.go @@ -1,16 +1,18 @@ package keeper import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/auction/types" ) // NewQuerier is the module level router for state queries func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { switch path[0] { case types.QueryGetAuction: return queryAuction(ctx, req, keeper) @@ -19,35 +21,35 @@ func NewQuerier(keeper Keeper) sdk.Querier { case types.QueryGetParams: return queryGetParams(ctx, req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown auction query endpoint") + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) } } } -func queryAuction(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryAuction(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Decode request var requestParams types.QueryAuctionParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } // Lookup auction auction, found := keeper.GetAuction(ctx, requestParams.AuctionID) if !found { - return nil, types.ErrAuctionNotFound(types.DefaultCodespace, requestParams.AuctionID) + return nil, sdkerrors.Wrapf(types.ErrAuctionNotFound, "%d", requestParams.AuctionID) } // Encode results bz, err := codec.MarshalJSONIndent(keeper.cdc, auction) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryAuctions(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryAuctions(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Get all auctions auctionsList := types.Auctions{} keeper.IterateAuctions(ctx, func(a types.Auction) bool { @@ -58,21 +60,21 @@ func queryAuctions(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byt // Encode Results bz, err := codec.MarshalJSONIndent(keeper.cdc, auctionsList) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query params in the auction store -func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Get params params := keeper.GetParams(ctx) // Encode results bz, err := codec.MarshalJSONIndent(keeper.cdc, params) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil diff --git a/x/auction/module.go b/x/auction/module.go index 5e25bff6..dd1134a0 100644 --- a/x/auction/module.go +++ b/x/auction/module.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" sim "github.com/cosmos/cosmos-sdk/x/simulation" abci "github.com/tendermint/tendermint/abci/types" @@ -24,7 +25,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic implements the sdk.AppModuleBasic interface. @@ -72,40 +73,21 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the auction module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for auction module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the auction module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams creates randomized auction param changes for the simulator. -func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { - return simulation.ParamChanges(r) -} - -//____________________________________________________________________________ - // AppModule implements the sdk.AppModule interface. type AppModule struct { AppModuleBasic - AppModuleSimulation - keeper Keeper - supplyKeeper types.SupplyKeeper + keeper Keeper + accountKeeper auth.AccountKeeper + supplyKeeper types.SupplyKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule { +func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, supplyKeeper types.SupplyKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, + accountKeeper: accountKeeper, supplyKeeper: supplyKeeper, } } @@ -159,3 +141,30 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// GenerateGenesisState creates a randomized GenState of the auction module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because auction has no params. +func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for auction module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the auction module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.accountKeeper, am.keeper) +} diff --git a/x/auction/simulation/decoder.go b/x/auction/simulation/decoder.go index 232fc005..a6a58e92 100644 --- a/x/auction/simulation/decoder.go +++ b/x/auction/simulation/decoder.go @@ -5,14 +5,15 @@ import ( "encoding/binary" "fmt" + "github.com/tendermint/tendermint/libs/kv" + "github.com/cosmos/cosmos-sdk/codec" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/kava-labs/kava/x/auction/types" ) // DecodeStore unmarshals the KVPair's Value to the corresponding auction type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Equal(kvA.Key[:1], types.AuctionKeyPrefix): var auctionA, auctionB types.Auction diff --git a/x/auction/simulation/decoder_test.go b/x/auction/simulation/decoder_test.go index 1aa5f749..f1109332 100644 --- a/x/auction/simulation/decoder_test.go +++ b/x/auction/simulation/decoder_test.go @@ -6,8 +6,7 @@ import ( "time" "github.com/stretchr/testify/require" - - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,11 +27,11 @@ func TestDecodeDistributionStore(t *testing.T) { oneCoin := sdk.NewCoin("coin", sdk.OneInt()) auction := types.NewSurplusAuction("me", oneCoin, "coin", time.Now().UTC()) - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: types.AuctionKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(&auction)}, - cmn.KVPair{Key: types.AuctionByTimeKeyPrefix, Value: sdk.Uint64ToBigEndian(2)}, - cmn.KVPair{Key: types.NextAuctionIDKey, Value: sdk.Uint64ToBigEndian(10)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: types.AuctionKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(&auction)}, + kv.Pair{Key: types.AuctionByTimeKeyPrefix, Value: sdk.Uint64ToBigEndian(2)}, + kv.Pair{Key: types.NextAuctionIDKey, Value: sdk.Uint64ToBigEndian(10)}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/auction/simulation/genesis.go b/x/auction/simulation/genesis.go index 51b14d04..3d4949f9 100644 --- a/x/auction/simulation/genesis.go +++ b/x/auction/simulation/genesis.go @@ -88,7 +88,7 @@ func RandomizedGenState(simState *module.SimulationState) { if !ok { panic("can't convert Auction to GenesisAuction") } - totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()) + totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...) } auctionGenesis.NextAuctionID = startingID + uint64(len(auctions)) auctionGenesis.Auctions = append(auctionGenesis.Auctions, auctions...) @@ -112,7 +112,7 @@ func RandomizedGenState(simState *module.SimulationState) { panic("bidder not found") } bidderCoins := sdk.NewCoins(sdk.NewInt64Coin("usdx", 10000000000)) - if err := bidder.SetCoins(bidder.GetCoins().Add(bidderCoins)); err != nil { + if err := bidder.SetCoins(bidder.GetCoins().Add(bidderCoins...)); err != nil { panic(err) } authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, bidder) @@ -123,7 +123,7 @@ func RandomizedGenState(simState *module.SimulationState) { // TODO find some way for this to happen automatically / move it elsewhere var supplyGenesis supply.GenesisState simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis) - supplyGenesis.Supply = supplyGenesis.Supply.Add(totalAuctionCoins).Add(bidderCoins) + supplyGenesis.Supply = supplyGenesis.Supply.Add(totalAuctionCoins...).Add(bidderCoins...) simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis) // TODO liquidator mod account doesn't need to be initialized for this example diff --git a/x/auction/simulation/operations.go b/x/auction/simulation/operations.go new file mode 100644 index 00000000..84f2297c --- /dev/null +++ b/x/auction/simulation/operations.go @@ -0,0 +1,251 @@ +package simulation + +import ( + "errors" + "fmt" + "math/big" + "math/rand" + "time" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + "github.com/cosmos/cosmos-sdk/x/simulation" + + appparams "github.com/kava-labs/kava/app/params" + "github.com/kava-labs/kava/x/auction/keeper" + "github.com/kava-labs/kava/x/auction/types" +) + +var ( + errorNotEnoughCoins = errors.New("account doesn't have enough coins") + errorCantReceiveBids = errors.New("auction can't receive bids (lot = 0 in reverse auction)") +) + +// Simulation operation weights constants +const ( + OpWeightMsgPlaceBid = "op_weight_msg_place_bid" +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simulation.AppParams, cdc *codec.Codec, ak auth.AccountKeeper, k keeper.Keeper, +) simulation.WeightedOperations { + var weightMsgPlaceBid int + + appParams.GetOrGenerate(cdc, OpWeightMsgPlaceBid, &weightMsgPlaceBid, nil, + func(_ *rand.Rand) { + weightMsgPlaceBid = appparams.DefaultWeightMsgPlaceBid + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgPlaceBid, + SimulateMsgPlaceBid(ak, k), + ), + } +} + +// SimulateMsgPlaceBid returns a function that runs a random state change on the module keeper. +// There's two error paths +// - return a OpMessage, but nil error - this will log a message but keep running the simulation +// - return an error - this will stop the simulation +func SimulateMsgPlaceBid(ak auth.AccountKeeper, keeper keeper.Keeper) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + // get open auctions + openAuctions := types.Auctions{} + keeper.IterateAuctions(ctx, func(a types.Auction) bool { + openAuctions = append(openAuctions, a) + return false + }) + + // shuffle auctions slice so that bids are evenly distributed across auctions + r.Shuffle(len(openAuctions), func(i, j int) { + openAuctions[i], openAuctions[j] = openAuctions[j], openAuctions[i] + }) + + // search through auctions and an accounts to find a pair where a bid can be placed (ie account has enough coins to place bid on auction) + blockTime := ctx.BlockHeader().Time + params := keeper.GetParams(ctx) + bidder, openAuction, found := findValidAccountAuctionPair(accs, openAuctions, func(acc simulation.Account, auc types.Auction) bool { + account := ak.GetAccount(ctx, acc.Address) + _, err := generateBidAmount(r, params, auc, account, blockTime) + if err == errorNotEnoughCoins || err == errorCantReceiveBids { + return false // keep searching + } else if err != nil { + panic(err) // raise errors + } + return true // found valid pair + }) + if !found { + return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation (no valid auction and bidder)", "", false, nil), nil, nil + } + + bidderAcc := ak.GetAccount(ctx, bidder.Address) + if bidderAcc == nil { + return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("couldn't find account %s", bidder.Address) + } + + // pick a bid amount for the chosen auction and bidder + amount, err := generateBidAmount(r, params, openAuction, bidderAcc, blockTime) + if err != nil { // shouldn't happen given the checks above + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + // create and deliver a tx + msg := types.NewMsgPlaceBid(openAuction.GetID(), bidder.Address, amount) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + sdk.NewCoins(), // TODO pick a random amount fees + helpers.DefaultGenTxGas, + chainID, + []uint64{bidderAcc.GetAccountNumber()}, + []uint64{bidderAcc.GetSequence()}, + bidder.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + // to aid debugging, add the stack trace to the comment field of the returned opMsg + return simulation.NewOperationMsg(msg, false, fmt.Sprintf("%+v", err)), nil, err + } + // to aid debugging, add the result log to the comment field + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil + } +} + +func generateBidAmount( + r *rand.Rand, params types.Params, auc types.Auction, + bidder authexported.Account, blockTime time.Time) (sdk.Coin, error) { + bidderBalance := bidder.SpendableCoins(blockTime) + + switch a := auc.(type) { + + case types.DebtAuction: + // Check bidder has enough (stable coin) to pay in + if bidderBalance.AmountOf(a.Bid.Denom).LT(a.Bid.Amount) { // stable coin + return sdk.Coin{}, errorNotEnoughCoins + } + // Check auction can still receive new bids + if a.Lot.Amount.Equal(sdk.ZeroInt()) { + return sdk.Coin{}, errorCantReceiveBids + } + // Generate a new lot amount (gov coin) + maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost + sdk.MaxInt( + sdk.NewInt(1), + sdk.NewDecFromInt(a.Lot.Amount).Mul(params.IncrementDebt).RoundInt(), + ), + ) + amt, err := RandIntInclusive(r, sdk.ZeroInt(), maxNewLotAmt) // maxNewLotAmt shouldn't be < 0 given the check above + if err != nil { + panic(err) + } + return sdk.NewCoin(a.Lot.Denom, amt), nil // gov coin + + case types.SurplusAuction: + // Check the bidder has enough (gov coin) to pay in + minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost + sdk.MaxInt( + sdk.NewInt(1), + sdk.NewDecFromInt(a.Bid.Amount).Mul(params.IncrementSurplus).RoundInt(), + ), + ) + if bidderBalance.AmountOf(a.Bid.Denom).LT(minNewBidAmt) { // gov coin + return sdk.Coin{}, errorNotEnoughCoins + } + // Generate a new bid amount (gov coin) + amt, err := RandIntInclusive(r, minNewBidAmt, bidderBalance.AmountOf(a.Bid.Denom)) + if err != nil { + panic(err) + } + return sdk.NewCoin(a.Bid.Denom, amt), nil // gov coin + + case types.CollateralAuction: + // Check the bidder has enough (stable coin) to pay in + minNewBidAmt := a.Bid.Amount.Add( // new bids must be some % greater than old bid, and at least 1 larger to avoid replacing an old bid at no cost + sdk.MaxInt( + sdk.NewInt(1), + sdk.NewDecFromInt(a.Bid.Amount).Mul(params.IncrementCollateral).RoundInt(), + ), + ) + minNewBidAmt = sdk.MinInt(minNewBidAmt, a.MaxBid.Amount) // allow new bids to hit MaxBid even though it may be less than the increment % + if bidderBalance.AmountOf(a.Bid.Denom).LT(minNewBidAmt) { + return sdk.Coin{}, errorNotEnoughCoins + } + // Check auction can still receive new bids + if a.IsReversePhase() && a.Lot.Amount.Equal(sdk.ZeroInt()) { + return sdk.Coin{}, errorCantReceiveBids + } + // Generate a new bid amount (collateral coin in reverse phase) + if a.IsReversePhase() { + maxNewLotAmt := a.Lot.Amount.Sub( // new lot must be some % less than old lot, and at least 1 smaller to avoid replacing an old bid at no cost + sdk.MaxInt( + sdk.NewInt(1), + sdk.NewDecFromInt(a.Lot.Amount).Mul(params.IncrementCollateral).RoundInt(), + ), + ) + amt, err := RandIntInclusive(r, sdk.ZeroInt(), maxNewLotAmt) // maxNewLotAmt shouldn't be < 0 given the check above + if err != nil { + panic(err) + } + return sdk.NewCoin(a.Lot.Denom, amt), nil // collateral coin + + // Generate a new bid amount (stable coin in forward phase) + } else { + amt, err := RandIntInclusive(r, minNewBidAmt, sdk.MinInt(bidderBalance.AmountOf(a.Bid.Denom), a.MaxBid.Amount)) + if err != nil { + panic(err) + } + // when the bidder has enough coins, pick the MaxBid amount more frequently to increase chance auctions phase get into reverse phase + if r.Intn(2) == 0 && bidderBalance.AmountOf(a.Bid.Denom).GTE(a.MaxBid.Amount) { // 50% + amt = a.MaxBid.Amount + } + return sdk.NewCoin(a.Bid.Denom, amt), nil // stable coin + } + + default: + return sdk.Coin{}, fmt.Errorf("unknown auction type") + } +} + +// findValidAccountAuctionPair finds an auction and account for which the callback func returns true +func findValidAccountAuctionPair(accounts []simulation.Account, auctions types.Auctions, cb func(simulation.Account, types.Auction) bool) (simulation.Account, types.Auction, bool) { + for _, auc := range auctions { + for _, acc := range accounts { + if isValid := cb(acc, auc); isValid { + return acc, auc, true + } + } + } + return simulation.Account{}, nil, false +} + +// RandIntInclusive randomly generates an sdk.Int in the range [inclusiveMin, inclusiveMax]. It works for negative and positive integers. +func RandIntInclusive(r *rand.Rand, inclusiveMin, inclusiveMax sdk.Int) (sdk.Int, error) { + if inclusiveMin.GT(inclusiveMax) { + return sdk.Int{}, fmt.Errorf("min larger than max") + } + return RandInt(r, inclusiveMin, inclusiveMax.Add(sdk.OneInt())) +} + +// RandInt randomly generates an sdk.Int in the range [inclusiveMin, exclusiveMax). It works for negative and positive integers. +func RandInt(r *rand.Rand, inclusiveMin, exclusiveMax sdk.Int) (sdk.Int, error) { + // validate input + if inclusiveMin.GTE(exclusiveMax) { + return sdk.Int{}, fmt.Errorf("min larger or equal to max") + } + // shift the range to start at 0 + shiftedRange := exclusiveMax.Sub(inclusiveMin) // should always be positive given the check above + // randomly pick from the shifted range + shiftedRandInt := sdk.NewIntFromBigInt(new(big.Int).Rand(r, shiftedRange.BigInt())) + // shift back to the original range + return shiftedRandInt.Add(inclusiveMin), nil +} diff --git a/x/auction/simulation/operations/msg.go b/x/auction/simulation/operations/msg.go deleted file mode 100644 index d38f97bc..00000000 --- a/x/auction/simulation/operations/msg.go +++ /dev/null @@ -1,177 +0,0 @@ -package operations - -import ( - "errors" - "fmt" - "math/big" - "math/rand" - "time" - - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/cosmos-sdk/x/simulation" - - "github.com/kava-labs/kava/x/auction" -) - -var ( - noOpMsg = simulation.NoOpMsg(auction.ModuleName) - ErrorNotEnoughCoins = errors.New("account doesn't have enough coins") -) - -// Return a function that runs a random state change on the module keeper. -// There's two error paths -// - return a OpMessage, but nil error - this will log a message but keep running the simulation -// - return an error - this will stop the simulation -func SimulateMsgPlaceBid(authKeeper auth.AccountKeeper, keeper auction.Keeper) simulation.Operation { - handler := auction.NewHandler(keeper) - - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - simulation.OperationMsg, []simulation.FutureOperation, error) { - - // get open auctions - openAuctions := auction.Auctions{} - keeper.IterateAuctions(ctx, func(a auction.Auction) bool { - openAuctions = append(openAuctions, a) - return false - }) - - // shuffle auctions slice so that bids are evenly distributed across auctions - r.Shuffle(len(openAuctions), func(i, j int) { - openAuctions[i], openAuctions[j] = openAuctions[j], openAuctions[i] - }) - // TODO do the same for accounts? - var accounts []authexported.Account - for _, acc := range accs { - accounts = append(accounts, authKeeper.GetAccount(ctx, acc.Address)) - } - - // search through auctions and an accounts to find a pair where a bid can be placed (ie account has enough coins to place bid on auction) - blockTime := ctx.BlockHeader().Time - bidder, openAuction, found := findValidAccountAuctionPair(accounts, openAuctions, func(acc authexported.Account, auc auction.Auction) bool { - _, err := generateBidAmount(r, auc, acc, blockTime) - if err == ErrorNotEnoughCoins { - return false // keep searching - } else if err != nil { - panic(err) // raise errors - } - return true // found valid pair - }) - if !found { - return simulation.NewOperationMsgBasic(auction.ModuleName, "no-operation (no valid auction and bidder)", "", false, nil), nil, nil - } - - // pick a bid amount for the chosen auction and bidder - amount, _ := generateBidAmount(r, openAuction, bidder, blockTime) - - // create a msg - msg := auction.NewMsgPlaceBid(openAuction.GetID(), bidder.GetAddress(), amount) - if err := msg.ValidateBasic(); err != nil { // don't submit errors that fail ValidateBasic, use unit tests for testing ValidateBasic - return noOpMsg, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) - } - - // submit the msg - result := submitMsg(ctx, handler, msg) - // Return an operationMsg indicating whether the msg was submitted successfully - // Using result.Log as the comment field as it contains any error message emitted by the keeper - return simulation.NewOperationMsg(msg, result.IsOK(), result.Log), nil, nil - } -} - -func submitMsg(ctx sdk.Context, handler sdk.Handler, msg sdk.Msg) sdk.Result { - ctx, write := ctx.CacheContext() - result := handler(ctx, msg) - if result.IsOK() { - write() - } - return result -} - -func generateBidAmount(r *rand.Rand, auc auction.Auction, bidder authexported.Account, blockTime time.Time) (sdk.Coin, error) { - bidderBalance := bidder.SpendableCoins(blockTime) - - switch a := auc.(type) { - - case auction.DebtAuction: - if bidderBalance.AmountOf(a.Bid.Denom).LT(a.Bid.Amount) { // stable coin - return sdk.Coin{}, ErrorNotEnoughCoins - } - amt, err := RandIntInclusive(r, sdk.ZeroInt(), a.Lot.Amount) // pick amount less than current lot amount // TODO min bid increments - if err != nil { - panic(err) - } - return sdk.NewCoin(a.Lot.Denom, amt), nil // gov coin - - case auction.SurplusAuction: - if bidderBalance.AmountOf(a.Bid.Denom).LT(a.Bid.Amount) { // gov coin // TODO account for bid increments - return sdk.Coin{}, ErrorNotEnoughCoins - } - amt, err := RandIntInclusive(r, a.Bid.Amount, bidderBalance.AmountOf(a.Bid.Denom)) - if err != nil { - panic(err) - } - return sdk.NewCoin(a.Bid.Denom, amt), nil // gov coin - - case auction.CollateralAuction: - if bidderBalance.AmountOf(a.Bid.Denom).LT(a.Bid.Amount) { // stable coin // TODO account for bid increments (in forward phase) - return sdk.Coin{}, ErrorNotEnoughCoins - } - if a.IsReversePhase() { - amt, err := RandIntInclusive(r, sdk.ZeroInt(), a.Lot.Amount) // pick amount less than current lot amount - if err != nil { - panic(err) - } - return sdk.NewCoin(a.Lot.Denom, amt), nil // collateral coin - } else { - amt, err := RandIntInclusive(r, a.Bid.Amount, sdk.MinInt(bidderBalance.AmountOf(a.Bid.Denom), a.MaxBid.Amount)) - if err != nil { - panic(err) - } - // pick the MaxBid amount more frequently to increase chance auctions phase get into reverse phase - if r.Intn(10) == 0 { // 10% - amt = a.MaxBid.Amount - } - return sdk.NewCoin(a.Bid.Denom, amt), nil // stable coin - } - - default: - return sdk.Coin{}, fmt.Errorf("unknown auction type") - } -} - -// findValidAccountAuctionPair finds an auction and account for which the callback func returns true -func findValidAccountAuctionPair(accounts []authexported.Account, auctions auction.Auctions, cb func(authexported.Account, auction.Auction) bool) (authexported.Account, auction.Auction, bool) { - for _, auc := range auctions { - for _, acc := range accounts { - if isValid := cb(acc, auc); isValid { - return acc, auc, true - } - - } - } - return nil, nil, false -} - -// RandInt randomly generates an sdk.Int in the range [inclusiveMin, inclusiveMax]. It works for negative and positive integers. -func RandIntInclusive(r *rand.Rand, inclusiveMin, inclusiveMax sdk.Int) (sdk.Int, error) { - if inclusiveMin.GT(inclusiveMax) { - return sdk.Int{}, fmt.Errorf("min larger than max") - } - return RandInt(r, inclusiveMin, inclusiveMax.Add(sdk.OneInt())) -} - -// RandInt randomly generates an sdk.Int in the range [inclusiveMin, exclusiveMax). It works for negative and positive integers. -func RandInt(r *rand.Rand, inclusiveMin, exclusiveMax sdk.Int) (sdk.Int, error) { - // validate input - if inclusiveMin.GTE(exclusiveMax) { - return sdk.Int{}, fmt.Errorf("min larger or equal to max") - } - // shift the range to start at 0 - shiftedRange := exclusiveMax.Sub(inclusiveMin) // should always be positive given the check above - // randomly pick from the shifted range - shiftedRandInt := sdk.NewIntFromBigInt(new(big.Int).Rand(r, shiftedRange.BigInt())) - // shift back to the original range - return shiftedRandInt.Add(inclusiveMin), nil -} diff --git a/x/auction/simulation/params.go b/x/auction/simulation/params.go index 7ae658cd..a241c17c 100644 --- a/x/auction/simulation/params.go +++ b/x/auction/simulation/params.go @@ -17,29 +17,29 @@ func ParamChanges(r *rand.Rand) []simulation.ParamChange { // as strings in JSON (such as time.Duration) have the escaped quotes. // TODO should we encode the values properly with ModuleCdc.MustMarshalJSON()? return []simulation.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, string(types.KeyBidDuration), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyBidDuration), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenBidDuration(r)) + return fmt.Sprintf("%d", GenBidDuration(r)) }, ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyMaxAuctionDuration), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyMaxAuctionDuration), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenMaxAuctionDuration(r)) + return fmt.Sprintf("%d", GenMaxAuctionDuration(r)) }, ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementCollateral), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementCollateral), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenIncrementCollateral(r)) + return fmt.Sprintf("%d", GenIncrementCollateral(r)) }, ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementDebt), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementDebt), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenIncrementDebt(r)) + return fmt.Sprintf("%d", GenIncrementDebt(r)) }, ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementSurplus), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementSurplus), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenIncrementSurplus(r)) + return fmt.Sprintf("%d", GenIncrementSurplus(r)) }, ), } diff --git a/x/auction/types/auctions.go b/x/auction/types/auctions.go index e0205e65..2e06b86e 100644 --- a/x/auction/types/auctions.go +++ b/x/auction/types/auctions.go @@ -204,7 +204,7 @@ func (a CollateralAuction) GetType() string { return "collateral" } // It is used in genesis initialize the module account correctly. func (a CollateralAuction) GetModuleAccountCoins() sdk.Coins { // a.Bid is paid out on bids, so is never stored in the module account - return sdk.NewCoins(a.Lot).Add(sdk.NewCoins(a.CorrespondingDebt)) + return sdk.NewCoins(a.Lot).Add(sdk.NewCoins(a.CorrespondingDebt)...) } // IsReversePhase returns whether the auction has switched over to reverse phase or not. @@ -276,24 +276,25 @@ type WeightedAddresses struct { } // NewWeightedAddresses returns a new list addresses with weights. -func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, sdk.Error) { +func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, error) { wa := WeightedAddresses{ Addresses: addrs, Weights: weights, } if err := wa.Validate(); err != nil { - return WeightedAddresses{}, sdk.ErrInternal(err.Error()) + return WeightedAddresses{}, err } return wa, nil } +// Validate checks for that the weights are not negative and that lengths match. func (wa WeightedAddresses) Validate() error { if len(wa.Addresses) != len(wa.Weights) { - return fmt.Errorf("number of addresses doesn't match number of weights") + return fmt.Errorf("number of addresses doesn't match number of weights, %d ≠ %d", len(wa.Addresses), len(wa.Weights)) } for _, w := range wa.Weights { if w.IsNegative() { - return fmt.Errorf("weights contain a negative amount") + return fmt.Errorf("weights contain a negative amount: %s", w) } } return nil diff --git a/x/auction/types/errors.go b/x/auction/types/errors.go index 85e6b718..6aa78673 100644 --- a/x/auction/types/errors.go +++ b/x/auction/types/errors.go @@ -1,98 +1,36 @@ -// DONTCOVER package types -import ( - "fmt" - "time" +import sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - sdk "github.com/cosmos/cosmos-sdk/types" +// DONTCOVER + +var ( + // ErrInvalidInitialAuctionID error for when the initial auction ID hasn't been set + ErrInvalidInitialAuctionID = sdkerrors.Register(ModuleName, 2, "initial auction ID hasn't been set") + // ErrInvalidModulePermissions error for when module doesn't have valid permissions + ErrInvalidModulePermissions = sdkerrors.Register(ModuleName, 3, "module does not have required permission") + // ErrUnrecognizedAuctionType error for unrecognized auction type + ErrUnrecognizedAuctionType = sdkerrors.Register(ModuleName, 4, "unrecognized auction type") + // ErrAuctionNotFound error for when an auction is not found + ErrAuctionNotFound = sdkerrors.Register(ModuleName, 5, "auction not found") + // ErrAuctionHasNotExpired error for attempting to close an auction that has not passed its end time + ErrAuctionHasNotExpired = sdkerrors.Register(ModuleName, 6, "auction can't be closed as curent block time has not passed auction end time") + // ErrAuctionHasExpired error for when an auction is closed and unavailable for bidding + ErrAuctionHasExpired = sdkerrors.Register(ModuleName, 7, "auction has closed") + // ErrInvalidBidDenom error for when bid denom doesn't match auction bid denom + ErrInvalidBidDenom = sdkerrors.Register(ModuleName, 8, "bid denom doesn't match auction bid denom") + // ErrInvalidLotDenom error for when lot denom doesn't match auction lot denom + ErrInvalidLotDenom = sdkerrors.Register(ModuleName, 9, "lot denom doesn't match auction lot denom") + // ErrBidTooSmall error for when bid is not greater than auction's min bid amount + ErrBidTooSmall = sdkerrors.Register(ModuleName, 10, "bid is not greater than auction's min new bid amount") + // ErrBidTooLarge error for when bid is larger than auction's maximum allowed bid + ErrBidTooLarge = sdkerrors.Register(ModuleName, 11, "bid is greater than auction's max bid") + // ErrLotTooSmall error for when lot is less than zero + ErrLotTooSmall = sdkerrors.Register(ModuleName, 12, "lot is not greater than auction's min new lot amount") + // ErrLotTooLarge error for when lot is not smaller than auction's max new lot amount + ErrLotTooLarge = sdkerrors.Register(ModuleName, 13, "lot is greater than auction's max new lot amount") + // ErrCollateralAuctionIsInReversePhase error for when attempting to place a forward bid on a collateral auction in reverse phase + ErrCollateralAuctionIsInReversePhase = sdkerrors.Register(ModuleName, 14, "invalid bid: auction is in reverse phase") + // ErrCollateralAuctionIsInForwardPhase error for when attempting to place a reverse bid on a collateral auction in forward phase + ErrCollateralAuctionIsInForwardPhase = sdkerrors.Register(ModuleName, 15, "invalid bid: auction is in forward phase") ) - -// Error codes specific to auction module -const ( - DefaultCodespace sdk.CodespaceType = ModuleName - CodeInvalidInitialAuctionID sdk.CodeType = 1 - CodeInvalidModulePermissions sdk.CodeType = 2 - CodeUnrecognizedAuctionType sdk.CodeType = 3 - CodeAuctionNotFound sdk.CodeType = 4 - CodeAuctionHasNotExpired sdk.CodeType = 5 - CodeAuctionHasExpired sdk.CodeType = 6 - CodeInvalidBidDenom sdk.CodeType = 7 - CodeInvalidLotDenom sdk.CodeType = 8 - CodeBidTooSmall sdk.CodeType = 9 - CodeBidTooLarge sdk.CodeType = 10 - CodeLotTooSmall sdk.CodeType = 11 - CodeLotTooLarge sdk.CodeType = 12 - CodeCollateralAuctionIsInReversePhase sdk.CodeType = 13 - CodeCollateralAuctionIsInForwardPhase sdk.CodeType = 14 -) - -// ErrInvalidInitialAuctionID error for when the initial auction ID hasn't been set -func ErrInvalidInitialAuctionID(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidInitialAuctionID, fmt.Sprintf("initial auction ID hasn't been set")) -} - -// ErrInvalidModulePermissions error for when module doesn't have valid permissions -func ErrInvalidModulePermissions(codespace sdk.CodespaceType, permission string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidModulePermissions, fmt.Sprintf("module does not have required permission '%s'", permission)) -} - -// ErrUnrecognizedAuctionType error for unrecognized auction type -func ErrUnrecognizedAuctionType(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeUnrecognizedAuctionType, fmt.Sprintf("unrecognized auction type")) -} - -// ErrAuctionNotFound error for when an auction is not found -func ErrAuctionNotFound(codespace sdk.CodespaceType, id uint64) sdk.Error { - return sdk.NewError(codespace, CodeAuctionNotFound, fmt.Sprintf("auction %d was not found", id)) -} - -// ErrAuctionHasNotExpired error for attempting to close an auction that has not passed its end time -func ErrAuctionHasNotExpired(codespace sdk.CodespaceType, blockTime time.Time, endTime time.Time) sdk.Error { - return sdk.NewError(codespace, CodeAuctionHasNotExpired, fmt.Sprintf("auction can't be closed as curent block time (%v) has not passed auction end time (%v)", blockTime, endTime)) -} - -// ErrAuctionHasExpired error for when an auction is closed and unavailable for bidding -func ErrAuctionHasExpired(codespace sdk.CodespaceType, id uint64) sdk.Error { - return sdk.NewError(codespace, CodeAuctionHasExpired, fmt.Sprintf("auction %d has closed", id)) -} - -// ErrInvalidBidDenom error for when bid denom doesn't match auction bid denom -func ErrInvalidBidDenom(codespace sdk.CodespaceType, bidDenom string, auctionBidDenom string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidBidDenom, fmt.Sprintf("bid denom %s doesn't match auction bid denom %s", bidDenom, auctionBidDenom)) -} - -// ErrInvalidLotDenom error for when lot denom doesn't match auction lot denom -func ErrInvalidLotDenom(codespace sdk.CodespaceType, lotDenom string, auctionLotDenom string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidLotDenom, fmt.Sprintf("lot denom %s doesn't match auction lot denom %s", lotDenom, auctionLotDenom)) -} - -// ErrBidTooSmall error for when bid is not greater than auction's min bid amount -func ErrBidTooSmall(codespace sdk.CodespaceType, bid sdk.Coin, minBid sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeBidTooSmall, fmt.Sprintf("bid %s is not greater than auction's min new bid amount %s", bid.String(), minBid.String())) -} - -// ErrBidTooLarge error for when bid is larger than auction's maximum allowed bid -func ErrBidTooLarge(codespace sdk.CodespaceType, bid sdk.Coin, maxBid sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeBidTooLarge, fmt.Sprintf("bid %s is greater than auction's max bid %s", bid.String(), maxBid.String())) -} - -// ErrLotToosmall error for when lot is less than zero -func ErrLotTooSmall(codespace sdk.CodespaceType, lot sdk.Coin, minLot sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeLotTooSmall, fmt.Sprintf("lot %s is not greater than auction's min new lot amount %s", lot.String(), minLot.String())) -} - -// ErrLotTooLarge error for when lot is not smaller than auction's max new lot amount -func ErrLotTooLarge(codespace sdk.CodespaceType, lot sdk.Coin, maxLot sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeLotTooLarge, fmt.Sprintf("lot %s is greater than auction's max new lot amount %s", lot.String(), maxLot.String())) -} - -// ErrCollateralAuctionIsInReversePhase error for when attempting to place a forward bid on a collateral auction in reverse phase -func ErrCollateralAuctionIsInReversePhase(codespace sdk.CodespaceType, id uint64) sdk.Error { - return sdk.NewError(codespace, CodeCollateralAuctionIsInReversePhase, fmt.Sprintf("invalid bid - auction %d is in reverse phase", id)) -} - -// ErrCollateralAuctionIsInForwardPhase error for when attempting to place a reverse bid on a collateral auction in forward phase -func ErrCollateralAuctionIsInForwardPhase(codespace sdk.CodespaceType, id uint64) sdk.Error { - return sdk.NewError(codespace, CodeCollateralAuctionIsInForwardPhase, fmt.Sprintf("invalid bid - auction %d is in forward phase", id)) -} diff --git a/x/auction/types/expected_keepers.go b/x/auction/types/expected_keepers.go index 83141c9e..bb58ef9b 100644 --- a/x/auction/types/expected_keepers.go +++ b/x/auction/types/expected_keepers.go @@ -10,10 +10,10 @@ type SupplyKeeper interface { GetModuleAddress(name string) sdk.AccAddress GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI - SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) sdk.Error - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error - MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error } diff --git a/x/auction/types/msg.go b/x/auction/types/msg.go index 435ad08f..4614f512 100644 --- a/x/auction/types/msg.go +++ b/x/auction/types/msg.go @@ -2,7 +2,9 @@ package types import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // ensure Msg interface compliance at compile time @@ -31,12 +33,12 @@ func (msg MsgPlaceBid) Route() string { return RouterKey } func (msg MsgPlaceBid) Type() string { return "place_bid" } // ValidateBasic does a simple validation check that doesn't require access to state. -func (msg MsgPlaceBid) ValidateBasic() sdk.Error { +func (msg MsgPlaceBid) ValidateBasic() error { if msg.Bidder.Empty() { - return sdk.ErrInvalidAddress("invalid (empty) bidder address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "bidder address cannot be empty") } if !msg.Amount.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid bid amount: %s", msg.Amount)) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "bid amount %s", msg.Amount) } return nil } @@ -51,3 +53,12 @@ func (msg MsgPlaceBid) GetSignBytes() []byte { func (msg MsgPlaceBid) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Bidder} } + +func (msg MsgPlaceBid) String() string { + // String implements the Stringer interface + return fmt.Sprintf(`Place Bid Message: + Auction ID: %d + Bidder: %s + Amount: %s +`, msg.AuctionID, msg.Bidder, msg.Amount) +} diff --git a/x/auction/types/params.go b/x/auction/types/params.go index 74f2037a..89b82aa5 100644 --- a/x/auction/types/params.go +++ b/x/auction/types/params.go @@ -2,13 +2,17 @@ package types import ( "bytes" + "errors" "fmt" "time" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/params/subspace" ) +var emptyDec = sdk.Dec{} + // Defaults for auction params const ( // DefaultMaxAuctionDuration max length of auction @@ -69,11 +73,11 @@ func ParamKeyTable() subspace.KeyTable { // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs. func (p *Params) ParamSetPairs() subspace.ParamSetPairs { return subspace.ParamSetPairs{ - {Key: KeyBidDuration, Value: &p.BidDuration}, - {Key: KeyMaxAuctionDuration, Value: &p.MaxAuctionDuration}, - {Key: KeyIncrementSurplus, Value: &p.IncrementSurplus}, - {Key: KeyIncrementDebt, Value: &p.IncrementDebt}, - {Key: KeyIncrementCollateral, Value: &p.IncrementCollateral}, + params.NewParamSetPair(KeyBidDuration, &p.BidDuration, validateBidDurationParam), + params.NewParamSetPair(KeyMaxAuctionDuration, &p.MaxAuctionDuration, validateMaxAuctionDurationParam), + params.NewParamSetPair(KeyIncrementSurplus, &p.IncrementSurplus, validateIncrementSurplusParam), + params.NewParamSetPair(KeyIncrementDebt, &p.IncrementDebt, validateIncrementDebtParam), + params.NewParamSetPair(KeyIncrementCollateral, &p.IncrementCollateral, validateIncrementCollateralParam), } } @@ -97,26 +101,102 @@ func (p Params) String() string { // Validate checks that the parameters have valid values. func (p Params) Validate() error { - if p.BidDuration < 0 { - return sdk.ErrInternal("bid duration cannot be negative") + if err := validateBidDurationParam(p.BidDuration); err != nil { + return err } - if p.MaxAuctionDuration < 0 { - return sdk.ErrInternal("max auction duration cannot be negative") + + if err := validateMaxAuctionDurationParam(p.MaxAuctionDuration); err != nil { + return err } + if p.BidDuration > p.MaxAuctionDuration { - return sdk.ErrInternal("bid duration param cannot be larger than max auction duration") + return errors.New("bid duration param cannot be larger than max auction duration") } - if p.IncrementSurplus == (sdk.Dec{}) || p.IncrementDebt == (sdk.Dec{}) || p.IncrementCollateral == (sdk.Dec{}) { - return sdk.ErrInternal("auction increment values cannot be nil") + + if err := validateIncrementSurplusParam(p.IncrementSurplus); err != nil { + return err } - if p.IncrementSurplus.IsNegative() { - return sdk.ErrInternal("surplus auction increment cannot be less than zero") + + if err := validateIncrementDebtParam(p.IncrementDebt); err != nil { + return err } - if p.IncrementDebt.IsNegative() { - return sdk.ErrInternal("debt auction increment cannot be less than zero") + + return validateIncrementCollateralParam(p.IncrementCollateral) +} + +func validateBidDurationParam(i interface{}) error { + bidDuration, ok := i.(time.Duration) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) } - if p.IncrementCollateral.IsNegative() { - return sdk.ErrInternal("collateral auction increment cannot be less than zero") + + if bidDuration < 0 { + return fmt.Errorf("bid duration cannot be negative %d", bidDuration) } + + return nil +} + +func validateMaxAuctionDurationParam(i interface{}) error { + maxAuctionDuration, ok := i.(time.Duration) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if maxAuctionDuration < 0 { + return fmt.Errorf("max auction duration cannot be negative %d", maxAuctionDuration) + } + + return nil +} + +func validateIncrementSurplusParam(i interface{}) error { + incrementSurplus, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if incrementSurplus == emptyDec || incrementSurplus.IsNil() { + return errors.New("surplus auction increment cannot be nil or empty") + } + + if incrementSurplus.IsNegative() { + return fmt.Errorf("surplus auction increment cannot be less than zero %s", incrementSurplus) + } + + return nil +} + +func validateIncrementDebtParam(i interface{}) error { + incrementDebt, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if incrementDebt == emptyDec || incrementDebt.IsNil() { + return errors.New("debt auction increment cannot be nil or empty") + } + + if incrementDebt.IsNegative() { + return fmt.Errorf("debt auction increment cannot be less than zero %s", incrementDebt) + } + + return nil +} + +func validateIncrementCollateralParam(i interface{}) error { + incrementCollateral, ok := i.(sdk.Dec) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if incrementCollateral == emptyDec || incrementCollateral.IsNil() { + return errors.New("collateral auction increment cannot be nil or empty") + } + + if incrementCollateral.IsNegative() { + return fmt.Errorf("collateral auction increment cannot be less than zero %s", incrementCollateral) + } + return nil } diff --git a/x/bep3/abci_test.go b/x/bep3/abci_test.go index 37ca6eb2..67da0c39 100644 --- a/x/bep3/abci_test.go +++ b/x/bep3/abci_test.go @@ -3,12 +3,14 @@ package bep3_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtime "github.com/tendermint/tendermint/types/time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/bep3" ) @@ -19,8 +21,8 @@ type ABCITestSuite struct { app app.TestApp ctx sdk.Context addrs []sdk.AccAddress - swapIDs []cmn.HexBytes - randomNumbers []cmn.HexBytes + swapIDs []tmbytes.HexBytes + randomNumbers []tmbytes.HexBytes } func (suite *ABCITestSuite) SetupTest() { @@ -46,8 +48,8 @@ func (suite *ABCITestSuite) SetupTest() { func (suite *ABCITestSuite) ResetKeeper() { suite.keeper = suite.app.GetBep3Keeper() - var swapIDs []cmn.HexBytes - var randomNumbers []cmn.HexBytes + var swapIDs []tmbytes.HexBytes + var randomNumbers []tmbytes.HexBytes for i := 0; i < 10; i++ { // Set up atomic swap variables expireHeight := int64(360) diff --git a/x/bep3/alias.go b/x/bep3/alias.go index c020c3af..81097b2c 100644 --- a/x/bep3/alias.go +++ b/x/bep3/alias.go @@ -1,5 +1,3 @@ -// nolint -// DO NOT EDIT - generated by aliasgen tool (github.com/rhuairahrighairidh/aliasgen) package bep3 import ( @@ -26,24 +24,8 @@ const ( AttributeValueCategory = types.AttributeValueCategory CalcSwapID = types.CalcSwapID ClaimAtomicSwap = types.ClaimAtomicSwap - CodeAssetNotActive = types.CodeAssetNotActive - CodeAssetNotSupported = types.CodeAssetNotSupported - CodeAssetSupplyNotFound = types.CodeAssetSupplyNotFound - CodeAtomicSwapAlreadyExists = types.CodeAtomicSwapAlreadyExists - CodeAtomicSwapNotFound = types.CodeAtomicSwapNotFound - CodeExceedsAvailableSupply = types.CodeExceedsAvailableSupply - CodeExceedsSupplyLimit = types.CodeExceedsSupplyLimit - CodeInvalidClaimSecret = types.CodeInvalidClaimSecret - CodeInvalidCurrentSupply = types.CodeInvalidCurrentSupply - CodeInvalidHeightSpan = types.CodeInvalidHeightSpan - CodeInvalidIncomingSupply = types.CodeInvalidIncomingSupply - CodeInvalidOutgoingSupply = types.CodeInvalidOutgoingSupply - CodeInvalidTimestamp = types.CodeInvalidTimestamp - CodeSwapNotClaimable = types.CodeSwapNotClaimable - CodeSwapNotRefundable = types.CodeSwapNotRefundable Completed = types.Completed CreateAtomicSwap = types.CreateAtomicSwap - DefaultCodespace = types.DefaultCodespace DefaultLongtermStorageDuration = types.DefaultLongtermStorageDuration DefaultParamspace = types.DefaultParamspace DepositAtomicSwap = types.DepositAtomicSwap @@ -144,7 +126,6 @@ type ( AssetSupply = types.AssetSupply AtomicSwap = types.AtomicSwap AtomicSwaps = types.AtomicSwaps - CodeType = types.CodeType GenesisState = types.GenesisState MsgClaimAtomicSwap = types.MsgClaimAtomicSwap MsgCreateAtomicSwap = types.MsgCreateAtomicSwap diff --git a/x/bep3/client/cli/query.go b/x/bep3/client/cli/query.go index 4523970d..a60e52af 100644 --- a/x/bep3/client/cli/query.go +++ b/x/bep3/client/cli/query.go @@ -6,8 +6,8 @@ import ( "strconv" "strings" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" @@ -24,7 +24,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Querying commands for the bep3 module", } - bep3QueryCmd.AddCommand(client.GetCommands( + bep3QueryCmd.AddCommand(flags.GetCommands( QueryCalcSwapIDCmd(queryRoute, cdc), QueryCalcRandomNumberHashCmd(queryRoute, cdc), QueryGetAtomicSwapCmd(queryRoute, cdc), diff --git a/x/bep3/client/cli/tx.go b/x/bep3/client/cli/tx.go index 957b0e8a..9543d5c2 100644 --- a/x/bep3/client/cli/tx.go +++ b/x/bep3/client/cli/tx.go @@ -1,13 +1,14 @@ package cli import ( + "bufio" "encoding/hex" "fmt" "strconv" "strings" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -25,7 +26,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { Short: "bep3 transactions subcommands", } - bep3TxCmd.AddCommand(client.PostCommands( + bep3TxCmd.AddCommand(flags.PostCommands( GetCmdCreateAtomicSwap(cdc), GetCmdClaimAtomicSwap(cdc), GetCmdRefundAtomicSwap(cdc), @@ -43,8 +44,9 @@ func GetCmdCreateAtomicSwap(cdc *codec.Codec) *cobra.Command { version.ClientName, types.ModuleName), Args: cobra.ExactArgs(8), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) from := cliCtx.GetFromAddress() // same as KavaExecutor.DeputyAddress (for cross-chain) to, err := sdk.AccAddressFromBech32(args[0]) @@ -119,8 +121,9 @@ func GetCmdClaimAtomicSwap(cdc *codec.Codec) *cobra.Command { Example: fmt.Sprintf("%s tx %s claim 6682c03cc3856879c8fb98c9733c6b0c30758299138166b6523fe94628b1d3af 56f13e6a5cd397447f8b5f8c82fdb5bbf56127db75269f5cc14e50acd8ac9a4c --from accA", version.ClientName, types.ModuleName), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) from := cliCtx.GetFromAddress() @@ -158,8 +161,9 @@ func GetCmdRefundAtomicSwap(cdc *codec.Codec) *cobra.Command { Example: fmt.Sprintf("%s tx %s refund 6682c03cc3856879c8fb98c9733c6b0c30758299138166b6523fe94628b1d3af --from accA", version.ClientName, types.ModuleName), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) from := cliCtx.GetFromAddress() diff --git a/x/bep3/client/rest/rest.go b/x/bep3/client/rest/rest.go index 02777077..1b5bfb21 100644 --- a/x/bep3/client/rest/rest.go +++ b/x/bep3/client/rest/rest.go @@ -3,10 +3,11 @@ package rest import ( "github.com/gorilla/mux" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/rest" - cmn "github.com/tendermint/tendermint/libs/common" ) // RegisterRoutes registers bep3-related REST handlers to a router @@ -17,30 +18,30 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { // PostCreateSwapReq defines the properties of a swap create request's body type PostCreateSwapReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - From sdk.AccAddress `json:"from" yaml:"from"` - To sdk.AccAddress `json:"to" yaml:"to"` - RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` - SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` - RandomNumberHash cmn.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` - Timestamp int64 `json:"timestamp" yaml:"timestamp"` - Amount sdk.Coins `json:"amount" yaml:"amount"` - ExpectedIncome string `json:"expected_income" yaml:"expected_income"` - HeightSpan int64 `json:"height_span" yaml:"height_span"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + From sdk.AccAddress `json:"from" yaml:"from"` + To sdk.AccAddress `json:"to" yaml:"to"` + RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` + SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` + RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` + Timestamp int64 `json:"timestamp" yaml:"timestamp"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + ExpectedIncome string `json:"expected_income" yaml:"expected_income"` + HeightSpan int64 `json:"height_span" yaml:"height_span"` + CrossChain bool `json:"cross_chain" yaml:"cross_chain"` } // PostClaimSwapReq defines the properties of a swap claim request's body type PostClaimSwapReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - From sdk.AccAddress `json:"from" yaml:"from"` - SwapID cmn.HexBytes `json:"swap_id" yaml:"swap_id"` - RandomNumber cmn.HexBytes `json:"random_number" yaml:"random_number"` + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + From sdk.AccAddress `json:"from" yaml:"from"` + SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` + RandomNumber tmbytes.HexBytes `json:"random_number" yaml:"random_number"` } // PostRefundSwapReq defines the properties of swap refund request's body type PostRefundSwapReq struct { - BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` - From sdk.AccAddress `json:"from" yaml:"from"` - SwapID cmn.HexBytes `json:"swap_id" yaml:"swap_id"` + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + From sdk.AccAddress `json:"from" yaml:"from"` + SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` } diff --git a/x/bep3/genesis.go b/x/bep3/genesis.go index 37f08878..b6d01ad4 100644 --- a/x/bep3/genesis.go +++ b/x/bep3/genesis.go @@ -71,9 +71,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper SupplyKeeper, gs G case Open: // This index expires unclaimed swaps keeper.InsertIntoByBlockIndex(ctx, swap) - incomingSupplies = incomingSupplies.Add(swap.Amount) + incomingSupplies = incomingSupplies.Add(swap.Amount...) case Expired: - incomingSupplies = incomingSupplies.Add(swap.Amount) + incomingSupplies = incomingSupplies.Add(swap.Amount...) case Completed: // This index stores swaps until deletion keeper.InsertIntoLongtermStorage(ctx, swap) @@ -84,9 +84,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper SupplyKeeper, gs G switch swap.Status { case Open: keeper.InsertIntoByBlockIndex(ctx, swap) - outgoingSupplies = outgoingSupplies.Add(swap.Amount) + outgoingSupplies = outgoingSupplies.Add(swap.Amount...) case Expired: - outgoingSupplies = outgoingSupplies.Add(swap.Amount) + outgoingSupplies = outgoingSupplies.Add(swap.Amount...) case Completed: keeper.InsertIntoLongtermStorage(ctx, swap) default: diff --git a/x/bep3/handler.go b/x/bep3/handler.go index 27accffe..fa92f055 100644 --- a/x/bep3/handler.go +++ b/x/bep3/handler.go @@ -1,14 +1,13 @@ package bep3 import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // NewHandler creates an sdk.Handler for all the bep3 type messages func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) switch msg := msg.(type) { case MsgCreateAtomicSwap: @@ -18,19 +17,18 @@ func NewHandler(k Keeper) sdk.Handler { case MsgRefundAtomicSwap: return handleMsgRefundAtomicSwap(ctx, k, msg) default: - errMsg := fmt.Sprintf("unrecognized %s message type: %T", ModuleName, msg) - return sdk.ErrUnknownRequest(errMsg).Result() + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } } // handleMsgCreateAtomicSwap handles requests to create a new AtomicSwap -func handleMsgCreateAtomicSwap(ctx sdk.Context, k Keeper, msg MsgCreateAtomicSwap) sdk.Result { +func handleMsgCreateAtomicSwap(ctx sdk.Context, k Keeper, msg MsgCreateAtomicSwap) (*sdk.Result, error) { err := k.CreateAtomicSwap(ctx, msg.RandomNumberHash, msg.Timestamp, msg.HeightSpan, msg.From, msg.To, msg.SenderOtherChain, msg.RecipientOtherChain, msg.Amount, msg.ExpectedIncome, msg.CrossChain) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -41,17 +39,17 @@ func handleMsgCreateAtomicSwap(ctx sdk.Context, k Keeper, msg MsgCreateAtomicSwa ), ) - return sdk.Result{ + return &sdk.Result{ Events: ctx.EventManager().Events(), - } + }, nil } // handleMsgClaimAtomicSwap handles requests to claim funds in an active AtomicSwap -func handleMsgClaimAtomicSwap(ctx sdk.Context, k Keeper, msg MsgClaimAtomicSwap) sdk.Result { +func handleMsgClaimAtomicSwap(ctx sdk.Context, k Keeper, msg MsgClaimAtomicSwap) (*sdk.Result, error) { err := k.ClaimAtomicSwap(ctx, msg.From, msg.SwapID, msg.RandomNumber) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -62,17 +60,17 @@ func handleMsgClaimAtomicSwap(ctx sdk.Context, k Keeper, msg MsgClaimAtomicSwap) ), ) - return sdk.Result{ + return &sdk.Result{ Events: ctx.EventManager().Events(), - } + }, nil } // handleMsgRefundAtomicSwap handles requests to refund an active AtomicSwap -func handleMsgRefundAtomicSwap(ctx sdk.Context, k Keeper, msg MsgRefundAtomicSwap) sdk.Result { +func handleMsgRefundAtomicSwap(ctx sdk.Context, k Keeper, msg MsgRefundAtomicSwap) (*sdk.Result, error) { err := k.RefundAtomicSwap(ctx, msg.From, msg.SwapID) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -83,7 +81,7 @@ func handleMsgRefundAtomicSwap(ctx sdk.Context, k Keeper, msg MsgRefundAtomicSwa ), ) - return sdk.Result{ + return &sdk.Result{ Events: ctx.EventManager().Events(), - } + }, nil } diff --git a/x/bep3/handler_test.go b/x/bep3/handler_test.go index b905b23d..888d819d 100644 --- a/x/bep3/handler_test.go +++ b/x/bep3/handler_test.go @@ -1,17 +1,16 @@ package bep3_test import ( - "encoding/hex" - "fmt" - "strings" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtime "github.com/tendermint/tendermint/types/time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/bep3" ) @@ -47,7 +46,7 @@ func (suite *HandlerTestSuite) SetupTest() { suite.ctx = ctx } -func (suite *HandlerTestSuite) AddAtomicSwap() (cmn.HexBytes, cmn.HexBytes) { +func (suite *HandlerTestSuite) AddAtomicSwap() (tmbytes.HexBytes, tmbytes.HexBytes) { expireHeight := int64(360) amount := cs(c("bnb", int64(50000))) timestamp := ts(0) @@ -74,8 +73,9 @@ func (suite *HandlerTestSuite) TestMsgCreateAtomicSwap() { suite.addrs[0], suite.addrs[2], TestRecipientOtherChain, TestSenderOtherChain, randomNumberHash, timestamp, amount, amount.String(), int64(300), true) - res := suite.handler(suite.ctx, msg) - suite.True(res.IsOK()) + res, err := suite.handler(suite.ctx, msg) + suite.Require().NoError(err) + suite.Require().NotNil(res) } func (suite *HandlerTestSuite) TestMsgClaimAtomicSwap() { @@ -84,15 +84,16 @@ func (suite *HandlerTestSuite) TestMsgClaimAtomicSwap() { badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber.Bytes(), ts(0)) badSwapID := bep3.CalculateSwapID(badRandomNumberHash, suite.addrs[0], TestSenderOtherChain) badMsg := bep3.NewMsgClaimAtomicSwap(suite.addrs[0], badSwapID, badRandomNumber.Bytes()) - badRes := suite.handler(suite.ctx, badMsg) - suite.False(badRes.IsOK()) - suite.True(strings.Contains(badRes.Log, fmt.Sprintf("AtomicSwap %s was not found", hex.EncodeToString(badSwapID)))) + badRes, err := suite.handler(suite.ctx, badMsg) + suite.Require().Error(err) + suite.Require().Nil(badRes) // Add an atomic swap before attempting new claim msg swapID, randomNumber := suite.AddAtomicSwap() msg := bep3.NewMsgClaimAtomicSwap(suite.addrs[0], swapID, randomNumber) - res := suite.handler(suite.ctx, msg) - suite.True(res.IsOK()) + res, err := suite.handler(suite.ctx, msg) + suite.Require().NoError(err) + suite.Require().NotNil(res) } func (suite *HandlerTestSuite) TestMsgRefundAtomicSwap() { @@ -101,30 +102,31 @@ func (suite *HandlerTestSuite) TestMsgRefundAtomicSwap() { badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber.Bytes(), ts(0)) badSwapID := bep3.CalculateSwapID(badRandomNumberHash, suite.addrs[0], TestSenderOtherChain) badMsg := bep3.NewMsgRefundAtomicSwap(suite.addrs[0], badSwapID) - badRes := suite.handler(suite.ctx, badMsg) - suite.False(badRes.IsOK()) - suite.True(strings.Contains(badRes.Log, fmt.Sprintf("AtomicSwap %s was not found", hex.EncodeToString(badSwapID)))) + badRes, err := suite.handler(suite.ctx, badMsg) + suite.Require().Error(err) + suite.Require().Nil(badRes) // Add an atomic swap and build refund msg swapID, _ := suite.AddAtomicSwap() msg := bep3.NewMsgRefundAtomicSwap(suite.addrs[0], swapID) // Attempt to refund active atomic swap - res1 := suite.handler(suite.ctx, msg) - suite.True(strings.Contains(res1.Log, "atomic swap is still active and cannot be refunded")) - suite.False(res1.IsOK()) + res1, err := suite.handler(suite.ctx, msg) + suite.Require().Error(err) + suite.Require().Nil(res1) // Expire the atomic swap with begin blocker and attempt refund laterCtx := suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400) bep3.BeginBlocker(laterCtx, suite.keeper) - res2 := suite.handler(laterCtx, msg) - suite.True(res2.IsOK()) + res2, err := suite.handler(laterCtx, msg) + suite.Require().NoError(err) + suite.Require().NotNil(res2) } func (suite *HandlerTestSuite) TestInvalidMsg() { - res := suite.handler(suite.ctx, sdk.NewTestMsg()) - suite.False(res.IsOK()) - suite.True(strings.Contains(res.Log, "unrecognized bep3 message type")) + res, err := suite.handler(suite.ctx, sdk.NewTestMsg()) + suite.Require().Error(err) + suite.Require().Nil(res) } func TestHandlerTestSuite(t *testing.T) { diff --git a/x/bep3/keeper/asset.go b/x/bep3/keeper/asset.go index df586cee..cf8f1ef7 100644 --- a/x/bep3/keeper/asset.go +++ b/x/bep3/keeper/asset.go @@ -2,19 +2,21 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/kava-labs/kava/x/bep3/types" ) // IncrementCurrentAssetSupply increments an asset's supply by the coin -func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Resulting current supply must be under asset's limit if !supply.Limit.IsGTE(supply.CurrentSupply.Add(coin)) { - return types.ErrExceedsSupplyLimit(k.codespace, coin, supply.CurrentSupply, supply.Limit) + return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.Limit) } supply.CurrentSupply = supply.CurrentSupply.Add(coin) @@ -23,16 +25,16 @@ func (k Keeper) IncrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk. } // DecrementCurrentAssetSupply decrement an asset's supply by the coin -func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Resulting current supply must be greater than or equal to 0 // Use sdk.Int instead of sdk.Coin to prevent panic if true if supply.CurrentSupply.Amount.Sub(coin.Amount).IsNegative() { - return types.ErrInvalidCurrentSupply(k.codespace, coin, supply.CurrentSupply) + return sdkerrors.Wrapf(types.ErrInvalidCurrentSupply, "decrease %s, asset supply %s", coin, supply.CurrentSupply) } supply.CurrentSupply = supply.CurrentSupply.Sub(coin) @@ -41,16 +43,16 @@ func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk. } // IncrementIncomingAssetSupply increments an asset's incoming supply -func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Result of (current + incoming + amount) must be under asset's limit totalSupply := supply.CurrentSupply.Add(supply.IncomingSupply) if !supply.Limit.IsGTE(totalSupply.Add(coin)) { - return types.ErrExceedsSupplyLimit(k.codespace, coin, totalSupply, supply.Limit) + return sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supply.Limit) } supply.IncomingSupply = supply.IncomingSupply.Add(coin) @@ -59,16 +61,16 @@ func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk } // DecrementIncomingAssetSupply decrements an asset's incoming supply -func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Resulting incoming supply must be greater than or equal to 0 // Use sdk.Int instead of sdk.Coin to prevent panic if true if supply.IncomingSupply.Amount.Sub(coin.Amount).IsNegative() { - return types.ErrInvalidIncomingSupply(k.codespace, coin, supply.IncomingSupply) + return sdkerrors.Wrapf(types.ErrInvalidIncomingSupply, "decrease %s, incoming supply %s", coin, supply.IncomingSupply) } supply.IncomingSupply = supply.IncomingSupply.Sub(coin) @@ -77,15 +79,15 @@ func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk } // IncrementOutgoingAssetSupply increments an asset's outoing supply -func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Result of (outgoing + amount) must be less than current supply if !supply.CurrentSupply.IsGTE(supply.OutgoingSupply.Add(coin)) { - return types.ErrExceedsAvailableSupply(k.codespace, coin, + return sdkerrors.Wrapf(types.ErrExceedsAvailableSupply, "swap amount %s, available supply %s", coin, supply.CurrentSupply.Amount.Sub(supply.OutgoingSupply.Amount)) } @@ -95,16 +97,16 @@ func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk } // DecrementOutgoingAssetSupply decrements an asset's outoing supply -func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error { supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom)) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } // Resulting outgoing supply must be greater than or equal to 0 // Use sdk.Int instead of sdk.Coin to prevent panic if true if supply.OutgoingSupply.Amount.Sub(coin.Amount).IsNegative() { - return types.ErrInvalidOutgoingSupply(k.codespace, coin, supply.OutgoingSupply) + return sdkerrors.Wrapf(types.ErrInvalidOutgoingSupply, "decrease %s, outgoing supply %s", coin, supply.OutgoingSupply) } supply.OutgoingSupply = supply.OutgoingSupply.Sub(coin) diff --git a/x/bep3/keeper/keeper.go b/x/bep3/keeper/keeper.go index 0a0641af..3a800422 100644 --- a/x/bep3/keeper/keeper.go +++ b/x/bep3/keeper/keeper.go @@ -17,20 +17,23 @@ type Keeper struct { cdc *codec.Codec paramSubspace subspace.Subspace supplyKeeper types.SupplyKeeper - codespace sdk.CodespaceType } // NewKeeper creates a bep3 keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk types.SupplyKeeper, paramstore subspace.Subspace, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk types.SupplyKeeper, paramstore subspace.Subspace) Keeper { if addr := sk.GetModuleAddress(types.ModuleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", types.ModuleName)) } + + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } + keeper := Keeper{ key: key, cdc: cdc, - paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()), + paramSubspace: paramstore, supplyKeeper: sk, - codespace: codespace, } return keeper } diff --git a/x/bep3/keeper/params.go b/x/bep3/keeper/params.go index 7b8cc88c..f3dc1abf 100644 --- a/x/bep3/keeper/params.go +++ b/x/bep3/keeper/params.go @@ -2,6 +2,8 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/kava-labs/kava/x/bep3/types" ) @@ -63,13 +65,13 @@ func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam, } // ValidateLiveAsset checks if an asset is both supported and active -func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) sdk.Error { +func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error { asset, found := k.GetAssetByDenom(ctx, coin.Denom) if !found { - return types.ErrAssetNotSupported(k.codespace, coin.Denom) + return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom) } if !asset.Active { - return types.ErrAssetNotActive(k.codespace, asset.Denom) + return sdkerrors.Wrap(types.ErrAssetNotActive, asset.Denom) } return nil } diff --git a/x/bep3/keeper/params_test.go b/x/bep3/keeper/params_test.go index 2e3fd4b7..3c4bae7d 100644 --- a/x/bep3/keeper/params_test.go +++ b/x/bep3/keeper/params_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -92,7 +93,7 @@ func (suite *AssetTestSuite) TestValidateLiveAsset() { testCases := []struct { name string args args - expectedError sdk.CodeType + expectedError error expectPass bool }{ { @@ -100,7 +101,7 @@ func (suite *AssetTestSuite) TestValidateLiveAsset() { args{ coin: c("bnb", 1), }, - sdk.CodeType(0), + nil, true, }, { @@ -108,7 +109,7 @@ func (suite *AssetTestSuite) TestValidateLiveAsset() { args{ coin: c("bad", 1), }, - types.CodeAssetNotSupported, + types.ErrAssetNotSupported, false, }, { @@ -116,7 +117,7 @@ func (suite *AssetTestSuite) TestValidateLiveAsset() { args{ coin: c("inc", 1), }, - types.CodeAssetNotActive, + types.ErrAssetNotActive, false, }, } @@ -127,10 +128,10 @@ func (suite *AssetTestSuite) TestValidateLiveAsset() { err := suite.keeper.ValidateLiveAsset(suite.ctx, tc.args.coin) if tc.expectPass { - suite.NoError(err) + suite.Require().NoError(err) } else { - suite.Error(err) - suite.Equal(tc.expectedError, err.Result().Code) + suite.Require().Error(err) + suite.Require().True(errors.Is(err, tc.expectedError)) } }) } diff --git a/x/bep3/keeper/querier.go b/x/bep3/keeper/querier.go index a64db015..8cc573b6 100644 --- a/x/bep3/keeper/querier.go +++ b/x/bep3/keeper/querier.go @@ -1,15 +1,18 @@ package keeper import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/kava-labs/kava/x/bep3/types" - abci "github.com/tendermint/tendermint/abci/types" ) // NewQuerier is the module level router for state queries func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { switch path[0] { case types.QueryGetAssetSupply: return queryAssetSupply(ctx, req, keeper) @@ -20,57 +23,57 @@ func NewQuerier(keeper Keeper) sdk.Querier { case types.QueryGetParams: return queryGetParams(ctx, req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown bep3 query endpoint") + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) } } } -func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Decode request var requestParams types.QueryAssetSupply - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } assetSupply, found := keeper.GetAssetSupply(ctx, []byte(requestParams.Denom)) if !found { - return nil, sdk.ErrInternal("Not found") + return nil, sdkerrors.Wrap(types.ErrAssetSupplyNotFound, string(requestParams.Denom)) } // Encode results - bz, err := codec.MarshalJSONIndent(keeper.cdc, assetSupply) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, assetSupply) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryAtomicSwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryAtomicSwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Decode request var requestParams types.QueryAtomicSwapByID - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } // Lookup atomic swap atomicSwap, found := keeper.GetAtomicSwap(ctx, requestParams.SwapID) if !found { - return nil, sdk.ErrInternal("Not found") + return nil, sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%d", requestParams.SwapID) } // Encode results - bz, err := codec.MarshalJSONIndent(keeper.cdc, atomicSwap) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, atomicSwap) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryAtomicSwaps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { +func queryAtomicSwaps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err error) { var swaps types.AtomicSwaps keeper.IterateAtomicSwaps(ctx, func(s types.AtomicSwap) bool { @@ -78,23 +81,23 @@ func queryAtomicSwaps(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (re return false }) - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, swaps) + bz, err2 := codec.MarshalJSONIndent(types.ModuleCdc, swaps) if err2 != nil { - return nil, sdk.ErrInternal("could not marshal result to JSON") + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query params in the bep3 store -func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Get params params := keeper.GetParams(ctx) // Encode results - bz, err := codec.MarshalJSONIndent(keeper.cdc, params) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, params) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } diff --git a/x/bep3/keeper/querier_test.go b/x/bep3/keeper/querier_test.go index c7355e4d..ddf705e5 100644 --- a/x/bep3/keeper/querier_test.go +++ b/x/bep3/keeper/querier_test.go @@ -5,14 +5,17 @@ import ( "strings" "testing" + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmtime "github.com/tendermint/tendermint/types/time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/bep3/keeper" "github.com/kava-labs/kava/x/bep3/types" - "github.com/stretchr/testify/suite" - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - tmtime "github.com/tendermint/tendermint/types/time" ) const ( @@ -27,7 +30,7 @@ type QuerierTestSuite struct { querier sdk.Querier addrs []sdk.AccAddress isSupplyDenom map[string]bool - swapIDs []cmn.HexBytes + swapIDs []tmbytes.HexBytes isSwapID map[string]bool } @@ -55,7 +58,7 @@ func (suite *QuerierTestSuite) SetupTest() { suite.addrs = addrs // Create atomic swaps and save IDs - var swapIDs []cmn.HexBytes + var swapIDs []tmbytes.HexBytes isSwapID := make(map[string]bool) for i := 0; i < 10; i++ { // Set up atomic swap variables @@ -87,7 +90,7 @@ func (suite *QuerierTestSuite) TestQueryAssetSupply() { denom := "bnb" query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAssetSupply}, "/"), - Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupply(cmn.HexBytes(denom))), + Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupply(tmbytes.HexBytes(denom))), } // Execute query and check the []byte result diff --git a/x/bep3/keeper/swap.go b/x/bep3/keeper/swap.go index cbc9a26c..21fd2e08 100644 --- a/x/bep3/keeper/swap.go +++ b/x/bep3/keeper/swap.go @@ -7,40 +7,37 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/kava-labs/kava/x/bep3/types" ) // CreateAtomicSwap creates a new AtomicSwap func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, timestamp int64, heightSpan int64, sender sdk.AccAddress, recipient sdk.AccAddress, senderOtherChain, recipientOtherChain string, - amount sdk.Coins, expectedIncome string, crossChain bool) sdk.Error { + amount sdk.Coins, expectedIncome string, crossChain bool) error { // Confirm that this is not a duplicate swap swapID := types.CalculateSwapID(randomNumberHash, sender, senderOtherChain) _, found := k.GetAtomicSwap(ctx, swapID) if found { - return types.ErrAtomicSwapAlreadyExists(k.codespace, swapID) + return sdkerrors.Wrap(types.ErrAtomicSwapAlreadyExists, hex.EncodeToString(swapID)) } // The heightSpan period should be more than 10 minutes and less than one week // Assume average block time interval is 10 second. 10 mins = 60 blocks, 1 week = 60480 blocks if heightSpan < k.GetMinBlockLock(ctx) || heightSpan > k.GetMaxBlockLock(ctx) { - return types.ErrInvalidHeightSpan(k.codespace, heightSpan, k.GetMinBlockLock(ctx), k.GetMaxBlockLock(ctx)) + return sdkerrors.Wrapf(types.ErrInvalidHeightSpan, "height span %d, range %d - %d", heightSpan, k.GetMinBlockLock(ctx), k.GetMaxBlockLock(ctx)) } // Unix timestamp must be in range [-15 mins, 30 mins] of the current time pastTimestampLimit := ctx.BlockTime().Add(time.Duration(-15) * time.Minute).Unix() futureTimestampLimit := ctx.BlockTime().Add(time.Duration(30) * time.Minute).Unix() if timestamp < pastTimestampLimit || timestamp >= futureTimestampLimit { - return types.ErrInvalidTimestamp(k.codespace) - } - - // Sanity check on recipient address - if recipient.Empty() { - return sdk.ErrInvalidAddress("invalid (empty) recipient address") + return sdkerrors.Wrap(types.ErrInvalidTimestamp, time.Unix(timestamp, 0).UTC().String()) } if len(amount) != 1 { - return sdk.ErrInternal("amount must contain exactly one coin") + return fmt.Errorf("amount must contain exactly one coin") } err := k.ValidateLiveAsset(ctx, amount[0]) @@ -58,17 +55,15 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times switch direction { case types.Incoming: - err := k.IncrementIncomingAssetSupply(ctx, amount[0]) - if err != nil { - return err - } + err = k.IncrementIncomingAssetSupply(ctx, amount[0]) case types.Outgoing: - err := k.IncrementOutgoingAssetSupply(ctx, amount[0]) - if err != nil { - return err - } + err = k.IncrementOutgoingAssetSupply(ctx, amount[0]) default: - return sdk.ErrInternal("invalid swap direction") + err = fmt.Errorf("invalid swap direction: %s", direction.String()) + } + + if err != nil { + return err } // Transfer coins to module @@ -86,16 +81,16 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCreateAtomicSwap, - sdk.NewAttribute(types.AttributeKeySender, fmt.Sprintf("%s", atomicSwap.Sender)), - sdk.NewAttribute(types.AttributeKeyRecipient, fmt.Sprintf("%s", atomicSwap.Recipient)), - sdk.NewAttribute(types.AttributeKeyAtomicSwapID, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.GetSwapID()))), - sdk.NewAttribute(types.AttributeKeyRandomNumberHash, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.RandomNumberHash))), + sdk.NewAttribute(types.AttributeKeySender, atomicSwap.Sender.String()), + sdk.NewAttribute(types.AttributeKeyRecipient, atomicSwap.Recipient.String()), + sdk.NewAttribute(types.AttributeKeyAtomicSwapID, hex.EncodeToString(atomicSwap.GetSwapID())), + sdk.NewAttribute(types.AttributeKeyRandomNumberHash, hex.EncodeToString(atomicSwap.RandomNumberHash)), sdk.NewAttribute(types.AttributeKeyTimestamp, fmt.Sprintf("%d", atomicSwap.Timestamp)), - sdk.NewAttribute(types.AttributeKeySenderOtherChain, fmt.Sprintf("%s", atomicSwap.SenderOtherChain)), + sdk.NewAttribute(types.AttributeKeySenderOtherChain, atomicSwap.SenderOtherChain), sdk.NewAttribute(types.AttributeKeyExpireHeight, fmt.Sprintf("%d", atomicSwap.ExpireHeight)), - sdk.NewAttribute(types.AttributeKeyAmount, fmt.Sprintf("%s", atomicSwap.Amount[0].String())), - sdk.NewAttribute(types.AttributeKeyExpectedIncome, fmt.Sprintf("%s", expectedIncome)), - sdk.NewAttribute(types.AttributeKeyDirection, fmt.Sprintf("%s", atomicSwap.Direction.String())), + sdk.NewAttribute(types.AttributeKeyAmount, atomicSwap.Amount[0].String()), + sdk.NewAttribute(types.AttributeKeyExpectedIncome, expectedIncome), + sdk.NewAttribute(types.AttributeKeyDirection, atomicSwap.Direction.String()), ), ) @@ -105,15 +100,15 @@ func (k Keeper) CreateAtomicSwap(ctx sdk.Context, randomNumberHash []byte, times } // ClaimAtomicSwap validates a claim attempt, and if successful, sends the escrowed amount and closes the AtomicSwap -func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte, randomNumber []byte) sdk.Error { +func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte, randomNumber []byte) error { atomicSwap, found := k.GetAtomicSwap(ctx, swapID) if !found { - return types.ErrAtomicSwapNotFound(k.codespace, swapID) + return sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%d", swapID) } // Only open atomic swaps can be claimed if atomicSwap.Status != types.Open { - return types.ErrSwapNotClaimable(k.codespace) + return types.ErrSwapNotClaimable } // Calculate hashed secret using submitted number @@ -122,34 +117,33 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b // Confirm that secret unlocks the atomic swap if !bytes.Equal(hashedSecret, atomicSwap.GetSwapID()) { - return types.ErrInvalidClaimSecret(k.codespace, hashedSecret, atomicSwap.GetSwapID()) + return sdkerrors.Wrapf(types.ErrInvalidClaimSecret, "%s ≠ %s", hex.EncodeToString(hashedSecret), hex.EncodeToString(atomicSwap.GetSwapID())) } + var err error switch atomicSwap.Direction { case types.Incoming: err := k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0]) if err != nil { - return err + break } err = k.IncrementCurrentAssetSupply(ctx, atomicSwap.Amount[0]) - if err != nil { - return err - } case types.Outgoing: - err := k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0]) + err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0]) if err != nil { - return err + break } err = k.DecrementCurrentAssetSupply(ctx, atomicSwap.Amount[0]) - if err != nil { - return err - } default: - return sdk.ErrInternal("invalid swap direction") + err = fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String()) + } + + if err != nil { + return err } // Send intended recipient coins - err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Recipient, atomicSwap.Amount) + err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Recipient, atomicSwap.Amount) if err != nil { return err } @@ -158,11 +152,11 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeClaimAtomicSwap, - sdk.NewAttribute(types.AttributeKeyClaimSender, fmt.Sprintf("%s", from)), - sdk.NewAttribute(types.AttributeKeyRecipient, fmt.Sprintf("%s", atomicSwap.Recipient)), - sdk.NewAttribute(types.AttributeKeyAtomicSwapID, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.GetSwapID()))), - sdk.NewAttribute(types.AttributeKeyRandomNumberHash, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.RandomNumberHash))), - sdk.NewAttribute(types.AttributeKeyRandomNumber, fmt.Sprintf("%s", hex.EncodeToString(randomNumber))), + sdk.NewAttribute(types.AttributeKeyClaimSender, from.String()), + sdk.NewAttribute(types.AttributeKeyRecipient, atomicSwap.Recipient.String()), + sdk.NewAttribute(types.AttributeKeyAtomicSwapID, hex.EncodeToString(atomicSwap.GetSwapID())), + sdk.NewAttribute(types.AttributeKeyRandomNumberHash, hex.EncodeToString(atomicSwap.RandomNumberHash)), + sdk.NewAttribute(types.AttributeKeyRandomNumber, hex.EncodeToString(randomNumber)), ), ) @@ -178,33 +172,32 @@ func (k Keeper) ClaimAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []b } // RefundAtomicSwap refunds an AtomicSwap, sending assets to the original sender and closing the AtomicSwap -func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte) sdk.Error { +func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID []byte) error { atomicSwap, found := k.GetAtomicSwap(ctx, swapID) if !found { - return types.ErrAtomicSwapNotFound(k.codespace, swapID) + return sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%s", swapID) } // Only expired swaps may be refunded if atomicSwap.Status != types.Expired { - return types.ErrSwapNotRefundable(k.codespace) + return types.ErrSwapNotRefundable } + var err error switch atomicSwap.Direction { case types.Incoming: - err := k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0]) - if err != nil { - return err - } + err = k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0]) case types.Outgoing: - err := k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0]) - if err != nil { - return err - } + err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0]) default: - return sdk.ErrInternal("invalid swap direction") + err = fmt.Errorf("invalid swap direction: %s", atomicSwap.Direction.String()) + } + + if err != nil { + return err } // Refund coins to original swap sender - err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Sender, atomicSwap.Amount) + err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, atomicSwap.Sender, atomicSwap.Amount) if err != nil { return err } @@ -231,7 +224,7 @@ func (k Keeper) RefundAtomicSwap(ctx sdk.Context, from sdk.AccAddress, swapID [] } // UpdateExpiredAtomicSwaps finds all AtomicSwaps that are past (or at) their ending times and expires them. -func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) sdk.Error { +func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) error { var expiredSwaps [][]byte k.IterateAtomicSwapsByBlock(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { expiredSwaps = append(expiredSwaps, id) @@ -249,7 +242,7 @@ func (k Keeper) UpdateExpiredAtomicSwaps(ctx sdk.Context) sdk.Error { } // DeleteClosedAtomicSwapsFromLongtermStorage removes swaps one week after completion -func (k Keeper) DeleteClosedAtomicSwapsFromLongtermStorage(ctx sdk.Context) sdk.Error { +func (k Keeper) DeleteClosedAtomicSwapsFromLongtermStorage(ctx sdk.Context) error { var swapsToDelete [][]byte k.IterateAtomicSwapsLongtermStorage(ctx, uint64(ctx.BlockHeight()), func(id []byte) bool { swapsToDelete = append(swapsToDelete, id) diff --git a/x/bep3/keeper/swap_test.go b/x/bep3/keeper/swap_test.go index 7cfb1447..1440e3e2 100644 --- a/x/bep3/keeper/swap_test.go +++ b/x/bep3/keeper/swap_test.go @@ -5,15 +5,18 @@ import ( "testing" "time" + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmtime "github.com/tendermint/tendermint/types/time" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/bep3" "github.com/kava-labs/kava/x/bep3/keeper" "github.com/kava-labs/kava/x/bep3/types" - "github.com/stretchr/testify/suite" - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" - tmtime "github.com/tendermint/tendermint/types/time" ) type AtomicSwapTestSuite struct { @@ -25,9 +28,9 @@ type AtomicSwapTestSuite struct { deputy sdk.AccAddress addrs []sdk.AccAddress timestamps []int64 - randomNumberHashes []cmn.HexBytes - swapIDs []cmn.HexBytes - randomNumbers []cmn.HexBytes + randomNumberHashes []tmbytes.HexBytes + swapIDs []tmbytes.HexBytes + randomNumbers []tmbytes.HexBytes } const ( @@ -65,8 +68,8 @@ func (suite *AtomicSwapTestSuite) SetupTest() { func (suite *AtomicSwapTestSuite) GenerateSwapDetails() { var timestamps []int64 - var randomNumberHashes []cmn.HexBytes - var randomNumbers []cmn.HexBytes + var randomNumberHashes []tmbytes.HexBytes + var randomNumbers []tmbytes.HexBytes for i := 0; i < 10; i++ { // Set up atomic swap details timestamp := ts(i) diff --git a/x/bep3/module.go b/x/bep3/module.go index c11054c3..bf675bff 100644 --- a/x/bep3/module.go +++ b/x/bep3/module.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" sim "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -21,7 +22,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic defines the basic application module used by the bep3 module. @@ -70,40 +71,21 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the auction module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for auction module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the auction module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams creates randomized auction param changes for the simulator. -func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { - return simulation.ParamChanges(r) -} - -//____________________________________________________________________________ - // AppModule implements the sdk.AppModule interface. type AppModule struct { AppModuleBasic - AppModuleSimulation - keeper Keeper - supplyKeeper SupplyKeeper + keeper Keeper + accountKeeper auth.AccountKeeper + supplyKeeper SupplyKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, supplyKeeper SupplyKeeper) AppModule { +func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, supplyKeeper SupplyKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, + accountKeeper: accountKeeper, supplyKeeper: supplyKeeper, } } @@ -161,3 +143,30 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// GenerateGenesisState creates a randomized GenState of the bep3 module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because bep3 has no params. +func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for bep3 module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the bep3 module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.accountKeeper, am.keeper) +} diff --git a/x/bep3/simulation/decoder.go b/x/bep3/simulation/decoder.go index 351b0b00..8fc8dd61 100644 --- a/x/bep3/simulation/decoder.go +++ b/x/bep3/simulation/decoder.go @@ -4,14 +4,16 @@ import ( "bytes" "fmt" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/kv" + "github.com/cosmos/cosmos-sdk/codec" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/kava-labs/kava/x/bep3/types" ) // DecodeStore unmarshals the KVPair's Value to the module's corresponding type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Equal(kvA.Key[:1], types.AtomicSwapKeyPrefix): var swapA, swapB types.AtomicSwap @@ -27,8 +29,8 @@ func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { case bytes.Equal(kvA.Key[:1], types.AtomicSwapByBlockPrefix), bytes.Equal(kvA.Key[:1], types.AtomicSwapLongtermStoragePrefix): - var bytesA cmn.HexBytes = kvA.Value - var bytesB cmn.HexBytes = kvA.Value + var bytesA tmbytes.HexBytes = kvA.Value + var bytesB tmbytes.HexBytes = kvA.Value return fmt.Sprintf("%s\n%s", bytesA.String(), bytesB.String()) default: diff --git a/x/bep3/simulation/decoder_test.go b/x/bep3/simulation/decoder_test.go index e9fc0a75..a5b6ce08 100644 --- a/x/bep3/simulation/decoder_test.go +++ b/x/bep3/simulation/decoder_test.go @@ -6,7 +6,8 @@ import ( "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tendermint/libs/common" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -27,14 +28,14 @@ func TestDecodeDistributionStore(t *testing.T) { oneCoin := sdk.NewCoin("coin", sdk.OneInt()) swap := types.NewAtomicSwap(sdk.Coins{oneCoin}, nil, 10, 100, nil, nil, "otherChainSender", "otherChainRec", 200, types.Completed, true, types.Outgoing) supply := types.AssetSupply{Denom: "coin", IncomingSupply: oneCoin, OutgoingSupply: oneCoin, CurrentSupply: oneCoin, Limit: oneCoin} - bz := cmn.HexBytes([]byte{1, 2}) + bz := tmbytes.HexBytes([]byte{1, 2}) - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: types.AtomicSwapKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(swap)}, - cmn.KVPair{Key: types.AssetSupplyKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(supply)}, - cmn.KVPair{Key: types.AtomicSwapByBlockPrefix, Value: bz}, - cmn.KVPair{Key: types.AtomicSwapByBlockPrefix, Value: bz}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: types.AtomicSwapKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(swap)}, + kv.Pair{Key: types.AssetSupplyKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(supply)}, + kv.Pair{Key: types.AtomicSwapByBlockPrefix, Value: bz}, + kv.Pair{Key: types.AtomicSwapByBlockPrefix, Value: bz}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/bep3/simulation/genesis.go b/x/bep3/simulation/genesis.go index c05dad1d..bfac2d88 100644 --- a/x/bep3/simulation/genesis.go +++ b/x/bep3/simulation/genesis.go @@ -25,13 +25,14 @@ const ( var ( MaxSupplyLimit = sdk.NewInt(1000000000000) - Accs []simulation.Account + accs []simulation.Account ConsistentDenoms = [3]string{"bnb", "xrp", "btc"} ) -// GenBnbDeputyAddress randomized BnbDeputyAddress -func GenBnbDeputyAddress(r *rand.Rand) sdk.AccAddress { - return simulation.RandomAcc(r, Accs).Address +// GenRandBnbDeputy randomized BnbDeputyAddress +func GenRandBnbDeputy(r *rand.Rand) simulation.Account { + acc, _ := simulation.RandomAcc(r, accs) + return acc } // GenMinBlockLock randomized MinBlockLock @@ -77,7 +78,7 @@ func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam { // RandomizedGenState generates a random GenesisState func RandomizedGenState(simState *module.SimulationState) { - Accs = simState.Accounts + accs = simState.Accounts bep3Genesis := loadRandomBep3GenState(simState) fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, bep3Genesis)) @@ -90,13 +91,13 @@ func RandomizedGenState(simState *module.SimulationState) { var supplyGenesis supply.GenesisState simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis) for _, deputyCoin := range totalCoins { - supplyGenesis.Supply = supplyGenesis.Supply.Add(deputyCoin) + supplyGenesis.Supply = supplyGenesis.Supply.Add(deputyCoin...) } simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis) } func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState { - bnbDeputyAddress := simulation.RandomAcc(simState.Rand, simState.Accounts).Address + bnbDeputy := GenRandBnbDeputy(simState.Rand) // min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100 minBlockLock := int64(types.AbsoluteMinimumBlockLock) @@ -110,7 +111,7 @@ func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState bep3Genesis := types.GenesisState{ Params: types.Params{ - BnbDeputyAddress: bnbDeputyAddress, + BnbDeputyAddress: bnbDeputy.Address, MinBlockLock: minBlockLock, MaxBlockLock: maxBlockLock, SupportedAssets: supportedAssets, @@ -134,7 +135,7 @@ func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.Genesi var totalCoins []sdk.Coins for _, asset := range bep3Genesis.Params.SupportedAssets { assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.Limit)) - if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin)); err != nil { + if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin...)); err != nil { panic(err) } totalCoins = append(totalCoins, assetCoin) diff --git a/x/bep3/simulation/operations.go b/x/bep3/simulation/operations.go new file mode 100644 index 00000000..0f3592f5 --- /dev/null +++ b/x/bep3/simulation/operations.go @@ -0,0 +1,210 @@ +package simulation + +import ( + "fmt" + "math" + "math/rand" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/simulation" + + appparams "github.com/kava-labs/kava/app/params" + "github.com/kava-labs/kava/x/bep3/keeper" + "github.com/kava-labs/kava/x/bep3/types" +) + +var ( + noOpMsg = simulation.NoOpMsg(types.ModuleName) +) + +// Simulation operation weights constants +const ( + OpWeightMsgCreateAtomicSwap = "op_weight_msg_create_atomic_swap" +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simulation.AppParams, cdc *codec.Codec, ak auth.AccountKeeper, k keeper.Keeper, +) simulation.WeightedOperations { + var weightCreateAtomicSwap int + + appParams.GetOrGenerate(cdc, OpWeightMsgCreateAtomicSwap, &weightCreateAtomicSwap, nil, + func(_ *rand.Rand) { + weightCreateAtomicSwap = appparams.DefaultWeightMsgCreateAtomicSwap + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightCreateAtomicSwap, + SimulateMsgCreateAtomicSwap(ak, k), + ), + } +} + +// SimulateMsgCreateAtomicSwap generates a MsgCreateAtomicSwap with random values +func SimulateMsgCreateAtomicSwap(ak auth.AccountKeeper, k keeper.Keeper) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + + senderAddr := k.GetBnbDeputyAddress(ctx) + + sender, found := simulation.FindAccount(accs, senderAddr) + if !found { + return noOpMsg, nil, nil + } + + recipient, _ := simulation.RandomAcc(r, accs) + + recipientOtherChain := simulation.RandStringOfLength(r, 43) + senderOtherChain := simulation.RandStringOfLength(r, 43) + + // Generate cryptographically strong pseudo-random number + randomNumber, err := simulation.RandPositiveInt(r, sdk.NewInt(math.MaxInt64)) + if err != nil { + return noOpMsg, nil, err + } + // Must use current blocktime instead of 'now' since initial blocktime was randomly generated + timestamp := ctx.BlockTime().Unix() + randomNumberHash := types.CalculateRandomHash(randomNumber.BigInt().Bytes(), timestamp) + + // Randomly select an asset from supported assets + assets, found := k.GetAssets(ctx) + if !found { + return noOpMsg, nil, fmt.Errorf("no supported assets found") + } + asset := assets[r.Intn(len(assets))] + + // Check that the sender has coins of this type + senderAcc := ak.GetAccount(ctx, senderAddr) + fees, err := simulation.RandomFees(r, ctx, senderAcc.SpendableCoins(ctx.BlockTime())) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + availableAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(asset.Denom) + // Get an amount of coins between 0.1 and 2% of total coins + amount := availableAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000)))) + if amount.IsZero() { + return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil + } + coin := sdk.NewCoin(asset.Denom, amount) + coins := sdk.NewCoins(coin) + expectedIncome := coin.String() + + // We're assuming that sims are run with -NumBlocks=100 + heightSpan := int64(55) + crossChain := true + + msg := types.NewMsgCreateAtomicSwap( + senderAddr, recipient.Address, recipientOtherChain, senderOtherChain, randomNumberHash, + timestamp, coins, expectedIncome, heightSpan, crossChain, + ) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{senderAcc.GetAccountNumber()}, + []uint64{senderAcc.GetSequence()}, + sender.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + // If created, construct a MsgClaimAtomicSwap or MsgRefundAtomicSwap future operation + var futureOp simulation.FutureOperation + swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain) + if r.Intn(100) < 50 { + // Claim future operation + executionBlock := ctx.BlockHeight() + (msg.HeightSpan / 2) + futureOp = simulation.FutureOperation{ + BlockHeight: int(executionBlock), + Op: operationClaimAtomicSwap(ak, k, swapID, randomNumber.BigInt().Bytes()), + } + } else { + // Refund future operation + executionBlock := ctx.BlockHeight() + msg.HeightSpan + futureOp = simulation.FutureOperation{ + BlockHeight: int(executionBlock), + Op: operationRefundAtomicSwap(ak, k, swapID), + } + } + + return simulation.NewOperationMsg(msg, true, result.Log), []simulation.FutureOperation{futureOp}, nil + } +} + +func operationClaimAtomicSwap(ak auth.AccountKeeper, k keeper.Keeper, swapID []byte, randomNumber []byte) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + simAccount, _ := simulation.RandomAcc(r, accs) + acc := ak.GetAccount(ctx, simAccount.Address) + + msg := types.NewMsgClaimAtomicSwap(acc.GetAddress(), swapID, randomNumber) + + fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime())) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil + } +} + +func operationRefundAtomicSwap(ak auth.AccountKeeper, k keeper.Keeper, swapID []byte) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + simAccount, _ := simulation.RandomAcc(r, accs) + acc := ak.GetAccount(ctx, simAccount.Address) + + msg := types.NewMsgRefundAtomicSwap(acc.GetAddress(), swapID) + + fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime())) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil + } +} diff --git a/x/bep3/simulation/operations/msg.go b/x/bep3/simulation/operations/msg.go deleted file mode 100644 index 1fdaff6b..00000000 --- a/x/bep3/simulation/operations/msg.go +++ /dev/null @@ -1,146 +0,0 @@ -package operations - -import ( - "fmt" - "math" - "math/rand" - - "github.com/cosmos/cosmos-sdk/baseapp" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/simulation" - - "github.com/kava-labs/kava/x/bep3" - "github.com/kava-labs/kava/x/bep3/keeper" - "github.com/kava-labs/kava/x/bep3/types" -) - -var ( - noOpMsg = simulation.NoOpMsg(bep3.ModuleName) -) - -// SimulateMsgCreateAtomicSwap generates a MsgCreateAtomicSwap with random values -func SimulateMsgCreateAtomicSwap(ak auth.AccountKeeper, k keeper.Keeper) simulation.Operation { - handler := bep3.NewHandler(k) - - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - simulation.OperationMsg, []simulation.FutureOperation, error) { - - sender := k.GetBnbDeputyAddress(ctx) - recipient := simulation.RandomAcc(r, accs).Address - - recipientOtherChain := simulation.RandStringOfLength(r, 43) - senderOtherChain := simulation.RandStringOfLength(r, 43) - - // Generate cryptographically strong pseudo-random number - randomNumber, err := simulation.RandPositiveInt(r, sdk.NewInt(math.MaxInt64)) - if err != nil { - return noOpMsg, nil, err - } - // Must use current blocktime instead of 'now' since initial blocktime was randomly generated - timestamp := ctx.BlockTime().Unix() - randomNumberHash := types.CalculateRandomHash(randomNumber.BigInt().Bytes(), timestamp) - - // Randomly select an asset from supported assets - assets, found := k.GetAssets(ctx) - if !found { - return noOpMsg, nil, fmt.Errorf("no supported assets found") - } - asset := assets[r.Intn(len(assets))] - - // Check that the sender has coins of this type - availableAmount := ak.GetAccount(ctx, sender).GetCoins().AmountOf(asset.Denom) - // Get an amount of coins between 0.1 and 2% of total coins - amount := availableAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000)))) - if amount.IsZero() { - return simulation.NewOperationMsgBasic(bep3.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil - } - coin := sdk.NewCoin(asset.Denom, amount) - coins := sdk.NewCoins(coin) - expectedIncome := coin.String() - - // We're assuming that sims are run with -NumBlocks=100 - heightSpan := int64(55) - crossChain := true - - msg := types.NewMsgCreateAtomicSwap( - sender, recipient, recipientOtherChain, senderOtherChain, randomNumberHash, - timestamp, coins, expectedIncome, heightSpan, crossChain) - - if err := msg.ValidateBasic(); err != nil { - return noOpMsg, nil, fmt.Errorf("expected MsgCreateAtomicSwap to pass ValidateBasic: %s", err) - } - - // Submit msg - ok := submitMsg(ctx, handler, msg) - - // If created, construct a MsgClaimAtomicSwap or MsgRefundAtomicSwap future operation - var futureOp simulation.FutureOperation - if ok { - swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain) - acc := simulation.RandomAcc(r, accs) - evenOdd := r.Intn(2) + 1 - if evenOdd%2 == 0 { - // Claim future operation - executionBlock := ctx.BlockHeight() + (msg.HeightSpan / 2) - futureOp = loadClaimFutureOp(acc.Address, swapID, randomNumber.BigInt().Bytes(), executionBlock, handler) - } else { - // Refund future operation - executionBlock := ctx.BlockHeight() + msg.HeightSpan - futureOp = loadRefundFutureOp(acc.Address, swapID, executionBlock, handler) - } - } - - return simulation.NewOperationMsg(msg, ok, ""), []simulation.FutureOperation{futureOp}, nil - } -} - -func loadClaimFutureOp(sender sdk.AccAddress, swapID []byte, randomNumber []byte, height int64, handler sdk.Handler) simulation.FutureOperation { - claimOp := func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - simulation.OperationMsg, []simulation.FutureOperation, error) { - - // Build the refund msg and validate basic - claimMsg := types.NewMsgClaimAtomicSwap(sender, swapID, randomNumber) - if err := claimMsg.ValidateBasic(); err != nil { - return noOpMsg, nil, fmt.Errorf("expected MsgClaimAtomicSwap to pass ValidateBasic: %s", err) - } - - // Test msg submission at target block height - ok := handler(ctx.WithBlockHeight(height), claimMsg).IsOK() - return simulation.NewOperationMsg(claimMsg, ok, ""), nil, nil - } - - return simulation.FutureOperation{ - BlockHeight: int(height), - Op: claimOp, - } -} - -func loadRefundFutureOp(sender sdk.AccAddress, swapID []byte, height int64, handler sdk.Handler) simulation.FutureOperation { - refundOp := func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - simulation.OperationMsg, []simulation.FutureOperation, error) { - // Build the refund msg and validate basic - refundMsg := types.NewMsgRefundAtomicSwap(sender, swapID) - if err := refundMsg.ValidateBasic(); err != nil { - return noOpMsg, nil, fmt.Errorf("expected MsgRefundAtomicSwap to pass ValidateBasic: %s", err) - } - - // Test msg submission at target block height - ok := handler(ctx.WithBlockHeight(height), refundMsg).IsOK() - return simulation.NewOperationMsg(refundMsg, ok, ""), nil, nil - } - - return simulation.FutureOperation{ - BlockHeight: int(height), - Op: refundOp, - } -} - -func submitMsg(ctx sdk.Context, handler sdk.Handler, msg sdk.Msg) (ok bool) { - ctx, write := ctx.CacheContext() - ok = handler(ctx, msg).IsOK() - if ok { - write() - } - return ok -} diff --git a/x/bep3/simulation/params.go b/x/bep3/simulation/params.go index 41c3a346..76656750 100644 --- a/x/bep3/simulation/params.go +++ b/x/bep3/simulation/params.go @@ -22,22 +22,22 @@ func ParamChanges(r *rand.Rand) []simulation.ParamChange { minBlockLockVal := GenMinBlockLock(r) return []simulation.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress, "", + simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress, func(r *rand.Rand) string { - return fmt.Sprintf("\"%s\"", GenBnbDeputyAddress(r)) + return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address) }, ), - simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock, "", + simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock, func(r *rand.Rand) string { return fmt.Sprintf("\"%d\"", minBlockLockVal) }, ), - simulation.NewSimParamChange(types.ModuleName, keyMaxBlockLock, "", + simulation.NewSimParamChange(types.ModuleName, keyMaxBlockLock, func(r *rand.Rand) string { return fmt.Sprintf("\"%d\"", GenMaxBlockLock(r, minBlockLockVal)) }, ), - simulation.NewSimParamChange(types.ModuleName, keySupportedAssets, "", + simulation.NewSimParamChange(types.ModuleName, keySupportedAssets, func(r *rand.Rand) string { return fmt.Sprintf("\"%v\"", GenSupportedAssets(r)) }, diff --git a/x/bep3/types/errors.go b/x/bep3/types/errors.go index 06eb951f..71939c46 100644 --- a/x/bep3/types/errors.go +++ b/x/bep3/types/errors.go @@ -1,118 +1,40 @@ package types import ( - "encoding/hex" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - cmn "github.com/tendermint/tendermint/libs/common" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// CodeType is the local code type -type CodeType = sdk.CodeType +// DONTCOVER -const ( - // DefaultCodespace default bep3 codespace - DefaultCodespace sdk.CodespaceType = ModuleName - CodeInvalidTimestamp CodeType = 1 - CodeInvalidHeightSpan CodeType = 2 - CodeAssetNotSupported CodeType = 3 - CodeAssetNotActive CodeType = 4 - CodeAssetSupplyNotFound CodeType = 5 - CodeExceedsSupplyLimit CodeType = 6 - CodeExceedsAvailableSupply CodeType = 7 - CodeInvalidCurrentSupply CodeType = 8 - CodeInvalidIncomingSupply CodeType = 9 - CodeInvalidOutgoingSupply CodeType = 10 - CodeInvalidClaimSecret CodeType = 11 - CodeAtomicSwapAlreadyExists CodeType = 12 - CodeAtomicSwapNotFound CodeType = 13 - CodeSwapNotRefundable CodeType = 14 - CodeSwapNotClaimable CodeType = 15 +var ( + // ErrInvalidTimestamp error for when an timestamp is outside of bounds. Assumes block time of 10 seconds. + ErrInvalidTimestamp = sdkerrors.Register(ModuleName, 2, "timestamp can neither be 15 minutes ahead of the current time, nor 30 minutes later") + // ErrInvalidHeightSpan error a proposed height span is outside of lock time range + ErrInvalidHeightSpan = sdkerrors.Register(ModuleName, 3, "height span is outside acceptable range") + // ErrAssetNotSupported error for when an asset is not supported + ErrAssetNotSupported = sdkerrors.Register(ModuleName, 4, "asset not on the list of supported assets") + // ErrAssetNotActive error for when an asset is currently inactive + ErrAssetNotActive = sdkerrors.Register(ModuleName, 5, "asset is currently inactive") + // ErrAssetSupplyNotFound error for when an asset's supply is not found in the store + ErrAssetSupplyNotFound = sdkerrors.Register(ModuleName, 6, "asset supply not found in store") + // ErrExceedsSupplyLimit error for when the proposed supply increase would put the supply above limit + ErrExceedsSupplyLimit = sdkerrors.Register(ModuleName, 7, "asset supply over limit") + // ErrExceedsAvailableSupply error for when the proposed outgoing amount exceeds the total available supply + ErrExceedsAvailableSupply = sdkerrors.Register(ModuleName, 8, "outgoing swap exceeds total available supply") + // ErrInvalidCurrentSupply error for when the proposed decrease would result in a negative current supplyx + ErrInvalidCurrentSupply = sdkerrors.Register(ModuleName, 9, "supply decrease puts current asset supply below 0") + // ErrInvalidIncomingSupply error for when the proposed decrease would result in a negative incoming supply + ErrInvalidIncomingSupply = sdkerrors.Register(ModuleName, 10, "supply decrease puts incoming asset supply below 0") + // ErrInvalidOutgoingSupply error for when the proposed decrease would result in a negative outgoing supply + ErrInvalidOutgoingSupply = sdkerrors.Register(ModuleName, 11, "supply decrease puts outgoing asset supply below 0") + // ErrInvalidClaimSecret error when a submitted secret doesn't match an AtomicSwap's swapID + ErrInvalidClaimSecret = sdkerrors.Register(ModuleName, 12, "hashed claim attempt does not match") + // ErrAtomicSwapAlreadyExists error for when an AtomicSwap with this swapID already exists + ErrAtomicSwapAlreadyExists = sdkerrors.Register(ModuleName, 13, "atomic swap already exists") + // ErrAtomicSwapNotFound error for when an atomic swap is not found + ErrAtomicSwapNotFound = sdkerrors.Register(ModuleName, 14, "atomic swap not found") + // ErrSwapNotRefundable error for when an AtomicSwap has not expired and cannot be refunded + ErrSwapNotRefundable = sdkerrors.Register(ModuleName, 15, "atomic swap is still active and cannot be refunded") + // ErrSwapNotClaimable error for when an atomic swap is not open and cannot be claimed + ErrSwapNotClaimable = sdkerrors.Register(ModuleName, 16, "atomic swap is not claimable") ) - -// ErrInvalidTimestamp error for when an timestamp is outside of bounds. Assumes block time of 10 seconds. -func ErrInvalidTimestamp(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidTimestamp, fmt.Sprintf("Timestamp can neither be 15 minutes ahead of the current time, nor 30 minutes later")) -} - -// ErrInvalidHeightSpan error a proposed height span is outside of lock time range -func ErrInvalidHeightSpan(codespace sdk.CodespaceType, heightspan int64, minLockTime int64, maxLockTime int64) sdk.Error { - return sdk.NewError(codespace, CodeInvalidHeightSpan, fmt.Sprintf("height span %d is outside acceptable range %d - %d", heightspan, minLockTime, maxLockTime)) -} - -// ErrAssetNotSupported error for when an asset is not supported -func ErrAssetNotSupported(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeAssetNotSupported, fmt.Sprintf("asset %s is not on the list of supported assets", denom)) -} - -// ErrAssetNotActive error for when an asset is currently inactive -func ErrAssetNotActive(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeAssetNotActive, fmt.Sprintf("asset %s is currently inactive", denom)) -} - -// ErrAssetSupplyNotFound error for when an asset's supply is not found in the store -func ErrAssetSupplyNotFound(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeAssetSupplyNotFound, fmt.Sprintf("%s asset supply not found in store", denom)) -} - -// ErrExceedsSupplyLimit error for when the proposed supply increase would put the supply above limit -func ErrExceedsSupplyLimit(codespace sdk.CodespaceType, increase, current, limit sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeExceedsSupplyLimit, - fmt.Sprintf("a supply increase of %s puts current asset supply %s over supply limit %s", increase, current, limit)) -} - -// ErrExceedsAvailableSupply error for when the proposed outgoing amount exceeds the total available supply -func ErrExceedsAvailableSupply(codespace sdk.CodespaceType, increase sdk.Coin, available sdk.Int) sdk.Error { - return sdk.NewError(codespace, CodeExceedsAvailableSupply, - fmt.Sprintf("an outgoing swap with amount %s exceeds total available supply %s", - increase, sdk.NewCoin(increase.Denom, available))) -} - -// ErrInvalidCurrentSupply error for when the proposed decrease would result in a negative current supply -func ErrInvalidCurrentSupply(codespace sdk.CodespaceType, decrease, current sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeInvalidCurrentSupply, - fmt.Sprintf("a supply decrease of %s puts current asset supply %s below 0", decrease, current)) -} - -// ErrInvalidIncomingSupply error for when the proposed decrease would result in a negative incoming supply -func ErrInvalidIncomingSupply(codespace sdk.CodespaceType, decrease, incoming sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeInvalidIncomingSupply, - fmt.Sprintf("a supply decrease of %s puts incoming asset supply %s below 0", decrease, incoming)) -} - -// ErrInvalidOutgoingSupply error for when the proposed decrease would result in a negative outgoing supply -func ErrInvalidOutgoingSupply(codespace sdk.CodespaceType, decrease, outgoing sdk.Coin) sdk.Error { - return sdk.NewError(codespace, CodeInvalidOutgoingSupply, - fmt.Sprintf("a supply decrease of %s puts outgoing asset supply %s below 0", decrease, outgoing)) -} - -// ErrInvalidClaimSecret error when a submitted secret doesn't match an AtomicSwap's swapID -func ErrInvalidClaimSecret(codespace sdk.CodespaceType, submittedSecret []byte, swapID []byte) sdk.Error { - return sdk.NewError(codespace, CodeInvalidClaimSecret, - fmt.Sprintf("hashed claim attempt %s does not match %s", - hex.EncodeToString(submittedSecret), - hex.EncodeToString(swapID), - ), - ) -} - -// ErrAtomicSwapAlreadyExists error for when an AtomicSwap with this swapID already exists -func ErrAtomicSwapAlreadyExists(codespace sdk.CodespaceType, swapID cmn.HexBytes) sdk.Error { - return sdk.NewError(codespace, CodeAtomicSwapAlreadyExists, fmt.Sprintf("atomic swap %s already exists", swapID)) -} - -// ErrAtomicSwapNotFound error for when an atomic swap is not found -func ErrAtomicSwapNotFound(codespace sdk.CodespaceType, id []byte) sdk.Error { - return sdk.NewError(codespace, CodeAtomicSwapNotFound, fmt.Sprintf("AtomicSwap %s was not found", hex.EncodeToString(id))) -} - -// ErrSwapNotRefundable error for when an AtomicSwap has not expired and cannot be refunded -func ErrSwapNotRefundable(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeSwapNotRefundable, fmt.Sprintf("atomic swap is still active and cannot be refunded")) -} - -// ErrSwapNotClaimable error for when an atomic swap is not open and cannot be claimed -func ErrSwapNotClaimable(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeSwapNotClaimable, fmt.Sprintf("atomic swap is not claimable")) -} diff --git a/x/bep3/types/expected_keepers.go b/x/bep3/types/expected_keepers.go index 83141c9e..bb58ef9b 100644 --- a/x/bep3/types/expected_keepers.go +++ b/x/bep3/types/expected_keepers.go @@ -10,10 +10,10 @@ type SupplyKeeper interface { GetModuleAddress(name string) sdk.AccAddress GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI - SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) sdk.Error - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToModule(ctx sdk.Context, sender, recipient string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error - MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error } diff --git a/x/bep3/types/msg.go b/x/bep3/types/msg.go index 309fa2af..92be18da 100644 --- a/x/bep3/types/msg.go +++ b/x/bep3/types/msg.go @@ -1,11 +1,15 @@ package types import ( + "errors" "fmt" + "strings" + + "github.com/tendermint/tendermint/crypto" + tmbytes "github.com/tendermint/tendermint/libs/bytes" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/tendermint/tendermint/crypto" - cmn "github.com/tendermint/tendermint/libs/common" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -36,21 +40,21 @@ var ( // MsgCreateAtomicSwap contains an AtomicSwap struct type MsgCreateAtomicSwap struct { - From sdk.AccAddress `json:"from" yaml:"from"` - To sdk.AccAddress `json:"to" yaml:"to"` - RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` - SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` - RandomNumberHash cmn.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` - Timestamp int64 `json:"timestamp" yaml:"timestamp"` - Amount sdk.Coins `json:"amount" yaml:"amount"` - ExpectedIncome string `json:"expected_income" yaml:"expected_income"` - HeightSpan int64 `json:"height_span" yaml:"height_span"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` + From sdk.AccAddress `json:"from" yaml:"from"` + To sdk.AccAddress `json:"to" yaml:"to"` + RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` + SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` + RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` + Timestamp int64 `json:"timestamp" yaml:"timestamp"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + ExpectedIncome string `json:"expected_income" yaml:"expected_income"` + HeightSpan int64 `json:"height_span" yaml:"height_span"` + CrossChain bool `json:"cross_chain" yaml:"cross_chain"` } // NewMsgCreateAtomicSwap initializes a new MsgCreateAtomicSwap func NewMsgCreateAtomicSwap(from sdk.AccAddress, to sdk.AccAddress, recipientOtherChain, - senderOtherChain string, randomNumberHash cmn.HexBytes, timestamp int64, + senderOtherChain string, randomNumberHash tmbytes.HexBytes, timestamp int64, amount sdk.Coins, expectedIncome string, heightSpan int64, crossChain bool) MsgCreateAtomicSwap { return MsgCreateAtomicSwap{ From: from, @@ -91,49 +95,58 @@ func (msg MsgCreateAtomicSwap) GetSigners() []sdk.AccAddress { } // ValidateBasic validates the MsgCreateAtomicSwap -func (msg MsgCreateAtomicSwap) ValidateBasic() sdk.Error { +func (msg MsgCreateAtomicSwap) ValidateBasic() error { + if msg.From.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") + } if len(msg.From) != AddrByteCount { - return sdk.ErrInternal(fmt.Sprintf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.From))) + return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.From)) + } + if msg.To.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "recipient address cannot be empty") } if len(msg.To) != AddrByteCount { - return sdk.ErrInternal(fmt.Sprintf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.To))) + return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.To)) } - if !msg.CrossChain && len(msg.RecipientOtherChain) != 0 { - return sdk.ErrInternal(fmt.Sprintf("must leave recipient address on other chain to empty for single chain swap")) + if !msg.CrossChain && msg.RecipientOtherChain != "" { + return errors.New("must leave recipient address on other chain to empty for single chain swap") } - if !msg.CrossChain && len(msg.SenderOtherChain) != 0 { - return sdk.ErrInternal(fmt.Sprintf("must leave sender address on other chain to empty for single chain swap")) + if !msg.CrossChain && msg.SenderOtherChain != "" { + return errors.New("must leave sender address on other chain to empty for single chain swap") } - if msg.CrossChain && len(msg.RecipientOtherChain) == 0 { - return sdk.ErrInternal(fmt.Sprintf("missing recipient address on other chain for cross chain swap")) + if msg.CrossChain && strings.TrimSpace(msg.RecipientOtherChain) == "" { + return errors.New("missing recipient address on other chain for cross chain swap") } if len(msg.RecipientOtherChain) > MaxOtherChainAddrLength { - return sdk.ErrInternal(fmt.Sprintf("the length of recipient address on other chain should be less than %d", MaxOtherChainAddrLength)) + return fmt.Errorf("the length of recipient address on other chain should be less than %d", MaxOtherChainAddrLength) } if len(msg.SenderOtherChain) > MaxOtherChainAddrLength { - return sdk.ErrInternal(fmt.Sprintf("the length of sender address on other chain should be less than %d", MaxOtherChainAddrLength)) + return fmt.Errorf("the length of sender address on other chain should be less than %d", MaxOtherChainAddrLength) } if len(msg.RandomNumberHash) != RandomNumberHashLength { - return sdk.ErrInternal(fmt.Sprintf("the length of random number hash should be %d", RandomNumberHashLength)) + return fmt.Errorf("the length of random number hash should be %d", RandomNumberHashLength) } if msg.Timestamp <= 0 { - return sdk.ErrInternal("timestamp must be positive") + return errors.New("timestamp must be positive") } - if !msg.Amount.IsAllPositive() { - return sdk.ErrInternal(fmt.Sprintf("the swapped out coin must be positive")) + if len(msg.Amount) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, "amount cannot be empty") + } + if !msg.Amount.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) } if len(msg.ExpectedIncome) > MaxExpectedIncomeLength { - return sdk.ErrInternal(fmt.Sprintf("the length of expected income should be less than %d", MaxExpectedIncomeLength)) + return fmt.Errorf("the length of expected income should be less than %d", MaxExpectedIncomeLength) } expectedIncomeCoins, err := sdk.ParseCoins(msg.ExpectedIncome) if err != nil || expectedIncomeCoins == nil { - return sdk.ErrInternal(fmt.Sprintf("expected income %s must be in valid format e.g. 10000ukava", msg.ExpectedIncome)) + return fmt.Errorf("expected income %s must be in valid format e.g. 10000ukava", msg.ExpectedIncome) } if expectedIncomeCoins.IsAnyGT(msg.Amount) { - return sdk.ErrInternal(fmt.Sprintf("expected income %s cannot be greater than amount %s", msg.ExpectedIncome, msg.Amount.String())) + return fmt.Errorf("expected income %s cannot be greater than amount %s", msg.ExpectedIncome, msg.Amount.String()) } if msg.HeightSpan <= 0 { - return sdk.ErrInternal("height span must be positive") + return errors.New("height span must be positive") } return nil } @@ -146,9 +159,9 @@ func (msg MsgCreateAtomicSwap) GetSignBytes() []byte { // MsgClaimAtomicSwap defines a AtomicSwap claim type MsgClaimAtomicSwap struct { - From sdk.AccAddress `json:"from" yaml:"from"` - SwapID cmn.HexBytes `json:"swap_id" yaml:"swap_id"` - RandomNumber cmn.HexBytes `json:"random_number" yaml:"random_number"` + From sdk.AccAddress `json:"from" yaml:"from"` + SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` + RandomNumber tmbytes.HexBytes `json:"random_number" yaml:"random_number"` } // NewMsgClaimAtomicSwap initializes a new MsgClaimAtomicSwap @@ -182,15 +195,18 @@ func (msg MsgClaimAtomicSwap) GetSigners() []sdk.AccAddress { } // ValidateBasic validates the MsgClaimAtomicSwap -func (msg MsgClaimAtomicSwap) ValidateBasic() sdk.Error { +func (msg MsgClaimAtomicSwap) ValidateBasic() error { + if msg.From.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") + } if len(msg.From) != AddrByteCount { - return sdk.ErrInternal(fmt.Sprintf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.From))) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "actual address length ≠ expected length (%d ≠ %d)", len(msg.From), AddrByteCount) } if len(msg.SwapID) != SwapIDLength { - return sdk.ErrInternal(fmt.Sprintf("the length of swapID should be %d", SwapIDLength)) + return fmt.Errorf("the length of swapID should be %d", SwapIDLength) } if len(msg.RandomNumber) == 0 { - return sdk.ErrInternal("the length of random number cannot be 0") + return errors.New("the length of random number cannot be 0") } return nil } @@ -203,8 +219,8 @@ func (msg MsgClaimAtomicSwap) GetSignBytes() []byte { // MsgRefundAtomicSwap defines a refund msg type MsgRefundAtomicSwap struct { - From sdk.AccAddress `json:"from" yaml:"from"` - SwapID cmn.HexBytes `json:"swap_id" yaml:"swap_id"` + From sdk.AccAddress `json:"from" yaml:"from"` + SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` } // NewMsgRefundAtomicSwap initializes a new MsgRefundAtomicSwap @@ -237,12 +253,15 @@ func (msg MsgRefundAtomicSwap) GetSigners() []sdk.AccAddress { } // ValidateBasic validates the MsgRefundAtomicSwap -func (msg MsgRefundAtomicSwap) ValidateBasic() sdk.Error { +func (msg MsgRefundAtomicSwap) ValidateBasic() error { + if msg.From.Empty() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") + } if len(msg.From) != AddrByteCount { - return sdk.ErrInternal(fmt.Sprintf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.From))) + return fmt.Errorf("the expected address length is %d, actual length is %d", AddrByteCount, len(msg.From)) } if len(msg.SwapID) != SwapIDLength { - return sdk.ErrInternal(fmt.Sprintf("the length of swapID should be %d", SwapIDLength)) + return fmt.Errorf("the length of swapID should be %d", SwapIDLength) } return nil } diff --git a/x/bep3/types/msg_test.go b/x/bep3/types/msg_test.go index 30cdd2ef..3940e0b6 100644 --- a/x/bep3/types/msg_test.go +++ b/x/bep3/types/msg_test.go @@ -3,11 +3,14 @@ package types_test import ( "testing" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/kava-labs/kava/x/bep3/types" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" - cmn "github.com/tendermint/tendermint/libs/common" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/bep3/types" ) var ( @@ -33,7 +36,7 @@ func TestMsgCreateAtomicSwap(t *testing.T) { to sdk.AccAddress recipientOtherChain string senderOtherChain string - randomNumberHash cmn.HexBytes + randomNumberHash tmbytes.HexBytes timestamp int64 amount sdk.Coins expectedIncome string @@ -75,8 +78,8 @@ func TestMsgClaimAtomicSwap(t *testing.T) { tests := []struct { description string from sdk.AccAddress - swapID cmn.HexBytes - randomNumber cmn.HexBytes + swapID tmbytes.HexBytes + randomNumber tmbytes.HexBytes expectPass bool }{ {"normal", binanceAddrs[0], swapID, randomNumberHash, true}, @@ -102,7 +105,7 @@ func TestMsgRefundAtomicSwap(t *testing.T) { tests := []struct { description string from sdk.AccAddress - swapID cmn.HexBytes + swapID tmbytes.HexBytes expectPass bool }{ {"normal", binanceAddrs[0], swapID, true}, diff --git a/x/bep3/types/params.go b/x/bep3/types/params.go index cc8cc18f..b7d04493 100644 --- a/x/bep3/types/params.go +++ b/x/bep3/types/params.go @@ -1,12 +1,18 @@ package types import ( + "errors" "fmt" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" ) +const ( + bech32MainPrefix = "kava" +) + // Parameter keys var ( KeyBnbDeputyAddress = []byte("BnbDeputyAddress") @@ -59,7 +65,11 @@ func NewParams(bnbDeputyAddress sdk.AccAddress, minBlockLock, maxBlockLock int64 // DefaultParams returns default params for bep3 module func DefaultParams() Params { - defaultBnbDeputyAddress, _ := sdk.AccAddressFromBech32("kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj") + defaultBnbDeputyAddress, err := sdk.AccAddressFromBech32("kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj") + if err != nil { + panic(err) + } + return NewParams(defaultBnbDeputyAddress, DefaultMinBlockLock, DefaultMaxBlockLock, DefaultSupportedAssets) } @@ -103,44 +113,112 @@ func ParamKeyTable() params.KeyTable { // nolint func (p *Params) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - {Key: KeyBnbDeputyAddress, Value: &p.BnbDeputyAddress}, - {Key: KeyMinBlockLock, Value: &p.MinBlockLock}, - {Key: KeyMaxBlockLock, Value: &p.MaxBlockLock}, - {Key: KeySupportedAssets, Value: &p.SupportedAssets}, + params.NewParamSetPair(KeyBnbDeputyAddress, &p.BnbDeputyAddress, validateBnbDeputyAddressParam), + params.NewParamSetPair(KeyMinBlockLock, &p.MinBlockLock, validateMinBlockLockParam), + params.NewParamSetPair(KeyMaxBlockLock, &p.MaxBlockLock, validateMaxBlockLockParam), + params.NewParamSetPair(KeySupportedAssets, &p.SupportedAssets, validateSupportedAssetsParams), } } // Validate ensure that params have valid values func (p Params) Validate() error { - if p.MinBlockLock < AbsoluteMinimumBlockLock { - return fmt.Errorf(fmt.Sprintf("minimum block lock cannot be less than %d", AbsoluteMinimumBlockLock)) + if err := validateBnbDeputyAddressParam(p.BnbDeputyAddress); err != nil { + return err } + + if err := validateMinBlockLockParam(p.MinBlockLock); err != nil { + return err + } + + if err := validateMaxBlockLockParam(p.MaxBlockLock); err != nil { + return err + } + if p.MinBlockLock >= p.MaxBlockLock { - return fmt.Errorf("maximum block lock must be greater than minimum block lock") + return fmt.Errorf("minimum block lock cannot be ≥ maximum block lock, got %d ≥ %d", p.MinBlockLock, p.MaxBlockLock) } - if p.MaxBlockLock > AbsoluteMaximumBlockLock { - return fmt.Errorf(fmt.Sprintf("maximum block lock cannot be greater than %d", AbsoluteMaximumBlockLock)) + + return validateSupportedAssetsParams(p.SupportedAssets) +} + +func validateBnbDeputyAddressParam(i interface{}) error { + addr, ok := i.(sdk.AccAddress) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) } + + if addr.Empty() { + return errors.New("bnb deputy address cannot be empty") + } + + if len(addr.Bytes()) != sdk.AddrLen { + return fmt.Errorf("bnb deputy address invalid bytes length got %d, want %d", len(addr.Bytes()), sdk.AddrLen) + } + + return nil +} + +func validateMinBlockLockParam(i interface{}) error { + minBlockLock, ok := i.(int64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if minBlockLock < AbsoluteMinimumBlockLock { + return fmt.Errorf("minimum block lock cannot be less than %d, got %d", AbsoluteMinimumBlockLock, minBlockLock) + } + + return nil +} + +func validateMaxBlockLockParam(i interface{}) error { + maxBlockLock, ok := i.(int64) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if maxBlockLock > AbsoluteMaximumBlockLock { + return fmt.Errorf("maximum block lock cannot be greater than %d, got %d", AbsoluteMaximumBlockLock, maxBlockLock) + } + + return nil +} + +func validateSupportedAssetsParams(i interface{}) error { + assetParams, ok := i.(AssetParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + coinIDs := make(map[int]bool) coinDenoms := make(map[string]bool) - for _, asset := range p.SupportedAssets { - if len(asset.Denom) == 0 { - return fmt.Errorf("asset denom cannot be empty") + for _, asset := range assetParams { + if strings.TrimSpace(asset.Denom) == "" { + return errors.New("asset denom cannot be empty") } + if asset.CoinID < 0 { - return fmt.Errorf(fmt.Sprintf("asset %s must be a positive integer", asset.Denom)) + return fmt.Errorf(fmt.Sprintf("asset %s must be a non negative integer", asset.Denom)) } + if !asset.Limit.IsPositive() { return fmt.Errorf(fmt.Sprintf("asset %s must have a positive supply limit", asset.Denom)) } - if coinDenoms[asset.Denom] { + + _, found := coinDenoms[asset.Denom] + if found { return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate denom", asset.Denom)) } + coinDenoms[asset.Denom] = true - if coinIDs[asset.CoinID] { + + _, found = coinIDs[asset.CoinID] + if found { return fmt.Errorf(fmt.Sprintf("asset %s cannot have duplicate coin id %d", asset.Denom, asset.CoinID)) } + coinIDs[asset.CoinID] = true } + return nil } diff --git a/x/bep3/types/params_test.go b/x/bep3/types/params_test.go index 26a2e7e8..65e49cfd 100644 --- a/x/bep3/types/params_test.go +++ b/x/bep3/types/params_test.go @@ -1,7 +1,6 @@ package types_test import ( - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -204,10 +203,9 @@ func (suite *ParamsTestSuite) TestParamValidation() { err := params.Validate() if tc.expectPass { - suite.Nil(err) + suite.Require().NoError(err, tc.name) } else { - suite.NotNil(err) - suite.True(strings.Contains(err.Error(), tc.expectedErr)) + suite.Require().Error(err, tc.name) } } } diff --git a/x/bep3/types/querier.go b/x/bep3/types/querier.go index 8a88456a..034a851b 100644 --- a/x/bep3/types/querier.go +++ b/x/bep3/types/querier.go @@ -1,8 +1,6 @@ package types -import ( - cmn "github.com/tendermint/tendermint/libs/common" -) +import tmbytes "github.com/tendermint/tendermint/libs/bytes" const ( // QueryGetAssetSupply command for getting info about an asset's supply @@ -17,11 +15,11 @@ const ( // QueryAssetSupply contains the params for query 'custom/bep3/supply' type QueryAssetSupply struct { - Denom cmn.HexBytes `json:"denom" yaml:"denom"` + Denom tmbytes.HexBytes `json:"denom" yaml:"denom"` } // NewQueryAssetSupply creates a new QueryAssetSupply -func NewQueryAssetSupply(denom cmn.HexBytes) QueryAssetSupply { +func NewQueryAssetSupply(denom tmbytes.HexBytes) QueryAssetSupply { return QueryAssetSupply{ Denom: denom, } @@ -29,11 +27,11 @@ func NewQueryAssetSupply(denom cmn.HexBytes) QueryAssetSupply { // QueryAtomicSwapByID contains the params for query 'custom/bep3/swap' type QueryAtomicSwapByID struct { - SwapID cmn.HexBytes `json:"swap_id" yaml:"swap_id"` + SwapID tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"` } // NewQueryAtomicSwapByID creates a new QueryAtomicSwapByID -func NewQueryAtomicSwapByID(swapBytes cmn.HexBytes) QueryAtomicSwapByID { +func NewQueryAtomicSwapByID(swapBytes tmbytes.HexBytes) QueryAtomicSwapByID { return QueryAtomicSwapByID{ SwapID: swapBytes, } diff --git a/x/bep3/types/swap.go b/x/bep3/types/swap.go index 1a7dbacb..6fee11dc 100644 --- a/x/bep3/types/swap.go +++ b/x/bep3/types/swap.go @@ -5,13 +5,14 @@ import ( "encoding/json" "fmt" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + sdk "github.com/cosmos/cosmos-sdk/types" - cmn "github.com/tendermint/tendermint/libs/common" ) // Swap is an interface for handling common actions type Swap interface { - GetSwapID() cmn.HexBytes + GetSwapID() tmbytes.HexBytes GetModuleAccountCoins() sdk.Coins Validate() error } @@ -19,22 +20,22 @@ type Swap interface { // AtomicSwap contains the information for an atomic swap type AtomicSwap struct { Swap `json:"swap" yaml:"swap"` - Amount sdk.Coins `json:"amount" yaml:"amount"` - RandomNumberHash cmn.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` - ExpireHeight int64 `json:"expire_height" yaml:"expire_height"` - Timestamp int64 `json:"timestamp" yaml:"timestamp"` - Sender sdk.AccAddress `json:"sender" yaml:"sender"` - Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` - SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` - RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` - ClosedBlock int64 `json:"closed_block" yaml:"closed_block"` - Status SwapStatus `json:"status" yaml:"status"` - CrossChain bool `json:"cross_chain" yaml:"cross_chain"` - Direction SwapDirection `json:"direction" yaml:"direction"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + RandomNumberHash tmbytes.HexBytes `json:"random_number_hash" yaml:"random_number_hash"` + ExpireHeight int64 `json:"expire_height" yaml:"expire_height"` + Timestamp int64 `json:"timestamp" yaml:"timestamp"` + Sender sdk.AccAddress `json:"sender" yaml:"sender"` + Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` + SenderOtherChain string `json:"sender_other_chain" yaml:"sender_other_chain"` + RecipientOtherChain string `json:"recipient_other_chain" yaml:"recipient_other_chain"` + ClosedBlock int64 `json:"closed_block" yaml:"closed_block"` + Status SwapStatus `json:"status" yaml:"status"` + CrossChain bool `json:"cross_chain" yaml:"cross_chain"` + Direction SwapDirection `json:"direction" yaml:"direction"` } // NewAtomicSwap returns a new AtomicSwap -func NewAtomicSwap(amount sdk.Coins, randomNumberHash cmn.HexBytes, expireHeight, timestamp int64, sender, +func NewAtomicSwap(amount sdk.Coins, randomNumberHash tmbytes.HexBytes, expireHeight, timestamp int64, sender, recipient sdk.AccAddress, senderOtherChain string, recipientOtherChain string, closedBlock int64, status SwapStatus, crossChain bool, direction SwapDirection) AtomicSwap { return AtomicSwap{ @@ -54,7 +55,7 @@ func NewAtomicSwap(amount sdk.Coins, randomNumberHash cmn.HexBytes, expireHeight } // GetSwapID calculates the ID of an atomic swap -func (a AtomicSwap) GetSwapID() cmn.HexBytes { +func (a AtomicSwap) GetSwapID() tmbytes.HexBytes { return CalculateSwapID(a.RandomNumberHash, a.Sender, a.SenderOtherChain) } diff --git a/x/bep3/types/swap_test.go b/x/bep3/types/swap_test.go index db22dab0..fb7fcf20 100644 --- a/x/bep3/types/swap_test.go +++ b/x/bep3/types/swap_test.go @@ -3,18 +3,21 @@ package types_test import ( "testing" + "github.com/stretchr/testify/suite" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/x/bep3/types" - "github.com/stretchr/testify/suite" - cmn "github.com/tendermint/tendermint/libs/common" ) type AtomicSwapTestSuite struct { suite.Suite addrs []sdk.AccAddress timestamps []int64 - randomNumberHashes []cmn.HexBytes + randomNumberHashes []tmbytes.HexBytes } func (suite *AtomicSwapTestSuite) SetupTest() { @@ -25,7 +28,7 @@ func (suite *AtomicSwapTestSuite) SetupTest() { // Generate 10 timestamps and random number hashes var timestamps []int64 - var randomNumberHashes []cmn.HexBytes + var randomNumberHashes []tmbytes.HexBytes for i := 0; i < 10; i++ { timestamp := ts(i) randomNumber, _ := types.GenerateSecureRandomNumber() @@ -43,7 +46,7 @@ func (suite *AtomicSwapTestSuite) SetupTest() { func (suite *AtomicSwapTestSuite) TestNewAtomicSwap() { type args struct { amount sdk.Coins - randomNumberHash cmn.HexBytes + randomNumberHash tmbytes.HexBytes expireHeight int64 timestamp int64 sender sdk.AccAddress @@ -127,7 +130,7 @@ func (suite *AtomicSwapTestSuite) TestNewAtomicSwap() { suite.Nil(swap.Validate()) suite.Equal(tc.args.amount, swap.GetModuleAccountCoins()) expectedSwapID := types.CalculateSwapID(tc.args.randomNumberHash, tc.args.sender, tc.args.senderOtherChain) - suite.Equal(cmn.HexBytes(expectedSwapID), swap.GetSwapID()) + suite.Equal(tmbytes.HexBytes(expectedSwapID), swap.GetSwapID()) } else { suite.Error(swap.Validate()) } diff --git a/x/cdp/alias.go b/x/cdp/alias.go index 7cae19d8..9688a153 100644 --- a/x/cdp/alias.go +++ b/x/cdp/alias.go @@ -1,8 +1,3 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/kava-labs/kava/x/cdp/keeper -// ALIASGEN: github.com/kava-labs/kava/x/cdp/types package cdp import ( @@ -10,26 +5,14 @@ import ( "github.com/kava-labs/kava/x/cdp/types" ) +// nolint +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/kava-labs/kava/x/cdp/keeper +// ALIASGEN: github.com/kava-labs/kava/x/cdp/types + const ( BaseDigitFactor = keeper.BaseDigitFactor - DefaultCodespace = types.DefaultCodespace - CodeCdpAlreadyExists = types.CodeCdpAlreadyExists - CodeCollateralLengthInvalid = types.CodeCollateralLengthInvalid - CodeCollateralNotSupported = types.CodeCollateralNotSupported - CodeDebtNotSupported = types.CodeDebtNotSupported - CodeExceedsDebtLimit = types.CodeExceedsDebtLimit - CodeInvalidCollateralRatio = types.CodeInvalidCollateralRatio - CodeCdpNotFound = types.CodeCdpNotFound - CodeDepositNotFound = types.CodeDepositNotFound - CodeInvalidDepositDenom = types.CodeInvalidDepositDenom - CodeInvalidPaymentDenom = types.CodeInvalidPaymentDenom - CodeDepositNotAvailable = types.CodeDepositNotAvailable - CodeInvalidCollateralDenom = types.CodeInvalidCollateralDenom - CodeInvalidWithdrawAmount = types.CodeInvalidWithdrawAmount - CodeCdpNotAvailable = types.CodeCdpNotAvailable - CodeBelowDebtFloor = types.CodeBelowDebtFloor - CodePaymentExceedsDebt = types.CodePaymentExceedsDebt - CodeLoadingAugmentedCDP = types.CodeLoadingAugmentedCDP EventTypeCreateCdp = types.EventTypeCreateCdp EventTypeCdpDeposit = types.EventTypeCdpDeposit EventTypeCdpDraw = types.EventTypeCdpDraw @@ -75,14 +58,13 @@ var ( ErrInvalidCollateralRatio = types.ErrInvalidCollateralRatio ErrCdpNotFound = types.ErrCdpNotFound ErrDepositNotFound = types.ErrDepositNotFound - ErrInvalidDepositDenom = types.ErrInvalidDepositDenom - ErrInvalidPaymentDenom = types.ErrInvalidPaymentDenom + ErrInvalidDeposit = types.ErrInvalidDeposit + ErrInvalidPayment = types.ErrInvalidPayment ErrDepositNotAvailable = types.ErrDepositNotAvailable - ErrInvalidCollateralDenom = types.ErrInvalidCollateralDenom + ErrInvalidCollateral = types.ErrInvalidCollateral ErrInvalidWithdrawAmount = types.ErrInvalidWithdrawAmount ErrCdpNotAvailable = types.ErrCdpNotAvailable ErrBelowDebtFloor = types.ErrBelowDebtFloor - ErrPaymentExceedsDebt = types.ErrPaymentExceedsDebt ErrLoadingAugmentedCDP = types.ErrLoadingAugmentedCDP NewGenesisState = types.NewGenesisState DefaultGenesisState = types.DefaultGenesisState diff --git a/x/cdp/client/cli/query.go b/x/cdp/client/cli/query.go index 095b3fcb..12bb542a 100644 --- a/x/cdp/client/cli/query.go +++ b/x/cdp/client/cli/query.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -23,7 +23,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Querying commands for the cdp module", } - cdpQueryCmd.AddCommand(client.GetCommands( + cdpQueryCmd.AddCommand(flags.GetCommands( QueryCdpCmd(queryRoute, cdc), QueryCdpsByDenomCmd(queryRoute, cdc), QueryCdpsByDenomAndRatioCmd(queryRoute, cdc), @@ -131,9 +131,9 @@ $ %s query %s cdps-by-ratio uatom 1.5 cliCtx := context.NewCLIContext().WithCodec(cdc) // Prepare params for querier - ratio, errSdk := sdk.NewDecFromStr(args[1]) - if errSdk != nil { - return fmt.Errorf(errSdk.Error()) + ratio, err := sdk.NewDecFromStr(args[1]) + if err != nil { + return err } bz, err := cdc.MarshalJSON(types.QueryCdpsByRatioParams{ CollateralDenom: args[0], diff --git a/x/cdp/client/cli/tx.go b/x/cdp/client/cli/tx.go index 9214aa1b..8f15888e 100644 --- a/x/cdp/client/cli/tx.go +++ b/x/cdp/client/cli/tx.go @@ -1,13 +1,14 @@ package cli import ( + "bufio" "fmt" "strings" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" @@ -24,7 +25,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command { Short: "cdp transactions subcommands", } - cdpTxCmd.AddCommand(client.PostCommands( + cdpTxCmd.AddCommand(flags.PostCommands( GetCmdCreateCdp(cdc), GetCmdDeposit(cdc), GetCmdWithdraw(cdc), @@ -48,8 +49,9 @@ $ %s tx %s create 10000000uatom 1000usdx --from myKeyName `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) collateral, err := sdk.ParseCoins(args[0]) if err != nil { @@ -82,8 +84,9 @@ $ %s tx %s deposit kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom --f `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) collateral, err := sdk.ParseCoins(args[1]) if err != nil { @@ -116,8 +119,9 @@ $ %s tx %s withdraw kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw 10000000uatom -- `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) collateral, err := sdk.ParseCoins(args[1]) if err != nil { @@ -150,8 +154,9 @@ $ %s tx %s draw uatom 1000usdx --from myKeyName `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) debt, err := sdk.ParseCoins(args[1]) if err != nil { @@ -180,8 +185,9 @@ $ %s tx %s repay uatom 1000usdx --from myKeyName `, version.ClientName, types.ModuleName)), Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) payment, err := sdk.ParseCoins(args[1]) if err != nil { diff --git a/x/cdp/genesis.go b/x/cdp/genesis.go index 0a4e6880..3fa365ef 100644 --- a/x/cdp/genesis.go +++ b/x/cdp/genesis.go @@ -58,7 +58,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, pk PricefeedKeeper, sk SupplyKeeper, } k.SetCDP(ctx, cdp) k.IndexCdpByOwner(ctx, cdp) - ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + ratio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.IndexCdpByCollateralRatio(ctx, cdp.Collateral[0].Denom, cdp.ID, ratio) k.IncrementTotalPrincipal(ctx, cdp.Collateral[0].Denom, cdp.Principal) } diff --git a/x/cdp/handler.go b/x/cdp/handler.go index 13e1ef17..e1adfc4b 100644 --- a/x/cdp/handler.go +++ b/x/cdp/handler.go @@ -1,14 +1,13 @@ package cdp import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // NewHandler creates an sdk.Handler for cdp messages func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { switch msg := msg.(type) { case MsgCreateCDP: return handleMsgCreateCDP(ctx, k, msg) @@ -21,16 +20,15 @@ func NewHandler(k Keeper) sdk.Handler { case MsgRepayDebt: return handleMsgRepayDebt(ctx, k, msg) default: - errMsg := fmt.Sprintf("unrecognized cdp msg type: %T", msg) - return sdk.ErrUnknownRequest(errMsg).Result() + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } } -func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) sdk.Result { +func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) (*sdk.Result, error) { err := k.AddCdp(ctx, msg.Sender, msg.Collateral, msg.Principal) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -42,16 +40,16 @@ func handleMsgCreateCDP(ctx sdk.Context, k Keeper, msg MsgCreateCDP) sdk.Result ) id, _ := k.GetCdpID(ctx, msg.Sender, msg.Collateral[0].Denom) - return sdk.Result{ + return &sdk.Result{ Data: GetCdpIDBytes(id), Events: ctx.EventManager().Events(), - } + }, nil } -func handleMsgDeposit(ctx sdk.Context, k Keeper, msg MsgDeposit) sdk.Result { +func handleMsgDeposit(ctx sdk.Context, k Keeper, msg MsgDeposit) (*sdk.Result, error) { err := k.DepositCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -61,13 +59,13 @@ func handleMsgDeposit(ctx sdk.Context, k Keeper, msg MsgDeposit) sdk.Result { sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()), ), ) - return sdk.Result{Events: ctx.EventManager().Events()} + return &sdk.Result{Events: ctx.EventManager().Events()}, nil } -func handleMsgWithdraw(ctx sdk.Context, k Keeper, msg MsgWithdraw) sdk.Result { +func handleMsgWithdraw(ctx sdk.Context, k Keeper, msg MsgWithdraw) (*sdk.Result, error) { err := k.WithdrawCollateral(ctx, msg.Owner, msg.Depositor, msg.Collateral) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -77,13 +75,13 @@ func handleMsgWithdraw(ctx sdk.Context, k Keeper, msg MsgWithdraw) sdk.Result { sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor.String()), ), ) - return sdk.Result{Events: ctx.EventManager().Events()} + return &sdk.Result{Events: ctx.EventManager().Events()}, nil } -func handleMsgDrawDebt(ctx sdk.Context, k Keeper, msg MsgDrawDebt) sdk.Result { +func handleMsgDrawDebt(ctx sdk.Context, k Keeper, msg MsgDrawDebt) (*sdk.Result, error) { err := k.AddPrincipal(ctx, msg.Sender, msg.CdpDenom, msg.Principal) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -93,13 +91,13 @@ func handleMsgDrawDebt(ctx sdk.Context, k Keeper, msg MsgDrawDebt) sdk.Result { sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), ), ) - return sdk.Result{Events: ctx.EventManager().Events()} + return &sdk.Result{Events: ctx.EventManager().Events()}, nil } -func handleMsgRepayDebt(ctx sdk.Context, k Keeper, msg MsgRepayDebt) sdk.Result { +func handleMsgRepayDebt(ctx sdk.Context, k Keeper, msg MsgRepayDebt) (*sdk.Result, error) { err := k.RepayPrincipal(ctx, msg.Sender, msg.CdpDenom, msg.Payment) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -109,5 +107,5 @@ func handleMsgRepayDebt(ctx sdk.Context, k Keeper, msg MsgRepayDebt) sdk.Result sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), ), ) - return sdk.Result{Events: ctx.EventManager().Events()} + return &sdk.Result{Events: ctx.EventManager().Events()}, nil } diff --git a/x/cdp/handler_test.go b/x/cdp/handler_test.go index 0652c040..ed3cc576 100644 --- a/x/cdp/handler_test.go +++ b/x/cdp/handler_test.go @@ -1,7 +1,6 @@ package cdp_test import ( - "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -47,16 +46,16 @@ func (suite *HandlerTestSuite) TestMsgCreateCdp() { cs(c("xrp", 200000000)), cs(c("usdx", 10000000)), ) - res := suite.handler(suite.ctx, msg) - suite.True(res.IsOK()) - suite.Equal(cdp.GetCdpIDBytes(uint64(1)), res.Data) + res, err := suite.handler(suite.ctx, msg) + suite.Require().NoError(err) + suite.Require().Equal(cdp.GetCdpIDBytes(uint64(1)), res.Data) } func (suite *HandlerTestSuite) TestInvalidMsg() { - res := suite.handler(suite.ctx, sdk.NewTestMsg()) - suite.False(res.IsOK()) - suite.True(strings.Contains(res.Log, "unrecognized cdp msg type")) + res, err := suite.handler(suite.ctx, sdk.NewTestMsg()) + suite.Require().Error(err) + suite.Require().Nil(res) } func TestHandlerTestSuite(t *testing.T) { diff --git a/x/cdp/keeper/auctions.go b/x/cdp/keeper/auctions.go index 1d74f6ba..3042ad78 100644 --- a/x/cdp/keeper/auctions.go +++ b/x/cdp/keeper/auctions.go @@ -43,7 +43,7 @@ func (pd partialDeposits) SumDebt() (sum sdk.Int) { } // AuctionCollateral creates auctions from the input deposits which attempt to raise the corresponding amount of debt -func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt sdk.Int, bidDenom string) sdk.Error { +func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt sdk.Int, bidDenom string) error { auctionSize := k.getAuctionSize(ctx, deposits[0].Amount[0].Denom) partialAuctionDeposits := partialDeposits{} totalCollateral := deposits.SumCollateral() @@ -110,7 +110,7 @@ func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt } // CreateAuctionsFromDeposit creates auctions from the input deposit until there is less than auctionSize left on the deposit -func (k Keeper) CreateAuctionsFromDeposit(ctx sdk.Context, dep types.Deposit, debt sdk.Int, totalCollateral sdk.Int, auctionSize sdk.Int, principalDenom string) (debtChange sdk.Int, collateralChange sdk.Int, err sdk.Error) { +func (k Keeper) CreateAuctionsFromDeposit(ctx sdk.Context, dep types.Deposit, debt sdk.Int, totalCollateral sdk.Int, auctionSize sdk.Int, principalDenom string) (debtChange sdk.Int, collateralChange sdk.Int, err error) { debtChange = sdk.ZeroInt() collateralChange = sdk.ZeroInt() depositAmount := dep.Amount[0].Amount @@ -138,7 +138,7 @@ func (k Keeper) CreateAuctionsFromDeposit(ctx sdk.Context, dep types.Deposit, de } // CreateAuctionFromPartialDeposits creates an auction from the input partial deposits -func (k Keeper) CreateAuctionFromPartialDeposits(ctx sdk.Context, partialDeps partialDeposits, debt sdk.Int, collateral sdk.Int, auctionSize sdk.Int, bidDenom string) (debtChange, collateralChange sdk.Int, err sdk.Error) { +func (k Keeper) CreateAuctionFromPartialDeposits(ctx sdk.Context, partialDeps partialDeposits, debt sdk.Int, collateral sdk.Int, auctionSize sdk.Int, bidDenom string) (debtChange, collateralChange sdk.Int, err error) { returnAddrs := []sdk.AccAddress{} returnWeights := []sdk.Int{} @@ -159,7 +159,7 @@ func (k Keeper) CreateAuctionFromPartialDeposits(ctx sdk.Context, partialDeps pa // NetSurplusAndDebt burns surplus and debt coins equal to the minimum of surplus and debt balances held by the liquidator module account // for example, if there is 1000 debt and 100 surplus, 100 surplus and 100 debt are burned, netting to 900 debt -func (k Keeper) NetSurplusAndDebt(ctx sdk.Context) sdk.Error { +func (k Keeper) NetSurplusAndDebt(ctx sdk.Context) error { totalSurplus := k.GetTotalSurplus(ctx, types.LiquidatorMacc) debt := k.GetTotalDebt(ctx, types.LiquidatorMacc) netAmount := sdk.MinInt(totalSurplus, debt) @@ -210,7 +210,7 @@ func (k Keeper) GetTotalDebt(ctx sdk.Context, accountName string) sdk.Int { } // RunSurplusAndDebtAuctions nets the surplus and debt balances and then creates surplus or debt auctions if the remaining balance is above the auction threshold parameter -func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) sdk.Error { +func (k Keeper) RunSurplusAndDebtAuctions(ctx sdk.Context) error { k.NetSurplusAndDebt(ctx) remainingDebt := k.GetTotalDebt(ctx, types.LiquidatorMacc) params := k.GetParams(ctx) diff --git a/x/cdp/keeper/cdp.go b/x/cdp/keeper/cdp.go index 9e3611ee..beff0c2e 100644 --- a/x/cdp/keeper/cdp.go +++ b/x/cdp/keeper/cdp.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/cdp/types" ) @@ -13,7 +14,7 @@ import ( const BaseDigitFactor = 1000000000000000000 // AddCdp adds a cdp for a specific owner and collateral type -func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coins, principal sdk.Coins) sdk.Error { +func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coins, principal sdk.Coins) error { // validation err := k.ValidateCollateral(ctx, collateral) if err != nil { @@ -21,7 +22,7 @@ func (k Keeper) AddCdp(ctx sdk.Context, owner sdk.AccAddress, collateral sdk.Coi } _, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral[0].Denom) if found { - return types.ErrCdpAlreadyExists(k.codespace, owner, collateral[0].Denom) + return sdkerrors.Wrapf(types.ErrCdpAlreadyExists, "owner %s, denom %s", owner, collateral[0].Denom) } err = k.ValidatePrincipalAdd(ctx, principal) if err != nil { @@ -103,10 +104,10 @@ func (k Keeper) SetCdpAndCollateralRatioIndex(ctx sdk.Context, cdp types.CDP, ra } // MintDebtCoins mints debt coins in the cdp module account -func (k Keeper) MintDebtCoins(ctx sdk.Context, moduleAccount string, denom string, principalCoins sdk.Coins) sdk.Error { +func (k Keeper) MintDebtCoins(ctx sdk.Context, moduleAccount string, denom string, principalCoins sdk.Coins) error { coinsToMint := sdk.NewCoins() for _, sc := range principalCoins { - coinsToMint = coinsToMint.Add(sdk.NewCoins(sdk.NewCoin(denom, sc.Amount))) + coinsToMint = coinsToMint.Add(sdk.NewCoin(denom, sc.Amount)) } err := k.supplyKeeper.MintCoins(ctx, moduleAccount, coinsToMint) if err != nil { @@ -116,10 +117,10 @@ func (k Keeper) MintDebtCoins(ctx sdk.Context, moduleAccount string, denom strin } // BurnDebtCoins burns debt coins from the cdp module account -func (k Keeper) BurnDebtCoins(ctx sdk.Context, moduleAccount string, denom string, paymentCoins sdk.Coins) sdk.Error { +func (k Keeper) BurnDebtCoins(ctx sdk.Context, moduleAccount string, denom string, paymentCoins sdk.Coins) error { coinsToBurn := sdk.NewCoins() for _, pc := range paymentCoins { - coinsToBurn = coinsToBurn.Add(sdk.NewCoins(sdk.NewCoin(denom, pc.Amount))) + coinsToBurn = coinsToBurn.Add(sdk.NewCoin(denom, pc.Amount)) } err := k.supplyKeeper.BurnCoins(ctx, moduleAccount, coinsToBurn) if err != nil { @@ -340,64 +341,65 @@ func (k Keeper) SetGovDenom(ctx sdk.Context, denom string) { } // ValidateCollateral validates that a collateral is valid for use in cdps -func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coins) sdk.Error { +func (k Keeper) ValidateCollateral(ctx sdk.Context, collateral sdk.Coins) error { if len(collateral) != 1 { - return types.ErrInvalidCollateralLength(k.codespace, len(collateral)) + return sdkerrors.Wrapf(types.ErrInvalidCollateralLength, "%d", len(collateral)) } _, found := k.GetCollateral(ctx, collateral[0].Denom) if !found { - return types.ErrCollateralNotSupported(k.codespace, collateral[0].Denom) + return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateral[0].Denom) } return nil } // ValidatePrincipalAdd validates that an asset is valid for use as debt when creating a new cdp -func (k Keeper) ValidatePrincipalAdd(ctx sdk.Context, principal sdk.Coins) sdk.Error { +func (k Keeper) ValidatePrincipalAdd(ctx sdk.Context, principal sdk.Coins) error { for _, dc := range principal { dp, found := k.GetDebtParam(ctx, dc.Denom) if !found { - return types.ErrDebtNotSupported(k.codespace, dc.Denom) + return sdkerrors.Wrap(types.ErrDebtNotSupported, dc.Denom) } if dc.Amount.LT(dp.DebtFloor) { - return types.ErrBelowDebtFloor(k.codespace, sdk.NewCoins(dc), dp.DebtFloor) + return sdkerrors.Wrapf(types.ErrBelowDebtFloor, "proposed %s < minimum %s", dc, dp.DebtFloor) } } return nil } // ValidatePrincipalDraw validates that an asset is valid for use as debt when drawing debt off an existing cdp -func (k Keeper) ValidatePrincipalDraw(ctx sdk.Context, principal sdk.Coins) sdk.Error { +func (k Keeper) ValidatePrincipalDraw(ctx sdk.Context, principal sdk.Coins) error { for _, dc := range principal { _, found := k.GetDebtParam(ctx, dc.Denom) if !found { - return types.ErrDebtNotSupported(k.codespace, dc.Denom) + return sdkerrors.Wrap(types.ErrDebtNotSupported, dc.Denom) } } return nil } // ValidateDebtLimit validates that the input debt amount does not exceed the global debt limit or the debt limit for that collateral -func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralDenom string, principal sdk.Coins) sdk.Error { +func (k Keeper) ValidateDebtLimit(ctx sdk.Context, collateralDenom string, principal sdk.Coins) error { cp, found := k.GetCollateral(ctx, collateralDenom) if !found { - return types.ErrCollateralNotSupported(k.codespace, collateralDenom) + return sdkerrors.Wrap(types.ErrCollateralNotSupported, collateralDenom) } + for _, dc := range principal { totalPrincipal := k.GetTotalPrincipal(ctx, collateralDenom, dc.Denom).Add(dc.Amount) collateralLimit := cp.DebtLimit.AmountOf(dc.Denom) if totalPrincipal.GT(collateralLimit) { - return types.ErrExceedsDebtLimit(k.codespace, sdk.NewCoins(sdk.NewCoin(dc.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(dc.Denom, collateralLimit))) + return sdkerrors.Wrapf(types.ErrExceedsDebtLimit, "debt increase %s > collateral debt limit %s", sdk.NewCoins(sdk.NewCoin(dc.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(dc.Denom, collateralLimit))) } globalLimit := k.GetParams(ctx).GlobalDebtLimit.AmountOf(dc.Denom) if totalPrincipal.GT(globalLimit) { - return types.ErrExceedsDebtLimit(k.codespace, sdk.NewCoins(sdk.NewCoin(dc.Denom, totalPrincipal)), sdk.NewCoins(sdk.NewCoin(dc.Denom, globalLimit))) + return sdkerrors.Wrapf(types.ErrExceedsDebtLimit, "debt increase %s > global debt limit %s", sdk.NewCoin(dc.Denom, totalPrincipal), sdk.NewCoin(dc.Denom, globalLimit)) } } return nil } // ValidateCollateralizationRatio validate that adding the input principal doesn't put the cdp below the liquidation ratio -func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coins, principal sdk.Coins, fees sdk.Coins) sdk.Error { +func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coins, principal sdk.Coins, fees sdk.Coins) error { // collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, collateral, principal, fees) if err != nil { @@ -405,7 +407,7 @@ func (k Keeper) ValidateCollateralizationRatio(ctx sdk.Context, collateral sdk.C } liquidationRatio := k.getLiquidationRatio(ctx, collateral[0].Denom) if collateralizationRatio.LT(liquidationRatio) { - return types.ErrInvalidCollateralRatio(k.codespace, collateral[0].Denom, collateralizationRatio, liquidationRatio) + return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "collateral %s, collateral ratio %s, liquidation ratio %s", collateral[0].Denom, collateralizationRatio, liquidationRatio) } return nil } @@ -427,12 +429,11 @@ func (k Keeper) CalculateCollateralToDebtRatio(ctx sdk.Context, collateral sdk.C } // LoadAugmentedCDP creates a new augmented CDP from an existing CDP -func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) (types.AugmentedCDP, sdk.Error) { +func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) (types.AugmentedCDP, error) { // calculate additional fees periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) - totalFees := cdp.AccumulatedFees.Add(fees) - + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) + totalFees := cdp.AccumulatedFees.Add(fees...) // calculate collateralization ratio collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal, totalFees) if err != nil { @@ -444,7 +445,7 @@ func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) (types.Augmente for _, principalCoin := range cdp.Principal { totalDebt += principalCoin.Amount.Int64() } - for _, feeCoin := range cdp.AccumulatedFees.Add(fees) { + for _, feeCoin := range cdp.AccumulatedFees.Add(fees...) { totalDebt += feeCoin.Amount.Int64() } @@ -459,7 +460,7 @@ func (k Keeper) LoadAugmentedCDP(ctx sdk.Context, cdp types.CDP) (types.Augmente } // CalculateCollateralizationRatio returns the collateralization ratio of the input collateral to the input debt plus fees -func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coins, principal sdk.Coins, fees sdk.Coins) (sdk.Dec, sdk.Error) { +func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk.Coins, principal sdk.Coins, fees sdk.Coins) (sdk.Dec, error) { if collateral.IsZero() { return sdk.ZeroDec(), nil } @@ -485,7 +486,7 @@ func (k Keeper) CalculateCollateralizationRatio(ctx sdk.Context, collateral sdk. } // CalculateCollateralizationRatioFromAbsoluteRatio takes a coin's denom and an absolute ratio and returns the respective collateralization ratio -func (k Keeper) CalculateCollateralizationRatioFromAbsoluteRatio(ctx sdk.Context, collateralDenom string, absoluteRatio sdk.Dec) (sdk.Dec, sdk.Error) { +func (k Keeper) CalculateCollateralizationRatioFromAbsoluteRatio(ctx sdk.Context, collateralDenom string, absoluteRatio sdk.Dec) (sdk.Dec, error) { // get price collateral marketID := k.getMarketID(ctx, collateralDenom) price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID) diff --git a/x/cdp/keeper/cdp_test.go b/x/cdp/keeper/cdp_test.go index f7ca7982..0ca288d6 100644 --- a/x/cdp/keeper/cdp_test.go +++ b/x/cdp/keeper/cdp_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "testing" "time" @@ -44,17 +45,17 @@ func (suite *CdpTestSuite) TestAddCdp() { acc.SetCoins(cs(c("xrp", 200000000), c("btc", 500000000))) ak.SetAccount(suite.ctx, acc) err := suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 200000000)), cs(c("usdx", 26000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 500000000)), cs(c("usdx", 26000000))) suite.Error(err) // insufficient balance err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 200000000)), cs(c("xusd", 10000000))) - suite.Equal(types.CodeDebtNotSupported, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrDebtNotSupported)) acc2 := ak.NewAccountWithAddress(suite.ctx, addrs[1]) acc2.SetCoins(cs(c("btc", 500000000000))) ak.SetAccount(suite.ctx, acc2) err = suite.keeper.AddCdp(suite.ctx, addrs[1], cs(c("btc", 500000000000)), cs(c("usdx", 500000000001))) - suite.Equal(types.CodeExceedsDebtLimit, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit)) ctx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(time.Hour * 2)) pk := suite.app.GetPriceFeedKeeper() @@ -76,7 +77,7 @@ func (suite *CdpTestSuite) TestAddCdp() { suite.Equal(cs(c("usdx", 10000000), c("xrp", 100000000), c("btc", 500000000)), acc.GetCoins()) err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("btc", 500000000)), cs(c("usdx", 26667000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("btc", 500000000)), cs(c("usdx", 100000000))) suite.NoError(err) @@ -90,9 +91,9 @@ func (suite *CdpTestSuite) TestAddCdp() { suite.Equal(cs(c("usdx", 110000000), c("xrp", 100000000)), acc.GetCoins()) err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("lol", 100)), cs(c("usdx", 10))) - suite.Equal(types.CodeCollateralNotSupported, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCollateralNotSupported)) err = suite.keeper.AddCdp(suite.ctx, addrs[0], cs(c("xrp", 100)), cs(c("usdx", 10))) - suite.Equal(types.CodeCdpAlreadyExists, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpAlreadyExists)) } func (suite *CdpTestSuite) TestGetSetDenomByte() { @@ -247,10 +248,10 @@ func (suite *CdpTestSuite) TestValidateCollateral() { suite.NoError(err) c = sdk.NewCoins(sdk.NewCoin("lol", sdk.NewInt(1))) err = suite.keeper.ValidateCollateral(suite.ctx, c) - suite.Equal(types.CodeCollateralNotSupported, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCollateralNotSupported)) c = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1)), sdk.NewCoin("xrp", sdk.NewInt(1))) err = suite.keeper.ValidateCollateral(suite.ctx, c) - suite.Equal(types.CodeCollateralLengthInvalid, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralLength)) } func (suite *CdpTestSuite) TestValidatePrincipal() { @@ -262,10 +263,10 @@ func (suite *CdpTestSuite) TestValidatePrincipal() { suite.NoError(err) d = sdk.NewCoins(sdk.NewCoin("xusd", sdk.NewInt(1))) err = suite.keeper.ValidatePrincipalAdd(suite.ctx, d) - suite.Equal(types.CodeDebtNotSupported, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrDebtNotSupported)) d = sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(1000000000001))) err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp", d) - suite.Equal(types.CodeExceedsDebtLimit, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrExceedsDebtLimit)) d = sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100000000))) err = suite.keeper.ValidateDebtLimit(suite.ctx, "xrp", d) suite.NoError(err) @@ -290,16 +291,19 @@ func (suite *CdpTestSuite) TestMintBurnDebtCoins() { cd := cdps()[1] err := suite.keeper.MintDebtCoins(suite.ctx, types.ModuleName, suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) suite.NoError(err) - err = suite.keeper.MintDebtCoins(suite.ctx, "notamodule", suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) - suite.Error(err) + suite.Require().Panics(func() { + _ = suite.keeper.MintDebtCoins(suite.ctx, "notamodule", suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) + }) + sk := suite.app.GetSupplyKeeper() acc := sk.GetModuleAccount(suite.ctx, types.ModuleName) suite.Equal(cs(c("debt", 10000000)), acc.GetCoins()) err = suite.keeper.BurnDebtCoins(suite.ctx, types.ModuleName, suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) suite.NoError(err) - err = suite.keeper.BurnDebtCoins(suite.ctx, "notamodule", suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) - suite.Error(err) + suite.Require().Panics(func() { + _ = suite.keeper.BurnDebtCoins(suite.ctx, "notamodule", suite.keeper.GetDebtDenom(suite.ctx), cd.Principal) + }) sk = suite.app.GetSupplyKeeper() acc = sk.GetModuleAccount(suite.ctx, types.ModuleName) suite.Equal(sdk.Coins(nil), acc.GetCoins()) diff --git a/x/cdp/keeper/deposit.go b/x/cdp/keeper/deposit.go index 38b8dd85..6dcbf7ab 100644 --- a/x/cdp/keeper/deposit.go +++ b/x/cdp/keeper/deposit.go @@ -5,23 +5,24 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/cdp/types" ) // DepositCollateral adds collateral to a cdp -func (k Keeper) DepositCollateral(ctx sdk.Context, owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coins) sdk.Error { +func (k Keeper) DepositCollateral(ctx sdk.Context, owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coins) error { err := k.ValidateCollateral(ctx, collateral) if err != nil { return err } cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral[0].Denom) if !found { - return types.ErrCdpNotFound(k.codespace, owner, collateral[0].Denom) + return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, collateral %s", owner, collateral[0].Denom) } deposit, found := k.GetDeposit(ctx, cdp.ID, depositor) if found { - deposit.Amount = deposit.Amount.Add(collateral) + deposit.Amount = deposit.Amount.Add(collateral...) } else { deposit = types.NewDeposit(cdp.ID, depositor, collateral) } @@ -40,45 +41,45 @@ func (k Keeper) DepositCollateral(ctx sdk.Context, owner sdk.AccAddress, deposit k.SetDeposit(ctx, deposit) periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio) - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...) cdp.FeesUpdated = ctx.BlockTime() - cdp.Collateral = cdp.Collateral.Add(collateral) - collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + cdp.Collateral = cdp.Collateral.Add(collateral...) + collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) return nil } // WithdrawCollateral removes collateral from a cdp if it does not put the cdp below the liquidation ratio -func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coins) sdk.Error { +func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner sdk.AccAddress, depositor sdk.AccAddress, collateral sdk.Coins) error { err := k.ValidateCollateral(ctx, collateral) if err != nil { return err } cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, collateral[0].Denom) if !found { - return types.ErrCdpNotFound(k.codespace, owner, collateral[0].Denom) + return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, collateral %s", owner, collateral[0].Denom) } deposit, found := k.GetDeposit(ctx, cdp.ID, depositor) if !found { - return types.ErrDepositNotFound(k.codespace, depositor, cdp.ID) + return sdkerrors.Wrapf(types.ErrDepositNotFound, "depositor %s, collateral %s", depositor, collateral[0].Denom) } if collateral.IsAnyGT(deposit.Amount) { - return types.ErrInvalidWithdrawAmount(k.codespace, collateral, deposit.Amount) + return sdkerrors.Wrapf(types.ErrInvalidWithdrawAmount, "collateral %s, deposit %s", collateral, deposit.Amount) } periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) - collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Principal, cdp.AccumulatedFees.Add(fees)) + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) + collateralizationRatio, err := k.CalculateCollateralizationRatio(ctx, cdp.Collateral.Sub(collateral), cdp.Principal, cdp.AccumulatedFees.Add(fees...)) if err != nil { return err } liquidationRatio := k.getLiquidationRatio(ctx, collateral[0].Denom) if collateralizationRatio.LT(liquidationRatio) { - return types.ErrInvalidCollateralRatio(k.codespace, collateral[0].Denom, collateralizationRatio, liquidationRatio) + return sdkerrors.Wrapf(types.ErrInvalidCollateralRatio, "colateral %s, collateral ratio %s, liquidation ration %s", collateral[0].Denom, collateralizationRatio, liquidationRatio) } ctx.EventManager().EmitEvent( sdk.NewEvent( @@ -92,13 +93,13 @@ func (k Keeper) WithdrawCollateral(ctx sdk.Context, owner sdk.AccAddress, deposi if err != nil { panic(err) } - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio) - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...) cdp.FeesUpdated = ctx.BlockTime() cdp.Collateral = cdp.Collateral.Sub(collateral) - collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) deposit.Amount = deposit.Amount.Sub(collateral) diff --git a/x/cdp/keeper/deposit_test.go b/x/cdp/keeper/deposit_test.go index d9a5e6b0..b543ce3d 100644 --- a/x/cdp/keeper/deposit_test.go +++ b/x/cdp/keeper/deposit_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -76,10 +77,10 @@ func (suite *DepositTestSuite) TestDepositCollateral() { suite.Equal(i(90000000), acc.GetCoins().AmountOf("xrp")) err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], cs(c("btc", 1))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], cs(c("xrp", 1))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) err = suite.keeper.DepositCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], cs(c("xrp", 10000000))) suite.NoError(err) @@ -94,17 +95,17 @@ func (suite *DepositTestSuite) TestDepositCollateral() { func (suite *DepositTestSuite) TestWithdrawCollateral() { err := suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], cs(c("xrp", 400000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], cs(c("xrp", 321000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[0], cs(c("xrp", 10000000))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) cd, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1)) cd.AccumulatedFees = cs(c("usdx", 1)) suite.keeper.SetCDP(suite.ctx, cd) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], cs(c("xrp", 320000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[0], cs(c("xrp", 10000000))) suite.NoError(err) @@ -116,7 +117,7 @@ func (suite *DepositTestSuite) TestWithdrawCollateral() { suite.Equal(i(110000000), acc.GetCoins().AmountOf("xrp")) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[0], suite.addrs[1], cs(c("xrp", 10000000))) - suite.Equal(types.CodeDepositNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrDepositNotFound)) } func TestDepositTestSuite(t *testing.T) { diff --git a/x/cdp/keeper/draw.go b/x/cdp/keeper/draw.go index 94665f5b..876795d1 100644 --- a/x/cdp/keeper/draw.go +++ b/x/cdp/keeper/draw.go @@ -4,15 +4,16 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/cdp/types" ) // AddPrincipal adds debt to a cdp if the additional debt does not put the cdp below the liquidation ratio -func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, principal sdk.Coins) sdk.Error { +func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, principal sdk.Coins) error { // validation cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom) if !found { - return types.ErrCdpNotFound(k.codespace, owner, denom) + return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, denom) } err := k.ValidatePrincipalDraw(ctx, principal) if err != nil { @@ -26,9 +27,9 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string // fee calculation periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) - err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal.Add(principal), cdp.AccumulatedFees.Add(fees)) + err = k.ValidateCollateralizationRatio(ctx, cdp.Collateral, cdp.Principal.Add(principal...), cdp.AccumulatedFees.Add(fees...)) if err != nil { return err } @@ -59,19 +60,19 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string ) // remove old collateral:debt index - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio) // update cdp state - cdp.Principal = cdp.Principal.Add(principal) - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees) + cdp.Principal = cdp.Principal.Add(principal...) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...) cdp.FeesUpdated = ctx.BlockTime() // increment total principal for the input collateral type k.IncrementTotalPrincipal(ctx, cdp.Collateral[0].Denom, principal) // set cdp state and indexes in the store - collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) return nil @@ -79,32 +80,32 @@ func (k Keeper) AddPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string // RepayPrincipal removes debt from the cdp // If all debt is repaid, the collateral is returned to depositors and the cdp is removed from the store -func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, payment sdk.Coins) sdk.Error { +func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom string, payment sdk.Coins) error { // validation cdp, found := k.GetCdpByOwnerAndDenom(ctx, owner, denom) if !found { - return types.ErrCdpNotFound(k.codespace, owner, denom) + return sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", owner, denom) } // calculate fees periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) - err := k.ValidatePaymentCoins(ctx, cdp, payment, cdp.Principal.Add(cdp.AccumulatedFees).Add(fees)) + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) + err := k.ValidatePaymentCoins(ctx, cdp, payment, cdp.Principal.Add(cdp.AccumulatedFees...).Add(fees...)) if err != nil { return err } // calculate fee and principal payment - feePayment, principalPayment := k.calculatePayment(ctx, cdp.Principal.Add(cdp.AccumulatedFees).Add(fees), cdp.AccumulatedFees.Add(fees), payment) + feePayment, principalPayment := k.calculatePayment(ctx, cdp.Principal.Add(cdp.AccumulatedFees...).Add(fees...), cdp.AccumulatedFees.Add(fees...), payment) // send the payment from the sender to the cpd module - err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, feePayment.Add(principalPayment)) + err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, owner, types.ModuleName, feePayment.Add(principalPayment...)) if err != nil { return err } // burn the payment coins - err = k.supplyKeeper.BurnCoins(ctx, types.ModuleName, feePayment.Add(principalPayment)) + err = k.supplyKeeper.BurnCoins(ctx, types.ModuleName, feePayment.Add(principalPayment...)) if err != nil { panic(err) } @@ -112,7 +113,7 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri // burn the corresponding amount of debt coins cdpDebt := k.getModAccountDebt(ctx, types.ModuleName) paymentAmount := sdk.ZeroInt() - for _, c := range feePayment.Add(principalPayment) { + for _, c := range feePayment.Add(principalPayment...) { paymentAmount = paymentAmount.Add(c.Amount) } coinsToBurn := sdk.NewCoins(sdk.NewCoin(k.GetDebtDenom(ctx), paymentAmount)) @@ -128,24 +129,24 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCdpRepay, - sdk.NewAttribute(sdk.AttributeKeyAmount, feePayment.Add(principalPayment).String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, feePayment.Add(principalPayment...).String()), sdk.NewAttribute(types.AttributeKeyCdpID, fmt.Sprintf("%d", cdp.ID)), ), ) // remove the old collateral:debt ratio index - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.RemoveCdpCollateralRatioIndex(ctx, denom, cdp.ID, oldCollateralToDebtRatio) // update cdp state if !principalPayment.IsZero() { cdp.Principal = cdp.Principal.Sub(principalPayment) } - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees).Sub(feePayment) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...).Sub(feePayment) cdp.FeesUpdated = ctx.BlockTime() // decrement the total principal for the input collateral type - k.DecrementTotalPrincipal(ctx, denom, feePayment.Add(principalPayment)) + k.DecrementTotalPrincipal(ctx, denom, feePayment.Add(principalPayment...)) // if the debt is fully paid, return collateral to depositors, // and remove the cdp and indexes from the store @@ -165,13 +166,13 @@ func (k Keeper) RepayPrincipal(ctx sdk.Context, owner sdk.AccAddress, denom stri } // set cdp state and update indexes - collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) return nil } // ValidatePaymentCoins validates that the input coins are valid for repaying debt -func (k Keeper) ValidatePaymentCoins(ctx sdk.Context, cdp types.CDP, payment sdk.Coins, debt sdk.Coins) sdk.Error { +func (k Keeper) ValidatePaymentCoins(ctx sdk.Context, cdp types.CDP, payment sdk.Coins, debt sdk.Coins) error { subset := payment.DenomsSubsetOf(cdp.Principal) if !subset { var paymentDenoms []string @@ -182,13 +183,13 @@ func (k Keeper) ValidatePaymentCoins(ctx sdk.Context, cdp types.CDP, payment sdk for _, pc := range payment { paymentDenoms = append(paymentDenoms, pc.Denom) } - return types.ErrInvalidPaymentDenom(k.codespace, cdp.ID, principalDenoms, paymentDenoms) + return sdkerrors.Wrapf(types.ErrInvalidPayment, "cdp %d: expected %s, got %s", cdp.ID, principalDenoms, paymentDenoms) } for _, dc := range payment { dp, _ := k.GetDebtParam(ctx, dc.Denom) proposedBalance := cdp.Principal.AmountOf(dc.Denom).Sub(dc.Amount) if proposedBalance.GT(sdk.ZeroInt()) && proposedBalance.LT(dp.DebtFloor) { - return types.ErrBelowDebtFloor(k.codespace, sdk.NewCoins(sdk.NewCoin(dc.Denom, proposedBalance)), dp.DebtFloor) + return sdkerrors.Wrapf(types.ErrBelowDebtFloor, "proposed %s < minimum %s", sdk.NewCoins(sdk.NewCoin(dc.Denom, proposedBalance)), dp.DebtFloor) } } return nil @@ -223,12 +224,12 @@ func (k Keeper) calculatePayment(ctx sdk.Context, owed sdk.Coins, fees sdk.Coins for _, fc := range fees { if payment.AmountOf(fc.Denom).IsPositive() { if payment.AmountOf(fc.Denom).GT(fc.Amount) { - feePayment = feePayment.Add(sdk.NewCoins(fc)) + feePayment = feePayment.Add(fc) pc := sdk.NewCoin(fc.Denom, payment.AmountOf(fc.Denom).Sub(fc.Amount)) - principalPayment = principalPayment.Add(sdk.NewCoins(pc)) + principalPayment = principalPayment.Add(pc) } else { fc := sdk.NewCoin(fc.Denom, payment.AmountOf(fc.Denom)) - feePayment = feePayment.Add(sdk.NewCoins(fc)) + feePayment = feePayment.Add(fc) } } } diff --git a/x/cdp/keeper/draw_test.go b/x/cdp/keeper/draw_test.go index 9239ffe8..d353265e 100644 --- a/x/cdp/keeper/draw_test.go +++ b/x/cdp/keeper/draw_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "testing" "time" @@ -53,7 +54,7 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() { t, _ := suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1)) suite.Equal(cs(c("usdx", 20000000)), t.Principal) - ctd := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees)) + ctd := suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees...)) suite.Equal(d("20.0"), ctd) ts := suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("20.0")) suite.Equal(0, len(ts)) @@ -69,7 +70,7 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() { suite.NoError(err) t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1)) suite.Equal(cs(c("usdx", 20000000), c("susd", 10000000)), t.Principal) - ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees)) + ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees...)) suite.Equal(d("400000000").Quo(d("30000000")), ctd) ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("400").Quo(d("30"))) suite.Equal(0, len(ts)) @@ -82,18 +83,18 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() { suite.Equal(cs(c("xrp", 400000000), c("debt", 30000000)), acc.GetCoins()) err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[1], "xrp", cs(c("usdx", 10000000))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("xusd", 10000000))) - suite.Equal(types.CodeDebtNotSupported, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrDebtNotSupported)) err = suite.keeper.AddPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("usdx", 311000000))) - suite.Equal(types.CodeInvalidCollateralRatio, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidCollateralRatio)) err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("usdx", 10000000))) suite.NoError(err) t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1)) suite.Equal(cs(c("usdx", 10000000), c("susd", 10000000)), t.Principal) - ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees)) + ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees...)) suite.Equal(d("20.0"), ctd) ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("20.0")) suite.Equal(0, len(ts)) @@ -110,7 +111,7 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() { t, _ = suite.keeper.GetCDP(suite.ctx, "xrp", uint64(1)) suite.Equal(cs(c("usdx", 10000000)), t.Principal) - ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees)) + ctd = suite.keeper.CalculateCollateralToDebtRatio(suite.ctx, t.Collateral, t.Principal.Add(t.AccumulatedFees...)) suite.Equal(d("40.0"), ctd) ts = suite.keeper.GetAllCdpsByDenomAndRatio(suite.ctx, "xrp", d("40.0")) suite.Equal(0, len(ts)) @@ -123,12 +124,12 @@ func (suite *DrawTestSuite) TestAddRepayPrincipal() { suite.Equal(cs(c("xrp", 400000000), c("debt", 10000000)), acc.GetCoins()) err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("xusd", 10000000))) - suite.Equal(types.CodeInvalidPaymentDenom, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrInvalidPayment)) err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[1], "xrp", cs(c("xusd", 10000000))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("usdx", 9000000))) - suite.Equal(types.CodeBelowDebtFloor, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrBelowDebtFloor)) err = suite.keeper.RepayPrincipal(suite.ctx, suite.addrs[0], "xrp", cs(c("usdx", 10000000))) suite.NoError(err) diff --git a/x/cdp/keeper/fees.go b/x/cdp/keeper/fees.go index 936cfa74..e1f3c94e 100644 --- a/x/cdp/keeper/fees.go +++ b/x/cdp/keeper/fees.go @@ -22,7 +22,7 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coins, periods sdk. accumulator := sdk.NewDecFromInt(types.RelativePow(feeRateInt, periods, scalar)).Mul(sdk.SmallestDec()) feesAccumulated := (sdk.NewDecFromInt(pc.Amount).Mul(accumulator)).Sub(sdk.NewDecFromInt(pc.Amount)) // TODO this will always round down, causing precision loss between the sum of all fees in CDPs and surplus coins in liquidator account - newFees = newFees.Add(sdk.NewCoins(sdk.NewCoin(pc.Denom, feesAccumulated.TruncateInt()))) + newFees = newFees.Add(sdk.NewCoin(pc.Denom, feesAccumulated.TruncateInt())) } return newFees } @@ -33,7 +33,7 @@ func (k Keeper) CalculateFees(ctx sdk.Context, principal sdk.Coins, periods sdk. // Next we store the result of the fees in the cdp.AccumulatedFees field // Finally we set the cdp.FeesUpdated time to the current block time (ctx.BlockTime()) since that // is when we made the update -func (k Keeper) UpdateFeesForRiskyCdps(ctx sdk.Context, collateralDenom string, marketID string) sdk.Error { +func (k Keeper) UpdateFeesForRiskyCdps(ctx sdk.Context, collateralDenom string, marketID string) error { price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID) if err != nil { @@ -50,7 +50,7 @@ func (k Keeper) UpdateFeesForRiskyCdps(ctx sdk.Context, collateralDenom string, // now iterate over all the cdps based on collateral ratio k.IterateCdpsByCollateralRatio(ctx, collateralDenom, normalizedRatio, func(cdp types.CDP) bool { - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) // get the number of periods periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) @@ -58,11 +58,11 @@ func (k Keeper) UpdateFeesForRiskyCdps(ctx sdk.Context, collateralDenom string, additionalFees := k.CalculateFees(ctx, cdp.Principal, periods, collateralDenom) // now add the additional fees to the accumulated fees for the cdp - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(additionalFees) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(additionalFees...) // and set the fees updated time to the current block time since we just updated it cdp.FeesUpdated = ctx.BlockTime() - collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + collateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) k.RemoveCdpCollateralRatioIndex(ctx, cdp.Collateral[0].Denom, cdp.ID, oldCollateralToDebtRatio) k.SetCdpAndCollateralRatioIndex(ctx, cdp, collateralToDebtRatio) return false // this returns true when you want to stop iterating. Since we want to iterate through all we return false diff --git a/x/cdp/keeper/fees_test.go b/x/cdp/keeper/fees_test.go index 4367d368..1c6ca211 100644 --- a/x/cdp/keeper/fees_test.go +++ b/x/cdp/keeper/fees_test.go @@ -48,7 +48,7 @@ func (suite *FeeTestSuite) TestCalculateFeesPrecisionLoss() { suite.NoError(err) c := sdk.NewCoins(sdk.NewCoin("usdx", ri)) coins = append(coins, c) - total = total.Add(cs(sdk.NewCoin("usdx", ri))) + total = total.Add(sdk.NewCoin("usdx", ri)) } numBlocks := []int{100, 1000, 10000, 100000} @@ -57,13 +57,13 @@ func (suite *FeeTestSuite) TestCalculateFeesPrecisionLoss() { bulkFees := sdk.NewCoins() individualFees := sdk.NewCoins() for x := 0; x < nb; x++ { - fee := suite.keeper.CalculateFees(suite.ctx, total.Add(bulkFees), i(7), "xrp") - bulkFees = bulkFees.Add(fee) + fee := suite.keeper.CalculateFees(suite.ctx, total.Add(bulkFees...), i(7), "xrp") + bulkFees = bulkFees.Add(fee...) } for _, cns := range coins { fee := suite.keeper.CalculateFees(suite.ctx, cns, i(int64(nb*7)), "xrp") - individualFees = individualFees.Add(fee) + individualFees = individualFees.Add(fee...) } absError := (sdk.OneDec().Sub(sdk.NewDecFromInt(bulkFees[0].Amount).Quo(sdk.NewDecFromInt(individualFees[0].Amount)))).Abs() diff --git a/x/cdp/keeper/keeper.go b/x/cdp/keeper/keeper.go index 98d416a1..f7d3e585 100644 --- a/x/cdp/keeper/keeper.go +++ b/x/cdp/keeper/keeper.go @@ -19,21 +19,22 @@ type Keeper struct { supplyKeeper types.SupplyKeeper auctionKeeper types.AuctionKeeper accountKeeper types.AccountKeeper - codespace sdk.CodespaceType } // NewKeeper creates a new keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, pfk types.PricefeedKeeper, ak types.AuctionKeeper, sk types.SupplyKeeper, ack types.AccountKeeper, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, pfk types.PricefeedKeeper, ak types.AuctionKeeper, sk types.SupplyKeeper, ack types.AccountKeeper) Keeper { + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } return Keeper{ key: key, cdc: cdc, - paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()), + paramSubspace: paramstore, pricefeedKeeper: pfk, auctionKeeper: ak, supplyKeeper: sk, accountKeeper: ack, - codespace: codespace, } } diff --git a/x/cdp/keeper/keeper_bench_test.go b/x/cdp/keeper/keeper_bench_test.go index 20b88c82..bee4d37b 100644 --- a/x/cdp/keeper/keeper_bench_test.go +++ b/x/cdp/keeper/keeper_bench_test.go @@ -115,7 +115,7 @@ func BenchmarkCdpIteration(b *testing.B) { } -var errResult sdk.Error +var errResult error func BenchmarkCdpCreation(b *testing.B) { tApp := app.NewTestApp() diff --git a/x/cdp/keeper/querier.go b/x/cdp/keeper/querier.go index 440b4e1a..ba69c490 100644 --- a/x/cdp/keeper/querier.go +++ b/x/cdp/keeper/querier.go @@ -1,16 +1,20 @@ package keeper import ( + "fmt" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/kava-labs/kava/x/cdp/types" ) // NewQuerier returns a new querier function func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { switch path[0] { case types.QueryGetCdp: return queryGetCdp(ctx, req, keeper) @@ -23,85 +27,85 @@ func NewQuerier(keeper Keeper) sdk.Querier { case types.QueryGetCdpDeposits: return queryGetDeposits(ctx, req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown cdp query endpoint") + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint %s", types.ModuleName, path[0]) } } } // query a specific cdp -func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetCdp(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { var requestParams types.QueryCdpParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) if !valid { - return nil, types.ErrInvalidCollateralDenom(keeper.codespace, requestParams.CollateralDenom) + return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralDenom) } cdp, found := keeper.GetCdpByOwnerAndDenom(ctx, requestParams.Owner, requestParams.CollateralDenom) if !found { - return nil, types.ErrCdpNotFound(keeper.codespace, requestParams.Owner, requestParams.CollateralDenom) + return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralDenom) } augmentedCDP, err := keeper.LoadAugmentedCDP(ctx, cdp) if err != nil { - return nil, types.ErrLoadingAugmentedCDP(keeper.codespace, cdp.ID) + return nil, sdkerrors.Wrap(types.ErrLoadingAugmentedCDP, fmt.Sprintf("%v: %d", err.Error(), cdp.ID)) } - bz, err := codec.MarshalJSONIndent(keeper.cdc, augmentedCDP) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, augmentedCDP) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query deposits on a particular cdp -func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { var requestParams types.QueryCdpDeposits - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) if !valid { - return nil, types.ErrInvalidCollateralDenom(keeper.codespace, requestParams.CollateralDenom) + return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralDenom) } cdp, found := keeper.GetCdpByOwnerAndDenom(ctx, requestParams.Owner, requestParams.CollateralDenom) if !found { - return nil, types.ErrCdpNotFound(keeper.codespace, requestParams.Owner, requestParams.CollateralDenom) + return nil, sdkerrors.Wrapf(types.ErrCdpNotFound, "owner %s, denom %s", requestParams.Owner, requestParams.CollateralDenom) } deposits := keeper.GetDeposits(ctx, cdp.ID) - bz, err := codec.MarshalJSONIndent(keeper.cdc, deposits) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, deposits) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query cdps with matching denom and ratio LESS THAN the input ratio -func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { var requestParams types.QueryCdpsByRatioParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) if !valid { - return nil, types.ErrInvalidCollateralDenom(keeper.codespace, requestParams.CollateralDenom) + return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralDenom) } ratio, err := keeper.CalculateCollateralizationRatioFromAbsoluteRatio(ctx, requestParams.CollateralDenom, requestParams.Ratio) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could get collateralization ratio from absolute ratio", err.Error())) + return nil, sdkerrors.Wrap(err, "couldn't get collateralization ratio from absolute ratio") } cdps := keeper.GetAllCdpsByDenomAndRatio(ctx, requestParams.CollateralDenom, ratio) @@ -113,23 +117,23 @@ func queryGetCdpsByRatio(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) augmentedCDPs = append(augmentedCDPs, augmentedCDP) } } - bz, err := codec.MarshalJSONIndent(keeper.cdc, augmentedCDPs) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, augmentedCDPs) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query all cdps with matching collateral denom -func queryGetCdpsByDenom(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetCdpsByDenom(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { var requestParams types.QueryCdpsParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, valid := keeper.GetDenomPrefix(ctx, requestParams.CollateralDenom) if !valid { - return nil, types.ErrInvalidCollateralDenom(keeper.codespace, requestParams.CollateralDenom) + return nil, sdkerrors.Wrap(types.ErrInvalidCollateral, requestParams.CollateralDenom) } cdps := keeper.GetAllCdpsByDenom(ctx, requestParams.CollateralDenom) @@ -141,22 +145,22 @@ func queryGetCdpsByDenom(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) augmentedCDPs = append(augmentedCDPs, augmentedCDP) } } - bz, err := codec.MarshalJSONIndent(keeper.cdc, augmentedCDPs) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, augmentedCDPs) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query params in the cdp store -func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { // Get params params := keeper.GetParams(ctx) // Encode results - bz, err := codec.MarshalJSONIndent(keeper.cdc, params) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, params) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } diff --git a/x/cdp/keeper/savings.go b/x/cdp/keeper/savings.go index 68678e24..ec9c5944 100644 --- a/x/cdp/keeper/savings.go +++ b/x/cdp/keeper/savings.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" @@ -13,10 +14,10 @@ import ( ) // DistributeSavingsRate distributes surplus that has accumulated in the liquidator account to address holding stable coins according the the savings rate -func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) sdk.Error { +func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) error { dp, found := k.GetDebtParam(ctx, debtDenom) if !found { - return types.ErrDebtNotSupported(k.codespace, debtDenom) + return sdkerrors.Wrap(types.ErrDebtNotSupported, debtDenom) } savingsRateMacc := k.supplyKeeper.GetModuleAccount(ctx, types.SavingsRateMacc) surplusToDistribute := savingsRateMacc.GetCoins().AmountOf(dp.Denom) @@ -27,7 +28,7 @@ func (k Keeper) DistributeSavingsRate(ctx sdk.Context, debtDenom string) sdk.Err modAccountCoins := k.getModuleAccountCoins(ctx, dp.Denom) totalSupplyLessModAccounts := k.supplyKeeper.GetSupply(ctx).GetTotal().Sub(modAccountCoins) surplusDistributed := sdk.ZeroInt() - var iterationErr sdk.Error + var iterationErr error k.accountKeeper.IterateAccounts(ctx, func(acc authexported.Account) (stop bool) { _, ok := acc.(supplyexported.ModuleAccountI) if ok { diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index 6ef5d3e3..730c335b 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -15,13 +15,13 @@ import ( // 3. moves debt coins from the cdp module to the liquidator module account, // 4. decrements the total amount of principal outstanding for that collateral type // (this is the equivalent of saying that fees are no longer accumulated by a cdp once it gets liquidated) -func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) sdk.Error { +func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error { // Calculate the previous collateral ratio - oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees)) + oldCollateralToDebtRatio := k.CalculateCollateralToDebtRatio(ctx, cdp.Collateral, cdp.Principal.Add(cdp.AccumulatedFees...)) // Update fees periods := sdk.NewInt(ctx.BlockTime().Unix()).Sub(sdk.NewInt(cdp.FeesUpdated.Unix())) - fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees), periods, cdp.Collateral[0].Denom) - cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees) + fees := k.CalculateFees(ctx, cdp.Principal.Add(cdp.AccumulatedFees...), periods, cdp.Collateral[0].Denom) + cdp.AccumulatedFees = cdp.AccumulatedFees.Add(fees...) cdp.FeesUpdated = ctx.BlockTime() // Move debt coins from cdp to liquidator account @@ -70,7 +70,7 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) sdk.Error { coinsToDecrement := sdk.NewCoins(dc) if feeAmount.IsPositive() { feeCoins := sdk.NewCoins(sdk.NewCoin(dc.Denom, feeAmount)) - coinsToDecrement = coinsToDecrement.Add(feeCoins) + coinsToDecrement = coinsToDecrement.Add(feeCoins...) } k.DecrementTotalPrincipal(ctx, cdp.Collateral[0].Denom, coinsToDecrement) } @@ -100,11 +100,11 @@ func (k Keeper) HandleNewDebt(ctx sdk.Context, collateralDenom string, principal k.MintDebtCoins(ctx, types.ModuleName, k.GetDebtDenom(ctx), newFees) k.supplyKeeper.MintCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(principalDenom, newFeesSurplus))) k.supplyKeeper.MintCoins(ctx, types.SavingsRateMacc, sdk.NewCoins(sdk.NewCoin(principalDenom, newFeesSavings))) - k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, feeCoins.Add(newFees).AmountOf(principalDenom)) + k.SetTotalPrincipal(ctx, collateralDenom, principalDenom, feeCoins.Add(newFees...).AmountOf(principalDenom)) } // LiquidateCdps seizes collateral from all CDPs below the input liquidation ratio -func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, liquidationRatio sdk.Dec) sdk.Error { +func (k Keeper) LiquidateCdps(ctx sdk.Context, marketID string, denom string, liquidationRatio sdk.Dec) error { price, err := k.pricefeedKeeper.GetCurrentPrice(ctx, marketID) if err != nil { return err diff --git a/x/cdp/keeper/seize_test.go b/x/cdp/keeper/seize_test.go index 084423fc..355333bd 100644 --- a/x/cdp/keeper/seize_test.go +++ b/x/cdp/keeper/seize_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "math/rand" "testing" "time" @@ -145,7 +146,7 @@ func (suite *SeizeTestSuite) TestSeizeCollateral() { acc := ak.GetAccount(suite.ctx, suite.addrs[1]) suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64()) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], cs(c("xrp", 10))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) } func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { @@ -170,7 +171,7 @@ func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { acc := ak.GetAccount(suite.ctx, suite.addrs[1]) suite.Equal(p.Int64(), acc.GetCoins().AmountOf("usdx").Int64()) err = suite.keeper.WithdrawCollateral(suite.ctx, suite.addrs[1], suite.addrs[1], cs(c("xrp", 10))) - suite.Equal(types.CodeCdpNotFound, err.Result().Code) + suite.Require().True(errors.Is(err, types.ErrCdpNotFound)) } func (suite *SeizeTestSuite) TestLiquidateCdps() { diff --git a/x/cdp/module.go b/x/cdp/module.go index 5c2febb1..1794aba2 100644 --- a/x/cdp/module.go +++ b/x/cdp/module.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" sim "github.com/cosmos/cosmos-sdk/x/simulation" abci "github.com/tendermint/tendermint/abci/types" @@ -22,7 +23,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic app module basics object @@ -70,41 +71,22 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the cdp module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for cdp module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the cdp module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams creates randomized cdp param changes for the simulator. -func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { - return simulation.ParamChanges(r) -} - -//____________________________________________________________________________ - // AppModule app module type type AppModule struct { AppModuleBasic - AppModuleSimulation keeper Keeper + accountKeeper auth.AccountKeeper pricefeedKeeper PricefeedKeeper supplyKeeper SupplyKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper, pricefeedKeeper PricefeedKeeper, supplyKeeper SupplyKeeper) AppModule { +func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, pricefeedKeeper PricefeedKeeper, supplyKeeper SupplyKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, + accountKeeper: accountKeeper, pricefeedKeeper: pricefeedKeeper, supplyKeeper: supplyKeeper, } @@ -162,3 +144,30 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// GenerateGenesisState creates a randomized GenState of the cdp module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because cdp has no params. +func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for cdp module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the cdp module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.accountKeeper, am.keeper, am.pricefeedKeeper) +} diff --git a/x/cdp/simulation/decoder.go b/x/cdp/simulation/decoder.go index b2bc1fd0..1b0e347e 100644 --- a/x/cdp/simulation/decoder.go +++ b/x/cdp/simulation/decoder.go @@ -6,15 +6,16 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/libs/kv" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/kava-labs/kava/x/cdp/types" ) // DecodeStore unmarshals the KVPair's Value to the corresponding cdp type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Equal(kvA.Key[:1], types.CdpIDKeyPrefix): var cdpIDsA, cdpIDsB []uint64 diff --git a/x/cdp/simulation/decoder_test.go b/x/cdp/simulation/decoder_test.go index 52927624..7d092e6c 100644 --- a/x/cdp/simulation/decoder_test.go +++ b/x/cdp/simulation/decoder_test.go @@ -6,8 +6,7 @@ import ( "time" "github.com/stretchr/testify/require" - - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,17 +33,17 @@ func TestDecodeDistributionStore(t *testing.T) { prevDistTime := time.Now().UTC() cdp := types.CDP{ID: 1, FeesUpdated: prevDistTime, Collateral: oneCoins, Principal: oneCoins, AccumulatedFees: oneCoins} - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: types.CdpIDKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(cdpIds)}, - cmn.KVPair{Key: types.CdpKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(cdp)}, - cmn.KVPair{Key: types.CdpIDKey, Value: sdk.Uint64ToBigEndian(2)}, - cmn.KVPair{Key: types.CollateralRatioIndexPrefix, Value: sdk.Uint64ToBigEndian(10)}, - cmn.KVPair{Key: []byte(types.DebtDenomKey), Value: cdc.MustMarshalBinaryLengthPrefixed(denom)}, - cmn.KVPair{Key: []byte(types.GovDenomKey), Value: cdc.MustMarshalBinaryLengthPrefixed(denom)}, - cmn.KVPair{Key: []byte(types.DepositKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)}, - cmn.KVPair{Key: []byte(types.PrincipalKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(principal)}, - cmn.KVPair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevDistTime)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: types.CdpIDKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(cdpIds)}, + kv.Pair{Key: types.CdpKeyPrefix, Value: cdc.MustMarshalBinaryLengthPrefixed(cdp)}, + kv.Pair{Key: types.CdpIDKey, Value: sdk.Uint64ToBigEndian(2)}, + kv.Pair{Key: types.CollateralRatioIndexPrefix, Value: sdk.Uint64ToBigEndian(10)}, + kv.Pair{Key: []byte(types.DebtDenomKey), Value: cdc.MustMarshalBinaryLengthPrefixed(denom)}, + kv.Pair{Key: []byte(types.GovDenomKey), Value: cdc.MustMarshalBinaryLengthPrefixed(denom)}, + kv.Pair{Key: []byte(types.DepositKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(deposit)}, + kv.Pair{Key: []byte(types.PrincipalKeyPrefix), Value: cdc.MustMarshalBinaryLengthPrefixed(principal)}, + kv.Pair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevDistTime)}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/cdp/simulation/genesis.go b/x/cdp/simulation/genesis.go index 8f817997..6fa7680e 100644 --- a/x/cdp/simulation/genesis.go +++ b/x/cdp/simulation/genesis.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/supply" - supplyExported "github.com/cosmos/cosmos-sdk/x/supply/exported" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" "github.com/kava-labs/kava/x/cdp/types" ) @@ -23,7 +23,7 @@ func RandomizedGenState(simState *module.SimulationState) { simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis) totalCdpCoins := sdk.NewCoins() for _, acc := range authGenesis.Accounts { - _, ok := acc.(supplyExported.ModuleAccountI) + _, ok := acc.(supplyexported.ModuleAccountI) if ok { continue } @@ -33,18 +33,18 @@ func RandomizedGenState(simState *module.SimulationState) { sdk.NewCoin("btc", sdk.NewInt(int64(simState.Rand.Intn(500000000)))), sdk.NewCoin("usdx", sdk.NewInt(int64(simState.Rand.Intn(1000000000)))), ) - err := acc.SetCoins(acc.GetCoins().Add(coinsToAdd)) + err := acc.SetCoins(acc.GetCoins().Add(coinsToAdd...)) if err != nil { panic(err) } - totalCdpCoins = totalCdpCoins.Add(coinsToAdd) + totalCdpCoins = totalCdpCoins.Add(coinsToAdd...) authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, acc) } simState.GenState[auth.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis) var supplyGenesis supply.GenesisState simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis) - supplyGenesis.Supply = supplyGenesis.Supply.Add(totalCdpCoins) + supplyGenesis.Supply = supplyGenesis.Supply.Add(totalCdpCoins...) simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis) fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, cdpGenesis)) diff --git a/x/cdp/simulation/operations/msgs.go b/x/cdp/simulation/operations.go similarity index 50% rename from x/cdp/simulation/operations/msgs.go rename to x/cdp/simulation/operations.go index 4afe59b4..014b7fa1 100644 --- a/x/cdp/simulation/operations/msgs.go +++ b/x/cdp/simulation/operations.go @@ -1,48 +1,86 @@ -package operations +package simulation import ( - "fmt" "math/rand" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/kava-labs/kava/x/cdp" - "github.com/kava-labs/kava/x/pricefeed" + + appparams "github.com/kava-labs/kava/app/params" + "github.com/kava-labs/kava/x/cdp/keeper" + "github.com/kava-labs/kava/x/cdp/types" ) -// SimulateMsgCdp generates a MsgCreateCdp or MsgDepositCdp with random values. -func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) { +// Simulation operation weights constants +const ( + OpWeightMsgCdp = "op_weight_msg_cdp" +) - handler := cdp.NewHandler(k) - simacc := simulation.RandomAcc(r, accs) - acc := ak.GetAccount(ctx, simacc.Address) +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simulation.AppParams, cdc *codec.Codec, ak auth.AccountKeeper, + k keeper.Keeper, pfk types.PricefeedKeeper, +) simulation.WeightedOperations { + var weightMsgCdp int + + appParams.GetOrGenerate(cdc, OpWeightMsgCdp, &weightMsgCdp, nil, + func(_ *rand.Rand) { + weightMsgCdp = appparams.DefaultWeightMsgCdp + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgCdp, + SimulateMsgCdp(ak, k, pfk), + ), + } +} + +// SimulateMsgCdp generates a MsgCreateCdp or MsgDepositCdp with random values. +func SimulateMsgCdp(ak auth.AccountKeeper, k keeper.Keeper, pfk types.PricefeedKeeper) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { + + simAccount, _ := simulation.RandomAcc(r, accs) + acc := ak.GetAccount(ctx, simAccount.Address) if acc == nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, nil + return simulation.NoOpMsg(types.ModuleName), nil, nil } + coins := acc.GetCoins() collateralParams := k.GetParams(ctx).CollateralParams if len(collateralParams) == 0 { - return simulation.NoOpMsg(cdp.ModuleName), nil, nil + return simulation.NoOpMsg(types.ModuleName), nil, nil } + randCollateralParam := collateralParams[r.Intn(len(collateralParams))] randDebtAsset := randCollateralParam.DebtLimit[r.Intn(len(randCollateralParam.DebtLimit))] randDebtParam, _ := k.GetDebtParam(ctx, randDebtAsset.Denom) if coins.AmountOf(randCollateralParam.Denom).IsZero() { - return simulation.NoOpMsg(cdp.ModuleName), nil, nil + return simulation.NoOpMsg(types.ModuleName), nil, nil } price, err := pfk.GetCurrentPrice(ctx, randCollateralParam.MarketID) if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, err + return simulation.NoOpMsg(types.ModuleName), nil, err } // convert the price to the same units as the debt param priceShifted := ShiftDec(price.Price, randDebtParam.ConversionFactor) + spendableCoins := acc.SpendableCoins(ctx.BlockTime()) + fees, err := simulation.RandomFees(r, ctx, spendableCoins) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + spendableCoins = spendableCoins.Sub(fees) + existingCDP, found := k.GetCdpByOwnerAndDenom(ctx, acc.GetAddress(), randCollateralParam.Denom) if !found { // calculate the minimum amount of collateral that is needed to create a cdp with the debt floor amount of debt and the minimum liquidation ratio @@ -52,12 +90,12 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s minCollateralDeposit = ShiftDec(minCollateralDeposit, randCollateralParam.ConversionFactor) // convert to integer and always round up minCollateralDepositRounded := minCollateralDeposit.TruncateInt().Add(sdk.OneInt()) - if coins.AmountOf(randCollateralParam.Denom).LT(minCollateralDepositRounded) { + if spendableCoins.AmountOf(randCollateralParam.Denom).LT(minCollateralDepositRounded) { // account doesn't have enough funds to open a cdp for the min debt amount - return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "insufficient funds to open cdp", false, nil), nil, nil + return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "insufficient funds to open cdp", false, nil), nil, nil } // set the max collateral deposit to the amount of coins in the account - maxCollateralDeposit := coins.AmountOf(randCollateralParam.Denom) + maxCollateralDeposit := spendableCoins.AmountOf(randCollateralParam.Denom) // randomly select a collateral deposit amount collateralDeposit := sdk.NewInt(int64(simulation.RandIntBetween(r, int(minCollateralDepositRounded.Int64()), int(maxCollateralDeposit.Int64())))) @@ -69,53 +107,78 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s availableAssetDebt := randCollateralParam.DebtLimit.AmountOf(randDebtParam.Denom).Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Denom, randDebtParam.Denom)) if availableAssetDebt.LTE(randDebtParam.DebtFloor) { // debt limit has been reached - return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "debt limit reached, cannot open cdp", false, nil), nil, nil + return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "debt limit reached, cannot open cdp", false, nil), nil, nil } // ensure that the debt draw does not exceed the debt limit maxDebtDraw = sdk.MinInt(maxDebtDraw, availableAssetDebt) // randomly select a debt draw amount debtDraw := sdk.NewInt(int64(simulation.RandIntBetween(r, int(randDebtParam.DebtFloor.Int64()), int(maxDebtDraw.Int64())))) - msg := cdp.NewMsgCreateCDP(acc.GetAddress(), sdk.NewCoins(sdk.NewCoin(randCollateralParam.Denom, collateralDeposit)), sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, debtDraw))) - err := msg.ValidateBasic() + + msg := types.NewMsgCreateCDP(acc.GetAddress(), sdk.NewCoins(sdk.NewCoin(randCollateralParam.Denom, collateralDeposit)), sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, debtDraw))) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("expected msg to pass ValidateBasic: %v", err) + return simulation.NoOpMsg(types.ModuleName), nil, err } - ok := submitMsg(msg, handler, ctx) - if !ok { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("could not submit create cdp msg") - } - return simulation.NewOperationMsg(msg, ok, "create cdp"), nil, nil + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } // a cdp already exists, deposit to it, draw debt from it, or repay debt to it // close 25% of the time if canClose(acc, existingCDP, randDebtParam.Denom) && shouldClose(r) { - repaymentAmount := coins.AmountOf(randDebtParam.Denom) - msg := cdp.NewMsgRepayDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, repaymentAmount))) - err := msg.ValidateBasic() + repaymentAmount := spendableCoins.AmountOf(randDebtParam.Denom) + msg := types.NewMsgRepayDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, repaymentAmount))) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("expected repay (close) msg to pass ValidateBasic: %v", err) + return simulation.NoOpMsg(types.ModuleName), nil, err } - ok := submitMsg(msg, handler, ctx) - if !ok { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("could not submit repay (close) msg") - } - return simulation.NewOperationMsg(msg, ok, "repay debt (close) cdp"), nil, nil + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } // deposit 25% of the time if hasCoins(acc, randCollateralParam.Denom) && shouldDeposit(r) { - randDepositAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(acc.GetCoins().AmountOf(randCollateralParam.Denom).Int64())))) - msg := cdp.NewMsgDeposit(acc.GetAddress(), acc.GetAddress(), sdk.NewCoins(sdk.NewCoin(randCollateralParam.Denom, randDepositAmount))) - err := msg.ValidateBasic() + randDepositAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(spendableCoins.AmountOf(randCollateralParam.Denom).Int64())))) + msg := types.NewMsgDeposit(acc.GetAddress(), acc.GetAddress(), sdk.NewCoins(sdk.NewCoin(randCollateralParam.Denom, randDepositAmount))) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("expected deposit msg to pass ValidateBasic: %v", err) + return simulation.NoOpMsg(types.ModuleName), nil, err } - ok := submitMsg(msg, handler, ctx) - if !ok { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("could not submit deposit msg") - } - return simulation.NewOperationMsg(msg, ok, "deposit to cdp"), nil, nil + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } // draw debt 25% of the time @@ -130,109 +193,97 @@ func SimulateMsgCdp(ak auth.AccountKeeper, k cdp.Keeper, pfk pricefeed.Keeper) s maxDebt := (maxTotalDebt.Sub(sdk.NewDecFromInt(debt))).Mul(sdk.MustNewDecFromStr("0.95")).TruncateInt() if maxDebt.LTE(sdk.OneInt()) { // debt in cdp is maxed out - return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "cdp debt maxed out, cannot draw more debt", false, nil), nil, nil + return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "cdp debt maxed out, cannot draw more debt", false, nil), nil, nil } // check if the debt limit has been reached availableAssetDebt := randCollateralParam.DebtLimit.AmountOf(randDebtParam.Denom).Sub(k.GetTotalPrincipal(ctx, randCollateralParam.Denom, randDebtParam.Denom)) if availableAssetDebt.LTE(sdk.OneInt()) { // debt limit has been reached - return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation", "debt limit reached, cannot draw more debt", false, nil), nil, nil + return simulation.NewOperationMsgBasic(types.ModuleName, "no-operation", "debt limit reached, cannot draw more debt", false, nil), nil, nil } maxDraw := sdk.MinInt(maxDebt, availableAssetDebt) randDrawAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(maxDraw.Int64())))) - msg := cdp.NewMsgDrawDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, randDrawAmount))) - err := msg.ValidateBasic() - if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("expected draw msg to pass ValidateBasic: %v", err) - } - ok := submitMsg(msg, handler, ctx) - if !ok { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("could not submit draw msg") - } - return simulation.NewOperationMsg(msg, ok, "draw debt from cdp"), nil, nil + msg := types.NewMsgDrawDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, randDrawAmount))) + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err + } + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } // repay debt 25% of the time if hasCoins(acc, randDebtParam.Denom) { debt := existingCDP.Principal.AmountOf(randDebtParam.Denom) - maxRepay := acc.GetCoins().AmountOf(randDebtParam.Denom) + maxRepay := spendableCoins.AmountOf(randDebtParam.Denom) payableDebt := debt.Sub(randDebtParam.DebtFloor) if maxRepay.GT(payableDebt) { maxRepay = payableDebt } randRepayAmount := sdk.NewInt(int64(simulation.RandIntBetween(r, 1, int(maxRepay.Int64())))) - if debt.Equal(randDebtParam.DebtFloor) { - if acc.GetCoins().AmountOf(randDebtParam.Denom).GTE(debt) { - randRepayAmount = debt - } + if debt.Equal(randDebtParam.DebtFloor) && spendableCoins.AmountOf(randDebtParam.Denom).GTE(debt) { + randRepayAmount = debt } - msg := cdp.NewMsgRepayDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, randRepayAmount))) - err := msg.ValidateBasic() + + msg := types.NewMsgRepayDebt(acc.GetAddress(), randCollateralParam.Denom, sdk.NewCoins(sdk.NewCoin(randDebtParam.Denom, randRepayAmount))) + + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{acc.GetAccountNumber()}, + []uint64{acc.GetSequence()}, + simAccount.PrivKey, + ) + + _, result, err := app.Deliver(tx) if err != nil { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("expected repay msg to pass ValidateBasic: %v", err) + return simulation.NoOpMsg(types.ModuleName), nil, err } - ok := submitMsg(msg, handler, ctx) - if !ok { - return simulation.NoOpMsg(cdp.ModuleName), nil, fmt.Errorf("could not submit repay msg") - } - return simulation.NewOperationMsg(msg, ok, "repay debt cdp"), nil, nil + + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } - return simulation.NewOperationMsgBasic(cdp.ModuleName, "no-operation (no valid actions)", "", false, nil), nil, nil + return simulation.NoOpMsg(types.ModuleName), nil, nil } } -func submitMsg(msg sdk.Msg, handler sdk.Handler, ctx sdk.Context) (ok bool) { - ctx, write := ctx.CacheContext() - res := handler(ctx, msg) - if res.IsOK() { - write() - } else { - fmt.Println(res.Log) - } - return res.IsOK() -} - func shouldDraw(r *rand.Rand) bool { threshold := 50 value := simulation.RandIntBetween(r, 1, 100) - if value > threshold { - return true - } - return false + return value > threshold } func shouldDeposit(r *rand.Rand) bool { threshold := 66 value := simulation.RandIntBetween(r, 1, 100) - if value > threshold { - return true - } - return false + return value > threshold } func hasCoins(acc authexported.Account, denom string) bool { - if acc.GetCoins().AmountOf(denom).IsZero() { - return false - } - return true + return acc.GetCoins().AmountOf(denom).IsPositive() } func shouldClose(r *rand.Rand) bool { threshold := 75 value := simulation.RandIntBetween(r, 1, 100) - if value > threshold { - return true - } - return false + return value > threshold } -func canClose(acc authexported.Account, c cdp.CDP, denom string) bool { - repaymentAmount := c.Principal.Add(c.AccumulatedFees).AmountOf(denom) - if acc.GetCoins().AmountOf(denom).GTE(repaymentAmount) { - return true - } - return false +func canClose(acc authexported.Account, c types.CDP, denom string) bool { + repaymentAmount := c.Principal.Add(c.AccumulatedFees...).AmountOf(denom) + return acc.GetCoins().AmountOf(denom).GTE(repaymentAmount) } diff --git a/x/cdp/simulation/operations/utils.go b/x/cdp/simulation/utils.go similarity index 95% rename from x/cdp/simulation/operations/utils.go rename to x/cdp/simulation/utils.go index bf8a804e..f9daac89 100644 --- a/x/cdp/simulation/operations/utils.go +++ b/x/cdp/simulation/utils.go @@ -1,4 +1,4 @@ -package operations +package simulation import ( sdk "github.com/cosmos/cosmos-sdk/types" diff --git a/x/cdp/simulation/operations/utils_test.go b/x/cdp/simulation/utils_test.go similarity index 81% rename from x/cdp/simulation/operations/utils_test.go rename to x/cdp/simulation/utils_test.go index 5fedbef0..448366fa 100644 --- a/x/cdp/simulation/operations/utils_test.go +++ b/x/cdp/simulation/utils_test.go @@ -1,10 +1,10 @@ -package operations_test +package simulation_test import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/kava-labs/kava/x/cdp/simulation/operations" + "github.com/kava-labs/kava/x/cdp/simulation" "github.com/stretchr/testify/require" ) @@ -22,7 +22,7 @@ func TestShiftDec(t *testing.T) { for _, tt := range tests { t.Run(tt.value.String(), func(t *testing.T) { - require.Equal(t, tt.expected, operations.ShiftDec(tt.value, tt.shift)) + require.Equal(t, tt.expected, simulation.ShiftDec(tt.value, tt.shift)) }) } } diff --git a/x/cdp/types/errors.go b/x/cdp/types/errors.go index 95dc05b9..92953230 100644 --- a/x/cdp/types/errors.go +++ b/x/cdp/types/errors.go @@ -1,115 +1,42 @@ -// DONTCOVER package types import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// Error codes specific to cdp module -const ( - DefaultCodespace sdk.CodespaceType = ModuleName - CodeCdpAlreadyExists sdk.CodeType = 1 - CodeCollateralLengthInvalid sdk.CodeType = 2 - CodeCollateralNotSupported sdk.CodeType = 3 - CodeDebtNotSupported sdk.CodeType = 4 - CodeExceedsDebtLimit sdk.CodeType = 5 - CodeInvalidCollateralRatio sdk.CodeType = 6 - CodeCdpNotFound sdk.CodeType = 7 - CodeDepositNotFound sdk.CodeType = 8 - CodeInvalidDepositDenom sdk.CodeType = 9 - CodeInvalidPaymentDenom sdk.CodeType = 10 - CodeDepositNotAvailable sdk.CodeType = 11 - CodeInvalidCollateralDenom sdk.CodeType = 12 - CodeInvalidWithdrawAmount sdk.CodeType = 13 - CodeCdpNotAvailable sdk.CodeType = 14 - CodeBelowDebtFloor sdk.CodeType = 15 - CodePaymentExceedsDebt sdk.CodeType = 16 - CodeLoadingAugmentedCDP sdk.CodeType = 17 +// DONTCOVER + +var ( + // ErrCdpAlreadyExists error for duplicate cdps + ErrCdpAlreadyExists = sdkerrors.Register(ModuleName, 2, "cdp already exists") + // ErrInvalidCollateralLength error for invalid collateral input length + ErrInvalidCollateralLength = sdkerrors.Register(ModuleName, 3, "only one collateral type per cdp") + // ErrCollateralNotSupported error for unsupported collateral + ErrCollateralNotSupported = sdkerrors.Register(ModuleName, 4, "collateral not supported") + // ErrDebtNotSupported error for unsupported debt + ErrDebtNotSupported = sdkerrors.Register(ModuleName, 5, "debt not supported") + // ErrExceedsDebtLimit error for attempted draws that exceed debt limit + ErrExceedsDebtLimit = sdkerrors.Register(ModuleName, 6, "proposed debt increase would exceed debt limit") + // ErrInvalidCollateralRatio error for attempted draws that are below liquidation ratio + ErrInvalidCollateralRatio = sdkerrors.Register(ModuleName, 7, "proposed collateral ratio is below liquidation ratio") + // ErrCdpNotFound error cdp not found + ErrCdpNotFound = sdkerrors.Register(ModuleName, 8, "cdp not found") + // ErrDepositNotFound error for deposit not found + ErrDepositNotFound = sdkerrors.Register(ModuleName, 9, "deposit not found") + // ErrInvalidDeposit error for invalid deposit + ErrInvalidDeposit = sdkerrors.Register(ModuleName, 10, "invalid deposit") + // ErrInvalidCollateral error for invalid collateral + ErrInvalidCollateral = sdkerrors.Register(ModuleName, 11, "collateral not supported") + // ErrInvalidPayment error for invalid payment + ErrInvalidPayment = sdkerrors.Register(ModuleName, 12, "invalid payment") + //ErrDepositNotAvailable error for withdrawing deposits in liquidation + ErrDepositNotAvailable = sdkerrors.Register(ModuleName, 13, "deposit in liquidation") + // ErrInvalidWithdrawAmount error for invalid withdrawal amount + ErrInvalidWithdrawAmount = sdkerrors.Register(ModuleName, 14, "withdrawal amount exceeds deposit") + //ErrCdpNotAvailable error for depositing to a CDP in liquidation + ErrCdpNotAvailable = sdkerrors.Register(ModuleName, 15, "cannot modify cdp in liquidation") + // ErrBelowDebtFloor error for creating a cdp with debt below the minimum + ErrBelowDebtFloor = sdkerrors.Register(ModuleName, 16, "proposed cdp debt is below minimum") + // ErrLoadingAugmentedCDP error loading augmented cdp + ErrLoadingAugmentedCDP = sdkerrors.Register(ModuleName, 17, "augmented cdp could not be loaded from cdp") ) - -// ErrCdpAlreadyExists error for duplicate cdps -func ErrCdpAlreadyExists(codespace sdk.CodespaceType, owner sdk.AccAddress, denom string) sdk.Error { - return sdk.NewError(codespace, CodeCdpAlreadyExists, fmt.Sprintf("cdp for owner %s and collateral %s already exists", owner, denom)) -} - -// ErrInvalidCollateralLength error for invalid collateral input length -func ErrInvalidCollateralLength(codespace sdk.CodespaceType, length int) sdk.Error { - return sdk.NewError(codespace, CodeCollateralLengthInvalid, fmt.Sprintf("only one collateral type per cdp, has %d", length)) -} - -// ErrCollateralNotSupported error for unsupported collateral -func ErrCollateralNotSupported(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeCollateralNotSupported, fmt.Sprintf("collateral %s not supported", denom)) -} - -// ErrDebtNotSupported error for unsupported debt -func ErrDebtNotSupported(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeDebtNotSupported, fmt.Sprintf("collateral %s not supported", denom)) -} - -// ErrExceedsDebtLimit error for attempted draws that exceed debt limit -func ErrExceedsDebtLimit(codespace sdk.CodespaceType, proposed sdk.Coins, limit sdk.Coins) sdk.Error { - return sdk.NewError(codespace, CodeExceedsDebtLimit, fmt.Sprintf("proposed debt increase %s would exceed debt limit of %s", proposed, limit)) -} - -// ErrInvalidCollateralRatio error for attempted draws that are below liquidation ratio -func ErrInvalidCollateralRatio(codespace sdk.CodespaceType, denom string, collateralRatio sdk.Dec, liquidationRatio sdk.Dec) sdk.Error { - return sdk.NewError(codespace, CodeInvalidCollateralRatio, fmt.Sprintf("proposed collateral ratio of %s is below liqudation ratio of %s for collateral %s", collateralRatio, liquidationRatio, denom)) -} - -// ErrCdpNotFound error cdp not found -func ErrCdpNotFound(codespace sdk.CodespaceType, owner sdk.AccAddress, denom string) sdk.Error { - return sdk.NewError(codespace, CodeCdpNotFound, fmt.Sprintf("cdp for owner %s and collateral %s not found", owner, denom)) -} - -// ErrDepositNotFound error for deposit not found -func ErrDepositNotFound(codespace sdk.CodespaceType, depositor sdk.AccAddress, cdpID uint64) sdk.Error { - return sdk.NewError(codespace, CodeDepositNotFound, fmt.Sprintf("deposit for cdp %d not found for %s", cdpID, depositor)) -} - -// ErrInvalidDepositDenom error for invalid deposit denoms -func ErrInvalidDepositDenom(codespace sdk.CodespaceType, cdpID uint64, expected string, actual string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDepositDenom, fmt.Sprintf("invalid deposit for cdp %d, expects %s, got %s", cdpID, expected, actual)) -} - -// ErrInvalidPaymentDenom error for invalid payment denoms -func ErrInvalidPaymentDenom(codespace sdk.CodespaceType, cdpID uint64, expected []string, actual []string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidPaymentDenom, fmt.Sprintf("invalid payment for cdp %d, expects %s, got %s", cdpID, expected, actual)) -} - -//ErrDepositNotAvailable error for withdrawing deposits in liquidation -func ErrDepositNotAvailable(codespace sdk.CodespaceType, cdpID uint64, depositor sdk.AccAddress) sdk.Error { - return sdk.NewError(codespace, CodeDepositNotAvailable, fmt.Sprintf("deposit from %s for cdp %d in liquidation", depositor, cdpID)) -} - -// ErrInvalidCollateralDenom error for invalid collateral denoms -func ErrInvalidCollateralDenom(codespace sdk.CodespaceType, denom string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDepositDenom, fmt.Sprintf("invalid denom: %s", denom)) -} - -// ErrInvalidWithdrawAmount error for invalid withdrawal amount -func ErrInvalidWithdrawAmount(codespace sdk.CodespaceType, withdraw sdk.Coins, deposit sdk.Coins) sdk.Error { - return sdk.NewError(codespace, CodeInvalidWithdrawAmount, fmt.Sprintf("withdrawal amount of %s exceeds deposit of %s", withdraw, deposit)) -} - -//ErrCdpNotAvailable error for depositing to a CDP in liquidation -func ErrCdpNotAvailable(codespace sdk.CodespaceType, cdpID uint64) sdk.Error { - return sdk.NewError(codespace, CodeCdpNotAvailable, fmt.Sprintf("cannot modify cdp %d, in liquidation", cdpID)) -} - -// ErrBelowDebtFloor error for creating a cdp with debt below the minimum -func ErrBelowDebtFloor(codespace sdk.CodespaceType, debt sdk.Coins, floor sdk.Int) sdk.Error { - return sdk.NewError(codespace, CodeBelowDebtFloor, fmt.Sprintf("proposed cdp debt of %s is below the minimum of %s", debt, floor)) -} - -// ErrPaymentExceedsDebt error for repayments that are greater than the debt amount -func ErrPaymentExceedsDebt(codespace sdk.CodespaceType, payment sdk.Coins, principal sdk.Coins) sdk.Error { - return sdk.NewError(codespace, CodePaymentExceedsDebt, fmt.Sprintf("payment of %s exceeds debt of %s", payment, principal)) -} - -// ErrLoadingAugmentedCDP error loading augmented cdp -func ErrLoadingAugmentedCDP(codespace sdk.CodespaceType, cdpID uint64) sdk.Error { - return sdk.NewError(codespace, CodeCdpNotFound, fmt.Sprintf("augmented cdp could not be loaded from cdp id %d", cdpID)) -} diff --git a/x/cdp/types/expected_keepers.go b/x/cdp/types/expected_keepers.go index dcbd8314..ae5bb5bd 100644 --- a/x/cdp/types/expected_keepers.go +++ b/x/cdp/types/expected_keepers.go @@ -17,29 +17,29 @@ type SupplyKeeper interface { // TODO remove with genesis 2-phases refactor https://github.com/cosmos/cosmos-sdk/issues/2862 SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) sdk.Error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) sdk.Error - BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error - MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) } // PricefeedKeeper defines the expected interface for the pricefeed type PricefeedKeeper interface { - GetCurrentPrice(sdk.Context, string) (pftypes.CurrentPrice, sdk.Error) + GetCurrentPrice(sdk.Context, string) (pftypes.CurrentPrice, error) GetParams(sdk.Context) pftypes.Params // These are used for testing TODO replace mockApp with keeper in tests to remove these SetParams(sdk.Context, pftypes.Params) - SetPrice(sdk.Context, sdk.AccAddress, string, sdk.Dec, time.Time) (pftypes.PostedPrice, sdk.Error) - SetCurrentPrices(sdk.Context, string) sdk.Error + SetPrice(sdk.Context, sdk.AccAddress, string, sdk.Dec, time.Time) (pftypes.PostedPrice, error) + SetCurrentPrices(sdk.Context, string) error } // AuctionKeeper expected interface for the auction keeper (noalias) type AuctionKeeper interface { - StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin, bidDenom string) (uint64, sdk.Error) - StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin, debt sdk.Coin) (uint64, sdk.Error) - StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin) (uint64, sdk.Error) + StartSurplusAuction(ctx sdk.Context, seller string, lot sdk.Coin, bidDenom string) (uint64, error) + StartDebtAuction(ctx sdk.Context, buyer string, bid sdk.Coin, initialLot sdk.Coin, debt sdk.Coin) (uint64, error) + StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin) (uint64, error) } // AccountKeeper expected interface for the account keeper (noalias) diff --git a/x/cdp/types/msg.go b/x/cdp/types/msg.go index 1fdcc669..9b6df344 100644 --- a/x/cdp/types/msg.go +++ b/x/cdp/types/msg.go @@ -1,9 +1,12 @@ package types import ( + "errors" "fmt" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // ensure Msg interface compliance at compile time @@ -38,24 +41,18 @@ func (msg MsgCreateCDP) Route() string { return RouterKey } func (msg MsgCreateCDP) Type() string { return "create_cdp" } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgCreateCDP) ValidateBasic() sdk.Error { +func (msg MsgCreateCDP) ValidateBasic() error { if msg.Sender.Empty() { - return sdk.ErrInternal("invalid (empty) sender address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if len(msg.Collateral) != 1 { - return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral)) + if msg.Collateral.Len() != 1 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cdps do not support multiple collateral types: %s", msg.Collateral) } if !msg.Collateral.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral)) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral) } - if !msg.Collateral.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral)) - } - if !msg.Principal.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid principal amount: %s", msg.Principal)) - } - if !msg.Principal.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative principal amount: %s", msg.Principal)) + if msg.Principal.Empty() || !msg.Principal.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal) } return nil } @@ -103,21 +100,18 @@ func (msg MsgDeposit) Route() string { return RouterKey } func (msg MsgDeposit) Type() string { return "deposit_cdp" } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgDeposit) ValidateBasic() sdk.Error { +func (msg MsgDeposit) ValidateBasic() error { if msg.Owner.Empty() { - return sdk.ErrInternal("invalid (empty) sender address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "owner address cannot be empty") } if msg.Depositor.Empty() { - return sdk.ErrInternal("invalid (empty) owner address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if len(msg.Collateral) != 1 { - return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral)) + if msg.Collateral.Len() != 1 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cdps do not support multiple collateral types: %s", msg.Collateral) } if !msg.Collateral.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral)) - } - if !msg.Collateral.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral)) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral) } return nil } @@ -165,21 +159,18 @@ func (msg MsgWithdraw) Route() string { return RouterKey } func (msg MsgWithdraw) Type() string { return "withdraw_cdp" } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgWithdraw) ValidateBasic() sdk.Error { +func (msg MsgWithdraw) ValidateBasic() error { if msg.Owner.Empty() { - return sdk.ErrInternal("invalid (empty) sender address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "owner address cannot be empty") } if msg.Depositor.Empty() { - return sdk.ErrInternal("invalid (empty) owner address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if len(msg.Collateral) != 1 { - return sdk.ErrInvalidCoins(fmt.Sprintf("cdps do not support multiple collateral types: received %s", msg.Collateral)) + if msg.Collateral.Len() != 1 { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "cdps do not support multiple collateral types: %s", msg.Collateral) } if !msg.Collateral.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid collateral amount: %s", msg.Collateral)) - } - if !msg.Collateral.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative collateral amount: %s", msg.Collateral)) + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "collateral amount %s", msg.Collateral) } return nil } @@ -227,18 +218,15 @@ func (msg MsgDrawDebt) Route() string { return RouterKey } func (msg MsgDrawDebt) Type() string { return "draw_cdp" } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgDrawDebt) ValidateBasic() sdk.Error { +func (msg MsgDrawDebt) ValidateBasic() error { if msg.Sender.Empty() { - return sdk.ErrInternal("invalid (empty) sender address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if msg.CdpDenom == "" { - return sdk.ErrInternal("invalid (empty) cdp denom") + if strings.TrimSpace(msg.CdpDenom) == "" { + return errors.New("cdp denom cannot be blank") } - if !msg.Principal.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid principal amount: %s", msg.Principal)) - } - if !msg.Principal.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative principal amount: %s", msg.Principal)) + if msg.Principal.Empty() || !msg.Principal.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "principal amount %s", msg.Principal) } return nil } @@ -286,18 +274,15 @@ func (msg MsgRepayDebt) Route() string { return RouterKey } func (msg MsgRepayDebt) Type() string { return "repay_cdp" } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgRepayDebt) ValidateBasic() sdk.Error { +func (msg MsgRepayDebt) ValidateBasic() error { if msg.Sender.Empty() { - return sdk.ErrInternal("invalid (empty) sender address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if msg.CdpDenom == "" { - return sdk.ErrInternal("invalid (empty) cdp denom") + if strings.TrimSpace(msg.CdpDenom) == "" { + return errors.New("cdp denom cannot be blank") } - if !msg.Payment.IsValid() { - return sdk.ErrInvalidCoins(fmt.Sprintf("invalid payment amount: %s", msg.Payment)) - } - if !msg.Payment.IsAllPositive() { - return sdk.ErrInvalidCoins(fmt.Sprintf("negative payment amount: %s", msg.Payment)) + if msg.Payment.Empty() || !msg.Payment.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "payment amount %s", msg.Payment) } return nil } diff --git a/x/cdp/types/msg_test.go b/x/cdp/types/msg_test.go index dd034a0d..814f6c68 100644 --- a/x/cdp/types/msg_test.go +++ b/x/cdp/types/msg_test.go @@ -33,16 +33,16 @@ func TestMsgCreateCDP(t *testing.T) { {"create cdp empty owner", sdk.AccAddress{}, coinsSingle, coinsSingle, false}, } - for i, tc := range tests { + for _, tc := range tests { msg := NewMsgCreateCDP( tc.sender, tc.collateral, tc.principal, ) if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", tc.description) } } } @@ -63,16 +63,16 @@ func TestMsgDeposit(t *testing.T) { {"deposit empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, false}, } - for i, tc := range tests { + for _, tc := range tests { msg := NewMsgDeposit( tc.sender, tc.depositor, tc.collateral, ) if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", tc.description) } } } @@ -93,16 +93,16 @@ func TestMsgWithdraw(t *testing.T) { {"withdraw empty depositor", addrs[0], sdk.AccAddress{}, coinsSingle, false}, } - for i, tc := range tests { + for _, tc := range tests { msg := NewMsgWithdraw( tc.sender, tc.depositor, tc.collateral, ) if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", tc.description) } } } @@ -122,16 +122,16 @@ func TestMsgDrawDebt(t *testing.T) { {"draw debt empty denom", sdk.AccAddress{}, "", coinsSingle, false}, } - for i, tc := range tests { + for _, tc := range tests { msg := NewMsgDrawDebt( tc.sender, tc.denom, tc.principal, ) if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", tc.description) } } } @@ -151,16 +151,16 @@ func TestMsgRepayDebt(t *testing.T) { {"repay debt empty denom", sdk.AccAddress{}, "", coinsSingle, false}, } - for i, tc := range tests { + for _, tc := range tests { msg := NewMsgRepayDebt( tc.sender, tc.denom, tc.payment, ) if tc.expectPass { - require.NoError(t, msg.ValidateBasic(), "test: %v", i) + require.NoError(t, msg.ValidateBasic(), "test: %v", tc.description) } else { - require.Error(t, msg.ValidateBasic(), "test: %v", i) + require.Error(t, msg.ValidateBasic(), "test: %v", tc.description) } } } diff --git a/x/cdp/types/params.go b/x/cdp/types/params.go index db54cbf5..5f1fd938 100644 --- a/x/cdp/types/params.go +++ b/x/cdp/types/params.go @@ -2,9 +2,11 @@ package types import ( "fmt" + "strings" "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/params" tmtime "github.com/tendermint/tendermint/types/time" ) @@ -156,30 +158,49 @@ func ParamKeyTable() params.KeyTable { // nolint func (p *Params) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - {Key: KeyGlobalDebtLimit, Value: &p.GlobalDebtLimit}, - {Key: KeyCollateralParams, Value: &p.CollateralParams}, - {Key: KeyDebtParams, Value: &p.DebtParams}, - {Key: KeyCircuitBreaker, Value: &p.CircuitBreaker}, - {Key: KeySurplusThreshold, Value: &p.SurplusAuctionThreshold}, - {Key: KeyDebtThreshold, Value: &p.DebtAuctionThreshold}, - {Key: KeyDistributionFrequency, Value: &p.SavingsDistributionFrequency}, + params.NewParamSetPair(KeyGlobalDebtLimit, &p.GlobalDebtLimit, validateGlobalDebtLimitParam), + params.NewParamSetPair(KeyCollateralParams, &p.CollateralParams, validateCollateralParams), + params.NewParamSetPair(KeyDebtParams, &p.DebtParams, validateDebtParams), + params.NewParamSetPair(KeyCircuitBreaker, &p.CircuitBreaker, validateCircuitBreakerParam), + params.NewParamSetPair(KeySurplusThreshold, &p.SurplusAuctionThreshold, validateSurplusAuctionThresholdParam), + params.NewParamSetPair(KeyDebtThreshold, &p.DebtAuctionThreshold, validateDebtAuctionThresholdParam), + params.NewParamSetPair(KeyDistributionFrequency, &p.SavingsDistributionFrequency, validateSavingsDistributionFrequencyParam), } } // Validate checks that the parameters have valid values. func (p Params) Validate() error { - // validate debt params - debtDenoms := make(map[string]int) - for _, dp := range p.DebtParams { - _, found := debtDenoms[dp.Denom] - if found { - return fmt.Errorf("duplicate debt denom: %s", dp.Denom) - } - if dp.SavingsRate.LT(sdk.ZeroDec()) || dp.SavingsRate.GT(sdk.OneDec()) { - return fmt.Errorf("savings rate should be between 0 and 1, is %s for %s", dp.SavingsRate, dp.Denom) - } - debtDenoms[dp.Denom] = 1 + if err := validateGlobalDebtLimitParam(p.GlobalDebtLimit); err != nil { + return err + } + if err := validateCollateralParams(p.CollateralParams); err != nil { + return err + } + + if err := validateDebtParams(p.DebtParams); err != nil { + return err + } + + if err := validateCircuitBreakerParam(p.CircuitBreaker); err != nil { + return err + } + + if err := validateSurplusAuctionThresholdParam(p.SurplusAuctionThreshold); err != nil { + return err + } + + if err := validateDebtAuctionThresholdParam(p.DebtAuctionThreshold); err != nil { + return err + } + + if err := validateSavingsDistributionFrequencyParam(p.SavingsDistributionFrequency); err != nil { + return err + } + + debtDenoms := make(map[string]bool) + for _, dp := range p.DebtParams { + debtDenoms[dp.Denom] = true } // validate collateral params @@ -187,38 +208,76 @@ func (p Params) Validate() error { prefixDupMap := make(map[int]int) collateralParamsDebtLimit := sdk.Coins{} for _, cp := range p.CollateralParams { + + prefix := int(cp.Prefix) + prefixDupMap[prefix] = 1 + collateralDupMap[cp.Denom] = 1 + + collateralParamsDebtLimit = collateralParamsDebtLimit.Add(cp.DebtLimit...) + + if cp.DebtLimit.IsAnyGT(p.GlobalDebtLimit) { + return fmt.Errorf("collateral debt limit for %s exceeds global debt limit: \n\tglobal debt limit: %s\n\tcollateral debt limits: %s", + cp.Denom, p.GlobalDebtLimit, cp.DebtLimit) + } + } + + if collateralParamsDebtLimit.IsAnyGT(p.GlobalDebtLimit) { + return fmt.Errorf("collateral debt limit exceeds global debt limit:\n\tglobal debt limit: %s\n\tcollateral debt limits: %s", + p.GlobalDebtLimit, collateralParamsDebtLimit) + } + + return nil +} + +func validateGlobalDebtLimitParam(i interface{}) error { + globalDebtLimit, ok := i.(sdk.Coins) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if !globalDebtLimit.IsValid() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "global debt limit %s", globalDebtLimit.String()) + } + + return nil +} + +func validateCollateralParams(i interface{}) error { + collateralParams, ok := i.(CollateralParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + collateralDupMap := make(map[string]bool) + prefixDupMap := make(map[int]bool) + for _, cp := range collateralParams { + if strings.TrimSpace(cp.Denom) == "" { + return fmt.Errorf("debt denom cannot be blank %s", cp) + } + prefix := int(cp.Prefix) if prefix < minCollateralPrefix || prefix > maxCollateralPrefix { return fmt.Errorf("invalid prefix for collateral denom %s: %b", cp.Denom, cp.Prefix) } + _, found := prefixDupMap[prefix] if found { return fmt.Errorf("duplicate prefix for collateral denom %s: %v", cp.Denom, []byte{cp.Prefix}) } - prefixDupMap[prefix] = 1 - _, found = collateralDupMap[cp.Denom] + prefixDupMap[prefix] = true + _, found = collateralDupMap[cp.Denom] if found { return fmt.Errorf("duplicate collateral denom: %s", cp.Denom) } - collateralDupMap[cp.Denom] = 1 - if cp.DebtLimit.IsAnyNegative() { + collateralDupMap[cp.Denom] = true + + if !cp.DebtLimit.IsValid() { return fmt.Errorf("debt limit for all collaterals should be positive, is %s for %s", cp.DebtLimit, cp.Denom) } - collateralParamsDebtLimit = collateralParamsDebtLimit.Add(cp.DebtLimit) - for _, dc := range cp.DebtLimit { - _, found := debtDenoms[dc.Denom] - if !found { - return fmt.Errorf("debt limit for collateral %s contains invalid debt denom %s", cp.Denom, dc.Denom) - } - } - if cp.DebtLimit.IsAnyGT(p.GlobalDebtLimit) { - return fmt.Errorf("collateral debt limit for %s exceeds global debt limit: \n\tglobal debt limit: %s\n\tcollateral debt limits: %s", - cp.Denom, p.GlobalDebtLimit, cp.DebtLimit) - } if cp.LiquidationPenalty.LT(sdk.ZeroDec()) || cp.LiquidationPenalty.GT(sdk.OneDec()) { return fmt.Errorf("liquidation penalty should be between 0 and 1, is %s for %s", cp.LiquidationPenalty, cp.Denom) } @@ -229,24 +288,82 @@ func (p Params) Validate() error { return fmt.Errorf("stability fee must be ≥ 1.0, is %s for %s", cp.StabilityFee, cp.Denom) } } - if collateralParamsDebtLimit.IsAnyGT(p.GlobalDebtLimit) { - return fmt.Errorf("collateral debt limit exceeds global debt limit:\n\tglobal debt limit: %s\n\tcollateral debt limits: %s", - p.GlobalDebtLimit, collateralParamsDebtLimit) - } - // validate global params - if p.GlobalDebtLimit.IsAnyNegative() { - return fmt.Errorf("global debt limit should be positive for all debt tokens, is %s", p.GlobalDebtLimit) - } - if !p.SurplusAuctionThreshold.IsPositive() { - return fmt.Errorf("surplus auction threshold should be positive, is %s", p.SurplusAuctionThreshold) - } - if !p.DebtAuctionThreshold.IsPositive() { - return fmt.Errorf("debt auction threshold should be positive, is %s", p.DebtAuctionThreshold) - } - - if p.SavingsDistributionFrequency.Seconds() <= float64(0) { - return fmt.Errorf("savings distribution frequency should be positive, is %s", p.SavingsDistributionFrequency) - } + return nil +} + +func validateDebtParams(i interface{}) error { + debtParams, ok := i.(DebtParams) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + // validate debt params + debtDenoms := make(map[string]bool) + for _, dp := range debtParams { + if strings.TrimSpace(dp.Denom) == "" { + return fmt.Errorf("debt denom cannot be blank %s", dp) + } + + _, found := debtDenoms[dp.Denom] + if found { + return fmt.Errorf("duplicate debt denom: %s", dp.Denom) + } + + if dp.SavingsRate.LT(sdk.ZeroDec()) || dp.SavingsRate.GT(sdk.OneDec()) { + return fmt.Errorf("savings rate should be between 0 and 1, is %s for %s", dp.SavingsRate, dp.Denom) + } + + debtDenoms[dp.Denom] = true + } + + return nil +} + +func validateCircuitBreakerParam(i interface{}) error { + _, ok := i.(bool) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + +func validateSurplusAuctionThresholdParam(i interface{}) error { + sat, ok := i.(sdk.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if !sat.IsPositive() { + return fmt.Errorf("surplus auction threshold should be positive: %s", sat) + } + + return nil +} + +func validateDebtAuctionThresholdParam(i interface{}) error { + dat, ok := i.(sdk.Int) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if !dat.IsPositive() { + return fmt.Errorf("debt auction threshold should be positive: %s", dat) + } + + return nil +} + +func validateSavingsDistributionFrequencyParam(i interface{}) error { + sdf, ok := i.(time.Duration) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + if sdf.Seconds() <= float64(0) { + return fmt.Errorf("savings distribution frequency should be positive: %s", sdf) + } + return nil } diff --git a/x/kavadist/alias.go b/x/kavadist/alias.go index e9295e14..04cdf035 100644 --- a/x/kavadist/alias.go +++ b/x/kavadist/alias.go @@ -1,8 +1,3 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/kava-labs/kava/x/kavadist/keeper -// ALIASGEN: github.com/kava-labs/kava/x/kavadist/types package kavadist import ( @@ -10,8 +5,13 @@ import ( "github.com/kava-labs/kava/x/kavadist/types" ) +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/kava-labs/kava/x/kavadist/keeper +// ALIASGEN: github.com/kava-labs/kava/x/kavadist/types + +// nolint const ( - DefaultCodespace = types.DefaultCodespace EventTypeKavaDist = types.EventTypeKavaDist AttributeKeyInflation = types.AttributeKeyInflation ModuleName = types.ModuleName @@ -23,6 +23,7 @@ const ( QueryGetParams = types.QueryGetParams ) +// nolint var ( // functions aliases NewKeeper = keeper.NewKeeper @@ -45,6 +46,7 @@ var ( GovDenom = types.GovDenom ) +// nolint type ( Keeper = keeper.Keeper GenesisState = types.GenesisState diff --git a/x/kavadist/handler.go b/x/kavadist/handler.go index 585efbbb..ed7a1b60 100644 --- a/x/kavadist/handler.go +++ b/x/kavadist/handler.go @@ -1,18 +1,16 @@ package kavadist import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // NewHandler creates an sdk.Handler for kavadist messages func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { switch msg := msg.(type) { default: - errMsg := fmt.Sprintf("unrecognized cdp msg type: %T", msg) - return sdk.ErrUnknownRequest(errMsg).Result() + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } -} \ No newline at end of file +} diff --git a/x/kavadist/keeper/keeper.go b/x/kavadist/keeper/keeper.go index cf8729c6..34faa329 100644 --- a/x/kavadist/keeper/keeper.go +++ b/x/kavadist/keeper/keeper.go @@ -16,18 +16,19 @@ type Keeper struct { cdc *codec.Codec paramSubspace subspace.Subspace supplyKeeper types.SupplyKeeper - codespace sdk.CodespaceType } // NewKeeper creates a new keeper -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper, codespace sdk.CodespaceType) Keeper { +func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper) Keeper { + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } return Keeper{ key: key, cdc: cdc, - paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()), + paramSubspace: paramstore, supplyKeeper: sk, - codespace: codespace, } } diff --git a/x/kavadist/keeper/mint.go b/x/kavadist/keeper/mint.go index 4a1da2c3..c5d19215 100644 --- a/x/kavadist/keeper/mint.go +++ b/x/kavadist/keeper/mint.go @@ -7,7 +7,7 @@ import ( ) // MintPeriodInflation mints new tokens according to the inflation schedule specified in the paramters -func (k Keeper) MintPeriodInflation(ctx sdk.Context) sdk.Error { +func (k Keeper) MintPeriodInflation(ctx sdk.Context) error { params := k.GetParams(ctx) if !params.Active { ctx.EventManager().EmitEvent( @@ -59,7 +59,7 @@ func (k Keeper) MintPeriodInflation(ctx sdk.Context) sdk.Error { return nil } -func (k Keeper) mintInflationaryCoins(ctx sdk.Context, inflationRate sdk.Dec, timePeriods sdk.Int, denom string) sdk.Error { +func (k Keeper) mintInflationaryCoins(ctx sdk.Context, inflationRate sdk.Dec, timePeriods sdk.Int, denom string) error { totalSupply := k.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf(denom) // used to scale accumulator calculations by 10^18 scalar := sdk.NewInt(1000000000000000000) diff --git a/x/kavadist/module.go b/x/kavadist/module.go index 218655c6..f24005c8 100644 --- a/x/kavadist/module.go +++ b/x/kavadist/module.go @@ -20,7 +20,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic app module basics object @@ -62,30 +62,9 @@ func (AppModuleBasic) GetQueryCmd(_ *codec.Codec) *cobra.Command { return nil } //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the cdp module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for cdp module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the cdp module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams creates randomized cdp param changes for the simulator. -func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { - return simulation.ParamChanges(r) -} - -//____________________________________________________________________________ - // AppModule app module type type AppModule struct { AppModuleBasic - AppModuleSimulation keeper Keeper supplyKeeper types.SupplyKeeper @@ -94,10 +73,9 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - AppModuleSimulation: AppModuleSimulation{}, - keeper: keeper, - supplyKeeper: supplyKeeper, + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + supplyKeeper: supplyKeeper, } } @@ -151,3 +129,30 @@ func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// GenerateGenesisState creates a randomized GenState of the auction module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because auction has no params. +func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for auction module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the auction module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return nil +} diff --git a/x/kavadist/simulation/decoder.go b/x/kavadist/simulation/decoder.go index d28aed07..bc3787a0 100644 --- a/x/kavadist/simulation/decoder.go +++ b/x/kavadist/simulation/decoder.go @@ -5,14 +5,15 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/libs/kv" + "github.com/cosmos/cosmos-sdk/codec" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/kava-labs/kava/x/kavadist/types" ) // DecodeStore unmarshals the KVPair's Value to the corresponding cdp type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Equal(kvA.Key[:1], types.PreviousBlockTimeKey): var timeA, timeB time.Time diff --git a/x/kavadist/simulation/decoder_test.go b/x/kavadist/simulation/decoder_test.go index 235b22ff..0ed937dc 100644 --- a/x/kavadist/simulation/decoder_test.go +++ b/x/kavadist/simulation/decoder_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -28,9 +28,9 @@ func TestDecodeDistributionStore(t *testing.T) { prevBlockTime := time.Now().UTC() - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevBlockTime)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryLengthPrefixed(prevBlockTime)}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/kavadist/simulation/params.go b/x/kavadist/simulation/params.go index 909e3baf..af0b75e4 100644 --- a/x/kavadist/simulation/params.go +++ b/x/kavadist/simulation/params.go @@ -20,14 +20,14 @@ func ParamChanges(r *rand.Rand) []simulation.ParamChange { } return []simulation.ParamChange{ - simulation.NewSimParamChange(types.ModuleName, string(types.KeyActive), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyActive), func(r *rand.Rand) string { - return fmt.Sprintf("\"%t\"", active) + return fmt.Sprintf("%t", active) }, ), - simulation.NewSimParamChange(types.ModuleName, string(types.KeyPeriods), "", + simulation.NewSimParamChange(types.ModuleName, string(types.KeyPeriods), func(r *rand.Rand) string { - return fmt.Sprintf("\"%v\"", periods) + return fmt.Sprintf("%v", periods) }, ), } diff --git a/x/kavadist/types/errors.go b/x/kavadist/types/errors.go deleted file mode 100644 index 173f7a49..00000000 --- a/x/kavadist/types/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -// DONTCOVER -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Error codes specific to kavadist module -const ( - DefaultCodespace sdk.CodespaceType = ModuleName -) diff --git a/x/kavadist/types/expected_keepers.go b/x/kavadist/types/expected_keepers.go index a2e54262..cbbc3cef 100644 --- a/x/kavadist/types/expected_keepers.go +++ b/x/kavadist/types/expected_keepers.go @@ -10,6 +10,6 @@ type SupplyKeeper interface { GetModuleAddress(name string) sdk.AccAddress GetModuleAccount(ctx sdk.Context, name string) exported.ModuleAccountI GetSupply(ctx sdk.Context) (supply exported.SupplyI) - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) sdk.Error - MintCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error } diff --git a/x/kavadist/types/params.go b/x/kavadist/types/params.go index d2ab89e0..9eb01b08 100644 --- a/x/kavadist/types/params.go +++ b/x/kavadist/types/params.go @@ -90,22 +90,52 @@ func ParamKeyTable() params.KeyTable { // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs func (p *Params) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - {Key: KeyActive, Value: &p.Active}, - {Key: KeyPeriods, Value: &p.Periods}, + params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam), + params.NewParamSetPair(KeyPeriods, &p.Periods, validatePeriodsParams), } } // Validate checks that the parameters have valid values. func (p Params) Validate() error { + if err := validateActiveParam(p.Active); err != nil { + return err + } + + return validatePeriodsParams(p.Periods) +} + +func validateActiveParam(i interface{}) error { + _, ok := i.(bool) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + +func validatePeriodsParams(i interface{}) error { + periods, ok := i.(Periods) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + prevEnd := tmtime.Canonical(time.Unix(0, 0)) - for _, pr := range p.Periods { + for _, pr := range periods { if pr.End.Before(pr.Start) { return fmt.Errorf("end time for period is before start time: %s", pr) } + if pr.Start.Before(prevEnd) { - return fmt.Errorf("periods must be in chronological order: %s", p.Periods) + return fmt.Errorf("periods must be in chronological order: %s", periods) } prevEnd = pr.End + + if pr.Start.IsZero() || pr.End.IsZero() { + return fmt.Errorf("start or end time cannot be zero: %s", pr) + } + + //TODO: validate period Inflation? } + return nil } diff --git a/x/pricefeed/alias.go b/x/pricefeed/alias.go index 1e5aa2be..75affe59 100644 --- a/x/pricefeed/alias.go +++ b/x/pricefeed/alias.go @@ -1,8 +1,3 @@ -// nolint -// autogenerated code using github.com/rigelrozanski/multitool -// aliases generated for the following subdirectories: -// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/keeper -// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/types package pricefeed import ( @@ -10,13 +5,13 @@ import ( "github.com/kava-labs/kava/x/pricefeed/types" ) +// autogenerated code using github.com/rigelrozanski/multitool +// aliases generated for the following subdirectories: +// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/types/ +// ALIASGEN: github.com/kava-labs/kava/x/pricefeed/keeper/ + +// nolint const ( - DefaultCodespace = types.DefaultCodespace - CodeEmptyInput = types.CodeEmptyInput - CodeExpired = types.CodeExpired - CodeInvalidPrice = types.CodeInvalidPrice - CodeInvalidAsset = types.CodeInvalidAsset - CodeInvalidOracle = types.CodeInvalidOracle EventTypeMarketPriceUpdated = types.EventTypeMarketPriceUpdated EventTypeOracleUpdatedPrice = types.EventTypeOracleUpdatedPrice EventTypeNoValidPrices = types.EventTypeNoValidPrices @@ -39,6 +34,7 @@ const ( QueryPrice = types.QueryPrice ) +// nolint var ( // functions aliases NewKeeper = keeper.NewKeeper @@ -69,6 +65,7 @@ var ( DefaultMarkets = types.DefaultMarkets ) +// nolint type ( Keeper = keeper.Keeper GenesisState = types.GenesisState diff --git a/x/pricefeed/client/cli/query.go b/x/pricefeed/client/cli/query.go index d1a125c5..b41d388a 100644 --- a/x/pricefeed/client/cli/query.go +++ b/x/pricefeed/client/cli/query.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/kava-labs/kava/x/pricefeed/types" @@ -22,7 +23,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - pricefeedQueryCmd.AddCommand(client.GetCommands( + pricefeedQueryCmd.AddCommand(flags.GetCommands( GetCmdPrice(queryRoute, cdc), GetCmdRawPrices(queryRoute, cdc), GetCmdOracles(queryRoute, cdc), diff --git a/x/pricefeed/client/cli/tx.go b/x/pricefeed/client/cli/tx.go index 0b46a6af..2aec48d6 100644 --- a/x/pricefeed/client/cli/tx.go +++ b/x/pricefeed/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "bufio" "fmt" "time" @@ -8,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -26,7 +28,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } - pricefeedTxCmd.AddCommand(client.PostCommands( + pricefeedTxCmd.AddCommand(flags.PostCommands( GetCmdPostPrice(cdc), )...) @@ -40,8 +42,9 @@ func GetCmdPostPrice(cdc *codec.Codec) *cobra.Command { Short: "post the latest price for a particular market", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) price, err := sdk.NewDecFromStr(args[1]) if err != nil { diff --git a/x/pricefeed/handler.go b/x/pricefeed/handler.go index 628f8951..c43277e3 100644 --- a/x/pricefeed/handler.go +++ b/x/pricefeed/handler.go @@ -1,20 +1,18 @@ package pricefeed import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // NewHandler handles all pricefeed type messages func NewHandler(k Keeper) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { switch msg := msg.(type) { case MsgPostPrice: return HandleMsgPostPrice(ctx, k, msg) default: - errMsg := fmt.Sprintf("unrecognized pricefeed message type: %T", msg) - return sdk.ErrUnknownRequest(errMsg).Result() + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) } } } @@ -26,15 +24,15 @@ func NewHandler(k Keeper) sdk.Handler { func HandleMsgPostPrice( ctx sdk.Context, k Keeper, - msg MsgPostPrice) sdk.Result { + msg MsgPostPrice) (*sdk.Result, error) { _, err := k.GetOracle(ctx, msg.MarketID, msg.From) if err != nil { - return err.Result() + return nil, err } _, err = k.SetPrice(ctx, msg.From, msg.MarketID, msg.Price, msg.Expiry) if err != nil { - return err.Result() + return nil, err } ctx.EventManager().EmitEvent( @@ -45,5 +43,5 @@ func HandleMsgPostPrice( ), ) - return sdk.Result{Events: ctx.EventManager().Events()} + return &sdk.Result{Events: ctx.EventManager().Events()}, nil } diff --git a/x/pricefeed/keeper/keeper.go b/x/pricefeed/keeper/keeper.go index 211e797a..c25e08f2 100644 --- a/x/pricefeed/keeper/keeper.go +++ b/x/pricefeed/keeper/keeper.go @@ -1,12 +1,12 @@ package keeper import ( - "fmt" "sort" "time" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/params/subspace" "github.com/kava-labs/kava/x/pricefeed/types" @@ -20,19 +20,20 @@ type Keeper struct { cdc *codec.Codec // The reference to the Paramstore to get and set pricefeed specific params paramSubspace subspace.Subspace - // Reserved codespace - codespace sdk.CodespaceType } // NewKeeper returns a new keeper for the pricefeed module. func NewKeeper( - cdc *codec.Codec, key sdk.StoreKey, paramSubspace subspace.Subspace, codespace sdk.CodespaceType, + cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, ) Keeper { + if !paramstore.HasKeyTable() { + paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) + } + return Keeper{ - paramSubspace: paramSubspace.WithKeyTable(types.ParamKeyTable()), - key: key, cdc: cdc, - codespace: codespace, + key: key, + paramSubspace: paramstore, } } @@ -42,55 +43,55 @@ func (k Keeper) SetPrice( oracle sdk.AccAddress, marketID string, price sdk.Dec, - expiry time.Time) (types.PostedPrice, sdk.Error) { + expiry time.Time) (types.PostedPrice, error) { // If the expiry is less than or equal to the current blockheight, we consider the price valid - if expiry.After(ctx.BlockTime()) { - store := ctx.KVStore(k.key) - prices, err := k.GetRawPrices(ctx, marketID) - if err != nil { - return types.PostedPrice{}, err - } - var index int - found := false - for i := range prices { - if prices[i].OracleAddress.Equals(oracle) { - index = i - found = true - break - } - } - // set the price for that particular oracle - if found { - prices[index] = types.NewPostedPrice(marketID, oracle, price, expiry) - } else { - prices = append(prices, types.NewPostedPrice(marketID, oracle, price, expiry)) - index = len(prices) - 1 - } - - // Emit an event containing the oracle's new price - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeOracleUpdatedPrice, - sdk.NewAttribute(types.AttributeMarketID, marketID), - sdk.NewAttribute(types.AttributeOracle, oracle.String()), - sdk.NewAttribute(types.AttributeMarketPrice, price.String()), - sdk.NewAttribute(types.AttributeExpiry, fmt.Sprintf("%d", expiry.Unix())), - ), - ) - store.Set( - types.RawPriceKey(marketID), k.cdc.MustMarshalBinaryBare(prices), - ) - return prices[index], nil + if !expiry.After(ctx.BlockTime()) { + return types.PostedPrice{}, types.ErrExpired } - return types.PostedPrice{}, types.ErrExpired(k.codespace) + store := ctx.KVStore(k.key) + prices, err := k.GetRawPrices(ctx, marketID) + if err != nil { + return types.PostedPrice{}, err + } + var index int + found := false + for i := range prices { + if prices[i].OracleAddress.Equals(oracle) { + index = i + found = true + break + } + } + + // set the price for that particular oracle + if found { + prices[index] = types.NewPostedPrice(marketID, oracle, price, expiry) + } else { + prices = append(prices, types.NewPostedPrice(marketID, oracle, price, expiry)) + index = len(prices) - 1 + } + + // Emit an event containing the oracle's new price + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeOracleUpdatedPrice, + sdk.NewAttribute(types.AttributeMarketID, marketID), + sdk.NewAttribute(types.AttributeOracle, oracle.String()), + sdk.NewAttribute(types.AttributeMarketPrice, price.String()), + sdk.NewAttribute(types.AttributeExpiry, expiry.UTC().String()), + ), + ) + + store.Set(types.RawPriceKey(marketID), k.cdc.MustMarshalBinaryBare(prices)) + return prices[index], nil } // SetCurrentPrices updates the price of an asset to the median of all valid oracle inputs -func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error { +func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) error { _, ok := k.GetMarket(ctx, marketID) if !ok { - return types.ErrInvalidMarket(k.codespace, marketID) + return sdkerrors.Wrap(types.ErrInvalidMarket, marketID) } // store current price validPrevPrice := true @@ -115,8 +116,9 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error { store.Set( types.CurrentPriceKey(marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}), ) - return types.ErrNoValidPrice(k.codespace) + return types.ErrNoValidPrice } + medianPrice := k.CalculateMedianPrice(ctx, notExpiredPrices) // check case that market price was not set in genesis @@ -126,8 +128,8 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error { ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeMarketPriceUpdated, - sdk.NewAttribute(types.AttributeMarketID, fmt.Sprintf("%s", marketID)), - sdk.NewAttribute(types.AttributeMarketPrice, fmt.Sprintf("%s", medianPrice.String())), + sdk.NewAttribute(types.AttributeMarketID, marketID), + sdk.NewAttribute(types.AttributeMarketPrice, medianPrice.String()), ), ) } @@ -172,26 +174,26 @@ func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices types.CurrentPrices) } // GetCurrentPrice fetches the current median price of all oracles for a specific market -func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, sdk.Error) { +func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, error) { store := ctx.KVStore(k.key) bz := store.Get(types.CurrentPriceKey(marketID)) if bz == nil { - return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace) + return types.CurrentPrice{}, types.ErrNoValidPrice } var price types.CurrentPrice err := k.cdc.UnmarshalBinaryBare(bz, &price) if err != nil { - return types.CurrentPrice{}, sdk.ErrInternal(sdk.AppendMsgToErr("failed to unmarshal result", err.Error())) + return types.CurrentPrice{}, err } if price.Price.Equal(sdk.ZeroDec()) { - return types.CurrentPrice{}, types.ErrNoValidPrice(k.codespace) + return types.CurrentPrice{}, types.ErrNoValidPrice } return price, nil } // GetRawPrices fetches the set of all prices posted by oracles for an asset -func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) (types.PostedPrices, sdk.Error) { +func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) (types.PostedPrices, error) { store := ctx.KVStore(k.key) bz := store.Get(types.RawPriceKey(marketID)) if bz == nil { @@ -200,12 +202,7 @@ func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) (types.PostedPric var prices types.PostedPrices err := k.cdc.UnmarshalBinaryBare(bz, &prices) if err != nil { - return types.PostedPrices{}, sdk.ErrInternal(sdk.AppendMsgToErr("failed to unmarshal result", err.Error())) + return types.PostedPrices{}, err } return prices, nil } - -// Codespace return the codespace for the keeper -func (k Keeper) Codespace() sdk.CodespaceType { - return k.codespace -} diff --git a/x/pricefeed/keeper/params.go b/x/pricefeed/keeper/params.go index 8b297f57..16836359 100644 --- a/x/pricefeed/keeper/params.go +++ b/x/pricefeed/keeper/params.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/pricefeed/types" ) @@ -24,28 +25,27 @@ func (k Keeper) GetMarkets(ctx sdk.Context) types.Markets { } // GetOracles returns the oracles in the pricefeed store -func (k Keeper) GetOracles(ctx sdk.Context, marketID string) ([]sdk.AccAddress, sdk.Error) { - +func (k Keeper) GetOracles(ctx sdk.Context, marketID string) ([]sdk.AccAddress, error) { for _, m := range k.GetMarkets(ctx) { if marketID == m.MarketID { return m.Oracles, nil } } - return []sdk.AccAddress{}, types.ErrInvalidMarket(k.Codespace(), marketID) + return []sdk.AccAddress{}, sdkerrors.Wrap(types.ErrInvalidMarket, marketID) } // GetOracle returns the oracle from the store or an error if not found -func (k Keeper) GetOracle(ctx sdk.Context, marketID string, address sdk.AccAddress) (sdk.AccAddress, sdk.Error) { +func (k Keeper) GetOracle(ctx sdk.Context, marketID string, address sdk.AccAddress) (sdk.AccAddress, error) { oracles, err := k.GetOracles(ctx, marketID) if err != nil { - return sdk.AccAddress{}, types.ErrInvalidMarket(k.Codespace(), marketID) + return sdk.AccAddress{}, sdkerrors.Wrap(types.ErrInvalidMarket, marketID) } for _, addr := range oracles { if address.Equals(addr) { return addr, nil } } - return sdk.AccAddress{}, types.ErrInvalidOracle(k.codespace, address) + return sdk.AccAddress{}, sdkerrors.Wrap(types.ErrInvalidOracle, address.String()) } // GetMarket returns the market if it is in the pricefeed system diff --git a/x/pricefeed/keeper/querier.go b/x/pricefeed/keeper/querier.go index 1a111fdf..cef65d4c 100644 --- a/x/pricefeed/keeper/querier.go +++ b/x/pricefeed/keeper/querier.go @@ -1,19 +1,18 @@ package keeper import ( - "fmt" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/kava-labs/kava/x/pricefeed/types" ) // NewQuerier is the module level router for state queries func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { switch path[0] { case types.QueryPrice: return queryPrice(ctx, req, keeper) @@ -26,95 +25,97 @@ func NewQuerier(keeper Keeper) sdk.Querier { case types.QueryGetParams: return queryGetParams(ctx, req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown pricefeed query endpoint") + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName) } } } -func queryPrice(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) { +func queryPrice(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr error) { var requestParams types.QueryWithMarketIDParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, found := keeper.GetMarket(ctx, requestParams.MarketID) if !found { - return []byte{}, sdk.ErrUnknownRequest("asset not found") + return []byte{}, sdkerrors.Wrap(types.ErrAssetNotFound, requestParams.MarketID) } currentPrice, sdkErr := keeper.GetCurrentPrice(ctx, requestParams.MarketID) if sdkErr != nil { return nil, sdkErr } - bz, err := codec.MarshalJSONIndent(keeper.cdc, currentPrice) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, currentPrice) if err != nil { - panic("could not marshal result to JSON") + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryRawPrices(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) { +func queryRawPrices(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr error) { var requestParams types.QueryWithMarketIDParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) - if err2 != nil { - return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err2)) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } _, found := keeper.GetMarket(ctx, requestParams.MarketID) if !found { - return []byte{}, sdk.ErrUnknownRequest("asset not found") + return []byte{}, sdkerrors.Wrap(types.ErrAssetNotFound, requestParams.MarketID) } + rawPrices, err := keeper.GetRawPrices(ctx, requestParams.MarketID) if err != nil { return nil, err } - bz, err2 := codec.MarshalJSONIndent(keeper.cdc, rawPrices) - if err2 != nil { - panic("could not marshal result to JSON") + + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, rawPrices) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryOracles(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) { +func queryOracles(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr error) { var requestParams types.QueryWithMarketIDParams - err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams) + err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams) if err != nil { - return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } oracles, err := keeper.GetOracles(ctx, requestParams.MarketID) if err != nil { - return []byte{}, sdk.ErrUnknownRequest("market not found") + return []byte{}, sdkerrors.Wrap(types.ErrAssetNotFound, requestParams.MarketID) } - bz, err := codec.MarshalJSONIndent(keeper.cdc, oracles) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, oracles) if err != nil { - panic("could not marshal result to JSON") + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } -func queryMarkets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) { +func queryMarkets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr error) { markets := keeper.GetMarkets(ctx) - bz, err := codec.MarshalJSONIndent(keeper.cdc, markets) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, markets) if err != nil { - panic("could not marshal result to JSON") + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } // query params in the pricefeed store -func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetParams(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { params := keeper.GetParams(ctx) // Encode results - bz, err := codec.MarshalJSONIndent(keeper.cdc, params) + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, params) if err != nil { - return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) } return bz, nil } diff --git a/x/pricefeed/module.go b/x/pricefeed/module.go index ab167e02..3426a860 100644 --- a/x/pricefeed/module.go +++ b/x/pricefeed/module.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" sim "github.com/cosmos/cosmos-sdk/x/simulation" abci "github.com/tendermint/tendermint/abci/types" @@ -22,7 +23,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic app module basics object @@ -70,39 +71,20 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { //____________________________________________________________________________ -// AppModuleSimulation defines the module simulation functions used by the pricefeed module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for pricefeed module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the pricefeed module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams creates randomized pricefeed param changes for the simulator. -func (AppModuleSimulation) RandomizedParams(r *rand.Rand) []sim.ParamChange { - return simulation.ParamChanges(r) -} - -//____________________________________________________________________________ - // AppModule app module type type AppModule struct { AppModuleBasic - AppModuleSimulation - keeper Keeper + keeper Keeper + accountKeeper auth.AccountKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(keeper Keeper) AppModule { +func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: keeper, + accountKeeper: accountKeeper, } } @@ -156,3 +138,32 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return ModuleCdc.MustMarshalJSON(gs) } + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the price feed module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because price feed has no params. +func (AppModuleBasic) RandomizedParams(r *rand.Rand) []sim.ParamChange { + return simulation.ParamChanges(r) +} + +// RegisterStoreDecoder registers a decoder for price feed module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the price feed module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return simulation.WeightedOperations(simState.AppParams, simState.Cdc, am.accountKeeper, am.keeper) +} diff --git a/x/pricefeed/simulation/decoder.go b/x/pricefeed/simulation/decoder.go index 9693b6e3..e40c1932 100644 --- a/x/pricefeed/simulation/decoder.go +++ b/x/pricefeed/simulation/decoder.go @@ -5,13 +5,13 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/codec" - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/kava-labs/kava/x/pricefeed/types" ) // DecodeStore unmarshals the KVPair's Value to the corresponding pricefeed type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Contains(kvA.Key, []byte(types.CurrentPricePrefix)): var priceA, priceB types.CurrentPrice diff --git a/x/pricefeed/simulation/decoder_test.go b/x/pricefeed/simulation/decoder_test.go index bc886819..33ef65a8 100644 --- a/x/pricefeed/simulation/decoder_test.go +++ b/x/pricefeed/simulation/decoder_test.go @@ -6,8 +6,7 @@ import ( "time" "github.com/stretchr/testify/require" - - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -29,10 +28,10 @@ func TestDecodeDistributionStore(t *testing.T) { currentPrice := types.CurrentPrice{MarketID: "current", Price: sdk.OneDec()} postedPrice := []types.PostedPrice{{MarketID: "posted", Price: sdk.OneDec(), Expiry: time.Now().UTC()}} - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: []byte(types.CurrentPricePrefix), Value: cdc.MustMarshalBinaryBare(currentPrice)}, - cmn.KVPair{Key: []byte(types.RawPriceFeedPrefix), Value: cdc.MustMarshalBinaryBare(postedPrice)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: []byte(types.CurrentPricePrefix), Value: cdc.MustMarshalBinaryBare(currentPrice)}, + kv.Pair{Key: []byte(types.RawPriceFeedPrefix), Value: cdc.MustMarshalBinaryBare(postedPrice)}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/pricefeed/simulation/genesis.go b/x/pricefeed/simulation/genesis.go index 272695aa..a1d049a0 100644 --- a/x/pricefeed/simulation/genesis.go +++ b/x/pricefeed/simulation/genesis.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/kava-labs/kava/x/pricefeed/types" pricefeed "github.com/kava-labs/kava/x/pricefeed/types" ) @@ -31,7 +32,7 @@ func loadPricefeedGenState(simState *module.SimulationState) pricefeed.GenesisSt var postedPrices []pricefeed.PostedPrice for _, denom := range BaseAssets { // Select an account to be the oracle - oracle := simState.Accounts[simulation.RandIntBetween(simState.Rand, 0, len(simState.Accounts))] + oracle, _ := simulation.RandomAcc(simState.Rand, simState.Accounts) marketID := fmt.Sprintf("%s:%s", denom, QuoteAsset) // Construct market for asset diff --git a/x/pricefeed/simulation/operations/msg.go b/x/pricefeed/simulation/operations.go similarity index 61% rename from x/pricefeed/simulation/operations/msg.go rename to x/pricefeed/simulation/operations.go index 385168a8..f620d172 100644 --- a/x/pricefeed/simulation/operations/msg.go +++ b/x/pricefeed/simulation/operations.go @@ -1,35 +1,62 @@ -package operations +package simulation import ( - "fmt" "math/rand" "sync" "time" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp/helpers" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/simulation" - "github.com/kava-labs/kava/x/pricefeed" + appparams "github.com/kava-labs/kava/app/params" "github.com/kava-labs/kava/x/pricefeed/keeper" "github.com/kava-labs/kava/x/pricefeed/types" ) var ( - noOpMsg = simulation.NoOpMsg(pricefeed.ModuleName) + noOpMsg = simulation.NoOpMsg(types.ModuleName) btcPrices = []sdk.Dec{} bnbPrices = []sdk.Dec{} xrpPrices = []sdk.Dec{} genPrices sync.Once ) -// SimulateMsgUpdatePrices updates the prices of various assets by randomly varying them based on current price -func SimulateMsgUpdatePrices(keeper keeper.Keeper, blocks int) simulation.Operation { - // get a pricefeed handler - handler := pricefeed.NewHandler(keeper) +// Simulation operation weights constants +const ( + OpWeightMsgUpdatePrices = "op_weight_msg_update_prices" +) + +// WeightedOperations returns all the operations from the module with their respective weights +func WeightedOperations( + appParams simulation.AppParams, cdc *codec.Codec, ak auth.AccountKeeper, k keeper.Keeper, +) simulation.WeightedOperations { + var weightMsgUpdatePrices int + // var numBlocks int + + appParams.GetOrGenerate(cdc, OpWeightMsgUpdatePrices, &weightMsgUpdatePrices, nil, + func(_ *rand.Rand) { + weightMsgUpdatePrices = appparams.DefaultWeightMsgUpdatePrices + }, + ) + + return simulation.WeightedOperations{ + simulation.NewWeightedOperation( + weightMsgUpdatePrices, + SimulateMsgUpdatePrices(ak, k, 10000), + ), + } +} + +// SimulateMsgUpdatePrices updates the prices of various assets by randomly varying them based on current price +func SimulateMsgUpdatePrices(ak auth.AccountKeeper, keeper keeper.Keeper, blocks int) simulation.Operation { + return func( + r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, + ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) ( - simulation.OperationMsg, []simulation.FutureOperation, error) { genPrices.Do(func() { // generate a random walk for each asset exactly once, with observations equal to the number of blocks in the sim for _, m := range keeper.GetMarkets(ctx) { @@ -63,24 +90,46 @@ func SimulateMsgUpdatePrices(keeper keeper.Keeper, blocks int) simulation.Operat randomMarket := pickRandomAsset(ctx, keeper, r) marketID := randomMarket.MarketID address := getRandomOracle(r, randomMarket) + + oracle, found := simulation.FindAccount(accs, address) + if !found { + return simulation.NoOpMsg(types.ModuleName), nil, nil + } + + oracleAcc := ak.GetAccount(ctx, oracle.Address) + if oracleAcc == nil { + return simulation.NoOpMsg(types.ModuleName), nil, nil + } + price := pickNewRandomPrice(marketID, int(ctx.BlockHeight())) // get the expiry time based off the current time expiry := getExpiryTime(ctx) // now create the msg to post price - msg := types.NewMsgPostPrice(address, marketID, price, expiry) + msg := types.NewMsgPostPrice(oracle.Address, marketID, price, expiry) - // Perform basic validation of the msg - don't submit errors that fail ValidateBasic, use unit tests for testing ValidateBasic - if err := msg.ValidateBasic(); err != nil { - return noOpMsg, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) + spendable := oracleAcc.SpendableCoins(ctx.BlockTime()) + fees, err := simulation.RandomFees(r, ctx, spendable) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err } - // now we submit the pricefeed update message - if ok := submitMsg(ctx, handler, msg); !ok { - return noOpMsg, nil, fmt.Errorf("could not submit pricefeed msg") + tx := helpers.GenTx( + []sdk.Msg{msg}, + fees, + helpers.DefaultGenTxGas, + chainID, + []uint64{oracleAcc.GetAccountNumber()}, + []uint64{oracleAcc.GetSequence()}, + oracle.PrivKey, + ) + + _, result, err := app.Deliver(tx) + if err != nil { + return simulation.NoOpMsg(types.ModuleName), nil, err } - return simulation.NewOperationMsg(msg, true, "pricefeed update submitted"), nil, nil + return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } } @@ -130,10 +179,9 @@ func pickNewRandomPrice(marketID string, blockHeight int) (newPrice sdk.Dec) { } // getRandomOracle picks a random oracle from the list of oracles -func getRandomOracle(r *rand.Rand, market pricefeed.Market) sdk.AccAddress { +func getRandomOracle(r *rand.Rand, market types.Market) sdk.AccAddress { randomIndex := simulation.RandIntBetween(r, 0, len(market.Oracles)) - oracle := market.Oracles[randomIndex] - return oracle + return market.Oracles[randomIndex] } // pickRandomAsset picks a random asset out of the assets with equal probability @@ -143,25 +191,11 @@ func pickRandomAsset(ctx sdk.Context, keeper keeper.Keeper, r *rand.Rand) (marke params := keeper.GetParams(ctx) // now pick a random asset randomIndex := simulation.RandIntBetween(r, 0, len(params.Markets)) - market = params.Markets[randomIndex] - return market + return params.Markets[randomIndex] } // getExpiryTime gets a price expiry time by taking the current time and adding a delta to it func getExpiryTime(ctx sdk.Context) (t time.Time) { // need to use the blocktime from the context as the context generates random start time when running simulations - t = ctx.BlockTime().Add(time.Second * 1000000) - return t -} - -// submitMsg submits a message to the current instance of the keeper and returns a boolean whether the operation completed successfully or not -func submitMsg(ctx sdk.Context, handler sdk.Handler, msg sdk.Msg) (ok bool) { - ctx, write := ctx.CacheContext() - got := handler(ctx, msg) - - ok = got.IsOK() - if ok { - write() - } - return ok + return ctx.BlockTime().Add(time.Second * 1000000) } diff --git a/x/pricefeed/simulation/params.go b/x/pricefeed/simulation/params.go index 8c1f7aff..36ff612a 100644 --- a/x/pricefeed/simulation/params.go +++ b/x/pricefeed/simulation/params.go @@ -9,6 +9,5 @@ import ( // ParamChanges defines the parameters that can be modified by param change proposals // on the simulation func ParamChanges(r *rand.Rand) []simulation.ParamChange { - // TODO implement this return []simulation.ParamChange{} } diff --git a/x/pricefeed/types/errors.go b/x/pricefeed/types/errors.go index 6d545f02..1d14aa38 100644 --- a/x/pricefeed/types/errors.go +++ b/x/pricefeed/types/errors.go @@ -1,48 +1,22 @@ package types import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -const ( - // DefaultCodespace codespace for the module - DefaultCodespace sdk.CodespaceType = ModuleName +// DONTCOVER - // CodeEmptyInput error code for empty input errors - CodeEmptyInput sdk.CodeType = 1 - // CodeExpired error code for expired prices - CodeExpired sdk.CodeType = 2 - // CodeInvalidPrice error code for all input prices expired - CodeInvalidPrice sdk.CodeType = 3 - // CodeInvalidAsset error code for invalid asset - CodeInvalidAsset sdk.CodeType = 4 - // CodeInvalidOracle error code for invalid oracle - CodeInvalidOracle sdk.CodeType = 5 +var ( + // ErrEmptyInput error for empty input + ErrEmptyInput = sdkerrors.Register(ModuleName, 2, "input must not be empty") + // ErrExpired error for posted price messages with expired price + ErrExpired = sdkerrors.Register(ModuleName, 3, "price is expired") + // ErrNoValidPrice error for posted price messages with expired price + ErrNoValidPrice = sdkerrors.Register(ModuleName, 4, "all input prices are expired") + // ErrInvalidMarket error for posted price messages for invalid markets + ErrInvalidMarket = sdkerrors.Register(ModuleName, 5, "market does not exist") + // ErrInvalidOracle error for posted price messages for invalid oracles + ErrInvalidOracle = sdkerrors.Register(ModuleName, 6, "oracle does not exist or not authorized") + // ErrAssetNotFound error for not found asset + ErrAssetNotFound = sdkerrors.Register(ModuleName, 7, "asset not found") ) - -// ErrEmptyInput Error constructor -func ErrEmptyInput(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeEmptyInput, fmt.Sprintf("Input must not be empty.")) -} - -// ErrExpired Error constructor for posted price messages with expired price -func ErrExpired(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeExpired, fmt.Sprintf("Price is expired.")) -} - -// ErrNoValidPrice Error constructor for posted price messages with expired price -func ErrNoValidPrice(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidPrice, fmt.Sprintf("All input prices are expired.")) -} - -// ErrInvalidMarket Error constructor for posted price messages for invalid markets -func ErrInvalidMarket(codespace sdk.CodespaceType, marketId string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidAsset, fmt.Sprintf("market %s does not exist", marketId)) -} - -// ErrInvalidOracle Error constructor for posted price messages for invalid oracles -func ErrInvalidOracle(codespace sdk.CodespaceType, addr sdk.AccAddress) sdk.Error { - return sdk.NewError(codespace, CodeInvalidOracle, fmt.Sprintf("oracle %s does not exist or not authorized", addr)) -} diff --git a/x/pricefeed/types/msgs.go b/x/pricefeed/types/msgs.go index 059c3866..77476699 100644 --- a/x/pricefeed/types/msgs.go +++ b/x/pricefeed/types/msgs.go @@ -1,9 +1,13 @@ package types import ( + "errors" + "fmt" + "strings" "time" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) const ( @@ -55,16 +59,18 @@ func (msg MsgPostPrice) GetSigners() []sdk.AccAddress { } // ValidateBasic does a simple validation check that doesn't require access to any other information. -func (msg MsgPostPrice) ValidateBasic() sdk.Error { +func (msg MsgPostPrice) ValidateBasic() error { if msg.From.Empty() { - return sdk.ErrInternal("invalid (empty) from address") + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty") } - if len(msg.MarketID) == 0 { - return sdk.ErrInternal("invalid (empty) market id") + if strings.TrimSpace(msg.MarketID) == "" { + return errors.New("market id cannot be blank") } - if msg.Price.LT(sdk.ZeroDec()) { - return sdk.ErrInternal("invalid (negative) price") + if msg.Price.IsNegative() { + return fmt.Errorf("price cannot be negative: %s", msg.Price.String()) + } + if msg.Expiry.IsZero() { + return errors.New("must set an expiration time") } - // TODO check coin denoms return nil } diff --git a/x/pricefeed/types/params.go b/x/pricefeed/types/params.go index 6236a8eb..acb748d4 100644 --- a/x/pricefeed/types/params.go +++ b/x/pricefeed/types/params.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/params" ) @@ -39,7 +40,7 @@ func ParamKeyTable() params.KeyTable { // pairs of pricefeed module's parameters. func (p *Params) ParamSetPairs() params.ParamSetPairs { return params.ParamSetPairs{ - {Key: KeyMarkets, Value: &p.Markets}, + params.NewParamSetPair(KeyMarkets, &p.Markets, validateMarketParams), } } @@ -54,11 +55,21 @@ func (p Params) String() string { // Validate ensure that params have valid values func (p Params) Validate() error { + return validateMarketParams(p.Markets) +} + +func validateMarketParams(i interface{}) error { + markets, ok := i.(Markets) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + // iterate over assets and verify them - for _, asset := range p.Markets { - if asset.MarketID == "" { - return fmt.Errorf("invalid market: %s. missing market ID", asset.String()) + for _, asset := range markets { + if strings.TrimSpace(asset.MarketID) == "" { + return sdkerrors.Wrapf(ErrInvalidMarket, "market id for asset %s cannot be blank", asset) } } + return nil } diff --git a/x/validator-vesting/client/cli/query.go b/x/validator-vesting/client/cli/query.go index ce00168f..b4d45251 100644 --- a/x/validator-vesting/client/cli/query.go +++ b/x/validator-vesting/client/cli/query.go @@ -3,11 +3,12 @@ package cli import ( "fmt" - "github.com/kava-labs/kava/x/validator-vesting/types" "github.com/spf13/cobra" - "github.com/cosmos/cosmos-sdk/client" + "github.com/kava-labs/kava/x/validator-vesting/types" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" ) @@ -19,7 +20,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Querying commands for the validator vesting module", } - queryValidatorVestingCmd.AddCommand(client.GetCommands( + queryValidatorVestingCmd.AddCommand(flags.GetCommands( QueryCirculatingSupplyCmd(queryRoute, cdc), QueryTotalSupplyCmd(queryRoute, cdc), )...) diff --git a/x/validator-vesting/keeper/keeper.go b/x/validator-vesting/keeper/keeper.go index 54662879..f6bdade1 100644 --- a/x/validator-vesting/keeper/keeper.go +++ b/x/validator-vesting/keeper/keeper.go @@ -138,7 +138,7 @@ func (k Keeper) SetVestingProgress(ctx sdk.Context, addr sdk.AccAddress, period // AddDebt adds the input amount to DebtAfterFailedVesting field func (k Keeper) AddDebt(ctx sdk.Context, addr sdk.AccAddress, amount sdk.Coins) { vv := k.GetAccountFromAuthKeeper(ctx, addr) - vv.DebtAfterFailedVesting = vv.DebtAfterFailedVesting.Add(amount) + vv.DebtAfterFailedVesting = vv.DebtAfterFailedVesting.Add(amount...) k.ak.SetAccount(ctx, vv) } diff --git a/x/validator-vesting/keeper/keeper_test.go b/x/validator-vesting/keeper/keeper_test.go index 51d940ed..c3aaafb8 100644 --- a/x/validator-vesting/keeper/keeper_test.go +++ b/x/validator-vesting/keeper/keeper_test.go @@ -349,7 +349,7 @@ func TestHandleVestingDebtReturn(t *testing.T) { require.Equal(t, sdk.NewCoins(sdk.NewInt64Coin(stakeDenom, 30000000)), vva.DebtAfterFailedVesting) initialBalance := ak.GetAccount(ctx, TestAddrs[2]).GetCoins() - expectedBalance := initialBalance.Add(vva.DebtAfterFailedVesting) + expectedBalance := initialBalance.Add(vva.DebtAfterFailedVesting...) // Context needs the block time because bank keeper calls 'SpendableCoins' by getting the header from the context. ctx = ctx.WithBlockTime(now.Add(12 * time.Hour)) keeper.HandleVestingDebt(ctx, vva.Address, now.Add(12*time.Hour)) diff --git a/x/validator-vesting/keeper/querier.go b/x/validator-vesting/keeper/querier.go index 32c7a11e..4c5727e9 100644 --- a/x/validator-vesting/keeper/querier.go +++ b/x/validator-vesting/keeper/querier.go @@ -2,37 +2,40 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/cosmos-sdk/x/auth/vesting" + "github.com/kava-labs/kava/x/validator-vesting/types" + abci "github.com/tendermint/tendermint/abci/types" ) // NewQuerier returns a new querier function func NewQuerier(keeper Keeper) sdk.Querier { - return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { switch path[0] { case types.QueryCirculatingSupply: return queryGetCirculatingSupply(ctx, req, keeper) case types.QueryTotalSupply: return queryGetTotalSupply(ctx, req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown cdp query endpoint") + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint: %s", types.ModuleName, path[0]) } } } -func queryGetTotalSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetTotalSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { totalSupply := keeper.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf("ukava") supplyInt := sdk.NewDecFromInt(totalSupply).Mul(sdk.MustNewDecFromStr("0.000001")).TruncateInt64() - bz, err := keeper.cdc.MarshalJSON(supplyInt) + bz, err := types.ModuleCdc.MarshalJSON(supplyInt) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } return bz, nil } -func queryGetCirculatingSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { +func queryGetCirculatingSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) { circulatingSupply := keeper.supplyKeeper.GetSupply(ctx).GetTotal().AmountOf("ukava") keeper.ak.IterateAccounts(ctx, func(acc authexported.Account) (stop bool) { @@ -54,9 +57,9 @@ func queryGetCirculatingSupply(ctx sdk.Context, req abci.RequestQuery, keeper Ke return false }) supplyInt := sdk.NewDecFromInt(circulatingSupply).Mul(sdk.MustNewDecFromStr("0.000001")).TruncateInt64() - bz, err := keeper.cdc.MarshalJSON(supplyInt) + bz, err := types.ModuleCdc.MarshalJSON(supplyInt) if err != nil { - return nil, sdk.ErrInternal(err.Error()) + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } return bz, nil } diff --git a/x/validator-vesting/keeper/test_common.go b/x/validator-vesting/keeper/test_common.go index 36ba430e..fbb817ca 100644 --- a/x/validator-vesting/keeper/test_common.go +++ b/x/validator-vesting/keeper/test_common.go @@ -120,12 +120,12 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context cdc := MakeTestCodec() - pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + pk := params.NewKeeper(cdc, keyParams, tkeyParams) - stakingParams := staking.NewParams(time.Hour, 100, uint16(7), sdk.DefaultBondDenom) + stakingParams := staking.NewParams(time.Hour, 100, uint16(7), 0, sdk.DefaultBondDenom) accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs) + bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), blacklistedAddrs) maccPerms := map[string][]string{ auth.FeeCollectorName: nil, staking.NotBondedPoolName: {supply.Burner, supply.Staking}, @@ -134,7 +134,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context } supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) - stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) + stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace)) stakingKeeper.SetParams(ctx, stakingParams) keeper := NewKeeper(cdc, keyValidatorVesting, accountKeeper, bankKeeper, supplyKeeper, stakingKeeper) diff --git a/x/validator-vesting/module.go b/x/validator-vesting/module.go index 903e8b8a..f918d028 100644 --- a/x/validator-vesting/module.go +++ b/x/validator-vesting/module.go @@ -22,7 +22,7 @@ import ( var ( _ module.AppModule = AppModule{} _ module.AppModuleBasic = AppModuleBasic{} - _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModuleSimulation = AppModule{} ) // AppModuleBasic defines the basic application module used by the auth module. @@ -66,28 +66,10 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(StoreKey, cdc) } -// AppModuleSimulation defines the module simulation functions used by the auth module. -type AppModuleSimulation struct{} - -// RegisterStoreDecoder registers a decoder for auth module's types -func (AppModuleSimulation) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { - sdr[StoreKey] = simulation.DecodeStore -} - -// GenerateGenesisState creates a randomized GenState of the auth module -func (AppModuleSimulation) GenerateGenesisState(simState *module.SimulationState) { - simulation.RandomizedGenState(simState) -} - -// RandomizedParams returns nil because validatorvesting has no params. -func (AppModuleSimulation) RandomizedParams(_ *rand.Rand) []sim.ParamChange { - return []sim.ParamChange{} -} - // AppModule implements an application module for the validator-vesting module. type AppModule struct { AppModuleBasic - AppModuleSimulation + keeper Keeper accountKeeper types.AccountKeeper } @@ -95,10 +77,9 @@ type AppModule struct { // NewAppModule creates a new AppModule object func NewAppModule(keeper Keeper, ak types.AccountKeeper) AppModule { return AppModule{ - AppModuleBasic: AppModuleBasic{}, - AppModuleSimulation: AppModuleSimulation{}, - keeper: keeper, - accountKeeper: ak, + AppModuleBasic: AppModuleBasic{}, + keeper: keeper, + accountKeeper: ak, } } @@ -152,3 +133,32 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the auth module +func (AppModuleBasic) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents doesn't return any content functions for governance proposals. +func (AppModuleBasic) ProposalContents(_ module.SimulationState) []sim.WeightedProposalContent { + return nil +} + +// RandomizedParams returns nil because validatorvesting has no params. +func (AppModuleBasic) RandomizedParams(_ *rand.Rand) []sim.ParamChange { + return []sim.ParamChange{} +} + +// RegisterStoreDecoder registers a decoder for auth module's types +func (AppModuleBasic) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.DecodeStore +} + +// WeightedOperations returns the all the validator vesting module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []sim.WeightedOperation { + return nil +} diff --git a/x/validator-vesting/simulation/decoder.go b/x/validator-vesting/simulation/decoder.go index 445828a6..6e3207ab 100644 --- a/x/validator-vesting/simulation/decoder.go +++ b/x/validator-vesting/simulation/decoder.go @@ -5,15 +5,14 @@ import ( "fmt" "time" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/kava-labs/kava/x/validator-vesting/types" + "github.com/tendermint/tendermint/libs/kv" ) // DecodeStore unmarshals the KVPair's Value to the corresponding auth type -func DecodeStore(cdc *codec.Codec, kvA, kvB cmn.KVPair) string { +func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string { switch { case bytes.Equal(kvA.Key[:1], types.ValidatorVestingAccountPrefix): var accA, accB exported.Account diff --git a/x/validator-vesting/simulation/decoder_test.go b/x/validator-vesting/simulation/decoder_test.go index 7cb532ca..adc0cb24 100644 --- a/x/validator-vesting/simulation/decoder_test.go +++ b/x/validator-vesting/simulation/decoder_test.go @@ -6,8 +6,7 @@ import ( "time" "github.com/stretchr/testify/require" - - cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/kv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -32,10 +31,10 @@ func TestDecodeDistributionStore(t *testing.T) { acc := types.ValidatorVestingAccount{SigningThreshold: 1} now := time.Now().UTC() - kvPairs := cmn.KVPairs{ - cmn.KVPair{Key: types.ValidatorVestingAccountPrefix, Value: cdc.MustMarshalBinaryBare(acc)}, - cmn.KVPair{Key: types.BlocktimeKey, Value: cdc.MustMarshalBinaryLengthPrefixed(now)}, - cmn.KVPair{Key: []byte{0x99}, Value: []byte{0x99}}, + kvPairs := kv.Pairs{ + kv.Pair{Key: types.ValidatorVestingAccountPrefix, Value: cdc.MustMarshalBinaryBare(acc)}, + kv.Pair{Key: types.BlocktimeKey, Value: cdc.MustMarshalBinaryLengthPrefixed(now)}, + kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}}, } tests := []struct { diff --git a/x/validator-vesting/simulation/genesis.go b/x/validator-vesting/simulation/genesis.go index c77bb9a3..7b133dff 100644 --- a/x/validator-vesting/simulation/genesis.go +++ b/x/validator-vesting/simulation/genesis.go @@ -101,12 +101,12 @@ func getRandomVestingPeriods(duration int64, r *rand.Rand, origCoins sdk.Coins) vestingCoins := sdk.NewCoins() for _, ic := range origCoins { amountVesting := ic.Amount.Int64() / int64(coinFraction) - vestingCoins = vestingCoins.Add(sdk.NewCoins(sdk.NewInt64Coin(ic.Denom, amountVesting))) + vestingCoins = vestingCoins.Add(sdk.NewInt64Coin(ic.Denom, amountVesting)) } periodCoins := sdk.NewCoins() for _, c := range vestingCoins { amountPeriod := c.Amount.Int64() / int64(numPeriods) - periodCoins = periodCoins.Add(sdk.NewCoins(sdk.NewInt64Coin(c.Denom, amountPeriod))) + periodCoins = periodCoins.Add(sdk.NewInt64Coin(c.Denom, amountPeriod)) } vestingPeriods := make([]vestingtypes.Period, numPeriods) @@ -121,7 +121,7 @@ func getRandomVestingPeriods(duration int64, r *rand.Rand, origCoins sdk.Coins) func getVestingCoins(periods vestingtypes.Periods) sdk.Coins { vestingCoins := sdk.NewCoins() for _, p := range periods { - vestingCoins = vestingCoins.Add(p.Amount) + vestingCoins = vestingCoins.Add(p.Amount...) } return vestingCoins } diff --git a/x/validator-vesting/test_common.go b/x/validator-vesting/test_common.go index 0bc8c141..fdb4f83f 100644 --- a/x/validator-vesting/test_common.go +++ b/x/validator-vesting/test_common.go @@ -60,7 +60,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState types.GenesisState, genAc pk := mApp.ParamsKeeper - bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs) + bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) maccPerms := map[string][]string{ types.ModuleName: {supply.Burner}, @@ -69,7 +69,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState types.GenesisState, genAc } supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, maccPerms) sk := staking.NewKeeper( - mApp.Cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace, + mApp.Cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), ) keeper := keeper.NewKeeper( diff --git a/x/validator-vesting/types/expected_keepers.go b/x/validator-vesting/types/expected_keepers.go index 74969143..afdf6c16 100644 --- a/x/validator-vesting/types/expected_keepers.go +++ b/x/validator-vesting/types/expected_keepers.go @@ -19,7 +19,7 @@ type AccountKeeper interface { // BankKeeper defines the expected bank keeper (noalias) type BankKeeper interface { - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) sdk.Error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error } // StakingKeeper defines the expected staking keeper (noalias) @@ -28,13 +28,13 @@ type StakingKeeper interface { fn func(index int64, delegation stakingexported.DelegationI) (stop bool)) Undelegate( ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec, - ) (time.Time, sdk.Error) + ) (time.Time, error) } // SupplyKeeper defines the expected supply keeper for module accounts (noalias) type SupplyKeeper interface { - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) sdk.Error - BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error SetModuleAccount(sdk.Context, supplyexported.ModuleAccountI) GetSupply(ctx sdk.Context) (supply supplyexported.SupplyI) } diff --git a/x/validator-vesting/types/validator_vesting_account.go b/x/validator-vesting/types/validator_vesting_account.go index b09521a4..27eb452e 100644 --- a/x/validator-vesting/types/validator_vesting_account.go +++ b/x/validator-vesting/types/validator_vesting_account.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "errors" "time" @@ -11,6 +12,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/tendermint/tendermint/crypto" ) // Assert ValidatorVestingAccount implements the vestexported.VestingAccount interface @@ -143,7 +145,7 @@ func (vva ValidatorVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins x := blockTime.Unix() - currentPeriodStartTime if x >= vva.VestingPeriods[i].Length { if vva.VestingPeriodProgress[i].PeriodComplete { - vestedCoins = vestedCoins.Add(vva.VestingPeriods[i].Amount) + vestedCoins = vestedCoins.Add(vva.VestingPeriods[i].Amount...) } currentPeriodStartTime += vva.VestingPeriods[i].Length } else { @@ -161,7 +163,7 @@ func (vva ValidatorVestingAccount) GetFailedVestedCoins() sdk.Coins { for i := 0; i < numberPeriods; i++ { if vva.VestingPeriodProgress[i].PeriodComplete { if !vva.VestingPeriodProgress[i].VestingSuccessful { - failedVestedCoins = failedVestedCoins.Add(vva.VestingPeriods[i].Amount) + failedVestedCoins = failedVestedCoins.Add(vva.VestingPeriods[i].Amount...) } } else { break @@ -199,6 +201,97 @@ func (vva ValidatorVestingAccount) Validate() error { return vva.PeriodicVestingAccount.Validate() } +type validatorVestingAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` + DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` + DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` + EndTime int64 `json:"end_time" yaml:"end_time"` + StartTime int64 `json:"start_time" yaml:"start_time"` + VestingPeriods vestingtypes.Periods `json:"vesting_periods" yaml:"vesting_periods"` + ValidatorAddress sdk.ConsAddress `json:"validator_address" yaml:"validator_address"` + ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"` + SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"` + CurrentPeriodProgress CurrentPeriodProgress `json:"current_period_progress" yaml:"current_period_progress"` + VestingPeriodProgress []VestingProgress `json:"vesting_period_progress" yaml:"vesting_period_progress"` + DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` +} + +// MarshalJSON returns the JSON representation of a PeriodicVestingAccount. +func (vva ValidatorVestingAccount) MarshalJSON() ([]byte, error) { + alias := validatorVestingAccountPretty{ + Address: vva.Address, + Coins: vva.Coins, + AccountNumber: vva.AccountNumber, + Sequence: vva.Sequence, + OriginalVesting: vva.OriginalVesting, + DelegatedFree: vva.DelegatedFree, + DelegatedVesting: vva.DelegatedVesting, + EndTime: vva.EndTime, + StartTime: vva.StartTime, + VestingPeriods: vva.VestingPeriods, + ValidatorAddress: vva.ValidatorAddress, + ReturnAddress: vva.ReturnAddress, + SigningThreshold: vva.SigningThreshold, + CurrentPeriodProgress: vva.CurrentPeriodProgress, + VestingPeriodProgress: vva.VestingPeriodProgress, + DebtAfterFailedVesting: vva.DebtAfterFailedVesting, + } + + if vva.PubKey != nil { + pks, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, vva.PubKey) + if err != nil { + return nil, err + } + + alias.PubKey = pks + } + + return json.Marshal(alias) +} + +// UnmarshalJSON unmarshals raw JSON bytes into a PeriodicVestingAccount. +func (vva *ValidatorVestingAccount) UnmarshalJSON(bz []byte) error { + var alias validatorVestingAccountPretty + if err := json.Unmarshal(bz, &alias); err != nil { + return err + } + + var ( + pk crypto.PubKey + err error + ) + + if alias.PubKey != "" { + pk, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + } + + ba := authtypes.NewBaseAccount(alias.Address, alias.Coins, pk, alias.AccountNumber, alias.Sequence) + bva := &vestingtypes.BaseVestingAccount{ + BaseAccount: ba, + OriginalVesting: alias.OriginalVesting, + DelegatedFree: alias.DelegatedFree, + DelegatedVesting: alias.DelegatedVesting, + EndTime: alias.EndTime, + } + pva := vestingtypes.NewPeriodicVestingAccountRaw(bva, alias.StartTime, alias.VestingPeriods) + vva.PeriodicVestingAccount = pva + vva.ValidatorAddress = alias.ValidatorAddress + vva.ReturnAddress = alias.ReturnAddress + vva.SigningThreshold = alias.SigningThreshold + vva.CurrentPeriodProgress = alias.CurrentPeriodProgress + vva.VestingPeriodProgress = alias.VestingPeriodProgress + vva.DebtAfterFailedVesting = alias.DebtAfterFailedVesting + return nil +} + // MarshalYAML returns the YAML representation of an account. func (vva ValidatorVestingAccount) MarshalYAML() (interface{}, error) { var bs []byte @@ -206,7 +299,7 @@ func (vva ValidatorVestingAccount) MarshalYAML() (interface{}, error) { var pubkey string if vva.PubKey != nil { - pubkey, err = sdk.Bech32ifyAccPub(vva.PubKey) + pubkey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, vva.PubKey) if err != nil { return nil, err } diff --git a/x/validator-vesting/types/validator_vesting_account_test.go b/x/validator-vesting/types/validator_vesting_account_test.go index 4c19c68e..5bb91d76 100644 --- a/x/validator-vesting/types/validator_vesting_account_test.go +++ b/x/validator-vesting/types/validator_vesting_account_test.go @@ -222,7 +222,7 @@ func TestSpendableCoinsValidatorVestingAccount(t *testing.T) { // receive some coins recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} - vva.SetCoins(vva.GetCoins().Add(recvAmt)) + vva.SetCoins(vva.GetCoins().Add(recvAmt...)) // require that all vested coins (50%) are spendable plus any received after period 1 completes successfully vva.VestingPeriodProgress[0] = VestingProgress{true, true}