[R4R] bump SDK version to v0.38.3 (#421)

* bump SDK version to v0.38.3

Co-authored-by: Denali Marsh <denali@kava.io>
Co-authored-by: Kevin Davis <kjydavis3@gmail.com>
Co-authored-by: Kevin Davis <karzak@users.noreply.github.com>
Co-authored-by: denalimarsh <denalimarsh@gmail.com>
Co-authored-by: rhuairahrighairigh <ruaridh.odonnell@gmail.com>
This commit is contained in:
Federico Kunze 2020-04-23 12:35:58 -04:00 committed by GitHub
parent 0949a912cf
commit a573625df8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
140 changed files with 3247 additions and 2875 deletions

View File

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

View File

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

19
app/params/doc.go Normal file
View File

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

15
app/params/params.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

17
go.mod
View File

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

409
go.sum
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -115,7 +115,7 @@ func BenchmarkCdpIteration(b *testing.B) {
}
var errResult sdk.Error
var errResult error
func BenchmarkCdpCreation(b *testing.B) {
tApp := app.NewTestApp()

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package operations
package simulation
import (
sdk "github.com/cosmos/cosmos-sdk/types"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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