package app import ( "encoding/json" "fmt" "io" "os" "sort" 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" 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/stake" 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" tmtypes "github.com/tendermint/tendermint/types" ) 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 // keys to access the substores keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey keyStake *sdk.KVStoreKey tkeyStake *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 stakeKeeper stake.Keeper slashingKeeper slashing.Keeper mintKeeper mint.Keeper distrKeeper distr.Keeper govKeeper gov.Keeper paramsKeeper params.Keeper } // NewGaiaApp returns a reference to an initialized GaiaApp. func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, 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, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), keyStake: sdk.NewKVStoreKey("stake"), tkeyStake: sdk.NewTransientStoreKey("transient_stake"), keyMint: sdk.NewKVStoreKey("mint"), keyDistr: sdk.NewKVStoreKey("distr"), tkeyDistr: sdk.NewTransientStoreKey("transient_distr"), keySlashing: sdk.NewKVStoreKey("slashing"), keyGov: sdk.NewKVStoreKey("gov"), keyFeeCollection: sdk.NewKVStoreKey("fee"), keyParams: sdk.NewKVStoreKey("params"), tkeyParams: sdk.NewTransientStoreKey("transient_params"), } // define the accountKeeper app.accountKeeper = auth.NewAccountKeeper( app.cdc, app.keyAccount, // target store auth.ProtoBaseAccount, // prototype ) // add handlers app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper( app.cdc, app.keyFeeCollection, ) app.paramsKeeper = params.NewKeeper( app.cdc, app.keyParams, app.tkeyParams, ) stakeKeeper := stake.NewKeeper( app.cdc, app.keyStake, app.tkeyStake, app.bankKeeper, app.paramsKeeper.Subspace(stake.DefaultParamspace), stake.DefaultCodespace, ) app.mintKeeper = mint.NewKeeper(app.cdc, app.keyMint, app.paramsKeeper.Subspace(mint.DefaultParamspace), &stakeKeeper, app.feeCollectionKeeper, ) app.distrKeeper = distr.NewKeeper( app.cdc, app.keyDistr, app.paramsKeeper.Subspace(distr.DefaultParamspace), app.bankKeeper, &stakeKeeper, app.feeCollectionKeeper, distr.DefaultCodespace, ) app.slashingKeeper = slashing.NewKeeper( app.cdc, app.keySlashing, &stakeKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace, ) app.govKeeper = gov.NewKeeper( app.cdc, app.keyGov, app.paramsKeeper, app.paramsKeeper.Subspace(gov.DefaultParamspace), app.bankKeeper, &stakeKeeper, gov.DefaultCodespace, ) // register the staking hooks // NOTE: stakeKeeper above are passed by reference, // so that it can be modified like below: app.stakeKeeper = *stakeKeeper.SetHooks( NewHooks(app.distrKeeper.Hooks(), app.slashingKeeper.Hooks())) // register message routes app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). AddRoute("stake", stake.NewHandler(app.stakeKeeper)). AddRoute("distr", distr.NewHandler(app.distrKeeper)). AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). AddRoute("gov", gov.NewHandler(app.govKeeper)) app.QueryRouter(). AddRoute("gov", gov.NewQuerier(app.govKeeper)). AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc)) // initialize BaseApp app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyStake, app.keyMint, app.keyDistr, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams) app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper)) app.MountStoresTransient(app.tkeyParams, app.tkeyStake, app.tkeyDistr) app.SetEndBlocker(app.EndBlocker) 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) stake.RegisterCodec(cdc) distr.RegisterCodec(cdc) slashing.RegisterCodec(cdc) gov.RegisterCodec(cdc) auth.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 { tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper) // distribute rewards from previous block distr.BeginBlocker(ctx, req, app.distrKeeper) // mint new tokens for this new block mint.BeginBlocker(ctx, app.mintKeeper) 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 := stake.EndBlocker(ctx, app.stakeKeeper) app.assertRuntimeInvariants() 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 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, "") } // sort by account number to maintain consistency sort.Slice(genesisState.Accounts, func(i, j int) bool { return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber }) // load the accounts for _, gacc := range genesisState.Accounts { acc := gacc.ToAccount() acc.AccountNumber = app.accountKeeper.GetNextAccountNumber(ctx) app.accountKeeper.SetAccount(ctx, acc) } // load the initial stake information validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData) if err != nil { panic(err) // TODO find a way to do this w/o panics } // initialize module-specific stores auth.InitGenesis(ctx, app.feeCollectionKeeper, genesisState.AuthData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakeData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) distr.InitGenesis(ctx, app.distrKeeper, genesisState.DistrData) // validate genesis state err = GaiaValidateGenesisState(genesisState) if 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.stakeKeeper.ApplyAndReturnValidatorSetUpdates(ctx) } // 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)) } } } return abci.ResponseInitChain{ Validators: validators, } } // export the state of gaia for a genesis file func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { ctx := app.NewContext(true, abci.Header{}) // 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.feeCollectionKeeper), stake.ExportGenesis(ctx, app.stakeKeeper), mint.ExportGenesis(ctx, app.mintKeeper), distr.ExportGenesis(ctx, app.distrKeeper), gov.ExportGenesis(ctx, app.govKeeper), slashing.ExportGenesis(ctx, app.slashingKeeper), ) appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } validators = stake.WriteValidators(ctx, app.stakeKeeper) return appState, validators, nil } // load a particular height func (app *GaiaApp) LoadHeight(height int64) error { return app.LoadVersion(height, app.keyMain) } //______________________________________________________________________________________________ // Combined Staking Hooks type Hooks struct { dh distr.Hooks sh slashing.Hooks } func NewHooks(dh distr.Hooks, sh slashing.Hooks) Hooks { return Hooks{dh, sh} } var _ sdk.StakingHooks = Hooks{} // nolint func (h Hooks) OnValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorCreated(ctx, valAddr) h.sh.OnValidatorCreated(ctx, valAddr) } func (h Hooks) OnValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) { h.dh.OnValidatorModified(ctx, valAddr) h.sh.OnValidatorModified(ctx, valAddr) } func (h Hooks) OnValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorRemoved(ctx, consAddr, valAddr) h.sh.OnValidatorRemoved(ctx, consAddr, valAddr) } func (h Hooks) OnValidatorBonded(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorBonded(ctx, consAddr, valAddr) h.sh.OnValidatorBonded(ctx, consAddr, valAddr) } func (h Hooks) OnValidatorPowerDidChange(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorPowerDidChange(ctx, consAddr, valAddr) h.sh.OnValidatorPowerDidChange(ctx, consAddr, valAddr) } func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) { h.dh.OnValidatorBeginUnbonding(ctx, consAddr, valAddr) h.sh.OnValidatorBeginUnbonding(ctx, consAddr, valAddr) } func (h Hooks) OnDelegationCreated(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationCreated(ctx, delAddr, valAddr) h.sh.OnDelegationCreated(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationSharesModified(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationSharesModified(ctx, delAddr, valAddr) h.sh.OnDelegationSharesModified(ctx, delAddr, valAddr) } func (h Hooks) OnDelegationRemoved(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) { h.dh.OnDelegationRemoved(ctx, delAddr, valAddr) h.sh.OnDelegationRemoved(ctx, delAddr, valAddr) }