mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 07:45:18 +00:00
add 0.34 gaia
This commit is contained in:
parent
061c27bbc6
commit
76a7f3dd57
254
app/app.go
254
app/app.go
@ -1,254 +0,0 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
|
||||||
|
|
||||||
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"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/version"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/crisis"
|
|
||||||
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
|
||||||
distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/mint"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
|
||||||
paramsclient "github.com/cosmos/cosmos-sdk/x/params/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
||||||
)
|
|
||||||
|
|
||||||
const appName = "KavaApp"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// default home directories for cli
|
|
||||||
DefaultCLIHome = os.ExpandEnv("$HOME/.kvcli")
|
|
||||||
|
|
||||||
// default home directories for daemon
|
|
||||||
DefaultNodeHome = os.ExpandEnv("$HOME/.kvd")
|
|
||||||
|
|
||||||
// The ModuleBasicManager is in charge of setting up basic,
|
|
||||||
// non-dependant module elements, such as codec registration
|
|
||||||
// and genesis verification.
|
|
||||||
ModuleBasics module.BasicManager
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ModuleBasics = module.NewBasicManager(
|
|
||||||
genaccounts.AppModuleBasic{},
|
|
||||||
genutil.AppModuleBasic{},
|
|
||||||
auth.AppModuleBasic{},
|
|
||||||
bank.AppModuleBasic{},
|
|
||||||
staking.AppModuleBasic{},
|
|
||||||
mint.AppModuleBasic{},
|
|
||||||
distr.AppModuleBasic{},
|
|
||||||
gov.NewAppModuleBasic(paramsclient.ProposalHandler, distrclient.ProposalHandler),
|
|
||||||
params.AppModuleBasic{},
|
|
||||||
crisis.AppModuleBasic{},
|
|
||||||
slashing.AppModuleBasic{},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom tx codec
|
|
||||||
func MakeCodec() *codec.Codec {
|
|
||||||
var cdc = codec.New()
|
|
||||||
ModuleBasics.RegisterCodec(cdc)
|
|
||||||
sdk.RegisterCodec(cdc)
|
|
||||||
codec.RegisterCrypto(cdc)
|
|
||||||
return cdc
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAddressPrefixes sets the bech32 address prefixes on an sdk config.
|
|
||||||
func SetAddressPrefixes(config *sdk.Config) {
|
|
||||||
config.SetBech32PrefixForAccount("k", "k"+"pub")
|
|
||||||
config.SetBech32PrefixForValidator("k"+"val"+"oper", "k"+"val"+"oper"+"pub")
|
|
||||||
config.SetBech32PrefixForConsensusNode("k"+"val"+"cons", "k"+"val"+"cons"+"pub")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extended ABCI application
|
|
||||||
type App struct {
|
|
||||||
*bam.BaseApp
|
|
||||||
cdc *codec.Codec
|
|
||||||
|
|
||||||
invCheckPeriod uint
|
|
||||||
|
|
||||||
// keys to access the substores
|
|
||||||
keyMain *sdk.KVStoreKey
|
|
||||||
keyAccount *sdk.KVStoreKey
|
|
||||||
keyStaking *sdk.KVStoreKey
|
|
||||||
tkeyStaking *sdk.TransientStoreKey
|
|
||||||
keySlashing *sdk.KVStoreKey
|
|
||||||
keyMint *sdk.KVStoreKey
|
|
||||||
keyDistr *sdk.KVStoreKey
|
|
||||||
tkeyDistr *sdk.TransientStoreKey
|
|
||||||
keyGov *sdk.KVStoreKey
|
|
||||||
keyFeeCollection *sdk.KVStoreKey
|
|
||||||
keyParams *sdk.KVStoreKey
|
|
||||||
tkeyParams *sdk.TransientStoreKey
|
|
||||||
|
|
||||||
// keepers
|
|
||||||
accountKeeper auth.AccountKeeper
|
|
||||||
feeCollectionKeeper auth.FeeCollectionKeeper
|
|
||||||
bankKeeper bank.Keeper
|
|
||||||
stakingKeeper staking.Keeper
|
|
||||||
slashingKeeper slashing.Keeper
|
|
||||||
mintKeeper mint.Keeper
|
|
||||||
distrKeeper distr.Keeper
|
|
||||||
govKeeper gov.Keeper
|
|
||||||
crisisKeeper crisis.Keeper
|
|
||||||
paramsKeeper params.Keeper
|
|
||||||
|
|
||||||
// the module manager
|
|
||||||
mm *module.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApp returns a reference to an initialized App.
|
|
||||||
func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
|
||||||
invCheckPeriod uint, baseAppOptions ...func(*bam.BaseApp)) *App {
|
|
||||||
|
|
||||||
cdc := MakeCodec()
|
|
||||||
|
|
||||||
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
|
|
||||||
bApp.SetCommitMultiStoreTracer(traceStore)
|
|
||||||
bApp.SetAppVersion(version.Version)
|
|
||||||
|
|
||||||
var app = &App{
|
|
||||||
BaseApp: bApp,
|
|
||||||
cdc: cdc,
|
|
||||||
invCheckPeriod: invCheckPeriod,
|
|
||||||
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
|
|
||||||
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
|
|
||||||
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
|
|
||||||
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
|
|
||||||
keyMint: sdk.NewKVStoreKey(mint.StoreKey),
|
|
||||||
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
|
|
||||||
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
|
|
||||||
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
|
|
||||||
keyGov: sdk.NewKVStoreKey(gov.StoreKey),
|
|
||||||
keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey),
|
|
||||||
keyParams: sdk.NewKVStoreKey(params.StoreKey),
|
|
||||||
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
|
|
||||||
}
|
|
||||||
|
|
||||||
// init params keeper and subspaces
|
|
||||||
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams, params.DefaultCodespace)
|
|
||||||
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
|
|
||||||
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
|
|
||||||
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace)
|
|
||||||
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
|
|
||||||
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
|
|
||||||
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
|
|
||||||
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace)
|
|
||||||
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
|
|
||||||
|
|
||||||
// add keepers
|
|
||||||
app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.keyAccount, authSubspace, auth.ProtoBaseAccount)
|
|
||||||
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, bankSubspace, bank.DefaultCodespace)
|
|
||||||
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
|
|
||||||
stakingKeeper := staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper,
|
|
||||||
stakingSubspace, staking.DefaultCodespace)
|
|
||||||
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, mintSubspace, &stakingKeeper, app.feeCollectionKeeper)
|
|
||||||
app.distrKeeper = distr.NewKeeper(app.cdc, app.keyDistr, distrSubspace, app.bankKeeper, &stakingKeeper,
|
|
||||||
app.feeCollectionKeeper, distr.DefaultCodespace)
|
|
||||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, &stakingKeeper,
|
|
||||||
slashingSubspace, slashing.DefaultCodespace)
|
|
||||||
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.distrKeeper,
|
|
||||||
app.bankKeeper, app.feeCollectionKeeper)
|
|
||||||
|
|
||||||
// register the proposal types
|
|
||||||
govRouter := gov.NewRouter()
|
|
||||||
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
|
|
||||||
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
|
|
||||||
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
|
|
||||||
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.paramsKeeper, govSubspace,
|
|
||||||
app.bankKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
|
|
||||||
|
|
||||||
// register the staking hooks
|
|
||||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
|
||||||
app.stakingKeeper = *stakingKeeper.SetHooks(
|
|
||||||
staking.NewMultiStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()))
|
|
||||||
|
|
||||||
app.mm = module.NewManager(
|
|
||||||
genaccounts.NewAppModule(app.accountKeeper),
|
|
||||||
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
|
|
||||||
auth.NewAppModule(app.accountKeeper, app.feeCollectionKeeper),
|
|
||||||
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
|
|
||||||
crisis.NewAppModule(app.crisisKeeper, app.Logger()),
|
|
||||||
distr.NewAppModule(app.distrKeeper),
|
|
||||||
gov.NewAppModule(app.govKeeper),
|
|
||||||
mint.NewAppModule(app.mintKeeper),
|
|
||||||
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
|
|
||||||
staking.NewAppModule(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
app.mm.SetOrderBeginBlockers(mint.ModuleName, distr.ModuleName, slashing.ModuleName)
|
|
||||||
|
|
||||||
app.mm.SetOrderEndBlockers(gov.ModuleName, staking.ModuleName)
|
|
||||||
|
|
||||||
// genutils must occur after staking so that pools are properly
|
|
||||||
// initialized with tokens from genesis accounts.
|
|
||||||
app.mm.SetOrderInitGenesis(genaccounts.ModuleName, distr.ModuleName,
|
|
||||||
staking.ModuleName, auth.ModuleName, bank.ModuleName, slashing.ModuleName,
|
|
||||||
gov.ModuleName, mint.ModuleName, crisis.ModuleName, genutil.ModuleName)
|
|
||||||
|
|
||||||
app.mm.RegisterInvariants(&app.crisisKeeper)
|
|
||||||
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
|
|
||||||
|
|
||||||
// initialize stores
|
|
||||||
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keyMint,
|
|
||||||
app.keyDistr, app.keySlashing, app.keyGov, app.keyFeeCollection,
|
|
||||||
app.keyParams, app.tkeyParams, app.tkeyStaking, app.tkeyDistr)
|
|
||||||
|
|
||||||
// initialize BaseApp
|
|
||||||
app.SetInitChainer(app.InitChainer)
|
|
||||||
app.SetBeginBlocker(app.BeginBlocker)
|
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper, auth.DefaultSigVerificationGasConsumer))
|
|
||||||
app.SetEndBlocker(app.EndBlocker)
|
|
||||||
|
|
||||||
if loadLatest {
|
|
||||||
err := app.LoadLatestVersion(app.keyMain)
|
|
||||||
if err != nil {
|
|
||||||
cmn.Exit(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
// application updates every begin block
|
|
||||||
func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
|
||||||
return app.mm.BeginBlock(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// application updates every end block
|
|
||||||
func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
|
||||||
return app.mm.EndBlock(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// application update at chain initialization
|
|
||||||
func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
|
||||||
var genesisState GenesisState
|
|
||||||
app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
|
|
||||||
return app.mm.InitGenesis(ctx, genesisState)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load a particular height
|
|
||||||
func (app *App) LoadHeight(height int64) error {
|
|
||||||
return app.LoadVersion(height, app.keyMain)
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The genesis state of the blockchain is represented here as a map of raw json
|
|
||||||
// messages key'd by a identifier string.
|
|
||||||
// The identifier is used to determine which module genesis information belongs
|
|
||||||
// to so it may be appropriately routed during init chain.
|
|
||||||
// Within this application default genesis information is retrieved from
|
|
||||||
// the ModuleBasicManager which populates json from each BasicModule
|
|
||||||
// object provided to it during init.
|
|
||||||
type GenesisState map[string]json.RawMessage
|
|
||||||
|
|
||||||
// NewDefaultGenesisState generates the default state for gaia.
|
|
||||||
func NewDefaultGenesisState() GenesisState {
|
|
||||||
return ModuleBasics.DefaultGenesis()
|
|
||||||
}
|
|
408
gaia/app/app.go
Normal file
408
gaia/app/app.go
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"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/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
appName = "GaiaApp"
|
||||||
|
// DefaultKeyPass contains the default key password for genesis transactions
|
||||||
|
DefaultKeyPass = "12345678"
|
||||||
|
)
|
||||||
|
|
||||||
|
// default home directories for expected binaries
|
||||||
|
var (
|
||||||
|
DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli")
|
||||||
|
DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extended ABCI application
|
||||||
|
type GaiaApp struct {
|
||||||
|
*bam.BaseApp
|
||||||
|
cdc *codec.Codec
|
||||||
|
|
||||||
|
invCheckPeriod uint
|
||||||
|
|
||||||
|
// keys to access the substores
|
||||||
|
keyMain *sdk.KVStoreKey
|
||||||
|
keyAccount *sdk.KVStoreKey
|
||||||
|
keyStaking *sdk.KVStoreKey
|
||||||
|
tkeyStaking *sdk.TransientStoreKey
|
||||||
|
keySlashing *sdk.KVStoreKey
|
||||||
|
keyMint *sdk.KVStoreKey
|
||||||
|
keyDistr *sdk.KVStoreKey
|
||||||
|
tkeyDistr *sdk.TransientStoreKey
|
||||||
|
keyGov *sdk.KVStoreKey
|
||||||
|
keyFeeCollection *sdk.KVStoreKey
|
||||||
|
keyParams *sdk.KVStoreKey
|
||||||
|
tkeyParams *sdk.TransientStoreKey
|
||||||
|
|
||||||
|
// Manage getting and setting accounts
|
||||||
|
accountKeeper auth.AccountKeeper
|
||||||
|
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||||
|
bankKeeper bank.Keeper
|
||||||
|
stakingKeeper staking.Keeper
|
||||||
|
slashingKeeper slashing.Keeper
|
||||||
|
mintKeeper mint.Keeper
|
||||||
|
distrKeeper distr.Keeper
|
||||||
|
govKeeper gov.Keeper
|
||||||
|
crisisKeeper crisis.Keeper
|
||||||
|
paramsKeeper params.Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGaiaApp returns a reference to an initialized GaiaApp.
|
||||||
|
func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||||
|
invCheckPeriod uint,
|
||||||
|
baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
||||||
|
|
||||||
|
cdc := MakeCodec()
|
||||||
|
|
||||||
|
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
|
||||||
|
bApp.SetCommitMultiStoreTracer(traceStore)
|
||||||
|
|
||||||
|
var app = &GaiaApp{
|
||||||
|
BaseApp: bApp,
|
||||||
|
cdc: cdc,
|
||||||
|
invCheckPeriod: invCheckPeriod,
|
||||||
|
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
|
||||||
|
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
|
||||||
|
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
|
||||||
|
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
|
||||||
|
keyMint: sdk.NewKVStoreKey(mint.StoreKey),
|
||||||
|
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
|
||||||
|
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
|
||||||
|
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
|
||||||
|
keyGov: sdk.NewKVStoreKey(gov.StoreKey),
|
||||||
|
keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey),
|
||||||
|
keyParams: sdk.NewKVStoreKey(params.StoreKey),
|
||||||
|
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams)
|
||||||
|
|
||||||
|
// define the accountKeeper
|
||||||
|
app.accountKeeper = auth.NewAccountKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyAccount,
|
||||||
|
app.paramsKeeper.Subspace(auth.DefaultParamspace),
|
||||||
|
auth.ProtoBaseAccount,
|
||||||
|
)
|
||||||
|
|
||||||
|
// add handlers
|
||||||
|
app.bankKeeper = bank.NewBaseKeeper(
|
||||||
|
app.accountKeeper,
|
||||||
|
app.paramsKeeper.Subspace(bank.DefaultParamspace),
|
||||||
|
bank.DefaultCodespace,
|
||||||
|
)
|
||||||
|
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyFeeCollection,
|
||||||
|
)
|
||||||
|
stakingKeeper := staking.NewKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyStaking, app.tkeyStaking,
|
||||||
|
app.bankKeeper, app.paramsKeeper.Subspace(staking.DefaultParamspace),
|
||||||
|
staking.DefaultCodespace,
|
||||||
|
)
|
||||||
|
app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint,
|
||||||
|
app.paramsKeeper.Subspace(mint.DefaultParamspace),
|
||||||
|
&stakingKeeper, app.feeCollectionKeeper,
|
||||||
|
)
|
||||||
|
app.distrKeeper = distr.NewKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyDistr,
|
||||||
|
app.paramsKeeper.Subspace(distr.DefaultParamspace),
|
||||||
|
app.bankKeeper, &stakingKeeper, app.feeCollectionKeeper,
|
||||||
|
distr.DefaultCodespace,
|
||||||
|
)
|
||||||
|
app.slashingKeeper = slashing.NewKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keySlashing,
|
||||||
|
&stakingKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace),
|
||||||
|
slashing.DefaultCodespace,
|
||||||
|
)
|
||||||
|
app.govKeeper = gov.NewKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyGov,
|
||||||
|
app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakingKeeper,
|
||||||
|
gov.DefaultCodespace,
|
||||||
|
)
|
||||||
|
app.crisisKeeper = crisis.NewKeeper(
|
||||||
|
app.paramsKeeper.Subspace(crisis.DefaultParamspace),
|
||||||
|
app.distrKeeper,
|
||||||
|
app.bankKeeper,
|
||||||
|
app.feeCollectionKeeper,
|
||||||
|
)
|
||||||
|
|
||||||
|
// register the staking hooks
|
||||||
|
// NOTE: The stakingKeeper above is passed by reference, so that it can be
|
||||||
|
// modified like below:
|
||||||
|
app.stakingKeeper = *stakingKeeper.SetHooks(
|
||||||
|
NewStakingHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks()),
|
||||||
|
)
|
||||||
|
|
||||||
|
// register the crisis routes
|
||||||
|
bank.RegisterInvariants(&app.crisisKeeper, app.accountKeeper)
|
||||||
|
distr.RegisterInvariants(&app.crisisKeeper, app.distrKeeper, app.stakingKeeper)
|
||||||
|
staking.RegisterInvariants(&app.crisisKeeper, app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper)
|
||||||
|
|
||||||
|
// register message routes
|
||||||
|
app.Router().
|
||||||
|
AddRoute(bank.RouterKey, bank.NewHandler(app.bankKeeper)).
|
||||||
|
AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper)).
|
||||||
|
AddRoute(distr.RouterKey, distr.NewHandler(app.distrKeeper)).
|
||||||
|
AddRoute(slashing.RouterKey, slashing.NewHandler(app.slashingKeeper)).
|
||||||
|
AddRoute(gov.RouterKey, gov.NewHandler(app.govKeeper)).
|
||||||
|
AddRoute(crisis.RouterKey, crisis.NewHandler(app.crisisKeeper))
|
||||||
|
|
||||||
|
app.QueryRouter().
|
||||||
|
AddRoute(auth.QuerierRoute, auth.NewQuerier(app.accountKeeper)).
|
||||||
|
AddRoute(distr.QuerierRoute, distr.NewQuerier(app.distrKeeper)).
|
||||||
|
AddRoute(gov.QuerierRoute, gov.NewQuerier(app.govKeeper)).
|
||||||
|
AddRoute(slashing.QuerierRoute, slashing.NewQuerier(app.slashingKeeper, app.cdc)).
|
||||||
|
AddRoute(staking.QuerierRoute, staking.NewQuerier(app.stakingKeeper, app.cdc)).
|
||||||
|
AddRoute(mint.QuerierRoute, mint.NewQuerier(app.mintKeeper))
|
||||||
|
|
||||||
|
// initialize BaseApp
|
||||||
|
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keyMint, app.keyDistr,
|
||||||
|
app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams,
|
||||||
|
app.tkeyParams, app.tkeyStaking, app.tkeyDistr,
|
||||||
|
)
|
||||||
|
app.SetInitChainer(app.initChainer)
|
||||||
|
app.SetBeginBlocker(app.BeginBlocker)
|
||||||
|
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
|
||||||
|
app.SetEndBlocker(app.EndBlocker)
|
||||||
|
|
||||||
|
if loadLatest {
|
||||||
|
err := app.LoadLatestVersion(app.keyMain)
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom tx codec
|
||||||
|
func MakeCodec() *codec.Codec {
|
||||||
|
var cdc = codec.New()
|
||||||
|
bank.RegisterCodec(cdc)
|
||||||
|
staking.RegisterCodec(cdc)
|
||||||
|
distr.RegisterCodec(cdc)
|
||||||
|
slashing.RegisterCodec(cdc)
|
||||||
|
gov.RegisterCodec(cdc)
|
||||||
|
auth.RegisterCodec(cdc)
|
||||||
|
crisis.RegisterCodec(cdc)
|
||||||
|
sdk.RegisterCodec(cdc)
|
||||||
|
codec.RegisterCrypto(cdc)
|
||||||
|
return cdc
|
||||||
|
}
|
||||||
|
|
||||||
|
// application updates every end block
|
||||||
|
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||||
|
// mint new tokens for the previous block
|
||||||
|
mint.BeginBlocker(ctx, app.mintKeeper)
|
||||||
|
|
||||||
|
// distribute rewards for the previous block
|
||||||
|
distr.BeginBlocker(ctx, req, app.distrKeeper)
|
||||||
|
|
||||||
|
// slash anyone who double signed.
|
||||||
|
// NOTE: This should happen after distr.BeginBlocker so that
|
||||||
|
// there is nothing left over in the validator fee pool,
|
||||||
|
// so as to keep the CanWithdrawInvariant invariant.
|
||||||
|
// TODO: This should really happen at EndBlocker.
|
||||||
|
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
|
||||||
|
|
||||||
|
return abci.ResponseBeginBlock{
|
||||||
|
Tags: tags.ToKVPairs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// application updates every end block
|
||||||
|
// nolint: unparam
|
||||||
|
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||||
|
tags := gov.EndBlocker(ctx, app.govKeeper)
|
||||||
|
validatorUpdates, endBlockerTags := staking.EndBlocker(ctx, app.stakingKeeper)
|
||||||
|
tags = append(tags, endBlockerTags...)
|
||||||
|
|
||||||
|
if app.invCheckPeriod != 0 && ctx.BlockHeight()%int64(app.invCheckPeriod) == 0 {
|
||||||
|
app.assertRuntimeInvariants()
|
||||||
|
}
|
||||||
|
|
||||||
|
return abci.ResponseEndBlock{
|
||||||
|
ValidatorUpdates: validatorUpdates,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize store from a genesis state
|
||||||
|
func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisState) []abci.ValidatorUpdate {
|
||||||
|
genesisState.Sanitize()
|
||||||
|
|
||||||
|
// load the accounts
|
||||||
|
for _, gacc := range genesisState.Accounts {
|
||||||
|
acc := gacc.ToAccount()
|
||||||
|
acc = app.accountKeeper.NewAccount(ctx, acc) // set account number
|
||||||
|
app.accountKeeper.SetAccount(ctx, acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize distribution (must happen before staking)
|
||||||
|
distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData)
|
||||||
|
|
||||||
|
// load the initial staking information
|
||||||
|
validators, err := staking.InitGenesis(ctx, app.stakingKeeper, genesisState.StakingData)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO find a way to do this w/o panics
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize module-specific stores
|
||||||
|
auth.InitGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper, genesisState.AuthData)
|
||||||
|
bank.InitGenesis(ctx, app.bankKeeper, genesisState.BankData)
|
||||||
|
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData.Validators.ToSDKValidators())
|
||||||
|
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
|
||||||
|
crisis.InitGenesis(ctx, app.crisisKeeper, genesisState.CrisisData)
|
||||||
|
mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData)
|
||||||
|
|
||||||
|
// validate genesis state
|
||||||
|
if err := GaiaValidateGenesisState(genesisState); err != nil {
|
||||||
|
panic(err) // TODO find a way to do this w/o panics
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(genesisState.GenTxs) > 0 {
|
||||||
|
for _, genTx := range genesisState.GenTxs {
|
||||||
|
var tx auth.StdTx
|
||||||
|
err = app.cdc.UnmarshalJSON(genTx, &tx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bz := app.cdc.MustMarshalBinaryLengthPrefixed(tx)
|
||||||
|
res := app.BaseApp.DeliverTx(bz)
|
||||||
|
if !res.IsOK() {
|
||||||
|
panic(res.Log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validators = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
}
|
||||||
|
return validators
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom logic for gaia initialization
|
||||||
|
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
|
stateJSON := req.AppStateBytes
|
||||||
|
// TODO is this now the whole genesis file?
|
||||||
|
|
||||||
|
var genesisState GenesisState
|
||||||
|
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
validators := app.initFromGenesisState(ctx, genesisState)
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if len(req.Validators) > 0 {
|
||||||
|
if len(req.Validators) != len(validators) {
|
||||||
|
panic(fmt.Errorf("len(RequestInitChain.Validators) != len(validators) (%d != %d)",
|
||||||
|
len(req.Validators), len(validators)))
|
||||||
|
}
|
||||||
|
sort.Sort(abci.ValidatorUpdates(req.Validators))
|
||||||
|
sort.Sort(abci.ValidatorUpdates(validators))
|
||||||
|
for i, val := range validators {
|
||||||
|
if !val.Equal(req.Validators[i]) {
|
||||||
|
panic(fmt.Errorf("validators[%d] != req.Validators[%d] ", i, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert runtime invariants
|
||||||
|
app.assertRuntimeInvariants()
|
||||||
|
|
||||||
|
return abci.ResponseInitChain{
|
||||||
|
Validators: validators,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load a particular height
|
||||||
|
func (app *GaiaApp) LoadHeight(height int64) error {
|
||||||
|
return app.LoadVersion(height, app.keyMain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ______________________________________________________________________________________________
|
||||||
|
|
||||||
|
var _ sdk.StakingHooks = StakingHooks{}
|
||||||
|
|
||||||
|
// StakingHooks contains combined distribution and slashing hooks needed for the
|
||||||
|
// staking module.
|
||||||
|
type StakingHooks struct {
|
||||||
|
dh distr.Hooks
|
||||||
|
sh slashing.Hooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStakingHooks(dh distr.Hooks, sh slashing.Hooks) StakingHooks {
|
||||||
|
return StakingHooks{dh, sh}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
func (h StakingHooks) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.AfterValidatorCreated(ctx, valAddr)
|
||||||
|
h.sh.AfterValidatorCreated(ctx, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.BeforeValidatorModified(ctx, valAddr)
|
||||||
|
h.sh.BeforeValidatorModified(ctx, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.AfterValidatorRemoved(ctx, consAddr, valAddr)
|
||||||
|
h.sh.AfterValidatorRemoved(ctx, consAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) AfterValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.AfterValidatorBonded(ctx, consAddr, valAddr)
|
||||||
|
h.sh.AfterValidatorBonded(ctx, consAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) AfterValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
|
||||||
|
h.sh.AfterValidatorBeginUnbonding(ctx, consAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) BeforeDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.BeforeDelegationCreated(ctx, delAddr, valAddr)
|
||||||
|
h.sh.BeforeDelegationCreated(ctx, delAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) BeforeDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
|
||||||
|
h.sh.BeforeDelegationSharesModified(ctx, delAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) BeforeDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.BeforeDelegationRemoved(ctx, delAddr, valAddr)
|
||||||
|
h.sh.BeforeDelegationRemoved(ctx, delAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) AfterDelegationModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) {
|
||||||
|
h.dh.AfterDelegationModified(ctx, delAddr, valAddr)
|
||||||
|
h.sh.AfterDelegationModified(ctx, delAddr, valAddr)
|
||||||
|
}
|
||||||
|
func (h StakingHooks) BeforeValidatorSlashed(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
|
||||||
|
h.dh.BeforeValidatorSlashed(ctx, valAddr, fraction)
|
||||||
|
h.sh.BeforeValidatorSlashed(ctx, valAddr, fraction)
|
||||||
|
}
|
65
gaia/app/app_test.go
Normal file
65
gaia/app/app_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
||||||
|
genaccs := make([]GenesisAccount, len(accs))
|
||||||
|
for i, acc := range accs {
|
||||||
|
genaccs[i] = NewGenesisAccount(acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisState := NewGenesisState(
|
||||||
|
genaccs,
|
||||||
|
auth.DefaultGenesisState(),
|
||||||
|
bank.DefaultGenesisState(),
|
||||||
|
staking.DefaultGenesisState(),
|
||||||
|
mint.DefaultGenesisState(),
|
||||||
|
distr.DefaultGenesisState(),
|
||||||
|
gov.DefaultGenesisState(),
|
||||||
|
crisis.DefaultGenesisState(),
|
||||||
|
slashing.DefaultGenesisState(),
|
||||||
|
)
|
||||||
|
|
||||||
|
stateBytes, err := codec.MarshalJSONIndent(gapp.cdc, genesisState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the chain
|
||||||
|
vals := []abci.ValidatorUpdate{}
|
||||||
|
gapp.InitChain(abci.RequestInitChain{Validators: vals, AppStateBytes: stateBytes})
|
||||||
|
gapp.Commit()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiadExport(t *testing.T) {
|
||||||
|
db := db.NewMemDB()
|
||||||
|
gapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
|
||||||
|
setGenesis(gapp)
|
||||||
|
|
||||||
|
// Making a new app object with the db, so that initchain hasn't been called
|
||||||
|
newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)
|
||||||
|
_, _, err := newGapp.ExportAppStateAndValidators(false, []string{})
|
||||||
|
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
|
||||||
|
}
|
40
gaia/app/benchmarks/txsize_test.go
Normal file
40
gaia/app/benchmarks/txsize_test.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This will fail half the time with the second output being 173
|
||||||
|
// This is due to secp256k1 signatures not being constant size.
|
||||||
|
// nolint: vet
|
||||||
|
func ExampleTxSendSize() {
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
var gas uint64 = 1
|
||||||
|
|
||||||
|
priv1 := secp256k1.GenPrivKeySecp256k1([]byte{0})
|
||||||
|
addr1 := sdk.AccAddress(priv1.PubKey().Address())
|
||||||
|
priv2 := secp256k1.GenPrivKeySecp256k1([]byte{1})
|
||||||
|
addr2 := sdk.AccAddress(priv2.PubKey().Address())
|
||||||
|
coins := sdk.Coins{sdk.NewCoin("denom", sdk.NewInt(10))}
|
||||||
|
msg1 := bank.MsgMultiSend{
|
||||||
|
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||||
|
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||||
|
}
|
||||||
|
fee := auth.NewStdFee(gas, coins)
|
||||||
|
signBytes := auth.StdSignBytes("example-chain-ID",
|
||||||
|
1, 1, fee, []sdk.Msg{msg1}, "")
|
||||||
|
sig, _ := priv1.Sign(signBytes)
|
||||||
|
sigs := []auth.StdSignature{{nil, sig}}
|
||||||
|
tx := auth.NewStdTx([]sdk.Msg{msg1}, fee, sigs, "")
|
||||||
|
fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1})))
|
||||||
|
fmt.Println(len(cdc.MustMarshalBinaryBare(tx)))
|
||||||
|
// output: 80
|
||||||
|
// 169
|
||||||
|
}
|
@ -1,6 +1,3 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -12,13 +9,19 @@ import (
|
|||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||||
|
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
)
|
)
|
||||||
|
|
||||||
// export the state of the app for a genesis file
|
// export the state of gaia for a genesis file
|
||||||
func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string,
|
func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string) (
|
||||||
) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
|
||||||
|
|
||||||
// as if they could withdraw from the start of the next block
|
// as if they could withdraw from the start of the next block
|
||||||
ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()})
|
ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()})
|
||||||
@ -27,7 +30,26 @@ func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []
|
|||||||
app.prepForZeroHeightGenesis(ctx, jailWhiteList)
|
app.prepForZeroHeightGenesis(ctx, jailWhiteList)
|
||||||
}
|
}
|
||||||
|
|
||||||
genState := app.mm.ExportGenesis(ctx)
|
// iterate to get the accounts
|
||||||
|
accounts := []GenesisAccount{}
|
||||||
|
appendAccount := func(acc auth.Account) (stop bool) {
|
||||||
|
account := NewGenesisAccountI(acc)
|
||||||
|
accounts = append(accounts, account)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
app.accountKeeper.IterateAccounts(ctx, appendAccount)
|
||||||
|
|
||||||
|
genState := NewGenesisState(
|
||||||
|
accounts,
|
||||||
|
auth.ExportGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper),
|
||||||
|
bank.ExportGenesis(ctx, app.bankKeeper),
|
||||||
|
staking.ExportGenesis(ctx, app.stakingKeeper),
|
||||||
|
mint.ExportGenesis(ctx, app.mintKeeper),
|
||||||
|
distr.ExportGenesis(ctx, app.distrKeeper),
|
||||||
|
gov.ExportGenesis(ctx, app.govKeeper),
|
||||||
|
crisis.ExportGenesis(ctx, app.crisisKeeper),
|
||||||
|
slashing.ExportGenesis(ctx, app.slashingKeeper),
|
||||||
|
)
|
||||||
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
|
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -37,9 +59,7 @@ func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepare for fresh start at zero height
|
// prepare for fresh start at zero height
|
||||||
// NOTE zero height genesis is a temporary feature which will be deprecated
|
func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) {
|
||||||
// in favour of export at a block height
|
|
||||||
func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) {
|
|
||||||
applyWhiteList := false
|
applyWhiteList := false
|
||||||
|
|
||||||
//Check if there is a whitelist
|
//Check if there is a whitelist
|
||||||
@ -58,12 +78,12 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Just to be safe, assert the invariants on current state. */
|
/* Just to be safe, assert the invariants on current state. */
|
||||||
app.crisisKeeper.AssertInvariants(ctx, app.Logger())
|
app.assertRuntimeInvariantsOnContext(ctx)
|
||||||
|
|
||||||
/* Handle fee distribution state. */
|
/* Handle fee distribution state. */
|
||||||
|
|
||||||
// withdraw all validator commission
|
// withdraw all validator commission
|
||||||
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
|
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) {
|
||||||
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
_, _ = app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -85,7 +105,7 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string
|
|||||||
ctx = ctx.WithBlockHeight(0)
|
ctx = ctx.WithBlockHeight(0)
|
||||||
|
|
||||||
// reinitialize all validators
|
// reinitialize all validators
|
||||||
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
|
app.stakingKeeper.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) {
|
||||||
|
|
||||||
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
|
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
|
||||||
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
417
gaia/app/genesis.go
Normal file
417
gaia/app/genesis.go
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"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/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// bonded tokens given to genesis validators/accounts
|
||||||
|
freeTokensPerAcc = sdk.TokensFromTendermintPower(150)
|
||||||
|
defaultBondDenom = sdk.DefaultBondDenom
|
||||||
|
)
|
||||||
|
|
||||||
|
// State to Unmarshal
|
||||||
|
type GenesisState struct {
|
||||||
|
Accounts []GenesisAccount `json:"accounts"`
|
||||||
|
AuthData auth.GenesisState `json:"auth"`
|
||||||
|
BankData bank.GenesisState `json:"bank"`
|
||||||
|
StakingData staking.GenesisState `json:"staking"`
|
||||||
|
MintData mint.GenesisState `json:"mint"`
|
||||||
|
DistrData distr.GenesisState `json:"distr"`
|
||||||
|
GovData gov.GenesisState `json:"gov"`
|
||||||
|
CrisisData crisis.GenesisState `json:"crisis"`
|
||||||
|
SlashingData slashing.GenesisState `json:"slashing"`
|
||||||
|
GenTxs []json.RawMessage `json:"gentxs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState,
|
||||||
|
bankData bank.GenesisState,
|
||||||
|
stakingData staking.GenesisState, mintData mint.GenesisState,
|
||||||
|
distrData distr.GenesisState, govData gov.GenesisState, crisisData crisis.GenesisState,
|
||||||
|
slashingData slashing.GenesisState) GenesisState {
|
||||||
|
|
||||||
|
return GenesisState{
|
||||||
|
Accounts: accounts,
|
||||||
|
AuthData: authData,
|
||||||
|
BankData: bankData,
|
||||||
|
StakingData: stakingData,
|
||||||
|
MintData: mintData,
|
||||||
|
DistrData: distrData,
|
||||||
|
GovData: govData,
|
||||||
|
CrisisData: crisisData,
|
||||||
|
SlashingData: slashingData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize sorts accounts and coin sets.
|
||||||
|
func (gs GenesisState) Sanitize() {
|
||||||
|
sort.Slice(gs.Accounts, func(i, j int) bool {
|
||||||
|
return gs.Accounts[i].AccountNumber < gs.Accounts[j].AccountNumber
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, acc := range gs.Accounts {
|
||||||
|
acc.Coins = acc.Coins.Sort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisAccount defines an account initialized at genesis.
|
||||||
|
type GenesisAccount struct {
|
||||||
|
Address sdk.AccAddress `json:"address"`
|
||||||
|
Coins sdk.Coins `json:"coins"`
|
||||||
|
Sequence uint64 `json:"sequence_number"`
|
||||||
|
AccountNumber uint64 `json:"account_number"`
|
||||||
|
|
||||||
|
// vesting account fields
|
||||||
|
OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization
|
||||||
|
DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation
|
||||||
|
DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation
|
||||||
|
StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time)
|
||||||
|
EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
|
||||||
|
return GenesisAccount{
|
||||||
|
Address: acc.Address,
|
||||||
|
Coins: acc.Coins,
|
||||||
|
AccountNumber: acc.AccountNumber,
|
||||||
|
Sequence: acc.Sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenesisAccountI(acc auth.Account) GenesisAccount {
|
||||||
|
gacc := GenesisAccount{
|
||||||
|
Address: acc.GetAddress(),
|
||||||
|
Coins: acc.GetCoins(),
|
||||||
|
AccountNumber: acc.GetAccountNumber(),
|
||||||
|
Sequence: acc.GetSequence(),
|
||||||
|
}
|
||||||
|
|
||||||
|
vacc, ok := acc.(auth.VestingAccount)
|
||||||
|
if ok {
|
||||||
|
gacc.OriginalVesting = vacc.GetOriginalVesting()
|
||||||
|
gacc.DelegatedFree = vacc.GetDelegatedFree()
|
||||||
|
gacc.DelegatedVesting = vacc.GetDelegatedVesting()
|
||||||
|
gacc.StartTime = vacc.GetStartTime()
|
||||||
|
gacc.EndTime = vacc.GetEndTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
return gacc
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert GenesisAccount to auth.BaseAccount
|
||||||
|
func (ga *GenesisAccount) ToAccount() auth.Account {
|
||||||
|
bacc := &auth.BaseAccount{
|
||||||
|
Address: ga.Address,
|
||||||
|
Coins: ga.Coins.Sort(),
|
||||||
|
AccountNumber: ga.AccountNumber,
|
||||||
|
Sequence: ga.Sequence,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ga.OriginalVesting.IsZero() {
|
||||||
|
baseVestingAcc := &auth.BaseVestingAccount{
|
||||||
|
BaseAccount: bacc,
|
||||||
|
OriginalVesting: ga.OriginalVesting,
|
||||||
|
DelegatedFree: ga.DelegatedFree,
|
||||||
|
DelegatedVesting: ga.DelegatedVesting,
|
||||||
|
EndTime: ga.EndTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ga.StartTime != 0 && ga.EndTime != 0 {
|
||||||
|
return &auth.ContinuousVestingAccount{
|
||||||
|
BaseVestingAccount: baseVestingAcc,
|
||||||
|
StartTime: ga.StartTime,
|
||||||
|
}
|
||||||
|
} else if ga.EndTime != 0 {
|
||||||
|
return &auth.DelayedVestingAccount{
|
||||||
|
BaseVestingAccount: baseVestingAcc,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintf("invalid genesis vesting account: %+v", ga))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bacc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the core parameters for genesis initialization for gaia
|
||||||
|
// note that the pubkey input is this machines pubkey
|
||||||
|
func GaiaAppGenState(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
|
||||||
|
genesisState GenesisState, err error) {
|
||||||
|
|
||||||
|
if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil {
|
||||||
|
return genesisState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no gen txs to be processed, return the default empty state
|
||||||
|
if len(appGenTxs) == 0 {
|
||||||
|
return genesisState, errors.New("there must be at least one genesis tx")
|
||||||
|
}
|
||||||
|
|
||||||
|
stakingData := genesisState.StakingData
|
||||||
|
for i, genTx := range appGenTxs {
|
||||||
|
var tx auth.StdTx
|
||||||
|
if err := cdc.UnmarshalJSON(genTx, &tx); err != nil {
|
||||||
|
return genesisState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs := tx.GetMsgs()
|
||||||
|
if len(msgs) != 1 {
|
||||||
|
return genesisState, errors.New(
|
||||||
|
"must provide genesis StdTx with exactly 1 CreateValidator message")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := msgs[0].(staking.MsgCreateValidator); !ok {
|
||||||
|
return genesisState, fmt.Errorf(
|
||||||
|
"Genesis transaction %v does not contain a MsgCreateValidator", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, acc := range genesisState.Accounts {
|
||||||
|
for _, coin := range acc.Coins {
|
||||||
|
if coin.Denom == genesisState.StakingData.Params.BondDenom {
|
||||||
|
stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.
|
||||||
|
Add(coin.Amount) // increase the supply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisState.StakingData = stakingData
|
||||||
|
genesisState.GenTxs = appGenTxs
|
||||||
|
|
||||||
|
return genesisState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultGenesisState generates the default state for gaia.
|
||||||
|
func NewDefaultGenesisState() GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
Accounts: nil,
|
||||||
|
AuthData: auth.DefaultGenesisState(),
|
||||||
|
BankData: bank.DefaultGenesisState(),
|
||||||
|
StakingData: staking.DefaultGenesisState(),
|
||||||
|
MintData: mint.DefaultGenesisState(),
|
||||||
|
DistrData: distr.DefaultGenesisState(),
|
||||||
|
GovData: gov.DefaultGenesisState(),
|
||||||
|
CrisisData: crisis.DefaultGenesisState(),
|
||||||
|
SlashingData: slashing.DefaultGenesisState(),
|
||||||
|
GenTxs: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaiaValidateGenesisState ensures that the genesis state obeys the expected invariants
|
||||||
|
// TODO: No validators are both bonded and jailed (#2088)
|
||||||
|
// TODO: Error if there is a duplicate validator (#1708)
|
||||||
|
// TODO: Ensure all state machine parameters are in genesis (#1704)
|
||||||
|
func GaiaValidateGenesisState(genesisState GenesisState) error {
|
||||||
|
if err := validateGenesisStateAccounts(genesisState.Accounts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip stakingData validation as genesis is created from txs
|
||||||
|
if len(genesisState.GenTxs) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := auth.ValidateGenesis(genesisState.AuthData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := bank.ValidateGenesis(genesisState.BankData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := staking.ValidateGenesis(genesisState.StakingData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := mint.ValidateGenesis(genesisState.MintData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := distr.ValidateGenesis(genesisState.DistrData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := gov.ValidateGenesis(genesisState.GovData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := crisis.ValidateGenesis(genesisState.CrisisData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return slashing.ValidateGenesis(genesisState.SlashingData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateGenesisStateAccounts performs validation of genesis accounts. It
|
||||||
|
// ensures that there are no duplicate accounts in the genesis state and any
|
||||||
|
// provided vesting accounts are valid.
|
||||||
|
func validateGenesisStateAccounts(accs []GenesisAccount) error {
|
||||||
|
addrMap := make(map[string]bool, len(accs))
|
||||||
|
for _, acc := range accs {
|
||||||
|
addrStr := acc.Address.String()
|
||||||
|
|
||||||
|
// disallow any duplicate accounts
|
||||||
|
if _, ok := addrMap[addrStr]; ok {
|
||||||
|
return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate any vesting fields
|
||||||
|
if !acc.OriginalVesting.IsZero() {
|
||||||
|
if acc.EndTime == 0 {
|
||||||
|
return fmt.Errorf("missing end time for vesting account; address: %s", addrStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if acc.StartTime >= acc.EndTime {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"vesting start time must before end time; address: %s, start: %s, end: %s",
|
||||||
|
addrStr,
|
||||||
|
time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339),
|
||||||
|
time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addrMap[addrStr] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GaiaAppGenState but with JSON
|
||||||
|
func GaiaAppGenStateJSON(cdc *codec.Codec, genDoc tmtypes.GenesisDoc, appGenTxs []json.RawMessage) (
|
||||||
|
appState json.RawMessage, err error) {
|
||||||
|
// create the final app state
|
||||||
|
genesisState, err := GaiaAppGenState(cdc, genDoc, appGenTxs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return codec.MarshalJSONIndent(cdc, genesisState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectStdTxs processes and validates application's genesis StdTxs and returns
|
||||||
|
// the list of appGenTxs, and persistent peers required to generate genesis.json.
|
||||||
|
func CollectStdTxs(cdc *codec.Codec, moniker string, genTxsDir string, genDoc tmtypes.GenesisDoc) (
|
||||||
|
appGenTxs []auth.StdTx, persistentPeers string, err error) {
|
||||||
|
|
||||||
|
var fos []os.FileInfo
|
||||||
|
fos, err = ioutil.ReadDir(genTxsDir)
|
||||||
|
if err != nil {
|
||||||
|
return appGenTxs, persistentPeers, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare a map of all accounts in genesis state to then validate
|
||||||
|
// against the validators addresses
|
||||||
|
var appState GenesisState
|
||||||
|
if err := cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil {
|
||||||
|
return appGenTxs, persistentPeers, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrMap := make(map[string]GenesisAccount, len(appState.Accounts))
|
||||||
|
for i := 0; i < len(appState.Accounts); i++ {
|
||||||
|
acc := appState.Accounts[i]
|
||||||
|
addrMap[acc.Address.String()] = acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// addresses and IPs (and port) validator server info
|
||||||
|
var addressesIPs []string
|
||||||
|
|
||||||
|
for _, fo := range fos {
|
||||||
|
filename := filepath.Join(genTxsDir, fo.Name())
|
||||||
|
if !fo.IsDir() && (filepath.Ext(filename) != ".json") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the genStdTx
|
||||||
|
var jsonRawTx []byte
|
||||||
|
if jsonRawTx, err = ioutil.ReadFile(filename); err != nil {
|
||||||
|
return appGenTxs, persistentPeers, err
|
||||||
|
}
|
||||||
|
var genStdTx auth.StdTx
|
||||||
|
if err = cdc.UnmarshalJSON(jsonRawTx, &genStdTx); err != nil {
|
||||||
|
return appGenTxs, persistentPeers, err
|
||||||
|
}
|
||||||
|
appGenTxs = append(appGenTxs, genStdTx)
|
||||||
|
|
||||||
|
// the memo flag is used to store
|
||||||
|
// the ip and node-id, for example this may be:
|
||||||
|
// "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656"
|
||||||
|
nodeAddrIP := genStdTx.GetMemo()
|
||||||
|
if len(nodeAddrIP) == 0 {
|
||||||
|
return appGenTxs, persistentPeers, fmt.Errorf(
|
||||||
|
"couldn't find node's address and IP in %s", fo.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// genesis transactions must be single-message
|
||||||
|
msgs := genStdTx.GetMsgs()
|
||||||
|
if len(msgs) != 1 {
|
||||||
|
|
||||||
|
return appGenTxs, persistentPeers, errors.New(
|
||||||
|
"each genesis transaction must provide a single genesis message")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := msgs[0].(staking.MsgCreateValidator)
|
||||||
|
// validate delegator and validator addresses and funds against the accounts in the state
|
||||||
|
delAddr := msg.DelegatorAddress.String()
|
||||||
|
valAddr := sdk.AccAddress(msg.ValidatorAddress).String()
|
||||||
|
|
||||||
|
delAcc, delOk := addrMap[delAddr]
|
||||||
|
_, valOk := addrMap[valAddr]
|
||||||
|
|
||||||
|
accsNotInGenesis := []string{}
|
||||||
|
if !delOk {
|
||||||
|
accsNotInGenesis = append(accsNotInGenesis, delAddr)
|
||||||
|
}
|
||||||
|
if !valOk {
|
||||||
|
accsNotInGenesis = append(accsNotInGenesis, valAddr)
|
||||||
|
}
|
||||||
|
if len(accsNotInGenesis) != 0 {
|
||||||
|
return appGenTxs, persistentPeers, fmt.Errorf(
|
||||||
|
"account(s) %v not in genesis.json: %+v", strings.Join(accsNotInGenesis, " "), addrMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if delAcc.Coins.AmountOf(msg.Value.Denom).LT(msg.Value.Amount) {
|
||||||
|
return appGenTxs, persistentPeers, fmt.Errorf(
|
||||||
|
"insufficient fund for delegation %v: %v < %v",
|
||||||
|
delAcc.Address, delAcc.Coins.AmountOf(msg.Value.Denom), msg.Value.Amount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exclude itself from persistent peers
|
||||||
|
if msg.Description.Moniker != moniker {
|
||||||
|
addressesIPs = append(addressesIPs, nodeAddrIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(addressesIPs)
|
||||||
|
persistentPeers = strings.Join(addressesIPs, ",")
|
||||||
|
|
||||||
|
return appGenTxs, persistentPeers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultGenesisAccount(addr sdk.AccAddress) GenesisAccount {
|
||||||
|
accAuth := auth.NewBaseAccountWithAddress(addr)
|
||||||
|
coins := sdk.Coins{
|
||||||
|
sdk.NewCoin("footoken", sdk.NewInt(1000)),
|
||||||
|
sdk.NewCoin(defaultBondDenom, freeTokensPerAcc),
|
||||||
|
}
|
||||||
|
|
||||||
|
coins.Sort()
|
||||||
|
|
||||||
|
accAuth.Coins = coins
|
||||||
|
return NewGenesisAccount(&accAuth)
|
||||||
|
}
|
188
gaia/app/genesis_test.go
Normal file
188
gaia/app/genesis_test.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pk1 = ed25519.GenPrivKey().PubKey()
|
||||||
|
pk2 = ed25519.GenPrivKey().PubKey()
|
||||||
|
pk3 = ed25519.GenPrivKey().PubKey()
|
||||||
|
addr1 = sdk.ValAddress(pk1.Address())
|
||||||
|
addr2 = sdk.ValAddress(pk2.Address())
|
||||||
|
addr3 = sdk.ValAddress(pk3.Address())
|
||||||
|
|
||||||
|
emptyAddr sdk.ValAddress
|
||||||
|
emptyPubkey crypto.PubKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeGenesisState(t *testing.T, genTxs []auth.StdTx) GenesisState {
|
||||||
|
// start with the default staking genesis state
|
||||||
|
appState := NewDefaultGenesisState()
|
||||||
|
stakingData := appState.StakingData
|
||||||
|
genAccs := make([]GenesisAccount, len(genTxs))
|
||||||
|
|
||||||
|
for i, genTx := range genTxs {
|
||||||
|
msgs := genTx.GetMsgs()
|
||||||
|
require.Equal(t, 1, len(msgs))
|
||||||
|
msg := msgs[0].(staking.MsgCreateValidator)
|
||||||
|
|
||||||
|
acc := auth.NewBaseAccountWithAddress(sdk.AccAddress(msg.ValidatorAddress))
|
||||||
|
acc.Coins = sdk.NewCoins(sdk.NewInt64Coin(defaultBondDenom, 150))
|
||||||
|
genAccs[i] = NewGenesisAccount(&acc)
|
||||||
|
stakingData.Pool.NotBondedTokens = stakingData.Pool.NotBondedTokens.Add(sdk.NewInt(150)) // increase the supply
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the final app state
|
||||||
|
appState.Accounts = genAccs
|
||||||
|
return appState
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToAccount(t *testing.T) {
|
||||||
|
priv := ed25519.GenPrivKey()
|
||||||
|
addr := sdk.AccAddress(priv.PubKey().Address())
|
||||||
|
authAcc := auth.NewBaseAccountWithAddress(addr)
|
||||||
|
authAcc.SetCoins(sdk.NewCoins(sdk.NewInt64Coin(defaultBondDenom, 150)))
|
||||||
|
genAcc := NewGenesisAccount(&authAcc)
|
||||||
|
acc := genAcc.ToAccount()
|
||||||
|
require.IsType(t, &auth.BaseAccount{}, acc)
|
||||||
|
require.Equal(t, &authAcc, acc.(*auth.BaseAccount))
|
||||||
|
|
||||||
|
vacc := auth.NewContinuousVestingAccount(
|
||||||
|
&authAcc, time.Now().Unix(), time.Now().Add(24*time.Hour).Unix(),
|
||||||
|
)
|
||||||
|
genAcc = NewGenesisAccountI(vacc)
|
||||||
|
acc = genAcc.ToAccount()
|
||||||
|
require.IsType(t, &auth.ContinuousVestingAccount{}, acc)
|
||||||
|
require.Equal(t, vacc, acc.(*auth.ContinuousVestingAccount))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaAppGenTx(t *testing.T) {
|
||||||
|
cdc := MakeCodec()
|
||||||
|
_ = cdc
|
||||||
|
|
||||||
|
//TODO test that key overwrite flags work / no overwrites if set off
|
||||||
|
//TODO test validator created has provided pubkey
|
||||||
|
//TODO test the account created has the correct pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaAppGenState(t *testing.T) {
|
||||||
|
cdc := MakeCodec()
|
||||||
|
_ = cdc
|
||||||
|
var genDoc tmtypes.GenesisDoc
|
||||||
|
|
||||||
|
// test unmarshalling error
|
||||||
|
_, err := GaiaAppGenState(cdc, genDoc, []json.RawMessage{})
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
appState := makeGenesisState(t, []auth.StdTx{})
|
||||||
|
genDoc.AppState, err = json.Marshal(appState)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// test validation error
|
||||||
|
_, err = GaiaAppGenState(cdc, genDoc, []json.RawMessage{})
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// TODO test must provide at least genesis transaction
|
||||||
|
// TODO test with both one and two genesis transactions:
|
||||||
|
// TODO correct: genesis account created, canididates created, pool token variance
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
|
||||||
|
desc := staking.NewDescription(name, "", "", "")
|
||||||
|
comm := staking.CommissionMsg{}
|
||||||
|
msg := staking.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(defaultBondDenom,
|
||||||
|
50), desc, comm, sdk.OneInt())
|
||||||
|
return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaGenesisValidation(t *testing.T) {
|
||||||
|
genTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk2)}
|
||||||
|
dupGenTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk1)}
|
||||||
|
|
||||||
|
// require duplicate accounts fails validation
|
||||||
|
genesisState := makeGenesisState(t, dupGenTxs)
|
||||||
|
err := GaiaValidateGenesisState(genesisState)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// require invalid vesting account fails validation (invalid end time)
|
||||||
|
genesisState = makeGenesisState(t, genTxs)
|
||||||
|
genesisState.Accounts[0].OriginalVesting = genesisState.Accounts[0].Coins
|
||||||
|
err = GaiaValidateGenesisState(genesisState)
|
||||||
|
require.Error(t, err)
|
||||||
|
genesisState.Accounts[0].StartTime = 1548888000
|
||||||
|
genesisState.Accounts[0].EndTime = 1548775410
|
||||||
|
err = GaiaValidateGenesisState(genesisState)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// require bonded + jailed validator fails validation
|
||||||
|
genesisState = makeGenesisState(t, genTxs)
|
||||||
|
val1 := staking.NewValidator(addr1, pk1, staking.NewDescription("test #2", "", "", ""))
|
||||||
|
val1.Jailed = true
|
||||||
|
val1.Status = sdk.Bonded
|
||||||
|
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1)
|
||||||
|
err = GaiaValidateGenesisState(genesisState)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// require duplicate validator fails validation
|
||||||
|
val1.Jailed = false
|
||||||
|
genesisState = makeGenesisState(t, genTxs)
|
||||||
|
val2 := staking.NewValidator(addr1, pk1, staking.NewDescription("test #3", "", "", ""))
|
||||||
|
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1)
|
||||||
|
genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val2)
|
||||||
|
err = GaiaValidateGenesisState(genesisState)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewDefaultGenesisAccount(t *testing.T) {
|
||||||
|
addr := secp256k1.GenPrivKeySecp256k1([]byte("")).PubKey().Address()
|
||||||
|
acc := NewDefaultGenesisAccount(sdk.AccAddress(addr))
|
||||||
|
require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("footoken"))
|
||||||
|
require.Equal(t, sdk.TokensFromTendermintPower(150), acc.Coins.AmountOf(defaultBondDenom))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenesisStateSanitize(t *testing.T) {
|
||||||
|
genesisState := makeGenesisState(t, nil)
|
||||||
|
require.Nil(t, GaiaValidateGenesisState(genesisState))
|
||||||
|
|
||||||
|
addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
|
||||||
|
authAcc1 := auth.NewBaseAccountWithAddress(addr1)
|
||||||
|
authAcc1.SetCoins(sdk.Coins{
|
||||||
|
sdk.NewInt64Coin("bcoin", 150),
|
||||||
|
sdk.NewInt64Coin("acoin", 150),
|
||||||
|
})
|
||||||
|
authAcc1.SetAccountNumber(1)
|
||||||
|
genAcc1 := NewGenesisAccount(&authAcc1)
|
||||||
|
|
||||||
|
addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
|
||||||
|
authAcc2 := auth.NewBaseAccountWithAddress(addr2)
|
||||||
|
authAcc2.SetCoins(sdk.Coins{
|
||||||
|
sdk.NewInt64Coin("acoin", 150),
|
||||||
|
sdk.NewInt64Coin("bcoin", 150),
|
||||||
|
})
|
||||||
|
genAcc2 := NewGenesisAccount(&authAcc2)
|
||||||
|
|
||||||
|
genesisState.Accounts = []GenesisAccount{genAcc1, genAcc2}
|
||||||
|
require.True(t, genesisState.Accounts[0].AccountNumber > genesisState.Accounts[1].AccountNumber)
|
||||||
|
require.Equal(t, genesisState.Accounts[0].Coins[0].Denom, "bcoin")
|
||||||
|
require.Equal(t, genesisState.Accounts[0].Coins[1].Denom, "acoin")
|
||||||
|
require.Equal(t, genesisState.Accounts[1].Address, addr2)
|
||||||
|
genesisState.Sanitize()
|
||||||
|
require.False(t, genesisState.Accounts[0].AccountNumber > genesisState.Accounts[1].AccountNumber)
|
||||||
|
require.Equal(t, genesisState.Accounts[1].Address, addr1)
|
||||||
|
require.Equal(t, genesisState.Accounts[1].Coins[0].Denom, "acoin")
|
||||||
|
require.Equal(t, genesisState.Accounts[1].Coins[1].Denom, "bcoin")
|
||||||
|
}
|
31
gaia/app/invariants.go
Normal file
31
gaia/app/invariants.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (app *GaiaApp) assertRuntimeInvariants() {
|
||||||
|
ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1})
|
||||||
|
app.assertRuntimeInvariantsOnContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *GaiaApp) assertRuntimeInvariantsOnContext(ctx sdk.Context) {
|
||||||
|
start := time.Now()
|
||||||
|
invarRoutes := app.crisisKeeper.Routes()
|
||||||
|
for _, ir := range invarRoutes {
|
||||||
|
if err := ir.Invar(ctx); err != nil {
|
||||||
|
panic(fmt.Errorf("invariant broken: %s\n"+
|
||||||
|
"\tCRITICAL please submit the following transaction:\n"+
|
||||||
|
"\t\t gaiacli tx crisis invariant-broken %v %v", err, ir.ModuleName, ir.Route))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end := time.Now()
|
||||||
|
diff := end.Sub(start)
|
||||||
|
app.BaseApp.Logger().With("module", "invariants").Info(
|
||||||
|
"Asserted all invariants", "duration", diff, "height", app.LastBlockHeight())
|
||||||
|
}
|
564
gaia/app/sim_test.go
Normal file
564
gaia/app/sim_test.go
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authsim "github.com/cosmos/cosmos-sdk/x/auth/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation"
|
||||||
|
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
|
distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
govsim "github.com/cosmos/cosmos-sdk/x/gov/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
genesisFile string
|
||||||
|
seed int64
|
||||||
|
numBlocks int
|
||||||
|
blockSize int
|
||||||
|
enabled bool
|
||||||
|
verbose bool
|
||||||
|
lean bool
|
||||||
|
commit bool
|
||||||
|
period int
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.StringVar(&genesisFile, "SimulationGenesis", "", "custom simulation genesis file")
|
||||||
|
flag.Int64Var(&seed, "SimulationSeed", 42, "simulation random seed")
|
||||||
|
flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "number of blocks")
|
||||||
|
flag.IntVar(&blockSize, "SimulationBlockSize", 200, "operations per block")
|
||||||
|
flag.BoolVar(&enabled, "SimulationEnabled", false, "enable the simulation")
|
||||||
|
flag.BoolVar(&verbose, "SimulationVerbose", false, "verbose log output")
|
||||||
|
flag.BoolVar(&lean, "SimulationLean", false, "lean simulation log output")
|
||||||
|
flag.BoolVar(&commit, "SimulationCommit", false, "have the simulation commit")
|
||||||
|
flag.IntVar(&period, "SimulationPeriod", 1, "run slow invariants only once every period assertions")
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function for populating input for SimulateFromSeed
|
||||||
|
func getSimulateFromSeedInput(tb testing.TB, app *GaiaApp) (
|
||||||
|
testing.TB, *baseapp.BaseApp, simulation.AppStateFn, int64,
|
||||||
|
simulation.WeightedOperations, sdk.Invariants, int, int, bool, bool) {
|
||||||
|
|
||||||
|
return tb, app.BaseApp, appStateFn, seed,
|
||||||
|
testAndRunTxs(app), invariants(app), numBlocks, blockSize, commit, lean
|
||||||
|
}
|
||||||
|
|
||||||
|
func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) {
|
||||||
|
var genesis tmtypes.GenesisDoc
|
||||||
|
cdc := MakeCodec()
|
||||||
|
bytes, err := ioutil.ReadFile(genesisFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cdc.MustUnmarshalJSON(bytes, &genesis)
|
||||||
|
var appState GenesisState
|
||||||
|
cdc.MustUnmarshalJSON(genesis.AppState, &appState)
|
||||||
|
var newAccs []simulation.Account
|
||||||
|
for _, acc := range appState.Accounts {
|
||||||
|
// Pick a random private key, since we don't know the actual key
|
||||||
|
// This should be fine as it's only used for mock Tendermint validators
|
||||||
|
// and these keys are never actually used to sign by mock Tendermint.
|
||||||
|
privkeySeed := make([]byte, 15)
|
||||||
|
r.Read(privkeySeed)
|
||||||
|
privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed)
|
||||||
|
newAccs = append(newAccs, simulation.Account{privKey, privKey.PubKey(), acc.Address})
|
||||||
|
}
|
||||||
|
return genesis.AppState, newAccs, genesis.ChainID
|
||||||
|
}
|
||||||
|
|
||||||
|
func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) {
|
||||||
|
|
||||||
|
var genesisAccounts []GenesisAccount
|
||||||
|
|
||||||
|
amount := int64(r.Intn(1e12))
|
||||||
|
numInitiallyBonded := int64(r.Intn(250))
|
||||||
|
numAccs := int64(len(accs))
|
||||||
|
if numInitiallyBonded > numAccs {
|
||||||
|
numInitiallyBonded = numAccs
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated parameters for simulated genesis:\n"+
|
||||||
|
"\t{amount of stake per account: %v, initially bonded validators: %v}\n",
|
||||||
|
amount, numInitiallyBonded)
|
||||||
|
|
||||||
|
// randomly generate some genesis accounts
|
||||||
|
for i, acc := range accs {
|
||||||
|
coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(amount))}
|
||||||
|
bacc := auth.NewBaseAccountWithAddress(acc.Address)
|
||||||
|
bacc.SetCoins(coins)
|
||||||
|
|
||||||
|
var gacc GenesisAccount
|
||||||
|
|
||||||
|
// Only consider making a vesting account once the initial bonded validator
|
||||||
|
// set is exhausted due to needing to track DelegatedVesting.
|
||||||
|
if int64(i) > numInitiallyBonded && r.Intn(100) < 50 {
|
||||||
|
var (
|
||||||
|
vacc auth.VestingAccount
|
||||||
|
endTime int64
|
||||||
|
)
|
||||||
|
|
||||||
|
startTime := genesisTimestamp.Unix()
|
||||||
|
|
||||||
|
// Allow for some vesting accounts to vest very quickly while others very
|
||||||
|
// slowly.
|
||||||
|
if r.Intn(100) < 50 {
|
||||||
|
endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*24*30))))
|
||||||
|
} else {
|
||||||
|
endTime = int64(simulation.RandIntBetween(r, int(startTime), int(startTime+(60*60*12))))
|
||||||
|
}
|
||||||
|
|
||||||
|
if startTime == endTime {
|
||||||
|
endTime += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Intn(100) < 50 {
|
||||||
|
vacc = auth.NewContinuousVestingAccount(&bacc, startTime, endTime)
|
||||||
|
} else {
|
||||||
|
vacc = auth.NewDelayedVestingAccount(&bacc, endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
gacc = NewGenesisAccountI(vacc)
|
||||||
|
} else {
|
||||||
|
gacc = NewGenesisAccount(&bacc)
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisAccounts = append(genesisAccounts, gacc)
|
||||||
|
}
|
||||||
|
|
||||||
|
authGenesis := auth.GenesisState{
|
||||||
|
Params: auth.Params{
|
||||||
|
MaxMemoCharacters: uint64(simulation.RandIntBetween(r, 100, 200)),
|
||||||
|
TxSigLimit: uint64(r.Intn(7) + 1),
|
||||||
|
TxSizeCostPerByte: uint64(simulation.RandIntBetween(r, 5, 15)),
|
||||||
|
SigVerifyCostED25519: uint64(simulation.RandIntBetween(r, 500, 1000)),
|
||||||
|
SigVerifyCostSecp256k1: uint64(simulation.RandIntBetween(r, 500, 1000)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated auth parameters:\n\t%+v\n", authGenesis)
|
||||||
|
|
||||||
|
bankGenesis := bank.NewGenesisState(r.Int63n(2) == 0)
|
||||||
|
fmt.Printf("Selected randomly generated bank parameters:\n\t%+v\n", bankGenesis)
|
||||||
|
|
||||||
|
// Random genesis states
|
||||||
|
vp := time.Duration(r.Intn(2*172800)) * time.Second
|
||||||
|
govGenesis := gov.GenesisState{
|
||||||
|
StartingProposalID: uint64(r.Intn(100)),
|
||||||
|
DepositParams: gov.DepositParams{
|
||||||
|
MinDeposit: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, int64(r.Intn(1e3)))},
|
||||||
|
MaxDepositPeriod: vp,
|
||||||
|
},
|
||||||
|
VotingParams: gov.VotingParams{
|
||||||
|
VotingPeriod: vp,
|
||||||
|
},
|
||||||
|
TallyParams: gov.TallyParams{
|
||||||
|
Quorum: sdk.NewDecWithPrec(334, 3),
|
||||||
|
Threshold: sdk.NewDecWithPrec(5, 1),
|
||||||
|
Veto: sdk.NewDecWithPrec(334, 3),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated governance parameters:\n\t%+v\n", govGenesis)
|
||||||
|
|
||||||
|
stakingGenesis := staking.GenesisState{
|
||||||
|
Pool: staking.InitialPool(),
|
||||||
|
Params: staking.Params{
|
||||||
|
UnbondingTime: time.Duration(simulation.RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second,
|
||||||
|
MaxValidators: uint16(r.Intn(250) + 1),
|
||||||
|
BondDenom: sdk.DefaultBondDenom,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated staking parameters:\n\t%+v\n", stakingGenesis)
|
||||||
|
|
||||||
|
slashingGenesis := slashing.GenesisState{
|
||||||
|
Params: slashing.Params{
|
||||||
|
MaxEvidenceAge: stakingGenesis.Params.UnbondingTime,
|
||||||
|
SignedBlocksWindow: int64(simulation.RandIntBetween(r, 10, 1000)),
|
||||||
|
MinSignedPerWindow: sdk.NewDecWithPrec(int64(r.Intn(10)), 1),
|
||||||
|
DowntimeJailDuration: time.Duration(simulation.RandIntBetween(r, 60, 60*60*24)) * time.Second,
|
||||||
|
SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(50) + 1))),
|
||||||
|
SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(int64(r.Intn(200) + 1))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated slashing parameters:\n\t%+v\n", slashingGenesis)
|
||||||
|
|
||||||
|
mintGenesis := mint.GenesisState{
|
||||||
|
Minter: mint.InitialMinter(
|
||||||
|
sdk.NewDecWithPrec(int64(r.Intn(99)), 2)),
|
||||||
|
Params: mint.NewParams(
|
||||||
|
sdk.DefaultBondDenom,
|
||||||
|
sdk.NewDecWithPrec(int64(r.Intn(99)), 2),
|
||||||
|
sdk.NewDecWithPrec(20, 2),
|
||||||
|
sdk.NewDecWithPrec(7, 2),
|
||||||
|
sdk.NewDecWithPrec(67, 2),
|
||||||
|
uint64(60*60*8766/5)),
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated minting parameters:\n\t%+v\n", mintGenesis)
|
||||||
|
|
||||||
|
var validators []staking.Validator
|
||||||
|
var delegations []staking.Delegation
|
||||||
|
|
||||||
|
valAddrs := make([]sdk.ValAddress, numInitiallyBonded)
|
||||||
|
for i := 0; i < int(numInitiallyBonded); i++ {
|
||||||
|
valAddr := sdk.ValAddress(accs[i].Address)
|
||||||
|
valAddrs[i] = valAddr
|
||||||
|
|
||||||
|
validator := staking.NewValidator(valAddr, accs[i].PubKey, staking.Description{})
|
||||||
|
validator.Tokens = sdk.NewInt(amount)
|
||||||
|
validator.DelegatorShares = sdk.NewDec(amount)
|
||||||
|
delegation := staking.Delegation{accs[i].Address, valAddr, sdk.NewDec(amount)}
|
||||||
|
validators = append(validators, validator)
|
||||||
|
delegations = append(delegations, delegation)
|
||||||
|
}
|
||||||
|
|
||||||
|
stakingGenesis.Pool.NotBondedTokens = sdk.NewInt((amount * numAccs) + (numInitiallyBonded * amount))
|
||||||
|
stakingGenesis.Validators = validators
|
||||||
|
stakingGenesis.Delegations = delegations
|
||||||
|
|
||||||
|
distrGenesis := distr.GenesisState{
|
||||||
|
FeePool: distr.InitialFeePool(),
|
||||||
|
CommunityTax: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)),
|
||||||
|
BaseProposerReward: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)),
|
||||||
|
BonusProposerReward: sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(int64(r.Intn(30)), 2)),
|
||||||
|
}
|
||||||
|
fmt.Printf("Selected randomly generated distribution parameters:\n\t%+v\n", distrGenesis)
|
||||||
|
|
||||||
|
genesis := GenesisState{
|
||||||
|
Accounts: genesisAccounts,
|
||||||
|
AuthData: authGenesis,
|
||||||
|
BankData: bankGenesis,
|
||||||
|
StakingData: stakingGenesis,
|
||||||
|
MintData: mintGenesis,
|
||||||
|
DistrData: distrGenesis,
|
||||||
|
SlashingData: slashingGenesis,
|
||||||
|
GovData: govGenesis,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal genesis
|
||||||
|
appState, err := MakeCodec().MarshalJSON(genesis)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return appState, accs, "simulation"
|
||||||
|
}
|
||||||
|
|
||||||
|
func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) {
|
||||||
|
if genesisFile != "" {
|
||||||
|
return appStateFromGenesisFileFn(r, accs, genesisTimestamp)
|
||||||
|
}
|
||||||
|
return appStateRandomizedFn(r, accs, genesisTimestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
|
||||||
|
return []simulation.WeightedOperation{
|
||||||
|
{5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)},
|
||||||
|
{100, banksim.SimulateMsgSend(app.accountKeeper, app.bankKeeper)},
|
||||||
|
{10, banksim.SimulateSingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper)},
|
||||||
|
{50, distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper)},
|
||||||
|
{50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)},
|
||||||
|
{50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)},
|
||||||
|
{5, govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper)},
|
||||||
|
{100, govsim.SimulateMsgDeposit(app.govKeeper)},
|
||||||
|
{100, stakingsim.SimulateMsgCreateValidator(app.accountKeeper, app.stakingKeeper)},
|
||||||
|
{5, stakingsim.SimulateMsgEditValidator(app.stakingKeeper)},
|
||||||
|
{100, stakingsim.SimulateMsgDelegate(app.accountKeeper, app.stakingKeeper)},
|
||||||
|
{100, stakingsim.SimulateMsgUndelegate(app.accountKeeper, app.stakingKeeper)},
|
||||||
|
{100, stakingsim.SimulateMsgBeginRedelegate(app.accountKeeper, app.stakingKeeper)},
|
||||||
|
{100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func invariants(app *GaiaApp) []sdk.Invariant {
|
||||||
|
return []sdk.Invariant{
|
||||||
|
simulation.PeriodicInvariant(bank.NonnegativeBalanceInvariant(app.accountKeeper), period, 0),
|
||||||
|
simulation.PeriodicInvariant(distr.AllInvariants(app.distrKeeper, app.stakingKeeper), period, 0),
|
||||||
|
simulation.PeriodicInvariant(staking.AllInvariants(app.stakingKeeper, app.feeCollectionKeeper,
|
||||||
|
app.distrKeeper, app.accountKeeper), period, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass this in as an option to use a dbStoreAdapter instead of an IAVLStore for simulation speed.
|
||||||
|
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
||||||
|
bapp.SetFauxMerkleMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Profile with:
|
||||||
|
// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$ -SimulationCommit=true -cpuprofile cpu.out
|
||||||
|
func BenchmarkFullGaiaSimulation(b *testing.B) {
|
||||||
|
// Setup Gaia application
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
|
||||||
|
var db dbm.DB
|
||||||
|
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
|
||||||
|
db, _ = sdk.NewLevelDB("Simulation", dir)
|
||||||
|
defer func() {
|
||||||
|
db.Close()
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
app := NewGaiaApp(logger, db, nil, true, 0)
|
||||||
|
|
||||||
|
// Run randomized simulation
|
||||||
|
// TODO parameterize numbers, save for a later PR
|
||||||
|
_, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(b, app))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
b.Fail()
|
||||||
|
}
|
||||||
|
if commit {
|
||||||
|
fmt.Println("GoLevelDB Stats")
|
||||||
|
fmt.Println(db.Stats()["leveldb.stats"])
|
||||||
|
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullGaiaSimulation(t *testing.T) {
|
||||||
|
if !enabled {
|
||||||
|
t.Skip("Skipping Gaia simulation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Gaia application
|
||||||
|
var logger log.Logger
|
||||||
|
if verbose {
|
||||||
|
logger = log.TestingLogger()
|
||||||
|
} else {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
|
var db dbm.DB
|
||||||
|
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
|
||||||
|
db, _ = sdk.NewLevelDB("Simulation", dir)
|
||||||
|
defer func() {
|
||||||
|
db.Close()
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
app := NewGaiaApp(logger, db, nil, true, 0, fauxMerkleModeOpt)
|
||||||
|
require.Equal(t, "GaiaApp", app.Name())
|
||||||
|
|
||||||
|
// Run randomized simulation
|
||||||
|
_, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app))
|
||||||
|
if commit {
|
||||||
|
// for memdb:
|
||||||
|
// fmt.Println("Database Size", db.Stats()["database.size"])
|
||||||
|
fmt.Println("GoLevelDB Stats")
|
||||||
|
fmt.Println(db.Stats()["leveldb.stats"])
|
||||||
|
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaImportExport(t *testing.T) {
|
||||||
|
if !enabled {
|
||||||
|
t.Skip("Skipping Gaia import/export simulation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Gaia application
|
||||||
|
var logger log.Logger
|
||||||
|
if verbose {
|
||||||
|
logger = log.TestingLogger()
|
||||||
|
} else {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
|
var db dbm.DB
|
||||||
|
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
|
||||||
|
db, _ = sdk.NewLevelDB("Simulation", dir)
|
||||||
|
defer func() {
|
||||||
|
db.Close()
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
app := NewGaiaApp(logger, db, nil, true, 0, fauxMerkleModeOpt)
|
||||||
|
require.Equal(t, "GaiaApp", app.Name())
|
||||||
|
|
||||||
|
// Run randomized simulation
|
||||||
|
_, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app))
|
||||||
|
|
||||||
|
if commit {
|
||||||
|
// for memdb:
|
||||||
|
// fmt.Println("Database Size", db.Stats()["database.size"])
|
||||||
|
fmt.Println("GoLevelDB Stats")
|
||||||
|
fmt.Println(db.Stats()["leveldb.stats"])
|
||||||
|
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
fmt.Printf("Exporting genesis...\n")
|
||||||
|
|
||||||
|
appState, _, err := app.ExportAppStateAndValidators(false, []string{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
fmt.Printf("Importing genesis...\n")
|
||||||
|
|
||||||
|
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
|
||||||
|
newDB, _ := sdk.NewLevelDB("Simulation-2", dir)
|
||||||
|
defer func() {
|
||||||
|
newDB.Close()
|
||||||
|
os.RemoveAll(newDir)
|
||||||
|
}()
|
||||||
|
newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, 0, fauxMerkleModeOpt)
|
||||||
|
require.Equal(t, "GaiaApp", newApp.Name())
|
||||||
|
var genesisState GenesisState
|
||||||
|
err = app.cdc.UnmarshalJSON(appState, &genesisState)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ctxB := newApp.NewContext(true, abci.Header{})
|
||||||
|
newApp.initFromGenesisState(ctxB, genesisState)
|
||||||
|
|
||||||
|
fmt.Printf("Comparing stores...\n")
|
||||||
|
ctxA := app.NewContext(true, abci.Header{})
|
||||||
|
type StoreKeysPrefixes struct {
|
||||||
|
A sdk.StoreKey
|
||||||
|
B sdk.StoreKey
|
||||||
|
Prefixes [][]byte
|
||||||
|
}
|
||||||
|
storeKeysPrefixes := []StoreKeysPrefixes{
|
||||||
|
{app.keyMain, newApp.keyMain, [][]byte{}},
|
||||||
|
{app.keyAccount, newApp.keyAccount, [][]byte{}},
|
||||||
|
{app.keyStaking, newApp.keyStaking, [][]byte{staking.UnbondingQueueKey,
|
||||||
|
staking.RedelegationQueueKey, staking.ValidatorQueueKey}}, // ordering may change but it doesn't matter
|
||||||
|
{app.keySlashing, newApp.keySlashing, [][]byte{}},
|
||||||
|
{app.keyMint, newApp.keyMint, [][]byte{}},
|
||||||
|
{app.keyDistr, newApp.keyDistr, [][]byte{}},
|
||||||
|
{app.keyFeeCollection, newApp.keyFeeCollection, [][]byte{}},
|
||||||
|
{app.keyParams, newApp.keyParams, [][]byte{}},
|
||||||
|
{app.keyGov, newApp.keyGov, [][]byte{}},
|
||||||
|
}
|
||||||
|
for _, storeKeysPrefix := range storeKeysPrefixes {
|
||||||
|
storeKeyA := storeKeysPrefix.A
|
||||||
|
storeKeyB := storeKeysPrefix.B
|
||||||
|
prefixes := storeKeysPrefix.Prefixes
|
||||||
|
storeA := ctxA.KVStore(storeKeyA)
|
||||||
|
storeB := ctxB.KVStore(storeKeyB)
|
||||||
|
kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes)
|
||||||
|
fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB)
|
||||||
|
require.True(t, equal,
|
||||||
|
"unequal stores: %s / %s:\nstore A %X => %X\nstore B %X => %X",
|
||||||
|
storeKeyA, storeKeyB, kvA.Key, kvA.Value, kvB.Key, kvB.Value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGaiaSimulationAfterImport(t *testing.T) {
|
||||||
|
if !enabled {
|
||||||
|
t.Skip("Skipping Gaia simulation after import")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Gaia application
|
||||||
|
var logger log.Logger
|
||||||
|
if verbose {
|
||||||
|
logger = log.TestingLogger()
|
||||||
|
} else {
|
||||||
|
logger = log.NewNopLogger()
|
||||||
|
}
|
||||||
|
dir, _ := ioutil.TempDir("", "goleveldb-gaia-sim")
|
||||||
|
db, _ := sdk.NewLevelDB("Simulation", dir)
|
||||||
|
defer func() {
|
||||||
|
db.Close()
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
}()
|
||||||
|
app := NewGaiaApp(logger, db, nil, true, 0, fauxMerkleModeOpt)
|
||||||
|
require.Equal(t, "GaiaApp", app.Name())
|
||||||
|
|
||||||
|
// Run randomized simulation
|
||||||
|
stopEarly, err := simulation.SimulateFromSeed(getSimulateFromSeedInput(t, app))
|
||||||
|
|
||||||
|
if commit {
|
||||||
|
// for memdb:
|
||||||
|
// fmt.Println("Database Size", db.Stats()["database.size"])
|
||||||
|
fmt.Println("GoLevelDB Stats")
|
||||||
|
fmt.Println(db.Stats()["leveldb.stats"])
|
||||||
|
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
|
||||||
|
}
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
if stopEarly {
|
||||||
|
// we can't export or import a zero-validator genesis
|
||||||
|
fmt.Printf("We can't export or import a zero-validator genesis, exiting test...\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Exporting genesis...\n")
|
||||||
|
|
||||||
|
appState, _, err := app.ExportAppStateAndValidators(true, []string{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Importing genesis...\n")
|
||||||
|
|
||||||
|
newDir, _ := ioutil.TempDir("", "goleveldb-gaia-sim-2")
|
||||||
|
newDB, _ := sdk.NewLevelDB("Simulation-2", dir)
|
||||||
|
defer func() {
|
||||||
|
newDB.Close()
|
||||||
|
os.RemoveAll(newDir)
|
||||||
|
}()
|
||||||
|
newApp := NewGaiaApp(log.NewNopLogger(), newDB, nil, true, 0, fauxMerkleModeOpt)
|
||||||
|
require.Equal(t, "GaiaApp", newApp.Name())
|
||||||
|
newApp.InitChain(abci.RequestInitChain{
|
||||||
|
AppStateBytes: appState,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Run randomized simulation on imported app
|
||||||
|
_, err = simulation.SimulateFromSeed(getSimulateFromSeedInput(t, newApp))
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make another test for the fuzzer itself, which just has noOp txs
|
||||||
|
// and doesn't depend on gaia
|
||||||
|
func TestAppStateDeterminism(t *testing.T) {
|
||||||
|
if !enabled {
|
||||||
|
t.Skip("Skipping Gaia simulation")
|
||||||
|
}
|
||||||
|
|
||||||
|
numSeeds := 3
|
||||||
|
numTimesToRunPerSeed := 5
|
||||||
|
appHashList := make([]json.RawMessage, numTimesToRunPerSeed)
|
||||||
|
|
||||||
|
for i := 0; i < numSeeds; i++ {
|
||||||
|
seed := rand.Int63()
|
||||||
|
for j := 0; j < numTimesToRunPerSeed; j++ {
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
app := NewGaiaApp(logger, db, nil, true, 0)
|
||||||
|
|
||||||
|
// Run randomized simulation
|
||||||
|
simulation.SimulateFromSeed(
|
||||||
|
t, app.BaseApp, appStateFn, seed,
|
||||||
|
testAndRunTxs(app),
|
||||||
|
[]sdk.Invariant{},
|
||||||
|
50,
|
||||||
|
100,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
appHash := app.LastCommitID().Hash
|
||||||
|
appHashList[j] = appHash
|
||||||
|
}
|
||||||
|
for k := 1; k < numTimesToRunPerSeed; k++ {
|
||||||
|
require.Equal(t, appHashList[0], appHashList[k], "appHash list: %v", appHashList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
gaia/cli_test/README.md
Normal file
51
gaia/cli_test/README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Gaia CLI Integration tests
|
||||||
|
|
||||||
|
The gaia cli integration tests live in this folder. You can run the full suite by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go test -v -p 4 ./cmd/gaia/cli_test/...
|
||||||
|
# OR!
|
||||||
|
$ make test_cli
|
||||||
|
```
|
||||||
|
> NOTE: While the full suite runs in parallel, some of the tests can take up to a minute to complete
|
||||||
|
|
||||||
|
### Test Structure
|
||||||
|
|
||||||
|
This integration suite [uses a thin wrapper](https://godoc.org/github.com/cosmos/cosmos-sdk/tests) over the [`os/exec`](https://golang.org/pkg/os/exec/) package. This allows the integration test to run against built binaries (both `gaiad` and `gaiacli` are used) while being written in golang. This allows tests to take advantage of the various golang code we have for operations like marshal/unmarshal, crypto, etc...
|
||||||
|
|
||||||
|
> NOTE: The tests will use whatever `gaiad` or `gaiacli` binaries are available in your `$PATH`. You can check which binary will be run by the suite by running `which gaiad` or `which gaiacli`. If you have your `$GOPATH` properly setup they should be in `$GOPATH/bin/gaia*`. This will ensure that your test uses the latest binary you have built
|
||||||
|
|
||||||
|
Tests generally follow this structure:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestMyNewCommand(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
f := InitFixtures(t)
|
||||||
|
|
||||||
|
// start gaiad server
|
||||||
|
proc := f.GDStart()
|
||||||
|
defer proc.Stop(false)
|
||||||
|
|
||||||
|
// Your test code goes here...
|
||||||
|
|
||||||
|
f.Cleanup()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This boilerplate above:
|
||||||
|
- Ensures the tests run in parallel. Because the tests are calling out to `os/exec` for many operations these tests can take a long time to run.
|
||||||
|
- Creates `.gaiad` and `.gaiacli` folders in a new temp folder.
|
||||||
|
- Uses `gaiacli` to create 2 accounts for use in testing: `foo` and `bar`
|
||||||
|
- Creates a genesis file with coins (`1000footoken,1000feetoken,150stake`) controlled by the `foo` key
|
||||||
|
- Generates an initial bonding transaction (`gentx`) to make the `foo` key a validator at genesis
|
||||||
|
- Starts `gaiad` and stops it once the test exits
|
||||||
|
- Cleans up test state on a successful run
|
||||||
|
|
||||||
|
### Notes when adding/running tests
|
||||||
|
|
||||||
|
- Because the tests run against a built binary, you should make sure you build every time the code changes and you want to test again, otherwise you will be testing against an older version. If you are adding new tests this can easily lead to confusing test results.
|
||||||
|
- The [`test_helpers.go`](./test_helpers.go) file is organized according to the format of `gaiacli` and `gaiad` commands. There are comments with section headers describing the different areas. Helper functions to call CLI functionality are generally named after the command (e.g. `gaiacli query staking validator` would be `QueryStakingValidator`). Try to keep functions grouped by their position in the command tree.
|
||||||
|
- Test state that is needed by `tx` and `query` commands (`home`, `chain_id`, etc...) is stored on the `Fixtures` object. This makes constructing your new tests almost trivial.
|
||||||
|
- Sometimes if you exit a test early there can be still running `gaiad` and `gaiacli` processes that will interrupt subsequent runs. Still running `gaiacli` processes will block access to the keybase while still running `gaiad` processes will block ports and prevent new tests from spinning up. You can ensure new tests spin up clean by running `pkill -9 gaiad && pkill -9 gaiacli` before each test run.
|
||||||
|
- Most `query` and `tx` commands take a variadic `flags` argument. This pattern allows for the creation of a general function which is easily modified by adding flags. See the `TxSend` function and its use for a good example.
|
||||||
|
- `Tx*` functions follow a general pattern and return `(success bool, stdout string, stderr string)`. This allows for easy testing of multiple different flag configurations. See `TestGaiaCLICreateValidator` or `TestGaiaCLISubmitProposal` for a good example of the pattern.
|
1093
gaia/cli_test/cli_test.go
Normal file
1093
gaia/cli_test/cli_test.go
Normal file
File diff suppressed because it is too large
Load Diff
3
gaia/cli_test/doc.go
Normal file
3
gaia/cli_test/doc.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package clitest
|
||||||
|
|
||||||
|
// package clitest runs integration tests which make use of CLI commands.
|
685
gaia/cli_test/test_helpers.go
Normal file
685
gaia/cli_test/test_helpers.go
Normal file
@ -0,0 +1,685 @@
|
|||||||
|
package clitest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
|
||||||
|
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
denom = "stake"
|
||||||
|
keyFoo = "foo"
|
||||||
|
keyBar = "bar"
|
||||||
|
fooDenom = "footoken"
|
||||||
|
feeDenom = "feetoken"
|
||||||
|
fee2Denom = "fee2token"
|
||||||
|
keyBaz = "baz"
|
||||||
|
keyVesting = "vesting"
|
||||||
|
keyFooBarBaz = "foobarbaz"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
startCoins = sdk.Coins{
|
||||||
|
sdk.NewCoin(feeDenom, sdk.TokensFromTendermintPower(1000000)),
|
||||||
|
sdk.NewCoin(fee2Denom, sdk.TokensFromTendermintPower(1000000)),
|
||||||
|
sdk.NewCoin(fooDenom, sdk.TokensFromTendermintPower(1000)),
|
||||||
|
sdk.NewCoin(denom, sdk.TokensFromTendermintPower(150)),
|
||||||
|
}
|
||||||
|
|
||||||
|
vestingCoins = sdk.Coins{
|
||||||
|
sdk.NewCoin(feeDenom, sdk.TokensFromTendermintPower(500000)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// Fixtures
|
||||||
|
|
||||||
|
// Fixtures is used to setup the testing environment
|
||||||
|
type Fixtures struct {
|
||||||
|
ChainID string
|
||||||
|
RPCAddr string
|
||||||
|
Port string
|
||||||
|
GDHome string
|
||||||
|
GCLIHome string
|
||||||
|
P2PAddr string
|
||||||
|
T *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFixtures creates a new instance of Fixtures with many vars set
|
||||||
|
func NewFixtures(t *testing.T) *Fixtures {
|
||||||
|
tmpDir, err := ioutil.TempDir("", "gaia_integration_"+t.Name()+"_")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
servAddr, port, err := server.FreeTCPAddr()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
p2pAddr, _, err := server.FreeTCPAddr()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return &Fixtures{
|
||||||
|
T: t,
|
||||||
|
GDHome: filepath.Join(tmpDir, ".gaiad"),
|
||||||
|
GCLIHome: filepath.Join(tmpDir, ".gaiacli"),
|
||||||
|
RPCAddr: servAddr,
|
||||||
|
P2PAddr: p2pAddr,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisFile returns the path of the genesis file
|
||||||
|
func (f Fixtures) GenesisFile() string {
|
||||||
|
return filepath.Join(f.GDHome, "config", "genesis.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisFile returns the application's genesis state
|
||||||
|
func (f Fixtures) GenesisState() app.GenesisState {
|
||||||
|
cdc := codec.New()
|
||||||
|
genDoc, err := appInit.LoadGenesisDoc(cdc, f.GenesisFile())
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
|
||||||
|
var appState app.GenesisState
|
||||||
|
require.NoError(f.T, cdc.UnmarshalJSON(genDoc.AppState, &appState))
|
||||||
|
return appState
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitFixtures is called at the beginning of a test and initializes a chain
|
||||||
|
// with 1 validator.
|
||||||
|
func InitFixtures(t *testing.T) (f *Fixtures) {
|
||||||
|
f = NewFixtures(t)
|
||||||
|
|
||||||
|
// reset test state
|
||||||
|
f.UnsafeResetAll()
|
||||||
|
|
||||||
|
// ensure keystore has foo and bar keys
|
||||||
|
f.KeysDelete(keyFoo)
|
||||||
|
f.KeysDelete(keyBar)
|
||||||
|
f.KeysDelete(keyBar)
|
||||||
|
f.KeysDelete(keyFooBarBaz)
|
||||||
|
f.KeysAdd(keyFoo)
|
||||||
|
f.KeysAdd(keyBar)
|
||||||
|
f.KeysAdd(keyBaz)
|
||||||
|
f.KeysAdd(keyVesting)
|
||||||
|
f.KeysAdd(keyFooBarBaz, "--multisig-threshold=2", fmt.Sprintf(
|
||||||
|
"--multisig=%s,%s,%s", keyFoo, keyBar, keyBaz))
|
||||||
|
|
||||||
|
// ensure that CLI output is in JSON format
|
||||||
|
f.CLIConfig("output", "json")
|
||||||
|
|
||||||
|
// NOTE: GDInit sets the ChainID
|
||||||
|
f.GDInit(keyFoo)
|
||||||
|
|
||||||
|
f.CLIConfig("chain-id", f.ChainID)
|
||||||
|
f.CLIConfig("broadcast-mode", "block")
|
||||||
|
|
||||||
|
// start an account with tokens
|
||||||
|
f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins)
|
||||||
|
f.AddGenesisAccount(
|
||||||
|
f.KeyAddress(keyVesting), startCoins,
|
||||||
|
fmt.Sprintf("--vesting-amount=%s", vestingCoins),
|
||||||
|
fmt.Sprintf("--vesting-start-time=%d", time.Now().UTC().UnixNano()),
|
||||||
|
fmt.Sprintf("--vesting-end-time=%d", time.Now().Add(60*time.Second).UTC().UnixNano()),
|
||||||
|
)
|
||||||
|
|
||||||
|
f.GenTx(keyFoo)
|
||||||
|
f.CollectGenTxs()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup is meant to be run at the end of a test to clean up an remaining test state
|
||||||
|
func (f *Fixtures) Cleanup(dirs ...string) {
|
||||||
|
clean := append(dirs, f.GDHome, f.GCLIHome)
|
||||||
|
for _, d := range clean {
|
||||||
|
err := os.RemoveAll(d)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags returns the flags necessary for making most CLI calls
|
||||||
|
func (f *Fixtures) Flags() string {
|
||||||
|
return fmt.Sprintf("--home=%s --node=%s", f.GCLIHome, f.RPCAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiad
|
||||||
|
|
||||||
|
// UnsafeResetAll is gaiad unsafe-reset-all
|
||||||
|
func (f *Fixtures) UnsafeResetAll(flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad --home=%s unsafe-reset-all", f.GDHome)
|
||||||
|
executeWrite(f.T, addFlags(cmd, flags))
|
||||||
|
err := os.RemoveAll(filepath.Join(f.GDHome, "config", "gentx"))
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GDInit is gaiad init
|
||||||
|
// NOTE: GDInit sets the ChainID for the Fixtures instance
|
||||||
|
func (f *Fixtures) GDInit(moniker string, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad init -o --home=%s %s", f.GDHome, moniker)
|
||||||
|
_, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
|
||||||
|
var chainID string
|
||||||
|
var initRes map[string]json.RawMessage
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(stderr), &initRes)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
|
||||||
|
err = json.Unmarshal(initRes["chain_id"], &chainID)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
|
||||||
|
f.ChainID = chainID
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGenesisAccount is gaiad add-genesis-account
|
||||||
|
func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad add-genesis-account %s %s --home=%s", address, coins, f.GDHome)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenTx is gaiad gentx
|
||||||
|
func (f *Fixtures) GenTx(name string, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad gentx --name=%s --home=%s --home-client=%s", name, f.GDHome, f.GCLIHome)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectGenTxs is gaiad collect-gentxs
|
||||||
|
func (f *Fixtures) CollectGenTxs(flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad collect-gentxs --home=%s", f.GDHome)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GDStart runs gaiad start with the appropriate flags and returns a process
|
||||||
|
func (f *Fixtures) GDStart(flags ...string) *tests.Process {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.GDHome, f.RPCAddr, f.P2PAddr)
|
||||||
|
proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags))
|
||||||
|
tests.WaitForTMStart(f.Port)
|
||||||
|
tests.WaitForNextNBlocksTM(1, f.Port)
|
||||||
|
return proc
|
||||||
|
}
|
||||||
|
|
||||||
|
// GDTendermint returns the results of gaiad tendermint [query]
|
||||||
|
func (f *Fixtures) GDTendermint(query string) string {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad tendermint %s --home=%s", query, f.GDHome)
|
||||||
|
success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd)
|
||||||
|
require.Empty(f.T, stderr)
|
||||||
|
require.True(f.T, success)
|
||||||
|
return strings.TrimSpace(stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateGenesis runs gaiad validate-genesis
|
||||||
|
func (f *Fixtures) ValidateGenesis() {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiad validate-genesis --home=%s", f.GDHome)
|
||||||
|
executeWriteCheckErr(f.T, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli keys
|
||||||
|
|
||||||
|
// KeysDelete is gaiacli keys delete
|
||||||
|
func (f *Fixtures) KeysDelete(name string, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli keys delete --home=%s %s", f.GCLIHome, name)
|
||||||
|
executeWrite(f.T, addFlags(cmd, append(append(flags, "-y"), "-f")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysAdd is gaiacli keys add
|
||||||
|
func (f *Fixtures) KeysAdd(name string, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s %s", f.GCLIHome, name)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysAddRecover prepares gaiacli keys add --recover
|
||||||
|
func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) (exitSuccess bool, stdout, stderr string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli keys add --home=%s --recover %s", f.GCLIHome, name)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.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("../../../build/gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeysShow is gaiacli keys show
|
||||||
|
func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli keys show --home=%s %s", f.GCLIHome, name)
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var ko keys.KeyOutput
|
||||||
|
err := clientkeys.UnmarshalJSON([]byte(out), &ko)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
return ko
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyAddress returns the SDK account address from the key
|
||||||
|
func (f *Fixtures) KeyAddress(name string) sdk.AccAddress {
|
||||||
|
ko := f.KeysShow(name)
|
||||||
|
accAddr, err := sdk.AccAddressFromBech32(ko.Address)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
return accAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli config
|
||||||
|
|
||||||
|
// CLIConfig is gaiacli config
|
||||||
|
func (f *Fixtures) CLIConfig(key, value string, flags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli config --home=%s %s %s", f.GCLIHome, key, value)
|
||||||
|
executeWriteCheckErr(f.T, addFlags(cmd, flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli tx send/sign/broadcast
|
||||||
|
|
||||||
|
// 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("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fixtures) txSendWithConfirm(
|
||||||
|
from string, to sdk.AccAddress, amount sdk.Coin, confirm string, flags ...string,
|
||||||
|
) (bool, string, string) {
|
||||||
|
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx send %s %s %v --from=%s", to, amount, f.Flags(), from)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), confirm, app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxSign is gaiacli tx sign
|
||||||
|
func (f *Fixtures) TxSign(signer, fileName string, flags ...string) (bool, string, string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx sign %v --from=%s %v", f.Flags(), signer, fileName)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxBroadcast is gaiacli tx broadcast
|
||||||
|
func (f *Fixtures) TxBroadcast(fileName string, flags ...string) (bool, string, string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx broadcast %v %v", f.Flags(), fileName)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxEncode is gaiacli tx encode
|
||||||
|
func (f *Fixtures) TxEncode(fileName string, flags ...string) (bool, string, string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx encode %v %v", f.Flags(), fileName)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxMultisign is gaiacli tx multisign
|
||||||
|
func (f *Fixtures) TxMultisign(fileName, name string, signaturesFiles []string,
|
||||||
|
flags ...string) (bool, string, string) {
|
||||||
|
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx multisign %v %s %s %s", f.Flags(),
|
||||||
|
fileName, name, strings.Join(signaturesFiles, " "),
|
||||||
|
)
|
||||||
|
return executeWriteRetStdStreams(f.T, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli tx staking
|
||||||
|
|
||||||
|
// TxStakingCreateValidator is gaiacli tx staking create-validator
|
||||||
|
func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.Coin, flags ...string) (bool, string, string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey)
|
||||||
|
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), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxStakingUnbond is gaiacli tx staking unbond
|
||||||
|
func (f *Fixtures) TxStakingUnbond(from, shares string, validator sdk.ValAddress, flags ...string) bool {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx staking unbond %s %v --from=%s %v", validator, shares, from, f.Flags())
|
||||||
|
return executeWrite(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli tx gov
|
||||||
|
|
||||||
|
// TxGovSubmitProposal is gaiacli tx gov submit-proposal
|
||||||
|
func (f *Fixtures) TxGovSubmitProposal(from, typ, title, description string, deposit sdk.Coin, flags ...string) (bool, string, string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli tx gov submit-proposal %v --from=%s --type=%s", f.Flags(), from, typ)
|
||||||
|
cmd += fmt.Sprintf(" --title=%s --description=%s --deposit=%s", title, description, deposit)
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.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("../../../build/gaiacli tx gov deposit %d %s --from=%s %v", proposalID, amount, from, f.Flags())
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.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("../../../build/gaiacli tx gov vote %d %s --from=%s %v", proposalID, option, from, f.Flags())
|
||||||
|
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli query account
|
||||||
|
|
||||||
|
// QueryAccount is gaiacli query account
|
||||||
|
func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.BaseAccount {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query account %s %v", address, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var initRes map[string]json.RawMessage
|
||||||
|
err := json.Unmarshal([]byte(out), &initRes)
|
||||||
|
require.NoError(f.T, err, "out %v, err %v", out, err)
|
||||||
|
value := initRes["value"]
|
||||||
|
var acc auth.BaseAccount
|
||||||
|
cdc := codec.New()
|
||||||
|
codec.RegisterCrypto(cdc)
|
||||||
|
err = cdc.UnmarshalJSON(value, &acc)
|
||||||
|
require.NoError(f.T, err, "value %v, err %v", string(value), err)
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli query txs
|
||||||
|
|
||||||
|
// QueryTxs is gaiacli query txs
|
||||||
|
func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
var txs []sdk.TxResponse
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &txs)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryTxsInvalid query txs with wrong parameters and compare expected error
|
||||||
|
func (f *Fixtures) QueryTxsInvalid(expectedErr error, page, limit int, tags ...string) {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags())
|
||||||
|
_, err := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
require.EqualError(f.T, expectedErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli query staking
|
||||||
|
|
||||||
|
// QueryStakingValidator is gaiacli query staking validator
|
||||||
|
func (f *Fixtures) QueryStakingValidator(valAddr sdk.ValAddress, flags ...string) staking.Validator {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query staking validator %s %v", valAddr, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var validator staking.Validator
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &validator)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return validator
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryStakingUnbondingDelegationsFrom is gaiacli query staking unbonding-delegations-from
|
||||||
|
func (f *Fixtures) QueryStakingUnbondingDelegationsFrom(valAddr sdk.ValAddress, flags ...string) []staking.UnbondingDelegation {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query staking unbonding-delegations-from %s %v", valAddr, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var ubds []staking.UnbondingDelegation
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &ubds)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return ubds
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryStakingDelegationsTo is gaiacli query staking delegations-to
|
||||||
|
func (f *Fixtures) QueryStakingDelegationsTo(valAddr sdk.ValAddress, flags ...string) []staking.Delegation {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query staking delegations-to %s %v", valAddr, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var delegations []staking.Delegation
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &delegations)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return delegations
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryStakingPool is gaiacli query staking pool
|
||||||
|
func (f *Fixtures) QueryStakingPool(flags ...string) staking.Pool {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query staking pool %v", f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var pool staking.Pool
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &pool)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryStakingParameters is gaiacli query staking parameters
|
||||||
|
func (f *Fixtures) QueryStakingParameters(flags ...string) staking.Params {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query staking params %v", f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var params staking.Params
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), ¶ms)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// gaiacli query gov
|
||||||
|
|
||||||
|
// QueryGovParamDeposit is gaiacli query gov param deposit
|
||||||
|
func (f *Fixtures) QueryGovParamDeposit() gov.DepositParams {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov param deposit %s", f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
var depositParam gov.DepositParams
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &depositParam)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return depositParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovParamVoting is gaiacli query gov param voting
|
||||||
|
func (f *Fixtures) QueryGovParamVoting() gov.VotingParams {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov param voting %s", f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
var votingParam gov.VotingParams
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &votingParam)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return votingParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovParamTallying is gaiacli query gov param tallying
|
||||||
|
func (f *Fixtures) QueryGovParamTallying() gov.TallyParams {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov param tallying %s", f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
var tallyingParam gov.TallyParams
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &tallyingParam)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return tallyingParam
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovProposals is gaiacli query gov proposals
|
||||||
|
func (f *Fixtures) QueryGovProposals(flags ...string) gov.Proposals {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov proposals %v", f.Flags())
|
||||||
|
stdout, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
if strings.Contains(stderr, "No matching proposals found") {
|
||||||
|
return gov.Proposals{}
|
||||||
|
}
|
||||||
|
require.Empty(f.T, stderr)
|
||||||
|
var out gov.Proposals
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(stdout), &out)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovProposal is gaiacli query gov proposal
|
||||||
|
func (f *Fixtures) QueryGovProposal(proposalID int, flags ...string) gov.Proposal {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov proposal %d %v", proposalID, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var proposal gov.Proposal
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &proposal)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return proposal
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovVote is gaiacli query gov vote
|
||||||
|
func (f *Fixtures) QueryGovVote(proposalID int, voter sdk.AccAddress, flags ...string) gov.Vote {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov vote %d %s %v", proposalID, voter, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var vote gov.Vote
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &vote)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return vote
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovVotes is gaiacli query gov votes
|
||||||
|
func (f *Fixtures) QueryGovVotes(proposalID int, flags ...string) []gov.Vote {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov votes %d %v", proposalID, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var votes []gov.Vote
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &votes)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return votes
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovDeposit is gaiacli query gov deposit
|
||||||
|
func (f *Fixtures) QueryGovDeposit(proposalID int, depositor sdk.AccAddress, flags ...string) gov.Deposit {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov deposit %d %s %v", proposalID, depositor, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var deposit gov.Deposit
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &deposit)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return deposit
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryGovDeposits is gaiacli query gov deposits
|
||||||
|
func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposit {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query gov deposits %d %v", propsalID, f.Flags())
|
||||||
|
out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "")
|
||||||
|
var deposits []gov.Deposit
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
err := cdc.UnmarshalJSON([]byte(out), &deposits)
|
||||||
|
require.NoError(f.T, err, "out %v\n, err %v", out, err)
|
||||||
|
return deposits
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// query slashing
|
||||||
|
|
||||||
|
// QuerySigningInfo returns the signing info for a validator
|
||||||
|
func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query slashing signing-info %s %s", val, f.Flags())
|
||||||
|
res, errStr := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
require.Empty(f.T, errStr)
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
var sinfo slashing.ValidatorSigningInfo
|
||||||
|
err := cdc.UnmarshalJSON([]byte(res), &sinfo)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
return sinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuerySlashingParams is gaiacli query slashing params
|
||||||
|
func (f *Fixtures) QuerySlashingParams() slashing.Params {
|
||||||
|
cmd := fmt.Sprintf("../../../build/gaiacli query slashing params %s", f.Flags())
|
||||||
|
res, errStr := tests.ExecuteT(f.T, cmd, "")
|
||||||
|
require.Empty(f.T, errStr)
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
var params slashing.Params
|
||||||
|
err := cdc.UnmarshalJSON([]byte(res), ¶ms)
|
||||||
|
require.NoError(f.T, err)
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// executors
|
||||||
|
|
||||||
|
func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) {
|
||||||
|
require.True(t, executeWrite(t, cmdStr, writes...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) {
|
||||||
|
exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) {
|
||||||
|
proc := tests.GoExecuteT(t, cmdStr)
|
||||||
|
|
||||||
|
// Enables use of interactive commands
|
||||||
|
for _, write := range writes {
|
||||||
|
_, err := proc.StdinPipe.Write([]byte(write + "\n"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read both stdout and stderr from the process
|
||||||
|
stdout, stderr, err := proc.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Err on proc.ReadAll()", err, cmdStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log output.
|
||||||
|
if len(stdout) > 0 {
|
||||||
|
t.Log("Stdout:", cmn.Green(string(stdout)))
|
||||||
|
}
|
||||||
|
if len(stderr) > 0 {
|
||||||
|
t.Log("Stderr:", cmn.Red(string(stderr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for process to exit
|
||||||
|
proc.Wait()
|
||||||
|
|
||||||
|
// Return succes, stdout, stderr
|
||||||
|
return proc.ExitState.Success(), string(stdout), string(stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// utils
|
||||||
|
|
||||||
|
func addFlags(cmd string, flags []string) string {
|
||||||
|
for _, f := range flags {
|
||||||
|
cmd += " " + f
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryTags(tags []string) (out string) {
|
||||||
|
for _, tag := range tags {
|
||||||
|
out += tag + "&"
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(out, "&")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the given string to a new temporary file
|
||||||
|
func WriteToNewTempFile(t *testing.T, s string) *os.File {
|
||||||
|
fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_")
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = fp.WriteString(s)
|
||||||
|
require.Nil(t, err)
|
||||||
|
return fp
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalStdTx(t *testing.T, stdTx auth.StdTx) []byte {
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
bz, err := cdc.MarshalBinaryBare(stdTx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return bz
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalStdTx(t *testing.T, s string) (stdTx auth.StdTx) {
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
require.Nil(t, cdc.UnmarshalJSON([]byte(s), &stdTx))
|
||||||
|
return
|
||||||
|
}
|
@ -1,32 +1,52 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||||
|
|
||||||
|
"github.com/rakyll/statik/fs"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
"github.com/cosmos/cosmos-sdk/client/lcd"
|
||||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
"github.com/cosmos/cosmos-sdk/version"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
|
at "github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||||
|
bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest"
|
||||||
|
dist "github.com/cosmos/cosmos-sdk/x/distribution/client/rest"
|
||||||
|
gv "github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
|
gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
|
||||||
|
mintrest "github.com/cosmos/cosmos-sdk/x/mint/client/rest"
|
||||||
|
sl "github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
|
||||||
|
st "github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
staking "github.com/cosmos/cosmos-sdk/x/staking/client/rest"
|
||||||
|
|
||||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
||||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli"
|
||||||
|
crisisclient "github.com/cosmos/cosmos-sdk/x/crisis/client"
|
||||||
|
distcmd "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
|
distClient "github.com/cosmos/cosmos-sdk/x/distribution/client"
|
||||||
|
govClient "github.com/cosmos/cosmos-sdk/x/gov/client"
|
||||||
|
mintclient "github.com/cosmos/cosmos-sdk/x/mint/client"
|
||||||
|
slashingclient "github.com/cosmos/cosmos-sdk/x/slashing/client"
|
||||||
|
stakingclient "github.com/cosmos/cosmos-sdk/x/staking/client"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
_ "github.com/cosmos/cosmos-sdk/client/lcd/statik"
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
|
||||||
|
|
||||||
"github.com/kava-labs/_/app"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -38,16 +58,29 @@ func main() {
|
|||||||
|
|
||||||
// Read in the configuration file for the sdk
|
// Read in the configuration file for the sdk
|
||||||
config := sdk.GetConfig()
|
config := sdk.GetConfig()
|
||||||
app.SetAddressPrefixes(config)
|
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||||
|
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||||
|
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||||
config.Seal()
|
config.Seal()
|
||||||
|
|
||||||
// TODO: setup keybase, viper object, etc. to be passed into
|
// TODO: setup keybase, viper object, etc. to be passed into
|
||||||
// the below functions and eliminate global vars, like we do
|
// the below functions and eliminate global vars, like we do
|
||||||
// with the cdc
|
// with the cdc
|
||||||
|
|
||||||
|
// Module clients hold cli commnads (tx,query) and lcd routes
|
||||||
|
// TODO: Make the lcd command take a list of ModuleClient
|
||||||
|
mc := []sdk.ModuleClients{
|
||||||
|
govClient.NewModuleClient(gv.StoreKey, cdc),
|
||||||
|
distClient.NewModuleClient(distcmd.StoreKey, cdc),
|
||||||
|
stakingclient.NewModuleClient(st.StoreKey, cdc),
|
||||||
|
mintclient.NewModuleClient(mint.StoreKey, cdc),
|
||||||
|
slashingclient.NewModuleClient(sl.StoreKey, cdc),
|
||||||
|
crisisclient.NewModuleClient(sl.StoreKey, cdc),
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: "kvcli",
|
Use: "gaiacli",
|
||||||
Short: "Command line interface for interacting with kvd",
|
Short: "Command line interface for interacting with gaiad",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add --chain-id to persistent flags and mark it required
|
// Add --chain-id to persistent flags and mark it required
|
||||||
@ -60,14 +93,14 @@ func main() {
|
|||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
rpc.StatusCommand(),
|
rpc.StatusCommand(),
|
||||||
client.ConfigCmd(app.DefaultCLIHome),
|
client.ConfigCmd(app.DefaultCLIHome),
|
||||||
queryCmd(cdc),
|
queryCmd(cdc, mc),
|
||||||
txCmd(cdc),
|
txCmd(cdc, mc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
lcd.ServeCommand(cdc, registerRoutes),
|
lcd.ServeCommand(cdc, registerRoutes),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
keys.Commands(),
|
keys.Commands(),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
version.Cmd,
|
version.VersionCmd,
|
||||||
client.NewCompletionCmd(rootCmd, true),
|
client.NewCompletionCmd(rootCmd, true),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,7 +114,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryCmd(cdc *amino.Codec) *cobra.Command {
|
func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
|
||||||
queryCmd := &cobra.Command{
|
queryCmd := &cobra.Command{
|
||||||
Use: "query",
|
Use: "query",
|
||||||
Aliases: []string{"q"},
|
Aliases: []string{"q"},
|
||||||
@ -89,22 +122,25 @@ func queryCmd(cdc *amino.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queryCmd.AddCommand(
|
queryCmd.AddCommand(
|
||||||
authcmd.GetAccountCmd(cdc),
|
|
||||||
client.LineBreak,
|
|
||||||
rpc.ValidatorCommand(cdc),
|
rpc.ValidatorCommand(cdc),
|
||||||
rpc.BlockCommand(),
|
rpc.BlockCommand(),
|
||||||
tx.SearchTxCmd(cdc),
|
tx.SearchTxCmd(cdc),
|
||||||
tx.QueryTxCmd(cdc),
|
tx.QueryTxCmd(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
|
authcmd.GetAccountCmd(at.StoreKey, cdc),
|
||||||
)
|
)
|
||||||
|
|
||||||
// add modules' query commands
|
for _, m := range mc {
|
||||||
app.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
mQueryCmd := m.GetQueryCmd()
|
||||||
|
if mQueryCmd != nil {
|
||||||
|
queryCmd.AddCommand(mQueryCmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return queryCmd
|
return queryCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func txCmd(cdc *amino.Codec) *cobra.Command {
|
func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
|
||||||
txCmd := &cobra.Command{
|
txCmd := &cobra.Command{
|
||||||
Use: "tx",
|
Use: "tx",
|
||||||
Short: "Transactions subcommands",
|
Short: "Transactions subcommands",
|
||||||
@ -115,26 +151,15 @@ func txCmd(cdc *amino.Codec) *cobra.Command {
|
|||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
authcmd.GetSignCommand(cdc),
|
authcmd.GetSignCommand(cdc),
|
||||||
authcmd.GetMultiSignCommand(cdc),
|
authcmd.GetMultiSignCommand(cdc),
|
||||||
client.LineBreak,
|
|
||||||
tx.GetBroadcastCommand(cdc),
|
tx.GetBroadcastCommand(cdc),
|
||||||
tx.GetEncodeCommand(cdc),
|
tx.GetEncodeCommand(cdc),
|
||||||
client.LineBreak,
|
client.LineBreak,
|
||||||
)
|
)
|
||||||
|
|
||||||
// add modules' tx commands
|
for _, m := range mc {
|
||||||
app.ModuleBasics.AddTxCommands(txCmd, cdc)
|
txCmd.AddCommand(m.GetTxCmd())
|
||||||
|
|
||||||
// remove auth and bank commands as they're mounted under the root tx command
|
|
||||||
var cmdsToRemove []*cobra.Command
|
|
||||||
|
|
||||||
for _, cmd := range txCmd.Commands() {
|
|
||||||
if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName {
|
|
||||||
cmdsToRemove = append(cmdsToRemove, cmd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
txCmd.RemoveCommand(cmdsToRemove...)
|
|
||||||
|
|
||||||
return txCmd
|
return txCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,8 +167,25 @@ func txCmd(cdc *amino.Codec) *cobra.Command {
|
|||||||
// NOTE: details on the routes added for each module are in the module documentation
|
// NOTE: details on the routes added for each module are in the module documentation
|
||||||
// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go
|
// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go
|
||||||
func registerRoutes(rs *lcd.RestServer) {
|
func registerRoutes(rs *lcd.RestServer) {
|
||||||
client.RegisterRoutes(rs.CliCtx, rs.Mux)
|
registerSwaggerUI(rs)
|
||||||
app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux)
|
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
|
||||||
|
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
|
||||||
|
auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey)
|
||||||
|
bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
||||||
|
dist.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distcmd.StoreKey)
|
||||||
|
staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
||||||
|
slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase)
|
||||||
|
gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
|
||||||
|
mintrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerSwaggerUI(rs *lcd.RestServer) {
|
||||||
|
statikFS, err := fs.New()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
staticServer := http.FileServer(statikFS)
|
||||||
|
rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer))
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig(cmd *cobra.Command) error {
|
func initConfig(cmd *cobra.Command) error {
|
@ -1,6 +1,3 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -10,22 +7,19 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
|
|
||||||
genaccscli "github.com/cosmos/cosmos-sdk/x/auth/genaccounts/client/cli"
|
|
||||||
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/kava-labs/_/app"
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
gaiaInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gaiad custom flags
|
// gaiad custom flags
|
||||||
@ -37,45 +31,45 @@ func main() {
|
|||||||
cdc := app.MakeCodec()
|
cdc := app.MakeCodec()
|
||||||
|
|
||||||
config := sdk.GetConfig()
|
config := sdk.GetConfig()
|
||||||
app.SetAddressPrefixes(config)
|
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||||
|
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||||
|
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||||
config.Seal()
|
config.Seal()
|
||||||
|
|
||||||
ctx := server.NewDefaultContext()
|
ctx := server.NewDefaultContext()
|
||||||
cobra.EnableCommandSorting = false
|
cobra.EnableCommandSorting = false
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: "kvd",
|
Use: "gaiad",
|
||||||
Short: "Kava Daemon (server)",
|
Short: "Gaia Daemon (server)",
|
||||||
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
PersistentPreRunE: server.PersistentPreRunEFn(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.AddCommand(genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome))
|
rootCmd.AddCommand(gaiaInit.InitCmd(ctx, cdc))
|
||||||
rootCmd.AddCommand(genutilcli.CollectGenTxsCmd(ctx, cdc, genaccounts.AppModuleBasic{}, app.DefaultNodeHome))
|
rootCmd.AddCommand(gaiaInit.CollectGenTxsCmd(ctx, cdc))
|
||||||
rootCmd.AddCommand(genutilcli.GenTxCmd(ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{},
|
rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc))
|
||||||
genaccounts.AppModuleBasic{}, app.DefaultNodeHome, app.DefaultCLIHome))
|
rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc))
|
||||||
rootCmd.AddCommand(genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics))
|
rootCmd.AddCommand(gaiaInit.AddGenesisAccountCmd(ctx, cdc))
|
||||||
rootCmd.AddCommand(genaccscli.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome))
|
rootCmd.AddCommand(gaiaInit.ValidateGenesisCmd(ctx, cdc))
|
||||||
rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true))
|
rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true))
|
||||||
rootCmd.AddCommand(testnetCmd(ctx, cdc, app.ModuleBasics, genaccounts.AppModuleBasic{}))
|
|
||||||
rootCmd.AddCommand(replayCmd())
|
|
||||||
|
|
||||||
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
|
server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators)
|
||||||
|
|
||||||
// prepare and add flags
|
// prepare and add flags
|
||||||
executor := cli.PrepareBaseCmd(rootCmd, "KA", app.DefaultNodeHome)
|
executor := cli.PrepareBaseCmd(rootCmd, "GA", app.DefaultNodeHome)
|
||||||
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
|
rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod,
|
||||||
0, "Assert registered invariants every N blocks")
|
0, "Assert registered invariants every N blocks")
|
||||||
err := executor.Execute()
|
err := executor.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// handle with #870
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application {
|
||||||
return app.NewApp(
|
return app.NewGaiaApp(
|
||||||
logger, db, traceStore, true, invCheckPeriod,
|
logger, db, traceStore, true, invCheckPeriod,
|
||||||
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
|
baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))),
|
||||||
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
|
baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)),
|
||||||
baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,13 +78,13 @@ func exportAppStateAndTMValidators(
|
|||||||
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
) (json.RawMessage, []tmtypes.GenesisValidator, error) {
|
||||||
|
|
||||||
if height != -1 {
|
if height != -1 {
|
||||||
appStruct := app.NewApp(logger, db, traceStore, false, uint(1))
|
gApp := app.NewGaiaApp(logger, db, traceStore, false, uint(1))
|
||||||
err := appStruct.LoadHeight(height)
|
err := gApp.LoadHeight(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
}
|
}
|
||||||
appStruct := app.NewApp(logger, db, traceStore, true, uint(1))
|
gApp := app.NewGaiaApp(logger, db, traceStore, true, uint(1))
|
||||||
return appStruct.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList)
|
||||||
}
|
}
|
35
gaia/cmd/gaiadebug/README.md
Normal file
35
gaia/cmd/gaiadebug/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Gaiadebug
|
||||||
|
|
||||||
|
Simple tool for simple debugging.
|
||||||
|
|
||||||
|
We try to accept both hex and base64 formats and provide a useful response.
|
||||||
|
|
||||||
|
Note we often encode bytes as hex in the logs, but as base64 in the JSON.
|
||||||
|
|
||||||
|
## Pubkeys
|
||||||
|
|
||||||
|
The following give the same result:
|
||||||
|
|
||||||
|
```
|
||||||
|
gaiadebug pubkey TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4=
|
||||||
|
gaiadebug pubkey 4D94D09DFA8EB22F3D49EA17567230FAD9C5267AF85FCA950B453C02C126164E
|
||||||
|
```
|
||||||
|
|
||||||
|
## Txs
|
||||||
|
|
||||||
|
Pass in a hex/base64 tx and get back the full JSON
|
||||||
|
|
||||||
|
```
|
||||||
|
gaiadebug tx <hex or base64 transaction>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hack
|
||||||
|
|
||||||
|
This is a command with boilerplate for using Go as a scripting language to hack
|
||||||
|
on an existing Gaia state.
|
||||||
|
|
||||||
|
Currently we have an example for the state of gaia-6001 after it
|
||||||
|
[crashed](https://github.com/cosmos/cosmos-sdk/blob/master/cmd/gaia/testnets/STATUS.md#june-13-2018-230-est---published-postmortem-of-gaia-6001-failure).
|
||||||
|
If you run `gaiadebug hack $HOME/.gaiad` on that
|
||||||
|
state, it will do a binary search on the state history to find when the state
|
||||||
|
invariant was violated.
|
268
gaia/cmd/gaiadebug/hack.go
Normal file
268
gaia/cmd/gaiadebug/hack.go
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
|
||||||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
bam "github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
|
||||||
|
gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runHackCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected 1 arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ".gaiad"
|
||||||
|
dataDir := args[0]
|
||||||
|
dataDir = path.Join(dataDir, "data")
|
||||||
|
|
||||||
|
// load the app
|
||||||
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
db, err := sdk.NewLevelDB("gaia", dataDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))))
|
||||||
|
|
||||||
|
// print some info
|
||||||
|
id := app.LastCommitID()
|
||||||
|
lastBlockHeight := app.LastBlockHeight()
|
||||||
|
fmt.Println("ID", id)
|
||||||
|
fmt.Println("LastBlockHeight", lastBlockHeight)
|
||||||
|
|
||||||
|
//----------------------------------------------------
|
||||||
|
// XXX: start hacking!
|
||||||
|
//----------------------------------------------------
|
||||||
|
// eg. gaia-6001 testnet bug
|
||||||
|
// We paniced when iterating through the "bypower" keys.
|
||||||
|
// The following powerKey was there, but the corresponding "trouble" validator did not exist.
|
||||||
|
// So here we do a binary search on the past states to find when the powerKey first showed up ...
|
||||||
|
|
||||||
|
// operator of the validator the bonds, gets jailed, later unbonds, and then later is still found in the bypower store
|
||||||
|
trouble := hexToBytes("D3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||||
|
// this is his "bypower" key
|
||||||
|
powerKey := hexToBytes("05303030303030303030303033FFFFFFFFFFFF4C0C0000FFFED3DC0FF59F7C3B548B7AFA365561B87FD0208AF8")
|
||||||
|
|
||||||
|
topHeight := lastBlockHeight
|
||||||
|
bottomHeight := int64(0)
|
||||||
|
checkHeight := topHeight
|
||||||
|
for {
|
||||||
|
// load the given version of the state
|
||||||
|
err = app.LoadVersion(checkHeight, app.keyMain)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
ctx := app.NewContext(true, abci.Header{})
|
||||||
|
|
||||||
|
// check for the powerkey and the validator from the store
|
||||||
|
store := ctx.KVStore(app.keyStaking)
|
||||||
|
res := store.Get(powerKey)
|
||||||
|
val, _ := app.stakingKeeper.GetValidator(ctx, trouble)
|
||||||
|
fmt.Println("checking height", checkHeight, res, val)
|
||||||
|
if res == nil {
|
||||||
|
bottomHeight = checkHeight
|
||||||
|
} else {
|
||||||
|
topHeight = checkHeight
|
||||||
|
}
|
||||||
|
checkHeight = (topHeight + bottomHeight) / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func base64ToPub(b64 string) ed25519.PubKeyEd25519 {
|
||||||
|
data, _ := base64.StdEncoding.DecodeString(b64)
|
||||||
|
var pubKey ed25519.PubKeyEd25519
|
||||||
|
copy(pubKey[:], data)
|
||||||
|
return pubKey
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func hexToBytes(h string) []byte {
|
||||||
|
trouble, _ := hex.DecodeString(h)
|
||||||
|
return trouble
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
// NOTE: This is all copied from gaia/app/app.go
|
||||||
|
// so we can access internal fields!
|
||||||
|
|
||||||
|
const (
|
||||||
|
appName = "GaiaApp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// default home directories for expected binaries
|
||||||
|
var (
|
||||||
|
DefaultCLIHome = os.ExpandEnv("$HOME/.gaiacli")
|
||||||
|
DefaultNodeHome = os.ExpandEnv("$HOME/.gaiad")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extended ABCI application
|
||||||
|
type GaiaApp struct {
|
||||||
|
*bam.BaseApp
|
||||||
|
cdc *codec.Codec
|
||||||
|
|
||||||
|
// keys to access the substores
|
||||||
|
keyMain *sdk.KVStoreKey
|
||||||
|
keyAccount *sdk.KVStoreKey
|
||||||
|
keyStaking *sdk.KVStoreKey
|
||||||
|
tkeyStaking *sdk.TransientStoreKey
|
||||||
|
keySlashing *sdk.KVStoreKey
|
||||||
|
keyParams *sdk.KVStoreKey
|
||||||
|
tkeyParams *sdk.TransientStoreKey
|
||||||
|
|
||||||
|
// Manage getting and setting accounts
|
||||||
|
accountKeeper auth.AccountKeeper
|
||||||
|
feeCollectionKeeper auth.FeeCollectionKeeper
|
||||||
|
bankKeeper bank.Keeper
|
||||||
|
stakingKeeper staking.Keeper
|
||||||
|
slashingKeeper slashing.Keeper
|
||||||
|
paramsKeeper params.Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
||||||
|
cdc := MakeCodec()
|
||||||
|
|
||||||
|
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...)
|
||||||
|
bApp.SetCommitMultiStoreTracer(os.Stdout)
|
||||||
|
|
||||||
|
// create your application object
|
||||||
|
var app = &GaiaApp{
|
||||||
|
BaseApp: bApp,
|
||||||
|
cdc: cdc,
|
||||||
|
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
|
||||||
|
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
|
||||||
|
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
|
||||||
|
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
|
||||||
|
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
|
||||||
|
keyParams: sdk.NewKVStoreKey(params.StoreKey),
|
||||||
|
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams)
|
||||||
|
|
||||||
|
// define the accountKeeper
|
||||||
|
app.accountKeeper = auth.NewAccountKeeper(
|
||||||
|
app.cdc,
|
||||||
|
app.keyAccount, // target store
|
||||||
|
app.paramsKeeper.Subspace(auth.DefaultParamspace),
|
||||||
|
auth.ProtoBaseAccount, // prototype
|
||||||
|
)
|
||||||
|
|
||||||
|
// add handlers
|
||||||
|
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, app.paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace)
|
||||||
|
app.stakingKeeper = staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper, app.paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
|
||||||
|
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakingKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace)
|
||||||
|
|
||||||
|
// register message routes
|
||||||
|
app.Router().
|
||||||
|
AddRoute("bank", bank.NewHandler(app.bankKeeper)).
|
||||||
|
AddRoute(staking.RouterKey, staking.NewHandler(app.stakingKeeper))
|
||||||
|
|
||||||
|
// initialize BaseApp
|
||||||
|
app.SetInitChainer(app.initChainer)
|
||||||
|
app.SetBeginBlocker(app.BeginBlocker)
|
||||||
|
app.SetEndBlocker(app.EndBlocker)
|
||||||
|
app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper))
|
||||||
|
app.MountStores(app.keyMain, app.keyAccount, app.keyStaking, app.keySlashing, app.keyParams)
|
||||||
|
app.MountStore(app.tkeyParams, sdk.StoreTypeTransient)
|
||||||
|
err := app.LoadLatestVersion(app.keyMain)
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Seal()
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom tx codec
|
||||||
|
func MakeCodec() *codec.Codec {
|
||||||
|
var cdc = codec.New()
|
||||||
|
bank.RegisterCodec(cdc)
|
||||||
|
staking.RegisterCodec(cdc)
|
||||||
|
slashing.RegisterCodec(cdc)
|
||||||
|
auth.RegisterCodec(cdc)
|
||||||
|
sdk.RegisterCodec(cdc)
|
||||||
|
codec.RegisterCrypto(cdc)
|
||||||
|
cdc.Seal()
|
||||||
|
return cdc
|
||||||
|
}
|
||||||
|
|
||||||
|
// application updates every end block
|
||||||
|
func (app *GaiaApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||||
|
tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
|
||||||
|
|
||||||
|
return abci.ResponseBeginBlock{
|
||||||
|
Tags: tags.ToKVPairs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// application updates every end block
|
||||||
|
// nolint: unparam
|
||||||
|
func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||||
|
validatorUpdates, tags := staking.EndBlocker(ctx, app.stakingKeeper)
|
||||||
|
|
||||||
|
return abci.ResponseEndBlock{
|
||||||
|
ValidatorUpdates: validatorUpdates,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom logic for gaia initialization
|
||||||
|
func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
|
stateJSON := req.AppStateBytes
|
||||||
|
// TODO is this now the whole genesis file?
|
||||||
|
|
||||||
|
var genesisState gaia.GenesisState
|
||||||
|
err := app.cdc.UnmarshalJSON(stateJSON, &genesisState)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the accounts
|
||||||
|
for _, gacc := range genesisState.Accounts {
|
||||||
|
acc := gacc.ToAccount()
|
||||||
|
app.accountKeeper.SetAccount(ctx, acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the initial staking information
|
||||||
|
validators, err := staking.InitGenesis(ctx, app.stakingKeeper, genesisState.StakingData)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 // return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData.Validators.ToSDKValidators())
|
||||||
|
|
||||||
|
return abci.ResponseInitChain{
|
||||||
|
Validators: validators,
|
||||||
|
}
|
||||||
|
}
|
256
gaia/cmd/gaiadebug/main.go
Normal file
256
gaia/cmd/gaiadebug/main.go
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
|
||||||
|
gaia "github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
config := sdk.GetConfig()
|
||||||
|
config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub)
|
||||||
|
config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub)
|
||||||
|
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
|
||||||
|
config.Seal()
|
||||||
|
|
||||||
|
rootCmd.AddCommand(txCmd)
|
||||||
|
rootCmd.AddCommand(pubkeyCmd)
|
||||||
|
rootCmd.AddCommand(addrCmd)
|
||||||
|
rootCmd.AddCommand(hackCmd)
|
||||||
|
rootCmd.AddCommand(rawBytesCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "gaiadebug",
|
||||||
|
Short: "Gaia debug tool",
|
||||||
|
SilenceUsage: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var txCmd = &cobra.Command{
|
||||||
|
Use: "tx",
|
||||||
|
Short: "Decode a gaia tx from hex or base64",
|
||||||
|
RunE: runTxCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubkeyCmd = &cobra.Command{
|
||||||
|
Use: "pubkey",
|
||||||
|
Short: "Decode a pubkey from hex, base64, or bech32",
|
||||||
|
RunE: runPubKeyCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
var addrCmd = &cobra.Command{
|
||||||
|
Use: "addr",
|
||||||
|
Short: "Convert an address between hex and bech32",
|
||||||
|
RunE: runAddrCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
var hackCmd = &cobra.Command{
|
||||||
|
Use: "hack",
|
||||||
|
Short: "Boilerplate to Hack on an existing state by scripting some Go...",
|
||||||
|
RunE: runHackCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawBytesCmd = &cobra.Command{
|
||||||
|
Use: "raw-bytes",
|
||||||
|
Short: "Convert raw bytes output (eg. [10 21 13 255]) to hex",
|
||||||
|
RunE: runRawBytesCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRawBytesCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected single arg")
|
||||||
|
}
|
||||||
|
stringBytes := args[0]
|
||||||
|
stringBytes = strings.Trim(stringBytes, "[")
|
||||||
|
stringBytes = strings.Trim(stringBytes, "]")
|
||||||
|
spl := strings.Split(stringBytes, " ")
|
||||||
|
|
||||||
|
byteArray := []byte{}
|
||||||
|
for _, s := range spl {
|
||||||
|
b, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
byteArray = append(byteArray, byte(b))
|
||||||
|
}
|
||||||
|
fmt.Printf("%X\n", byteArray)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runPubKeyCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected single arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubkeyString := args[0]
|
||||||
|
var pubKeyI crypto.PubKey
|
||||||
|
|
||||||
|
// try hex, then base64, then bech32
|
||||||
|
pubkeyBytes, err := hex.DecodeString(pubkeyString)
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
pubkeyBytes, err2 = base64.StdEncoding.DecodeString(pubkeyString)
|
||||||
|
if err2 != nil {
|
||||||
|
var err3 error
|
||||||
|
pubKeyI, err3 = sdk.GetAccPubKeyBech32(pubkeyString)
|
||||||
|
if err3 != nil {
|
||||||
|
var err4 error
|
||||||
|
pubKeyI, err4 = sdk.GetValPubKeyBech32(pubkeyString)
|
||||||
|
|
||||||
|
if err4 != nil {
|
||||||
|
var err5 error
|
||||||
|
pubKeyI, err5 = sdk.GetConsPubKeyBech32(pubkeyString)
|
||||||
|
if err5 != nil {
|
||||||
|
return fmt.Errorf(`Expected hex, base64, or bech32. Got errors:
|
||||||
|
hex: %v,
|
||||||
|
base64: %v
|
||||||
|
bech32 Acc: %v
|
||||||
|
bech32 Val: %v
|
||||||
|
bech32 Cons: %v`,
|
||||||
|
err, err2, err3, err4, err5)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubKey ed25519.PubKeyEd25519
|
||||||
|
if pubKeyI == nil {
|
||||||
|
copy(pubKey[:], pubkeyBytes)
|
||||||
|
} else {
|
||||||
|
pubKey = pubKeyI.(ed25519.PubKeyEd25519)
|
||||||
|
pubkeyBytes = pubKey[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
cdc := gaia.MakeCodec()
|
||||||
|
pubKeyJSONBytes, err := cdc.MarshalJSON(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
accPub, err := sdk.Bech32ifyAccPub(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
valPub, err := sdk.Bech32ifyValPub(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
consenusPub, err := sdk.Bech32ifyConsPub(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Address:", pubKey.Address())
|
||||||
|
fmt.Printf("Hex: %X\n", pubkeyBytes)
|
||||||
|
fmt.Println("JSON (base64):", string(pubKeyJSONBytes))
|
||||||
|
fmt.Println("Bech32 Acc:", accPub)
|
||||||
|
fmt.Println("Bech32 Validator Operator:", valPub)
|
||||||
|
fmt.Println("Bech32 Validator Consensus:", consenusPub)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddrCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected single arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
addrString := args[0]
|
||||||
|
var addr []byte
|
||||||
|
|
||||||
|
// try hex, then bech32
|
||||||
|
var err error
|
||||||
|
addr, err = hex.DecodeString(addrString)
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
addr, err2 = sdk.AccAddressFromBech32(addrString)
|
||||||
|
if err2 != nil {
|
||||||
|
var err3 error
|
||||||
|
addr, err3 = sdk.ValAddressFromBech32(addrString)
|
||||||
|
|
||||||
|
if err3 != nil {
|
||||||
|
return fmt.Errorf(`Expected hex or bech32. Got errors:
|
||||||
|
hex: %v,
|
||||||
|
bech32 acc: %v
|
||||||
|
bech32 val: %v
|
||||||
|
`, err, err2, err3)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accAddr := sdk.AccAddress(addr)
|
||||||
|
valAddr := sdk.ValAddress(addr)
|
||||||
|
|
||||||
|
fmt.Println("Address:", addr)
|
||||||
|
fmt.Printf("Address (hex): %X\n", addr)
|
||||||
|
fmt.Printf("Bech32 Acc: %s\n", accAddr)
|
||||||
|
fmt.Printf("Bech32 Val: %s\n", valAddr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("Expected single arg")
|
||||||
|
}
|
||||||
|
|
||||||
|
txString := args[0]
|
||||||
|
|
||||||
|
// try hex, then base64
|
||||||
|
txBytes, err := hex.DecodeString(txString)
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
txBytes, err2 = base64.StdEncoding.DecodeString(txString)
|
||||||
|
if err2 != nil {
|
||||||
|
return fmt.Errorf(`Expected hex or base64. Got errors:
|
||||||
|
hex: %v,
|
||||||
|
base64: %v
|
||||||
|
`, err, err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = auth.StdTx{}
|
||||||
|
cdc := gaia.MakeCodec()
|
||||||
|
|
||||||
|
err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := cdc.MarshalJSON(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
err = json.Indent(buf, bz, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(buf.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
59
gaia/cmd/gaiakeyutil/main.go
Normal file
59
gaia/cmd/gaiakeyutil/main.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/tendermint/tendermint/libs/bech32"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bech32Prefixes = []string{
|
||||||
|
sdk.Bech32PrefixAccAddr,
|
||||||
|
sdk.Bech32PrefixAccPub,
|
||||||
|
sdk.Bech32PrefixValAddr,
|
||||||
|
sdk.Bech32PrefixValPub,
|
||||||
|
sdk.Bech32PrefixConsAddr,
|
||||||
|
sdk.Bech32PrefixConsPub,
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Println("Must specify an input string")
|
||||||
|
}
|
||||||
|
arg := os.Args[1]
|
||||||
|
runFromBech32(arg)
|
||||||
|
runFromHex(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print info from bech32.
|
||||||
|
func runFromBech32(bech32str string) {
|
||||||
|
hrp, bz, err := bech32.DecodeAndConvert(bech32str)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Not a valid bech32 string")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Bech32 parse:")
|
||||||
|
fmt.Printf("Human readible part: %v\nBytes (hex): %X\n",
|
||||||
|
hrp,
|
||||||
|
bz,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runFromHex(hexaddr string) {
|
||||||
|
bz, err := hex.DecodeString(hexaddr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Not a valid hex string")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Hex parse:")
|
||||||
|
fmt.Println("Bech32 formats:")
|
||||||
|
for _, prefix := range bech32Prefixes {
|
||||||
|
bech32Addr, err := bech32.ConvertAndEncode(prefix, bz)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(" - " + bech32Addr)
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,3 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -10,13 +7,11 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
|
||||||
cpm "github.com/otiai10/copy"
|
cpm "github.com/otiai10/copy"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
|
||||||
"github.com/cosmos/cosmos-sdk/store"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
bcm "github.com/tendermint/tendermint/blockchain"
|
bcm "github.com/tendermint/tendermint/blockchain"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
@ -24,32 +19,49 @@ import (
|
|||||||
tmsm "github.com/tendermint/tendermint/state"
|
tmsm "github.com/tendermint/tendermint/state"
|
||||||
tm "github.com/tendermint/tendermint/types"
|
tm "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/kava-labs/_/app"
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func replayCmd() *cobra.Command {
|
var (
|
||||||
return &cobra.Command{
|
rootDir string
|
||||||
Use: "replay <root-dir>",
|
)
|
||||||
Short: "Replay transactions",
|
|
||||||
RunE: func(_ *cobra.Command, args []string) error {
|
var rootCmd = &cobra.Command{
|
||||||
return replayTxs(args[0])
|
Use: "gaiareplay",
|
||||||
},
|
Short: "Replay gaia transactions",
|
||||||
Args: cobra.ExactArgs(1),
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
run(rootDir)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// cobra.OnInitialize(initConfig)
|
||||||
|
rootCmd.PersistentFlags().StringVar(&rootDir, "root", "r", "root dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func replayTxs(rootDir string) error {
|
func run(rootDir string) {
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
// Copy the rootDir to a new directory, to preserve the old one.
|
// Copy the rootDir to a new directory, to preserve the old one.
|
||||||
fmt.Fprintln(os.Stderr, "Copying rootdir over")
|
fmt.Println("Copying rootdir over")
|
||||||
oldRootDir := rootDir
|
oldRootDir := rootDir
|
||||||
rootDir = oldRootDir + "_replay"
|
rootDir = oldRootDir + "_replay"
|
||||||
if cmn.FileExists(rootDir) {
|
if cmn.FileExists(rootDir) {
|
||||||
cmn.Exit(fmt.Sprintf("temporary copy dir %v already exists", rootDir))
|
cmn.Exit(fmt.Sprintf("temporary copy dir %v already exists", rootDir))
|
||||||
}
|
}
|
||||||
if err := cpm.Copy(oldRootDir, rootDir); err != nil {
|
err := cpm.Copy(oldRootDir, rootDir)
|
||||||
return err
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,25 +71,25 @@ func replayTxs(rootDir string) error {
|
|||||||
|
|
||||||
// App DB
|
// App DB
|
||||||
// appDB := dbm.NewMemDB()
|
// appDB := dbm.NewMemDB()
|
||||||
fmt.Fprintln(os.Stderr, "Opening app database")
|
fmt.Println("Opening app database")
|
||||||
appDB, err := sdk.NewLevelDB("application", dataDir)
|
appDB, err := sdk.NewLevelDB("application", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TM DB
|
// TM DB
|
||||||
// tmDB := dbm.NewMemDB()
|
// tmDB := dbm.NewMemDB()
|
||||||
fmt.Fprintln(os.Stderr, "Opening tendermint state database")
|
fmt.Println("Opening tendermint state database")
|
||||||
tmDB, err := sdk.NewLevelDB("state", dataDir)
|
tmDB, err := sdk.NewLevelDB("state", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blockchain DB
|
// Blockchain DB
|
||||||
fmt.Fprintln(os.Stderr, "Opening blockstore database")
|
fmt.Println("Opening blockstore database")
|
||||||
bcDB, err := sdk.NewLevelDB("blockstore", dataDir)
|
bcDB, err := sdk.NewLevelDB("blockstore", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceStore
|
// TraceStore
|
||||||
@ -89,12 +101,12 @@ func replayTxs(rootDir string) error {
|
|||||||
0666,
|
0666,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application
|
// Application
|
||||||
fmt.Fprintln(os.Stderr, "Creating application")
|
fmt.Println("Creating application")
|
||||||
myapp := app.NewApp(
|
myapp := app.NewGaiaApp(
|
||||||
ctx.Logger, appDB, traceStoreWriter, true, uint(1),
|
ctx.Logger, appDB, traceStoreWriter, true, uint(1),
|
||||||
baseapp.SetPruning(store.PruneEverything), // nothing
|
baseapp.SetPruning(store.PruneEverything), // nothing
|
||||||
)
|
)
|
||||||
@ -103,11 +115,11 @@ func replayTxs(rootDir string) error {
|
|||||||
var genDocPath = filepath.Join(configDir, "genesis.json")
|
var genDocPath = filepath.Join(configDir, "genesis.json")
|
||||||
genDoc, err := tm.GenesisDocFromFile(genDocPath)
|
genDoc, err := tm.GenesisDocFromFile(genDocPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
genState, err := tmsm.MakeGenesisState(genDoc)
|
genState, err := tmsm.MakeGenesisState(genDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
// tmsm.SaveState(tmDB, genState)
|
// tmsm.SaveState(tmDB, genState)
|
||||||
|
|
||||||
@ -115,7 +127,7 @@ func replayTxs(rootDir string) error {
|
|||||||
proxyApp := proxy.NewAppConns(cc)
|
proxyApp := proxy.NewAppConns(cc)
|
||||||
err = proxyApp.Start()
|
err = proxyApp.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = proxyApp.Stop()
|
_ = proxyApp.Stop()
|
||||||
@ -124,7 +136,7 @@ func replayTxs(rootDir string) error {
|
|||||||
state := tmsm.LoadState(tmDB)
|
state := tmsm.LoadState(tmDB)
|
||||||
if state.LastBlockHeight == 0 {
|
if state.LastBlockHeight == 0 {
|
||||||
// Send InitChain msg
|
// Send InitChain msg
|
||||||
fmt.Fprintln(os.Stderr, "Sending InitChain msg")
|
fmt.Println("Sending InitChain msg")
|
||||||
validators := tm.TM2PB.ValidatorUpdates(genState.Validators)
|
validators := tm.TM2PB.ValidatorUpdates(genState.Validators)
|
||||||
csParams := tm.TM2PB.ConsensusParams(genDoc.ConsensusParams)
|
csParams := tm.TM2PB.ConsensusParams(genDoc.ConsensusParams)
|
||||||
req := abci.RequestInitChain{
|
req := abci.RequestInitChain{
|
||||||
@ -136,11 +148,11 @@ func replayTxs(rootDir string) error {
|
|||||||
}
|
}
|
||||||
res, err := proxyApp.Consensus().InitChainSync(req)
|
res, err := proxyApp.Consensus().InitChainSync(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
newValidatorz, err := tm.PB2TM.ValidatorUpdates(res.Validators)
|
newValidatorz, err := tm.PB2TM.ValidatorUpdates(res.Validators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
newValidators := tm.NewValidatorSet(newValidatorz)
|
newValidators := tm.NewValidatorSet(newValidatorz)
|
||||||
|
|
||||||
@ -151,17 +163,17 @@ func replayTxs(rootDir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create executor
|
// Create executor
|
||||||
fmt.Fprintln(os.Stderr, "Creating block executor")
|
fmt.Println("Creating block executor")
|
||||||
blockExec := tmsm.NewBlockExecutor(tmDB, ctx.Logger, proxyApp.Consensus(),
|
blockExec := tmsm.NewBlockExecutor(tmDB, ctx.Logger, proxyApp.Consensus(),
|
||||||
tmsm.MockMempool{}, tmsm.MockEvidencePool{})
|
tmsm.MockMempool{}, tmsm.MockEvidencePool{})
|
||||||
|
|
||||||
// Create block store
|
// Create block store
|
||||||
fmt.Fprintln(os.Stderr, "Creating block store")
|
fmt.Println("Creating block store")
|
||||||
blockStore := bcm.NewBlockStore(bcDB)
|
blockStore := bcm.NewBlockStore(bcDB)
|
||||||
|
|
||||||
tz := []time.Duration{0, 0, 0}
|
tz := []time.Duration{0, 0, 0}
|
||||||
for i := int(state.LastBlockHeight) + 1; ; i++ {
|
for i := int(state.LastBlockHeight) + 1; ; i++ {
|
||||||
fmt.Fprintln(os.Stderr, "Running block ", i)
|
fmt.Println("Running block ", i)
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
|
|
||||||
// Apply block
|
// Apply block
|
||||||
@ -169,25 +181,26 @@ func replayTxs(rootDir string) error {
|
|||||||
blockmeta := blockStore.LoadBlockMeta(int64(i))
|
blockmeta := blockStore.LoadBlockMeta(int64(i))
|
||||||
if blockmeta == nil {
|
if blockmeta == nil {
|
||||||
fmt.Printf("Couldn't find block meta %d... done?\n", i)
|
fmt.Printf("Couldn't find block meta %d... done?\n", i)
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
block := blockStore.LoadBlock(int64(i))
|
block := blockStore.LoadBlock(int64(i))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return fmt.Errorf("couldn't find block %d", i)
|
panic(fmt.Sprintf("couldn't find block %d", i))
|
||||||
}
|
}
|
||||||
|
|
||||||
t2 := time.Now()
|
t2 := time.Now()
|
||||||
|
|
||||||
state, err = blockExec.ApplyBlock(state, blockmeta.BlockID, block)
|
state, err = blockExec.ApplyBlock(state, blockmeta.BlockID, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t3 := time.Now()
|
t3 := time.Now()
|
||||||
tz[0] += t2.Sub(t1)
|
tz[0] += t2.Sub(t1)
|
||||||
tz[1] += t3.Sub(t2)
|
tz[1] += t3.Sub(t2)
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "new app hash: %X\n", state.AppHash)
|
fmt.Printf("new app hash: %X\n", state.AppHash)
|
||||||
fmt.Fprintln(os.Stderr, tz)
|
fmt.Println(tz)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
145
gaia/init/collect.go
Normal file
145
gaia/init/collect.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
// DONTCOVER
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagGenTxDir = "gentx-dir"
|
||||||
|
)
|
||||||
|
|
||||||
|
type initConfig struct {
|
||||||
|
ChainID string
|
||||||
|
GenTxsDir string
|
||||||
|
Name string
|
||||||
|
NodeID string
|
||||||
|
ValPubKey crypto.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "collect-gentxs",
|
||||||
|
Short: "Collect genesis txs and output a genesis.json file",
|
||||||
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
|
config := ctx.Config
|
||||||
|
config.SetRoot(viper.GetString(cli.HomeFlag))
|
||||||
|
name := viper.GetString(client.FlagName)
|
||||||
|
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
genTxsDir := viper.GetString(flagGenTxDir)
|
||||||
|
if genTxsDir == "" {
|
||||||
|
genTxsDir = filepath.Join(config.RootDir, "config", "gentx")
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage(""))
|
||||||
|
initCfg := newInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey)
|
||||||
|
|
||||||
|
appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrint.AppMessage = appMessage
|
||||||
|
|
||||||
|
// print out some key information
|
||||||
|
return displayInfo(cdc, toPrint)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
||||||
|
cmd.Flags().String(flagGenTxDir, "",
|
||||||
|
"override default \"gentx\" directory from which collect and execute "+
|
||||||
|
"genesis transactions; default [--home]/config/gentx/")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func genAppStateFromConfig(
|
||||||
|
cdc *codec.Codec, config *cfg.Config, initCfg initConfig, genDoc types.GenesisDoc,
|
||||||
|
) (appState json.RawMessage, err error) {
|
||||||
|
|
||||||
|
genFile := config.GenesisFile()
|
||||||
|
var (
|
||||||
|
appGenTxs []auth.StdTx
|
||||||
|
persistentPeers string
|
||||||
|
genTxs []json.RawMessage
|
||||||
|
jsonRawTx json.RawMessage
|
||||||
|
)
|
||||||
|
|
||||||
|
// process genesis transactions, else create default genesis.json
|
||||||
|
appGenTxs, persistentPeers, err = app.CollectStdTxs(
|
||||||
|
cdc, config.Moniker, initCfg.GenTxsDir, genDoc,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
genTxs = make([]json.RawMessage, len(appGenTxs))
|
||||||
|
config.P2P.PersistentPeers = persistentPeers
|
||||||
|
|
||||||
|
for i, stdTx := range appGenTxs {
|
||||||
|
jsonRawTx, err = cdc.MarshalJSON(stdTx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
genTxs[i] = jsonRawTx
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
|
||||||
|
|
||||||
|
appState, err = app.GaiaAppGenStateJSON(cdc, genDoc, genTxs)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInitConfig(chainID, genTxsDir, name, nodeID string,
|
||||||
|
valPubKey crypto.PubKey) initConfig {
|
||||||
|
|
||||||
|
return initConfig{
|
||||||
|
ChainID: chainID,
|
||||||
|
GenTxsDir: genTxsDir,
|
||||||
|
Name: name,
|
||||||
|
NodeID: nodeID,
|
||||||
|
ValPubKey: valPubKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPrintInfo(moniker, chainID, nodeID, genTxsDir string,
|
||||||
|
appMessage json.RawMessage) printInfo {
|
||||||
|
|
||||||
|
return printInfo{
|
||||||
|
Moniker: moniker,
|
||||||
|
ChainID: chainID,
|
||||||
|
NodeID: nodeID,
|
||||||
|
GenTxsDir: genTxsDir,
|
||||||
|
AppMessage: appMessage,
|
||||||
|
}
|
||||||
|
}
|
141
gaia/init/genesis_accts.go
Normal file
141
gaia/init/genesis_accts.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/libs/common"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddGenesisAccountCmd returns add-genesis-account cobra Command.
|
||||||
|
func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
|
||||||
|
Short: "Add genesis account to genesis.json",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
config := ctx.Config
|
||||||
|
config.SetRoot(viper.GetString(cli.HomeFlag))
|
||||||
|
|
||||||
|
addr, err := sdk.AccAddressFromBech32(args[0])
|
||||||
|
if err != nil {
|
||||||
|
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := kb.Get(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = info.GetAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
coins, err := sdk.ParseCoins(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vestingStart := viper.GetInt64(flagVestingStart)
|
||||||
|
vestingEnd := viper.GetInt64(flagVestingEnd)
|
||||||
|
vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
genFile := config.GenesisFile()
|
||||||
|
if !common.FileExists(genFile) {
|
||||||
|
return fmt.Errorf("%s does not exist, run `gaiad init` first", genFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
genDoc, err := LoadGenesisDoc(cdc, genFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var appState app.GenesisState
|
||||||
|
if err = cdc.UnmarshalJSON(genDoc.AppState, &appState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
appState, err = addGenesisAccount(cdc, appState, addr, coins, vestingAmt, vestingStart, vestingEnd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
appStateJSON, err := cdc.MarshalJSON(appState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExportGenesisFile(genFile, genDoc.ChainID, nil, appStateJSON)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
||||||
|
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
|
||||||
|
cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
|
||||||
|
cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
|
||||||
|
cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGenesisAccount(
|
||||||
|
cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress,
|
||||||
|
coins, vestingAmt sdk.Coins, vestingStart, vestingEnd int64,
|
||||||
|
) (app.GenesisState, error) {
|
||||||
|
|
||||||
|
for _, stateAcc := range appState.Accounts {
|
||||||
|
if stateAcc.Address.Equals(addr) {
|
||||||
|
return appState, fmt.Errorf("the application state already contains account %v", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc := auth.NewBaseAccountWithAddress(addr)
|
||||||
|
acc.Coins = coins
|
||||||
|
|
||||||
|
if !vestingAmt.IsZero() {
|
||||||
|
var vacc auth.VestingAccount
|
||||||
|
|
||||||
|
bvacc := &auth.BaseVestingAccount{
|
||||||
|
BaseAccount: &acc,
|
||||||
|
OriginalVesting: vestingAmt,
|
||||||
|
EndTime: vestingEnd,
|
||||||
|
}
|
||||||
|
|
||||||
|
if bvacc.OriginalVesting.IsAllGT(acc.Coins) {
|
||||||
|
return appState, fmt.Errorf("vesting amount cannot be greater than total amount")
|
||||||
|
}
|
||||||
|
if vestingStart >= vestingEnd {
|
||||||
|
return appState, fmt.Errorf("vesting start time must before end time")
|
||||||
|
}
|
||||||
|
|
||||||
|
if vestingStart != 0 {
|
||||||
|
vacc = &auth.ContinuousVestingAccount{
|
||||||
|
BaseVestingAccount: bvacc,
|
||||||
|
StartTime: vestingStart,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vacc = &auth.DelayedVestingAccount{
|
||||||
|
BaseVestingAccount: bvacc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appState.Accounts = append(appState.Accounts, app.NewGenesisAccountI(vacc))
|
||||||
|
} else {
|
||||||
|
appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc))
|
||||||
|
}
|
||||||
|
|
||||||
|
return appState, nil
|
||||||
|
}
|
88
gaia/init/genesis_accts_test.go
Normal file
88
gaia/init/genesis_accts_test.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddGenesisAccount(t *testing.T) {
|
||||||
|
cdc := codec.New()
|
||||||
|
addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
|
||||||
|
type args struct {
|
||||||
|
appState app.GenesisState
|
||||||
|
addr sdk.AccAddress
|
||||||
|
coins sdk.Coins
|
||||||
|
vestingAmt sdk.Coins
|
||||||
|
vestingStart int64
|
||||||
|
vestingEnd int64
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"valid account",
|
||||||
|
args{
|
||||||
|
app.GenesisState{},
|
||||||
|
addr1,
|
||||||
|
sdk.NewCoins(),
|
||||||
|
sdk.NewCoins(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dup account",
|
||||||
|
args{
|
||||||
|
app.GenesisState{Accounts: []app.GenesisAccount{{Address: addr1}}},
|
||||||
|
addr1,
|
||||||
|
sdk.NewCoins(),
|
||||||
|
sdk.NewCoins(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"invalid vesting amount",
|
||||||
|
args{
|
||||||
|
app.GenesisState{},
|
||||||
|
addr1,
|
||||||
|
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
|
||||||
|
sdk.NewCoins(sdk.NewInt64Coin("stake", 100)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"invalid vesting times",
|
||||||
|
args{
|
||||||
|
app.GenesisState{},
|
||||||
|
addr1,
|
||||||
|
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
|
||||||
|
sdk.NewCoins(sdk.NewInt64Coin("stake", 50)),
|
||||||
|
1654668078,
|
||||||
|
1554668078,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := addGenesisAccount(
|
||||||
|
cdc, tt.args.appState, tt.args.addr, tt.args.coins,
|
||||||
|
tt.args.vestingAmt, tt.args.vestingStart, tt.args.vestingEnd,
|
||||||
|
)
|
||||||
|
require.Equal(t, tt.wantErr, (err != nil))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
312
gaia/init/gentx.go
Normal file
312
gaia/init/gentx.go
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
// DONTCOVER
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
tmcli "github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/libs/common"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
kbkeys "github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/client/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultTokens = sdk.TokensFromTendermintPower(100)
|
||||||
|
defaultAmount = defaultTokens.String() + sdk.DefaultBondDenom
|
||||||
|
defaultCommissionRate = "0.1"
|
||||||
|
defaultCommissionMaxRate = "0.2"
|
||||||
|
defaultCommissionMaxChangeRate = "0.01"
|
||||||
|
defaultMinSelfDelegation = "1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenTxCmd builds the gaiad gentx command.
|
||||||
|
// nolint: errcheck
|
||||||
|
func GenTxCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "gentx",
|
||||||
|
Short: "Generate a genesis tx carrying a self delegation",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Long: fmt.Sprintf(`This command is an alias of the 'gaiad tx create-validator' command'.
|
||||||
|
|
||||||
|
It creates a genesis piece carrying a self delegation with the
|
||||||
|
following delegation and commission default parameters:
|
||||||
|
|
||||||
|
delegation amount: %s
|
||||||
|
commission rate: %s
|
||||||
|
commission max rate: %s
|
||||||
|
commission max change rate: %s
|
||||||
|
minimum self delegation: %s
|
||||||
|
`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
config := ctx.Config
|
||||||
|
config.SetRoot(viper.GetString(tmcli.HomeFlag))
|
||||||
|
nodeID, valPubKey, err := InitializeNodeValidatorFiles(ctx.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read --nodeID, if empty take it from priv_validator.json
|
||||||
|
if nodeIDString := viper.GetString(cli.FlagNodeID); nodeIDString != "" {
|
||||||
|
nodeID = nodeIDString
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := viper.GetString(cli.FlagIP)
|
||||||
|
if ip == "" {
|
||||||
|
fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+
|
||||||
|
"the tx's memo field will be unset")
|
||||||
|
}
|
||||||
|
|
||||||
|
genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
genesisState := app.GenesisState{}
|
||||||
|
if err = cdc.UnmarshalJSON(genDoc.AppState, &genesisState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = app.GaiaValidateGenesisState(genesisState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kb, err := keys.NewKeyBaseFromDir(viper.GetString(flagClientHome))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
name := viper.GetString(client.FlagName)
|
||||||
|
key, err := kb.Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read --pubkey, if empty take it from priv_validator.json
|
||||||
|
if valPubKeyString := viper.GetString(cli.FlagPubKey); valPubKeyString != "" {
|
||||||
|
valPubKey, err = sdk.GetConsPubKeyBech32(valPubKeyString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
website := viper.GetString(cli.FlagWebsite)
|
||||||
|
details := viper.GetString(cli.FlagDetails)
|
||||||
|
identity := viper.GetString(cli.FlagIdentity)
|
||||||
|
|
||||||
|
// Set flags for creating gentx
|
||||||
|
prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey, website, details, identity)
|
||||||
|
|
||||||
|
// Fetch the amount of coins staked
|
||||||
|
amount := viper.GetString(cli.FlagAmount)
|
||||||
|
coins, err := sdk.ParseCoins(amount)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = accountInGenesis(genesisState, key.GetAddress(), coins)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
// XXX: Set the generate-only flag here after the CLI context has
|
||||||
|
// been created. This allows the from name/key to be correctly populated.
|
||||||
|
//
|
||||||
|
// TODO: Consider removing the manual setting of generate-only in
|
||||||
|
// favor of a 'gentx' flag in the create-validator command.
|
||||||
|
viper.Set(client.FlagGenerateOnly, true)
|
||||||
|
|
||||||
|
// create a 'create-validator' message
|
||||||
|
txBldr, msg, err := cli.BuildCreateValidatorMsg(cliCtx, txBldr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := txBldr.Keybase().Get(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.GetType() == kbkeys.TypeOffline || info.GetType() == kbkeys.TypeMulti {
|
||||||
|
fmt.Println("Offline key passed in. Use `gaiacli tx sign` command to sign:")
|
||||||
|
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the unsigned transaction to the buffer
|
||||||
|
w := bytes.NewBuffer([]byte{})
|
||||||
|
cliCtx = cliCtx.WithOutput(w)
|
||||||
|
|
||||||
|
if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the transaction
|
||||||
|
stdTx, err := readUnsignedGenTxFile(cdc, w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign the transaction and write it to the output file
|
||||||
|
signedTx, err := utils.SignStdTx(txBldr, cliCtx, name, stdTx, false, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch output file name
|
||||||
|
outputDocument := viper.GetString(client.FlagOutputDocument)
|
||||||
|
if outputDocument == "" {
|
||||||
|
outputDocument, err = makeOutputFilepath(config.RootDir, nodeID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "Genesis transaction written to %q\n", outputDocument)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, _ := server.ExternalIP()
|
||||||
|
|
||||||
|
cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
||||||
|
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
|
||||||
|
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
|
||||||
|
cmd.Flags().String(client.FlagOutputDocument, "",
|
||||||
|
"write the genesis transaction JSON document to the given file instead of the default location")
|
||||||
|
cmd.Flags().String(cli.FlagIP, ip, "The node's public IP")
|
||||||
|
cmd.Flags().String(cli.FlagNodeID, "", "The node's NodeID")
|
||||||
|
cmd.Flags().String(cli.FlagWebsite, "", "The validator's (optional) website")
|
||||||
|
cmd.Flags().String(cli.FlagDetails, "", "The validator's (optional) details")
|
||||||
|
cmd.Flags().String(cli.FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)")
|
||||||
|
cmd.Flags().AddFlagSet(cli.FsCommissionCreate)
|
||||||
|
cmd.Flags().AddFlagSet(cli.FsMinSelfDelegation)
|
||||||
|
cmd.Flags().AddFlagSet(cli.FsAmount)
|
||||||
|
cmd.Flags().AddFlagSet(cli.FsPk)
|
||||||
|
cmd.MarkFlagRequired(client.FlagName)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func accountInGenesis(genesisState app.GenesisState, key sdk.AccAddress, coins sdk.Coins) error {
|
||||||
|
accountIsInGenesis := false
|
||||||
|
bondDenom := genesisState.StakingData.Params.BondDenom
|
||||||
|
|
||||||
|
// Check if the account is in genesis
|
||||||
|
for _, acc := range genesisState.Accounts {
|
||||||
|
// Ensure that account is in genesis
|
||||||
|
if acc.Address.Equals(key) {
|
||||||
|
|
||||||
|
// Ensure account contains enough funds of default bond denom
|
||||||
|
if coins.AmountOf(bondDenom).GT(acc.Coins.AmountOf(bondDenom)) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"account %v is in genesis, but it only has %v%v available to stake, not %v%v",
|
||||||
|
key.String(), acc.Coins.AmountOf(bondDenom), bondDenom, coins.AmountOf(bondDenom), bondDenom,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
accountIsInGenesis = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if accountIsInGenesis {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("account %s in not in the app_state.accounts array of genesis.json", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareFlagsForTxCreateValidator(
|
||||||
|
config *cfg.Config, nodeID, ip, chainID string, valPubKey crypto.PubKey, website, details, identity string,
|
||||||
|
) {
|
||||||
|
viper.Set(tmcli.HomeFlag, viper.GetString(flagClientHome))
|
||||||
|
viper.Set(client.FlagChainID, chainID)
|
||||||
|
viper.Set(client.FlagFrom, viper.GetString(client.FlagName))
|
||||||
|
viper.Set(cli.FlagNodeID, nodeID)
|
||||||
|
viper.Set(cli.FlagIP, ip)
|
||||||
|
viper.Set(cli.FlagPubKey, sdk.MustBech32ifyConsPub(valPubKey))
|
||||||
|
viper.Set(cli.FlagMoniker, config.Moniker)
|
||||||
|
viper.Set(cli.FlagWebsite, website)
|
||||||
|
viper.Set(cli.FlagDetails, details)
|
||||||
|
viper.Set(cli.FlagIdentity, identity)
|
||||||
|
|
||||||
|
if config.Moniker == "" {
|
||||||
|
viper.Set(cli.FlagMoniker, viper.GetString(client.FlagName))
|
||||||
|
}
|
||||||
|
if viper.GetString(cli.FlagAmount) == "" {
|
||||||
|
viper.Set(cli.FlagAmount, defaultAmount)
|
||||||
|
}
|
||||||
|
if viper.GetString(cli.FlagCommissionRate) == "" {
|
||||||
|
viper.Set(cli.FlagCommissionRate, defaultCommissionRate)
|
||||||
|
}
|
||||||
|
if viper.GetString(cli.FlagCommissionMaxRate) == "" {
|
||||||
|
viper.Set(cli.FlagCommissionMaxRate, defaultCommissionMaxRate)
|
||||||
|
}
|
||||||
|
if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" {
|
||||||
|
viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate)
|
||||||
|
}
|
||||||
|
if viper.GetString(cli.FlagMinSelfDelegation) == "" {
|
||||||
|
viper.Set(cli.FlagMinSelfDelegation, defaultMinSelfDelegation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeOutputFilepath(rootDir, nodeID string) (string, error) {
|
||||||
|
writePath := filepath.Join(rootDir, "config", "gentx")
|
||||||
|
if err := common.EnsureDir(writePath, 0700); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUnsignedGenTxFile(cdc *codec.Codec, r io.Reader) (auth.StdTx, error) {
|
||||||
|
var stdTx auth.StdTx
|
||||||
|
bytes, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return stdTx, err
|
||||||
|
}
|
||||||
|
err = cdc.UnmarshalJSON(bytes, &stdTx)
|
||||||
|
return stdTx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: errcheck
|
||||||
|
func writeSignedGenTx(cdc *codec.Codec, outputDocument string, tx auth.StdTx) error {
|
||||||
|
outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
json, err := cdc.MarshalJSON(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(outputFile, "%s\n", json)
|
||||||
|
return err
|
||||||
|
}
|
85
gaia/init/gentx_test.go
Normal file
85
gaia/init/gentx_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/client/cli"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_prepareFlagsForTxCreateValidator(t *testing.T) {
|
||||||
|
defer server.SetupViper(t)()
|
||||||
|
defer setupClientHome(t)()
|
||||||
|
config, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
ctx := server.NewContext(config, logger)
|
||||||
|
|
||||||
|
valPubKey, _ := sdk.GetConsPubKeyBech32("cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a")
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
config *cfg.Config
|
||||||
|
nodeID string
|
||||||
|
ip string
|
||||||
|
chainID string
|
||||||
|
valPubKey crypto.PubKey
|
||||||
|
website string
|
||||||
|
details string
|
||||||
|
identity string
|
||||||
|
}
|
||||||
|
|
||||||
|
type extraParams struct {
|
||||||
|
amount string
|
||||||
|
commissionRate string
|
||||||
|
commissionMaxRate string
|
||||||
|
commissionMaxChangeRate string
|
||||||
|
minSelfDelegation string
|
||||||
|
}
|
||||||
|
|
||||||
|
type testcase struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest := func(t *testing.T, tt testcase, params extraParams) {
|
||||||
|
prepareFlagsForTxCreateValidator(tt.args.config, tt.args.nodeID, tt.args.ip, tt.args.chainID, tt.args.valPubKey, tt.args.website, tt.args.details, tt.args.identity)
|
||||||
|
require.Equal(t, tt.args.website, viper.GetString(cli.FlagWebsite))
|
||||||
|
require.Equal(t, tt.args.details, viper.GetString(cli.FlagDetails))
|
||||||
|
require.Equal(t, tt.args.identity, viper.GetString(cli.FlagIdentity))
|
||||||
|
require.Equal(t, params.amount, viper.GetString(cli.FlagAmount))
|
||||||
|
require.Equal(t, params.commissionRate, viper.GetString(cli.FlagCommissionRate))
|
||||||
|
require.Equal(t, params.commissionMaxRate, viper.GetString(cli.FlagCommissionMaxRate))
|
||||||
|
require.Equal(t, params.commissionMaxChangeRate, viper.GetString(cli.FlagCommissionMaxChangeRate))
|
||||||
|
require.Equal(t, params.minSelfDelegation, viper.GetString(cli.FlagMinSelfDelegation))
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []testcase{
|
||||||
|
{"No parameters", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "", "", ""}},
|
||||||
|
{"Optional parameters fed", args{ctx.Config, "X", "0.0.0.0", "chainId", valPubKey, "cosmos.network", "details", "identity"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultParams := extraParams{defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Run(tt.name, func(t *testing.T) { runTest(t, tt, defaultParams) })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default params
|
||||||
|
params := extraParams{"5stake", "1.0", "1.0", "1.0", "1.0"}
|
||||||
|
viper.Set(cli.FlagAmount, params.amount)
|
||||||
|
viper.Set(cli.FlagCommissionRate, params.commissionRate)
|
||||||
|
viper.Set(cli.FlagCommissionMaxRate, params.commissionMaxRate)
|
||||||
|
viper.Set(cli.FlagCommissionMaxChangeRate, params.commissionMaxChangeRate)
|
||||||
|
viper.Set(cli.FlagMinSelfDelegation, params.minSelfDelegation)
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) { runTest(t, tt, params) })
|
||||||
|
}
|
||||||
|
}
|
95
gaia/init/init.go
Normal file
95
gaia/init/init.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/libs/common"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
flagOverwrite = "overwrite"
|
||||||
|
flagClientHome = "home-client"
|
||||||
|
flagVestingStart = "vesting-start-time"
|
||||||
|
flagVestingEnd = "vesting-end-time"
|
||||||
|
flagVestingAmt = "vesting-amount"
|
||||||
|
)
|
||||||
|
|
||||||
|
type printInfo struct {
|
||||||
|
Moniker string `json:"moniker"`
|
||||||
|
ChainID string `json:"chain_id"`
|
||||||
|
NodeID string `json:"node_id"`
|
||||||
|
GenTxsDir string `json:"gentxs_dir"`
|
||||||
|
AppMessage json.RawMessage `json:"app_message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayInfo(cdc *codec.Codec, info printInfo) error {
|
||||||
|
out, err := codec.MarshalJSONIndent(cdc, info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", string(out)) // nolint: errcheck
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCmd returns a command that initializes all files needed for Tendermint
|
||||||
|
// and the respective application.
|
||||||
|
func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { // nolint: golint
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "init [moniker]",
|
||||||
|
Short: "Initialize private validator, p2p, genesis, and application configuration files",
|
||||||
|
Long: `Initialize validators's and node's configuration files.`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(_ *cobra.Command, args []string) error {
|
||||||
|
config := ctx.Config
|
||||||
|
config.SetRoot(viper.GetString(cli.HomeFlag))
|
||||||
|
|
||||||
|
chainID := viper.GetString(client.FlagChainID)
|
||||||
|
if chainID == "" {
|
||||||
|
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeID, _, err := InitializeNodeValidatorFiles(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Moniker = args[0]
|
||||||
|
|
||||||
|
var appState json.RawMessage
|
||||||
|
genFile := config.GenesisFile()
|
||||||
|
|
||||||
|
if appState, err = initializeEmptyGenesis(cdc, genFile, chainID,
|
||||||
|
viper.GetBool(flagOverwrite)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ExportGenesisFile(genFile, chainID, nil, appState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState)
|
||||||
|
|
||||||
|
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
|
||||||
|
return displayInfo(cdc, toPrint)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
|
||||||
|
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file")
|
||||||
|
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
136
gaia/init/init_test.go
Normal file
136
gaia/init/init_test.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server/mock"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
abciServer "github.com/tendermint/tendermint/abci/server"
|
||||||
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInitCmd(t *testing.T) {
|
||||||
|
defer server.SetupViper(t)()
|
||||||
|
defer setupClientHome(t)()
|
||||||
|
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
cfg, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
ctx := server.NewContext(cfg, logger)
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
cmd := InitCmd(ctx, cdc)
|
||||||
|
|
||||||
|
require.NoError(t, cmd.RunE(nil, []string{"gaianode-test"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupClientHome(t *testing.T) func() {
|
||||||
|
clientDir, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||||
|
require.Nil(t, err)
|
||||||
|
viper.Set(flagClientHome, clientDir)
|
||||||
|
return func() {
|
||||||
|
if err := os.RemoveAll(clientDir); err != nil {
|
||||||
|
// TODO: Handle with #870
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyState(t *testing.T) {
|
||||||
|
defer server.SetupViper(t)()
|
||||||
|
defer setupClientHome(t)()
|
||||||
|
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
cfg, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
ctx := server.NewContext(cfg, logger)
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
|
||||||
|
cmd := InitCmd(ctx, cdc)
|
||||||
|
require.NoError(t, cmd.RunE(nil, []string{"gaianode-test"}))
|
||||||
|
|
||||||
|
old := os.Stdout
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
os.Stdout = w
|
||||||
|
cmd = server.ExportCmd(ctx, cdc, nil)
|
||||||
|
|
||||||
|
err = cmd.RunE(nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
outC := make(chan string)
|
||||||
|
go func() {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, r)
|
||||||
|
outC <- buf.String()
|
||||||
|
}()
|
||||||
|
|
||||||
|
w.Close()
|
||||||
|
os.Stdout = old
|
||||||
|
out := <-outC
|
||||||
|
require.Contains(t, out, "genesis_time")
|
||||||
|
require.Contains(t, out, "chain_id")
|
||||||
|
require.Contains(t, out, "consensus_params")
|
||||||
|
require.Contains(t, out, "validators")
|
||||||
|
require.Contains(t, out, "app_hash")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartStandAlone(t *testing.T) {
|
||||||
|
home, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(home)
|
||||||
|
}()
|
||||||
|
viper.Set(cli.HomeFlag, home)
|
||||||
|
defer setupClientHome(t)()
|
||||||
|
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
cfg, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
ctx := server.NewContext(cfg, logger)
|
||||||
|
cdc := app.MakeCodec()
|
||||||
|
initCmd := InitCmd(ctx, cdc)
|
||||||
|
require.NoError(t, initCmd.RunE(nil, []string{"gaianode-test"}))
|
||||||
|
|
||||||
|
app, err := mock.NewApp(home, logger)
|
||||||
|
require.Nil(t, err)
|
||||||
|
svrAddr, _, err := server.FreeTCPAddr()
|
||||||
|
require.Nil(t, err)
|
||||||
|
svr, err := abciServer.NewServer(svrAddr, "socket", app)
|
||||||
|
require.Nil(t, err, "error creating listener")
|
||||||
|
svr.SetLogger(logger.With("module", "abci-server"))
|
||||||
|
svr.Start()
|
||||||
|
|
||||||
|
timer := time.NewTimer(time.Duration(2) * time.Second)
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
svr.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitNodeValidatorFiles(t *testing.T) {
|
||||||
|
home, err := ioutil.TempDir("", "mock-sdk-cmd")
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() {
|
||||||
|
os.RemoveAll(home)
|
||||||
|
}()
|
||||||
|
viper.Set(cli.HomeFlag, home)
|
||||||
|
viper.Set(client.FlagName, "moniker")
|
||||||
|
cfg, err := tcmd.ParseConfig()
|
||||||
|
require.Nil(t, err)
|
||||||
|
nodeID, valPubKey, err := InitializeNodeValidatorFiles(cfg)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.NotEqual(t, "", nodeID)
|
||||||
|
require.NotEqual(t, 0, len(valPubKey.Bytes()))
|
||||||
|
}
|
@ -1,7 +1,4 @@
|
|||||||
// Copyright 2016 All in Bits, inc
|
package init
|
||||||
// Modifications copyright 2019 Kava Labs
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// DONTCOVER
|
// DONTCOVER
|
||||||
|
|
||||||
@ -12,6 +9,17 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
tmconfig "github.com/tendermint/tendermint/config"
|
tmconfig "github.com/tendermint/tendermint/config"
|
||||||
@ -20,17 +28,7 @@ import (
|
|||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
|
||||||
"github.com/cosmos/cosmos-sdk/server"
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
srvconfig "github.com/cosmos/cosmos-sdk/server/config"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/genaccounts"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -38,70 +36,68 @@ var (
|
|||||||
flagNumValidators = "v"
|
flagNumValidators = "v"
|
||||||
flagOutputDir = "output-dir"
|
flagOutputDir = "output-dir"
|
||||||
flagNodeDaemonHome = "node-daemon-home"
|
flagNodeDaemonHome = "node-daemon-home"
|
||||||
flagNodeCLIHome = "node-cli-home"
|
flagNodeCliHome = "node-cli-home"
|
||||||
flagStartingIPAddress = "starting-ip-address"
|
flagStartingIPAddress = "starting-ip-address"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const nodeDirPerm = 0755
|
||||||
|
|
||||||
// get cmd to initialize all files for tendermint testnet and application
|
// get cmd to initialize all files for tendermint testnet and application
|
||||||
func testnetCmd(ctx *server.Context, cdc *codec.Codec,
|
func TestnetFilesCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
||||||
mbm module.BasicManager, genAccIterator genutil.GenesisAccountsIterator) *cobra.Command {
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "testnet",
|
Use: "testnet",
|
||||||
Short: "Initialize files for a kvd testnet",
|
Short: "Initialize files for a Gaiad testnet",
|
||||||
Long: `testnet will create "v" number of directories and populate each with
|
Long: `testnet will create "v" number of directories and populate each with
|
||||||
necessary files (private validator, genesis, config, etc.).
|
necessary files (private validator, genesis, config, etc.).
|
||||||
|
|
||||||
Note, strict routability for addresses is turned off in the config file.
|
Note, strict routability for addresses is turned off in the config file.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
kvd testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
|
gaiad testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2
|
||||||
`,
|
`,
|
||||||
RunE: func(_ *cobra.Command, _ []string) error {
|
RunE: func(_ *cobra.Command, _ []string) error {
|
||||||
config := ctx.Config
|
config := ctx.Config
|
||||||
|
return initTestnet(config, cdc)
|
||||||
outputDir := viper.GetString(flagOutputDir)
|
|
||||||
chainID := viper.GetString(client.FlagChainID)
|
|
||||||
minGasPrices := viper.GetString(server.FlagMinGasPrices)
|
|
||||||
nodeDirPrefix := viper.GetString(flagNodeDirPrefix)
|
|
||||||
nodeDaemonHome := viper.GetString(flagNodeDaemonHome)
|
|
||||||
nodeCLIHome := viper.GetString(flagNodeCLIHome)
|
|
||||||
startingIPAddress := viper.GetString(flagStartingIPAddress)
|
|
||||||
numValidators := viper.GetInt(flagNumValidators)
|
|
||||||
|
|
||||||
return InitTestnet(config, cdc, mbm, genAccIterator, outputDir, chainID, minGasPrices,
|
|
||||||
nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, numValidators)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Flags().Int(flagNumValidators, 4,
|
cmd.Flags().Int(flagNumValidators, 4,
|
||||||
"Number of validators to initialize the testnet with")
|
"Number of validators to initialize the testnet with",
|
||||||
|
)
|
||||||
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
|
cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet",
|
||||||
"Directory to store initialization data for the testnet")
|
"Directory to store initialization data for the testnet",
|
||||||
|
)
|
||||||
cmd.Flags().String(flagNodeDirPrefix, "node",
|
cmd.Flags().String(flagNodeDirPrefix, "node",
|
||||||
"Prefix the directory name for each node with (node results in node0, node1, ...)")
|
"Prefix the directory name for each node with (node results in node0, node1, ...)",
|
||||||
cmd.Flags().String(flagNodeDaemonHome, "kvd",
|
)
|
||||||
"Home directory of the node's daemon configuration")
|
cmd.Flags().String(flagNodeDaemonHome, "gaiad",
|
||||||
cmd.Flags().String(flagNodeCLIHome, "kvcli",
|
"Home directory of the node's daemon configuration",
|
||||||
"Home directory of the node's cli configuration")
|
)
|
||||||
|
cmd.Flags().String(flagNodeCliHome, "gaiacli",
|
||||||
|
"Home directory of the node's cli configuration",
|
||||||
|
)
|
||||||
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1",
|
cmd.Flags().String(flagStartingIPAddress, "192.168.0.1",
|
||||||
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
|
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
|
||||||
|
|
||||||
cmd.Flags().String(
|
cmd.Flags().String(
|
||||||
client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
|
client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created",
|
||||||
|
)
|
||||||
cmd.Flags().String(
|
cmd.Flags().String(
|
||||||
server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
|
server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
|
||||||
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
|
"Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)",
|
||||||
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeDirPerm = 0755
|
func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error {
|
||||||
|
var chainID string
|
||||||
|
|
||||||
// Initialize the testnet
|
outDir := viper.GetString(flagOutputDir)
|
||||||
func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicManager,
|
numValidators := viper.GetInt(flagNumValidators)
|
||||||
genAccIterator genutil.GenesisAccountsIterator,
|
|
||||||
outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome,
|
|
||||||
nodeCLIHome, startingIPAddress string, numValidators int) error {
|
|
||||||
|
|
||||||
|
chainID = viper.GetString(client.FlagChainID)
|
||||||
if chainID == "" {
|
if chainID == "" {
|
||||||
chainID = "chain-" + cmn.RandStr(6)
|
chainID = "chain-" + cmn.RandStr(6)
|
||||||
}
|
}
|
||||||
@ -110,47 +106,49 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
nodeIDs := make([]string, numValidators)
|
nodeIDs := make([]string, numValidators)
|
||||||
valPubKeys := make([]crypto.PubKey, numValidators)
|
valPubKeys := make([]crypto.PubKey, numValidators)
|
||||||
|
|
||||||
appConfig := srvconfig.DefaultConfig()
|
gaiaConfig := srvconfig.DefaultConfig()
|
||||||
appConfig.MinGasPrices = minGasPrices
|
gaiaConfig.MinGasPrices = viper.GetString(server.FlagMinGasPrices)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
accs []genaccounts.GenesisAccount
|
accs []app.GenesisAccount
|
||||||
genFiles []string
|
genFiles []string
|
||||||
)
|
)
|
||||||
|
|
||||||
// generate private keys, node IDs, and initial transactions
|
// generate private keys, node IDs, and initial transactions
|
||||||
for i := 0; i < numValidators; i++ {
|
for i := 0; i < numValidators; i++ {
|
||||||
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
nodeDirName := fmt.Sprintf("%s%d", viper.GetString(flagNodeDirPrefix), i)
|
||||||
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
|
nodeDaemonHomeName := viper.GetString(flagNodeDaemonHome)
|
||||||
clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome)
|
nodeCliHomeName := viper.GetString(flagNodeCliHome)
|
||||||
gentxsDir := filepath.Join(outputDir, "gentxs")
|
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
|
||||||
|
clientDir := filepath.Join(outDir, nodeDirName, nodeCliHomeName)
|
||||||
|
gentxsDir := filepath.Join(outDir, "gentxs")
|
||||||
|
|
||||||
config.SetRoot(nodeDir)
|
config.SetRoot(nodeDir)
|
||||||
|
|
||||||
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.MkdirAll(clientDir, nodeDirPerm)
|
err = os.MkdirAll(clientDir, nodeDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
monikers = append(monikers, nodeDirName)
|
monikers = append(monikers, nodeDirName)
|
||||||
config.Moniker = nodeDirName
|
config.Moniker = nodeDirName
|
||||||
|
|
||||||
ip, err := getIP(i, startingIPAddress)
|
ip, err := getIP(i, viper.GetString(flagStartingIPAddress))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config)
|
nodeIDs[i], valPubKeys[i], err = InitializeNodeValidatorFiles(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +157,7 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
|
|
||||||
buf := client.BufferStdin()
|
buf := client.BufferStdin()
|
||||||
prompt := fmt.Sprintf(
|
prompt := fmt.Sprintf(
|
||||||
"Password for account '%s' (default %s):", nodeDirName, client.DefaultKeyPass,
|
"Password for account '%s' (default %s):", nodeDirName, app.DefaultKeyPass,
|
||||||
)
|
)
|
||||||
|
|
||||||
keyPass, err := client.GetPassword(prompt, buf)
|
keyPass, err := client.GetPassword(prompt, buf)
|
||||||
@ -171,12 +169,12 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
}
|
}
|
||||||
|
|
||||||
if keyPass == "" {
|
if keyPass == "" {
|
||||||
keyPass = client.DefaultKeyPass
|
keyPass = app.DefaultKeyPass
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
|
addr, secret, err := server.GenerateSaveCoinKey(clientDir, nodeDirName, keyPass, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +193,7 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
|
|
||||||
accTokens := sdk.TokensFromTendermintPower(1000)
|
accTokens := sdk.TokensFromTendermintPower(1000)
|
||||||
accStakingTokens := sdk.TokensFromTendermintPower(500)
|
accStakingTokens := sdk.TokensFromTendermintPower(500)
|
||||||
accs = append(accs, genaccounts.GenesisAccount{
|
accs = append(accs, app.GenesisAccount{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Coins: sdk.Coins{
|
Coins: sdk.Coins{
|
||||||
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens),
|
sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens),
|
||||||
@ -209,7 +207,7 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
valPubKeys[i],
|
valPubKeys[i],
|
||||||
sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
|
sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
|
||||||
staking.NewDescription(nodeDirName, "", "", ""),
|
staking.NewDescription(nodeDirName, "", "", ""),
|
||||||
staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||||
sdk.OneInt(),
|
sdk.OneInt(),
|
||||||
)
|
)
|
||||||
kb, err := keys.NewKeyBaseFromDir(clientDir)
|
kb, err := keys.NewKeyBaseFromDir(clientDir)
|
||||||
@ -217,40 +215,38 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
|
tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{}, memo)
|
||||||
txBldr := auth.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
|
txBldr := authtx.NewTxBuilderFromCLI().WithChainID(chainID).WithMemo(memo).WithKeybase(kb)
|
||||||
|
|
||||||
signedTx, err := txBldr.SignStdTx(nodeDirName, client.DefaultKeyPass, tx, false)
|
signedTx, err := txBldr.SignStdTx(nodeDirName, app.DefaultKeyPass, tx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
txBytes, err := cdc.MarshalJSON(signedTx)
|
txBytes, err := cdc.MarshalJSON(signedTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// gather gentxs folder
|
// gather gentxs folder
|
||||||
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
|
err = writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Rename config file to server.toml as it's not particular to Gaia
|
gaiaConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml")
|
||||||
// (REF: https://github.com/cosmos/cosmos-sdk/issues/4125).
|
srvconfig.WriteConfigFile(gaiaConfigFilePath, gaiaConfig)
|
||||||
appConfigFilePath := filepath.Join(nodeDir, "config/gaiad.toml")
|
|
||||||
srvconfig.WriteConfigFile(appConfigFilePath, appConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initGenFiles(cdc, mbm, chainID, accs, genFiles, numValidators); err != nil {
|
if err := initGenFiles(cdc, chainID, accs, genFiles, numValidators); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := collectGenFiles(
|
err := collectGenFiles(
|
||||||
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
|
cdc, config, chainID, monikers, nodeIDs, valPubKeys, numValidators,
|
||||||
outputDir, nodeDirPrefix, nodeDaemonHome, genAccIterator,
|
outDir, viper.GetString(flagNodeDirPrefix), viper.GetString(flagNodeDaemonHome),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -260,13 +256,13 @@ func InitTestnet(config *tmconfig.Config, cdc *codec.Codec, mbm module.BasicMana
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGenFiles(cdc *codec.Codec, mbm module.BasicManager, chainID string,
|
func initGenFiles(
|
||||||
accs []genaccounts.GenesisAccount, genFiles []string, numValidators int) error {
|
cdc *codec.Codec, chainID string, accs []app.GenesisAccount,
|
||||||
|
genFiles []string, numValidators int,
|
||||||
|
) error {
|
||||||
|
|
||||||
appGenState := mbm.DefaultGenesis()
|
appGenState := app.NewDefaultGenesisState()
|
||||||
|
appGenState.Accounts = accs
|
||||||
// set the accounts in the genesis state
|
|
||||||
appGenState = genaccounts.SetGenesisStateInAppState(cdc, appGenState, accs)
|
|
||||||
|
|
||||||
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
|
appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -285,36 +281,37 @@ func initGenFiles(cdc *codec.Codec, mbm module.BasicManager, chainID string,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectGenFiles(
|
func collectGenFiles(
|
||||||
cdc *codec.Codec, config *tmconfig.Config, chainID string,
|
cdc *codec.Codec, config *tmconfig.Config, chainID string,
|
||||||
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
|
monikers, nodeIDs []string, valPubKeys []crypto.PubKey,
|
||||||
numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string,
|
numValidators int, outDir, nodeDirPrefix, nodeDaemonHomeName string,
|
||||||
genAccIterator genutil.GenesisAccountsIterator) error {
|
) error {
|
||||||
|
|
||||||
var appState json.RawMessage
|
var appState json.RawMessage
|
||||||
genTime := tmtime.Now()
|
genTime := tmtime.Now()
|
||||||
|
|
||||||
for i := 0; i < numValidators; i++ {
|
for i := 0; i < numValidators; i++ {
|
||||||
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
||||||
nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome)
|
nodeDir := filepath.Join(outDir, nodeDirName, nodeDaemonHomeName)
|
||||||
gentxsDir := filepath.Join(outputDir, "gentxs")
|
gentxsDir := filepath.Join(outDir, "gentxs")
|
||||||
moniker := monikers[i]
|
moniker := monikers[i]
|
||||||
config.Moniker = nodeDirName
|
config.Moniker = nodeDirName
|
||||||
|
|
||||||
config.SetRoot(nodeDir)
|
config.SetRoot(nodeDir)
|
||||||
|
|
||||||
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
|
nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
|
||||||
initCfg := genutil.NewInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
|
initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
|
||||||
|
|
||||||
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator)
|
nodeAppState, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -327,7 +324,7 @@ func collectGenFiles(
|
|||||||
genFile := config.GenesisFile()
|
genFile := config.GenesisFile()
|
||||||
|
|
||||||
// overwrite each validator's genesis file to have a canonical genesis time
|
// overwrite each validator's genesis file to have a canonical genesis time
|
||||||
err = genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
|
err = ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -336,28 +333,25 @@ func collectGenFiles(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIP(i int, startingIPAddr string) (ip string, err error) {
|
func getIP(i int, startingIPAddr string) (string, error) {
|
||||||
|
var (
|
||||||
|
ip string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
if len(startingIPAddr) == 0 {
|
if len(startingIPAddr) == 0 {
|
||||||
ip, err = server.ExternalIP()
|
ip, err = server.ExternalIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return ip, nil
|
} else {
|
||||||
}
|
ip, err = calculateIP(startingIPAddr, i)
|
||||||
return calculateIP(startingIPAddr, i)
|
if err != nil {
|
||||||
}
|
return "", err
|
||||||
|
}
|
||||||
func calculateIP(ip string, i int) (string, error) {
|
|
||||||
ipv4 := net.ParseIP(ip).To4()
|
|
||||||
if ipv4 == nil {
|
|
||||||
return "", fmt.Errorf("%v: non ipv4 address", ip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for j := 0; j < i; j++ {
|
return ip, nil
|
||||||
ipv4[3]++
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipv4.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(name string, dir string, contents []byte) error {
|
func writeFile(name string, dir string, contents []byte) error {
|
||||||
@ -376,3 +370,16 @@ func writeFile(name string, dir string, contents []byte) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateIP(ip string, i int) (string, error) {
|
||||||
|
ipv4 := net.ParseIP(ip).To4()
|
||||||
|
if ipv4 == nil {
|
||||||
|
return "", fmt.Errorf("%v: non ipv4 address", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
ipv4[3]++
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4.String(), nil
|
||||||
|
}
|
114
gaia/init/utils.go
Normal file
114
gaia/init/utils.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/libs/common"
|
||||||
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
"github.com/tendermint/tendermint/privval"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExportGenesisFile creates and writes the genesis configuration to disk. An
|
||||||
|
// error is returned if building or writing the configuration to file fails.
|
||||||
|
func ExportGenesisFile(
|
||||||
|
genFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
genDoc := types.GenesisDoc{
|
||||||
|
ChainID: chainID,
|
||||||
|
Validators: validators,
|
||||||
|
AppState: appState,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := genDoc.ValidateAndComplete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return genDoc.SaveAs(genFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportGenesisFileWithTime creates and writes the genesis configuration to disk.
|
||||||
|
// An error is returned if building or writing the configuration to file fails.
|
||||||
|
func ExportGenesisFileWithTime(
|
||||||
|
genFile, chainID string, validators []types.GenesisValidator,
|
||||||
|
appState json.RawMessage, genTime time.Time,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
genDoc := types.GenesisDoc{
|
||||||
|
GenesisTime: genTime,
|
||||||
|
ChainID: chainID,
|
||||||
|
Validators: validators,
|
||||||
|
AppState: appState,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := genDoc.ValidateAndComplete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return genDoc.SaveAs(genFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
|
||||||
|
func InitializeNodeValidatorFiles(
|
||||||
|
config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error,
|
||||||
|
) {
|
||||||
|
|
||||||
|
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
||||||
|
if err != nil {
|
||||||
|
return nodeID, valPubKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeID = string(nodeKey.ID())
|
||||||
|
server.UpgradeOldPrivValFile(config)
|
||||||
|
|
||||||
|
pvKeyFile := config.PrivValidatorKeyFile()
|
||||||
|
if err := common.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil {
|
||||||
|
return nodeID, valPubKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pvStateFile := config.PrivValidatorStateFile()
|
||||||
|
if err := common.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil {
|
||||||
|
return nodeID, valPubKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
valPubKey = privval.LoadOrGenFilePV(pvKeyFile, pvStateFile).GetPubKey()
|
||||||
|
|
||||||
|
return nodeID, valPubKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadGenesisDoc reads and unmarshals GenesisDoc from the given file.
|
||||||
|
func LoadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) {
|
||||||
|
genContents, err := ioutil.ReadFile(genFile)
|
||||||
|
if err != nil {
|
||||||
|
return genDoc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cdc.UnmarshalJSON(genContents, &genDoc); err != nil {
|
||||||
|
return genDoc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return genDoc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeEmptyGenesis(
|
||||||
|
cdc *codec.Codec, genFile, chainID string, overwrite bool,
|
||||||
|
) (appState json.RawMessage, err error) {
|
||||||
|
|
||||||
|
if !overwrite && common.FileExists(genFile) {
|
||||||
|
return nil, fmt.Errorf("genesis.json file already exists: %v", genFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return codec.MarshalJSONIndent(cdc, app.NewDefaultGenesisState())
|
||||||
|
}
|
49
gaia/init/utils_test.go
Normal file
49
gaia/init/utils_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExportGenesisFileWithTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
dir, cleanup := tests.NewTestCaseDir(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
fname := filepath.Join(dir, "genesis.json")
|
||||||
|
require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadGenesisDoc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
dir, cleanup := tests.NewTestCaseDir(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
fname := filepath.Join(dir, "genesis.json")
|
||||||
|
require.NoError(t, ExportGenesisFileWithTime(fname, "test", nil, json.RawMessage(""), time.Now()))
|
||||||
|
|
||||||
|
_, err := LoadGenesisDoc(codec.Cdc, fname)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Non-existing file
|
||||||
|
_, err = LoadGenesisDoc(codec.Cdc, "non-existing-file")
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
malformedFilename := filepath.Join(dir, "malformed")
|
||||||
|
malformedFile, err := os.Create(malformedFilename)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fmt.Fprint(malformedFile, "invalidjson")
|
||||||
|
malformedFile.Close()
|
||||||
|
// Non-existing file
|
||||||
|
_, err = LoadGenesisDoc(codec.Cdc, malformedFilename)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
51
gaia/init/validate_genesis.go
Normal file
51
gaia/init/validate_genesis.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package init
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/server"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate genesis command takes
|
||||||
|
func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "validate-genesis [file]",
|
||||||
|
Args: cobra.RangeArgs(0, 1),
|
||||||
|
Short: "validates the genesis file at the default location or at the location passed as an arg",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||||
|
|
||||||
|
// Load default if passed no args, otherwise load passed file
|
||||||
|
var genesis string
|
||||||
|
if len(args) == 0 {
|
||||||
|
genesis = ctx.Config.GenesisFile()
|
||||||
|
} else {
|
||||||
|
genesis = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis)
|
||||||
|
|
||||||
|
var genDoc types.GenesisDoc
|
||||||
|
if genDoc, err = LoadGenesisDoc(cdc, genesis); err != nil {
|
||||||
|
return fmt.Errorf("Error loading genesis doc from %s: %s", genesis, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var genstate app.GenesisState
|
||||||
|
if err = cdc.UnmarshalJSON(genDoc.AppState, &genstate); err != nil {
|
||||||
|
return fmt.Errorf("Error unmarshaling genesis doc %s: %s", genesis, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = app.GaiaValidateGenesisState(genstate); err != nil {
|
||||||
|
return fmt.Errorf("Error validating genesis file %s: %s", genesis, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("File at %s is a valid genesis file for gaiad\n", genesis)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
7
gaia/testnets/README.md
Normal file
7
gaia/testnets/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# DEPRECATED
|
||||||
|
|
||||||
|
The content of this file was moved to the `/docs` folder and is hosted on the
|
||||||
|
[website](https://cosmos.network/docs/getting-started/full-node.html#run-a-full-node).
|
||||||
|
|
||||||
|
The rest of this folder was moved to the [testnets
|
||||||
|
repo](https://github.com/cosmos/testnets).
|
132
gaia/testnets/STATUS.md
Normal file
132
gaia/testnets/STATUS.md
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# DEPRECATED
|
||||||
|
|
||||||
|
See [testnets repo](https://github.com/cosmos/testnets).
|
||||||
|
|
||||||
|
## *July 22, 2018, 5:30 EST* - Gaia-7001 Consensus Failure
|
||||||
|
|
||||||
|
- [Consensus Failure at Block 24570](https://github.com/cosmos/cosmos-sdk/issues/1787)
|
||||||
|
|
||||||
|
|
||||||
|
## *July 17, 2018, 4:00 EST* - New Testnet Gaia-7001
|
||||||
|
|
||||||
|
- New testnet with fixes for the genesis file
|
||||||
|
- Increased max validators to 128
|
||||||
|
|
||||||
|
## *July 17, 2018, 3:00 EST* - Gaia-7000 consensus failure
|
||||||
|
|
||||||
|
- Misconfiguration in the genesis file led to a consensus failure
|
||||||
|
- New genesis file for gaia-7001 will be up soon
|
||||||
|
|
||||||
|
## *July 17, 2018, 2:40 EST* - Gaia-7000 is making blocks!
|
||||||
|
|
||||||
|
- Gaia-7000 is live and making blocks!
|
||||||
|
|
||||||
|
## *July 16, 2018, 17:00 EST* - New Testnet Gaia-7000
|
||||||
|
|
||||||
|
- Gaia-7000 is up!
|
||||||
|
- 108 validators in the genesis.json file.
|
||||||
|
|
||||||
|
## *July 2, 2018, 1:00 EST* - Gaia-6002 slashing failure
|
||||||
|
|
||||||
|
- Gaia-6002 has been halted due to a slashing issue.
|
||||||
|
- The team is taking its time to look into this Gaia-7000 will be introduced this week.
|
||||||
|
|
||||||
|
## *June 13, 2018, 17:00 EST* - Gaia-6002 is making blocks!
|
||||||
|
|
||||||
|
- Gaia-6002 is live and making blocks
|
||||||
|
- Absent validators have been slashed and jailed
|
||||||
|
- Currently live with 17 validators
|
||||||
|
|
||||||
|
## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002
|
||||||
|
|
||||||
|
- After fixing bugs from gaia-6001, especially [issue
|
||||||
|
#1197](https://github.com/cosmos/cosmos-sdk/issues/1197), we are announcing a
|
||||||
|
new testnet, Gaia-6002
|
||||||
|
- Gaia-6002 has the same genesis file as Gaia-6001, just with the chain-id
|
||||||
|
updated
|
||||||
|
- Update from previous testnet [here](https://github.com/cosmos/cosmos-sdk/tree/master/cmd/gaia/testnets#upgrading-from-previous-testnet)
|
||||||
|
|
||||||
|
## *June 13, 2018, 4:30 EST* - New Release
|
||||||
|
|
||||||
|
- Released gaia
|
||||||
|
[v0.19.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.19.0)
|
||||||
|
- Includes various bug-fixes for staking found on Gaia-6001
|
||||||
|
|
||||||
|
## *June 13, 2018, 2:30 EST* - Published Postmortem of Gaia-6001 failure
|
||||||
|
|
||||||
|
- A bug in the design of the staking data model caused a sanity check to fail
|
||||||
|
- Full writeup
|
||||||
|
[here](https://github.com/cosmos/cosmos-sdk/issues/1197#issuecomment-396823021)
|
||||||
|
|
||||||
|
## *June 10, 2018, 8:30 EST* - Gaia-6001 consensus failure
|
||||||
|
|
||||||
|
- Validator unbonding and revocation activity caused a consensus failure
|
||||||
|
- There is a bug in the staking module that must be fixed
|
||||||
|
- The team is taking its time to look into this and release a fix following a
|
||||||
|
proper protocol for hotfix upgrades to the testnet
|
||||||
|
- Please stay tuned!
|
||||||
|
|
||||||
|
## *June 9, 2018, 14:00 EST* - New Release
|
||||||
|
|
||||||
|
- Released gaia
|
||||||
|
[v0.18.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.18.0) with
|
||||||
|
update for Tendermint
|
||||||
|
[v0.20.0](https://github.com/tendermint/tendermint/releases/tag/v0.20.0)
|
||||||
|
- Includes bug fix for declaring candidacy from the command line
|
||||||
|
|
||||||
|
## *June 8, 2018, 23:30 EST* - Gaia-6001 is making blocks
|
||||||
|
|
||||||
|
- +2/3 of the voting power is finally online for Gaia-6001 and it is making
|
||||||
|
blocks!
|
||||||
|
- This is a momentous achievement - a successful asynchronous decentralized
|
||||||
|
testnet launch
|
||||||
|
- Congrats everyone!
|
||||||
|
|
||||||
|
## *June 8, 2018, 12:00 EST* - New Testnet Gaia-6001
|
||||||
|
|
||||||
|
- After some confusion around testnet deployment and a contention testnet
|
||||||
|
hardfork, a new genesis file and network was released for `gaia-6001`
|
||||||
|
|
||||||
|
## *June 7, 2018, 9:00 EST* - New Testnet Gaia-6000
|
||||||
|
|
||||||
|
- Released a new `genesis.json` file for `gaia-6000`
|
||||||
|
- Initial validators include those that were most active in
|
||||||
|
the gaia-5001 testnet
|
||||||
|
- Join the network via gaia `v0.18.0-rc0`
|
||||||
|
|
||||||
|
## *June 5, 2018, 21:00 EST* - New Release
|
||||||
|
|
||||||
|
- Released gaia
|
||||||
|
[v0.17.5](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.17.5)
|
||||||
|
with update for Tendermint
|
||||||
|
[v0.19.9](https://github.com/tendermint/tendermint/releases/tag/v0.19.9)
|
||||||
|
- Fixes many bugs!
|
||||||
|
- evidence gossipping
|
||||||
|
- mempool deadlock
|
||||||
|
- WAL panic
|
||||||
|
- memory leak
|
||||||
|
- Please update to this to put a stop to the rampant invalid evidence gossiping
|
||||||
|
:)
|
||||||
|
|
||||||
|
## *May 31, 2018, 14:00 EST* - New Release
|
||||||
|
|
||||||
|
- Released gaia
|
||||||
|
[v0.17.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.17.4) with update for Tendermint v0.19.7
|
||||||
|
- Fixes a WAL bug and some more
|
||||||
|
- Please update to this if you have trouble restarting a node
|
||||||
|
|
||||||
|
## *May 31, 2018, 2:00 EST* - Testnet Halt
|
||||||
|
|
||||||
|
- A validator equivocated last week and Evidence is being rampantly gossipped
|
||||||
|
- Peers that can't process the evidence (either too far behind or too far ahead) are disconnecting from the peers that
|
||||||
|
sent it, causing high peer turn-over
|
||||||
|
- The high peer turn-over may be causing a memory-leak, resulting in some nodes
|
||||||
|
crashing and the testnet halting
|
||||||
|
- We need to fix some issues in the EvidenceReactor to address this and also
|
||||||
|
investigate the possible memory-leak
|
||||||
|
|
||||||
|
## *May 29, 2018* - New Release
|
||||||
|
|
||||||
|
- Released v0.17.3 with update for Tendermint v0.19.6
|
||||||
|
- Fixes fast-sync bug
|
||||||
|
- Please update to this to sync with the testnet
|
34
go.mod
34
go.mod
@ -1,35 +1,15 @@
|
|||||||
module github.com/kava-labs/_
|
module github.com/Kava-Labs/kava
|
||||||
|
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c // indirect
|
github.com/cosmos/cosmos-sdk v0.34.7
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57
|
github.com/rakyll/statik v0.1.4
|
||||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d // indirect
|
github.com/spf13/cobra v0.0.3
|
||||||
github.com/google/gofuzz v1.0.0 // indirect
|
github.com/spf13/viper v1.0.3
|
||||||
github.com/gorilla/mux v1.7.2 // indirect
|
github.com/stretchr/testify v1.3.0
|
||||||
github.com/magiconair/properties v1.8.1 // indirect
|
github.com/tendermint/go-amino v0.14.1
|
||||||
github.com/mattn/go-isatty v0.0.8 // indirect
|
|
||||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
|
||||||
github.com/onsi/gomega v1.5.0 // indirect
|
|
||||||
github.com/otiai10/copy v1.0.1
|
|
||||||
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776 // indirect
|
|
||||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
|
||||||
github.com/prometheus/common v0.4.1 // indirect
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389 // indirect
|
|
||||||
github.com/rakyll/statik v0.1.6 // indirect
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
|
|
||||||
github.com/spf13/afero v1.2.2 // indirect
|
|
||||||
github.com/spf13/cobra v0.0.4
|
|
||||||
github.com/spf13/viper v1.4.0
|
|
||||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
|
||||||
github.com/tendermint/go-amino v0.15.0
|
|
||||||
github.com/tendermint/tendermint v0.31.5
|
github.com/tendermint/tendermint v0.31.5
|
||||||
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e // indirect
|
|
||||||
golang.org/x/text v0.3.2 // indirect
|
|
||||||
google.golang.org/appengine v1.4.0 // indirect
|
|
||||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5
|
replace golang.org/x/crypto => github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5
|
||||||
|
63
go.sum
63
go.sum
@ -9,17 +9,17 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8=
|
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/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
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.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c h1:aEbSeNALREWXk0G7UdNhR3ayBV7tZ4M2PNmnrCAph6Q=
|
|
||||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
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-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/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/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/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||||
@ -34,10 +34,12 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
|
|||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/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-20180928190104-399ea9e2e55f/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.28.2-0.20190606154315-3180e68c7b57 h1:bLViLq/BPtEMhxsYhE5NxxTM664Bt1Z+vM6SJflQXTU=
|
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57/go.mod h1:MvaJDmjgAK7X1rTnpk8+c6tUFfIZ++iuNCp2sUWzprM=
|
github.com/cosmos/cosmos-sdk v0.28.2-0.20190606154315-3180e68c7b57/go.mod h1:MvaJDmjgAK7X1rTnpk8+c6tUFfIZ++iuNCp2sUWzprM=
|
||||||
|
github.com/cosmos/cosmos-sdk v0.34.7 h1:S6yMldhrAMB/SDMsR2Hvz05tpUpQQGCHf0INXAZ7VW0=
|
||||||
|
github.com/cosmos/cosmos-sdk v0.34.7/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I=
|
||||||
|
github.com/cosmos/cosmos-sdk v0.35.0 h1:EPeie1aKHwnXtTzKggvabG7aAPN+DDmju2xquvjFwao=
|
||||||
|
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/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||||
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/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||||
github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
|
github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
|
||||||
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
|
||||||
@ -58,15 +60,15 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
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.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
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.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
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 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
@ -75,8 +77,8 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
|
|||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
|
||||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
|
|
||||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
@ -100,11 +102,11 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
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/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA=
|
||||||
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
@ -118,14 +120,14 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/otiai10/copy v1.0.1 h1:gtBjD8aq4nychvRZ2CyJvFWAw0aja+VHazDdruZKGZA=
|
github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1/go.mod h1:pXzZSDlN+HPzSdyIBnKNN9ptD9Hx7iZMWIJPTwo4FPE=
|
||||||
github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
|
github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
|
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
|
||||||
github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
|
github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
|
||||||
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
|
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
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.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
@ -133,30 +135,30 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
|
||||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
|
||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
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-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
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.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs=
|
||||||
github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389 h1:F/k2nob1S9M6v5Xkq7KjSTQirOYaYQord0jR4TwyVmY=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190523193104-a7aeb8df3389/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rakyll/statik v0.1.4 h1:zCS/YQCxfo/fQjCtGVGIyWGFnRbQ18Y55mhS3XPE+Oo=
|
||||||
github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
||||||
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
|
|
||||||
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-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/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/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 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||||
@ -166,38 +168,40 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
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.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/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE=
|
|
||||||
github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
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 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
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.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.0.3 h1:z5LPUc2iz8VLT5Cw1UyrESG6FUUnOGecYGY08BLKSuc=
|
||||||
github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
|
||||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
|
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/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
|
||||||
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 h1:u8i49c+BxloX3XQ55cvzFNXplizZP/q00i+IlttUjAU=
|
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 h1:u8i49c+BxloX3XQ55cvzFNXplizZP/q00i+IlttUjAU=
|
||||||
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
|
github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
|
||||||
github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk=
|
github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk=
|
||||||
|
github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso=
|
||||||
github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
|
github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
|
||||||
github.com/tendermint/iavl v0.12.2 h1:Ls5p5VINCM1HRT9g5Vvs2zmDOCU/CCIvIHzd/pZ8P0E=
|
github.com/tendermint/iavl v0.12.1 h1:JDfyhM/Hhrumu1CL1Nxrypm8sNTPYqmeHo1IZLiJoXM=
|
||||||
|
github.com/tendermint/iavl v0.12.1/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM=
|
||||||
github.com/tendermint/iavl v0.12.2/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM=
|
github.com/tendermint/iavl v0.12.2/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM=
|
||||||
github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4=
|
github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4=
|
||||||
github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc=
|
github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc=
|
||||||
@ -211,6 +215,9 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|||||||
go.uber.org/atomic v1.4.0/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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -219,11 +226,11 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20181220203305-927f97764cc3/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-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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
|
||||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -237,11 +244,11 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20181205085412-a5c9d58dba9a/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190527104216-9cd6430ef91e h1:Pzdi8HRppinixnWWzN6KSa0QkBM+GKsTJaWwwfJskNw=
|
|
||||||
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190527104216-9cd6430ef91e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/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-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -251,12 +258,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.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 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
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-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69 h1:4rNOqY4ULrKzS6twXa619uQgI7h9PaVd4ZhjFQ7C5zs=
|
|
||||||
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||||
|
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
|
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
Loading…
Reference in New Issue
Block a user