383 lines
13 KiB
Go
383 lines
13 KiB
Go
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"
|
|
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
|
|
|
|
// 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
|
|
paramsKeeper params.Keeper
|
|
}
|
|
|
|
// NewGaiaApp returns a reference to an initialized GaiaApp.
|
|
func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, 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(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,
|
|
)
|
|
|
|
// 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 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))
|
|
|
|
app.QueryRouter().
|
|
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))
|
|
|
|
// 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)
|
|
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...)
|
|
|
|
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)
|
|
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)
|
|
}
|