mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 15:55:18 +00:00
Merge branch 'master' into ro-emergency-gov-module
This commit is contained in:
commit
6c3d525f0f
31
Makefile
31
Makefile
@ -101,7 +101,7 @@ clean:
|
||||
# This tool checks local markdown links as well.
|
||||
# Set to exclude riot links as they trigger false positives
|
||||
link-check:
|
||||
@go run github.com/raviqqe/liche -r . --exclude "^http://127.*|^https://riot.im/app*"
|
||||
@go run github.com/raviqqe/liche -r . --exclude "^http://127.*|^https://riot.im/app*|^http://kava-testnet*|^https://testnet-dex*"
|
||||
|
||||
########################################
|
||||
### Testing
|
||||
@ -112,19 +112,32 @@ link-check:
|
||||
test-all: build
|
||||
# basic app tests
|
||||
@go test ./app -v
|
||||
# cli tests
|
||||
@go test ./cli_test -tags cli_test -v -p 4
|
||||
# basic simulation (seed "2" happens to not unbond all validators before reaching 100 blocks)
|
||||
@go test ./app -run TestFullAppSimulation -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 2 -v -timeout 24h
|
||||
# basic simulation (seed "4" happens to not unbond all validators before reaching 100 blocks)
|
||||
@go test ./app -run TestFullAppSimulation -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 4 -v -timeout 24h
|
||||
# other sim tests
|
||||
@go test ./app -run TestAppImportExport -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 2 -v -timeout 24h
|
||||
@go test ./app -run TestAppSimulationAfterImport -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 2 -v -timeout 24h
|
||||
@go test ./app -run TestAppImportExport -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 4 -v -timeout 24h
|
||||
@go test ./app -run TestAppSimulationAfterImport -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 4 -v -timeout 24h
|
||||
@# AppStateDeterminism does not use Seed flag
|
||||
@go test ./app -run TestAppStateDeterminism -Enabled -Commit -NumBlocks=100 -BlockSize=200 -v -timeout 24h
|
||||
@go test ./app -run TestAppStateDeterminism -Enabled -Commit -NumBlocks=100 -BlockSize=200 -Seed 4 -v -timeout 24h
|
||||
|
||||
# run module tests and short simulations
|
||||
test-basic: test
|
||||
@go test ./app -run TestFullAppSimulation -Enabled -Commit -NumBlocks=5 -BlockSize=200 -Seed 4 -v -timeout 2m
|
||||
# other sim tests
|
||||
@go test ./app -run TestAppImportExport -Enabled -Commit -NumBlocks=5 -BlockSize=200 -Seed 4 -v -timeout 2m
|
||||
@go test ./app -run TestAppSimulationAfterImport -Enabled -Commit -NumBlocks=5 -BlockSize=200 -Seed 4 -v -timeout 2m
|
||||
@# AppStateDeterminism does not use Seed flag
|
||||
@go test ./app -run TestAppStateDeterminism -Enabled -Commit -NumBlocks=5 -BlockSize=200 -Seed 4 -v -timeout 2m
|
||||
|
||||
test:
|
||||
@go test ./...
|
||||
|
||||
test-rest:
|
||||
rest_test/./run_all_tests_from_make.sh
|
||||
|
||||
test-cli:
|
||||
@go test ./cli_test -tags cli_test -v -p 4
|
||||
|
||||
# Kick start lots of sims on an AWS cluster.
|
||||
# This submits an AWS Batch job to run a lot of sims, each within a docker image. Results are uploaded to S3
|
||||
start-remote-sims:
|
||||
@ -140,4 +153,4 @@ start-remote-sims:
|
||||
-—job-definition kava-sim-master \
|
||||
-—container-override environment=[{SIM_NAME=master-$(VERSION)}]
|
||||
|
||||
.PHONY: all build-linux install clean build test test-all start-remote-sims
|
||||
.PHONY: all build-linux install clean build test test-cli test-all test-rest test-basic start-remote-sims
|
||||
|
@ -19,11 +19,9 @@
|
||||
|
||||
### [Telegram](https://t.me/kavalabs) | [Medium](https://medium.com/kava-labs) | [Validator Chat](https://riot.im/app/#/room/#kava-validators:matrix.org)
|
||||
|
||||
### Participate in Kava testnets and [snag a founder badge](./docs/REWARDS.md)!
|
||||
|
||||
</div>
|
||||
|
||||
Reference implementation of Kava, a blockchain for cross-chain DeFi. Built using the [comsos-sdk](https://github.com/cosmos/cosmos-sdk).
|
||||
Reference implementation of Kava, a blockchain for cross-chain DeFi. Built using the [cosmos-sdk](https://github.com/cosmos/cosmos-sdk).
|
||||
|
||||
## Mainnet
|
||||
|
||||
|
176
app/app.go
176
app/app.go
@ -4,13 +4,15 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
|
||||
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"
|
||||
@ -19,6 +21,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"
|
||||
@ -29,10 +32,14 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction"
|
||||
"github.com/kava-labs/kava/x/bep3"
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
"github.com/kava-labs/kava/x/committee"
|
||||
"github.com/kava-labs/kava/x/incentive"
|
||||
"github.com/kava-labs/kava/x/kavadist"
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
@ -60,10 +67,14 @@ var (
|
||||
crisis.AppModuleBasic{},
|
||||
slashing.AppModuleBasic{},
|
||||
supply.AppModuleBasic{},
|
||||
evidence.AppModuleBasic{},
|
||||
auction.AppModuleBasic{},
|
||||
cdp.AppModuleBasic{},
|
||||
pricefeed.AppModuleBasic{},
|
||||
committee.AppModuleBasic{},
|
||||
bep3.AppModuleBasic{},
|
||||
kavadist.AppModuleBasic{},
|
||||
incentive.AppModuleBasic{},
|
||||
)
|
||||
|
||||
// module account permissions
|
||||
@ -78,10 +89,16 @@ var (
|
||||
auction.ModuleName: nil,
|
||||
cdp.ModuleName: {supply.Minter, supply.Burner},
|
||||
cdp.LiquidatorMacc: {supply.Minter, supply.Burner},
|
||||
cdp.SavingsRateMacc: {supply.Minter},
|
||||
bep3.ModuleName: {supply.Minter, supply.Burner},
|
||||
kavadist.ModuleName: {supply.Minter},
|
||||
}
|
||||
)
|
||||
|
||||
// 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
|
||||
@ -103,11 +120,15 @@ type App struct {
|
||||
govKeeper gov.Keeper
|
||||
crisisKeeper crisis.Keeper
|
||||
paramsKeeper params.Keeper
|
||||
evidenceKeeper evidence.Keeper
|
||||
vvKeeper validatorvesting.Keeper
|
||||
auctionKeeper auction.Keeper
|
||||
cdpKeeper cdp.Keeper
|
||||
pricefeedKeeper pricefeed.Keeper
|
||||
committeeKeeper committee.Keeper
|
||||
bep3Keeper bep3.Keeper
|
||||
kavadistKeeper kavadist.Keeper
|
||||
incentiveKeeper incentive.Keeper
|
||||
|
||||
// the module manager
|
||||
mm *module.Manager
|
||||
@ -130,8 +151,9 @@ 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,
|
||||
auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, committee.StoreKey,
|
||||
gov.StoreKey, params.StoreKey, evidence.StoreKey, validatorvesting.StoreKey,
|
||||
auction.StoreKey, cdp.StoreKey, pricefeed.StoreKey, bep3.StoreKey,
|
||||
kavadist.StoreKey, incentive.StoreKey, committee.StoreKey,
|
||||
)
|
||||
tkeys := sdk.NewTransientStoreKeys(params.TStoreKey)
|
||||
|
||||
@ -144,7 +166,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)
|
||||
@ -152,61 +174,83 @@ 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)
|
||||
pricefeedSubspace := app.paramsKeeper.Subspace(pricefeed.DefaultParamspace)
|
||||
bep3Subspace := app.paramsKeeper.Subspace(bep3.DefaultParamspace)
|
||||
kavadistSubspace := app.paramsKeeper.Subspace(kavadist.DefaultParamspace)
|
||||
incentiveSubspace := app.paramsKeeper.Subspace(incentive.DefaultParamspace)
|
||||
|
||||
// add keepers
|
||||
app.accountKeeper = auth.NewAccountKeeper(
|
||||
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
|
||||
|
||||
// create committee keeper with router
|
||||
committeeGovRouter := gov.NewRouter()
|
||||
committeeGovRouter.
|
||||
AddRoute(gov.RouterKey, gov.ProposalHandler).
|
||||
@ -218,7 +262,10 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||
app.cdc,
|
||||
keys[committee.StoreKey],
|
||||
committeeGovRouter,
|
||||
committee.DefaultCodespace) // TODO blacklist module addresses?)
|
||||
committee.DefaultCodespace, // TODO blacklist module addresses?)
|
||||
)
|
||||
|
||||
// create gov keeper with router
|
||||
govRouter := gov.NewRouter()
|
||||
govRouter.
|
||||
AddRoute(gov.RouterKey, gov.ProposalHandler).
|
||||
@ -231,26 +278,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)
|
||||
// NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, pfk types.PricefeedKeeper, sk types.SupplyKeeper, codespace sdk.CodespaceType)
|
||||
)
|
||||
app.auctionKeeper = auction.NewKeeper(
|
||||
app.cdc,
|
||||
keys[auction.StoreKey],
|
||||
app.supplyKeeper,
|
||||
auctionSubspace)
|
||||
auctionSubspace,
|
||||
)
|
||||
app.cdpKeeper = cdp.NewKeeper(
|
||||
app.cdc,
|
||||
keys[cdp.StoreKey],
|
||||
@ -258,7 +307,28 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||
app.pricefeedKeeper,
|
||||
app.auctionKeeper,
|
||||
app.supplyKeeper,
|
||||
cdp.DefaultCodespace)
|
||||
app.accountKeeper,
|
||||
)
|
||||
app.bep3Keeper = bep3.NewKeeper(
|
||||
app.cdc,
|
||||
keys[bep3.StoreKey],
|
||||
app.supplyKeeper,
|
||||
bep3Subspace,
|
||||
)
|
||||
app.kavadistKeeper = kavadist.NewKeeper(
|
||||
app.cdc,
|
||||
keys[kavadist.StoreKey],
|
||||
kavadistSubspace,
|
||||
app.supplyKeeper,
|
||||
)
|
||||
app.incentiveKeeper = incentive.NewKeeper(
|
||||
app.cdc,
|
||||
keys[incentive.StoreKey],
|
||||
incentiveSubspace,
|
||||
app.supplyKeeper,
|
||||
app.cdpKeeper,
|
||||
app.accountKeeper,
|
||||
)
|
||||
|
||||
// register the staking hooks
|
||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
||||
@ -273,36 +343,41 @@ 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),
|
||||
pricefeed.NewAppModule(app.pricefeedKeeper),
|
||||
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),
|
||||
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
||||
committee.NewAppModule(app.committeeKeeper),
|
||||
)
|
||||
|
||||
// During begin block slashing happens after distr.BeginBlocker so that
|
||||
// there is nothing left over in the validator fee pool, so as to keep the
|
||||
// CanWithdrawInvariant invariant.
|
||||
// Auction.BeginBlocker will close out expired auctions and pay debt back to cdp. So it should be run before cdp.BeginBlocker which cancels out debt with stable and starts more auctions.
|
||||
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, auction.ModuleName, cdp.ModuleName, committee.ModuleName)
|
||||
// Auction.BeginBlocker will close out expired auctions and pay debt back to cdp.
|
||||
// So it should be run before cdp.BeginBlocker which cancels out debt with stable and starts more auctions.
|
||||
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName, validatorvesting.ModuleName, kavadist.ModuleName, auction.ModuleName, cdp.ModuleName, bep3.ModuleName, incentive.ModuleName committee.ModuleName)
|
||||
|
||||
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName)
|
||||
|
||||
// Note: genutils must occur after staking so that pools are properly
|
||||
// initialized with tokens from genesis accounts.
|
||||
//
|
||||
// Note: Changing the order of the auth module and modules that use module accounts
|
||||
// results in subtle changes to the way accounts are loaded from genesis.
|
||||
app.mm.SetOrderInitGenesis(
|
||||
auth.ModuleName, validatorvesting.ModuleName, distr.ModuleName,
|
||||
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, supply.ModuleName, crisis.ModuleName, genutil.ModuleName,
|
||||
pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName, committee.ModuleName, // TODO is this order ok?
|
||||
gov.ModuleName, mint.ModuleName, evidence.ModuleName,
|
||||
pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName,
|
||||
bep3.ModuleName, kavadist.ModuleName, incentive.ModuleName, committee.ModuleName
|
||||
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
|
||||
genutil.ModuleName, // genutils must occur after staking so that pools are properly initialized with tokens from genesis accounts.
|
||||
)
|
||||
|
||||
app.mm.RegisterInvariants(&app.crisisKeeper)
|
||||
@ -317,15 +392,17 @@ 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),
|
||||
cdp.NewAppModule(app.cdpKeeper, app.pricefeedKeeper), // TODO how is the order be decided here? Is this order correct?
|
||||
pricefeed.NewAppModule(app.pricefeedKeeper),
|
||||
auction.NewAppModule(app.auctionKeeper, app.supplyKeeper),
|
||||
// TODO committee
|
||||
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),
|
||||
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
||||
)
|
||||
|
||||
app.sm.RegisterStoreDecoders()
|
||||
@ -344,7 +421,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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -413,6 +490,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)
|
||||
|
@ -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
19
app/params/doc.go
Normal 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
|
16
app/params/params.go
Normal file
16
app/params/params.go
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
DefaultWeightMsgClaimReward int = 100
|
||||
)
|
607
app/sim_test.go
607
app/sim_test.go
@ -3,60 +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"
|
||||
)
|
||||
|
||||
// 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"
|
||||
)
|
||||
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) {
|
||||
@ -66,207 +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),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
|
||||
// an IAVLStore for faster simulation speed.
|
||||
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
||||
@ -279,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)
|
||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
||||
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"])
|
||||
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{}},
|
||||
@ -486,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)
|
||||
// export state and simParams before the simulation error is checked
|
||||
err = simapp.CheckExportSimulation(app, config, simParams)
|
||||
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"])
|
||||
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")
|
||||
@ -608,27 +252,25 @@ func TestAppStateDeterminism(t *testing.T) {
|
||||
config.ExportParamsPath = ""
|
||||
config.OnOperation = false
|
||||
config.AllInvariants = false
|
||||
config.ChainID = helpers.SimAppChainID
|
||||
|
||||
numSeeds := 3
|
||||
numTimesToRunPerSeed := 5
|
||||
numTimesToRunPerSeed := 2
|
||||
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
||||
|
||||
for i := 0; i < numSeeds; i++ {
|
||||
config.Seed = rand.Int63()
|
||||
|
||||
for j := 0; j < numTimesToRunPerSeed; j++ {
|
||||
logger := log.NewNopLogger()
|
||||
db := dbm.NewMemDB()
|
||||
app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, interBlockCacheOpt())
|
||||
|
||||
fmt.Printf(
|
||||
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
||||
config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
||||
"running non-determinism simulation; seed %d: attempt: %d/%d\n",
|
||||
config.Seed, j+1, numTimesToRunPerSeed,
|
||||
)
|
||||
|
||||
_, _, 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)
|
||||
|
||||
@ -638,69 +280,8 @@ func TestAppStateDeterminism(t *testing.T) {
|
||||
if j != 0 {
|
||||
require.Equal(
|
||||
t, appHashList[0], appHashList[j],
|
||||
"non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
||||
"non-determinism in seed %d: attempt: %d/%d\n", config.Seed, j+1, numTimesToRunPerSeed,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -28,8 +28,11 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction"
|
||||
"github.com/kava-labs/kava/x/bep3"
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
"github.com/kava-labs/kava/x/committee"
|
||||
"github.com/kava-labs/kava/x/incentive"
|
||||
"github.com/kava-labs/kava/x/kavadist"
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
|
||||
)
|
||||
@ -49,6 +52,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}
|
||||
@ -68,6 +75,9 @@ func (tApp TestApp) GetVVKeeper() validatorvesting.Keeper { return tApp.vvKeeper
|
||||
func (tApp TestApp) GetAuctionKeeper() auction.Keeper { return tApp.auctionKeeper }
|
||||
func (tApp TestApp) GetCDPKeeper() cdp.Keeper { return tApp.cdpKeeper }
|
||||
func (tApp TestApp) GetPriceFeedKeeper() pricefeed.Keeper { return tApp.pricefeedKeeper }
|
||||
func (tApp TestApp) GetBep3Keeper() bep3.Keeper { return tApp.bep3Keeper }
|
||||
func (tApp TestApp) GetKavadistKeeper() kavadist.Keeper { return tApp.kavadistKeeper }
|
||||
func (tApp TestApp) GetIncentiveKeeper() incentive.Keeper { return tApp.incentiveKeeper }
|
||||
func (tApp TestApp) GetCommitteeKeeper() committee.Keeper { return tApp.committeeKeeper }
|
||||
|
||||
// This calls InitChain on the app using the default genesis state, overwitten with any passed in genesis states
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
//___________________________________________________________________________________
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -1,4 +1,19 @@
|
||||
|
||||
# Contrib
|
||||
|
||||
Resources and examples for running and interacting with the kava blockchain.
|
||||
Resources and examples for running and interacting with the kava blockchain. `contrib` contains sample genesis files, example governance proposals, and rest-server request examples for different versions of kava.
|
||||
|
||||
## testnet-4000
|
||||
|
||||
kava-testnet-4000 introduces the cdp and auction modules, allowing users to create CDPs and mint usdx as well as participate in auctions.
|
||||
|
||||
Configuration and rest-server interaction files are available in the [testnet-4000](./testnet-4000/README.md) directory.
|
||||
|
||||
## testnet-5000
|
||||
|
||||
kava-testnet-5000 introduces the bep3 modules, allowing users to transfer BNB between the bnbchain testnet and kava.
|
||||
|
||||
Configuration and rest-server interaction files are available in the [testnet-5000](./testnet-5000/README.md) directory.
|
||||
|
||||
## dev
|
||||
|
||||
During development new features offer require modifications to the genesis file. The dev directory houses these genesis files.
|
||||
|
352
contrib/dev/genesis_examples/genesis_savings_rate.json
Normal file
352
contrib/dev/genesis_examples/genesis_savings_rate.json
Normal file
@ -0,0 +1,352 @@
|
||||
{
|
||||
"genesis_time": "2020-03-07T18:27:07.837213082Z",
|
||||
"chain_id": "testing",
|
||||
"consensus_params": {
|
||||
"block": {
|
||||
"max_bytes": "22020096",
|
||||
"max_gas": "-1",
|
||||
"time_iota_ms": "1000"
|
||||
},
|
||||
"evidence": {
|
||||
"max_age": "100000"
|
||||
},
|
||||
"validator": {
|
||||
"pub_key_types": [
|
||||
"ed25519"
|
||||
]
|
||||
}
|
||||
},
|
||||
"app_hash": "",
|
||||
"app_state": {
|
||||
"cdp": {
|
||||
"cdps": [],
|
||||
"debt_denom": "debt",
|
||||
"deposits": [],
|
||||
"gov_denom": "ukava",
|
||||
"previous_distribution_time": "1970-01-01T00:00:00Z",
|
||||
"params": {
|
||||
"circuit_breaker": false,
|
||||
"collateral_params": [
|
||||
{
|
||||
"auction_size": "5000000000",
|
||||
"conversion_factor": "6",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "10000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "xrp",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "2.0",
|
||||
"market_id": "xrp:usd",
|
||||
"prefix": 0,
|
||||
"stability_fee": "1.000000001547126"
|
||||
},
|
||||
{
|
||||
"auction_size": "10000000",
|
||||
"conversion_factor": "8",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "10000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "btc",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "1.5",
|
||||
"market_id": "btc:usd",
|
||||
"prefix": 1,
|
||||
"stability_fee": "1.0000000007829977"
|
||||
}
|
||||
],
|
||||
"debt_auction_threshold": "1000000000",
|
||||
"debt_params": [
|
||||
{
|
||||
"conversion_factor": "6",
|
||||
"debt_floor": "10000000",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "usdx",
|
||||
"reference_asset": "usd",
|
||||
"savings_rate": "0.95"
|
||||
}
|
||||
],
|
||||
"global_debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"savings_distribution_frequency": "120000000000",
|
||||
"surplus_auction_threshold": "1000000000"
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z",
|
||||
"starting_cdp_id": "1"
|
||||
},
|
||||
"bank": {
|
||||
"send_enabled": true
|
||||
},
|
||||
"params": null,
|
||||
"bep3": {
|
||||
"params": {
|
||||
"bnb_deputy_address": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
|
||||
"min_block_lock": "80",
|
||||
"max_block_lock": "600",
|
||||
"supported_assets": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"coin_id": "459",
|
||||
"limit": "1",
|
||||
"active": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"atomic_swaps": [],
|
||||
"assets_supplies": []
|
||||
},
|
||||
"pricefeed": {
|
||||
"params": {
|
||||
"markets": [
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "xrp",
|
||||
"market_id": "xrp:usd",
|
||||
"oracles": [
|
||||
"kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"kava1xq4cspcgl9thzmn6lkvd6dlx28wsr63zw4mlmf"
|
||||
],
|
||||
"quote_asset": "usd"
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "btc",
|
||||
"market_id": "btc:usd",
|
||||
"oracles": [
|
||||
"kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw"
|
||||
],
|
||||
"quote_asset": "usd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"posted_prices": [
|
||||
{
|
||||
"expiry": "2050-01-01T00:00:00Z",
|
||||
"market_id": "btc:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "8700.0"
|
||||
},
|
||||
{
|
||||
"expiry": "2050-01-01T00:00:00Z",
|
||||
"market_id": "xrp:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "0.25"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gov": {
|
||||
"starting_proposal_id": "1",
|
||||
"deposits": null,
|
||||
"votes": null,
|
||||
"proposals": null,
|
||||
"deposit_params": {
|
||||
"min_deposit": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "10000000"
|
||||
}
|
||||
],
|
||||
"max_deposit_period": "172800000000000"
|
||||
},
|
||||
"voting_params": {
|
||||
"voting_period": "172800000000000"
|
||||
},
|
||||
"tally_params": {
|
||||
"quorum": "0.334000000000000000",
|
||||
"threshold": "0.500000000000000000",
|
||||
"veto": "0.334000000000000000"
|
||||
}
|
||||
},
|
||||
"staking": {
|
||||
"params": {
|
||||
"unbonding_time": "1814400000000000",
|
||||
"max_validators": 100,
|
||||
"max_entries": 7,
|
||||
"bond_denom": "ukava"
|
||||
},
|
||||
"last_total_power": "0",
|
||||
"last_validator_powers": null,
|
||||
"validators": null,
|
||||
"delegations": null,
|
||||
"unbonding_delegations": null,
|
||||
"redelegations": null,
|
||||
"exported": false
|
||||
},
|
||||
"mint": {
|
||||
"minter": {
|
||||
"inflation": "0.130000000000000000",
|
||||
"annual_provisions": "0.000000000000000000"
|
||||
},
|
||||
"params": {
|
||||
"mint_denom": "ukava",
|
||||
"inflation_rate_change": "0.130000000000000000",
|
||||
"inflation_max": "0.200000000000000000",
|
||||
"inflation_min": "0.070000000000000000",
|
||||
"goal_bonded": "0.670000000000000000",
|
||||
"blocks_per_year": "6311520"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"params": {
|
||||
"max_memo_characters": "256",
|
||||
"tx_sig_limit": "7",
|
||||
"tx_size_cost_per_byte": "10",
|
||||
"sig_verify_cost_ed25519": "590",
|
||||
"sig_verify_cost_secp256k1": "1000"
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"type": "cosmos-sdk/Account",
|
||||
"value": {
|
||||
"address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "1000000000000"
|
||||
},
|
||||
{
|
||||
"denom": "btc",
|
||||
"amount": "10000000000"
|
||||
},
|
||||
{
|
||||
"denom": "xrp",
|
||||
"amount": "1000000000000"
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"account_number": "0",
|
||||
"sequence": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cosmos-sdk/Account",
|
||||
"value": {
|
||||
"address": "kava19dp7luf32mlqnw6mhpgk0g37ule7wm2g8gck8a",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "1000000000000"
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"account_number": "0",
|
||||
"sequence": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"auction": {
|
||||
"next_auction_id": "1",
|
||||
"params": {
|
||||
"max_auction_duration": "172800000000000",
|
||||
"bid_duration": "3600000000000"
|
||||
},
|
||||
"auctions": []
|
||||
},
|
||||
"validatorvesting": {
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
},
|
||||
"supply": {
|
||||
"supply": []
|
||||
},
|
||||
"crisis": {
|
||||
"constant_fee": {
|
||||
"denom": "ukava",
|
||||
"amount": "1000"
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"fee_pool": {
|
||||
"community_pool": []
|
||||
},
|
||||
"community_tax": "0.020000000000000000",
|
||||
"base_proposer_reward": "0.010000000000000000",
|
||||
"bonus_proposer_reward": "0.040000000000000000",
|
||||
"withdraw_addr_enabled": true,
|
||||
"delegator_withdraw_infos": [],
|
||||
"previous_proposer": "",
|
||||
"outstanding_rewards": [],
|
||||
"validator_accumulated_commissions": [],
|
||||
"validator_historical_rewards": [],
|
||||
"validator_current_rewards": [],
|
||||
"delegator_starting_infos": [],
|
||||
"validator_slash_events": []
|
||||
},
|
||||
"genutil": {
|
||||
"gentxs": [
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "cosmos-sdk/MsgCreateValidator",
|
||||
"value": {
|
||||
"description": {
|
||||
"moniker": "kava-tester",
|
||||
"identity": "",
|
||||
"website": "",
|
||||
"security_contact": "",
|
||||
"details": ""
|
||||
},
|
||||
"commission": {
|
||||
"rate": "0.100000000000000000",
|
||||
"max_rate": "0.200000000000000000",
|
||||
"max_change_rate": "0.010000000000000000"
|
||||
},
|
||||
"min_self_delegation": "1",
|
||||
"delegator_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"validator_address": "kavavaloper15qdefkmwswysgg4qxgqpqr35k3m49pkx8yhpte",
|
||||
"pubkey": "kavavalconspub1zcjduepq3qyhcxfpa0u2efeu3htuxjz0248khl2cqfcm8jz2n0dr2e2a6tuqqafg2g",
|
||||
"value": {
|
||||
"denom": "ukava",
|
||||
"amount": "10000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "200000"
|
||||
},
|
||||
"signatures": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeySecp256k1",
|
||||
"value": "Az740XKIPCJtnZLmJfktTfhsEStEJE3n2iRVyJ3wko43"
|
||||
},
|
||||
"signature": "ZIzw2qsaJzsNuROW1JYYH1ZOA3jrc4ZCHvrCxirWNlEZqIvnyC42nBQLIPQ+d+PIcpldLVy0KAkb8NBXj9G0nQ=="
|
||||
}
|
||||
],
|
||||
"memo": "6fff8e9b327f0811e7a25c1419781167f82ec7b3@172.31.40.66:26656"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"slashing": {
|
||||
"params": {
|
||||
"max_evidence_age": "120000000000",
|
||||
"signed_blocks_window": "100",
|
||||
"min_signed_per_window": "0.500000000000000000",
|
||||
"downtime_jail_duration": "600000000000",
|
||||
"slash_fraction_double_sign": "0.050000000000000000",
|
||||
"slash_fraction_downtime": "0.010000000000000000"
|
||||
},
|
||||
"signing_infos": {},
|
||||
"missed_blocks": {}
|
||||
}
|
||||
}
|
||||
}
|
352
contrib/genesis_examples/genesis_savings_rate.json
Normal file
352
contrib/genesis_examples/genesis_savings_rate.json
Normal file
@ -0,0 +1,352 @@
|
||||
{
|
||||
"genesis_time": "2020-03-07T18:27:07.837213082Z",
|
||||
"chain_id": "testing",
|
||||
"consensus_params": {
|
||||
"block": {
|
||||
"max_bytes": "22020096",
|
||||
"max_gas": "-1",
|
||||
"time_iota_ms": "1000"
|
||||
},
|
||||
"evidence": {
|
||||
"max_age": "100000"
|
||||
},
|
||||
"validator": {
|
||||
"pub_key_types": [
|
||||
"ed25519"
|
||||
]
|
||||
}
|
||||
},
|
||||
"app_hash": "",
|
||||
"app_state": {
|
||||
"cdp": {
|
||||
"cdps": [],
|
||||
"debt_denom": "debt",
|
||||
"deposits": [],
|
||||
"gov_denom": "ukava",
|
||||
"previous_distribution_time": "1970-01-01T00:00:00Z",
|
||||
"params": {
|
||||
"circuit_breaker": false,
|
||||
"collateral_params": [
|
||||
{
|
||||
"auction_size": "5000000000",
|
||||
"conversion_factor": "6",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "10000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "xrp",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "2.0",
|
||||
"market_id": "xrp:usd",
|
||||
"prefix": 0,
|
||||
"stability_fee": "1.000000001547126"
|
||||
},
|
||||
{
|
||||
"auction_size": "10000000",
|
||||
"conversion_factor": "8",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "10000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "btc",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "1.5",
|
||||
"market_id": "btc:usd",
|
||||
"prefix": 1,
|
||||
"stability_fee": "1.0000000007829977"
|
||||
}
|
||||
],
|
||||
"debt_auction_threshold": "1000000000",
|
||||
"debt_params": [
|
||||
{
|
||||
"conversion_factor": "6",
|
||||
"debt_floor": "10000000",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "usdx",
|
||||
"reference_asset": "usd",
|
||||
"savings_rate": "0.95"
|
||||
}
|
||||
],
|
||||
"global_debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"savings_distribution_frequency": "120000000000",
|
||||
"surplus_auction_threshold": "1000000000"
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z",
|
||||
"starting_cdp_id": "1"
|
||||
},
|
||||
"bank": {
|
||||
"send_enabled": true
|
||||
},
|
||||
"params": null,
|
||||
"bep3": {
|
||||
"params": {
|
||||
"bnb_deputy_address": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
|
||||
"min_block_lock": "80",
|
||||
"max_block_lock": "600",
|
||||
"supported_assets": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"coin_id": "459",
|
||||
"limit": "1",
|
||||
"active": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"atomic_swaps": [],
|
||||
"assets_supplies": []
|
||||
},
|
||||
"pricefeed": {
|
||||
"params": {
|
||||
"markets": [
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "xrp",
|
||||
"market_id": "xrp:usd",
|
||||
"oracles": [
|
||||
"kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"kava1xq4cspcgl9thzmn6lkvd6dlx28wsr63zw4mlmf"
|
||||
],
|
||||
"quote_asset": "usd"
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "btc",
|
||||
"market_id": "btc:usd",
|
||||
"oracles": [
|
||||
"kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw"
|
||||
],
|
||||
"quote_asset": "usd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"posted_prices": [
|
||||
{
|
||||
"expiry": "2050-01-01T00:00:00Z",
|
||||
"market_id": "btc:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "8700.0"
|
||||
},
|
||||
{
|
||||
"expiry": "2050-01-01T00:00:00Z",
|
||||
"market_id": "xrp:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "0.25"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gov": {
|
||||
"starting_proposal_id": "1",
|
||||
"deposits": null,
|
||||
"votes": null,
|
||||
"proposals": null,
|
||||
"deposit_params": {
|
||||
"min_deposit": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "10000000"
|
||||
}
|
||||
],
|
||||
"max_deposit_period": "172800000000000"
|
||||
},
|
||||
"voting_params": {
|
||||
"voting_period": "172800000000000"
|
||||
},
|
||||
"tally_params": {
|
||||
"quorum": "0.334000000000000000",
|
||||
"threshold": "0.500000000000000000",
|
||||
"veto": "0.334000000000000000"
|
||||
}
|
||||
},
|
||||
"staking": {
|
||||
"params": {
|
||||
"unbonding_time": "1814400000000000",
|
||||
"max_validators": 100,
|
||||
"max_entries": 7,
|
||||
"bond_denom": "ukava"
|
||||
},
|
||||
"last_total_power": "0",
|
||||
"last_validator_powers": null,
|
||||
"validators": null,
|
||||
"delegations": null,
|
||||
"unbonding_delegations": null,
|
||||
"redelegations": null,
|
||||
"exported": false
|
||||
},
|
||||
"mint": {
|
||||
"minter": {
|
||||
"inflation": "0.130000000000000000",
|
||||
"annual_provisions": "0.000000000000000000"
|
||||
},
|
||||
"params": {
|
||||
"mint_denom": "ukava",
|
||||
"inflation_rate_change": "0.130000000000000000",
|
||||
"inflation_max": "0.200000000000000000",
|
||||
"inflation_min": "0.070000000000000000",
|
||||
"goal_bonded": "0.670000000000000000",
|
||||
"blocks_per_year": "6311520"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"params": {
|
||||
"max_memo_characters": "256",
|
||||
"tx_sig_limit": "7",
|
||||
"tx_size_cost_per_byte": "10",
|
||||
"sig_verify_cost_ed25519": "590",
|
||||
"sig_verify_cost_secp256k1": "1000"
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"type": "cosmos-sdk/Account",
|
||||
"value": {
|
||||
"address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "1000000000000"
|
||||
},
|
||||
{
|
||||
"denom": "btc",
|
||||
"amount": "10000000000"
|
||||
},
|
||||
{
|
||||
"denom": "xrp",
|
||||
"amount": "1000000000000"
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"account_number": "0",
|
||||
"sequence": "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cosmos-sdk/Account",
|
||||
"value": {
|
||||
"address": "kava19dp7luf32mlqnw6mhpgk0g37ule7wm2g8gck8a",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "ukava",
|
||||
"amount": "1000000000000"
|
||||
}
|
||||
],
|
||||
"public_key": null,
|
||||
"account_number": "0",
|
||||
"sequence": "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"auction": {
|
||||
"next_auction_id": "1",
|
||||
"params": {
|
||||
"max_auction_duration": "172800000000000",
|
||||
"bid_duration": "3600000000000"
|
||||
},
|
||||
"auctions": []
|
||||
},
|
||||
"validatorvesting": {
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
},
|
||||
"supply": {
|
||||
"supply": []
|
||||
},
|
||||
"crisis": {
|
||||
"constant_fee": {
|
||||
"denom": "ukava",
|
||||
"amount": "1000"
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"fee_pool": {
|
||||
"community_pool": []
|
||||
},
|
||||
"community_tax": "0.020000000000000000",
|
||||
"base_proposer_reward": "0.010000000000000000",
|
||||
"bonus_proposer_reward": "0.040000000000000000",
|
||||
"withdraw_addr_enabled": true,
|
||||
"delegator_withdraw_infos": [],
|
||||
"previous_proposer": "",
|
||||
"outstanding_rewards": [],
|
||||
"validator_accumulated_commissions": [],
|
||||
"validator_historical_rewards": [],
|
||||
"validator_current_rewards": [],
|
||||
"delegator_starting_infos": [],
|
||||
"validator_slash_events": []
|
||||
},
|
||||
"genutil": {
|
||||
"gentxs": [
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "cosmos-sdk/MsgCreateValidator",
|
||||
"value": {
|
||||
"description": {
|
||||
"moniker": "kava-tester",
|
||||
"identity": "",
|
||||
"website": "",
|
||||
"security_contact": "",
|
||||
"details": ""
|
||||
},
|
||||
"commission": {
|
||||
"rate": "0.100000000000000000",
|
||||
"max_rate": "0.200000000000000000",
|
||||
"max_change_rate": "0.010000000000000000"
|
||||
},
|
||||
"min_self_delegation": "1",
|
||||
"delegator_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"validator_address": "kavavaloper15qdefkmwswysgg4qxgqpqr35k3m49pkx8yhpte",
|
||||
"pubkey": "kavavalconspub1zcjduepq3qyhcxfpa0u2efeu3htuxjz0248khl2cqfcm8jz2n0dr2e2a6tuqqafg2g",
|
||||
"value": {
|
||||
"denom": "ukava",
|
||||
"amount": "10000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "200000"
|
||||
},
|
||||
"signatures": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeySecp256k1",
|
||||
"value": "Az740XKIPCJtnZLmJfktTfhsEStEJE3n2iRVyJ3wko43"
|
||||
},
|
||||
"signature": "ZIzw2qsaJzsNuROW1JYYH1ZOA3jrc4ZCHvrCxirWNlEZqIvnyC42nBQLIPQ+d+PIcpldLVy0KAkb8NBXj9G0nQ=="
|
||||
}
|
||||
],
|
||||
"memo": "6fff8e9b327f0811e7a25c1419781167f82ec7b3@172.31.40.66:26656"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"slashing": {
|
||||
"params": {
|
||||
"max_evidence_age": "120000000000",
|
||||
"signed_blocks_window": "100",
|
||||
"min_signed_per_window": "0.500000000000000000",
|
||||
"downtime_jail_duration": "600000000000",
|
||||
"slash_fraction_double_sign": "0.050000000000000000",
|
||||
"slash_fraction_downtime": "0.010000000000000000"
|
||||
},
|
||||
"signing_infos": {},
|
||||
"missed_blocks": {}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ Resources and examples for running and interacting with kava-testnet-4000
|
||||
|
||||
### Setup
|
||||
|
||||
Before making a request, query account information for the signing account. Note the 'accountnumber' and 'sequence' fields, we'll need them later in order to send our request:
|
||||
Before making a request, query account information for the signing account. Note the 'accountnumber' and 'sequence' fields, we'll need them later in order to send our request:
|
||||
|
||||
```bash
|
||||
kvcli q auth account $(kvcli keys show accB -a)
|
||||
@ -22,37 +22,37 @@ Now we'll create an unsigned request, sign it, and broadcast it to the Kava bloc
|
||||
|
||||
### Create CDP example request
|
||||
|
||||
Format the base request in create-cdp.json. You'll need to update the 'from', 'chain-id', 'account_number', 'sequence', and 'gas' as appropriate. Then, populate the CDP creation request's params 'owner', 'collateral', and 'principal'. An example formatted request can be found in `example-create-cdp.json`.
|
||||
Format the base request in create-cdp.json. You'll need to update the 'from', 'chain-id', 'account_number', 'sequence', and 'gas' as appropriate. Then, populate the CDP creation request's params 'owner', 'collateral', and 'principal'. An example formatted request can be found in `example-create-cdp.json`.
|
||||
|
||||
```bash
|
||||
# Create an unsigned request
|
||||
curl -H "Content-Type: application/json" -X PUT -d @./contrib/requests/create-cdp.json http://127.0.0.1:1317/cdp | jq > ./contrib/requests/create-cdp-unsigned.json
|
||||
curl -H "Content-Type: application/json" -X PUT -d @./contrib/testnet-4000/rest_examples/cdp/create-cdp.json http://127.0.0.1:1317/cdp | jq > ./contrib/testnet-4000/rest_examples/cdp/create-cdp-unsigned.json
|
||||
|
||||
# Sign the request
|
||||
kvcli tx sign ./contrib/requests/create-cdp-unsigned.json --from accB --offline --chain-id testing --sequence 1 --account-number 2 | jq > ./contrib/requests/broadcast-create-cdp.json
|
||||
kvcli tx sign ./contrib/testnet-4000/rest_examples/cdp/create-cdp-unsigned.json --from accB --offline --chain-id testing --sequence 1 --account-number 2 | jq > ./contrib/testnet-4000/rest_examples/cdp/broadcast-create-cdp.json
|
||||
|
||||
# Broadcast the request
|
||||
kvcli tx broadcast ./contrib/requests/broadcast-create-cdp.json
|
||||
kvcli tx broadcast ./contrib/testnet-4000/rest_examples/cdp/broadcast-create-cdp.json
|
||||
```
|
||||
|
||||
Congratulations, you've just created a CDP on Kava using the rest server!
|
||||
|
||||
### Post market price example request
|
||||
|
||||
Note that only market oracles can post prices, other senders will have their transactions rejected by Kava.
|
||||
Note that only market oracles can post prices, other senders will have their transactions rejected by Kava.
|
||||
|
||||
Format the base request in post-price.json. You'll need to update the 'from', 'chain-id', 'account_number', 'sequence', and 'gas' as appropriate. Then, populate the post price request's params 'from', 'market_id', 'price', and 'expiry'. An example formatted request can be found in `example-post-price.json`.
|
||||
Format the base request in post-price.json. You'll need to update the 'from', 'chain-id', 'account_number', 'sequence', and 'gas' as appropriate. Then, populate the post price request's params 'from', 'market_id', 'price', and 'expiry'. An example formatted request can be found in `example-post-price.json`.
|
||||
|
||||
```bash
|
||||
# Create an unsigned request
|
||||
curl -H "Content-Type: application/json" -X PUT -d @./contrib/requests/post-price.json http://127.0.0.1:1317/pricefeed/postprice | jq > ./contrib/requests/post-price-unsigned.json
|
||||
curl -H "Content-Type: application/json" -X PUT -d @./contrib/testnet-4000/rest_examples/pricefeed/post-price.json http://127.0.0.1:1317/pricefeed/postprice | jq > ./contrib/testnet-4000/rest_examples/pricefeed/post-price-unsigned.json
|
||||
|
||||
|
||||
# Sign the request
|
||||
kvcli tx sign ./contrib/requests/post-price-unsigned.json --from validator --offline --chain-id testing --sequence 96 --account-number 0 | jq > ./contrib/requests/broadcast-post-price.json
|
||||
kvcli tx sign ./contrib/testnet-4000/rest_examples/pricefeed/post-price-unsigned.json --from validator --offline --chain-id testing --sequence 96 --account-number 0 | jq > ./contrib/testnet-4000/rest_examples/pricefeed/broadcast-post-price.json
|
||||
|
||||
# Broadcast the request
|
||||
kvcli tx broadcast ./contrib/requests/broadcast-post-price.json
|
||||
kvcli tx broadcast ./contrib/testnet-4000/rest_examples/pricefeed/broadcast-post-price.json
|
||||
```
|
||||
|
||||
Congratulations, you've just posted a current market price on Kava using the rest server!
|
||||
@ -60,3 +60,11 @@ Congratulations, you've just posted a current market price on Kava using the res
|
||||
## Governance proposals
|
||||
|
||||
Example governance proposals are located in `/proposal_examples`.
|
||||
|
||||
## Dredd automated REST API testing
|
||||
|
||||
To run the dredd tests to hit the endpoints and test the api run the following command from the `kava` folder:
|
||||
|
||||
```bash
|
||||
make test_dredd
|
||||
```
|
125
contrib/testnet-5000/README.md
Normal file
125
contrib/testnet-5000/README.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Testnet-5000
|
||||
|
||||
Testnet-5000 introduces transfers between Kava and Bnbchain via BEP3.
|
||||
|
||||
This guide will walk you through interacting with the blockchains and transferring coins via the rest server. To send transactions, we'll create an unsigned request, sign it, and broadcast it to the Kava blockchain.
|
||||
|
||||
## Rest server requests
|
||||
|
||||
### Setup
|
||||
|
||||
We'll be using Kava's CLI to build, sign, and broadcast the transactions:
|
||||
|
||||
```bash
|
||||
# Download kvcli
|
||||
make install
|
||||
```
|
||||
|
||||
Before making a request, query account information for the signing account. Note the 'accountnumber' and 'sequence' fields, we'll need them later in order to send our request:
|
||||
|
||||
```bash
|
||||
kvcli q auth account $(kvcli keys show testuser -a)
|
||||
```
|
||||
|
||||
### Create swap
|
||||
|
||||
Use the example file in `rest_examples/create-swap.json` to format the request. First, update the header parameters 'from', 'chain-id', 'account_number', 'sequence'.
|
||||
|
||||
Next, we'll update the swap's creation parameters. For that, we need a unique random number that will be used to claim the funds.
|
||||
|
||||
WARNING: Don't use `calc-rnh` for the generation of secrets in production. These values should be generated client-side for the safety of user funds.
|
||||
|
||||
```bash
|
||||
# Generate a sample random number, timestamp, and random number hash
|
||||
kvcli q bep3 calc-rnh now
|
||||
|
||||
# Expected output:
|
||||
# Random number: 110802331073994018312675691928205725441742309715720953510374321628333109608728
|
||||
# Timestamp: 1585203985
|
||||
# Random number hash: 4644fc2d9a2389c60e621785b873ae187e320eaded1687edaa120961428eba9e
|
||||
```
|
||||
|
||||
In the same json file, populate each of the following parameters
|
||||
|
||||
- from
|
||||
- to
|
||||
- recipient_other_chain
|
||||
- sender_other_chain
|
||||
- random_number_hash
|
||||
- timestamp
|
||||
- amount
|
||||
- expected_income
|
||||
- height_span
|
||||
- cross_chain
|
||||
|
||||
Once each parameter is populated, it's time to create our swap:
|
||||
|
||||
```bash
|
||||
# Create an unsigned request
|
||||
curl -H "Content-Type: application/json" -X POST -d @./contrib/testnet-5000/rest_examples/create-swap.json http://127.0.0.1:1317/bep3/swap/create | jq > ./contrib/testnet-5000/rest_examples/create-swap-unsigned.json
|
||||
|
||||
# Sign the request
|
||||
kvcli tx sign ./contrib/testnet-5000/rest_examples/create-swap-unsigned.json --from testnetdeputy --offline --chain-id testing --sequence 0 --account-number 5 | jq > ./contrib/testnet-5000/rest_examples/broadcast-create-swap.json
|
||||
|
||||
# Broadcast the request
|
||||
kvcli tx broadcast ./contrib/testnet-5000/rest_examples/broadcast-create-swap.json
|
||||
```
|
||||
|
||||
The tx broadcast will log information in the terminal, including the txhash. This tx hash can be used to get information about the transaction, including the swap creation event that includes the swap's ID:
|
||||
|
||||
```bash
|
||||
# Get information about the transaction
|
||||
curl -H "Content-Type: application/json" -X GET http://localhost:1317/txs/81A1955216F6D985ECB4770E29B9BCED8F73A42D0C0FD566372CF673CCB81587
|
||||
```
|
||||
|
||||
Congratulations, you've just created a swap on Kava! The swap will be automatically relayed over to Bnbchain where it it can be claimed using the secret random number from above.
|
||||
|
||||
# Claim swap
|
||||
|
||||
Only unexpired swaps can be claimed. To claim a swap, we'll use the secret random number that matches this swap's timestamp and random number hash.
|
||||
|
||||
Generally, claimable swaps must be created on Bnbchain.
|
||||
// TODO: add link to Bnbchain document with interaction steps
|
||||
|
||||
Use the example file in `rest_examples/claim-swap.json` to format the request. Again, update the header parameters 'from', 'account_number', 'sequence'. Check your account using the command from above to ensure that the parameters match the blockchain's state.
|
||||
|
||||
In the same json file, populate each listed parameter:
|
||||
|
||||
- swap_id
|
||||
- random_number
|
||||
|
||||
Once the `swap_id` parameter is populated, it's time to claim our swap:
|
||||
|
||||
```bash
|
||||
# Create an unsigned request
|
||||
curl -H "Content-Type: application/json" -X POST -d @./contrib/testnet-5000/rest_examples/claim-swap.json http://127.0.0.1:1317/bep3/swap/claim | jq > ./contrib/testnet-5000/rest_examples/claim-swap-unsigned.json
|
||||
|
||||
# Sign the request
|
||||
kvcli tx sign ./contrib/testnet-5000/rest_examples/claim-swap-unsigned.json --from user --offline --chain-id testing --sequence 0 --account-number 1 | jq > ./contrib/testnet-5000/rest_examples/broadcast-claim-swap.json
|
||||
|
||||
# Broadcast the request
|
||||
kvcli tx broadcast ./contrib/testnet-5000/rest_examples/broadcast-claim-swap.json
|
||||
```
|
||||
|
||||
# Refund swap
|
||||
|
||||
Only expired swaps may be refunded.
|
||||
|
||||
Use the example file in `rest_examples/refund-swap.json` to format the request. Again, update the header parameters 'from', 'account_number', 'sequence'. Check your account using the command from above to ensure that the parameters match the blockchain's state.
|
||||
|
||||
In the same json file, populate each parameter:
|
||||
|
||||
- swap_id
|
||||
|
||||
Once the `swap_id` parameter is populated, it's time to refund our swap:
|
||||
|
||||
```bash
|
||||
# Create an unsigned request
|
||||
curl -H "Content-Type: application/json" -X POST -d @./contrib/testnet-5000/rest_examples/refund-swap.json http://127.0.0.1:1317/bep3/swap/refund | jq > ./contrib/testnet-5000/rest_examples/refund-swap-unsigned.json
|
||||
|
||||
# Sign the request
|
||||
kvcli tx sign ./contrib/testnet-5000/rest_examples/refund-swap-unsigned.json --from user --offline --chain-id testing --sequence 0 --account-number 1 | jq > ./contrib/testnet-5000/rest_examples/broadcast-refund-swap.json
|
||||
|
||||
# Broadcast the request
|
||||
kvcli tx broadcast ./contrib/testnet-5000/rest_examples/broadcast-refund-swap.json
|
||||
```
|
@ -0,0 +1,244 @@
|
||||
{
|
||||
"genesis_time": "2020-03-27T21:00:00Z",
|
||||
"chain_id": "testing",
|
||||
"consensus_params": {
|
||||
"block": {
|
||||
"max_bytes": "200000",
|
||||
"max_gas": "2000000",
|
||||
"time_iota_ms": "1000"
|
||||
},
|
||||
"evidence": {
|
||||
"max_age": "1000000"
|
||||
},
|
||||
"validator": {
|
||||
"pub_key_types": ["ed25519"]
|
||||
}
|
||||
},
|
||||
"app_hash": "",
|
||||
"app_state": {
|
||||
"auth": {
|
||||
"accounts": [],
|
||||
"params": {
|
||||
"max_memo_characters": "256",
|
||||
"sig_verify_cost_ed25519": "590",
|
||||
"sig_verify_cost_secp256k1": "1000",
|
||||
"tx_sig_limit": "7",
|
||||
"tx_size_cost_per_byte": "10"
|
||||
}
|
||||
},
|
||||
"kavadist": {
|
||||
"params": {
|
||||
"active": true,
|
||||
"periods": [
|
||||
{
|
||||
"start": "2020-03-28T15:20:00Z",
|
||||
"end": "2021-03-28T15:20:00Z",
|
||||
"inflation": "1.000000003022265980"
|
||||
}
|
||||
]
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
},
|
||||
"slashing": {
|
||||
"missed_blocks": {},
|
||||
"params": {
|
||||
"downtime_jail_duration": "600000000000",
|
||||
"max_evidence_age": "3600000000000",
|
||||
"min_signed_per_window": "0.010000000000000000",
|
||||
"signed_blocks_window": "1000",
|
||||
"slash_fraction_double_sign": "0.050000000000000000",
|
||||
"slash_fraction_downtime": "0.000100000000000000"
|
||||
},
|
||||
"signing_infos": {}
|
||||
},
|
||||
"bank": {
|
||||
"send_enabled": true
|
||||
},
|
||||
"distribution": {
|
||||
"base_proposer_reward": "0.010000000000000000",
|
||||
"bonus_proposer_reward": "0.040000000000000000",
|
||||
"community_tax": "0.000000000000000000",
|
||||
"delegator_starting_infos": [],
|
||||
"delegator_withdraw_infos": [],
|
||||
"fee_pool": {
|
||||
"community_pool": []
|
||||
},
|
||||
"outstanding_rewards": [],
|
||||
"previous_proposer": "",
|
||||
"validator_accumulated_commissions": [],
|
||||
"validator_current_rewards": [],
|
||||
"validator_historical_rewards": [],
|
||||
"validator_slash_events": [],
|
||||
"withdraw_addr_enabled": true
|
||||
},
|
||||
"mint": {
|
||||
"minter": {
|
||||
"annual_provisions": "0.000000000000000000",
|
||||
"inflation": "0.020000000000000000"
|
||||
},
|
||||
"params": {
|
||||
"blocks_per_year": "6311520",
|
||||
"goal_bonded": "0.670000000000000000",
|
||||
"inflation_max": "0.130000000000000000",
|
||||
"inflation_min": "0.010000000000000000",
|
||||
"inflation_rate_change": "0.130000000000000000",
|
||||
"mint_denom": "ukava"
|
||||
}
|
||||
},
|
||||
"cdp": {
|
||||
"cdps": [],
|
||||
"debt_denom": "debt",
|
||||
"deposits": [],
|
||||
"gov_denom": "ukava",
|
||||
"params": {
|
||||
"circuit_breaker": false,
|
||||
"collateral_params": [
|
||||
{
|
||||
"auction_size": "50000000000",
|
||||
"conversion_factor": "8",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "bnb",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "2.0",
|
||||
"market_id": "bnb:usd",
|
||||
"prefix": 1,
|
||||
"stability_fee": "1.0000000007829977"
|
||||
}
|
||||
],
|
||||
"debt_auction_threshold": "10000000000",
|
||||
"debt_params": [
|
||||
{
|
||||
"conversion_factor": "6",
|
||||
"debt_floor": "10000000",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "usdx",
|
||||
"reference_asset": "usd",
|
||||
"savings_rate": "0.95"
|
||||
}
|
||||
],
|
||||
"global_debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"savings_distribution_frequency": "120000000000",
|
||||
"surplus_auction_threshold": "1000000000"
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z",
|
||||
"previous_distribution_time": "1970-01-01T00:00:00Z",
|
||||
"starting_cdp_id": "1"
|
||||
},
|
||||
"gov": {
|
||||
"deposit_params": {
|
||||
"max_deposit_period": "600000000000",
|
||||
"min_deposit": [
|
||||
{
|
||||
"amount": "200000000",
|
||||
"denom": "ukava"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deposits": null,
|
||||
"proposals": null,
|
||||
"starting_proposal_id": "1",
|
||||
"tally_params": {
|
||||
"quorum": "0.334000000000000000",
|
||||
"threshold": "0.500000000000000000",
|
||||
"veto": "0.334000000000000000"
|
||||
},
|
||||
"votes": null,
|
||||
"voting_params": {
|
||||
"voting_period": "3600000000000"
|
||||
}
|
||||
},
|
||||
"staking": {
|
||||
"delegations": null,
|
||||
"exported": false,
|
||||
"last_total_power": "0",
|
||||
"last_validator_powers": null,
|
||||
"params": {
|
||||
"bond_denom": "ukava",
|
||||
"max_entries": 7,
|
||||
"max_validators": 100,
|
||||
"unbonding_time": "3600000000000"
|
||||
},
|
||||
"redelegations": null,
|
||||
"unbonding_delegations": null,
|
||||
"validators": null
|
||||
},
|
||||
"supply": {
|
||||
"supply": []
|
||||
},
|
||||
"crisis": {
|
||||
"constant_fee": {
|
||||
"amount": "1333000000",
|
||||
"denom": "ukava"
|
||||
}
|
||||
},
|
||||
"pricefeed": {
|
||||
"params": {
|
||||
"markets": [
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "bnb",
|
||||
"market_id": "bnb:usd",
|
||||
"oracles": ["kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw"],
|
||||
"quote_asset": "usd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"posted_prices": [
|
||||
{
|
||||
"expiry": "2020-04-20T00:00:00Z",
|
||||
"market_id": "bnb:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "12.2639061184"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auction": {
|
||||
"auctions": [],
|
||||
"next_auction_id": "0",
|
||||
"params": {
|
||||
"bid_duration": "600000000000",
|
||||
"max_auction_duration": "172800000000000",
|
||||
"increment_surplus": "0.01",
|
||||
"increment_debt": "0.01",
|
||||
"increment_collateral": "0.01"
|
||||
}
|
||||
},
|
||||
"bep3": {
|
||||
"params": {
|
||||
"bnb_deputy_address": "kava15wmww3chakqllq4n3ksm37lx36qz067gxe6f0k",
|
||||
"min_block_lock": "80",
|
||||
"max_block_lock": "600",
|
||||
"supported_assets": [
|
||||
{
|
||||
"denom": "bnb",
|
||||
"coin_id": "714",
|
||||
"limit": "100000000000",
|
||||
"active": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"genutil": {
|
||||
"gentxs": []
|
||||
},
|
||||
"validatorvesting": {
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
},
|
||||
"params": null
|
||||
}
|
||||
}
|
29
contrib/testnet-5000/rest_examples/broadcast-claim-swap.json
Normal file
29
contrib/testnet-5000/rest_examples/broadcast-claim-swap.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgClaimAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "3CA20F0152F03B0AABE73E7AA1DDF78B9048EDE5A9A73846E1EF53BEBBFA4185",
|
||||
"random_number": "E3D0A98B459F72231DA69C3BD771C1E721FEF4BE83C14B80DC805BA71019EEBE"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeySecp256k1",
|
||||
"value": "AsGjurKnae7kuQQawGO3FLzrjeLCRO2W6orV74fekVYo"
|
||||
},
|
||||
"signature": "aOM9ui+LOSw9GDHe2cKWXPno8Oa1dInphCCJE4WVC5Q/3Kv0j4a6fkfT7sR9uXEQX5rDN7CAH2WrmWxE7E6P7Q=="
|
||||
}
|
||||
],
|
||||
"memo": ""
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgCreateAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1sl8glhaa9f9tep0d9h8gdcfmwcatghtdrfcd2x",
|
||||
"to": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"recipient_other_chain": "tbnb1etjxt3t5r9899w3tf7dnktdue5ngu4ncdwckvt",
|
||||
"sender_other_chain": "tbnb10uypsspvl6jlxcx5xse02pag39l8xpe7a3468h",
|
||||
"random_number_hash": "C0544B7F4B890A673EA3F61BDB4650FBFE2F3E56BDA1B397D6D592FCA7163C8C",
|
||||
"timestamp": "1585252531",
|
||||
"amount": [
|
||||
{
|
||||
"denom": "bnb",
|
||||
"amount": "555555"
|
||||
}
|
||||
],
|
||||
"expected_income": "555555bnb",
|
||||
"height_span": "360",
|
||||
"cross_chain": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeySecp256k1",
|
||||
"value": "A6ExM8g1WQtVD7U5hg+whlWvPza6p24ABFqv6ofv0lQn"
|
||||
},
|
||||
"signature": "1bC1pe1oH20pScI2FimQ98VfvUA/galjDBNkaXcbZKYsE3ig7oGr322PraO2tO44OlY0AiZ1LCVZ15kCxnXO3w=="
|
||||
}
|
||||
],
|
||||
"memo": ""
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgRefundAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "1B00244021EC239867E5B8C44BCD98E40F3148806A8C0A8FD3418872986BECBA"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": [
|
||||
{
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeySecp256k1",
|
||||
"value": "AsGjurKnae7kuQQawGO3FLzrjeLCRO2W6orV74fekVYo"
|
||||
},
|
||||
"signature": "y/T0zLHuvk7oRgTqU9bc6wnWm5JHzhnEk/U1xSWVkxVD2NvC9MvI6lHjXy1iBzyn388x4U8DQ+sxsT/BBA1ehg=="
|
||||
}
|
||||
],
|
||||
"memo": ""
|
||||
}
|
||||
}
|
21
contrib/testnet-5000/rest_examples/claim-swap-unsigned.json
Normal file
21
contrib/testnet-5000/rest_examples/claim-swap-unsigned.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgClaimAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "3CA20F0152F03B0AABE73E7AA1DDF78B9048EDE5A9A73846E1EF53BEBBFA4185",
|
||||
"random_number": "E3D0A98B459F72231DA69C3BD771C1E721FEF4BE83C14B80DC805BA71019EEBE"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": null,
|
||||
"memo": ""
|
||||
}
|
||||
}
|
15
contrib/testnet-5000/rest_examples/claim-swap.json
Normal file
15
contrib/testnet-5000/rest_examples/claim-swap.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"base_req": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"memo": "",
|
||||
"chain_id": "testing",
|
||||
"account_number": "1",
|
||||
"sequence": "0",
|
||||
"gas": "500000",
|
||||
"gas_adjustment": "1.0",
|
||||
"simulate": false
|
||||
},
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "3ca20f0152f03b0aabe73e7aa1ddf78b9048ede5a9a73846e1ef53bebbfa4185",
|
||||
"random_number": "e3d0a98b459f72231da69c3bd771c1e721fef4be83c14b80dc805ba71019eebe"
|
||||
}
|
33
contrib/testnet-5000/rest_examples/create-swap-unsigned.json
Normal file
33
contrib/testnet-5000/rest_examples/create-swap-unsigned.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgCreateAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1sl8glhaa9f9tep0d9h8gdcfmwcatghtdrfcd2x",
|
||||
"to": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"recipient_other_chain": "tbnb1etjxt3t5r9899w3tf7dnktdue5ngu4ncdwckvt",
|
||||
"sender_other_chain": "tbnb10uypsspvl6jlxcx5xse02pag39l8xpe7a3468h",
|
||||
"random_number_hash": "C0544B7F4B890A673EA3F61BDB4650FBFE2F3E56BDA1B397D6D592FCA7163C8C",
|
||||
"timestamp": "1585252531",
|
||||
"amount": [
|
||||
{
|
||||
"denom": "bnb",
|
||||
"amount": "555555"
|
||||
}
|
||||
],
|
||||
"expected_income": "555555bnb",
|
||||
"height_span": "360",
|
||||
"cross_chain": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": null,
|
||||
"memo": ""
|
||||
}
|
||||
}
|
27
contrib/testnet-5000/rest_examples/create-swap.json
Normal file
27
contrib/testnet-5000/rest_examples/create-swap.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"base_req": {
|
||||
"from": "kava1sl8glhaa9f9tep0d9h8gdcfmwcatghtdrfcd2x",
|
||||
"memo": "",
|
||||
"chain_id": "testing",
|
||||
"account_number": "5",
|
||||
"sequence": "0",
|
||||
"gas": "500000",
|
||||
"gas_adjustment": "1.0",
|
||||
"simulate": false
|
||||
},
|
||||
"from": "kava1sl8glhaa9f9tep0d9h8gdcfmwcatghtdrfcd2x",
|
||||
"to": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"recipient_other_chain": "tbnb1etjxt3t5r9899w3tf7dnktdue5ngu4ncdwckvt",
|
||||
"sender_other_chain": "tbnb10uypsspvl6jlxcx5xse02pag39l8xpe7a3468h",
|
||||
"random_number_hash": "c0544b7f4b890a673ea3f61bdb4650fbfe2f3e56bda1b397d6d592fca7163c8c",
|
||||
"timestamp": "1585252531",
|
||||
"amount": [
|
||||
{
|
||||
"denom": "bnb",
|
||||
"amount": "555555"
|
||||
}
|
||||
],
|
||||
"expected_income": "555555bnb",
|
||||
"height_span": "360",
|
||||
"cross_chain": true
|
||||
}
|
20
contrib/testnet-5000/rest_examples/refund-swap-unsigned.json
Normal file
20
contrib/testnet-5000/rest_examples/refund-swap-unsigned.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "cosmos-sdk/StdTx",
|
||||
"value": {
|
||||
"msg": [
|
||||
{
|
||||
"type": "bep3/MsgRefundAtomicSwap",
|
||||
"value": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "1B00244021EC239867E5B8C44BCD98E40F3148806A8C0A8FD3418872986BECBA"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fee": {
|
||||
"amount": [],
|
||||
"gas": "500000"
|
||||
},
|
||||
"signatures": null,
|
||||
"memo": ""
|
||||
}
|
||||
}
|
14
contrib/testnet-5000/rest_examples/refund-swap.json
Normal file
14
contrib/testnet-5000/rest_examples/refund-swap.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"base_req": {
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"memo": "",
|
||||
"chain_id": "testing",
|
||||
"account_number": "1",
|
||||
"sequence": "0",
|
||||
"gas": "500000",
|
||||
"gas_adjustment": "1.0",
|
||||
"simulate": false
|
||||
},
|
||||
"from": "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p",
|
||||
"swap_id": "1b00244021ec239867e5b8c44bcd98e40f3148806a8c0a8fd3418872986becba"
|
||||
}
|
259
contrib/testnet-6000/genesis_examples/wip_genesis.json
Normal file
259
contrib/testnet-6000/genesis_examples/wip_genesis.json
Normal file
@ -0,0 +1,259 @@
|
||||
{
|
||||
"genesis_time": "2020-04-23T16:32:31.393515Z",
|
||||
"chain_id": "testing",
|
||||
"consensus_params": {
|
||||
"block": {
|
||||
"max_bytes": "22020096",
|
||||
"max_gas": "-1",
|
||||
"time_iota_ms": "1000"
|
||||
},
|
||||
"evidence": {
|
||||
"max_age_num_blocks": "100000",
|
||||
"max_age_duration": "172800000000000"
|
||||
},
|
||||
"validator": {
|
||||
"pub_key_types": [
|
||||
"ed25519"
|
||||
]
|
||||
}
|
||||
},
|
||||
"app_hash": "",
|
||||
"app_state": {
|
||||
"auction": {
|
||||
"auctions": [],
|
||||
"next_auction_id": "1",
|
||||
"params": {
|
||||
"bid_duration": "600000000000",
|
||||
"increment_collateral": "0.010000000000000000",
|
||||
"increment_debt": "0.010000000000000000",
|
||||
"increment_surplus": "0.010000000000000000",
|
||||
"max_auction_duration": "172800000000000"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"accounts": [],
|
||||
"params": {
|
||||
"max_memo_characters": "256",
|
||||
"sig_verify_cost_ed25519": "590",
|
||||
"sig_verify_cost_secp256k1": "1000",
|
||||
"tx_sig_limit": "7",
|
||||
"tx_size_cost_per_byte": "10"
|
||||
}
|
||||
},
|
||||
"bank": {
|
||||
"send_enabled": true
|
||||
},
|
||||
"bep3": {
|
||||
"assets_supplies": [],
|
||||
"atomic_swaps": [],
|
||||
"params": {
|
||||
"bnb_deputy_address": "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj",
|
||||
"max_block_lock": "600",
|
||||
"min_block_lock": "80",
|
||||
"supported_assets": [
|
||||
{
|
||||
"active": true,
|
||||
"coin_id": "714",
|
||||
"denom": "bnb",
|
||||
"limit": "100000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdp": {
|
||||
"cdps": [],
|
||||
"debt_denom": "debt",
|
||||
"deposits": [],
|
||||
"gov_denom": "ukava",
|
||||
"params": {
|
||||
"circuit_breaker": false,
|
||||
"collateral_params": [
|
||||
{
|
||||
"auction_size": "50000000000",
|
||||
"conversion_factor": "8",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "bnb",
|
||||
"liquidation_penalty": "0.05",
|
||||
"liquidation_ratio": "2.0",
|
||||
"market_id": "bnb:usd",
|
||||
"prefix": 1,
|
||||
"stability_fee": "1.0000000007829977"
|
||||
}
|
||||
],
|
||||
"debt_auction_threshold": "10000000000",
|
||||
"debt_params": [
|
||||
{
|
||||
"conversion_factor": "6",
|
||||
"debt_floor": "10000000",
|
||||
"debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"denom": "usdx",
|
||||
"reference_asset": "usd",
|
||||
"savings_rate": "0.95"
|
||||
}
|
||||
],
|
||||
"global_debt_limit": [
|
||||
{
|
||||
"amount": "2000000000000",
|
||||
"denom": "usdx"
|
||||
}
|
||||
],
|
||||
"savings_distribution_frequency": "120000000000",
|
||||
"surplus_auction_threshold": "1000000000"
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z",
|
||||
"previous_distribution_time": "1970-01-01T00:00:00Z",
|
||||
"starting_cdp_id": "1"
|
||||
},
|
||||
"crisis": {
|
||||
"constant_fee": {
|
||||
"amount": "1333000000",
|
||||
"denom": "ukava"
|
||||
}
|
||||
},
|
||||
"distribution": {
|
||||
"delegator_starting_infos": [],
|
||||
"delegator_withdraw_infos": [],
|
||||
"fee_pool": {
|
||||
"community_pool": []
|
||||
},
|
||||
"outstanding_rewards": [],
|
||||
"params": {
|
||||
"base_proposer_reward": "0.010000000000000000",
|
||||
"bonus_proposer_reward": "0.040000000000000000",
|
||||
"community_tax": "0.000000000000000000",
|
||||
"withdraw_addr_enabled": true
|
||||
},
|
||||
"previous_proposer": "",
|
||||
"validator_accumulated_commissions": [],
|
||||
"validator_current_rewards": [],
|
||||
"validator_historical_rewards": [],
|
||||
"validator_slash_events": []
|
||||
},
|
||||
"evidence": {
|
||||
"evidence": [],
|
||||
"params": {
|
||||
"max_evidence_age": "120000000000"
|
||||
}
|
||||
},
|
||||
"genutil": {
|
||||
"gentxs": []
|
||||
},
|
||||
"gov": {
|
||||
"deposit_params": {
|
||||
"max_deposit_period": "600000000000",
|
||||
"min_deposit": [
|
||||
{
|
||||
"amount": "200000000",
|
||||
"denom": "ukava"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deposits": null,
|
||||
"proposals": null,
|
||||
"starting_proposal_id": "1",
|
||||
"tally_params": {
|
||||
"quorum": "0.334000000000000000",
|
||||
"threshold": "0.500000000000000000",
|
||||
"veto": "0.334000000000000000"
|
||||
},
|
||||
"votes": null,
|
||||
"voting_params": {
|
||||
"voting_period": "600000000000"
|
||||
}
|
||||
},
|
||||
"kavadist": {
|
||||
"params": {
|
||||
"active": true,
|
||||
"periods": [
|
||||
{
|
||||
"end": "2021-03-28T15:20:00Z",
|
||||
"inflation": "1.000000003022265980",
|
||||
"start": "2020-03-28T15:20:00Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
},
|
||||
"mint": {
|
||||
"minter": {
|
||||
"annual_provisions": "0.000000000000000000",
|
||||
"inflation": "0.020000000000000000"
|
||||
},
|
||||
"params": {
|
||||
"blocks_per_year": "6311520",
|
||||
"goal_bonded": "0.670000000000000000",
|
||||
"inflation_max": "0.130000000000000000",
|
||||
"inflation_min": "0.010000000000000000",
|
||||
"inflation_rate_change": "0.130000000000000000",
|
||||
"mint_denom": "ukava"
|
||||
}
|
||||
},
|
||||
"params": null,
|
||||
"pricefeed": {
|
||||
"params": {
|
||||
"markets": [
|
||||
{
|
||||
"active": true,
|
||||
"base_asset": "bnb",
|
||||
"market_id": "bnb:usd",
|
||||
"oracles": [
|
||||
"kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw"
|
||||
],
|
||||
"quote_asset": "usd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"posted_prices": [
|
||||
{
|
||||
"expiry": "2021-04-20T00:00:00Z",
|
||||
"market_id": "bnb:usd",
|
||||
"oracle_address": "kava15qdefkmwswysgg4qxgqpqr35k3m49pkx2jdfnw",
|
||||
"price": "12.2639061184"
|
||||
}
|
||||
]
|
||||
},
|
||||
"slashing": {
|
||||
"missed_blocks": {},
|
||||
"params": {
|
||||
"downtime_jail_duration": "600000000000",
|
||||
"min_signed_per_window": "0.010000000000000000",
|
||||
"signed_blocks_window": "1000",
|
||||
"slash_fraction_double_sign": "0.050000000000000000",
|
||||
"slash_fraction_downtime": "0.000100000000000000"
|
||||
},
|
||||
"signing_infos": {}
|
||||
},
|
||||
"staking": {
|
||||
"delegations": null,
|
||||
"exported": false,
|
||||
"last_total_power": "0",
|
||||
"last_validator_powers": null,
|
||||
"params": {
|
||||
"bond_denom": "ukava",
|
||||
"historical_entries": 0,
|
||||
"max_entries": 7,
|
||||
"max_validators": 100,
|
||||
"unbonding_time": "3600000000000"
|
||||
},
|
||||
"redelegations": null,
|
||||
"unbonding_delegations": null,
|
||||
"validators": null
|
||||
},
|
||||
"supply": {
|
||||
"supply": []
|
||||
},
|
||||
"validatorvesting": {
|
||||
"previous_block_time": "1970-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
19
go.mod
19
go.mod
@ -3,14 +3,15 @@ module github.com/kava-labs/kava
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/cosmos/cosmos-sdk v0.34.4-0.20191010193331-18de630d0ae1
|
||||
github.com/btcsuite/btcd v0.20.1-beta // indirect
|
||||
github.com/cosmos/cosmos-sdk v0.38.3
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/raviqqe/liche v0.0.0-20191208214012-e144e0808a75 // indirect
|
||||
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
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
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.8
|
||||
)
|
||||
|
470
go.sum
470
go.sum
@ -1,56 +1,84 @@
|
||||
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 h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw=
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE=
|
||||
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
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.20190925161702-9d0bed8f4f4e h1:V8WpJTIAjajE2PE+1wWCG5LUYkWQal+aH6uqPUiZ9Qc=
|
||||
github.com/cosmos/cosmos-sdk v0.34.4-0.20190925161702-9d0bed8f4f4e/go.mod h1:gwKdI16dOjylNYJkaHbcx0TcEIHyRs1xyc5qROmjCJE=
|
||||
github.com/cosmos/cosmos-sdk v0.34.4-0.20191010155330-64a27412505c h1:LM81MVa0CG0Q118dOp6f2Q0MoYIMRTi26zoyqZiL8sY=
|
||||
github.com/cosmos/cosmos-sdk v0.34.4-0.20191010155330-64a27412505c/go.mod h1:FxjFo2Y2ZuqUczTcVfnDUG413OgofBbxFNXA72eaUR4=
|
||||
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/cosmos-sdk v0.37.1 h1:mz5W3Au32VIPPtrY65dheVYeVDSFfS3eSSmuIj+cXsI=
|
||||
github.com/cosmos/cosmos-sdk v0.37.4 h1:1ioXxkpiS+wOgaUbROeDIyuF7hciU5nti0TSyBmV2Ok=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI=
|
||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/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/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.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=
|
||||
@ -59,11 +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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
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=
|
||||
@ -72,72 +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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
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 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||
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=
|
||||
@ -145,153 +234,197 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
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/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.8.5 h1:2ucYeik+NtUTg+IAiNQtoFC5ZGs5mIVidI7Ome0Br3Y=
|
||||
github.com/klauspost/compress v1.8.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
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=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
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.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg=
|
||||
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
|
||||
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/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
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-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
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.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
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/raviqqe/liche v0.0.0-20191208214012-e144e0808a75 h1:o0pGzJnfjk0E+CZg6jxQrADfk9WYO9fMuLOtSP4owtE=
|
||||
github.com/raviqqe/liche v0.0.0-20191208214012-e144e0808a75/go.mod h1:/L9q8uCsB8BOWdzLK+6WIwkAlcMfKhFCZY0n8/CLHRY=
|
||||
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 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||
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 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
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.3 h1:GEnWpGQ795h5oTFNbfBLsY0LW/CW2j6p6HtiYNfxsgg=
|
||||
github.com/tendermint/tendermint v0.32.3/go.mod h1:ZK2c29jl1QRYznIRyRWRDsmm1yvtPzBRT00x4t1JToY=
|
||||
github.com/tendermint/tendermint v0.32.5 h1:2hCLwuzfCKZxXSe/+iMEl+ChJWKJx6g/Wcvq3NMxVN4=
|
||||
github.com/tendermint/tendermint v0.32.5/go.mod h1:D2+A3pNjY+Po72X0mTfaXorFhiVI8dh/Zg640FGyGtE=
|
||||
github.com/tendermint/tendermint v0.32.6 h1:HozXi0USWvKrWuEh5ratnJV10ykkTy4nwXUi0UvPVzg=
|
||||
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 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0=
|
||||
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/tendermint v0.33.4 h1:NM3G9618yC5PaaxGrcAySc5ylc1PAANeIx42u2Re/jo=
|
||||
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.5.0 h1:dhq+O9pmNZFF6qAXpasMO1xSm7dL4qEz2ylfZN8BG9w=
|
||||
github.com/valyala/fasthttp v1.5.0/go.mod h1:eriCz9OhZjKCGfJ185a/IDgNl0bg9IbzfpcslMZXU1c=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
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=
|
||||
@ -299,90 +432,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 h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
|
||||
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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
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-20190827160401-ba9fcec4b297/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-20190222072716-a9d3bda3a223/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-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw=
|
||||
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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
|
||||
gopkg.in/yaml.v2 v2.2.3/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=
|
||||
|
@ -1,44 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="725px" height="213px" viewBox="0 0 725 213" enable-background="new 0 0 725 213" xml:space="preserve">
|
||||
viewBox="0 0 1186 360" style="enable-background:new 0 0 1186 360;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FF564F;}
|
||||
</style>
|
||||
<g>
|
||||
<g opacity="0.2">
|
||||
<polygon fill="#FF564F" points="30,213 111.625,106.851 30,0 20.246,38.336 72.809,106.851 22.664,171.296 "/>
|
||||
</g>
|
||||
<g>
|
||||
<polygon fill="#FF564F" points="154.344,213 72.211,106.851 154.125,0 193,0 111.027,106.851 193.625,213 "/>
|
||||
</g>
|
||||
<g>
|
||||
<rect fill="#FF564F" width="30" height="213"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FF564F" d="M288.04,39.144c-12.1,0-23.001,1.318-32.403,3.917c-9.313,2.58-18.633,6.056-27.701,10.332l-2.473,1.167
|
||||
l9.252,25.495l2.977-1.405c7.314-3.451,14.881-6.231,22.489-8.262c7.549-2.01,16.08-3.028,25.355-3.028
|
||||
c14.401,0,25.424,3.462,33.168,10.29C326.336,84.381,330,94.554,330,107.887v1.038c-6-1.637-12.337-2.99-18.681-4.032
|
||||
c-7.742-1.272-16.909-1.918-27.45-1.918c-10.432,0-20.067,1.193-28.741,3.548c-8.765,2.384-16.429,5.904-22.829,10.463
|
||||
c-6.528,4.646-11.531,10.538-15.07,17.512c-3.538,6.964-5.23,15.158-5.23,24.357v0.626c0,8.815,1.772,16.671,5.445,23.348
|
||||
c3.632,6.604,8.441,12.239,14.38,16.745c5.909,4.489,12.749,7.867,20.369,10.039c7.543,2.152,15.284,3.244,23.028,3.244
|
||||
c14.647,0,26.893-2.927,36.813-8.698c7.052-4.105,12.965-8.749,17.965-13.859V213h30V107.539c0-21.171-5.885-37.887-17.68-49.682
|
||||
C329.902,45.439,311.703,39.144,288.04,39.144z M280.081,187.145c-5.101,0-10.005-0.691-14.581-2.057
|
||||
c-4.535-1.347-8.585-3.293-12.042-5.786c-3.356-2.419-5.818-5.483-7.772-9.111c-1.935-3.584-2.686-7.661-2.686-12.118v-0.626
|
||||
c0-8.815,3.391-15.708,10.839-21.071c7.658-5.517,18.654-8.313,32.907-8.313c9.735,0,18.548,0.716,26.031,2.128
|
||||
c6.664,1.263,12.223,2.613,18.223,4.023v12.649c0,5.912-1.437,11.356-3.999,16.185c-2.594,4.894-6.277,9.184-10.81,12.751
|
||||
c-4.585,3.604-10.025,6.423-16.101,8.377C293.978,186.146,287.223,187.145,280.081,187.145z"/>
|
||||
<polygon fill="#FF564F" points="473.281,171.271 420.85,42 387.678,42 461.364,213 484.532,213 558.564,42 526.015,42 "/>
|
||||
<path fill="#FF564F" d="M707.237,57.857c-12.418-12.418-30.707-18.714-54.369-18.714c-12.1,0-22.999,1.318-32.4,3.917
|
||||
c-9.313,2.58-18.631,6.056-27.7,10.332l-2.472,1.167l9.252,25.495l2.978-1.405c7.314-3.451,14.881-6.231,22.489-8.262
|
||||
c7.549-2.01,16.08-3.028,25.355-3.028c14.401,0,25.507,3.462,33.251,10.29C691.253,84.381,695,94.554,695,107.887v1.038
|
||||
c-6-1.637-12.42-2.99-18.764-4.032c-7.742-1.272-16.951-1.918-27.491-1.918c-10.432,0-20.088,1.193-28.762,3.548
|
||||
c-8.765,2.384-16.439,5.904-22.839,10.463c-6.528,4.646-11.453,10.538-14.992,17.512c-3.538,6.964-5.152,15.158-5.152,24.357
|
||||
v0.626c0,8.815,1.689,16.671,5.362,23.348c3.632,6.604,8.399,12.239,14.339,16.745c5.909,4.489,12.728,7.867,20.348,10.039
|
||||
c7.543,2.152,15.273,3.244,23.017,3.244c14.647,0,26.971-2.927,36.891-8.698c7.052-4.105,13.043-8.749,18.043-13.859V213h30
|
||||
V107.539C725,86.368,719.032,69.652,707.237,57.857z M644.915,187.145c-5.101,0-10.005-0.691-14.581-2.057
|
||||
c-4.535-1.347-8.585-3.293-12.042-5.786c-3.356-2.419-6.235-5.483-8.189-9.111c-1.935-3.584-3.103-7.661-3.103-12.118v-0.626
|
||||
c0-8.815,3.808-15.708,11.256-21.071c7.658-5.517,18.862-8.313,33.116-8.313c9.735,0,18.735,0.716,26.218,2.128
|
||||
c6.664,1.263,12.41,2.613,18.41,4.023v12.649c0,5.912-1.52,11.356-4.082,16.185c-2.594,4.894-6.318,9.184-10.852,12.751
|
||||
c-4.585,3.604-10.046,6.423-16.122,8.377C658.833,186.146,652.057,187.145,644.915,187.145z"/>
|
||||
</g>
|
||||
<polygon class="st0" points="843.7,63.3 909.9,63.3 794.7,349.6 718.2,349.6 603,63.3 669.2,63.3 756.3,280.6 "/>
|
||||
<rect x="9" y="4.1" class="st0" width="60.3" height="345.6"/>
|
||||
<polygon class="st0" points="234.2,349.7 102.1,176.9 234.2,4.1 309.9,4.1 179.5,176.9 309.9,349.7 "/>
|
||||
<path class="st0" d="M454.2,55.7c-66,0-115.7,36.5-119.3,90.8l-0.4,6.4h59.2l1-4.7c5.5-25.5,26.3-39.5,58.5-39.5
|
||||
c35,0,55.1,17.6,55.1,48.4v17.7L434,179c-66.8,3.9-107.2,36.6-107.2,87.4c0,53.6,40.2,89.5,99.3,89.5c35.2,0,60.9-9.6,81.9-31.1
|
||||
V351h60V152.5C568,93.7,522.6,55.7,454.2,55.7z M508.2,221.2V239c-3.9,38.9-30.8,63.3-72,65.3c-15.9,0.8-29.6-3.5-38.6-12.1
|
||||
c-7.1-6.7-10.8-15.7-10.8-25.9c0-22.8,19.4-37.1,53.5-39.3L508.2,221.2z"/>
|
||||
<path class="st0" d="M1063.2,55.7c-66,0-115.7,36.5-119.3,90.8l-0.4,6.4h59.2l1-4.7c5.5-25.5,26.3-39.5,58.5-39.5
|
||||
c35,0,55.1,17.6,55.1,48.4v17.7l-74.2,4.2c-66.8,3.9-107.2,36.6-107.2,87.4c0,53.6,40.2,89.5,99.3,89.5c35.2,0,60.9-9.6,81.9-31.1
|
||||
V351h60V152.5C1177,93.7,1131.6,55.7,1063.2,55.7z M1117.3,221.2V239c-3.9,38.9-30.8,63.3-72,65.3c-15.9,0.8-29.6-3.5-38.6-12.1
|
||||
c-7.1-6.7-10.8-15.7-10.8-25.9c0-22.8,19.4-37.1,53.5-39.3L1117.3,221.2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.5 KiB |
25
rest_test/how_to_run_dredd_tests.md
Normal file
25
rest_test/how_to_run_dredd_tests.md
Normal file
@ -0,0 +1,25 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
///// IF YOU FOLLOW THESE INSTRUCTIONS ALL THE TESTS SHOULD PASS THE FIRST TIME ///////
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Instructions on how to run the `dredd` tests
|
||||
|
||||
(Prerequisite) Make sure that you have the latest versions of `node` and `npm` and `npx` installed. Then install `dredd` globally using the following command folder:
|
||||
|
||||
`npm install dredd --global`
|
||||
|
||||
(Running tests) Run `make test_dredd` from the `kava` directory.
|
||||
|
||||
This builds the `test.go` file, creates the genesis state for the blockchain, starts the blockchain, starts the rest server, sends the required transactions to the blockchain, runs all the `dredd` tests, shuts
|
||||
down the blockchain, cleans up, and propagates up an error code if the tests do not all pass.
|
||||
**IMPORTANT** - It takes about 3 minutes for the script to run and complete:
|
||||
**When you run the script 62 tests should pass and zero should fail**
|
||||
|
||||
(Shutdown) If the script fails or you stop it midway using `ctrl-c` then you should manually stop the blockchain and rest server using the following script. If you let it complete
|
||||
it will automatically shut down the blockchain, rest server and clean up.
|
||||
|
||||
`./stopchain.sh`
|
||||
|
||||
**DEBUGGING NOTE**: If you start getting `Validator set is different` errors then you need to try starting the chain from scratch (do NOT just use `unsafe-reset-all`, instead use `./stopchain.sh` and then `./run_all_tests.sh`)
|
||||
|
||||
|
124
rest_test/run_all_tests_from_make.sh
Executable file
124
rest_test/run_all_tests_from_make.sh
Executable file
@ -0,0 +1,124 @@
|
||||
#! /bin/bash
|
||||
|
||||
# kill kava if it is already running
|
||||
pgrep kvd | xargs kill
|
||||
pgrep kvcli | xargs kill
|
||||
|
||||
# TODO import from development environment in envkey
|
||||
password="password"
|
||||
validatorMnemonic="equip town gesture square tomorrow volume nephew minute witness beef rich gadget actress egg sing secret pole winter alarm law today check violin uncover"
|
||||
# address: kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
|
||||
# address: kavavaloper1ffv7nhd3z6sych2qpqkk03ec6hzkmufyz4scd0
|
||||
faucet="chief access utility giant burger winner jar false naive mobile often perfect advice village love enroll embark bacon under flock harbor render father since"
|
||||
# address: kava1ls82zzghsx0exkpr52m8vht5jqs3un0ceysshz
|
||||
# address: kavavaloper1ls82zzghsx0exkpr52m8vht5jqs3un0c5j2c04
|
||||
# variables for home directories for kvd and kvcli
|
||||
kvdHome=/tmp/kvdHome
|
||||
kvcliHome=/tmp/kvcliHome
|
||||
genesis=$kvdHome/config/genesis.json
|
||||
swaggerFile=swagger-ui/testnet-4000/swagger-testnet-4000.yaml
|
||||
# Remove any existing data directory
|
||||
rm -rf $kvdHome
|
||||
rm -rf $kvcliHome
|
||||
# make the directories
|
||||
mkdir /tmp/kvdHome
|
||||
mkdir /tmp/kvcliHome
|
||||
# create validator key
|
||||
printf "$password\n$validatorMnemonic\n" | kvcli keys add vlad --recover --home $kvcliHome
|
||||
# create faucet key
|
||||
printf "$password\n$faucet\n" | kvcli --home $kvcliHome keys add faucet --recover --home $kvcliHome
|
||||
# function used to show that it is still loading
|
||||
showLoading() {
|
||||
mypid=$!
|
||||
loadingText=$1
|
||||
echo -ne "$loadingText\r"
|
||||
while kill -0 $mypid 2>/dev/null; do
|
||||
echo -ne "$loadingText.\r"
|
||||
sleep 0.5
|
||||
echo -ne "$loadingText..\r"
|
||||
sleep 0.5
|
||||
echo -ne "$loadingText...\r"
|
||||
sleep 0.5
|
||||
echo -ne "\r\033[K"
|
||||
echo -ne "$loadingText\r"
|
||||
sleep 0.5
|
||||
done
|
||||
echo "$loadingText...finished"
|
||||
}
|
||||
# Create new data directory
|
||||
{
|
||||
kvd --home $kvdHome init --chain-id=testing vlad # doesn't need to be the same as the validator
|
||||
} > /dev/null 2>&1
|
||||
kvcli --home $kvcliHome config chain-id testing # or set trust-node true
|
||||
# add validator account to genesis
|
||||
kvd --home $kvdHome add-genesis-account $(kvcli --home $kvcliHome keys show vlad -a) 10000000000000stake,1000000000000xrp,100000000000btc
|
||||
# add faucet account to genesis
|
||||
kvd --home $kvdHome add-genesis-account $(kvcli --home $kvcliHome keys show faucet -a) 10000000000000stake,1000000000000xrp,100000000000btc
|
||||
# Create a delegation tx for the validator and add to genesis
|
||||
printf "$password\n" | kvd --home $kvdHome gentx --name vlad --home-client $kvcliHome
|
||||
{
|
||||
kvd --home $kvdHome collect-gentxs
|
||||
} > /dev/null 2>&1
|
||||
# set blocktime to ~1s
|
||||
jq '.app_state.mint.params.blocks_per_year = "31540000"' $genesis > $genesis.tmp && mv $genesis.tmp $genesis
|
||||
# update pricefeed information
|
||||
jq '.app_state.pricefeed.params.markets += [{"active": true, "base_asset": "xrp", "market_id": "xrp:usd", "oracles": ["kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c"], "quote_asset": "usd"}, {"active": true, "base_asset": "btc", "market_id": "btc:usd", "oracles": ["kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c"], "quote_asset": "usd"}]' $genesis > $genesis.tmp && mv $genesis.tmp $genesis
|
||||
jq '.app_state.pricefeed.posted_prices += [{"expiry": "2050-01-01T00:00:00Z", "market_id": "btc:usd", "oracle_address": "kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c", "price": "8700.0"}, {"expiry": "2050-01-01T00:00:00Z", "market_id": "xrp:usd", "oracle_address": "kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c", "price": "0.25"}]' $genesis > $genesis.tmp && mv $genesis.tmp $genesis
|
||||
# now update cdp params
|
||||
jq '.app_state.cdp.params = { "circuit_breaker": false, "collateral_params": [ { "auction_size": "10000000000", "conversion_factor": "8", "debt_limit": [ { "amount": "1000000000", "denom": "usdx" } ], "denom": "btc", "liquidation_penalty": "0.05", "liquidation_ratio": "1.5", "market_id": "btc:usd", "prefix": 0, "stability_fee": "1.0000000007829977" }, { "auction_size": "100000000", "conversion_factor": "6", "debt_limit": [ { "amount": "10000000", "denom": "usdx" } ], "denom": "xrp", "liquidation_penalty": "0.1", "liquidation_ratio": "2.0", "market_id": "xrp:usd", "prefix": 1, "stability_fee": "1.0000000007829977"} ], "debt_auction_threshold": "9000000", "debt_params": [ { "conversion_factor": "6", "debt_floor": "10000000", "debt_limit": [ { "amount": "2000000000000", "denom": "usdx" } ], "denom": "usdx", "reference_asset": "usd", "savings_rate": "0.95" } ], "global_debt_limit": [ { "amount": "2000000000000", "denom": "usdx" } ], "surplus_auction_threshold": "9000000", "savings_distribution_frequency": "120000000000" }' $genesis > $genesis.tmp && mv $genesis.tmp $genesis
|
||||
# start the blockchain in the background, wait until it starts making blocks
|
||||
{
|
||||
kvd start --home $kvdHome & kvdPid="$!"
|
||||
} > /dev/null 2>&1
|
||||
printf "\n"
|
||||
sleep 10 & showLoading "Starting rest server, please wait"
|
||||
# start the rest server. Use ./stopchain.sh to stop both rest server and the blockchain
|
||||
{
|
||||
kvcli rest-server --laddr tcp://127.0.0.1:1317 --chain-id=testing --home $kvcliHome & kvcliPid="$!"
|
||||
} > /dev/null 2>&1
|
||||
printf "\n"
|
||||
sleep 10 & showLoading "Preparing blockchain setup transactions, please wait"
|
||||
printf "\n"
|
||||
# build the go setup test file
|
||||
rm -f rest_test/setuptest
|
||||
go build rest_test/setup/setuptest.go & showLoading "Building go test file, please wait"
|
||||
# run the go code to send transactions to the chain and set it up correctly
|
||||
./setuptest $kvcliHome & showLoading "Sending messages to blockchain"
|
||||
printf "\n"
|
||||
printf "Blockchain setup completed"
|
||||
printf "\n\n"
|
||||
############################
|
||||
# Now run the dredd tests
|
||||
############################
|
||||
dredd $swaggerFile localhost:1317 2>&1 | tee output & showLoading "Running dredd tests"
|
||||
########################################################
|
||||
# Now run the check the return code from the dredd command.
|
||||
# If 0 then all test passed OK, otherwise some failed and propagate the error
|
||||
########################################################
|
||||
# check that the error code was zero
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
# check that all the tests passed (ie zero failing)
|
||||
if [[ $(cat output | grep "0 failing") ]]
|
||||
then
|
||||
# check for no errors
|
||||
if [[ $(cat output | grep "0 errors") ]]
|
||||
then
|
||||
echo "Success"
|
||||
rm setuptest & showLoading "Cleaning up go binary"
|
||||
# kill the kvd and kvcli processes (blockchain and rest api)
|
||||
pgrep kvd | xargs kill
|
||||
pgrep kvcli | xargs kill & showLoading "Stopping blockchain"
|
||||
rm -f output
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# otherwise return an error code and redirect stderr to stdout so user sees the error output
|
||||
echo "Failure" >&2
|
||||
rm setuptest & showLoading "Cleaning up go binary"
|
||||
# kill the kvd and kvcli processes (blockchain and rest api)
|
||||
pgrep kvd | xargs kill
|
||||
pgrep kvcli | xargs kill & showLoading "Stopping blockchain"
|
||||
rm -f output
|
||||
exit 1
|
481
rest_test/setup/setuptest.go
Normal file
481
rest_test/setup/setuptest.go
Normal file
@ -0,0 +1,481 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
crkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkrest "github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
"github.com/tendermint/go-amino"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
config := sdk.GetConfig()
|
||||
app.SetBech32AddressPrefixes(config)
|
||||
app.SetBip44CoinType(config)
|
||||
config.Seal()
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Printf("Please include the kvcli home directory as a command line argument\n")
|
||||
fmt.Printf("For example: ./setuptest /tmp/kvcliHome\n")
|
||||
fmt.Printf("Exiting...goodbye!\n")
|
||||
return
|
||||
}
|
||||
|
||||
// setup messages send to blockchain so it is in the correct state for testing
|
||||
sendProposal()
|
||||
sendDeposit()
|
||||
sendVote()
|
||||
sendDelegation()
|
||||
sendUndelegation()
|
||||
sendCoins()
|
||||
|
||||
sendProposal()
|
||||
sendDeposit()
|
||||
sendVote()
|
||||
sendDelegation()
|
||||
sendUndelegation()
|
||||
|
||||
sendCoins()
|
||||
|
||||
// create an XRP cdp and send to blockchain
|
||||
sendXrpCdp()
|
||||
|
||||
// create a BTC cdp and send to blockchain
|
||||
sendBtcCdp()
|
||||
|
||||
// reduce the price of BTC to trigger an auction
|
||||
sendMsgPostPrice()
|
||||
}
|
||||
|
||||
// lower the price of xrp to trigger an auction
|
||||
func sendMsgPostPrice() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator address
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
price, err := sdk.NewDecFromStr("1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// set the expiry time
|
||||
expiry := tmtime.Now().Add(time.Second * 100000)
|
||||
|
||||
// create a cdp message to send to the blockchain
|
||||
// from, assetcode, price, expiry
|
||||
msg := pricefeed.NewMsgPostPrice(
|
||||
addr,
|
||||
"btc:usd",
|
||||
price,
|
||||
expiry,
|
||||
)
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// cast to the generic msg type
|
||||
msgToSend := []sdk.Msg{msg}
|
||||
|
||||
// send the message to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, msgToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func sendBtcCdp() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator address
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create a cdp message to send to the blockchain
|
||||
// sender, collateral, principal
|
||||
msg := cdp.NewMsgCreateCDP(
|
||||
addr,
|
||||
sdk.NewCoins(sdk.NewInt64Coin("btc", 200000000)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("usdx", 10000000)),
|
||||
)
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// cast to the generic msg type
|
||||
msgToSend := []sdk.Msg{msg}
|
||||
|
||||
// send the message to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, msgToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func sendXrpCdp() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator address
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create a cdp message to send to the blockchain
|
||||
// sender, collateral, principal
|
||||
msg := cdp.NewMsgCreateCDP(
|
||||
addr,
|
||||
sdk.NewCoins(sdk.NewInt64Coin("xrp", 200000000)),
|
||||
sdk.NewCoins(sdk.NewInt64Coin("usdx", 10000000)),
|
||||
)
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// cast to the generic msg type
|
||||
msgToSend := []sdk.Msg{msg}
|
||||
|
||||
// send the message to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, msgToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func sendProposal() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
proposalContent := gov.ContentFromProposalType("A Test Title", "A test description on this proposal.", gov.ProposalTypeText)
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator address
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create a message to send to the blockchain
|
||||
msg := gov.NewMsgSubmitProposal(
|
||||
proposalContent,
|
||||
sdk.NewCoins(sdk.NewInt64Coin("stake", 1000)),
|
||||
addr,
|
||||
)
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// SEND THE PROPOSAL
|
||||
// cast to the generic msg type
|
||||
msgToSend := []sdk.Msg{msg}
|
||||
|
||||
// send the PROPOSAL message to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, msgToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func sendDeposit() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// NOW SEND THE DEPOSIT
|
||||
|
||||
// create a deposit transaction to send to the proposal
|
||||
amount := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000))
|
||||
deposit := gov.NewMsgDeposit(addr, 2, amount) // TODO IMPORTANT '2' must match 'x-example' in swagger.yaml
|
||||
depositToSend := []sdk.Msg{deposit}
|
||||
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, depositToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func sendVote() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addr, err := sdk.AccAddressFromBech32(address) // validator
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// NOW SEND THE VOTE
|
||||
|
||||
// create a vote on a proposal to send to the blockchain
|
||||
vote := gov.NewMsgVote(addr, uint64(2), types.OptionYes) // TODO IMPORTANT '2' must match 'x-example' in swagger.yaml
|
||||
|
||||
// send a vote to the blockchain
|
||||
voteToSend := []sdk.Msg{vote}
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, voteToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
// this should send coins from one address to another
|
||||
func sendCoins() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addrFrom, err := sdk.AccAddressFromBech32(address) // validator
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addrTo, err := sdk.AccAddressFromBech32("kava1ls82zzghsx0exkpr52m8vht5jqs3un0ceysshz") // TODO IMPORTANT this is the faucet address
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// create coins
|
||||
amount := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 2000000))
|
||||
|
||||
coins := bank.NewMsgSend(addrFrom, addrTo, amount) // TODO IMPORTANT '2' must match 'x-example' in swagger.yaml
|
||||
coinsToSend := []sdk.Msg{coins}
|
||||
|
||||
// NOW SEND THE COINS
|
||||
|
||||
// send the coin message to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, coinsToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func getTestAddress() (address string) {
|
||||
// the test address - TODO IMPORTANT make sure this lines up with startchain.sh
|
||||
address = "kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c"
|
||||
return address
|
||||
}
|
||||
|
||||
func getKeynameAndPassword() (keyname string, password string) {
|
||||
keyname = "vlad" // TODO - IMPORTANT this must match the keys in the startchain.sh script
|
||||
password = "password" // TODO - IMPORTANT this must match the keys in the startchain.sh script
|
||||
return keyname, password
|
||||
}
|
||||
|
||||
// this should send a delegation
|
||||
func sendDelegation() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addrFrom, err := sdk.AccAddressFromBech32(address) // validator
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// get the validator address for delegation
|
||||
valAddr, err := sdk.ValAddressFromBech32("kavavaloper1ffv7nhd3z6sych2qpqkk03ec6hzkmufyz4scd0") // **FAUCET**
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create delegation amount
|
||||
delAmount := sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000)
|
||||
delegation := staking.NewMsgDelegate(addrFrom, valAddr, delAmount)
|
||||
delegationToSend := []sdk.Msg{delegation}
|
||||
|
||||
// send the delegation to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, delegationToSend, keybase)
|
||||
}
|
||||
|
||||
// this should send a MsgUndelegate
|
||||
func sendUndelegation() {
|
||||
// get the address
|
||||
address := getTestAddress()
|
||||
// get the keyname and password
|
||||
keyname, password := getKeynameAndPassword()
|
||||
|
||||
addrFrom, err := sdk.AccAddressFromBech32(address) // validator
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// helper methods for transactions
|
||||
cdc := app.MakeCodec() // make codec for the app
|
||||
|
||||
// get the keybase
|
||||
keybase := getKeybase()
|
||||
|
||||
// get the validator address for delegation
|
||||
valAddr, err := sdk.ValAddressFromBech32("kavavaloper1ffv7nhd3z6sych2qpqkk03ec6hzkmufyz4scd0") // **FAUCET**
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create delegation amount
|
||||
undelAmount := sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000)
|
||||
undelegation := staking.NewMsgUndelegate(addrFrom, valAddr, undelAmount)
|
||||
delegationToSend := []sdk.Msg{undelegation}
|
||||
|
||||
// send the delegation to the blockchain
|
||||
sendMsgToBlockchain(cdc, address, keyname, password, delegationToSend, keybase)
|
||||
|
||||
}
|
||||
|
||||
func getKeybase() crkeys.Keybase {
|
||||
// create a keybase
|
||||
// IMPORTANT - TAKE THIS FROM COMMAND LINE PARAMETER and does NOT work with tilde i.e. ~/ does NOT work
|
||||
keybase, err := keys.NewKeyBaseFromDir(os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return keybase
|
||||
}
|
||||
|
||||
// sendMsgToBlockchain sends a message to the blockchain via the rest api
|
||||
func sendMsgToBlockchain(cdc *amino.Codec, address string, keyname string,
|
||||
password string, msg []sdk.Msg, keybase crkeys.Keybase) {
|
||||
|
||||
// get the account number and sequence number
|
||||
accountNumber, sequenceNumber := getAccountNumberAndSequenceNumber(cdc, address)
|
||||
inBuf := bufio.NewReader(os.Stdin)
|
||||
txBldr := auth.NewTxBuilderFromCLI(inBuf).
|
||||
WithTxEncoder(authclient.GetTxEncoder(cdc)).WithChainID("testing").
|
||||
WithKeybase(keybase).WithAccountNumber(accountNumber).
|
||||
WithSequence(sequenceNumber).WithGas(500000)
|
||||
|
||||
// build and sign the transaction
|
||||
// this is the *Amino* encoded version of the transaction
|
||||
// fmt.Printf("%+v", txBldr.Keybase())
|
||||
txBytes, err := txBldr.BuildAndSign("vlad", "password", msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// fmt.Printf("txBytes: %s", txBytes)
|
||||
|
||||
// need to convert the Amino encoded version back to an actual go struct
|
||||
var tx auth.StdTx
|
||||
cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) // might be unmarshal binary bare
|
||||
|
||||
// now we re-marshall it again into json
|
||||
jsonBytes, err := cdc.MarshalJSON(
|
||||
authrest.BroadcastReq{
|
||||
Tx: tx,
|
||||
Mode: "block",
|
||||
},
|
||||
)
|
||||
|
||||
fmt.Printf("%s", bytes.NewBuffer(jsonBytes))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// fmt.Println("post body: ", string(jsonBytes))
|
||||
|
||||
resp, err := http.Post("http://localhost:1317/txs", "application/json", bytes.NewBuffer(jsonBytes))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("\n\nBody:\n\n")
|
||||
fmt.Println(string(body))
|
||||
|
||||
}
|
||||
|
||||
// getAccountNumberAndSequenceNumber gets an account number and sequence number from the blockchain
|
||||
func getAccountNumberAndSequenceNumber(cdc *amino.Codec, address string) (accountNumber uint64, sequenceNumber uint64) {
|
||||
|
||||
// we need to setup the account number and sequence in order to have a valid transaction
|
||||
resp, err := http.Get("http://localhost:1317/auth/accounts/" + address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var bodyUnmarshalled sdkrest.ResponseWithHeight
|
||||
err = cdc.UnmarshalJSON(body, &bodyUnmarshalled)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var account authexported.Account
|
||||
err = cdc.UnmarshalJSON(bodyUnmarshalled.Result, &account)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return account.GetAccountNumber(), account.GetSequence()
|
||||
|
||||
}
|
7
rest_test/stopchain.sh
Executable file
7
rest_test/stopchain.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#! /bin/bash
|
||||
|
||||
# THIS IS A BASH SCRIPT THAT STOPS THE BLOCKCHAIN AND THE REST API
|
||||
echo "Stopping blockchain"
|
||||
pgrep kvd | xargs kill
|
||||
pgrep kvcli | xargs kill
|
||||
echo "COMPLETED"
|
@ -41,3 +41,5 @@ AWS Batch allows for "array jobs" which are a way of specifying many duplicates
|
||||
|
||||
- click on the compute environment name, to get details, then click the link ECS Cluster Name to get details on the actual machines running
|
||||
- for array jobs, click the job name to get details of the individual jobs
|
||||
|
||||
## Sims - TODO
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -55,6 +41,7 @@ var (
|
||||
// functions aliases
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
RegisterInvariants = keeper.RegisterInvariants
|
||||
NewSurplusAuction = types.NewSurplusAuction
|
||||
NewDebtAuction = types.NewDebtAuction
|
||||
NewCollateralAuction = types.NewCollateralAuction
|
||||
|
@ -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),
|
||||
|
@ -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 {
|
||||
|
@ -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.Add(a.GetModuleAccountCoins())
|
||||
totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...)
|
||||
}
|
||||
|
||||
// check if the module account exists
|
||||
|
@ -29,6 +29,12 @@ func TestInitGenesis(t *testing.T) {
|
||||
tApp := app.NewTestApp()
|
||||
keeper := tApp.GetAuctionKeeper()
|
||||
ctx := tApp.NewContext(true, abci.Header{})
|
||||
// setup module account
|
||||
supplyKeeper := tApp.GetSupplyKeeper()
|
||||
moduleAcc := supplyKeeper.GetModuleAccount(ctx, auction.ModuleName)
|
||||
require.NoError(t, moduleAcc.SetCoins(testAuction.GetModuleAccountCoins()))
|
||||
supplyKeeper.SetModuleAccount(ctx, moduleAcc)
|
||||
|
||||
// create genesis
|
||||
gs := auction.NewGenesisState(
|
||||
10,
|
||||
@ -38,7 +44,7 @@ func TestInitGenesis(t *testing.T) {
|
||||
|
||||
// run init
|
||||
require.NotPanics(t, func() {
|
||||
auction.InitGenesis(ctx, keeper, tApp.GetSupplyKeeper(), gs)
|
||||
auction.InitGenesis(ctx, keeper, supplyKeeper, gs)
|
||||
})
|
||||
|
||||
// check state is as expected
|
||||
@ -59,7 +65,7 @@ func TestInitGenesis(t *testing.T) {
|
||||
return false
|
||||
})
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
t.Run("invalid (invalid nextAuctionID)", func(t *testing.T) {
|
||||
// setup keepers
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{})
|
||||
@ -71,6 +77,24 @@ func TestInitGenesis(t *testing.T) {
|
||||
auction.GenesisAuctions{testAuction},
|
||||
)
|
||||
|
||||
// check init fails
|
||||
require.Panics(t, func() {
|
||||
auction.InitGenesis(ctx, tApp.GetAuctionKeeper(), tApp.GetSupplyKeeper(), gs)
|
||||
})
|
||||
})
|
||||
t.Run("invalid (missing mod account coins)", func(t *testing.T) {
|
||||
// setup keepers
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{})
|
||||
|
||||
// create invalid genesis
|
||||
gs := auction.NewGenesisState(
|
||||
10,
|
||||
auction.DefaultParams(),
|
||||
auction.GenesisAuctions{testAuction},
|
||||
)
|
||||
// invalid as there is no module account setup
|
||||
|
||||
// check init fails
|
||||
require.Panics(t, func() {
|
||||
auction.InitGenesis(ctx, tApp.GetAuctionKeeper(), tApp.GetSupplyKeeper(), gs)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,42 +124,41 @@ 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)
|
||||
}
|
||||
default:
|
||||
err = sdkerrors.Wrap(types.ErrUnrecognizedAuctionType, auction.GetType())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return types.ErrUnrecognizedAuctionType(k.codespace)
|
||||
}
|
||||
|
||||
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.NewCoin(a.Bid.Denom, minNewBidAmt))
|
||||
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.NewCoin(a.Bid.Denom, minNewBidAmt))
|
||||
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.NewCoin(a.Lot.Denom, maxNewLotAmt))
|
||||
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.NewCoin(a.Lot.Denom, 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.NewCoin(a.Lot.Denom, maxNewLotAmt))
|
||||
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.NewCoin(a.Lot.Denom, 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)
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
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, cs(oldBidderOldCoins.Add(cs(oldAuction.GetBid()))...).Add(cs(c("debt", oldAuction.GetBid().Amount.Int64()))), bank.GetCoins(ctx, oldBidder))
|
||||
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)
|
||||
|
143
x/auction/keeper/invariants.go
Normal file
143
x/auction/keeper/invariants.go
Normal file
@ -0,0 +1,143 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction/types"
|
||||
)
|
||||
|
||||
// RegisterInvariants registers all staking invariants
|
||||
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
|
||||
|
||||
ir.RegisterRoute(types.ModuleName, "module-account",
|
||||
ModuleAccountInvariants(k))
|
||||
ir.RegisterRoute(types.ModuleName, "valid-auctions",
|
||||
ValidAuctionInvariant(k))
|
||||
ir.RegisterRoute(types.ModuleName, "valid-index",
|
||||
ValidIndexInvariant(k))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
totalAuctionCoins := sdk.NewCoins()
|
||||
k.IterateAuctions(ctx, func(auction types.Auction) bool {
|
||||
a, ok := auction.(types.GenesisAuction)
|
||||
if !ok {
|
||||
panic("stored auction type does not fulfill GenesisAuction interface")
|
||||
}
|
||||
totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...)
|
||||
return false
|
||||
})
|
||||
|
||||
moduleAccCoins := k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName).GetCoins()
|
||||
broken := !moduleAccCoins.IsEqual(totalAuctionCoins)
|
||||
|
||||
invariantMessage := sdk.FormatInvariant(
|
||||
types.ModuleName,
|
||||
"module account",
|
||||
fmt.Sprintf(
|
||||
"\texpected ModuleAccount coins: %s\n"+
|
||||
"\tactual ModuleAccount coins: %s\n",
|
||||
totalAuctionCoins, moduleAccCoins),
|
||||
)
|
||||
return invariantMessage, broken
|
||||
}
|
||||
}
|
||||
|
||||
// ValidAuctionInvariant verifies that all auctions in the store are independently valid
|
||||
func ValidAuctionInvariant(k Keeper) sdk.Invariant {
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
var validationErr error
|
||||
var invalidAuction types.Auction
|
||||
k.IterateAuctions(ctx, func(auction types.Auction) bool {
|
||||
a, ok := auction.(types.GenesisAuction)
|
||||
if !ok {
|
||||
panic("stored auction type does not fulfill GenesisAuction interface")
|
||||
}
|
||||
|
||||
currentTime := ctx.BlockTime()
|
||||
if !currentTime.Equal(time.Time{}) { // this avoids a simulator bug where app.InitGenesis is called with blockTime=0 instead of the correct time
|
||||
if a.GetEndTime().Before(currentTime) {
|
||||
validationErr = fmt.Errorf("endTime after current block time (%s)", currentTime)
|
||||
invalidAuction = a
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Validate(); err != nil {
|
||||
validationErr = err
|
||||
invalidAuction = a
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
broken := validationErr != nil
|
||||
invariantMessage := sdk.FormatInvariant(
|
||||
types.ModuleName,
|
||||
"valid auctions",
|
||||
fmt.Sprintf(
|
||||
"\tfound invalid auction, reason: %s\n"+
|
||||
"\tauction:\n\t%s\n",
|
||||
validationErr, invalidAuction),
|
||||
)
|
||||
return invariantMessage, broken
|
||||
}
|
||||
}
|
||||
|
||||
// ValidIndexInvariant checks that all auctions in the store are also in the index and vice versa.
|
||||
func ValidIndexInvariant(k Keeper) sdk.Invariant {
|
||||
return func(ctx sdk.Context) (string, bool) {
|
||||
/* Method:
|
||||
- check all the auction IDs in the index have a corresponding auction in the store
|
||||
- index is now valid but there could be extra auction in the store
|
||||
- check for these extra auctions by checking num items in the store equals that of index (store keys are always unique)
|
||||
- doesn't check the IDs in the auction structs match the IDs in the keys
|
||||
*/
|
||||
|
||||
// Check all auction IDs in the index are in the auction store
|
||||
store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AuctionKeyPrefix)
|
||||
|
||||
indexIterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.AuctionByTimeKeyPrefix)
|
||||
defer indexIterator.Close()
|
||||
|
||||
var indexLength int
|
||||
for ; indexIterator.Valid(); indexIterator.Next() {
|
||||
indexLength++
|
||||
|
||||
idBytes := indexIterator.Value()
|
||||
auctionBytes := store.Get(idBytes)
|
||||
if auctionBytes == nil {
|
||||
invariantMessage := sdk.FormatInvariant(
|
||||
types.ModuleName,
|
||||
"valid index",
|
||||
fmt.Sprintf("\tauction with ID '%d' found in index but not in store", types.Uint64FromBytes(idBytes)))
|
||||
return invariantMessage, true
|
||||
}
|
||||
}
|
||||
|
||||
// Check length of auction store matches the length of the index
|
||||
storeIterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.AuctionKeyPrefix)
|
||||
defer storeIterator.Close()
|
||||
var storeLength int
|
||||
for ; storeIterator.Valid(); storeIterator.Next() {
|
||||
storeLength++
|
||||
}
|
||||
|
||||
if storeLength != indexLength {
|
||||
invariantMessage := sdk.FormatInvariant(
|
||||
types.ModuleName,
|
||||
"valid index",
|
||||
fmt.Sprintf("\tmismatched number of items in auction store (%d) and index (%d)", storeLength, indexLength))
|
||||
return invariantMessage, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,46 +73,29 @@ 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
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterInvariants performs a no-op.
|
||||
func (AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
|
||||
// RegisterInvariants registers the module invariants.
|
||||
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
|
||||
RegisterInvariants(ir, am.keeper)
|
||||
}
|
||||
|
||||
// Route module message route name
|
||||
func (AppModule) Route() string {
|
||||
@ -157,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)
|
||||
}
|
||||
|
@ -1,12 +1,33 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"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 {
|
||||
// TODO implement this
|
||||
return ""
|
||||
func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
|
||||
switch {
|
||||
case bytes.Equal(kvA.Key[:1], types.AuctionKeyPrefix):
|
||||
var auctionA, auctionB types.Auction
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &auctionA)
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &auctionB)
|
||||
return fmt.Sprintf("%v\n%v", auctionA, auctionB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.AuctionByTimeKeyPrefix),
|
||||
bytes.Equal(kvA.Key[:1], types.NextAuctionIDKey):
|
||||
auctionIDA := binary.BigEndian.Uint64(kvA.Value)
|
||||
auctionIDB := binary.BigEndian.Uint64(kvB.Value)
|
||||
return fmt.Sprintf("%d\n%d", auctionIDA, auctionIDB)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1]))
|
||||
}
|
||||
}
|
||||
|
57
x/auction/simulation/decoder_test.go
Normal file
57
x/auction/simulation/decoder_test.go
Normal file
@ -0,0 +1,57 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/libs/kv"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction/types"
|
||||
)
|
||||
|
||||
func makeTestCodec() (cdc *codec.Codec) {
|
||||
cdc = codec.New()
|
||||
sdk.RegisterCodec(cdc)
|
||||
types.RegisterCodec(cdc)
|
||||
return
|
||||
}
|
||||
|
||||
func TestDecodeDistributionStore(t *testing.T) {
|
||||
cdc := makeTestCodec()
|
||||
|
||||
oneCoin := sdk.NewCoin("coin", sdk.OneInt())
|
||||
auction := types.NewSurplusAuction("me", oneCoin, "coin", time.Now().UTC())
|
||||
|
||||
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 {
|
||||
name string
|
||||
expectedLog string
|
||||
}{
|
||||
{"Auction", fmt.Sprintf("%v\n%v", auction, auction)},
|
||||
{"AuctionByTime", "2\n2"},
|
||||
{"NextAuctionI", "10\n10"},
|
||||
{"other", ""},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
i, tt := i, tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
switch i {
|
||||
case len(tests) - 1:
|
||||
require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name)
|
||||
default:
|
||||
require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -2,21 +2,170 @@ package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction/types"
|
||||
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// Block time params are un-exported constants in cosmos-sdk/x/simulation.
|
||||
// Copy them here in lieu of importing them.
|
||||
minTimePerBlock time.Duration = (10000 / 2) * time.Second
|
||||
maxTimePerBlock time.Duration = 10000 * time.Second
|
||||
|
||||
// Calculate the average block time
|
||||
AverageBlockTime time.Duration = (maxTimePerBlock - minTimePerBlock) / 2
|
||||
// MaxBidDuration is a crude way of ensuring that BidDuration ≤ MaxAuctionDuration for all generated params
|
||||
MaxBidDuration time.Duration = AverageBlockTime * 50
|
||||
)
|
||||
|
||||
func GenBidDuration(r *rand.Rand) time.Duration {
|
||||
d, err := RandomPositiveDuration(r, 0, MaxBidDuration)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
func GenMaxAuctionDuration(r *rand.Rand) time.Duration {
|
||||
d, err := RandomPositiveDuration(r, MaxBidDuration, AverageBlockTime*200)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func GenIncrementCollateral(r *rand.Rand) sdk.Dec {
|
||||
return simulation.RandomDecAmount(r, sdk.MustNewDecFromStr("1"))
|
||||
}
|
||||
|
||||
var GenIncrementDebt = GenIncrementCollateral
|
||||
var GenIncrementSurplus = GenIncrementCollateral
|
||||
|
||||
// RandomizedGenState generates a random GenesisState for auction
|
||||
func RandomizedGenState(simState *module.SimulationState) {
|
||||
|
||||
// TODO implement this fully
|
||||
// - randomly generating the genesis params
|
||||
// - overwriting with genesis provided to simulation
|
||||
auctionGenesis := types.DefaultGenesisState()
|
||||
p := types.NewParams(
|
||||
GenMaxAuctionDuration(simState.Rand),
|
||||
GenBidDuration(simState.Rand),
|
||||
GenIncrementSurplus(simState.Rand),
|
||||
GenIncrementDebt(simState.Rand),
|
||||
GenIncrementCollateral(simState.Rand),
|
||||
)
|
||||
if err := p.Validate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
auctionGenesis := types.NewGenesisState(
|
||||
types.DefaultNextAuctionID,
|
||||
p,
|
||||
nil,
|
||||
)
|
||||
|
||||
// Add auctions
|
||||
auctions := types.GenesisAuctions{
|
||||
types.NewDebtAuction(
|
||||
cdptypes.LiquidatorMacc, // using cdp account rather than generic test one to avoid having to set permissions on the supply keeper
|
||||
sdk.NewInt64Coin("usdx", 100),
|
||||
sdk.NewInt64Coin("ukava", 1000000000000),
|
||||
simState.GenTimestamp.Add(time.Hour*5),
|
||||
sdk.NewInt64Coin("debt", 100), // same as usdx
|
||||
),
|
||||
}
|
||||
var startingID = auctionGenesis.NextAuctionID
|
||||
var ok bool
|
||||
var totalAuctionCoins sdk.Coins
|
||||
for i, a := range auctions {
|
||||
auctions[i], ok = a.WithID(uint64(i) + startingID).(types.GenesisAuction)
|
||||
if !ok {
|
||||
panic("can't convert Auction to GenesisAuction")
|
||||
}
|
||||
totalAuctionCoins = totalAuctionCoins.Add(a.GetModuleAccountCoins()...)
|
||||
}
|
||||
auctionGenesis.NextAuctionID = startingID + uint64(len(auctions))
|
||||
auctionGenesis.Auctions = append(auctionGenesis.Auctions, auctions...)
|
||||
|
||||
// Also need to update the auction module account (to reflect the coins held in the auctions)
|
||||
var authGenesis auth.GenesisState
|
||||
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis)
|
||||
|
||||
auctionModAcc, found := getAccount(authGenesis.Accounts, supply.NewModuleAddress(types.ModuleName))
|
||||
if !found {
|
||||
auctionModAcc = supply.NewEmptyModuleAccount(types.ModuleName)
|
||||
}
|
||||
if err := auctionModAcc.SetCoins(totalAuctionCoins); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, auctionModAcc)
|
||||
|
||||
// TODO adding bidder coins as well - this should be moved elsewhere
|
||||
bidder, found := getAccount(authGenesis.Accounts, simState.Accounts[0].Address) // 0 is the bidder // FIXME
|
||||
if !found {
|
||||
panic("bidder not found")
|
||||
}
|
||||
bidderCoins := sdk.NewCoins(sdk.NewInt64Coin("usdx", 10000000000))
|
||||
if err := bidder.SetCoins(bidder.GetCoins().Add(bidderCoins...)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, bidder)
|
||||
|
||||
simState.GenState[auth.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
|
||||
|
||||
// Update the supply genesis state to reflect the new coins
|
||||
// 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...)
|
||||
simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis)
|
||||
|
||||
// TODO liquidator mod account doesn't need to be initialized for this example
|
||||
// - it just mints kava, doesn't need a starting balance
|
||||
// - and supply.GetModuleAccount creates one if it doesn't exist
|
||||
|
||||
// Note: this line prints out the auction genesis state, not just the auction parameters. Some sdk modules print out just the parameters.
|
||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, auctionGenesis))
|
||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(auctionGenesis)
|
||||
}
|
||||
|
||||
// Return an account from a list of accounts that matches an address.
|
||||
func getAccount(accounts []authexported.GenesisAccount, addr sdk.AccAddress) (authexported.GenesisAccount, bool) {
|
||||
for _, a := range accounts {
|
||||
if a.GetAddress().Equals(addr) {
|
||||
return a, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// In a list of accounts, replace the first account found with the same address. If not found, append the account.
|
||||
func replaceOrAppendAccount(accounts []authexported.GenesisAccount, acc authexported.GenesisAccount) []authexported.GenesisAccount {
|
||||
newAccounts := accounts
|
||||
for i, a := range accounts {
|
||||
if a.GetAddress().Equals(acc.GetAddress()) {
|
||||
newAccounts[i] = acc
|
||||
return newAccounts
|
||||
}
|
||||
}
|
||||
return append(newAccounts, acc)
|
||||
}
|
||||
|
||||
func RandomPositiveDuration(r *rand.Rand, inclusiveMin, exclusiveMax time.Duration) (time.Duration, error) {
|
||||
min := int64(inclusiveMin)
|
||||
max := int64(exclusiveMax)
|
||||
if min < 0 || max < 0 {
|
||||
return 0, fmt.Errorf("min and max must be positive")
|
||||
}
|
||||
if min >= max {
|
||||
return 0, fmt.Errorf("max must be < min")
|
||||
}
|
||||
randPositiveInt64 := r.Int63n(max-min) + min
|
||||
return time.Duration(randPositiveInt64), nil
|
||||
}
|
||||
|
251
x/auction/simulation/operations.go
Normal file
251
x/auction/simulation/operations.go
Normal 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
|
||||
}
|
@ -1,14 +1,46 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
|
||||
"github.com/kava-labs/kava/x/auction/types"
|
||||
)
|
||||
|
||||
// ParamChanges defines the parameters that can be modified by param change proposals
|
||||
// on the simulation
|
||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
||||
// TODO implement this
|
||||
return []simulation.ParamChange{}
|
||||
// Note: params are encoded to JSON before being stored in the param store. These param changes
|
||||
// update the raw values in the store so values need to be JSON. This is why values that are represented
|
||||
// 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),
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("%d", GenBidDuration(r))
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, string(types.KeyMaxAuctionDuration),
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("%d", GenMaxAuctionDuration(r))
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementCollateral),
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("%d", GenIncrementCollateral(r))
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementDebt),
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("%d", GenIncrementDebt(r))
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, string(types.KeyIncrementSurplus),
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("%d", GenIncrementSurplus(r))
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,12 @@ func (a BaseAuction) Validate() error {
|
||||
if a.EndTime.After(a.MaxEndTime) {
|
||||
return fmt.Errorf("MaxEndTime < EndTime (%s < %s)", a.MaxEndTime, a.EndTime)
|
||||
}
|
||||
if !a.Lot.IsValid() {
|
||||
return fmt.Errorf("invalid lot: %s", a.Lot)
|
||||
}
|
||||
if !a.Bid.IsValid() {
|
||||
return fmt.Errorf("invalid bid: %s", a.Bid)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -148,6 +154,13 @@ func (a DebtAuction) GetModuleAccountCoins() sdk.Coins {
|
||||
// GetPhase returns the direction of a debt auction, which never changes.
|
||||
func (a DebtAuction) GetPhase() string { return "reverse" }
|
||||
|
||||
func (a DebtAuction) Validate() error {
|
||||
if !a.CorrespondingDebt.IsValid() {
|
||||
return fmt.Errorf("invalid corresponding debt: %s", a.CorrespondingDebt)
|
||||
}
|
||||
return a.BaseAuction.Validate()
|
||||
}
|
||||
|
||||
// NewDebtAuction returns a new debt auction.
|
||||
func NewDebtAuction(buyerModAccName string, bid sdk.Coin, initialLot sdk.Coin, endTime time.Time, debt sdk.Coin) DebtAuction {
|
||||
// Note: Bidder is set to the initiator's module account address instead of module name. (when the first bid is placed, it is paid out to the initiator)
|
||||
@ -191,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.
|
||||
@ -208,6 +221,19 @@ func (a CollateralAuction) GetPhase() string {
|
||||
return "forward"
|
||||
}
|
||||
|
||||
func (a CollateralAuction) Validate() error {
|
||||
if !a.CorrespondingDebt.IsValid() {
|
||||
return fmt.Errorf("invalid corresponding debt: %s", a.CorrespondingDebt)
|
||||
}
|
||||
if !a.MaxBid.IsValid() {
|
||||
return fmt.Errorf("invalid max bid: %s", a.MaxBid)
|
||||
}
|
||||
if err := a.LotReturns.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid lot returns: %w", err)
|
||||
}
|
||||
return a.BaseAuction.Validate()
|
||||
}
|
||||
|
||||
func (a CollateralAuction) String() string {
|
||||
return fmt.Sprintf(`Auction %d:
|
||||
Initiator: %s
|
||||
@ -250,17 +276,26 @@ type WeightedAddresses struct {
|
||||
}
|
||||
|
||||
// NewWeightedAddresses returns a new list addresses with weights.
|
||||
func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, sdk.Error) {
|
||||
if len(addrs) != len(weights) {
|
||||
return WeightedAddresses{}, sdk.ErrInternal("number of addresses doesn't match number of weights")
|
||||
}
|
||||
for _, w := range weights {
|
||||
if w.IsNegative() {
|
||||
return WeightedAddresses{}, sdk.ErrInternal("weights contain a negative amount")
|
||||
}
|
||||
}
|
||||
return WeightedAddresses{
|
||||
func NewWeightedAddresses(addrs []sdk.AccAddress, weights []sdk.Int) (WeightedAddresses, error) {
|
||||
wa := WeightedAddresses{
|
||||
Addresses: addrs,
|
||||
Weights: weights,
|
||||
}, nil
|
||||
}
|
||||
if err := wa.Validate(); err != nil {
|
||||
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, %d ≠ %d", len(wa.Addresses), len(wa.Weights))
|
||||
}
|
||||
for _, w := range wa.Weights {
|
||||
if w.IsNegative() {
|
||||
return fmt.Errorf("weights contain a negative amount: %s", w)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
18
x/bep3/abci.go
Normal file
18
x/bep3/abci.go
Normal file
@ -0,0 +1,18 @@
|
||||
package bep3
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// BeginBlocker runs at the start of every block
|
||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||
err := k.UpdateExpiredAtomicSwaps(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = k.DeleteClosedAtomicSwapsFromLongtermStorage(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
244
x/bep3/abci_test.go
Normal file
244
x/bep3/abci_test.go
Normal file
@ -0,0 +1,244 @@
|
||||
package bep3_test
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type ABCITestSuite struct {
|
||||
suite.Suite
|
||||
keeper bep3.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
addrs []sdk.AccAddress
|
||||
swapIDs []tmbytes.HexBytes
|
||||
randomNumbers []tmbytes.HexBytes
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
|
||||
// Set up auth GenesisState
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(11)
|
||||
coins := []sdk.Coins{}
|
||||
for j := 0; j < 11; j++ {
|
||||
coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
|
||||
}
|
||||
authGS := app.NewAuthGenState(addrs, coins)
|
||||
// Initialize test app
|
||||
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[0]))
|
||||
|
||||
suite.ctx = ctx
|
||||
suite.app = tApp
|
||||
suite.addrs = addrs
|
||||
suite.ResetKeeper()
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) ResetKeeper() {
|
||||
suite.keeper = suite.app.GetBep3Keeper()
|
||||
|
||||
var swapIDs []tmbytes.HexBytes
|
||||
var randomNumbers []tmbytes.HexBytes
|
||||
for i := 0; i < 10; i++ {
|
||||
// Set up atomic swap variables
|
||||
expireHeight := int64(360)
|
||||
amount := cs(c("bnb", int64(100)))
|
||||
timestamp := ts(i)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
// Create atomic swap and check err to confirm creation
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight,
|
||||
suite.addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain,
|
||||
amount, amount.String(), true)
|
||||
suite.Nil(err)
|
||||
|
||||
// Store swap's calculated ID and secret random number
|
||||
swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[0], TestSenderOtherChain)
|
||||
swapIDs = append(swapIDs, swapID)
|
||||
randomNumbers = append(randomNumbers, randomNumber.Bytes())
|
||||
}
|
||||
suite.swapIDs = swapIDs
|
||||
suite.randomNumbers = randomNumbers
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestBeginBlocker_UpdateExpiredAtomicSwaps() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
firstCtx sdk.Context
|
||||
secondCtx sdk.Context
|
||||
expectedStatus bep3.SwapStatus
|
||||
expectInStorage bool
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
firstCtx: suite.ctx,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 10),
|
||||
expectedStatus: bep3.Open,
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "after expiration",
|
||||
firstCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400),
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 410),
|
||||
expectedStatus: bep3.Expired,
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "after completion",
|
||||
firstCtx: suite.ctx,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 10),
|
||||
expectedStatus: bep3.Completed,
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "after deletion",
|
||||
firstCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400),
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400 + bep3.DefaultLongtermStorageDuration),
|
||||
expectedStatus: bep3.NULL,
|
||||
expectInStorage: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
// Reset keeper and run the initial begin blocker
|
||||
suite.ResetKeeper()
|
||||
suite.Run(tc.name, func() {
|
||||
bep3.BeginBlocker(tc.firstCtx, suite.keeper)
|
||||
|
||||
switch tc.expectedStatus {
|
||||
case bep3.Completed:
|
||||
for i, swapID := range suite.swapIDs {
|
||||
err := suite.keeper.ClaimAtomicSwap(tc.firstCtx, suite.addrs[5], swapID, suite.randomNumbers[i])
|
||||
suite.Nil(err)
|
||||
}
|
||||
case bep3.NULL:
|
||||
for _, swapID := range suite.swapIDs {
|
||||
err := suite.keeper.RefundAtomicSwap(tc.firstCtx, suite.addrs[5], swapID)
|
||||
suite.Nil(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the second begin blocker
|
||||
bep3.BeginBlocker(tc.secondCtx, suite.keeper)
|
||||
|
||||
// Check each swap's availibility and status
|
||||
for _, swapID := range suite.swapIDs {
|
||||
storedSwap, found := suite.keeper.GetAtomicSwap(tc.secondCtx, swapID)
|
||||
if tc.expectInStorage {
|
||||
suite.True(found)
|
||||
} else {
|
||||
suite.False(found)
|
||||
}
|
||||
suite.Equal(tc.expectedStatus, storedSwap.Status)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ABCITestSuite) TestBeginBlocker_DeleteClosedAtomicSwapsFromLongtermStorage() {
|
||||
type Action int
|
||||
const (
|
||||
NULL Action = 0x00
|
||||
Refund Action = 0x01
|
||||
Claim Action = 0x02
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
firstCtx sdk.Context
|
||||
action Action
|
||||
secondCtx sdk.Context
|
||||
expectInStorage bool
|
||||
}{
|
||||
{
|
||||
name: "no action with long storage duration",
|
||||
firstCtx: suite.ctx,
|
||||
action: NULL,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration),
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "claim with short storage duration",
|
||||
firstCtx: suite.ctx,
|
||||
action: Claim,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 5000),
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "claim with long storage duration",
|
||||
firstCtx: suite.ctx,
|
||||
action: Claim,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration),
|
||||
expectInStorage: false,
|
||||
},
|
||||
{
|
||||
name: "refund with short storage duration",
|
||||
firstCtx: suite.ctx,
|
||||
action: Refund,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 5000),
|
||||
expectInStorage: true,
|
||||
},
|
||||
{
|
||||
name: "refund with long storage duration",
|
||||
firstCtx: suite.ctx,
|
||||
action: Refund,
|
||||
secondCtx: suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + bep3.DefaultLongtermStorageDuration),
|
||||
expectInStorage: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
// Reset keeper and run the initial begin blocker
|
||||
suite.ResetKeeper()
|
||||
suite.Run(tc.name, func() {
|
||||
bep3.BeginBlocker(tc.firstCtx, suite.keeper)
|
||||
|
||||
switch tc.action {
|
||||
case Claim:
|
||||
for i, swapID := range suite.swapIDs {
|
||||
err := suite.keeper.ClaimAtomicSwap(tc.firstCtx, suite.addrs[5], swapID, suite.randomNumbers[i])
|
||||
suite.Nil(err)
|
||||
}
|
||||
case Refund:
|
||||
for _, swapID := range suite.swapIDs {
|
||||
swap, _ := suite.keeper.GetAtomicSwap(tc.firstCtx, swapID)
|
||||
refundCtx := suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + swap.ExpireHeight)
|
||||
bep3.BeginBlocker(refundCtx, suite.keeper)
|
||||
err := suite.keeper.RefundAtomicSwap(refundCtx, suite.addrs[5], swapID)
|
||||
suite.Nil(err)
|
||||
// Add expire height to second ctx block height
|
||||
tc.secondCtx = tc.secondCtx.WithBlockHeight(tc.secondCtx.BlockHeight() + swap.ExpireHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the second begin blocker
|
||||
bep3.BeginBlocker(tc.secondCtx, suite.keeper)
|
||||
|
||||
// Check each swap's availibility and status
|
||||
for _, swapID := range suite.swapIDs {
|
||||
_, found := suite.keeper.GetAtomicSwap(tc.secondCtx, swapID)
|
||||
if tc.expectInStorage {
|
||||
suite.True(found)
|
||||
} else {
|
||||
suite.False(found)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestABCITestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ABCITestSuite))
|
||||
}
|
141
x/bep3/alias.go
Normal file
141
x/bep3/alias.go
Normal file
@ -0,0 +1,141 @@
|
||||
package bep3
|
||||
|
||||
import (
|
||||
"github.com/kava-labs/kava/x/bep3/client/rest"
|
||||
"github.com/kava-labs/kava/x/bep3/keeper"
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
const (
|
||||
AddrByteCount = types.AddrByteCount
|
||||
AttributeKeyAmount = types.AttributeKeyAmount
|
||||
AttributeKeyAtomicSwapID = types.AttributeKeyAtomicSwapID
|
||||
AttributeKeyAtomicSwapIDs = types.AttributeKeyAtomicSwapIDs
|
||||
AttributeKeyClaimSender = types.AttributeKeyClaimSender
|
||||
AttributeKeyDirection = types.AttributeKeyDirection
|
||||
AttributeKeyExpectedIncome = types.AttributeKeyExpectedIncome
|
||||
AttributeKeyExpireHeight = types.AttributeKeyExpireHeight
|
||||
AttributeKeyRandomNumber = types.AttributeKeyRandomNumber
|
||||
AttributeKeyRandomNumberHash = types.AttributeKeyRandomNumberHash
|
||||
AttributeKeyRecipient = types.AttributeKeyRecipient
|
||||
AttributeKeyRefundSender = types.AttributeKeyRefundSender
|
||||
AttributeKeySender = types.AttributeKeySender
|
||||
AttributeKeySenderOtherChain = types.AttributeKeySenderOtherChain
|
||||
AttributeKeyTimestamp = types.AttributeKeyTimestamp
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
CalcSwapID = types.CalcSwapID
|
||||
ClaimAtomicSwap = types.ClaimAtomicSwap
|
||||
Completed = types.Completed
|
||||
CreateAtomicSwap = types.CreateAtomicSwap
|
||||
DefaultLongtermStorageDuration = types.DefaultLongtermStorageDuration
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
DepositAtomicSwap = types.DepositAtomicSwap
|
||||
EventTypeClaimAtomicSwap = types.EventTypeClaimAtomicSwap
|
||||
EventTypeCreateAtomicSwap = types.EventTypeCreateAtomicSwap
|
||||
EventTypeRefundAtomicSwap = types.EventTypeRefundAtomicSwap
|
||||
EventTypeSwapsExpired = types.EventTypeSwapsExpired
|
||||
Expired = types.Expired
|
||||
INVALID = types.INVALID
|
||||
Incoming = types.Incoming
|
||||
Int64Size = types.Int64Size
|
||||
MaxExpectedIncomeLength = types.MaxExpectedIncomeLength
|
||||
MaxOtherChainAddrLength = types.MaxOtherChainAddrLength
|
||||
ModuleName = types.ModuleName
|
||||
NULL = types.NULL
|
||||
Open = types.Open
|
||||
Outgoing = types.Outgoing
|
||||
QuerierRoute = types.QuerierRoute
|
||||
QueryGetAssetSupply = types.QueryGetAssetSupply
|
||||
QueryGetAtomicSwap = types.QueryGetAtomicSwap
|
||||
QueryGetAtomicSwaps = types.QueryGetAtomicSwaps
|
||||
QueryGetParams = types.QueryGetParams
|
||||
RandomNumberHashLength = types.RandomNumberHashLength
|
||||
RandomNumberLength = types.RandomNumberLength
|
||||
RefundAtomicSwap = types.RefundAtomicSwap
|
||||
RouterKey = types.RouterKey
|
||||
StoreKey = types.StoreKey
|
||||
SwapIDLength = types.SwapIDLength
|
||||
)
|
||||
|
||||
var (
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
RegisterRoutes = rest.RegisterRoutes
|
||||
BytesToHex = types.BytesToHex
|
||||
CalculateRandomHash = types.CalculateRandomHash
|
||||
CalculateSwapID = types.CalculateSwapID
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
DefaultParams = types.DefaultParams
|
||||
ErrAssetNotActive = types.ErrAssetNotActive
|
||||
ErrAssetNotSupported = types.ErrAssetNotSupported
|
||||
ErrAssetSupplyNotFound = types.ErrAssetSupplyNotFound
|
||||
ErrAtomicSwapAlreadyExists = types.ErrAtomicSwapAlreadyExists
|
||||
ErrAtomicSwapNotFound = types.ErrAtomicSwapNotFound
|
||||
ErrExceedsAvailableSupply = types.ErrExceedsAvailableSupply
|
||||
ErrExceedsSupplyLimit = types.ErrExceedsSupplyLimit
|
||||
ErrInvalidClaimSecret = types.ErrInvalidClaimSecret
|
||||
ErrInvalidCurrentSupply = types.ErrInvalidCurrentSupply
|
||||
ErrInvalidHeightSpan = types.ErrInvalidHeightSpan
|
||||
ErrInvalidIncomingSupply = types.ErrInvalidIncomingSupply
|
||||
ErrInvalidOutgoingSupply = types.ErrInvalidOutgoingSupply
|
||||
ErrInvalidTimestamp = types.ErrInvalidTimestamp
|
||||
ErrSwapNotClaimable = types.ErrSwapNotClaimable
|
||||
ErrSwapNotRefundable = types.ErrSwapNotRefundable
|
||||
GenerateSecureRandomNumber = types.GenerateSecureRandomNumber
|
||||
GetAtomicSwapByHeightKey = types.GetAtomicSwapByHeightKey
|
||||
HexToBytes = types.HexToBytes
|
||||
NewAssetSupply = types.NewAssetSupply
|
||||
NewAtomicSwap = types.NewAtomicSwap
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewMsgClaimAtomicSwap = types.NewMsgClaimAtomicSwap
|
||||
NewMsgCreateAtomicSwap = types.NewMsgCreateAtomicSwap
|
||||
NewMsgRefundAtomicSwap = types.NewMsgRefundAtomicSwap
|
||||
NewParams = types.NewParams
|
||||
NewQueryAssetSupply = types.NewQueryAssetSupply
|
||||
NewQueryAtomicSwapByID = types.NewQueryAtomicSwapByID
|
||||
NewQueryAtomicSwaps = types.NewQueryAtomicSwaps
|
||||
NewSwapDirectionFromString = types.NewSwapDirectionFromString
|
||||
NewSwapStatusFromString = types.NewSwapStatusFromString
|
||||
ParamKeyTable = types.ParamKeyTable
|
||||
RegisterCodec = types.RegisterCodec
|
||||
Uint64FromBytes = types.Uint64FromBytes
|
||||
Uint64ToBytes = types.Uint64ToBytes
|
||||
|
||||
// variable aliases
|
||||
AbsoluteMaximumBlockLock = types.AbsoluteMaximumBlockLock
|
||||
AbsoluteMinimumBlockLock = types.AbsoluteMinimumBlockLock
|
||||
AssetSupplyKeyPrefix = types.AssetSupplyKeyPrefix
|
||||
AtomicSwapByBlockPrefix = types.AtomicSwapByBlockPrefix
|
||||
AtomicSwapCoinsAccAddr = types.AtomicSwapCoinsAccAddr
|
||||
AtomicSwapKeyPrefix = types.AtomicSwapKeyPrefix
|
||||
AtomicSwapLongtermStoragePrefix = types.AtomicSwapLongtermStoragePrefix
|
||||
DefaultMaxBlockLock = types.DefaultMaxBlockLock
|
||||
DefaultMinBlockLock = types.DefaultMinBlockLock
|
||||
DefaultSupportedAssets = types.DefaultSupportedAssets
|
||||
KeyBnbDeputyAddress = types.KeyBnbDeputyAddress
|
||||
KeyMaxBlockLock = types.KeyMaxBlockLock
|
||||
KeyMinBlockLock = types.KeyMinBlockLock
|
||||
KeySupportedAssets = types.KeySupportedAssets
|
||||
ModuleCdc = types.ModuleCdc
|
||||
)
|
||||
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
AssetParam = types.AssetParam
|
||||
AssetParams = types.AssetParams
|
||||
AssetSupplies = types.AssetSupplies
|
||||
AssetSupply = types.AssetSupply
|
||||
AtomicSwap = types.AtomicSwap
|
||||
AtomicSwaps = types.AtomicSwaps
|
||||
GenesisState = types.GenesisState
|
||||
MsgClaimAtomicSwap = types.MsgClaimAtomicSwap
|
||||
MsgCreateAtomicSwap = types.MsgCreateAtomicSwap
|
||||
MsgRefundAtomicSwap = types.MsgRefundAtomicSwap
|
||||
Params = types.Params
|
||||
QueryAssetSupply = types.QueryAssetSupply
|
||||
QueryAtomicSwapByID = types.QueryAtomicSwapByID
|
||||
QueryAtomicSwaps = types.QueryAtomicSwaps
|
||||
SupplyKeeper = types.SupplyKeeper
|
||||
SwapDirection = types.SwapDirection
|
||||
SwapStatus = types.SwapStatus
|
||||
)
|
225
x/bep3/client/cli/query.go
Normal file
225
x/bep3/client/cli/query.go
Normal file
@ -0,0 +1,225 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
// GetQueryCmd returns the cli query commands for this module
|
||||
func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
// Group bep3 queries under a subcommand
|
||||
bep3QueryCmd := &cobra.Command{
|
||||
Use: "bep3",
|
||||
Short: "Querying commands for the bep3 module",
|
||||
}
|
||||
|
||||
bep3QueryCmd.AddCommand(flags.GetCommands(
|
||||
QueryCalcSwapIDCmd(queryRoute, cdc),
|
||||
QueryCalcRandomNumberHashCmd(queryRoute, cdc),
|
||||
QueryGetAtomicSwapCmd(queryRoute, cdc),
|
||||
QueryGetAssetSupplyCmd(queryRoute, cdc),
|
||||
QueryGetAtomicSwapsCmd(queryRoute, cdc),
|
||||
QueryParamsCmd(queryRoute, cdc),
|
||||
)...)
|
||||
|
||||
return bep3QueryCmd
|
||||
}
|
||||
|
||||
// QueryCalcRandomNumberHashCmd calculates the random number hash for a number and timestamp
|
||||
func QueryCalcRandomNumberHashCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "calc-rnh [unix-timestamp]",
|
||||
Short: "calculates an example random number hash from an optional timestamp",
|
||||
Example: "bep3 calc-rnh now",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
userTimestamp := "now"
|
||||
if len(args) > 0 {
|
||||
userTimestamp = args[0]
|
||||
}
|
||||
|
||||
// Timestamp defaults to time.Now() unless it's explicitly set
|
||||
var timestamp int64
|
||||
if strings.Compare(userTimestamp, "now") == 0 {
|
||||
timestamp = tmtime.Now().Unix()
|
||||
} else {
|
||||
userTimestamp, err := strconv.ParseInt(userTimestamp, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timestamp = userTimestamp
|
||||
}
|
||||
|
||||
// Load hex-encoded cryptographically strong pseudo-random number
|
||||
randomNumber, err := types.GenerateSecureRandomNumber()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
// Prepare random number, timestamp, and hash for output
|
||||
randomNumberStr := fmt.Sprintf("Random number: %s\n", randomNumber)
|
||||
timestampStr := fmt.Sprintf("Timestamp: %d\n", timestamp)
|
||||
randomNumberHashStr := fmt.Sprintf("Random number hash: %s", hex.EncodeToString(randomNumberHash))
|
||||
output := []string{randomNumberStr, timestampStr, randomNumberHashStr}
|
||||
return cliCtx.PrintOutput(strings.Join(output, ""))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryCalcSwapIDCmd calculates the swapID for a random number hash, sender, and sender other chain
|
||||
func QueryCalcSwapIDCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "calc-swapid [random-number-hash] [sender] [sender-other-chain]",
|
||||
Short: "calculate swap ID for the given random number hash, sender, and sender other chain",
|
||||
Example: "bep3 calc-swapid 0677bd8a303dd981810f34d8e5cc6507f13b391899b84d3c1be6c6045a17d747 kava15qdefkmwswysgg4qxgcqpqr35k3m49pkx2jdfnw bnb1ud3q90r98l3mhd87kswv3h8cgrymzeljct8qn7",
|
||||
Args: cobra.MinimumNArgs(3),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
// Parse query params
|
||||
randomNumberHash, err := types.HexToBytes(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sender := sdk.AccAddress(args[1])
|
||||
senderOtherChain := args[2]
|
||||
|
||||
// Calculate swap ID and convert to human-readable string
|
||||
swapID := types.CalculateSwapID(randomNumberHash, sender, senderOtherChain)
|
||||
return cliCtx.PrintOutput(hex.EncodeToString(swapID))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryGetAssetSupplyCmd queries as asset's current in swap supply, active, supply, and supply limit
|
||||
func QueryGetAssetSupplyCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "supply [denom]",
|
||||
Short: "get information about an asset's supply",
|
||||
Example: "bep3 supply bnb",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
// Prepare query params
|
||||
bz, err := cdc.MarshalJSON(types.NewQueryAssetSupply([]byte(args[0])))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute query
|
||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAssetSupply), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode and print results
|
||||
var assetSupply types.AssetSupply
|
||||
cdc.MustUnmarshalJSON(res, &assetSupply)
|
||||
return cliCtx.PrintOutput(assetSupply)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryGetAtomicSwapCmd queries an AtomicSwap by swapID
|
||||
func QueryGetAtomicSwapCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "swap [swap-id]",
|
||||
Short: "get atomic swap information",
|
||||
Example: "bep3 swap 6682c03cc3856879c8fb98c9733c6b0c30758299138166b6523fe94628b1d3af",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
// Decode swapID's hex encoded string to []byte
|
||||
swapID, err := types.HexToBytes(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare query params
|
||||
bz, err := cdc.MarshalJSON(types.NewQueryAtomicSwapByID(swapID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute query
|
||||
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAtomicSwap), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var atomicSwap types.AtomicSwap
|
||||
cdc.MustUnmarshalJSON(res, &atomicSwap)
|
||||
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
return cliCtx.PrintOutput(atomicSwap.String())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryGetAtomicSwapsCmd queries AtomicSwaps in the store
|
||||
func QueryGetAtomicSwapsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "swaps",
|
||||
Short: "get a list of active atomic swaps",
|
||||
Example: "bep3 swaps",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetAtomicSwaps), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var atomicSwaps types.AtomicSwaps
|
||||
cdc.MustUnmarshalJSON(res, &atomicSwaps)
|
||||
|
||||
if len(atomicSwaps) == 0 {
|
||||
return fmt.Errorf("There are currently no atomic swaps")
|
||||
}
|
||||
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
return cliCtx.PrintOutput(atomicSwaps.String())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryParamsCmd queries the bep3 module parameters
|
||||
func QueryParamsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "params",
|
||||
Short: "get the bep3 module parameters",
|
||||
Example: "bep3 params",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
// Query
|
||||
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetParams)
|
||||
res, _, err := cliCtx.QueryWithData(route, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode and print results
|
||||
var out types.Params
|
||||
cdc.MustUnmarshalJSON(res, &out)
|
||||
return cliCtx.PrintOutput(out)
|
||||
},
|
||||
}
|
||||
}
|
185
x/bep3/client/cli/tx.go
Normal file
185
x/bep3/client/cli/tx.go
Normal file
@ -0,0 +1,185 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
"github.com/spf13/cobra"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
// GetTxCmd returns the transaction commands for this module
|
||||
func GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||
bep3TxCmd := &cobra.Command{
|
||||
Use: "bep3",
|
||||
Short: "bep3 transactions subcommands",
|
||||
}
|
||||
|
||||
bep3TxCmd.AddCommand(flags.PostCommands(
|
||||
GetCmdCreateAtomicSwap(cdc),
|
||||
GetCmdClaimAtomicSwap(cdc),
|
||||
GetCmdRefundAtomicSwap(cdc),
|
||||
)...)
|
||||
|
||||
return bep3TxCmd
|
||||
}
|
||||
|
||||
// GetCmdCreateAtomicSwap cli command for creating atomic swaps
|
||||
func GetCmdCreateAtomicSwap(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "create [to] [recipient-other-chain] [sender-other-chain] [timestamp] [coins] [expected-income] [height-span] [cross-chain]",
|
||||
Short: "create a new atomic swap",
|
||||
Example: fmt.Sprintf("%s tx %s create kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj bnb1urfermcg92dwq36572cx4xg84wpk3lfpksr5g7 bnb1uky3me9ggqypmrsvxk7ur6hqkzq7zmv4ed4ng7 now 100bnb 100bnb 360 true --from validator",
|
||||
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(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||
|
||||
from := cliCtx.GetFromAddress() // same as KavaExecutor.DeputyAddress (for cross-chain)
|
||||
to, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recipientOtherChain := args[1] // same as OtherExecutor.DeputyAddress
|
||||
senderOtherChain := args[2]
|
||||
|
||||
// Timestamp defaults to time.Now() unless it's explicitly set
|
||||
var timestamp int64
|
||||
if strings.Compare(args[3], "now") == 0 {
|
||||
timestamp = tmtime.Now().Unix()
|
||||
} else {
|
||||
timestamp, err = strconv.ParseInt(args[3], 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate cryptographically strong pseudo-random number
|
||||
randomNumber, err := types.GenerateSecureRandomNumber()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
// Print random number, timestamp, and hash to user's console
|
||||
fmt.Printf("\nRandom number: %s\n", hex.EncodeToString(randomNumber.Bytes()))
|
||||
fmt.Printf("Timestamp: %d\n", timestamp)
|
||||
fmt.Printf("Random number hash: %s\n\n", hex.EncodeToString(randomNumberHash))
|
||||
|
||||
coins, err := sdk.ParseCoins(args[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedIncome := args[5]
|
||||
|
||||
heightSpan, err := strconv.ParseInt(args[6], 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crossChain, err := strconv.ParseBool(args[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := types.NewMsgCreateAtomicSwap(
|
||||
from, to, recipientOtherChain, senderOtherChain, randomNumberHash,
|
||||
timestamp, coins, expectedIncome, heightSpan, crossChain,
|
||||
)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetCmdClaimAtomicSwap cli command for claiming an atomic swap
|
||||
func GetCmdClaimAtomicSwap(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "claim [swap-id] [random-number]",
|
||||
Short: "claim coins in an atomic swap using the secret number",
|
||||
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(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||
|
||||
from := cliCtx.GetFromAddress()
|
||||
|
||||
swapID, err := types.HexToBytes(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(strings.TrimSpace(args[1])) == 0 {
|
||||
return fmt.Errorf("random-number cannot be empty")
|
||||
}
|
||||
|
||||
randomNumber, err := types.HexToBytes(args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := types.NewMsgClaimAtomicSwap(from, swapID, randomNumber)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetCmdRefundAtomicSwap cli command for claiming an atomic swap
|
||||
func GetCmdRefundAtomicSwap(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "refund [swap-id]",
|
||||
Short: "refund the coins in an atomic swap",
|
||||
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(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||
|
||||
from := cliCtx.GetFromAddress()
|
||||
|
||||
swapID, err := types.HexToBytes(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := types.NewMsgRefundAtomicSwap(from, swapID)
|
||||
|
||||
err = msg.ValidateBasic()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||
},
|
||||
}
|
||||
}
|
163
x/bep3/client/rest/query.go
Normal file
163
x/bep3/client/rest/query.go
Normal file
@ -0,0 +1,163 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
const restSwapID = "swap-id"
|
||||
const restDenom = "denom"
|
||||
|
||||
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||
r.HandleFunc(fmt.Sprintf("/%s/swap/{%s}", types.ModuleName, restSwapID), queryAtomicSwapHandlerFn(cliCtx)).Methods("GET")
|
||||
r.HandleFunc(fmt.Sprintf("/%s/swaps", types.ModuleName), queryAtomicSwapsHandlerFn(cliCtx)).Methods("GET")
|
||||
r.HandleFunc(fmt.Sprintf("/%s/supply/{%s}", types.ModuleName, restDenom), queryAssetSupplyHandlerFn(cliCtx)).Methods("GET")
|
||||
r.HandleFunc(fmt.Sprintf("/%s/parameters", types.ModuleName), queryParamsHandlerFn(cliCtx)).Methods("GET")
|
||||
|
||||
}
|
||||
|
||||
func queryAtomicSwapHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the query height
|
||||
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare params for querier
|
||||
vars := mux.Vars(r)
|
||||
if len(vars[restSwapID]) == 0 {
|
||||
err := fmt.Errorf("%s required but not specified", restSwapID)
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
swapID, err := types.HexToBytes(vars[restSwapID])
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
bz, err := cliCtx.Codec.MarshalJSON(types.QueryAtomicSwapByID{SwapID: swapID})
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Query
|
||||
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("/custom/%s/%s", types.ModuleName, types.QueryGetAtomicSwap), bz)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Decode and return results
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
|
||||
var swap types.AtomicSwap
|
||||
err = cliCtx.Codec.UnmarshalJSON(res, &swap)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(swap))
|
||||
}
|
||||
}
|
||||
|
||||
func queryAtomicSwapsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the query height
|
||||
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
route := fmt.Sprintf("custom/%s/%s", types.ModuleName, types.QueryGetAtomicSwaps)
|
||||
|
||||
res, height, err := cliCtx.QueryWithData(route, nil)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Decode and return results
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
|
||||
var swaps types.AtomicSwaps
|
||||
err = cliCtx.Codec.UnmarshalJSON(res, &swaps)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// using empty slice so json returns [] instead of null when there's no swaps
|
||||
sliceSwaps := types.AtomicSwaps{}
|
||||
for _, s := range swaps {
|
||||
sliceSwaps = append(sliceSwaps, s)
|
||||
}
|
||||
rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(sliceSwaps))
|
||||
}
|
||||
}
|
||||
|
||||
func queryAssetSupplyHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Parse the query height
|
||||
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Prepare params for querier
|
||||
vars := mux.Vars(r)
|
||||
denom := []byte(vars[restDenom])
|
||||
params := types.NewQueryAssetSupply(denom)
|
||||
|
||||
bz, err := cliCtx.Codec.MarshalJSON(params)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Query
|
||||
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("/custom/%s/%s", types.ModuleName, types.QueryGetAssetSupply), bz)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Decode and return results
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
|
||||
var assetSupply types.AssetSupply
|
||||
err = cliCtx.Codec.UnmarshalJSON(res, &assetSupply)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
rest.PostProcessResponse(w, cliCtx, cliCtx.Codec.MustMarshalJSON(assetSupply))
|
||||
}
|
||||
}
|
||||
|
||||
func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryGetParams)
|
||||
|
||||
res, height, err := cliCtx.QueryWithData(route, nil)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
rest.PostProcessResponse(w, cliCtx, res)
|
||||
}
|
||||
}
|
47
x/bep3/client/rest/rest.go
Normal file
47
x/bep3/client/rest/rest.go
Normal file
@ -0,0 +1,47 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// RegisterRoutes registers bep3-related REST handlers to a router
|
||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||
registerQueryRoutes(cliCtx, r)
|
||||
registerTxRoutes(cliCtx, r)
|
||||
}
|
||||
|
||||
// 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 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 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 tmbytes.HexBytes `json:"swap_id" yaml:"swap_id"`
|
||||
}
|
110
x/bep3/client/rest/tx.go
Normal file
110
x/bep3/client/rest/tx.go
Normal file
@ -0,0 +1,110 @@
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||
r.HandleFunc(fmt.Sprintf("/%s/swap/create", types.ModuleName), postCreateHandlerFn(cliCtx)).Methods("POST")
|
||||
r.HandleFunc(fmt.Sprintf("/%s/swap/claim", types.ModuleName), postClaimHandlerFn(cliCtx)).Methods("POST")
|
||||
r.HandleFunc(fmt.Sprintf("/%s/swap/refund", types.ModuleName), postRefundHandlerFn(cliCtx)).Methods("POST")
|
||||
}
|
||||
|
||||
func postCreateHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Decode PUT request body
|
||||
var req PostCreateSwapReq
|
||||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
|
||||
return
|
||||
}
|
||||
req.BaseReq = req.BaseReq.Sanitize()
|
||||
if !req.BaseReq.ValidateBasic(w) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create and return msg
|
||||
msg := types.NewMsgCreateAtomicSwap(
|
||||
req.From,
|
||||
req.To,
|
||||
req.RecipientOtherChain,
|
||||
req.SenderOtherChain,
|
||||
req.RandomNumberHash,
|
||||
req.Timestamp,
|
||||
req.Amount,
|
||||
req.ExpectedIncome,
|
||||
req.HeightSpan,
|
||||
req.CrossChain,
|
||||
)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
|
||||
}
|
||||
}
|
||||
|
||||
func postClaimHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Decode PUT request body
|
||||
var req PostClaimSwapReq
|
||||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
|
||||
return
|
||||
}
|
||||
req.BaseReq = req.BaseReq.Sanitize()
|
||||
if !req.BaseReq.ValidateBasic(w) {
|
||||
return
|
||||
}
|
||||
|
||||
// Create and return msg
|
||||
msg := types.NewMsgClaimAtomicSwap(
|
||||
req.From,
|
||||
req.SwapID,
|
||||
req.RandomNumber,
|
||||
)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
|
||||
}
|
||||
}
|
||||
|
||||
func postRefundHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Decode PUT request body
|
||||
var req PostRefundSwapReq
|
||||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
|
||||
return
|
||||
}
|
||||
req.BaseReq = req.BaseReq.Sanitize()
|
||||
if !req.BaseReq.ValidateBasic(w) {
|
||||
return
|
||||
}
|
||||
|
||||
senderAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
|
||||
if err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create and return msg
|
||||
msg := types.NewMsgRefundAtomicSwap(
|
||||
senderAddr,
|
||||
req.SwapID,
|
||||
)
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
|
||||
}
|
||||
}
|
122
x/bep3/genesis.go
Normal file
122
x/bep3/genesis.go
Normal file
@ -0,0 +1,122 @@
|
||||
package bep3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// InitGenesis initializes the store state from a genesis state.
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, supplyKeeper SupplyKeeper, gs GenesisState) {
|
||||
if err := gs.Validate(); err != nil {
|
||||
panic(fmt.Sprintf("failed to validate %s genesis state: %s", ModuleName, err))
|
||||
}
|
||||
|
||||
keeper.SetParams(ctx, gs.Params)
|
||||
|
||||
// Initialize supported assets
|
||||
for _, asset := range gs.Params.SupportedAssets {
|
||||
zeroCoin := sdk.NewCoin(asset.Denom, sdk.NewInt(0))
|
||||
supply := NewAssetSupply(asset.Denom, zeroCoin, zeroCoin, zeroCoin, sdk.NewCoin(asset.Denom, asset.Limit))
|
||||
keeper.SetAssetSupply(ctx, supply, []byte(asset.Denom))
|
||||
}
|
||||
|
||||
// Increment an asset's incoming, current, and outgoing supply
|
||||
// It it required that assets are supported but they do not have to be active
|
||||
for _, supply := range gs.AssetSupplies {
|
||||
// Asset must be supported but does not have to be active
|
||||
coin, found := keeper.GetAssetByDenom(ctx, supply.Denom)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("invalid asset supply: %s is not a supported asset", coin.Denom))
|
||||
}
|
||||
if !coin.Limit.Equal(supply.Limit.Amount) {
|
||||
panic(fmt.Sprintf("supported asset limit %s does not equal asset supply %s", coin.Limit, supply.Limit.Amount))
|
||||
}
|
||||
|
||||
// Increment current, incoming, and outgoing asset supplies
|
||||
err := keeper.IncrementCurrentAssetSupply(ctx, supply.CurrentSupply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = keeper.IncrementIncomingAssetSupply(ctx, supply.IncomingSupply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = keeper.IncrementOutgoingAssetSupply(ctx, supply.OutgoingSupply)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var incomingSupplies sdk.Coins
|
||||
var outgoingSupplies sdk.Coins
|
||||
for _, swap := range gs.AtomicSwaps {
|
||||
if swap.Validate() != nil {
|
||||
panic(fmt.Sprintf("invalid swap %s", swap.GetSwapID()))
|
||||
}
|
||||
|
||||
// Atomic swap assets must be both supported and active
|
||||
err := keeper.ValidateLiveAsset(ctx, swap.Amount[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
keeper.SetAtomicSwap(ctx, swap)
|
||||
|
||||
// Add swap to block index or longterm storage based on swap.Status
|
||||
// Increment incoming or outgoing supply based on swap.Direction
|
||||
switch swap.Direction {
|
||||
case Incoming:
|
||||
switch swap.Status {
|
||||
case Open:
|
||||
// This index expires unclaimed swaps
|
||||
keeper.InsertIntoByBlockIndex(ctx, swap)
|
||||
incomingSupplies = incomingSupplies.Add(swap.Amount...)
|
||||
case Expired:
|
||||
incomingSupplies = incomingSupplies.Add(swap.Amount...)
|
||||
case Completed:
|
||||
// This index stores swaps until deletion
|
||||
keeper.InsertIntoLongtermStorage(ctx, swap)
|
||||
default:
|
||||
panic(fmt.Sprintf("swap %s has invalid status %s", swap.GetSwapID(), swap.Status.String()))
|
||||
}
|
||||
case Outgoing:
|
||||
switch swap.Status {
|
||||
case Open:
|
||||
keeper.InsertIntoByBlockIndex(ctx, swap)
|
||||
outgoingSupplies = outgoingSupplies.Add(swap.Amount...)
|
||||
case Expired:
|
||||
outgoingSupplies = outgoingSupplies.Add(swap.Amount...)
|
||||
case Completed:
|
||||
keeper.InsertIntoLongtermStorage(ctx, swap)
|
||||
default:
|
||||
panic(fmt.Sprintf("swap %s has invalid status %s", swap.GetSwapID(), swap.Status.String()))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("swap %s has invalid direction %s", swap.GetSwapID(), swap.Direction.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// Asset's given incoming/outgoing supply much match the amount of coins in incoming/outgoing atomic swaps
|
||||
supplies := keeper.GetAllAssetSupplies(ctx)
|
||||
for _, supply := range supplies {
|
||||
incomingSupply := incomingSupplies.AmountOf(supply.Denom)
|
||||
if !supply.IncomingSupply.Amount.Equal(incomingSupply) {
|
||||
panic(fmt.Sprintf("asset's incoming supply %s does not match amount %s in incoming atomic swaps",
|
||||
supply.IncomingSupply, incomingSupply))
|
||||
}
|
||||
outgoingSupply := outgoingSupplies.AmountOf(supply.Denom)
|
||||
if !supply.OutgoingSupply.Amount.Equal(outgoingSupply) {
|
||||
panic(fmt.Sprintf("asset's outgoing supply %s does not match amount %s in outgoing atomic swaps",
|
||||
supply.OutgoingSupply, outgoingSupply))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExportGenesis writes the current store values to a genesis file, which can be imported again with InitGenesis
|
||||
func ExportGenesis(ctx sdk.Context, k Keeper) (data GenesisState) {
|
||||
params := k.GetParams(ctx)
|
||||
swaps := k.GetAllAtomicSwaps(ctx)
|
||||
assets := k.GetAllAssetSupplies(ctx)
|
||||
return NewGenesisState(params, swaps, assets)
|
||||
}
|
298
x/bep3/genesis_test.go
Normal file
298
x/bep3/genesis_test.go
Normal file
@ -0,0 +1,298 @@
|
||||
package bep3_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/bep3"
|
||||
)
|
||||
|
||||
type GenesisTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
keeper bep3.Keeper
|
||||
addrs []sdk.AccAddress
|
||||
}
|
||||
|
||||
func (suite *GenesisTestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
suite.ctx = tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
suite.keeper = tApp.GetBep3Keeper()
|
||||
suite.app = tApp
|
||||
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(3)
|
||||
suite.addrs = addrs
|
||||
}
|
||||
|
||||
func (suite *GenesisTestSuite) TestGenesisState() {
|
||||
|
||||
type GenState func() app.GenesisState
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
genState GenState
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
genState: func() app.GenesisState {
|
||||
return NewBep3GenStateMulti(suite.addrs[0])
|
||||
},
|
||||
expectPass: true,
|
||||
},
|
||||
{
|
||||
name: "import atomic swaps and asset supplies",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(3)
|
||||
var swaps bep3.AtomicSwaps
|
||||
var supplies bep3.AssetSupplies
|
||||
for i := 0; i < 3; i++ {
|
||||
swap, supply := loadSwapAndSupply(addrs[i], i)
|
||||
swaps = append(swaps, swap)
|
||||
supplies = append(supplies, supply)
|
||||
}
|
||||
gs.AtomicSwaps = swaps
|
||||
gs.AssetSupplies = supplies
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: true,
|
||||
},
|
||||
{
|
||||
name: "incoming supply doesn't match amount in incoming atomic swaps",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(1)
|
||||
swap, _ := loadSwapAndSupply(addrs[0], 2)
|
||||
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "current supply above limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
|
||||
gs.AssetSupplies = bep3.AssetSupplies{
|
||||
bep3.AssetSupply{
|
||||
Denom: "bnb",
|
||||
IncomingSupply: c("bnb", 0),
|
||||
OutgoingSupply: c("bnb", 0),
|
||||
CurrentSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()),
|
||||
Limit: c("bnb", assetParam.Limit.Int64()),
|
||||
},
|
||||
}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "incoming supply above limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
// Set up overlimit amount
|
||||
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
|
||||
overLimitAmount := assetParam.Limit.Add(i(1))
|
||||
|
||||
// Set up an atomic swap with amount equal to the currently asset supply
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
swap := bep3.NewAtomicSwap(cs(c("bnb", overLimitAmount.Int64())), randomNumberHash,
|
||||
int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain,
|
||||
TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming)
|
||||
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
|
||||
|
||||
// Set up asset supply with overlimit current supply
|
||||
gs.AssetSupplies = bep3.AssetSupplies{
|
||||
bep3.AssetSupply{
|
||||
Denom: "bnb",
|
||||
IncomingSupply: c("bnb", assetParam.Limit.Add(i(1)).Int64()),
|
||||
OutgoingSupply: c("bnb", 0),
|
||||
CurrentSupply: c("bnb", 0),
|
||||
Limit: c("bnb", assetParam.Limit.Int64()),
|
||||
},
|
||||
}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "incoming supply + current supply above limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
// Set up overlimit amount
|
||||
assetParam, _ := suite.keeper.GetAssetByDenom(suite.ctx, "bnb")
|
||||
halfLimit := assetParam.Limit.Int64() / 2
|
||||
overHalfLimit := halfLimit + 1
|
||||
|
||||
// Set up an atomic swap with amount equal to the currently asset supply
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
swap := bep3.NewAtomicSwap(cs(c("bnb", halfLimit)), randomNumberHash,
|
||||
int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain,
|
||||
TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming)
|
||||
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
|
||||
|
||||
// Set up asset supply with overlimit current supply
|
||||
gs.AssetSupplies = bep3.AssetSupplies{
|
||||
bep3.AssetSupply{
|
||||
Denom: "bnb",
|
||||
IncomingSupply: c("bnb", halfLimit),
|
||||
OutgoingSupply: c("bnb", 0),
|
||||
CurrentSupply: c("bnb", overHalfLimit),
|
||||
Limit: c("bnb", assetParam.Limit.Int64()),
|
||||
},
|
||||
}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "asset supply denom is not a supported asset",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.AssetSupplies = bep3.AssetSupplies{
|
||||
bep3.AssetSupply{
|
||||
Denom: "fake",
|
||||
IncomingSupply: c("fake", 0),
|
||||
OutgoingSupply: c("fake", 0),
|
||||
CurrentSupply: c("fake", 0),
|
||||
Limit: c("fake", StandardSupplyLimit.Int64()),
|
||||
},
|
||||
}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "atomic swap asset type is unsupported",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
swap := bep3.NewAtomicSwap(cs(c("fake", 500000)), randomNumberHash,
|
||||
int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain,
|
||||
TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming)
|
||||
|
||||
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "atomic swap status is invalid",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(2)
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
swap := bep3.NewAtomicSwap(cs(c("bnb", 5000)), randomNumberHash,
|
||||
int64(360), timestamp, suite.addrs[0], addrs[1], TestSenderOtherChain,
|
||||
TestRecipientOtherChain, 0, bep3.NULL, true, bep3.Incoming)
|
||||
|
||||
gs.AtomicSwaps = bep3.AtomicSwaps{swap}
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "minimum block lock below limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.MinBlockLock = 1
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "minimum block lock above limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.MinBlockLock = 500000
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "maximum block lock below limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.MaxBlockLock = 1
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "maximum block lock above limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.MaxBlockLock = 100000000
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "empty supported asset denom",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.SupportedAssets[0].Denom = ""
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "negative supported asset limit",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.SupportedAssets[0].Limit = i(-100)
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "duplicate supported asset denom",
|
||||
genState: func() app.GenesisState {
|
||||
gs := baseGenState(suite.addrs[0])
|
||||
gs.Params.SupportedAssets[1].Denom = "bnb"
|
||||
return app.GenesisState{"bep3": bep3.ModuleCdc.MustMarshalJSON(gs)}
|
||||
},
|
||||
expectPass: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
if tc.expectPass {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.NotPanics(func() {
|
||||
suite.app.InitializeFromGenesisStates(tc.genState())
|
||||
})
|
||||
})
|
||||
} else {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.Panics(func() {
|
||||
suite.app.InitializeFromGenesisStates(tc.genState())
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesisTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(GenesisTestSuite))
|
||||
}
|
87
x/bep3/handler.go
Normal file
87
x/bep3/handler.go
Normal file
@ -0,0 +1,87 @@
|
||||
package bep3
|
||||
|
||||
import (
|
||||
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, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
switch msg := msg.(type) {
|
||||
case MsgCreateAtomicSwap:
|
||||
return handleMsgCreateAtomicSwap(ctx, k, msg)
|
||||
case MsgClaimAtomicSwap:
|
||||
return handleMsgClaimAtomicSwap(ctx, k, msg)
|
||||
case MsgRefundAtomicSwap:
|
||||
return handleMsgRefundAtomicSwap(ctx, k, msg)
|
||||
default:
|
||||
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, 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 nil, err
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()),
|
||||
),
|
||||
)
|
||||
|
||||
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, error) {
|
||||
|
||||
err := k.ClaimAtomicSwap(ctx, msg.From, msg.SwapID, msg.RandomNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()),
|
||||
),
|
||||
)
|
||||
|
||||
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, error) {
|
||||
|
||||
err := k.RefundAtomicSwap(ctx, msg.From, msg.SwapID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()),
|
||||
),
|
||||
)
|
||||
|
||||
return &sdk.Result{
|
||||
Events: ctx.EventManager().Events(),
|
||||
}, nil
|
||||
}
|
134
x/bep3/handler_test.go
Normal file
134
x/bep3/handler_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
package bep3_test
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
type HandlerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx sdk.Context
|
||||
app app.TestApp
|
||||
handler sdk.Handler
|
||||
keeper bep3.Keeper
|
||||
addrs []sdk.AccAddress
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
keeper := tApp.GetBep3Keeper()
|
||||
|
||||
// Set up genesis state and initialize
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(3)
|
||||
coins := []sdk.Coins{}
|
||||
for j := 0; j < 3; j++ {
|
||||
coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
|
||||
}
|
||||
authGS := app.NewAuthGenState(addrs, coins)
|
||||
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(addrs[0]))
|
||||
|
||||
suite.addrs = addrs
|
||||
suite.handler = bep3.NewHandler(keeper)
|
||||
suite.keeper = keeper
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) AddAtomicSwap() (tmbytes.HexBytes, tmbytes.HexBytes) {
|
||||
expireHeight := int64(360)
|
||||
amount := cs(c("bnb", int64(50000)))
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
// Create atomic swap and check err to confirm creation
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight,
|
||||
suite.addrs[0], suite.addrs[1], TestSenderOtherChain, TestRecipientOtherChain,
|
||||
amount, amount.String(), true)
|
||||
suite.Nil(err)
|
||||
|
||||
swapID := bep3.CalculateSwapID(randomNumberHash, suite.addrs[0], TestSenderOtherChain)
|
||||
return swapID, randomNumber.Bytes()
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) TestMsgCreateAtomicSwap() {
|
||||
amount := cs(c("bnb", int64(10000)))
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
msg := bep3.NewMsgCreateAtomicSwap(
|
||||
suite.addrs[0], suite.addrs[2], TestRecipientOtherChain, TestSenderOtherChain,
|
||||
randomNumberHash, timestamp, amount, amount.String(), int64(300), true)
|
||||
|
||||
res, err := suite.handler(suite.ctx, msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) TestMsgClaimAtomicSwap() {
|
||||
// Attempt claim msg on fake atomic swap
|
||||
badRandomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
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, 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, err := suite.handler(suite.ctx, msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res)
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) TestMsgRefundAtomicSwap() {
|
||||
// Attempt refund msg on fake atomic swap
|
||||
badRandomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
badRandomNumberHash := bep3.CalculateRandomHash(badRandomNumber.Bytes(), ts(0))
|
||||
badSwapID := bep3.CalculateSwapID(badRandomNumberHash, suite.addrs[0], TestSenderOtherChain)
|
||||
badMsg := bep3.NewMsgRefundAtomicSwap(suite.addrs[0], 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, 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, err := suite.handler(laterCtx, msg)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().NotNil(res2)
|
||||
}
|
||||
|
||||
func (suite *HandlerTestSuite) TestInvalidMsg() {
|
||||
res, err := suite.handler(suite.ctx, sdk.NewTestMsg())
|
||||
suite.Require().Error(err)
|
||||
suite.Require().Nil(res)
|
||||
}
|
||||
|
||||
func TestHandlerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(HandlerTestSuite))
|
||||
}
|
87
x/bep3/integration_test.go
Normal file
87
x/bep3/integration_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package bep3_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/bep3"
|
||||
)
|
||||
|
||||
const (
|
||||
TestSenderOtherChain = "bnb1uky3me9ggqypmrsvxk7ur6hqkzq7zmv4ed4ng7"
|
||||
TestRecipientOtherChain = "bnb1urfermcg92dwq36572cx4xg84wpk3lfpksr5g7"
|
||||
TestDeputy = "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj"
|
||||
TestUser = "kava1vry5lhegzlulehuutcr7nmdlmktw88awp0a39p"
|
||||
)
|
||||
|
||||
var (
|
||||
StandardSupplyLimit = i(100000000000)
|
||||
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
|
||||
)
|
||||
|
||||
func i(in int64) sdk.Int { return sdk.NewInt(in) }
|
||||
func d(de int64) sdk.Dec { return sdk.NewDec(de) }
|
||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
|
||||
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
||||
func ts(minOffset int) int64 { return tmtime.Now().Add(time.Duration(minOffset) * time.Minute).Unix() }
|
||||
|
||||
func NewBep3GenStateMulti(deputy sdk.AccAddress) app.GenesisState {
|
||||
bep3Genesis := baseGenState(deputy)
|
||||
return app.GenesisState{bep3.ModuleName: bep3.ModuleCdc.MustMarshalJSON(bep3Genesis)}
|
||||
}
|
||||
|
||||
func baseGenState(deputy sdk.AccAddress) bep3.GenesisState {
|
||||
bep3Genesis := bep3.GenesisState{
|
||||
Params: bep3.Params{
|
||||
BnbDeputyAddress: deputy,
|
||||
MinBlockLock: bep3.DefaultMinBlockLock, // 80
|
||||
MaxBlockLock: bep3.DefaultMaxBlockLock, // 360
|
||||
SupportedAssets: bep3.AssetParams{
|
||||
bep3.AssetParam{
|
||||
Denom: "btc",
|
||||
CoinID: 714,
|
||||
Limit: StandardSupplyLimit,
|
||||
Active: true,
|
||||
},
|
||||
bep3.AssetParam{
|
||||
Denom: "eth",
|
||||
CoinID: 999999,
|
||||
Limit: StandardSupplyLimit,
|
||||
Active: true,
|
||||
},
|
||||
bep3.AssetParam{
|
||||
Denom: "bnb",
|
||||
CoinID: 99999,
|
||||
Limit: StandardSupplyLimit,
|
||||
Active: true,
|
||||
},
|
||||
bep3.AssetParam{
|
||||
Denom: "inc",
|
||||
CoinID: 9999,
|
||||
Limit: i(100),
|
||||
Active: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return bep3Genesis
|
||||
}
|
||||
|
||||
func loadSwapAndSupply(addr sdk.AccAddress, index int) (bep3.AtomicSwap, bep3.AssetSupply) {
|
||||
coin := c(DenomMap[index], 50000)
|
||||
expireOffset := int64((index * 15) + 360) // Default expire height + offet to match timestamp
|
||||
timestamp := ts(index) // One minute apart
|
||||
randomNumber, _ := bep3.GenerateSecureRandomNumber()
|
||||
randomNumberHash := bep3.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
swap := bep3.NewAtomicSwap(cs(coin), randomNumberHash,
|
||||
expireOffset, timestamp, addr, addr, TestSenderOtherChain,
|
||||
TestRecipientOtherChain, 0, bep3.Open, true, bep3.Incoming)
|
||||
|
||||
supply := bep3.NewAssetSupply(coin.Denom, coin, c(coin.Denom, 0),
|
||||
c(coin.Denom, 0), c(coin.Denom, StandardSupplyLimit.Int64()))
|
||||
|
||||
return swap, supply
|
||||
}
|
115
x/bep3/keeper/asset.go
Normal file
115
x/bep3/keeper/asset.go
Normal file
@ -0,0 +1,115 @@
|
||||
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) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, supply.CurrentSupply, supply.Limit)
|
||||
}
|
||||
|
||||
supply.CurrentSupply = supply.CurrentSupply.Add(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecrementCurrentAssetSupply decrement an asset's supply by the coin
|
||||
func (k Keeper) DecrementCurrentAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrInvalidCurrentSupply, "decrease %s, asset supply %s", coin, supply.CurrentSupply)
|
||||
}
|
||||
|
||||
supply.CurrentSupply = supply.CurrentSupply.Sub(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementIncomingAssetSupply increments an asset's incoming supply
|
||||
func (k Keeper) IncrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrExceedsSupplyLimit, "increase %s, asset supply %s, limit %s", coin, totalSupply, supply.Limit)
|
||||
}
|
||||
|
||||
supply.IncomingSupply = supply.IncomingSupply.Add(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecrementIncomingAssetSupply decrements an asset's incoming supply
|
||||
func (k Keeper) DecrementIncomingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrInvalidIncomingSupply, "decrease %s, incoming supply %s", coin, supply.IncomingSupply)
|
||||
}
|
||||
|
||||
supply.IncomingSupply = supply.IncomingSupply.Sub(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncrementOutgoingAssetSupply increments an asset's outoing supply
|
||||
func (k Keeper) IncrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrExceedsAvailableSupply, "swap amount %s, available supply %s", coin,
|
||||
supply.CurrentSupply.Amount.Sub(supply.OutgoingSupply.Amount))
|
||||
}
|
||||
|
||||
supply.OutgoingSupply = supply.OutgoingSupply.Add(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecrementOutgoingAssetSupply decrements an asset's outoing supply
|
||||
func (k Keeper) DecrementOutgoingAssetSupply(ctx sdk.Context, coin sdk.Coin) error {
|
||||
supply, found := k.GetAssetSupply(ctx, []byte(coin.Denom))
|
||||
if !found {
|
||||
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 sdkerrors.Wrapf(types.ErrInvalidOutgoingSupply, "decrease %s, outgoing supply %s", coin, supply.OutgoingSupply)
|
||||
}
|
||||
|
||||
supply.OutgoingSupply = supply.OutgoingSupply.Sub(coin)
|
||||
k.SetAssetSupply(ctx, supply, []byte(coin.Denom))
|
||||
return nil
|
||||
}
|
415
x/bep3/keeper/asset_test.go
Normal file
415
x/bep3/keeper/asset_test.go
Normal file
@ -0,0 +1,415 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
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"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
type AssetTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper keeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) SetupTest() {
|
||||
config := sdk.GetConfig()
|
||||
app.SetBech32AddressPrefixes(config)
|
||||
|
||||
// Initialize test app and set context
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
|
||||
// Initialize genesis state
|
||||
deputy, _ := sdk.AccAddressFromBech32(TestDeputy)
|
||||
tApp.InitializeFromGenesisStates(NewBep3GenStateMulti(deputy))
|
||||
|
||||
keeper := tApp.GetBep3Keeper()
|
||||
|
||||
// Set asset supply with standard value for testing
|
||||
supply := types.AssetSupply{
|
||||
Denom: "bnb",
|
||||
IncomingSupply: c("bnb", 5),
|
||||
OutgoingSupply: c("bnb", 5),
|
||||
CurrentSupply: c("bnb", 40),
|
||||
Limit: c("bnb", 50),
|
||||
}
|
||||
keeper.SetAssetSupply(ctx, supply, []byte(supply.Denom))
|
||||
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.keeper = keeper
|
||||
return
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestIncrementCurrentAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 5),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"equal limit",
|
||||
args{
|
||||
coin: c("bnb", 10),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"exceeds limit",
|
||||
args{
|
||||
coin: c("bnb", 11),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 5),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.Equal(preSupply.CurrentSupply.Add(tc.args.coin), postSupply.CurrentSupply)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.CurrentSupply, postSupply.CurrentSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestDecrementCurrentAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 30),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"equal current",
|
||||
args{
|
||||
coin: c("bnb", 40),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"exceeds current",
|
||||
args{
|
||||
coin: c("bnb", 41),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 30),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.DecrementCurrentAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.True(preSupply.CurrentSupply.Sub(tc.args.coin).IsEqual(postSupply.CurrentSupply))
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.CurrentSupply, postSupply.CurrentSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestIncrementIncomingAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 2),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"incoming + current = limit",
|
||||
args{
|
||||
coin: c("bnb", 5),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"incoming + current > limit",
|
||||
args{
|
||||
coin: c("bnb", 6),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 2),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.IncrementIncomingAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.Equal(preSupply.IncomingSupply.Add(tc.args.coin), postSupply.IncomingSupply)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.IncomingSupply, postSupply.IncomingSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestDecrementIncomingAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 4),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"equal incoming",
|
||||
args{
|
||||
coin: c("bnb", 5),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"exceeds incoming",
|
||||
args{
|
||||
coin: c("bnb", 6),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 4),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.DecrementIncomingAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.True(preSupply.IncomingSupply.Sub(tc.args.coin).IsEqual(postSupply.IncomingSupply))
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.IncomingSupply, postSupply.IncomingSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestIncrementOutgoingAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 30),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"outgoing + amount = current",
|
||||
args{
|
||||
coin: c("bnb", 35),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"outoing + amount > current",
|
||||
args{
|
||||
coin: c("bnb", 36),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 30),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.IncrementOutgoingAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.Equal(preSupply.OutgoingSupply.Add(tc.args.coin), postSupply.OutgoingSupply)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.OutgoingSupply, postSupply.OutgoingSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestDecrementOutgoingAssetSupply() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 4),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"equal outgoing",
|
||||
args{
|
||||
coin: c("bnb", 5),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"exceeds outgoing",
|
||||
args{
|
||||
coin: c("bnb", 6),
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
args{
|
||||
coin: c("xyz", 4),
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
supplyKeyPrefix := []byte(tc.args.coin.Denom)
|
||||
|
||||
preSupply, found := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
err := suite.keeper.DecrementOutgoingAssetSupply(suite.ctx, tc.args.coin)
|
||||
postSupply, _ := suite.keeper.GetAssetSupply(suite.ctx, supplyKeyPrefix)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.True(found)
|
||||
suite.NoError(err)
|
||||
suite.True(preSupply.OutgoingSupply.Sub(tc.args.coin).IsEqual(postSupply.OutgoingSupply))
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(preSupply.OutgoingSupply, postSupply.OutgoingSupply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssetTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AssetTestSuite))
|
||||
}
|
94
x/bep3/keeper/integration_test.go
Normal file
94
x/bep3/keeper/integration_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"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/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
const (
|
||||
TestSenderOtherChain = "bnb1uky3me9ggqypmrsvxk7ur6hqkzq7zmv4ed4ng7"
|
||||
TestRecipientOtherChain = "bnb1urfermcg92dwq36572cx4xg84wpk3lfpksr5g7"
|
||||
TestDeputy = "kava1xy7hrjy9r0algz9w3gzm8u6mrpq97kwta747gj"
|
||||
)
|
||||
|
||||
var (
|
||||
StandardSupplyLimit = i(100000000000)
|
||||
DenomMap = map[int]string{0: "btc", 1: "eth", 2: "bnb", 3: "xrp", 4: "dai"}
|
||||
TestUser1 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1")))
|
||||
TestUser2 = sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser2")))
|
||||
)
|
||||
|
||||
func i(in int64) sdk.Int { return sdk.NewInt(in) }
|
||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
|
||||
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
||||
func ts(minOffset int) int64 { return tmtime.Now().Add(time.Duration(minOffset) * time.Minute).Unix() }
|
||||
|
||||
func NewBep3GenStateMulti(deputy sdk.AccAddress) app.GenesisState {
|
||||
bep3Genesis := types.GenesisState{
|
||||
Params: bep3.Params{
|
||||
BnbDeputyAddress: deputy,
|
||||
MinBlockLock: types.DefaultMinBlockLock, // 80
|
||||
MaxBlockLock: types.DefaultMaxBlockLock, // 360
|
||||
SupportedAssets: types.AssetParams{
|
||||
types.AssetParam{
|
||||
Denom: "bnb",
|
||||
CoinID: 714,
|
||||
Limit: StandardSupplyLimit,
|
||||
Active: true,
|
||||
},
|
||||
types.AssetParam{
|
||||
Denom: "inc",
|
||||
CoinID: 9999,
|
||||
Limit: i(100),
|
||||
Active: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return app.GenesisState{bep3.ModuleName: bep3.ModuleCdc.MustMarshalJSON(bep3Genesis)}
|
||||
}
|
||||
|
||||
func atomicSwaps(ctx sdk.Context, count int) types.AtomicSwaps {
|
||||
var swaps types.AtomicSwaps
|
||||
for i := 0; i < count; i++ {
|
||||
swap := atomicSwap(ctx, i)
|
||||
swaps = append(swaps, swap)
|
||||
}
|
||||
return swaps
|
||||
}
|
||||
|
||||
func atomicSwap(ctx sdk.Context, index int) types.AtomicSwap {
|
||||
expireOffset := int64((index * 15) + 360) // Default expire height + offet to match timestamp
|
||||
timestamp := ts(index) // One minute apart
|
||||
randomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
return types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash,
|
||||
ctx.BlockHeight()+expireOffset, timestamp, TestUser1, TestUser2,
|
||||
TestSenderOtherChain, TestRecipientOtherChain, 0, types.Open, true,
|
||||
types.Incoming)
|
||||
}
|
||||
|
||||
func assetSupplies(count int) types.AssetSupplies {
|
||||
if count > 5 { // Max 5 asset supplies
|
||||
return types.AssetSupplies{}
|
||||
}
|
||||
|
||||
var supplies types.AssetSupplies
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
supply := assetSupply(DenomMap[i])
|
||||
supplies = append(supplies, supply)
|
||||
}
|
||||
return supplies
|
||||
}
|
||||
|
||||
func assetSupply(denom string) types.AssetSupply {
|
||||
return types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 0), c(denom, 10000))
|
||||
}
|
226
x/bep3/keeper/keeper.go
Normal file
226
x/bep3/keeper/keeper.go
Normal file
@ -0,0 +1,226 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
)
|
||||
|
||||
// Keeper of the bep3 store
|
||||
type Keeper struct {
|
||||
key sdk.StoreKey
|
||||
cdc *codec.Codec
|
||||
paramSubspace subspace.Subspace
|
||||
supplyKeeper types.SupplyKeeper
|
||||
}
|
||||
|
||||
// NewKeeper creates a bep3 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,
|
||||
supplyKeeper: sk,
|
||||
}
|
||||
return keeper
|
||||
}
|
||||
|
||||
// Logger returns a module-specific logger.
|
||||
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
||||
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Atomic Swaps
|
||||
// ------------------------------------------
|
||||
|
||||
// SetAtomicSwap puts the AtomicSwap into the store, and updates any indexes.
|
||||
func (k Keeper) SetAtomicSwap(ctx sdk.Context, atomicSwap types.AtomicSwap) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryLengthPrefixed(atomicSwap)
|
||||
store.Set(atomicSwap.GetSwapID(), bz)
|
||||
}
|
||||
|
||||
// GetAtomicSwap gets an AtomicSwap from the store.
|
||||
func (k Keeper) GetAtomicSwap(ctx sdk.Context, swapID []byte) (types.AtomicSwap, bool) {
|
||||
var atomicSwap types.AtomicSwap
|
||||
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapKeyPrefix)
|
||||
bz := store.Get(swapID)
|
||||
if bz == nil {
|
||||
return atomicSwap, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &atomicSwap)
|
||||
return atomicSwap, true
|
||||
}
|
||||
|
||||
// RemoveAtomicSwap removes an AtomicSwap from the AtomicSwapKeyPrefix.
|
||||
func (k Keeper) RemoveAtomicSwap(ctx sdk.Context, swapID []byte) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapKeyPrefix)
|
||||
store.Delete(swapID)
|
||||
}
|
||||
|
||||
// IterateAtomicSwaps provides an iterator over all stored AtomicSwaps.
|
||||
// For each AtomicSwap, cb will be called. If cb returns true, the iterator will close and stop.
|
||||
func (k Keeper) IterateAtomicSwaps(ctx sdk.Context, cb func(atomicSwap types.AtomicSwap) (stop bool)) {
|
||||
iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.key), types.AtomicSwapKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var atomicSwap types.AtomicSwap
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &atomicSwap)
|
||||
|
||||
if cb(atomicSwap) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllAtomicSwaps returns all AtomicSwaps from the store
|
||||
func (k Keeper) GetAllAtomicSwaps(ctx sdk.Context) (atomicSwaps types.AtomicSwaps) {
|
||||
k.IterateAtomicSwaps(ctx, func(atomicSwap types.AtomicSwap) bool {
|
||||
atomicSwaps = append(atomicSwaps, atomicSwap)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Atomic Swap Block Index
|
||||
// ------------------------------------------
|
||||
|
||||
// InsertIntoByBlockIndex adds a swap ID and expiration time into the byBlock index.
|
||||
func (k Keeper) InsertIntoByBlockIndex(ctx sdk.Context, atomicSwap types.AtomicSwap) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapByBlockPrefix)
|
||||
store.Set(types.GetAtomicSwapByHeightKey(atomicSwap.ExpireHeight, atomicSwap.GetSwapID()), atomicSwap.GetSwapID())
|
||||
}
|
||||
|
||||
// RemoveFromByBlockIndex removes an AtomicSwap from the byBlock index.
|
||||
func (k Keeper) RemoveFromByBlockIndex(ctx sdk.Context, atomicSwap types.AtomicSwap) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapByBlockPrefix)
|
||||
store.Delete(types.GetAtomicSwapByHeightKey(atomicSwap.ExpireHeight, atomicSwap.GetSwapID()))
|
||||
}
|
||||
|
||||
// IterateAtomicSwapsByBlock provides an iterator over AtomicSwaps ordered by AtomicSwap expiration block
|
||||
// For each AtomicSwap cb will be called. If cb returns true the iterator will close and stop.
|
||||
func (k Keeper) IterateAtomicSwapsByBlock(ctx sdk.Context, inclusiveCutoffTime uint64, cb func(swapID []byte) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapByBlockPrefix)
|
||||
iterator := store.Iterator(
|
||||
nil, // start at the very start of the prefix store
|
||||
sdk.PrefixEndBytes(types.Uint64ToBytes(inclusiveCutoffTime)), // end of range
|
||||
)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
id := iterator.Value()
|
||||
|
||||
if cb(id) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Atomic Swap Longterm Storage Index
|
||||
// ------------------------------------------
|
||||
|
||||
// InsertIntoLongtermStorage adds a swap ID and deletion time into the longterm storage index.
|
||||
// Completed swaps are stored for 1 week.
|
||||
func (k Keeper) InsertIntoLongtermStorage(ctx sdk.Context, atomicSwap types.AtomicSwap) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix)
|
||||
store.Set(types.GetAtomicSwapByHeightKey(atomicSwap.ClosedBlock+types.DefaultLongtermStorageDuration,
|
||||
atomicSwap.GetSwapID()), atomicSwap.GetSwapID())
|
||||
}
|
||||
|
||||
// RemoveFromLongtermStorage removes a swap from the into the longterm storage index
|
||||
func (k Keeper) RemoveFromLongtermStorage(ctx sdk.Context, atomicSwap types.AtomicSwap) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix)
|
||||
store.Delete(types.GetAtomicSwapByHeightKey(atomicSwap.ClosedBlock+types.DefaultLongtermStorageDuration,
|
||||
atomicSwap.GetSwapID()))
|
||||
}
|
||||
|
||||
// IterateAtomicSwapsLongtermStorage provides an iterator over AtomicSwaps ordered by deletion height.
|
||||
// For each AtomicSwap cb will be called. If cb returns true the iterator will close and stop.
|
||||
func (k Keeper) IterateAtomicSwapsLongtermStorage(ctx sdk.Context, inclusiveCutoffTime uint64,
|
||||
cb func(swapID []byte) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AtomicSwapLongtermStoragePrefix)
|
||||
iterator := store.Iterator(
|
||||
nil, // start at the very start of the prefix store
|
||||
sdk.PrefixEndBytes(types.Uint64ToBytes(inclusiveCutoffTime)), // end of range
|
||||
)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
id := iterator.Value()
|
||||
|
||||
if cb(id) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Asset Supplies
|
||||
// ------------------------------------------
|
||||
|
||||
// GetAssetSupply gets an asset's current supply from the store.
|
||||
func (k Keeper) GetAssetSupply(ctx sdk.Context, denom []byte) (types.AssetSupply, bool) {
|
||||
var supply types.AssetSupply
|
||||
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
|
||||
bz := store.Get(denom)
|
||||
if bz == nil {
|
||||
return types.AssetSupply{}, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &supply)
|
||||
return supply, true
|
||||
}
|
||||
|
||||
// SetAssetSupply updates an asset's current active supply
|
||||
func (k Keeper) SetAssetSupply(ctx sdk.Context, supply types.AssetSupply, denom []byte) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryLengthPrefixed(supply)
|
||||
store.Set(denom, bz)
|
||||
}
|
||||
|
||||
// IterateAssetSupplies provides an iterator over current asset supplies.
|
||||
// For each asset supply, cb will be called. If cb returns true, the iterator will close and stop.
|
||||
func (k Keeper) IterateAssetSupplies(ctx sdk.Context, cb func(supply types.AssetSupply) (stop bool)) {
|
||||
iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.key), types.AssetSupplyKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var supply types.AssetSupply
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &supply)
|
||||
|
||||
if cb(supply) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllAssetSupplies returns current asset supplies from the store as an array of sdk.Coin
|
||||
func (k Keeper) GetAllAssetSupplies(ctx sdk.Context) (supplies types.AssetSupplies) {
|
||||
k.IterateAssetSupplies(ctx, func(supply types.AssetSupply) bool {
|
||||
supplies = append(supplies, supply)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
370
x/bep3/keeper/keeper_test.go
Normal file
370
x/bep3/keeper/keeper_test.go
Normal file
@ -0,0 +1,370 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"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"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
const LongtermStorageDuration = 86400
|
||||
|
||||
type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper keeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupTest() {
|
||||
config := sdk.GetConfig()
|
||||
app.SetBech32AddressPrefixes(config)
|
||||
suite.ResetChain()
|
||||
return
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) ResetChain() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
keeper := tApp.GetBep3Keeper()
|
||||
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.keeper = keeper
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetAtomicSwap() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set new atomic swap
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
suite.keeper.SetAtomicSwap(suite.ctx, atomicSwap)
|
||||
|
||||
// Check atomic swap in store
|
||||
s, found := suite.keeper.GetAtomicSwap(suite.ctx, atomicSwap.GetSwapID())
|
||||
suite.True(found)
|
||||
suite.Equal(atomicSwap, s)
|
||||
|
||||
// Check fake atomic swap not in store
|
||||
fakeSwapID := types.CalculateSwapID(atomicSwap.RandomNumberHash, TestUser2, "otheraddress")
|
||||
_, found = suite.keeper.GetAtomicSwap(suite.ctx, fakeSwapID)
|
||||
suite.False(found)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestRemoveAtomicSwap() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set new atomic swap
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
suite.keeper.SetAtomicSwap(suite.ctx, atomicSwap)
|
||||
|
||||
// Check atomic swap in store
|
||||
s, found := suite.keeper.GetAtomicSwap(suite.ctx, atomicSwap.GetSwapID())
|
||||
suite.True(found)
|
||||
suite.Equal(atomicSwap, s)
|
||||
|
||||
suite.keeper.RemoveAtomicSwap(suite.ctx, atomicSwap.GetSwapID())
|
||||
|
||||
// Check atomic swap not in store
|
||||
_, found = suite.keeper.GetAtomicSwap(suite.ctx, atomicSwap.GetSwapID())
|
||||
suite.False(found)
|
||||
}
|
||||
func (suite *KeeperTestSuite) TestIterateAtomicSwaps() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set atomic swaps
|
||||
atomicSwaps := atomicSwaps(suite.ctx, 4)
|
||||
for _, s := range atomicSwaps {
|
||||
suite.keeper.SetAtomicSwap(suite.ctx, s)
|
||||
}
|
||||
|
||||
// Read each atomic swap from the store
|
||||
var readAtomicSwaps types.AtomicSwaps
|
||||
suite.keeper.IterateAtomicSwaps(suite.ctx, func(a types.AtomicSwap) bool {
|
||||
readAtomicSwaps = append(readAtomicSwaps, a)
|
||||
return false
|
||||
})
|
||||
|
||||
// Check expected values
|
||||
suite.Equal(len(atomicSwaps), len(readAtomicSwaps))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetAllAtomicSwaps() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set atomic swaps
|
||||
atomicSwaps := atomicSwaps(suite.ctx, 4)
|
||||
for _, s := range atomicSwaps {
|
||||
suite.keeper.SetAtomicSwap(suite.ctx, s)
|
||||
}
|
||||
|
||||
// Get and check atomic swaps
|
||||
res := suite.keeper.GetAllAtomicSwaps(suite.ctx)
|
||||
suite.Equal(4, len(res))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestInsertIntoByBlockIndex() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set new atomic swap in by block index
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
suite.keeper.InsertIntoByBlockIndex(suite.ctx, atomicSwap)
|
||||
|
||||
// Block index lacks getter methods, must use iteration to get count of swaps in store
|
||||
var swapIDs [][]byte
|
||||
suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool {
|
||||
swapIDs = append(swapIDs, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDs), 1)
|
||||
|
||||
// Marshal the expected swapID
|
||||
cdc := suite.app.Codec()
|
||||
res, _ := cdc.MarshalBinaryBare(atomicSwap.GetSwapID())
|
||||
expectedSwapID := res[1:]
|
||||
|
||||
suite.Equal(expectedSwapID, swapIDs[0])
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestRemoveFromByBlockIndex() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set new atomic swap in by block index
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
suite.keeper.InsertIntoByBlockIndex(suite.ctx, atomicSwap)
|
||||
|
||||
// Check stored data in block index
|
||||
var swapIDsPre [][]byte
|
||||
suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool {
|
||||
swapIDsPre = append(swapIDsPre, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDsPre), 1)
|
||||
|
||||
suite.keeper.RemoveFromByBlockIndex(suite.ctx, atomicSwap)
|
||||
|
||||
// Check stored data not in block index
|
||||
var swapIDsPost [][]byte
|
||||
suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(atomicSwap.ExpireHeight+1), func(id []byte) bool {
|
||||
swapIDsPost = append(swapIDsPost, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDsPost), 0)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestIterateAtomicSwapsByBlock() {
|
||||
suite.ResetChain()
|
||||
|
||||
type args struct {
|
||||
blockCtx sdk.Context
|
||||
swap types.AtomicSwap
|
||||
}
|
||||
|
||||
var testCases []args
|
||||
for i := 0; i < 8; i++ {
|
||||
// Set up context 100 blocks apart
|
||||
blockCtx := suite.ctx.WithBlockHeight(int64(i) * 100)
|
||||
|
||||
// Initialize a new atomic swap (different randomNumberHash = different swap IDs)
|
||||
timestamp := tmtime.Now().Add(time.Duration(i) * time.Minute).Unix()
|
||||
randomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
atomicSwap := types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash,
|
||||
blockCtx.BlockHeight(), timestamp, TestUser1, TestUser2,
|
||||
TestSenderOtherChain, TestRecipientOtherChain, 0, types.Open,
|
||||
true, types.Incoming)
|
||||
|
||||
// Insert into block index
|
||||
suite.keeper.InsertIntoByBlockIndex(blockCtx, atomicSwap)
|
||||
// Add to local block index
|
||||
testCases = append(testCases, args{blockCtx, atomicSwap})
|
||||
}
|
||||
|
||||
// Set up the expected swap IDs for a given cutoff block
|
||||
cutoffBlock := int64(450)
|
||||
var expectedSwapIDs [][]byte
|
||||
for _, tc := range testCases {
|
||||
if tc.blockCtx.BlockHeight() < cutoffBlock || tc.blockCtx.BlockHeight() == cutoffBlock {
|
||||
expectedSwapIDs = append(expectedSwapIDs, tc.swap.GetSwapID())
|
||||
}
|
||||
}
|
||||
|
||||
// Read the swap IDs from store for a given cutoff block
|
||||
var readSwapIDs [][]byte
|
||||
suite.keeper.IterateAtomicSwapsByBlock(suite.ctx, uint64(cutoffBlock), func(id []byte) bool {
|
||||
readSwapIDs = append(readSwapIDs, id)
|
||||
return false
|
||||
})
|
||||
|
||||
suite.Equal(expectedSwapIDs, readSwapIDs)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestInsertIntoLongtermStorage() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set atomic swap in longterm storage
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
atomicSwap.ClosedBlock = suite.ctx.BlockHeight()
|
||||
suite.keeper.InsertIntoLongtermStorage(suite.ctx, atomicSwap)
|
||||
|
||||
// Longterm storage lacks getter methods, must use iteration to get count of swaps in store
|
||||
var swapIDs [][]byte
|
||||
suite.keeper.IterateAtomicSwapsLongtermStorage(suite.ctx, uint64(atomicSwap.ClosedBlock+LongtermStorageDuration), func(id []byte) bool {
|
||||
swapIDs = append(swapIDs, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDs), 1)
|
||||
|
||||
// Marshal the expected swapID
|
||||
cdc := suite.app.Codec()
|
||||
res, _ := cdc.MarshalBinaryBare(atomicSwap.GetSwapID())
|
||||
expectedSwapID := res[1:]
|
||||
|
||||
suite.Equal(expectedSwapID, swapIDs[0])
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestRemoveFromLongtermStorage() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set atomic swap in longterm storage
|
||||
atomicSwap := atomicSwap(suite.ctx, 1)
|
||||
atomicSwap.ClosedBlock = suite.ctx.BlockHeight()
|
||||
suite.keeper.InsertIntoLongtermStorage(suite.ctx, atomicSwap)
|
||||
|
||||
// Longterm storage lacks getter methods, must use iteration to get count of swaps in store
|
||||
var swapIDs [][]byte
|
||||
suite.keeper.IterateAtomicSwapsLongtermStorage(suite.ctx, uint64(atomicSwap.ClosedBlock+LongtermStorageDuration), func(id []byte) bool {
|
||||
swapIDs = append(swapIDs, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDs), 1)
|
||||
|
||||
suite.keeper.RemoveFromLongtermStorage(suite.ctx, atomicSwap)
|
||||
|
||||
// Check stored data not in block index
|
||||
var swapIDsPost [][]byte
|
||||
suite.keeper.IterateAtomicSwapsLongtermStorage(suite.ctx, uint64(atomicSwap.ClosedBlock+LongtermStorageDuration), func(id []byte) bool {
|
||||
swapIDsPost = append(swapIDsPost, id)
|
||||
return false
|
||||
})
|
||||
suite.Equal(len(swapIDsPost), 0)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestIterateAtomicSwapsLongtermStorage() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set up atomic swaps with stagged closed blocks
|
||||
var swaps types.AtomicSwaps
|
||||
for i := 0; i < 8; i++ {
|
||||
timestamp := tmtime.Now().Unix()
|
||||
randomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
atomicSwap := types.NewAtomicSwap(cs(c("bnb", 50000)), randomNumberHash,
|
||||
suite.ctx.BlockHeight(), timestamp, TestUser1, TestUser2,
|
||||
TestSenderOtherChain, TestRecipientOtherChain, 100, types.Open,
|
||||
true, types.Incoming)
|
||||
|
||||
// Set closed block staggered by 100 blocks and insert into longterm storage
|
||||
atomicSwap.ClosedBlock = int64(i) * 100
|
||||
suite.keeper.InsertIntoLongtermStorage(suite.ctx, atomicSwap)
|
||||
// Add to local longterm storage
|
||||
swaps = append(swaps, atomicSwap)
|
||||
}
|
||||
|
||||
// Set up the expected swap IDs for a given cutoff block.
|
||||
cutoffBlock := int64(LongtermStorageDuration + 350)
|
||||
var expectedSwapIDs [][]byte
|
||||
for _, swap := range swaps {
|
||||
if swap.ClosedBlock+LongtermStorageDuration < cutoffBlock ||
|
||||
swap.ClosedBlock+LongtermStorageDuration == cutoffBlock {
|
||||
expectedSwapIDs = append(expectedSwapIDs, swap.GetSwapID())
|
||||
}
|
||||
}
|
||||
|
||||
// Read the swap IDs from store for a given cutoff block
|
||||
var readSwapIDs [][]byte
|
||||
suite.keeper.IterateAtomicSwapsLongtermStorage(suite.ctx, uint64(cutoffBlock), func(id []byte) bool {
|
||||
readSwapIDs = append(readSwapIDs, id)
|
||||
return false
|
||||
})
|
||||
|
||||
// At the cutoff block, iteration should return half of the swap IDs
|
||||
suite.Equal(len(swaps)/2, len(expectedSwapIDs))
|
||||
suite.Equal(len(swaps)/2, len(readSwapIDs))
|
||||
// Should be the same IDs
|
||||
suite.Equal(expectedSwapIDs, readSwapIDs)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetAssetSupply() {
|
||||
suite.ResetChain()
|
||||
|
||||
denom := "bnb"
|
||||
// Put asset supply in store
|
||||
assetSupply := types.NewAssetSupply(denom, c(denom, 0), c(denom, 0), c(denom, 50000), c(denom, 100000))
|
||||
suite.keeper.SetAssetSupply(suite.ctx, assetSupply, []byte(denom))
|
||||
|
||||
// Check asset in store
|
||||
storedAssetSupply, found := suite.keeper.GetAssetSupply(suite.ctx, []byte(denom))
|
||||
suite.True(found)
|
||||
suite.Equal(assetSupply, storedAssetSupply)
|
||||
|
||||
// Check fake asset supply not in store
|
||||
fakeDenom := "xyz"
|
||||
_, found = suite.keeper.GetAssetSupply(suite.ctx, []byte(fakeDenom))
|
||||
suite.False(found)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestIterateAssetSupplies() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set asset supplies
|
||||
supplies := assetSupplies(5)
|
||||
for _, supply := range supplies {
|
||||
suite.keeper.SetAssetSupply(suite.ctx, supply, []byte(supply.Denom))
|
||||
}
|
||||
|
||||
// Read each asset supply from the store
|
||||
var readSupplies types.AssetSupplies
|
||||
suite.keeper.IterateAssetSupplies(suite.ctx, func(a types.AssetSupply) bool {
|
||||
readSupplies = append(readSupplies, a)
|
||||
return false
|
||||
})
|
||||
|
||||
// Check expected values
|
||||
for i := 0; i < len(supplies); i++ {
|
||||
suite.Contains(readSupplies, supplies[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetAllAssetSupplies() {
|
||||
suite.ResetChain()
|
||||
|
||||
// Set asset supplies
|
||||
count := 3
|
||||
supplies := assetSupplies(count)
|
||||
for _, supply := range supplies {
|
||||
suite.keeper.SetAssetSupply(suite.ctx, supply, []byte(supply.Denom))
|
||||
}
|
||||
|
||||
// Get all asset supplies
|
||||
readSupplies := suite.keeper.GetAllAssetSupplies(suite.ctx)
|
||||
suite.Equal(count, len(readSupplies))
|
||||
|
||||
// Check expected values
|
||||
for i := 0; i < count; i++ {
|
||||
suite.Contains(readSupplies, supplies[i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
77
x/bep3/keeper/params.go
Normal file
77
x/bep3/keeper/params.go
Normal file
@ -0,0 +1,77 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// GetParams returns the total set of bep3 parameters.
|
||||
func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
|
||||
k.paramSubspace.GetParamSet(ctx, ¶ms)
|
||||
return params
|
||||
}
|
||||
|
||||
// SetParams sets the bep3 parameters to the param space.
|
||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||
}
|
||||
|
||||
// GetBnbDeputyAddress returns the Bnbchain's deputy address
|
||||
func (k Keeper) GetBnbDeputyAddress(ctx sdk.Context) sdk.AccAddress {
|
||||
params := k.GetParams(ctx)
|
||||
return params.BnbDeputyAddress
|
||||
}
|
||||
|
||||
// GetMaxBlockLock returns the maximum block lock
|
||||
func (k Keeper) GetMaxBlockLock(ctx sdk.Context) int64 {
|
||||
params := k.GetParams(ctx)
|
||||
return params.MaxBlockLock
|
||||
}
|
||||
|
||||
// GetMinBlockLock returns the minimum block lock
|
||||
func (k Keeper) GetMinBlockLock(ctx sdk.Context) int64 {
|
||||
params := k.GetParams(ctx)
|
||||
return params.MinBlockLock
|
||||
}
|
||||
|
||||
// GetAssets returns a list containing all supported assets
|
||||
func (k Keeper) GetAssets(ctx sdk.Context) (types.AssetParams, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
return params.SupportedAssets, len(params.SupportedAssets) > 0
|
||||
}
|
||||
|
||||
// GetAssetByDenom returns an asset by its denom
|
||||
func (k Keeper) GetAssetByDenom(ctx sdk.Context, denom string) (types.AssetParam, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, asset := range params.SupportedAssets {
|
||||
if asset.Denom == denom {
|
||||
return asset, true
|
||||
}
|
||||
}
|
||||
return types.AssetParam{}, false
|
||||
}
|
||||
|
||||
// GetAssetByCoinID returns an asset by its denom
|
||||
func (k Keeper) GetAssetByCoinID(ctx sdk.Context, coinID int) (types.AssetParam, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, asset := range params.SupportedAssets {
|
||||
if asset.CoinID == coinID {
|
||||
return asset, true
|
||||
}
|
||||
}
|
||||
return types.AssetParam{}, false
|
||||
}
|
||||
|
||||
// ValidateLiveAsset checks if an asset is both supported and active
|
||||
func (k Keeper) ValidateLiveAsset(ctx sdk.Context, coin sdk.Coin) error {
|
||||
asset, found := k.GetAssetByDenom(ctx, coin.Denom)
|
||||
if !found {
|
||||
return sdkerrors.Wrap(types.ErrAssetNotSupported, coin.Denom)
|
||||
}
|
||||
if !asset.Active {
|
||||
return sdkerrors.Wrap(types.ErrAssetNotActive, asset.Denom)
|
||||
}
|
||||
return nil
|
||||
}
|
142
x/bep3/keeper/params_test.go
Normal file
142
x/bep3/keeper/params_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
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"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
type ParamsTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper keeper.Keeper
|
||||
addrs []sdk.AccAddress
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(10)
|
||||
tApp.InitializeFromGenesisStates(NewBep3GenStateMulti(addrs[0]))
|
||||
suite.keeper = tApp.GetBep3Keeper()
|
||||
suite.ctx = ctx
|
||||
suite.addrs = addrs
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetSetBnbDeputyAddress() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
params.BnbDeputyAddress = suite.addrs[1]
|
||||
suite.NotPanics(func() { suite.keeper.SetParams(suite.ctx, params) })
|
||||
|
||||
params = suite.keeper.GetParams(suite.ctx)
|
||||
suite.Equal(suite.addrs[1], params.BnbDeputyAddress)
|
||||
addr := suite.keeper.GetBnbDeputyAddress(suite.ctx)
|
||||
suite.Equal(suite.addrs[1], addr)
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetMaxBlockLock() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
maxBlockLock := params.MaxBlockLock
|
||||
|
||||
res := suite.keeper.GetMaxBlockLock(suite.ctx)
|
||||
suite.Equal(maxBlockLock, res)
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetMinBlockLock() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
minBlockLock := params.MinBlockLock
|
||||
|
||||
res := suite.keeper.GetMinBlockLock(suite.ctx)
|
||||
suite.Equal(minBlockLock, res)
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetAssets() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
assets := params.SupportedAssets
|
||||
|
||||
res, found := suite.keeper.GetAssets(suite.ctx)
|
||||
suite.True(found)
|
||||
suite.Equal(assets, res)
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetAssetByDenom() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
asset := params.SupportedAssets[0]
|
||||
|
||||
res, found := suite.keeper.GetAssetByDenom(suite.ctx, asset.Denom)
|
||||
suite.True(found)
|
||||
suite.Equal(asset, res)
|
||||
}
|
||||
|
||||
func (suite *ParamsTestSuite) TestGetAssetByCoinID() {
|
||||
params := suite.keeper.GetParams(suite.ctx)
|
||||
asset := params.SupportedAssets[0]
|
||||
|
||||
res, found := suite.keeper.GetAssetByCoinID(suite.ctx, asset.CoinID)
|
||||
suite.True(found)
|
||||
suite.Equal(asset, res)
|
||||
}
|
||||
|
||||
func (suite *AssetTestSuite) TestValidateLiveAsset() {
|
||||
type args struct {
|
||||
coin sdk.Coin
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
expectedError error
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal",
|
||||
args{
|
||||
coin: c("bnb", 1),
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"asset not supported",
|
||||
args{
|
||||
coin: c("bad", 1),
|
||||
},
|
||||
types.ErrAssetNotSupported,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"asset not active",
|
||||
args{
|
||||
coin: c("inc", 1),
|
||||
},
|
||||
types.ErrAssetNotActive,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.SetupTest()
|
||||
suite.Run(tc.name, func() {
|
||||
err := suite.keeper.ValidateLiveAsset(suite.ctx, tc.args.coin)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.Require().NoError(err)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().True(errors.Is(err, tc.expectedError))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParamsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ParamsTestSuite))
|
||||
}
|
103
x/bep3/keeper/querier.go
Normal file
103
x/bep3/keeper/querier.go
Normal file
@ -0,0 +1,103 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 error) {
|
||||
switch path[0] {
|
||||
case types.QueryGetAssetSupply:
|
||||
return queryAssetSupply(ctx, req, keeper)
|
||||
case types.QueryGetAtomicSwap:
|
||||
return queryAtomicSwap(ctx, req, keeper)
|
||||
case types.QueryGetAtomicSwaps:
|
||||
return queryAtomicSwaps(ctx, req, keeper)
|
||||
case types.QueryGetParams:
|
||||
return queryGetParams(ctx, req, keeper)
|
||||
default:
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func queryAssetSupply(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
||||
// Decode request
|
||||
var requestParams types.QueryAssetSupply
|
||||
err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
|
||||
assetSupply, found := keeper.GetAssetSupply(ctx, []byte(requestParams.Denom))
|
||||
if !found {
|
||||
return nil, sdkerrors.Wrap(types.ErrAssetSupplyNotFound, string(requestParams.Denom))
|
||||
}
|
||||
|
||||
// Encode results
|
||||
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, assetSupply)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryAtomicSwap(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, error) {
|
||||
// Decode request
|
||||
var requestParams types.QueryAtomicSwapByID
|
||||
err := types.ModuleCdc.UnmarshalJSON(req.Data, &requestParams)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
|
||||
// Lookup atomic swap
|
||||
atomicSwap, found := keeper.GetAtomicSwap(ctx, requestParams.SwapID)
|
||||
if !found {
|
||||
return nil, sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%d", requestParams.SwapID)
|
||||
}
|
||||
|
||||
// Encode results
|
||||
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, atomicSwap)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
swaps = append(swaps, s)
|
||||
return false
|
||||
})
|
||||
|
||||
bz, err2 := codec.MarshalJSONIndent(types.ModuleCdc, swaps)
|
||||
if err2 != nil {
|
||||
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, error) {
|
||||
// Get params
|
||||
params := keeper.GetParams(ctx)
|
||||
|
||||
// Encode results
|
||||
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, params)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
return bz, nil
|
||||
}
|
170
x/bep3/keeper/querier_test.go
Normal file
170
x/bep3/keeper/querier_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
custom = "custom"
|
||||
)
|
||||
|
||||
type QuerierTestSuite struct {
|
||||
suite.Suite
|
||||
keeper keeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
querier sdk.Querier
|
||||
addrs []sdk.AccAddress
|
||||
isSupplyDenom map[string]bool
|
||||
swapIDs []tmbytes.HexBytes
|
||||
isSwapID map[string]bool
|
||||
}
|
||||
|
||||
func (suite *QuerierTestSuite) SetupTest() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
|
||||
// Set up auth GenesisState
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(10)
|
||||
coins := []sdk.Coins{}
|
||||
for j := 0; j < 10; j++ {
|
||||
coins = append(coins, cs(c("bnb", 10000000000), c("ukava", 10000000000)))
|
||||
}
|
||||
authGS := app.NewAuthGenState(addrs, coins)
|
||||
|
||||
tApp.InitializeFromGenesisStates(
|
||||
authGS,
|
||||
NewBep3GenStateMulti(addrs[0]),
|
||||
)
|
||||
|
||||
suite.ctx = ctx
|
||||
suite.app = tApp
|
||||
suite.keeper = tApp.GetBep3Keeper()
|
||||
suite.querier = keeper.NewQuerier(suite.keeper)
|
||||
suite.addrs = addrs
|
||||
|
||||
// Create atomic swaps and save IDs
|
||||
var swapIDs []tmbytes.HexBytes
|
||||
isSwapID := make(map[string]bool)
|
||||
for i := 0; i < 10; i++ {
|
||||
// Set up atomic swap variables
|
||||
expireHeight := int64(360)
|
||||
amount := cs(c("bnb", 100))
|
||||
timestamp := ts(0)
|
||||
randomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
// Create atomic swap and check err
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, randomNumberHash, timestamp, expireHeight,
|
||||
addrs[0], suite.addrs[i], TestSenderOtherChain, TestRecipientOtherChain, amount,
|
||||
amount.String(), true)
|
||||
suite.Nil(err)
|
||||
|
||||
// Calculate swap ID and save
|
||||
swapID := types.CalculateSwapID(randomNumberHash, addrs[0], TestSenderOtherChain)
|
||||
swapIDs = append(swapIDs, swapID)
|
||||
isSwapID[hex.EncodeToString(swapID)] = true
|
||||
}
|
||||
suite.swapIDs = swapIDs
|
||||
suite.isSwapID = isSwapID
|
||||
}
|
||||
|
||||
func (suite *QuerierTestSuite) TestQueryAssetSupply() {
|
||||
ctx := suite.ctx.WithIsCheckTx(false)
|
||||
|
||||
// Set up request query
|
||||
denom := "bnb"
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAssetSupply}, "/"),
|
||||
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAssetSupply(tmbytes.HexBytes(denom))),
|
||||
}
|
||||
|
||||
// Execute query and check the []byte result
|
||||
bz, err := suite.querier(ctx, []string{types.QueryGetAssetSupply}, query)
|
||||
suite.Nil(err)
|
||||
suite.NotNil(bz)
|
||||
|
||||
// Unmarshal the bytes into type asset supply
|
||||
var supply types.AssetSupply
|
||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &supply))
|
||||
|
||||
expectedSupply := types.NewAssetSupply(denom, c(denom, 1000),
|
||||
c(denom, 0), c(denom, 0), c(denom, StandardSupplyLimit.Int64()))
|
||||
suite.Equal(supply, expectedSupply)
|
||||
}
|
||||
|
||||
func (suite *QuerierTestSuite) TestQueryAtomicSwap() {
|
||||
ctx := suite.ctx.WithIsCheckTx(false)
|
||||
|
||||
// Set up request query
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAtomicSwap}, "/"),
|
||||
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAtomicSwapByID(suite.swapIDs[0])),
|
||||
}
|
||||
|
||||
// Execute query and check the []byte result
|
||||
bz, err := suite.querier(ctx, []string{types.QueryGetAtomicSwap}, query)
|
||||
suite.Nil(err)
|
||||
suite.NotNil(bz)
|
||||
|
||||
// Unmarshal the bytes into type atomic swap
|
||||
var swap types.AtomicSwap
|
||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &swap))
|
||||
|
||||
// Check the returned atomic swap's ID
|
||||
suite.True(suite.isSwapID[hex.EncodeToString(swap.GetSwapID())])
|
||||
}
|
||||
|
||||
func (suite *QuerierTestSuite) TestQueryAtomicSwaps() {
|
||||
ctx := suite.ctx.WithIsCheckTx(false)
|
||||
// Set up request query
|
||||
query := abci.RequestQuery{
|
||||
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryGetAtomicSwaps}, "/"),
|
||||
Data: types.ModuleCdc.MustMarshalJSON(types.NewQueryAtomicSwaps(1, 100)),
|
||||
}
|
||||
|
||||
bz, err := suite.querier(ctx, []string{types.QueryGetAtomicSwaps}, query)
|
||||
suite.Nil(err)
|
||||
suite.NotNil(bz)
|
||||
|
||||
var swaps types.AtomicSwaps
|
||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &swaps))
|
||||
|
||||
suite.Equal(len(suite.swapIDs), len(swaps))
|
||||
for _, swap := range swaps {
|
||||
suite.True(suite.isSwapID[hex.EncodeToString(swap.GetSwapID())])
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *QuerierTestSuite) TestQueryParams() {
|
||||
ctx := suite.ctx.WithIsCheckTx(false)
|
||||
bz, err := suite.querier(ctx, []string{types.QueryGetParams}, abci.RequestQuery{})
|
||||
suite.Nil(err)
|
||||
suite.NotNil(bz)
|
||||
|
||||
var p types.Params
|
||||
suite.Nil(types.ModuleCdc.UnmarshalJSON(bz, &p))
|
||||
|
||||
bep3GenesisState := NewBep3GenStateMulti(suite.addrs[0])
|
||||
gs := types.GenesisState{}
|
||||
types.ModuleCdc.UnmarshalJSON(bep3GenesisState["bep3"], &gs)
|
||||
suite.Equal(gs.Params, p)
|
||||
}
|
||||
|
||||
func TestQuerierTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(QuerierTestSuite))
|
||||
}
|
274
x/bep3/keeper/swap.go
Normal file
274
x/bep3/keeper/swap.go
Normal file
@ -0,0 +1,274 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"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) error {
|
||||
// Confirm that this is not a duplicate swap
|
||||
swapID := types.CalculateSwapID(randomNumberHash, sender, senderOtherChain)
|
||||
_, found := k.GetAtomicSwap(ctx, swapID)
|
||||
if found {
|
||||
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 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 sdkerrors.Wrap(types.ErrInvalidTimestamp, time.Unix(timestamp, 0).UTC().String())
|
||||
}
|
||||
|
||||
if len(amount) != 1 {
|
||||
return fmt.Errorf("amount must contain exactly one coin")
|
||||
}
|
||||
|
||||
err := k.ValidateLiveAsset(ctx, amount[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var direction types.SwapDirection
|
||||
deputy := k.GetBnbDeputyAddress(ctx)
|
||||
if sender.Equals(deputy) {
|
||||
direction = types.Incoming
|
||||
} else {
|
||||
direction = types.Outgoing
|
||||
}
|
||||
|
||||
switch direction {
|
||||
case types.Incoming:
|
||||
err = k.IncrementIncomingAssetSupply(ctx, amount[0])
|
||||
case types.Outgoing:
|
||||
err = k.IncrementOutgoingAssetSupply(ctx, amount[0])
|
||||
default:
|
||||
err = fmt.Errorf("invalid swap direction: %s", direction.String())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Transfer coins to module
|
||||
err = k.supplyKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the details of the swap
|
||||
atomicSwap := types.NewAtomicSwap(amount, randomNumberHash, ctx.BlockHeight()+heightSpan,
|
||||
timestamp, sender, recipient, senderOtherChain, recipientOtherChain, 0, types.Open,
|
||||
crossChain, direction)
|
||||
|
||||
k.SetAtomicSwap(ctx, atomicSwap)
|
||||
k.InsertIntoByBlockIndex(ctx, atomicSwap)
|
||||
|
||||
// Emit 'create_atomic_swap' event
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeCreateAtomicSwap,
|
||||
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, atomicSwap.SenderOtherChain),
|
||||
sdk.NewAttribute(types.AttributeKeyExpireHeight, fmt.Sprintf("%d", atomicSwap.ExpireHeight)),
|
||||
sdk.NewAttribute(types.AttributeKeyAmount, atomicSwap.Amount[0].String()),
|
||||
sdk.NewAttribute(types.AttributeKeyExpectedIncome, expectedIncome),
|
||||
sdk.NewAttribute(types.AttributeKeyDirection, atomicSwap.Direction.String()),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) error {
|
||||
atomicSwap, found := k.GetAtomicSwap(ctx, swapID)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%s", swapID)
|
||||
}
|
||||
|
||||
// Only open atomic swaps can be claimed
|
||||
if atomicSwap.Status != types.Open {
|
||||
return types.ErrSwapNotClaimable
|
||||
}
|
||||
|
||||
// Calculate hashed secret using submitted number
|
||||
hashedSubmittedNumber := types.CalculateRandomHash(randomNumber, atomicSwap.Timestamp)
|
||||
hashedSecret := types.CalculateSwapID(hashedSubmittedNumber, atomicSwap.Sender, atomicSwap.SenderOtherChain)
|
||||
|
||||
// Confirm that secret unlocks the atomic swap
|
||||
if !bytes.Equal(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 {
|
||||
break
|
||||
}
|
||||
err = k.IncrementCurrentAssetSupply(ctx, atomicSwap.Amount[0])
|
||||
case types.Outgoing:
|
||||
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
err = k.DecrementCurrentAssetSupply(ctx, atomicSwap.Amount[0])
|
||||
default:
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Complete swap
|
||||
atomicSwap.Status = types.Completed
|
||||
atomicSwap.ClosedBlock = ctx.BlockHeight()
|
||||
k.SetAtomicSwap(ctx, atomicSwap)
|
||||
|
||||
// Remove from byBlock index and transition to longterm storage
|
||||
k.RemoveFromByBlockIndex(ctx, atomicSwap)
|
||||
k.InsertIntoLongtermStorage(ctx, atomicSwap)
|
||||
|
||||
// Emit 'claim_atomic_swap' event
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeClaimAtomicSwap,
|
||||
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)),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) error {
|
||||
atomicSwap, found := k.GetAtomicSwap(ctx, swapID)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrAtomicSwapNotFound, "%s", swapID)
|
||||
}
|
||||
// Only expired swaps may be refunded
|
||||
if atomicSwap.Status != types.Expired {
|
||||
return types.ErrSwapNotRefundable
|
||||
}
|
||||
|
||||
var err error
|
||||
switch atomicSwap.Direction {
|
||||
case types.Incoming:
|
||||
err = k.DecrementIncomingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||
case types.Outgoing:
|
||||
err = k.DecrementOutgoingAssetSupply(ctx, atomicSwap.Amount[0])
|
||||
default:
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Complete swap
|
||||
atomicSwap.Status = types.Completed
|
||||
atomicSwap.ClosedBlock = ctx.BlockHeight()
|
||||
k.SetAtomicSwap(ctx, atomicSwap)
|
||||
|
||||
// Transition to longterm storage
|
||||
k.InsertIntoLongtermStorage(ctx, atomicSwap)
|
||||
|
||||
// Emit 'refund_atomic_swap' event
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeRefundAtomicSwap,
|
||||
sdk.NewAttribute(types.AttributeKeyRefundSender, fmt.Sprintf("%s", from)),
|
||||
sdk.NewAttribute(types.AttributeKeySender, fmt.Sprintf("%s", atomicSwap.Sender)),
|
||||
sdk.NewAttribute(types.AttributeKeyAtomicSwapID, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.GetSwapID()))),
|
||||
sdk.NewAttribute(types.AttributeKeyRandomNumberHash, fmt.Sprintf("%s", hex.EncodeToString(atomicSwap.RandomNumberHash))),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateExpiredAtomicSwaps finds all AtomicSwaps that are past (or at) their ending times and expires them.
|
||||
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)
|
||||
return false
|
||||
})
|
||||
|
||||
// Expire incomplete swaps (claimed swaps have already been removed from byBlock index)
|
||||
var expiredSwapIDs []string
|
||||
for _, id := range expiredSwaps {
|
||||
atomicSwap, _ := k.GetAtomicSwap(ctx, id)
|
||||
atomicSwap.Status = types.Expired
|
||||
k.SetAtomicSwap(ctx, atomicSwap)
|
||||
k.RemoveFromByBlockIndex(ctx, atomicSwap)
|
||||
expiredSwapIDs = append(expiredSwapIDs, hex.EncodeToString(atomicSwap.GetSwapID()))
|
||||
}
|
||||
|
||||
// Emit 'swaps_expired' event
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeSwapsExpired,
|
||||
sdk.NewAttribute(types.AttributeKeyAtomicSwapIDs, fmt.Sprintf("%s", expiredSwapIDs)),
|
||||
sdk.NewAttribute(types.AttributeExpirationBlock, fmt.Sprintf("%d", ctx.BlockHeight())),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteClosedAtomicSwapsFromLongtermStorage removes swaps one week after completion
|
||||
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)
|
||||
return false
|
||||
})
|
||||
|
||||
// Delete closed atomic swaps
|
||||
for _, id := range swapsToDelete {
|
||||
swap, _ := k.GetAtomicSwap(ctx, id)
|
||||
k.RemoveAtomicSwap(ctx, swap.GetSwapID())
|
||||
k.RemoveFromLongtermStorage(ctx, swap)
|
||||
}
|
||||
return nil
|
||||
}
|
699
x/bep3/keeper/swap_test.go
Normal file
699
x/bep3/keeper/swap_test.go
Normal file
@ -0,0 +1,699 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
)
|
||||
|
||||
type AtomicSwapTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper keeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
deputy sdk.AccAddress
|
||||
addrs []sdk.AccAddress
|
||||
timestamps []int64
|
||||
randomNumberHashes []tmbytes.HexBytes
|
||||
swapIDs []tmbytes.HexBytes
|
||||
randomNumbers []tmbytes.HexBytes
|
||||
}
|
||||
|
||||
const (
|
||||
STARING_BNB_BALANCE = int64(1000000000)
|
||||
BNB_DENOM = "bnb"
|
||||
)
|
||||
|
||||
func (suite *AtomicSwapTestSuite) SetupTest() {
|
||||
config := sdk.GetConfig()
|
||||
app.SetBech32AddressPrefixes(config)
|
||||
|
||||
// Initialize test app and set context
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
|
||||
// Create and load 20 accounts with bnb tokens
|
||||
coins := []sdk.Coins{}
|
||||
for i := 0; i < 20; i++ {
|
||||
coins = append(coins, cs(c(BNB_DENOM, STARING_BNB_BALANCE)))
|
||||
}
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(20)
|
||||
deputy := addrs[0]
|
||||
authGS := app.NewAuthGenState(addrs, coins)
|
||||
|
||||
// Initialize genesis state
|
||||
tApp.InitializeFromGenesisStates(authGS, NewBep3GenStateMulti(deputy))
|
||||
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.deputy = deputy
|
||||
suite.addrs = addrs
|
||||
suite.keeper = suite.app.GetBep3Keeper()
|
||||
suite.GenerateSwapDetails()
|
||||
}
|
||||
|
||||
func (suite *AtomicSwapTestSuite) GenerateSwapDetails() {
|
||||
var timestamps []int64
|
||||
var randomNumberHashes []tmbytes.HexBytes
|
||||
var randomNumbers []tmbytes.HexBytes
|
||||
for i := 0; i < 10; i++ {
|
||||
// Set up atomic swap details
|
||||
timestamp := ts(i)
|
||||
randomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||
|
||||
timestamps = append(timestamps, timestamp)
|
||||
randomNumberHashes = append(randomNumberHashes, randomNumberHash)
|
||||
randomNumbers = append(randomNumbers, randomNumber.Bytes())
|
||||
}
|
||||
suite.timestamps = timestamps
|
||||
suite.randomNumberHashes = randomNumberHashes
|
||||
suite.randomNumbers = randomNumbers
|
||||
}
|
||||
|
||||
func (suite *AtomicSwapTestSuite) TestCreateAtomicSwap() {
|
||||
currentTmTime := tmtime.Now()
|
||||
type args struct {
|
||||
randomNumberHash []byte
|
||||
timestamp int64
|
||||
heightSpan int64
|
||||
sender sdk.AccAddress
|
||||
recipient sdk.AccAddress
|
||||
senderOtherChain string
|
||||
recipientOtherChain string
|
||||
coins sdk.Coins
|
||||
expectedIncome string
|
||||
crossChain bool
|
||||
direction types.SwapDirection
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
blockTime time.Time
|
||||
args args
|
||||
expectPass bool
|
||||
shouldBeFound bool
|
||||
}{
|
||||
{
|
||||
"incoming swap",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[0],
|
||||
timestamp: suite.timestamps[0],
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[1],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"outgoing swap",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[0],
|
||||
timestamp: suite.timestamps[0],
|
||||
heightSpan: int64(360),
|
||||
sender: suite.addrs[1],
|
||||
recipient: suite.addrs[2],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Outgoing,
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"unsupported asset",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[1],
|
||||
timestamp: suite.timestamps[1],
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[2],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c("xyz", 50000)),
|
||||
expectedIncome: "50000xyz",
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"past timestamp",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[2],
|
||||
timestamp: suite.timestamps[2] - 2000,
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[3],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"future timestamp",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[3],
|
||||
timestamp: suite.timestamps[3] + 5000,
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[4],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"small height span",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[4],
|
||||
timestamp: suite.timestamps[4],
|
||||
heightSpan: int64(5),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[5],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"big height span",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[5],
|
||||
timestamp: suite.timestamps[5],
|
||||
heightSpan: int64(1000000),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[6],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: fmt.Sprintf("50000%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"zero amount",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[6],
|
||||
timestamp: suite.timestamps[6],
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[7],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 0)),
|
||||
expectedIncome: fmt.Sprintf("0%s", BNB_DENOM),
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"duplicate swap",
|
||||
currentTmTime,
|
||||
args{
|
||||
randomNumberHash: suite.randomNumberHashes[0],
|
||||
timestamp: suite.timestamps[0],
|
||||
heightSpan: int64(360),
|
||||
sender: suite.deputy,
|
||||
recipient: suite.addrs[1],
|
||||
senderOtherChain: TestSenderOtherChain,
|
||||
recipientOtherChain: TestRecipientOtherChain,
|
||||
coins: cs(c(BNB_DENOM, 50000)),
|
||||
expectedIncome: "50000bnb",
|
||||
crossChain: true,
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
// Increment current asset supply to support outgoing swaps
|
||||
if tc.args.direction == types.Outgoing {
|
||||
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, tc.args.coins[0])
|
||||
suite.Nil(err)
|
||||
}
|
||||
|
||||
// Load asset denom (required for zero coins test case)
|
||||
var swapAssetDenom string
|
||||
if len(tc.args.coins) == 1 {
|
||||
swapAssetDenom = tc.args.coins[0].Denom
|
||||
} else {
|
||||
swapAssetDenom = BNB_DENOM
|
||||
}
|
||||
|
||||
// Load sender's account prior to swap creation
|
||||
ak := suite.app.GetAccountKeeper()
|
||||
senderAccPre := ak.GetAccount(suite.ctx, tc.args.sender)
|
||||
senderBalancePre := senderAccPre.GetCoins().AmountOf(swapAssetDenom)
|
||||
assetSupplyPre, _ := suite.keeper.GetAssetSupply(suite.ctx, []byte(swapAssetDenom))
|
||||
|
||||
// Create atomic swap
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, tc.args.randomNumberHash, tc.args.timestamp,
|
||||
tc.args.heightSpan, tc.args.sender, tc.args.recipient, tc.args.senderOtherChain,
|
||||
tc.args.recipientOtherChain, tc.args.coins, tc.args.expectedIncome, tc.args.crossChain)
|
||||
|
||||
// Load sender's account after swap creation
|
||||
senderAccPost := ak.GetAccount(suite.ctx, tc.args.sender)
|
||||
senderBalancePost := senderAccPost.GetCoins().AmountOf(swapAssetDenom)
|
||||
assetSupplyPost, _ := suite.keeper.GetAssetSupply(suite.ctx, []byte(swapAssetDenom))
|
||||
|
||||
// Load expected swap ID
|
||||
expectedSwapID := types.CalculateSwapID(tc.args.randomNumberHash, tc.args.sender, tc.args.senderOtherChain)
|
||||
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
|
||||
// Check coins moved
|
||||
suite.Equal(senderBalancePre.Sub(tc.args.coins[0].Amount), senderBalancePost)
|
||||
|
||||
// Check incoming/outgoing asset supply increased
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply.Add(tc.args.coins[0]), assetSupplyPost.IncomingSupply)
|
||||
case types.Outgoing:
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply.Add(tc.args.coins[0]), assetSupplyPost.OutgoingSupply)
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
|
||||
// Check swap in store
|
||||
actualSwap, found := suite.keeper.GetAtomicSwap(suite.ctx, expectedSwapID)
|
||||
suite.True(found)
|
||||
suite.NotNil(actualSwap)
|
||||
|
||||
// Confirm swap contents
|
||||
expectedSwap :=
|
||||
types.AtomicSwap{
|
||||
Amount: tc.args.coins,
|
||||
RandomNumberHash: tc.args.randomNumberHash,
|
||||
ExpireHeight: suite.ctx.BlockHeight() + tc.args.heightSpan,
|
||||
Timestamp: tc.args.timestamp,
|
||||
Sender: tc.args.sender,
|
||||
Recipient: tc.args.recipient,
|
||||
SenderOtherChain: tc.args.senderOtherChain,
|
||||
RecipientOtherChain: tc.args.recipientOtherChain,
|
||||
ClosedBlock: 0,
|
||||
Status: types.Open,
|
||||
CrossChain: tc.args.crossChain,
|
||||
Direction: tc.args.direction,
|
||||
}
|
||||
suite.Equal(expectedSwap, actualSwap)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
// Check coins not moved
|
||||
suite.Equal(senderBalancePre, senderBalancePost)
|
||||
|
||||
// Check incoming/outgoing asset supply not increased
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
case types.Outgoing:
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
|
||||
// Check if swap found in store
|
||||
_, found := suite.keeper.GetAtomicSwap(suite.ctx, expectedSwapID)
|
||||
if !tc.shouldBeFound {
|
||||
suite.False(found)
|
||||
} else {
|
||||
suite.True(found)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AtomicSwapTestSuite) TestClaimAtomicSwap() {
|
||||
suite.SetupTest()
|
||||
invalidRandomNumber, _ := types.GenerateSecureRandomNumber()
|
||||
type args struct {
|
||||
swapID []byte
|
||||
randomNumber []byte
|
||||
direction types.SwapDirection
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
claimCtx sdk.Context
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal incoming swap",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: []byte{},
|
||||
randomNumber: []byte{},
|
||||
direction: types.Incoming,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"normal outgoing swap",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: []byte{},
|
||||
randomNumber: []byte{},
|
||||
direction: types.Outgoing,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid random number",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: []byte{},
|
||||
randomNumber: invalidRandomNumber.Bytes(),
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong swap ID",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: types.CalculateSwapID(suite.randomNumberHashes[3], suite.addrs[6], TestRecipientOtherChain),
|
||||
randomNumber: []byte{},
|
||||
direction: types.Outgoing,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"past expiration",
|
||||
suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 2000),
|
||||
args{
|
||||
swapID: []byte{},
|
||||
randomNumber: []byte{},
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
suite.GenerateSwapDetails()
|
||||
suite.Run(tc.name, func() {
|
||||
expectedRecipient := suite.addrs[5]
|
||||
expectedClaimAmount := cs(c(BNB_DENOM, 50000))
|
||||
sender := suite.deputy
|
||||
|
||||
// Set sender to other and increment current asset supply for outgoing swap
|
||||
if tc.args.direction == types.Outgoing {
|
||||
sender = suite.addrs[6]
|
||||
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, expectedClaimAmount[0])
|
||||
suite.Nil(err)
|
||||
}
|
||||
|
||||
// Create atomic swap
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i],
|
||||
int64(360), sender, expectedRecipient, TestSenderOtherChain, TestRecipientOtherChain,
|
||||
expectedClaimAmount, expectedClaimAmount.String(), true)
|
||||
suite.NoError(err)
|
||||
|
||||
realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain)
|
||||
|
||||
// If args contains an invalid swap ID claim attempt will use it instead of the real swap ID
|
||||
var claimSwapID []byte
|
||||
if len(tc.args.swapID) == 0 {
|
||||
claimSwapID = realSwapID
|
||||
} else {
|
||||
claimSwapID = tc.args.swapID
|
||||
}
|
||||
|
||||
// If args contains an invalid random number claim attempt will use it instead of the real random number
|
||||
var claimRandomNumber []byte
|
||||
if len(tc.args.randomNumber) == 0 {
|
||||
claimRandomNumber = suite.randomNumbers[i]
|
||||
} else {
|
||||
claimRandomNumber = tc.args.randomNumber
|
||||
}
|
||||
|
||||
// Run the beginblocker before attempting claim
|
||||
bep3.BeginBlocker(tc.claimCtx, suite.keeper)
|
||||
|
||||
// Load expected recipient's account prior to claim attempt
|
||||
ak := suite.app.GetAccountKeeper()
|
||||
expectedRecipientAccPre := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
||||
expectedRecipientBalancePre := expectedRecipientAccPre.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
|
||||
// Load asset supplies prior to claim attempt
|
||||
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.claimCtx, []byte(expectedClaimAmount[0].Denom))
|
||||
|
||||
// Attempt to claim atomic swap
|
||||
err = suite.keeper.ClaimAtomicSwap(tc.claimCtx, expectedRecipient, claimSwapID, claimRandomNumber)
|
||||
|
||||
// Load expected recipient's account after the claim attempt
|
||||
expectedRecipientAccPost := ak.GetAccount(tc.claimCtx, expectedRecipient)
|
||||
expectedRecipientBalancePost := expectedRecipientAccPost.GetCoins().AmountOf(expectedClaimAmount[0].Denom)
|
||||
// Load asset supplies after the claim attempt
|
||||
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.claimCtx, []byte(expectedClaimAmount[0].Denom))
|
||||
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
// Check coins moved
|
||||
suite.Equal(expectedRecipientBalancePre.Add(expectedClaimAmount[0].Amount), expectedRecipientBalancePost)
|
||||
|
||||
// Check asset supply changes
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
// Check incoming supply decreased
|
||||
suite.True(assetSupplyPre.IncomingSupply.Amount.Sub(expectedClaimAmount[0].Amount).Equal(assetSupplyPost.IncomingSupply.Amount))
|
||||
// Check current supply increased
|
||||
suite.Equal(assetSupplyPre.CurrentSupply.Add(expectedClaimAmount[0]), assetSupplyPost.CurrentSupply)
|
||||
// Check outgoing supply not changed
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
case types.Outgoing:
|
||||
// Check incoming supply not changed
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
// Check current supply decreased
|
||||
suite.Equal(assetSupplyPre.CurrentSupply.Sub(expectedClaimAmount[0]), assetSupplyPost.CurrentSupply)
|
||||
// Check outgoing supply decreased
|
||||
suite.True(assetSupplyPre.OutgoingSupply.Sub(expectedClaimAmount[0]).IsEqual(assetSupplyPost.OutgoingSupply))
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
} else {
|
||||
suite.Error(err)
|
||||
// Check coins not moved
|
||||
suite.Equal(expectedRecipientBalancePre, expectedRecipientBalancePost)
|
||||
|
||||
// Check asset supply has not changed
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
case types.Outgoing:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *AtomicSwapTestSuite) TestRefundAtomicSwap() {
|
||||
suite.SetupTest()
|
||||
|
||||
type args struct {
|
||||
swapID []byte
|
||||
direction types.SwapDirection
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
refundCtx sdk.Context
|
||||
args args
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"normal incoming swap",
|
||||
suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400),
|
||||
args{
|
||||
swapID: []byte{},
|
||||
direction: types.Incoming,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"normal outgoing swap",
|
||||
suite.ctx.WithBlockHeight(suite.ctx.BlockHeight() + 400),
|
||||
args{
|
||||
swapID: []byte{},
|
||||
direction: types.Outgoing,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"before expiration",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: []byte{},
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"wrong swapID",
|
||||
suite.ctx,
|
||||
args{
|
||||
swapID: types.CalculateSwapID(suite.randomNumberHashes[6], suite.addrs[1], TestRecipientOtherChain),
|
||||
direction: types.Incoming,
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
suite.GenerateSwapDetails()
|
||||
suite.Run(tc.name, func() {
|
||||
// Create atomic swap
|
||||
expectedRefundAmount := cs(c(BNB_DENOM, 50000))
|
||||
sender := suite.deputy
|
||||
|
||||
// Set sender to other and increment current asset supply for outgoing swap
|
||||
if tc.args.direction == types.Outgoing {
|
||||
sender = suite.addrs[6]
|
||||
err := suite.keeper.IncrementCurrentAssetSupply(suite.ctx, expectedRefundAmount[0])
|
||||
suite.Nil(err)
|
||||
}
|
||||
|
||||
err := suite.keeper.CreateAtomicSwap(suite.ctx, suite.randomNumberHashes[i], suite.timestamps[i],
|
||||
int64(360), sender, suite.addrs[8], TestSenderOtherChain, TestRecipientOtherChain,
|
||||
expectedRefundAmount, expectedRefundAmount.String(), true)
|
||||
suite.NoError(err)
|
||||
|
||||
realSwapID := types.CalculateSwapID(suite.randomNumberHashes[i], sender, TestSenderOtherChain)
|
||||
|
||||
// If args contains an invalid swap ID refund attempt will use it instead of the real swap ID
|
||||
var refundSwapID []byte
|
||||
if len(tc.args.swapID) == 0 {
|
||||
refundSwapID = realSwapID
|
||||
} else {
|
||||
refundSwapID = tc.args.swapID
|
||||
}
|
||||
|
||||
// Run the beginblocker before attempting refund
|
||||
bep3.BeginBlocker(tc.refundCtx, suite.keeper)
|
||||
|
||||
// Load sender's account prior to swap refund
|
||||
ak := suite.app.GetAccountKeeper()
|
||||
originalSenderAccPre := ak.GetAccount(tc.refundCtx, sender)
|
||||
originalSenderBalancePre := originalSenderAccPre.GetCoins().AmountOf(expectedRefundAmount[0].Denom)
|
||||
// Load asset supply prior to swap refund
|
||||
assetSupplyPre, _ := suite.keeper.GetAssetSupply(tc.refundCtx, []byte(expectedRefundAmount[0].Denom))
|
||||
|
||||
// Attempt to refund atomic swap
|
||||
err = suite.keeper.RefundAtomicSwap(tc.refundCtx, sender, refundSwapID)
|
||||
|
||||
// Load sender's account after refund
|
||||
originalSenderAccPost := ak.GetAccount(tc.refundCtx, sender)
|
||||
originalSenderBalancePost := originalSenderAccPost.GetCoins().AmountOf(expectedRefundAmount[0].Denom)
|
||||
// Load asset supply after to swap refund
|
||||
assetSupplyPost, _ := suite.keeper.GetAssetSupply(tc.refundCtx, []byte(expectedRefundAmount[0].Denom))
|
||||
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
// Check coins moved
|
||||
suite.Equal(originalSenderBalancePre.Add(expectedRefundAmount[0].Amount), originalSenderBalancePost)
|
||||
|
||||
// Check asset supply changes
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
// Check incoming supply decreased
|
||||
suite.True(assetSupplyPre.IncomingSupply.Sub(expectedRefundAmount[0]).IsEqual(assetSupplyPost.IncomingSupply))
|
||||
// Check current, outgoing supply not changed
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
case types.Outgoing:
|
||||
// Check incoming, current supply not changed
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
// Check outgoing supply decreased
|
||||
suite.True(assetSupplyPre.OutgoingSupply.Sub(expectedRefundAmount[0]).IsEqual(assetSupplyPost.OutgoingSupply))
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
} else {
|
||||
suite.Error(err)
|
||||
// Check coins not moved
|
||||
suite.Equal(originalSenderBalancePre, originalSenderBalancePost)
|
||||
|
||||
// Check asset supply has not changed
|
||||
switch tc.args.direction {
|
||||
case types.Incoming:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
case types.Outgoing:
|
||||
suite.Equal(assetSupplyPre.IncomingSupply, assetSupplyPost.IncomingSupply)
|
||||
suite.Equal(assetSupplyPre.CurrentSupply, assetSupplyPost.CurrentSupply)
|
||||
suite.Equal(assetSupplyPre.OutgoingSupply, assetSupplyPost.OutgoingSupply)
|
||||
default:
|
||||
suite.Fail("should not have invalid direction")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtomicSwapTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(AtomicSwapTestSuite))
|
||||
}
|
172
x/bep3/module.go
Normal file
172
x/bep3/module.go
Normal file
@ -0,0 +1,172 @@
|
||||
package bep3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/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"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/bep3/client/cli"
|
||||
"github.com/kava-labs/kava/x/bep3/client/rest"
|
||||
"github.com/kava-labs/kava/x/bep3/simulation"
|
||||
)
|
||||
|
||||
var (
|
||||
_ module.AppModule = AppModule{}
|
||||
_ module.AppModuleBasic = AppModuleBasic{}
|
||||
_ module.AppModuleSimulation = AppModule{}
|
||||
)
|
||||
|
||||
// AppModuleBasic defines the basic application module used by the bep3 module.
|
||||
type AppModuleBasic struct{}
|
||||
|
||||
// Name returns the bep3 module's name.
|
||||
func (AppModuleBasic) Name() string {
|
||||
return ModuleName
|
||||
}
|
||||
|
||||
// RegisterCodec registers the bep3 module's types for the given codec.
|
||||
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
|
||||
RegisterCodec(cdc)
|
||||
}
|
||||
|
||||
// DefaultGenesis returns default genesis state as raw bytes for the bep3
|
||||
// module.
|
||||
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
|
||||
return ModuleCdc.MustMarshalJSON(DefaultGenesisState())
|
||||
}
|
||||
|
||||
// ValidateGenesis performs genesis state validation for the bep3 module.
|
||||
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
|
||||
var gs GenesisState
|
||||
err := ModuleCdc.UnmarshalJSON(bz, &gs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return gs.Validate()
|
||||
}
|
||||
|
||||
// RegisterRESTRoutes registers the REST routes for the bep3 module.
|
||||
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
|
||||
rest.RegisterRoutes(ctx, rtr)
|
||||
}
|
||||
|
||||
// GetTxCmd returns the root tx command for the bep3 module.
|
||||
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||
return cli.GetTxCmd(cdc)
|
||||
}
|
||||
|
||||
// GetQueryCmd returns no root query command for the bep3 module.
|
||||
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
|
||||
return cli.GetQueryCmd(StoreKey, cdc)
|
||||
}
|
||||
|
||||
//____________________________________________________________________________
|
||||
|
||||
// AppModule implements the sdk.AppModule interface.
|
||||
type AppModule struct {
|
||||
AppModuleBasic
|
||||
|
||||
keeper Keeper
|
||||
accountKeeper auth.AccountKeeper
|
||||
supplyKeeper SupplyKeeper
|
||||
}
|
||||
|
||||
// NewAppModule creates a new AppModule object
|
||||
func NewAppModule(keeper Keeper, accountKeeper auth.AccountKeeper, supplyKeeper SupplyKeeper) AppModule {
|
||||
return AppModule{
|
||||
AppModuleBasic: AppModuleBasic{},
|
||||
keeper: keeper,
|
||||
accountKeeper: accountKeeper,
|
||||
supplyKeeper: supplyKeeper,
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the bep3 module's name.
|
||||
func (AppModule) Name() string {
|
||||
return ModuleName
|
||||
}
|
||||
|
||||
// RegisterInvariants registers the bep3 module invariants.
|
||||
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
|
||||
|
||||
// Route returns the message routing key for the bep3 module.
|
||||
func (AppModule) Route() string {
|
||||
return RouterKey
|
||||
}
|
||||
|
||||
// NewHandler returns an sdk.Handler for the bep3 module.
|
||||
func (am AppModule) NewHandler() sdk.Handler {
|
||||
return NewHandler(am.keeper)
|
||||
}
|
||||
|
||||
// QuerierRoute returns the bep3 module's querier route name.
|
||||
func (AppModule) QuerierRoute() string {
|
||||
return QuerierRoute
|
||||
}
|
||||
|
||||
// NewQuerierHandler returns the bep3 module sdk.Querier.
|
||||
func (am AppModule) NewQuerierHandler() sdk.Querier {
|
||||
return NewQuerier(am.keeper)
|
||||
}
|
||||
|
||||
// InitGenesis performs genesis initialization for the bep3 module. It returns
|
||||
// no validator updates.
|
||||
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
|
||||
var genesisState GenesisState
|
||||
ModuleCdc.MustUnmarshalJSON(data, &genesisState)
|
||||
InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState)
|
||||
return []abci.ValidatorUpdate{}
|
||||
}
|
||||
|
||||
// ExportGenesis returns the exported genesis state as raw bytes for the bep3
|
||||
// module.
|
||||
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
|
||||
gs := ExportGenesis(ctx, am.keeper)
|
||||
return ModuleCdc.MustMarshalJSON(gs)
|
||||
}
|
||||
|
||||
// BeginBlock returns the begin blocker for the bep3 module.
|
||||
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
|
||||
BeginBlocker(ctx, am.keeper)
|
||||
}
|
||||
|
||||
// EndBlock returns the end blocker for the bep3 module. It returns no validator updates.
|
||||
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)
|
||||
}
|
39
x/bep3/simulation/decoder.go
Normal file
39
x/bep3/simulation/decoder.go
Normal file
@ -0,0 +1,39 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/libs/kv"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
|
||||
"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 kv.Pair) string {
|
||||
switch {
|
||||
case bytes.Equal(kvA.Key[:1], types.AtomicSwapKeyPrefix):
|
||||
var swapA, swapB types.AtomicSwap
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &swapA)
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &swapB)
|
||||
return fmt.Sprintf("%v\n%v", swapA, swapB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.AssetSupplyKeyPrefix):
|
||||
var supplyA, supplyB types.AssetSupply
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvA.Value, &supplyA)
|
||||
cdc.MustUnmarshalBinaryLengthPrefixed(kvB.Value, &supplyB)
|
||||
return fmt.Sprintf("%s\n%s", supplyA, supplyB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.AtomicSwapByBlockPrefix),
|
||||
bytes.Equal(kvA.Key[:1], types.AtomicSwapLongtermStoragePrefix):
|
||||
var bytesA tmbytes.HexBytes = kvA.Value
|
||||
var bytesB tmbytes.HexBytes = kvA.Value
|
||||
return fmt.Sprintf("%s\n%s", bytesA.String(), bytesB.String())
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1]))
|
||||
}
|
||||
}
|
62
x/bep3/simulation/decoder_test.go
Normal file
62
x/bep3/simulation/decoder_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
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"
|
||||
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
func makeTestCodec() (cdc *codec.Codec) {
|
||||
cdc = codec.New()
|
||||
sdk.RegisterCodec(cdc)
|
||||
types.RegisterCodec(cdc)
|
||||
return
|
||||
}
|
||||
|
||||
func TestDecodeDistributionStore(t *testing.T) {
|
||||
cdc := makeTestCodec()
|
||||
|
||||
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 := tmbytes.HexBytes([]byte{1, 2})
|
||||
|
||||
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 {
|
||||
name string
|
||||
expectedLog string
|
||||
}{
|
||||
{"AtomicSwap", fmt.Sprintf("%v\n%v", swap, swap)},
|
||||
{"AssetSupply", fmt.Sprintf("%v\n%v", supply, supply)},
|
||||
{"AtomicSwapByBlock", fmt.Sprintf("%s\n%s", bz, bz)},
|
||||
{"AtomicSwapLongtermStorage", fmt.Sprintf("%s\n%s", bz, bz)},
|
||||
{"other", ""},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
i, tt := i, tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
switch i {
|
||||
case len(tests) - 1:
|
||||
require.Panics(t, func() { DecodeStore(cdc, kvPairs[i], kvPairs[i]) }, tt.name)
|
||||
default:
|
||||
require.Equal(t, tt.expectedLog, DecodeStore(cdc, kvPairs[i], kvPairs[i]), tt.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
168
x/bep3/simulation/genesis.go
Normal file
168
x/bep3/simulation/genesis.go
Normal file
@ -0,0 +1,168 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
// Simulation parameter constants
|
||||
const (
|
||||
BnbDeputyAddress = "bnb_deputy_address"
|
||||
MinBlockLock = "min_block_lock"
|
||||
MaxBlockLock = "max_block_lock"
|
||||
SupportedAssets = "supported_assets"
|
||||
)
|
||||
|
||||
var (
|
||||
MaxSupplyLimit = sdk.NewInt(1000000000000)
|
||||
accs []simulation.Account
|
||||
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"}
|
||||
)
|
||||
|
||||
// GenRandBnbDeputy randomized BnbDeputyAddress
|
||||
func GenRandBnbDeputy(r *rand.Rand) simulation.Account {
|
||||
acc, _ := simulation.RandomAcc(r, accs)
|
||||
return acc
|
||||
}
|
||||
|
||||
// GenMinBlockLock randomized MinBlockLock
|
||||
func GenMinBlockLock(r *rand.Rand) int64 {
|
||||
min := int(types.AbsoluteMinimumBlockLock)
|
||||
max := int(types.AbsoluteMaximumBlockLock)
|
||||
return int64(r.Intn(max-min) + min)
|
||||
}
|
||||
|
||||
// GenMaxBlockLock randomized MaxBlockLock
|
||||
func GenMaxBlockLock(r *rand.Rand, minBlockLock int64) int64 {
|
||||
min := int(minBlockLock)
|
||||
max := int(types.AbsoluteMaximumBlockLock)
|
||||
return int64(r.Intn(max-min) + min)
|
||||
}
|
||||
|
||||
// GenSupportedAssets gets randomized SupportedAssets
|
||||
func GenSupportedAssets(r *rand.Rand) types.AssetParams {
|
||||
var assets types.AssetParams
|
||||
numAssets := (r.Intn(10) + 1)
|
||||
for i := 0; i < numAssets; i++ {
|
||||
denom := strings.ToLower(simulation.RandStringOfLength(r, (r.Intn(3) + 3)))
|
||||
asset := genSupportedAsset(r, denom)
|
||||
assets = append(assets, asset)
|
||||
}
|
||||
// Add bnb, btc, or xrp as a supported asset for interactions with other modules
|
||||
stableAsset := genSupportedAsset(r, ConsistentDenoms[r.Intn(3)])
|
||||
assets = append(assets, stableAsset)
|
||||
|
||||
return assets
|
||||
}
|
||||
|
||||
func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam {
|
||||
coinID, _ := simulation.RandPositiveInt(r, sdk.NewInt(100000))
|
||||
limit, _ := simulation.RandPositiveInt(r, MaxSupplyLimit)
|
||||
return types.AssetParam{
|
||||
Denom: denom,
|
||||
CoinID: int(coinID.Int64()),
|
||||
Limit: limit,
|
||||
Active: true,
|
||||
}
|
||||
}
|
||||
|
||||
// RandomizedGenState generates a random GenesisState
|
||||
func RandomizedGenState(simState *module.SimulationState) {
|
||||
accs = simState.Accounts
|
||||
|
||||
bep3Genesis := loadRandomBep3GenState(simState)
|
||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, bep3Genesis))
|
||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(bep3Genesis)
|
||||
|
||||
authGenesis, totalCoins := loadAuthGenState(simState, bep3Genesis)
|
||||
simState.GenState[auth.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
|
||||
|
||||
// Update supply to match amount of coins in auth
|
||||
var supplyGenesis supply.GenesisState
|
||||
simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis)
|
||||
for _, deputyCoin := range totalCoins {
|
||||
supplyGenesis.Supply = supplyGenesis.Supply.Add(deputyCoin...)
|
||||
}
|
||||
simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis)
|
||||
}
|
||||
|
||||
func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState {
|
||||
bnbDeputy := GenRandBnbDeputy(simState.Rand)
|
||||
|
||||
// min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100
|
||||
minBlockLock := int64(types.AbsoluteMinimumBlockLock)
|
||||
maxBlockLock := minBlockLock * 2
|
||||
|
||||
var supportedAssets types.AssetParams
|
||||
simState.AppParams.GetOrGenerate(
|
||||
simState.Cdc, SupportedAssets, &supportedAssets, simState.Rand,
|
||||
func(r *rand.Rand) { supportedAssets = GenSupportedAssets(r) },
|
||||
)
|
||||
|
||||
bep3Genesis := types.GenesisState{
|
||||
Params: types.Params{
|
||||
BnbDeputyAddress: bnbDeputy.Address,
|
||||
MinBlockLock: minBlockLock,
|
||||
MaxBlockLock: maxBlockLock,
|
||||
SupportedAssets: supportedAssets,
|
||||
},
|
||||
}
|
||||
|
||||
return bep3Genesis
|
||||
}
|
||||
|
||||
func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.GenesisState) (
|
||||
auth.GenesisState, []sdk.Coins) {
|
||||
var authGenesis auth.GenesisState
|
||||
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis)
|
||||
|
||||
deputy, found := getAccount(authGenesis.Accounts, bep3Genesis.Params.BnbDeputyAddress)
|
||||
if !found {
|
||||
panic("deputy address not found in available accounts")
|
||||
}
|
||||
|
||||
// Load total limit of each supported asset to deputy's account
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
totalCoins = append(totalCoins, assetCoin)
|
||||
}
|
||||
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
|
||||
|
||||
return authGenesis, totalCoins
|
||||
}
|
||||
|
||||
// Return an account from a list of accounts that matches an address.
|
||||
func getAccount(accounts []authexported.GenesisAccount, addr sdk.AccAddress) (authexported.GenesisAccount, bool) {
|
||||
for _, a := range accounts {
|
||||
if a.GetAddress().Equals(addr) {
|
||||
return a, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// In a list of accounts, replace the first account found with the same address. If not found, append the account.
|
||||
func replaceOrAppendAccount(accounts []authexported.GenesisAccount, acc authexported.GenesisAccount) []authexported.GenesisAccount {
|
||||
newAccounts := accounts
|
||||
for i, a := range accounts {
|
||||
if a.GetAddress().Equals(acc.GetAddress()) {
|
||||
newAccounts[i] = acc
|
||||
return newAccounts
|
||||
}
|
||||
}
|
||||
return append(newAccounts, acc)
|
||||
}
|
210
x/bep3/simulation/operations.go
Normal file
210
x/bep3/simulation/operations.go
Normal 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
|
||||
}
|
||||
}
|
46
x/bep3/simulation/params.go
Normal file
46
x/bep3/simulation/params.go
Normal file
@ -0,0 +1,46 @@
|
||||
package simulation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
|
||||
"github.com/kava-labs/kava/x/bep3/types"
|
||||
)
|
||||
|
||||
const (
|
||||
keyBnbDeputyAddress = "BnbDeputyAddress"
|
||||
keyMinBlockLock = "MinBlockLock"
|
||||
keyMaxBlockLock = "MaxBlockLock"
|
||||
keySupportedAssets = "SupportedAssets"
|
||||
)
|
||||
|
||||
// ParamChanges defines the parameters that can be modified by param change proposals
|
||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
||||
// We generate MinBlockLock first because the result is required by GenMaxBlockLock()
|
||||
minBlockLockVal := GenMinBlockLock(r)
|
||||
|
||||
return []simulation.ParamChange{
|
||||
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress,
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("\"%s\"", GenRandBnbDeputy(r).Address)
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock,
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("\"%d\"", minBlockLockVal)
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, keyMaxBlockLock,
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("\"%d\"", GenMaxBlockLock(r, minBlockLockVal))
|
||||
},
|
||||
),
|
||||
simulation.NewSimParamChange(types.ModuleName, keySupportedAssets,
|
||||
func(r *rand.Rand) string {
|
||||
return fmt.Sprintf("\"%v\"", GenSupportedAssets(r))
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user