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 = "kava" // DefaultKeyPass contains the default key password for genesis transactions DefaultKeyPass = "12345678" ) // default home directories for expected binaries var ( DefaultCLIHome = os.ExpandEnv("$HOME/.kvcli") DefaultNodeHome = os.ExpandEnv("$HOME/.kvd") ) // 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 // 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 } // 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) 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), } 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 *App) 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 *App) 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 *App) 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 := ValidateGenesisState(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 app initialization func (app *App) 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 *App) 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) }