Merge pull request #476 from cosmos/bez/436-initial-abci-impl

R4R: Implement Initial ABCI Application
This commit is contained in:
Jack Zampolin 2018-08-24 13:14:22 -07:00 committed by GitHub
commit 58a8b42b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 258 additions and 33 deletions

26
Gopkg.lock generated
View File

@ -11,11 +11,11 @@
[[projects]]
branch = "master"
digest = "1:8ad24ea05e770b745b5c286b4f94cf73d5be87005680e36b2d0dd1de0a2f9fbf"
digest = "1:b9f5e0f033febe59a62d01e78486c0dd9e4afc9ac5d240aee6ce78a927142e8b"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "T"
revision = "d81d8877b8f327112e94e814937143a71d1692a7"
revision = "79e00513b1011888b1e675157ab89f527f901cae"
[[projects]]
digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533"
@ -34,6 +34,16 @@
"version",
"wire",
"x/auth",
"x/bank",
"x/gov",
"x/gov/tags",
"x/mock",
"x/params",
"x/slashing",
"x/stake",
"x/stake/keeper",
"x/stake/tags",
"x/stake/types",
]
pruneopts = "T"
revision = "1c38c70468ec721e3a555ba2f3bf5f9da31f0cc9"
@ -232,7 +242,7 @@
version = "v1.0.2"
[[projects]]
digest = "1:e95496462101745805bd4e041a5b841e108c7cf761264d53648246308de2761e"
digest = "1:8f39978e4fb2a11d43cc954f2ab458cb38995d4c1557b6d3a7c8cafe0ec2277c"
name = "github.com/stretchr/testify"
packages = [
"assert",
@ -240,8 +250,8 @@
"suite",
]
pruneopts = "T"
revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
version = "v1.2.2"
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
@ -438,6 +448,11 @@
"github.com/cosmos/cosmos-sdk/types",
"github.com/cosmos/cosmos-sdk/wire",
"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/ethereum/go-ethereum/common",
"github.com/ethereum/go-ethereum/common/hexutil",
"github.com/ethereum/go-ethereum/common/math",
@ -459,6 +474,7 @@
"github.com/pkg/errors",
"github.com/stretchr/testify/require",
"github.com/stretchr/testify/suite",
"github.com/tendermint/tendermint/abci/types",
"github.com/tendermint/tendermint/libs/common",
"github.com/tendermint/tendermint/libs/db",
"github.com/tendermint/tendermint/libs/log",

View File

@ -24,7 +24,7 @@
[[constraint]]
name = "github.com/stretchr/testify"
version = "=1.2.2"
version = "=1.2.1"
[[constraint]]
name = "github.com/pkg/errors"

View File

@ -5,12 +5,19 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"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/types"
ethparams "github.com/ethereum/go-ethereum/params"
abci "github.com/tendermint/tendermint/abci/types"
tmcmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
tmlog "github.com/tendermint/tendermint/libs/log"
@ -27,52 +34,146 @@ type (
EthermintApp struct {
*bam.BaseApp
codec *wire.Codec
sealed bool
codec *wire.Codec
accountKey *sdk.KVStoreKey
accountMapper auth.AccountMapper
// TODO: keys, stores, mappers, and keepers
accountKey *sdk.KVStoreKey
mainKey *sdk.KVStoreKey
stakeKey *sdk.KVStoreKey
slashingKey *sdk.KVStoreKey
govKey *sdk.KVStoreKey
feeCollKey *sdk.KVStoreKey
paramsKey *sdk.KVStoreKey
tParamsKey *sdk.TransientStoreKey
accountMapper auth.AccountMapper
feeCollKeeper auth.FeeCollectionKeeper
coinKeeper bank.Keeper
stakeKeeper stake.Keeper
slashingKeeper slashing.Keeper
govKeeper gov.Keeper
paramsKeeper params.Keeper
}
// Options is a function signature that provides the ability to modify
// options of an EthermintApp during initialization.
Options func(*EthermintApp)
)
// NewEthermintApp returns a reference to a new initialized Ethermint
// 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 {
codec := CreateCodec()
app := &EthermintApp{
BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec)),
codec: codec,
accountKey: sdk.NewKVStoreKey("accounts"),
BaseApp: bam.NewBaseApp(appName, logger, db, types.TxDecoder(codec), baseAppOptions...),
codec: codec,
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.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))
app.MountStoresIAVL(app.accountKey)
// register message handlers
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 {
opt(app)
}
// initialize the underlying ABCI BaseApp
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)
if err != nil {
app.MountStoresIAVL(
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())
}
app.seal()
app.BaseApp.Seal()
return app
}
// seal seals the Ethermint application and prohibits any future modifications
// that change critical components.
func (app *EthermintApp) seal() {
app.sealed = true
// BeginBlocker signals the beginning of a block. It performs application
// updates on the start of every block.
func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
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
@ -80,7 +181,12 @@ func (app *EthermintApp) seal() {
func CreateCodec() *wire.Codec {
codec := wire.NewCodec()
// Register other modules, types, and messages...
types.RegisterWire(codec)
auth.RegisterWire(codec)
gov.RegisterWire(codec)
slashing.RegisterWire(codec)
stake.RegisterWire(codec)
wire.RegisterCrypto(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
// processing (e.g. fee payment, signature verification) before being passed
// 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) {
var (
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"
)
var _ sdk.Tx = (*Transaction)(nil)
const (
// TypeTxEthereum reflects an Ethereum Transaction type.
TypeTxEthereum = "Ethereum"

View File

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