wip basecoin refactoring

This commit is contained in:
rigelrozanski
2018-02-17 16:32:30 -05:00
committed by Ethan Buchman
parent 34ff225c31
commit f446b94ac7
13 changed files with 204 additions and 337 deletions
+24 -44
View File
@@ -8,6 +8,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/pkg/errors"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@@ -21,44 +22,22 @@ var mainHeaderKey = []byte("header")
// The ABCI application
type BaseApp struct {
logger log.Logger
// Application name from abci.Info
name string
// Common DB backend
db dbm.DB
// Main (uncached) state
cms sdk.CommitMultiStore
// unmarshal []byte into sdk.Tx
txDecoder sdk.TxDecoder
// unmarshal rawjsonbytes to initialize the application
// TODO unexpose and call from InitChain
InitStater sdk.InitStater
// ante handler for fee and auth
defaultAnteHandler sdk.AnteHandler
// handle any kind of message
router Router
logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
InitStater sdk.InitStater // TODO unexpose
defaultAnteHandler sdk.AnteHandler // ante handler for fee and auth
router Router // handle any kind of message
//--------------------
// Volatile
// CheckTx state, a cache-wrap of `.cms`
msCheck sdk.CacheMultiStore
// DeliverTx state, a cache-wrap of `.cms`
msDeliver sdk.CacheMultiStore
// current block header
header *abci.Header
// cached validator changes from DeliverTx
valUpdates []abci.Validator
msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms`
msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms`
header *abci.Header // current block header
valUpdates []abci.Validator // cached validator changes from DeliverTx
}
var _ abci.Application = &BaseApp{}
@@ -122,40 +101,40 @@ func (app *BaseApp) SetBeginBlocker(...) {}
func (app *BaseApp) SetEndBlocker(...) {}
*/
// TODO add description
// load latest application version
func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error {
app.cms.LoadLatestVersion()
return app.initFromStore(mainKey)
}
// Load application version
// load application version
func (app *BaseApp) LoadVersion(version int64, mainKey sdk.StoreKey) error {
app.cms.LoadVersion(version)
return app.initFromStore(mainKey)
}
// The last CommitID of the multistore.
// the last CommitID of the multistore
func (app *BaseApp) LastCommitID() sdk.CommitID {
return app.cms.LastCommitID()
}
// The last commited block height.
// the last commited block height
func (app *BaseApp) LastBlockHeight() int64 {
return app.cms.LastCommitID().Version
}
// Initializes the remaining logic from app.cms.
// initializes the remaining logic from app.cms
func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
var lastCommitID = app.cms.LastCommitID()
var main = app.cms.GetKVStore(mainKey)
var header *abci.Header
// Main store should exist.
// main store should exist.
if main == nil {
return errors.New("BaseApp expects MultiStore with 'main' KVStore")
}
// If we've committed before, we expect main://<mainHeaderKey>.
// if we've committed before, we expect main://<mainHeaderKey>
if !lastCommitID.IsZero() {
headerBytes := main.Get(mainHeaderKey)
if len(headerBytes) == 0 {
@@ -173,7 +152,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
}
}
// Set BaseApp state.
// set BaseApp state
app.header = header
app.msCheck = nil
app.msDeliver = nil
@@ -183,6 +162,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
}
//----------------------------------------
// ABCI
// Implements ABCI
func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
@@ -205,7 +185,7 @@ func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOp
// Implements ABCI
func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) {
// TODO: Use req.Validators
// TODO: Use req.AppStateJSON (?)
// TODO: Use req.AppState
return
}
@@ -367,7 +347,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) {
}
//----------------------------------------
// Misc.
// Helpers
func (app *BaseApp) getMultiStore(isCheckTx bool) sdk.MultiStore {
if isCheckTx {
+6 -29
View File
@@ -5,8 +5,7 @@ import (
"io/ioutil"
)
// TODO: remove from here and pass the AppState
// through InitChain
// TODO: remove from here and pass the AppState through InitChain
// GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
type GenesisDoc struct {
@@ -14,12 +13,11 @@ type GenesisDoc struct {
}
// GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc.
func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
if genDocFile == "" {
var g GenesisDoc
return &g, nil
func ReadGenesisAppState(genesisPath string) (state json.RawMessage, err error) {
if genesisPath == "" {
return
}
jsonBlob, err := ioutil.ReadFile(genDocFile)
jsonBlob, err := ioutil.ReadFile(genesisPath)
if err != nil {
return nil, err
}
@@ -28,26 +26,5 @@ func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
if err != nil {
return nil, err
}
return &genDoc, nil
return genDoc.AppState, nil
}
// read app state from the genesis file
//func GenesisAppState(genesisFile string) (state json.RawMessage, err error) {
//if genesisFile == "" {
//return
//}
//jsonBlob, err := ioutil.ReadFile(genesisFile)
//if err != nil {
//return nil, err
//}
//data := make(map[string]interface{})
//err = json.Unmarshal(jsonBlob, &data)
//if err != nil {
//return nil, err
//}
//state, ok := data["app_state"].(json.RawMessage)
//if !ok {
//return nil, errors.New("app state genesis parse error")
//}
//return state, nil
//}
+24
View File
@@ -0,0 +1,24 @@
package baseapp
import (
"github.com/tendermint/abci/server"
abci "github.com/tendermint/abci/types"
cmn "github.com/tendermint/tmlibs/common"
)
// RunForever - BasecoinApp execution and cleanup
func RunForever(app abci.Application) {
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", app)
if err != nil {
cmn.Exit(err.Error())
}
srv.Start()
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
}
+111 -40
View File
@@ -1,12 +1,16 @@
package app
import (
"encoding/json"
"fmt"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/sketchy"
"github.com/tendermint/abci/server"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
@@ -28,63 +32,130 @@ type BasecoinApp struct {
accountMapper sdk.AccountMapper
}
// TODO: This should take in more configuration options.
// TODO: This should be moved into baseapp to isolate complexity
// construct top level keys
func (app *BasecoinApp) initCapKeys() {
app.capKeyMainStore = sdk.NewKVStoreKey("main")
app.capKeyIBCStore = sdk.NewKVStoreKey("ibc")
}
func (app *BasecoinApp) initDefaultAnteHandler() {
// deducts fee from payer, verifies signatures and nonces, sets Signers to ctx.
app.BaseApp.SetDefaultAnteHandler(auth.NewAnteHandler(app.accountMapper))
}
func (app *BasecoinApp) initRouterHandlers() {
// All handlers must be added here, the order matters
app.router.AddRoute("bank", bank.NewHandler(bank.NewCoinKeeper(app.accountMapper)))
app.router.AddRoute("sketchy", sketchy.NewHandler())
}
func (app *BasecoinApp) initBaseAppTxDecoder() {
cdc := makeTxCodec()
app.BaseApp.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
// StdTx.Msg is an interface whose concrete
// types are registered in app/msgs.go.
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxParse("").TraceCause(err, "")
}
return tx, nil
})
}
// define the custom logic for basecoin initialization
func (app *BasecoinApp) initBaseAppInitStater() {
accountMapper := app.accountMapper
app.BaseApp.SetInitStater(func(ctx sdk.Context, state json.RawMessage) sdk.Error {
if state == nil {
return nil
}
genesisState := new(types.GenesisState)
err := json.Unmarshal(state, genesisState)
if err != nil {
return sdk.ErrGenesisParse("").TraceCause(err, "")
}
for _, gacc := range genesisState.Accounts {
acc, err := gacc.ToAppAccount()
if err != nil {
return sdk.ErrGenesisParse("").TraceCause(err, "")
}
accountMapper.SetAccount(ctx, acc)
}
return nil
})
}
// Initialize root stores.
func (app *BasecoinApp) mountStores() {
// Create MultiStore mounts.
app.BaseApp.MountStore(app.capKeyMainStore, sdk.StoreTypeIAVL)
app.BaseApp.MountStore(app.capKeyIBCStore, sdk.StoreTypeIAVL)
}
// Initialize the AccountMapper.
func (app *BasecoinApp) initAccountMapper() {
var accountMapper = auth.NewAccountMapper(
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
)
// Register all interfaces and concrete types that
// implement those interfaces, here.
cdc := accountMapper.WireCodec()
auth.RegisterWireBaseAccount(cdc)
// Make accountMapper's WireCodec() inaccessible.
app.accountMapper = accountMapper.Seal()
}
func NewBasecoinApp(genesisPath string) *BasecoinApp {
// Create and configure app.
// create and configure app
var app = &BasecoinApp{}
bapp := bam.NewBaseApp(appName)
app.BaseApp = bapp
app.router = bapp.Router()
app.initBaseAppTxDecoder()
// TODO open up out of functions, or introduce clarity,
// interdependancies are a nightmare to debug
app.initCapKeys() // ./init_capkeys.go
app.initBaseApp() // ./init_baseapp.go
app.initStores() // ./init_stores.go
// add keys
app.initCapKeys()
// initialize the stores
app.mountStores()
app.initAccountMapper()
// initialize the genesis function
app.initBaseAppInitStater()
app.initHandlers() // ./init_handlers.go
genesisiDoc, err := bam.GenesisDocFromFile(genesisPath)
// initialize the handler
app.initDefaultAnteHandler()
app.initRouterHandlers()
genesisAppState, err := bam.ReadGenesisAppState(genesisPath)
if err != nil {
panic(fmt.Errorf("error loading genesis state: %v", err))
}
// set up the cache store for ctx, get ctx
// TODO: can InitChain handle this too ?
// TODO: combine with InitChain and let tendermint invoke it.
app.BaseApp.BeginBlock(abci.RequestBeginBlock{Header: abci.Header{}})
ctx := app.BaseApp.NewContext(false, nil) // context for DeliverTx
// TODO: combine with InitChain and let tendermint invoke it.
err = app.BaseApp.InitStater(ctx, genesisiDoc.AppState)
err = app.BaseApp.InitStater(ctx, genesisAppState)
if err != nil {
panic(fmt.Errorf("error initializing application genesis state: %v", err))
}
app.loadStores()
return app
}
// RunForever - BasecoinApp execution and cleanup
func (app *BasecoinApp) RunForever() {
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", app)
if err != nil {
cmn.Exit(err.Error())
}
srv.Start()
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
}
// Load the stores
func (app *BasecoinApp) loadStores() {
// load the stores
if err := app.LoadLatestVersion(app.capKeyMainStore); err != nil {
cmn.Exit(err.Error())
}
return app
}
+18 -3
View File
@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
bam "github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
@@ -15,6 +16,20 @@ import (
crypto "github.com/tendermint/go-crypto"
)
type testBasecoinApp struct {
*BasecoinApp
*bam.TestApp
}
func newTestBasecoinApp() *testBasecoinApp {
app := NewBasecoinApp("")
tba := &testBasecoinApp{
BasecoinApp: app,
}
tba.TestApp = bam.NewTestApp(app.BaseApp)
return tba
}
func TestSendMsg(t *testing.T) {
tba := newTestBasecoinApp()
tba.RunBeginBlock()
@@ -62,9 +77,9 @@ func TestGenesis(t *testing.T) {
}
acc := &types.AppAccount{baseAcc, "foobart"}
genesisState := GenesisState{
Accounts: []*GenesisAccount{
NewGenesisAccount(acc),
genesisState := types.GenesisState{
Accounts: []*types.GenesisAccount{
types.NewGenesisAccount(acc),
},
}
bytes, err := json.MarshalIndent(genesisState, "", "\t")
-94
View File
@@ -1,94 +0,0 @@
package app
import (
"encoding/json"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
crypto "github.com/tendermint/go-crypto"
)
// initCapKeys, initBaseApp, initStores, initHandlers.
func (app *BasecoinApp) initBaseApp() {
bapp := baseapp.NewBaseApp(appName)
app.BaseApp = bapp
app.router = bapp.Router()
app.initBaseAppTxDecoder()
app.initBaseAppInitStater()
}
func (app *BasecoinApp) initBaseAppTxDecoder() {
cdc := makeTxCodec()
app.BaseApp.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
// StdTx.Msg is an interface whose concrete
// types are registered in app/msgs.go.
err := cdc.UnmarshalBinary(txBytes, &tx)
if err != nil {
return nil, sdk.ErrTxParse("").TraceCause(err, "")
}
return tx, nil
})
}
// define the custom logic for basecoin initialization
func (app *BasecoinApp) initBaseAppInitStater() {
accountMapper := app.accountMapper
app.BaseApp.SetInitStater(func(ctx sdk.Context, state json.RawMessage) sdk.Error {
if state == nil {
return nil
}
genesisState := new(GenesisState)
err := json.Unmarshal(state, genesisState)
if err != nil {
return sdk.ErrGenesisParse("").TraceCause(err, "")
}
for _, gacc := range genesisState.Accounts {
acc, err := gacc.toAppAccount()
if err != nil {
return sdk.ErrGenesisParse("").TraceCause(err, "")
}
accountMapper.SetAccount(ctx, acc)
}
return nil
})
}
//-----------------------------------------------------
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `accounts`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Name string `json:"name"`
Address crypto.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(aa *types.AppAccount) *GenesisAccount {
return &GenesisAccount{
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins,
}
}
// convert GenesisAccount to AppAccount
func (ga *GenesisAccount) toAppAccount() (acc *types.AppAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins,
}
return &types.AppAccount{
BaseAccount: baseAcc,
Name: ga.Name,
}, nil
}
-16
View File
@@ -1,16 +0,0 @@
package app
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// initCapKeys, initBaseApp, initStores, initHandlers.
func (app *BasecoinApp) initCapKeys() {
// All top-level capabilities keys
// should be constructed here.
// For more information, see http://www.erights.org/elib/capability/ode/ode.pdf.
app.capKeyMainStore = sdk.NewKVStoreKey("main")
app.capKeyIBCStore = sdk.NewKVStoreKey("ibc")
}
-30
View File
@@ -1,30 +0,0 @@
package app
import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/sketchy"
)
// initCapKeys, initBaseApp, initStores, initHandlers.
func (app *BasecoinApp) initHandlers() {
app.initDefaultAnteHandler()
app.initRouterHandlers()
}
func (app *BasecoinApp) initDefaultAnteHandler() {
// Deducts fee from payer.
// Verifies signatures and nonces.
// Sets Signers to ctx.
app.BaseApp.SetDefaultAnteHandler(
auth.NewAnteHandler(app.accountMapper))
}
func (app *BasecoinApp) initRouterHandlers() {
// All handlers must be added here.
// The order matters.
app.router.AddRoute("bank", bank.NewHandler(bank.NewCoinKeeper(app.accountMapper)))
app.router.AddRoute("sketchy", sketchy.NewHandler())
}
-38
View File
@@ -1,38 +0,0 @@
package app
import (
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
)
// initCapKeys, initBaseApp, initStores, initHandlers.
func (app *BasecoinApp) initStores() {
app.mountStores()
app.initAccountMapper()
}
// Initialize root stores.
func (app *BasecoinApp) mountStores() {
// Create MultiStore mounts.
app.BaseApp.MountStore(app.capKeyMainStore, sdk.StoreTypeIAVL)
app.BaseApp.MountStore(app.capKeyIBCStore, sdk.StoreTypeIAVL)
}
// Initialize the AccountMapper.
func (app *BasecoinApp) initAccountMapper() {
var accountMapper = auth.NewAccountMapper(
app.capKeyMainStore, // target store
&types.AppAccount{}, // prototype
)
// Register all interfaces and concrete types that
// implement those interfaces, here.
cdc := accountMapper.WireCodec()
auth.RegisterWireBaseAccount(cdc)
// Make accountMapper's WireCodec() inaccessible.
app.accountMapper = accountMapper.Seal()
}
+1 -1
View File
@@ -6,7 +6,7 @@ import (
wire "github.com/tendermint/go-wire"
)
// Wire requires registration of interfaces & concrete types. All
// Wire requires registration of interfaces & concrete types. All
// interfaces to be encoded/decoded in a Msg must be registered
// here, along with all the concrete types that implement them.
func makeTxCodec() (cdc *wire.Codec) {
-19
View File
@@ -1,19 +0,0 @@
package app
import (
bam "github.com/cosmos/cosmos-sdk/baseapp"
)
type testBasecoinApp struct {
*BasecoinApp
*bam.TestApp
}
func newTestBasecoinApp() *testBasecoinApp {
app := NewBasecoinApp("")
tba := &testBasecoinApp{
BasecoinApp: app,
}
tba.TestApp = bam.NewTestApp(app.BaseApp)
return tba
}
+5 -2
View File
@@ -1,10 +1,13 @@
package main
import "github.com/cosmos/cosmos-sdk/examples/basecoin/app"
import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/examples/basecoin/app"
)
func main() {
// TODO CREATE CLI
bapp := app.NewBasecoinApp("")
bapp.RunForever()
baseapp.RunForever(bapp)
}
+15 -21
View File
@@ -4,7 +4,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
)
var _ sdk.Account = (*AppAccount)(nil)
@@ -25,36 +24,31 @@ func (acc *AppAccount) SetName(name string) { acc.Name = name }
//___________________________________________________________________________________
// We use GenesisAccount instead of AppAccount for cleaner json input of PubKey
// State to Unmarshal
type GenesisState struct {
Accounts []*GenesisAccount `json:"accounts"`
}
// GenesisAccount doesn't need pubkey or sequence
type GenesisAccount struct {
Name string `json:"name"`
Address crypto.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey cmn.HexBytes `json:"public_key"`
Sequence int64 `json:"sequence"`
Name string `json:"name"`
Address crypto.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
}
func NewGenesisAccount(aa *AppAccount) *GenesisAccount {
return &GenesisAccount{
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins,
PubKey: aa.PubKey.Bytes(),
Sequence: aa.Sequence,
Name: aa.Name,
Address: aa.Address,
Coins: aa.Coins,
}
}
// convert GenesisAccount to AppAccount
func (ga *GenesisAccount) toAppAccount() (acc *AppAccount, err error) {
pk, err := crypto.PubKeyFromBytes(ga.PubKey)
if err != nil {
return
}
func (ga *GenesisAccount) ToAppAccount() (acc *AppAccount, err error) {
baseAcc := auth.BaseAccount{
Address: ga.Address,
Coins: ga.Coins,
PubKey: pk,
Sequence: ga.Sequence,
Address: ga.Address,
Coins: ga.Coins,
}
return &AppAccount{
BaseAccount: baseAcc,