Big cleanup of app dir

StoreApp just the queries, BaseApp with handler/ticker
Ticker now defined top level, as an interface, with context
Name for info taken as parameter, start cmd uses commandline name
Cleaner compisition of apps.
This commit is contained in:
Ethan Frey 2017-10-16 19:21:43 +02:00
parent 3a16fa9482
commit f65215ad92
12 changed files with 202 additions and 204 deletions

View File

@ -55,7 +55,7 @@ func DefaultHandler(feeDenom string) sdk.Handler {
type appTest struct {
t *testing.T
chainID string
app *Basecoin
app *BaseApp
acctIn *coin.AccountWithKey
acctOut *coin.AccountWithKey
}
@ -113,14 +113,9 @@ func (at *appTest) reset() {
logger := log.TestingLogger()
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
var err error
at.app, err = NewBasecoin(
DefaultHandler("mycoin"),
"",
0,
logger.With("module", "app"),
)
store, err := NewStoreApp("app-test", "", 0, logger)
require.Nil(at.t, err, "%+v", err)
at.app = NewBaseApp(store, DefaultHandler("mycoin"), nil)
_, err = at.app.InitState("base", "chain_id", at.chainID)
require.Nil(at.t, err, "%+v", err)
@ -173,13 +168,9 @@ func TestInitState(t *testing.T) {
require := require.New(t)
logger := log.TestingLogger()
app, err := NewBasecoin(
DefaultHandler("atom"),
"",
0,
logger.With("module", "app"),
)
store, err := NewStoreApp("app-test", "", 0, logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("atom"), nil)
//testing ChainID
chainID := "testChain"

113
app/base.go Normal file
View File

@ -0,0 +1,113 @@
package app
import (
"fmt"
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/stack"
)
// BaseApp - The ABCI application
type BaseApp struct {
*StoreApp
handler sdk.Handler
clock sdk.Ticker
}
var _ abci.Application = &BaseApp{}
// NewBaseApp extends a StoreApp with a handler and a ticker,
// which it binds to the proper abci calls
func NewBaseApp(store *StoreApp, handler sdk.Handler, clock sdk.Ticker) *BaseApp {
return &BaseApp{
StoreApp: store,
handler: handler,
clock: clock,
}
}
// DeliverTx - ABCI - dispatches to the handler
func (app *BaseApp) DeliverTx(txBytes []byte) abci.Result {
tx, err := sdk.LoadTx(txBytes)
if err != nil {
return errors.Result(err)
}
ctx := stack.NewContext(
app.GetChainID(),
app.WorkingHeight(),
app.Logger().With("call", "delivertx"),
)
res, err := app.handler.DeliverTx(ctx, app.Append(), tx)
if err != nil {
return errors.Result(err)
}
app.AddValChange(res.Diff)
return sdk.ToABCI(res)
}
// CheckTx - ABCI - dispatches to the handler
func (app *BaseApp) CheckTx(txBytes []byte) abci.Result {
tx, err := sdk.LoadTx(txBytes)
if err != nil {
return errors.Result(err)
}
ctx := stack.NewContext(
app.GetChainID(),
app.WorkingHeight(),
app.Logger().With("call", "checktx"),
)
res, err := app.handler.CheckTx(ctx, app.Check(), tx)
if err != nil {
return errors.Result(err)
}
return sdk.ToABCI(res)
}
// BeginBlock - ABCI - triggers Tick actions
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) {
// execute tick if present
if app.clock != nil {
ctx := stack.NewContext(
app.GetChainID(),
app.WorkingHeight(),
app.Logger().With("call", "tick"),
)
diff, err := app.clock.Tick(ctx, app.Append())
if err != nil {
panic(err)
}
app.AddValChange(diff)
}
}
// InitState - used to setup state (was SetOption)
// to be used by InitChain later
//
// TODO: rethink this a bit more....
func (app *BaseApp) InitState(module, key, value string) (string, error) {
state := app.Append()
if module == ModuleNameBase {
if key == ChainKey {
app.info.SetChainID(state, value)
return "Success", nil
}
return "", fmt.Errorf("unknown base option: %s", key)
}
log, err := app.handler.InitState(app.Logger(), state, module, key, value)
if err != nil {
app.Logger().Error("Genesis App Options", "err", err)
} else {
app.Logger().Info(log)
}
return log, err
}

127
app/bc.go
View File

@ -1,127 +0,0 @@
package app
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/stack"
sm "github.com/cosmos/cosmos-sdk/state"
"github.com/cosmos/cosmos-sdk/version"
)
// Basecoin - The ABCI application
type Basecoin struct {
*BaseApp
handler sdk.Handler
tick Ticker
}
// Ticker - tick function
type Ticker func(sm.SimpleDB) ([]*abci.Validator, error)
var _ abci.Application = &Basecoin{}
// NewBasecoin - create a new instance of the basecoin application
func NewBasecoin(handler sdk.Handler, dbName string, cacheSize int, logger log.Logger) (*Basecoin, error) {
appName := fmt.Sprintf("Basecoin v%v", version.Version)
base, err := NewBaseApp(appName, dbName, cacheSize, logger)
app := &Basecoin{
BaseApp: base,
handler: handler,
}
return app, err
}
// NewBasecoinTick - create a new instance of the basecoin application with tick functionality
func NewBasecoinTick(handler sdk.Handler, tick Ticker, dbName string, cacheSize int, logger log.Logger) (*Basecoin, error) {
appName := fmt.Sprintf("Basecoin v%v", version.Version)
base, err := NewBaseApp(appName, dbName, cacheSize, logger)
app := &Basecoin{
BaseApp: base,
handler: handler,
tick: tick,
}
return app, err
}
// InitState - used to setup state (was SetOption)
// to be used by InitChain later
func (app *Basecoin) InitState(module, key, value string) (string, error) {
state := app.Append()
if module == ModuleNameBase {
if key == ChainKey {
app.info.SetChainID(state, value)
return "Success", nil
}
return "", fmt.Errorf("unknown base option: %s", key)
}
log, err := app.handler.InitState(app.Logger(), state, module, key, value)
if err != nil {
app.Logger().Error("Genesis App Options", "err", err)
} else {
app.Logger().Info(log)
}
return log, err
}
// DeliverTx - ABCI
func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
tx, err := sdk.LoadTx(txBytes)
if err != nil {
return errors.Result(err)
}
ctx := stack.NewContext(
app.GetChainID(),
app.height+1,
app.Logger().With("call", "delivertx"),
)
res, err := app.handler.DeliverTx(ctx, app.Append(), tx)
if err != nil {
return errors.Result(err)
}
app.AddValChange(res.Diff)
return sdk.ToABCI(res)
}
// CheckTx - ABCI
func (app *Basecoin) CheckTx(txBytes []byte) abci.Result {
tx, err := sdk.LoadTx(txBytes)
if err != nil {
return errors.Result(err)
}
ctx := stack.NewContext(
app.GetChainID(),
app.height+1,
app.Logger().With("call", "checktx"),
)
res, err := app.handler.CheckTx(ctx, app.Check(), tx)
if err != nil {
return errors.Result(err)
}
return sdk.ToABCI(res)
}
// BeginBlock - ABCI
func (app *Basecoin) BeginBlock(req abci.RequestBeginBlock) {
// call the embeded Begin
app.BaseApp.BeginBlock(req)
// now execute tick
if app.tick != nil {
diff, err := app.tick(app.Append())
if err != nil {
panic(err)
}
app.AddValChange(diff)
}
}

View File

@ -19,11 +19,9 @@ const genesisAcctFilepath = "./testdata/genesis2.json"
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
logger := log.TestingLogger()
app, err := NewBasecoin(DefaultHandler("mycoin"),
"",
0,
logger)
store, err := MockStoreApp("genesis", logger)
require.Nil(t, err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = LoadGenesis(app, "./testdata/genesis3.json")
require.Nil(t, err, "%+v", err)
@ -33,11 +31,9 @@ func TestLoadGenesis(t *testing.T) {
assert, require := assert.New(t), require.New(t)
logger := log.TestingLogger()
app, err := NewBasecoin(DefaultHandler("mycoin"),
"",
0,
logger)
store, err := MockStoreApp("genesis", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = LoadGenesis(app, genesisFilepath)
require.Nil(err, "%+v", err)
@ -67,11 +63,9 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
assert, require := assert.New(t), require.New(t)
logger := log.TestingLogger()
app, err := NewBasecoin(DefaultHandler("mycoin"),
"",
0,
logger)
store, err := MockStoreApp("genesis", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, DefaultHandler("mycoin"), nil)
err = LoadGenesis(app, genesisAcctFilepath)
require.Nil(err, "%+v", err)

View File

@ -22,12 +22,12 @@ const (
ChainKey = "chain_id"
)
// BaseApp contains a data store and all info needed
// StoreApp contains a data store and all info needed
// to perform queries and handshakes.
//
// It should be embeded in another struct for CheckTx,
// DeliverTx and initializing state from the genesis.
type BaseApp struct {
type StoreApp struct {
// Name is what is returned from info
Name string
@ -44,63 +44,78 @@ type BaseApp struct {
logger log.Logger
}
// NewBaseApp creates a data store to handle queries
func NewBaseApp(appName, dbName string, cacheSize int, logger log.Logger) (*BaseApp, error) {
// NewStoreApp creates a data store to handle queries
func NewStoreApp(appName, dbName string, cacheSize int, logger log.Logger) (*StoreApp, error) {
state, err := loadState(dbName, cacheSize)
if err != nil {
return nil, err
}
app := &BaseApp{
app := &StoreApp{
Name: appName,
State: state,
height: state.LatestHeight(),
info: sm.NewChainState(),
logger: logger,
logger: logger.With("module", "app"),
}
return app, nil
}
// MockStoreApp returns a Store app with no persistence
func MockStoreApp(appName string, logger log.Logger) (*StoreApp, error) {
return NewStoreApp(appName, "", 0, logger)
}
// GetChainID returns the currently stored chain
func (app *BaseApp) GetChainID() string {
func (app *StoreApp) GetChainID() string {
return app.info.GetChainID(app.Committed())
}
// Logger returns the application base logger
func (app *BaseApp) Logger() log.Logger {
func (app *StoreApp) Logger() log.Logger {
return app.logger
}
// Hash gets the last hash stored in the database
func (app *BaseApp) Hash() []byte {
func (app *StoreApp) Hash() []byte {
return app.State.LatestHash()
}
// CommittedHeight gets the last block height committed
// to the db
func (app *StoreApp) CommittedHeight() uint64 {
return app.height
}
// WorkingHeight gets the current block we are writing
func (app *StoreApp) WorkingHeight() uint64 {
return app.height + 1
}
// Info implements abci.Application. It returns the height and hash,
// as well as the abci name and version.
//
// The height is the block that holds the transactions, not the apphash itself.
func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo {
func (app *StoreApp) Info(req abci.RequestInfo) abci.ResponseInfo {
hash := app.Hash()
app.logger.Info("Info synced",
"height", app.height,
"height", app.CommittedHeight(),
"hash", fmt.Sprintf("%X", hash))
return abci.ResponseInfo{
// TODO
Data: app.Name,
LastBlockHeight: app.height,
LastBlockHeight: app.CommittedHeight(),
LastBlockAppHash: hash,
}
}
// SetOption - ABCI
func (app *BaseApp) SetOption(key string, value string) string {
func (app *StoreApp) SetOption(key string, value string) string {
return "Not Implemented"
}
// Query - ABCI
func (app *BaseApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
func (app *StoreApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
if len(reqQuery.Data) == 0 {
resQuery.Log = "Query cannot be zero length"
resQuery.Code = abci.CodeType_EncodingError
@ -120,7 +135,7 @@ func (app *BaseApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQue
// if tree.Tree.VersionExists(app.height - 1) {
// height = app.height - 1
// } else {
height = app.height
height = app.CommittedHeight()
// }
}
resQuery.Height = height
@ -150,7 +165,7 @@ func (app *BaseApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQue
}
// Commit implements abci.Application
func (app *BaseApp) Commit() (res abci.Result) {
func (app *StoreApp) Commit() (res abci.Result) {
app.height++
hash, err := app.State.Commit(app.height)
@ -170,16 +185,14 @@ func (app *BaseApp) Commit() (res abci.Result) {
}
// InitChain - ABCI
func (app *BaseApp) InitChain(req abci.RequestInitChain) {
}
func (app *StoreApp) InitChain(req abci.RequestInitChain) {}
// BeginBlock - ABCI
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) {
}
func (app *StoreApp) BeginBlock(req abci.RequestBeginBlock) {}
// EndBlock - ABCI
// Returns a list of all validator changes made in this block
func (app *BaseApp) EndBlock(height uint64) (res abci.ResponseEndBlock) {
func (app *StoreApp) EndBlock(height uint64) (res abci.ResponseEndBlock) {
// TODO: cleanup in case a validator exists multiple times in the list
res.Diffs = app.pending
app.pending = nil
@ -189,7 +202,7 @@ func (app *BaseApp) EndBlock(height uint64) (res abci.ResponseEndBlock) {
// AddValChange is meant to be called by apps on DeliverTx
// results, this is added to the cache for the endblock
// changeset
func (app *BaseApp) AddValChange(diffs []*abci.Validator) {
func (app *StoreApp) AddValChange(diffs []*abci.Validator) {
for _, d := range diffs {
idx := pubKeyIndex(d, app.pending)
if idx >= 0 {

View File

@ -40,8 +40,9 @@ func TestEndBlock(t *testing.T) {
logger := log.NewNopLogger()
handler := base.ValSetHandler{}
app, err := NewBasecoin(handler, "", 0, logger)
store, err := MockStoreApp("vals", logger)
require.Nil(err, "%+v", err)
app := NewBaseApp(store, handler, nil)
val1 := makeVal()
val2 := makeVal()

View File

@ -10,7 +10,7 @@ import (
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/app"
sdkapp "github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/modules/auth"
"github.com/cosmos/cosmos-sdk/modules/base"
"github.com/cosmos/cosmos-sdk/modules/coin"
@ -21,7 +21,7 @@ import (
)
type BenchApp struct {
App *app.Basecoin
App *sdkapp.BaseApp
Accounts []*coin.AccountWithKey
ChainID string
}
@ -59,15 +59,11 @@ func NewBenchApp(h sdk.Handler, chainID string, n int,
cache = 500
}
app, err := app.NewBasecoin(
h,
dbDir,
cache,
logger.With("module", "app"),
)
store, err := sdkapp.NewStoreApp("bench", dbDir, cache, logger)
if err != nil {
panic(err)
}
app := sdkapp.NewBaseApp(store, h, nil)
_, err = app.InitState("base", "chain_id", chainID)
if err != nil {

View File

@ -18,7 +18,7 @@ import (
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tmlibs/log"
"github.com/cosmos/cosmos-sdk/app"
sdkapp "github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/modules/eyes"
)
@ -26,10 +26,11 @@ var node *nm.Node
func TestMain(m *testing.M) {
logger := log.TestingLogger()
app, err := app.NewBasecoin(eyes.NewHandler(), "", 0, logger)
store, err := sdkapp.MockStoreApp("query", logger)
if err != nil {
panic(err)
}
app := sdkapp.NewBaseApp(store, eyes.NewHandler(), nil)
node = rpctest.StartTendermint(app)

View File

@ -66,7 +66,7 @@ test01GetInsecure() {
INFO=$(${CLIENT_EXE} rpc info)
assertTrue "line=${LINENO}, get info" "$?"
DATA=$(echo $INFO | jq .response.data)
assertEquals "line=${LINENO}, basecoin info" '"Basecoin v0.7.1"' "$DATA"
assertEquals "line=${LINENO}, basecoin info" '"basecoin v0.7.1"' "$DATA"
}
test02GetSecure() {

View File

@ -28,13 +28,9 @@ func TestCounterPlugin(t *testing.T) {
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
h := NewHandler("gold")
bcApp, err := app.NewBasecoin(
h,
"",
0,
logger.With("module", "app"),
)
store, err := app.MockStoreApp("counter", logger)
require.Nil(err, "%+v", err)
bcApp := app.NewBaseApp(store, h, nil)
_, err = bcApp.InitState("base", "chain_id", chainID)
require.Nil(err, "%+v", err)

View File

@ -25,6 +25,18 @@ type Handler interface {
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
}
// Ticker can be executed every block
type Ticker interface {
Tick(Context, state.SimpleDB) ([]*abci.Validator, error)
}
// TickerFunc allows a function to implement the interface
type TickerFunc func(Context, state.SimpleDB) ([]*abci.Validator, error)
func (t TickerFunc) Tick(ctx Context, store state.SimpleDB) ([]*abci.Validator, error) {
return t(ctx, store)
}
// Named ensures there is a name for the item
type Named interface {
Name() string

View File

@ -21,6 +21,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/version"
)
// StartCmd - command to start running the abci app (and tendermint)!
@ -31,7 +32,7 @@ var StartCmd = &cobra.Command{
}
// GetTickStartCmd - initialize a command as the start command with tick
func GetTickStartCmd(tick app.Ticker) *cobra.Command {
func GetTickStartCmd(tick sdk.Ticker) *cobra.Command {
startCmd := &cobra.Command{
Use: "start",
Short: "Start this full node",
@ -70,20 +71,23 @@ func addStartFlag(startCmd *cobra.Command) {
}
//returns the start command which uses the tick
func tickStartCmd(tick app.Ticker) func(cmd *cobra.Command, args []string) error {
func tickStartCmd(clock sdk.Ticker) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
rootDir := viper.GetString(cli.HomeFlag)
// Create Basecoin app
basecoinApp, err := app.NewBasecoinTick(
Handler,
tick,
cmdName := cmd.Root().Name()
appName := fmt.Sprintf("%s v%v", cmdName, version.Version)
storeApp, err := app.NewStoreApp(
appName,
path.Join(rootDir, "data", "merkleeyes.db"),
EyesCacheSize,
logger.With("module", "app"))
if err != nil {
return err
}
// Create Basecoin app
basecoinApp := app.NewBaseApp(storeApp, Handler, clock)
return start(rootDir, basecoinApp)
}
}
@ -91,19 +95,23 @@ func tickStartCmd(tick app.Ticker) func(cmd *cobra.Command, args []string) error
func startCmd(cmd *cobra.Command, args []string) error {
rootDir := viper.GetString(cli.HomeFlag)
// Create Basecoin app
basecoinApp, err := app.NewBasecoin(
Handler,
cmdName := cmd.Root().Name()
appName := fmt.Sprintf("%s v%v", cmdName, version.Version)
storeApp, err := app.NewStoreApp(
appName,
path.Join(rootDir, "data", "merkleeyes.db"),
EyesCacheSize,
logger.With("module", "app"))
if err != nil {
return err
}
// Create Basecoin app
basecoinApp := app.NewBaseApp(storeApp, Handler, nil)
return start(rootDir, basecoinApp)
}
func start(rootDir string, basecoinApp *app.Basecoin) error {
func start(rootDir string, basecoinApp *app.BaseApp) error {
// if chain_id has not been set yet, load the genesis.
// else, assume it's been loaded