Implement initial ABCI logic

This commit is contained in:
Aleksandr Bezobchuk 2018-08-24 11:56:43 -07:00
parent 50e0602923
commit f93135f0f8
6 changed files with 236 additions and 27 deletions

View File

@ -5,12 +5,19 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/gov"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/pkg/errors"
"github.com/cosmos/ethermint/handlers" "github.com/cosmos/ethermint/handlers"
"github.com/cosmos/ethermint/types" "github.com/cosmos/ethermint/types"
ethparams "github.com/ethereum/go-ethereum/params" ethparams "github.com/ethereum/go-ethereum/params"
abci "github.com/tendermint/tendermint/abci/types"
tmcmn "github.com/tendermint/tendermint/libs/common" tmcmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db" dbm "github.com/tendermint/tendermint/libs/db"
tmlog "github.com/tendermint/tendermint/libs/log" tmlog "github.com/tendermint/tendermint/libs/log"
@ -28,51 +35,145 @@ type (
*bam.BaseApp *bam.BaseApp
codec *wire.Codec codec *wire.Codec
sealed bool
accountKey *sdk.KVStoreKey accountKey *sdk.KVStoreKey
accountMapper auth.AccountMapper mainKey *sdk.KVStoreKey
// TODO: keys, stores, mappers, and keepers stakeKey *sdk.KVStoreKey
} slashingKey *sdk.KVStoreKey
govKey *sdk.KVStoreKey
feeCollKey *sdk.KVStoreKey
paramsKey *sdk.KVStoreKey
tParamsKey *sdk.TransientStoreKey
// Options is a function signature that provides the ability to modify accountMapper auth.AccountMapper
// options of an EthermintApp during initialization. feeCollKeeper auth.FeeCollectionKeeper
Options func(*EthermintApp) coinKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
}
) )
// NewEthermintApp returns a reference to a new initialized Ethermint // NewEthermintApp returns a reference to a new initialized Ethermint
// application. // application.
func NewEthermintApp(logger tmlog.Logger, db dbm.DB, ethChainCfg *ethparams.ChainConfig, opts ...Options, func NewEthermintApp(
logger tmlog.Logger, db dbm.DB, ethChainCfg *ethparams.ChainConfig, baseAppOptions ...func(*bam.BaseApp),
) *EthermintApp { ) *EthermintApp {
codec := CreateCodec() codec := CreateCodec()
app := &EthermintApp{ app := &EthermintApp{
BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec)), BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec), baseAppOptions...),
codec: codec, codec: codec,
accountKey: sdk.NewKVStoreKey("accounts"), accountKey: sdk.NewKVStoreKey("acc"),
mainKey: sdk.NewKVStoreKey("main"),
stakeKey: sdk.NewKVStoreKey("stake"),
slashingKey: sdk.NewKVStoreKey("slashing"),
govKey: sdk.NewKVStoreKey("gov"),
feeCollKey: sdk.NewKVStoreKey("fee"),
paramsKey: sdk.NewKVStoreKey("params"),
tParamsKey: sdk.NewTransientStoreKey("transient_params"),
} }
// set application keepers and mappers
app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount) app.accountMapper = auth.NewAccountMapper(codec, app.accountKey, auth.ProtoBaseAccount)
app.coinKeeper = bank.NewKeeper(app.accountMapper)
app.paramsKeeper = params.NewKeeper(app.codec, app.paramsKey)
app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.codec, app.feeCollKey)
app.stakeKeeper = stake.NewKeeper(
app.codec, app.stakeKey, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace),
)
app.govKeeper = gov.NewKeeper(
app.codec, app.govKey, app.paramsKeeper.Setter(), app.coinKeeper,
app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace),
)
app.slashingKeeper = slashing.NewKeeper(
app.codec, app.slashingKey, app.stakeKeeper,
app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace),
)
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper)) // register message handlers
app.MountStoresIAVL(app.accountKey) app.Router().
// TODO: Do we need to mount bank and IBC handlers? Should be handled
// directly in the EVM.
AddRoute("stake", stake.NewHandler(app.stakeKeeper)).
AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)).
AddRoute("gov", gov.NewHandler(app.govKeeper))
for _, opt := range opts { // initialize the underlying ABCI BaseApp
opt(app) app.SetInitChainer(app.initChainer)
} app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.SetAnteHandler(handlers.AnteHandler(app.accountMapper, app.feeCollKeeper))
err := app.LoadLatestVersion(app.accountKey) app.MountStoresIAVL(
if err != nil { app.mainKey, app.accountKey, app.stakeKey, app.slashingKey,
app.govKey, app.feeCollKey, app.paramsKey,
)
app.MountStore(app.tParamsKey, sdk.StoreTypeTransient)
if err := app.LoadLatestVersion(app.accountKey); err != nil {
tmcmn.Exit(err.Error()) tmcmn.Exit(err.Error())
} }
app.seal() app.BaseApp.Seal()
return app return app
} }
// seal seals the Ethermint application and prohibits any future modifications // BeginBlocker signals the beginning of a block. It performs application
// that change critical components. // updates on the start of every block.
func (app *EthermintApp) seal() { func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
app.sealed = true tags := slashing.BeginBlocker(ctx, req, app.slashingKeeper)
return abci.ResponseBeginBlock{
Tags: tags.ToKVPairs(),
}
}
// EndBlocker signals the end of a block. It performs application updates on
// the end of every block.
func (app *EthermintApp) EndBlocker(ctx sdk.Context, _ abci.RequestEndBlock) abci.ResponseEndBlock {
tags := gov.EndBlocker(ctx, app.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, app.stakeKeeper)
app.slashingKeeper.AddValidators(ctx, validatorUpdates)
return abci.ResponseEndBlock{
ValidatorUpdates: validatorUpdates,
Tags: tags,
}
}
// initChainer initializes the application blockchain with validators and other
// state data from TendermintCore.
func (app *EthermintApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
stateJSON := req.AppStateBytes
err := app.codec.UnmarshalJSON(stateJSON, &genesisState)
if err != nil {
panic(errors.Wrap(err, "failed to parse application genesis state"))
}
// load the genesis accounts
for _, genAcc := range genesisState.Accounts {
acc := genAcc.ToAccount()
acc.AccountNumber = app.accountMapper.GetNextAccountNumber(ctx)
app.accountMapper.SetAccount(ctx, acc)
}
// load the genesis stake information
validators, err := stake.InitGenesis(ctx, app.stakeKeeper, genesisState.StakeData)
if err != nil {
panic(errors.Wrap(err, "failed to initialize genesis validators"))
}
slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData)
gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData)
return abci.ResponseInitChain{
Validators: validators,
}
} }
// CreateCodec creates a new amino wire codec and registers all the necessary // CreateCodec creates a new amino wire codec and registers all the necessary
@ -80,7 +181,12 @@ func (app *EthermintApp) seal() {
func CreateCodec() *wire.Codec { func CreateCodec() *wire.Codec {
codec := wire.NewCodec() codec := wire.NewCodec()
// Register other modules, types, and messages...
types.RegisterWire(codec) types.RegisterWire(codec)
auth.RegisterWire(codec)
gov.RegisterWire(codec)
slashing.RegisterWire(codec)
stake.RegisterWire(codec)
wire.RegisterCrypto(codec)
return codec return codec
} }

47
app/genesis.go Normal file
View File

@ -0,0 +1,47 @@
package app
import (
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/stake"
"github.com/cosmos/ethermint/types"
)
type (
// GenesisState defines the application's genesis state. It contains all the
// information required and accounts to initialize the blockchain.
GenesisState struct {
Accounts []GenesisAccount `json:"accounts"`
StakeData stake.GenesisState `json:"stake"`
GovData gov.GenesisState `json:"gov"`
}
// GenesisAccount defines an account to be initialized in the genesis state.
GenesisAccount struct {
Address sdk.AccAddress `json:"address"`
Coins sdk.Coins `json:"coins"`
Code []byte `json:"code,omitempty"`
Storage types.Storage `json:"storage,omitempty"`
}
)
// NewGenesisAccount returns a reference to a new initialized genesis account.
func NewGenesisAccount(acc *types.Account) GenesisAccount {
return GenesisAccount{
Address: acc.GetAddress(),
Coins: acc.GetCoins(),
Code: acc.Code,
Storage: acc.Storage,
}
}
// ToAccount converts a genesis account to an initialized Ethermint account.
func (ga *GenesisAccount) ToAccount() (acc *types.Account) {
base := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins.Sort(),
}
return types.NewAccount(base, ga.Code, ga.Storage)
}

View File

@ -30,7 +30,7 @@ type internalAnteHandler func(
// transaction to an internal ante handler for performing transaction-level // transaction to an internal ante handler for performing transaction-level
// processing (e.g. fee payment, signature verification) before being passed // processing (e.g. fee payment, signature verification) before being passed
// onto it's respective handler. // onto it's respective handler.
func AnteHandler(am auth.AccountMapper) sdk.AnteHandler { func AnteHandler(am auth.AccountMapper, _ auth.FeeCollectionKeeper) sdk.AnteHandler {
return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return func(sdkCtx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
var ( var (
gasLimit int64 gasLimit int64

53
types/account.go Normal file
View File

@ -0,0 +1,53 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
ethcmn "github.com/ethereum/go-ethereum/common"
)
var _ auth.Account = (*Account)(nil)
type (
// Storage defines account storage
Storage map[ethcmn.Hash]ethcmn.Hash
// Account defines an auth.BaseAccount extension for Ethermint. It is
// compatible with the auth.AccountMapper.
Account struct {
auth.BaseAccount
Code []byte
Storage Storage
}
)
// NewAccount returns a reference to a new initialized account.
func NewAccount(base auth.BaseAccount, code []byte, storage Storage) *Account {
return &Account{
BaseAccount: base,
Code: code,
Storage: storage,
}
}
// GetAccountDecoder returns the auth.AccountDecoder function for the custom
// Account type.
func GetAccountDecoder(cdc *wire.Codec) auth.AccountDecoder {
return func(accBytes []byte) (auth.Account, error) {
if len(accBytes) == 0 {
return nil, sdk.ErrTxDecode("account bytes are empty")
}
acc := new(Account)
err := cdc.UnmarshalBinaryBare(accBytes, &acc)
if err != nil {
return nil, sdk.ErrTxDecode("failed to decode account bytes")
}
return acc, err
}
}

View File

@ -17,6 +17,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
var _ sdk.Tx = (*Transaction)(nil)
const ( const (
// TypeTxEthereum reflects an Ethereum Transaction type. // TypeTxEthereum reflects an Ethereum Transaction type.
TypeTxEthereum = "Ethereum" TypeTxEthereum = "Ethereum"

View File

@ -15,6 +15,7 @@ func init() {
// codec. // codec.
func RegisterWire(codec *wire.Codec) { func RegisterWire(codec *wire.Codec) {
sdk.RegisterWire(codec) sdk.RegisterWire(codec)
codec.RegisterConcrete(&Account{}, "types/Account", nil)
codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil) codec.RegisterConcrete(&EthSignature{}, "types/EthSignature", nil)
codec.RegisterConcrete(TxData{}, "types/TxData", nil) codec.RegisterConcrete(TxData{}, "types/TxData", nil)
codec.RegisterConcrete(Transaction{}, "types/Transaction", nil) codec.RegisterConcrete(Transaction{}, "types/Transaction", nil)