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

View File

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

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)