diff --git a/cmd/basecoin/commands/start.go b/cmd/basecoin/commands/start.go index 8f12efed35..aeb49a1c35 100644 --- a/cmd/basecoin/commands/start.go +++ b/cmd/basecoin/commands/start.go @@ -55,7 +55,7 @@ func init() { func getHandler() basecoin.Handler { // use the default stack h := coin.NewHandler() - app := stack.NewDefault("change-this").Use(h) + app := stack.NewDefault().Use(h) return app // register IBC plugn diff --git a/docs/guide/counter/plugins/counter/counter_test.go b/docs/guide/counter/plugins/counter/counter_test.go index a15ad3ecc5..73f5ffb112 100644 --- a/docs/guide/counter/plugins/counter/counter_test.go +++ b/docs/guide/counter/plugins/counter/counter_test.go @@ -7,27 +7,34 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/app" + "github.com/tendermint/basecoin/modules/coin" + "github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/types" "github.com/tendermint/go-wire" eyescli "github.com/tendermint/merkleeyes/client" "github.com/tendermint/tmlibs/log" ) +// TODO: actually handle the counter here... +func CounterHandler() basecoin.Handler { + // use the default stack + h := coin.NewHandler() + return stack.NewDefault().Use(h) +} + func TestCounterPlugin(t *testing.T) { assert := assert.New(t) // Basecoin initialization eyesCli := eyescli.NewLocalClient("", 0) chainID := "test_chain_id" - bcApp := app.NewBasecoin(eyesCli, log.TestingLogger().With("module", "app")) + bcApp := app.NewBasecoin(CounterHandler(), eyesCli, + log.TestingLogger().With("module", "app")) bcApp.SetOption("base/chain_id", chainID) // t.Log(bcApp.Info()) - // Add Counter plugin - counterPlugin := New() - bcApp.RegisterPlugin(counterPlugin) - // Account initialization test1PrivAcc := types.PrivAccountFromSecret("test1") @@ -44,7 +51,7 @@ func TestCounterPlugin(t *testing.T) { tx := &types.AppTx{ Gas: gas, Fee: fee, - Name: counterPlugin.Name(), + Name: "counter", Input: types.NewTxInput(test1Acc.PubKey, inputCoins, inputSequence), Data: wire.BinaryBytes(CounterTx{Valid: true, Fee: appFee}), } diff --git a/handler.go b/handler.go index 6a6d556191..b0089ed280 100644 --- a/handler.go +++ b/handler.go @@ -3,10 +3,24 @@ package basecoin import ( abci "github.com/tendermint/abci/types" "github.com/tendermint/go-wire/data" + "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin/types" ) +// Handler is anything that processes a transaction +type Handler interface { + Checker + Deliver + SetOptioner + Named + // TODO: flesh these out as well + // SetOption(store types.KVStore, key, value string) (log string) + // InitChain(store types.KVStore, vals []*abci.Validator) + // BeginBlock(store types.KVStore, hash []byte, header *abci.Header) + // EndBlock(store types.KVStore, height uint64) abci.ResponseEndBlock +} + type Named interface { Name() string } @@ -33,16 +47,15 @@ func (c DeliverFunc) DeliverTx(ctx Context, store types.KVStore, tx Tx) (Result, return c(ctx, store, tx) } -// Handler is anything that processes a transaction -type Handler interface { - Checker - Deliver - Named - // TODO: flesh these out as well - // SetOption(store types.KVStore, key, value string) (log string) - // InitChain(store types.KVStore, vals []*abci.Validator) - // BeginBlock(store types.KVStore, hash []byte, header *abci.Header) - // EndBlock(store types.KVStore, height uint64) abci.ResponseEndBlock +type SetOptioner interface { + SetOption(l log.Logger, store types.KVStore, key, value string) (string, error) +} + +// SetOptionFunc (like http.HandlerFunc) is a shortcut for making wrapers +type SetOptionFunc func(log.Logger, types.KVStore, string, string) (string, error) + +func (c SetOptionFunc) SetOption(l log.Logger, store types.KVStore, key, value string) (string, error) { + return c(l, store, key, value) } // Result captures any non-error abci result @@ -58,3 +71,19 @@ func (r Result) ToABCI() abci.Result { Log: r.Log, } } + +// placeholders +// holders +type NopCheck struct{} + +func (_ NopCheck) CheckTx(Context, types.KVStore, Tx) (r Result, e error) { return } + +type NopDeliver struct{} + +func (_ NopDeliver) DeliverTx(Context, types.KVStore, Tx) (r Result, e error) { return } + +type NopOption struct{} + +func (_ NopOption) SetOption(log.Logger, types.KVStore, string, string) (string, error) { + return "", nil +} diff --git a/modules/coin/handler.go b/modules/coin/handler.go index 41eda35900..cabc6a062c 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -1,6 +1,8 @@ package coin import ( + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/errors" "github.com/tendermint/basecoin/types" @@ -74,6 +76,11 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoi return basecoin.Result{}, nil } +func (h Handler) SetOption(l log.Logger, store types.KVStore, key, value string) (log string, err error) { + // TODO + return "ok", nil +} + func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { // check if the tx is proper type and valid send, ok := tx.Unwrap().(SendTx) diff --git a/modules/fee/handler.go b/modules/fee/handler.go index 1b4556ef4e..f711cac953 100644 --- a/modules/fee/handler.go +++ b/modules/fee/handler.go @@ -23,6 +23,7 @@ type AccountChecker interface { type SimpleFeeHandler struct { AccountChecker MinFee types.Coins + stack.PassOption } func (_ SimpleFeeHandler) Name() string { diff --git a/stack/chain.go b/stack/chain.go index 70da742f67..e07e6dd688 100644 --- a/stack/chain.go +++ b/stack/chain.go @@ -12,7 +12,9 @@ const ( ) // Chain enforces that this tx was bound to the named chain -type Chain struct{} +type Chain struct { + PassOption +} func (_ Chain) Name() string { return NameRecovery diff --git a/stack/chain_test.go b/stack/chain_test.go index c9afaa7001..cb1e39059e 100644 --- a/stack/chain_test.go +++ b/stack/chain_test.go @@ -34,7 +34,7 @@ func TestChain(t *testing.T) { store := types.NewMemKVStore() // build the stack - ok := OKHandler{msg} + ok := OKHandler{Log: msg} app := New(Chain{}).Use(ok) for idx, tc := range cases { diff --git a/stack/helpers.go b/stack/helpers.go index ec7247a6fe..387a2ae96c 100644 --- a/stack/helpers.go +++ b/stack/helpers.go @@ -19,6 +19,7 @@ const ( // OKHandler just used to return okay to everything type OKHandler struct { Log string + basecoin.NopOption } var _ basecoin.Handler = OKHandler{} @@ -38,7 +39,9 @@ func (ok OKHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx base } // EchoHandler returns success, echoing res.Data = tx bytes -type EchoHandler struct{} +type EchoHandler struct { + basecoin.NopOption +} var _ basecoin.Handler = EchoHandler{} @@ -61,6 +64,7 @@ func (_ EchoHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas // FailHandler always returns an error type FailHandler struct { Err error + basecoin.NopOption } var _ basecoin.Handler = FailHandler{} @@ -83,6 +87,7 @@ func (f FailHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas type PanicHandler struct { Msg string Err error + basecoin.NopOption } var _ basecoin.Handler = PanicHandler{} diff --git a/stack/helpers_test.go b/stack/helpers_test.go index 10e3655800..8787bb6a6e 100644 --- a/stack/helpers_test.go +++ b/stack/helpers_test.go @@ -20,7 +20,7 @@ func TestOK(t *testing.T) { data := "this looks okay" tx := basecoin.Tx{} - ok := OKHandler{data} + ok := OKHandler{Log: data} res, err := ok.CheckTx(ctx, store, tx) assert.Nil(err, "%+v", err) assert.Equal(data, res.Log) @@ -38,7 +38,7 @@ func TestFail(t *testing.T) { msg := "big problem" tx := basecoin.Tx{} - fail := FailHandler{errors.New(msg)} + fail := FailHandler{Err: errors.New(msg)} _, err := fail.CheckTx(ctx, store, tx) if assert.NotNil(err) { assert.Equal(msg, err.Error()) diff --git a/stack/helperware.go b/stack/helperware.go index ec2282ac84..25c93f4e51 100644 --- a/stack/helperware.go +++ b/stack/helperware.go @@ -15,6 +15,7 @@ const ( // Required Actor, otherwise passes along the call untouched type CheckMiddleware struct { Required basecoin.Actor + PassOption } var _ Middleware = CheckMiddleware{} @@ -40,6 +41,7 @@ func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx // GrantMiddleware tries to set the permission to this Actor, which may be prohibited type GrantMiddleware struct { Auth basecoin.Actor + PassOption } var _ Middleware = GrantMiddleware{} diff --git a/stack/interface.go b/stack/interface.go index b57d6dbd04..951a7fa73f 100644 --- a/stack/interface.go +++ b/stack/interface.go @@ -1,18 +1,12 @@ package stack import ( + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" ) -type CheckerMiddle interface { - CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) -} - -type DeliverMiddle interface { - DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) -} - // Middleware is anything that wraps another handler to enhance functionality. // // You can use utilities in handlers to construct them, the interfaces @@ -20,5 +14,55 @@ type DeliverMiddle interface { type Middleware interface { CheckerMiddle DeliverMiddle + SetOptionMiddle basecoin.Named } + +type CheckerMiddle interface { + CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) +} + +type CheckerMiddleFunc func(basecoin.Context, types.KVStore, basecoin.Tx, basecoin.Checker) (basecoin.Result, error) + +func (c CheckerMiddleFunc) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) { + return c(ctx, store, tx, next) +} + +type DeliverMiddle interface { + DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) +} + +type DeliverMiddleFunc func(basecoin.Context, types.KVStore, basecoin.Tx, basecoin.Deliver) (basecoin.Result, error) + +func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) { + return d(ctx, store, tx, next) +} + +type SetOptionMiddle interface { + SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) +} + +type SetOptionMiddleFunc func(log.Logger, types.KVStore, string, string, basecoin.SetOptioner) (string, error) + +func (c SetOptionMiddleFunc) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) { + return c(l, store, key, value, next) +} + +// holders +type PassCheck struct{} + +func (_ PassCheck) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) { + return next.CheckTx(ctx, store, tx) +} + +type PassDeliver struct{} + +func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) { + return next.DeliverTx(ctx, store, tx) +} + +type PassOption struct{} + +func (_ PassOption) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) { + return next.SetOption(l, store, key, value) +} diff --git a/stack/logger.go b/stack/logger.go index 93cc8e5e50..2bd41a5fa2 100644 --- a/stack/logger.go +++ b/stack/logger.go @@ -3,6 +3,8 @@ package stack import ( "time" + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" ) @@ -48,6 +50,20 @@ func (_ Logger) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin return } +func (_ Logger) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (string, error) { + start := time.Now() + res, err := next.SetOption(l, store, key, value) + delta := time.Now().Sub(start) + // TODO: log the value being set also? + l = l.With("duration", micros(delta)).With("key", key) + if err == nil { + l.Info("SetOption", "log", res) + } else { + l.Error("SetOption", "err", err) + } + return res, err +} + // micros returns how many microseconds passed in a call func micros(d time.Duration) int { return int(d.Seconds() * 1000000) diff --git a/stack/middleware.go b/stack/middleware.go index f61b233da2..fcc450b730 100644 --- a/stack/middleware.go +++ b/stack/middleware.go @@ -1,6 +1,8 @@ package stack import ( + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" ) @@ -37,6 +39,10 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas return m.middleware.DeliverTx(ctx, store, tx, next) } +func (m *middleware) SetOption(l log.Logger, store types.KVStore, key, value string) (string, error) { + return m.middleware.SetOption(l, store, key, value, m.next) +} + // Stack is the entire application stack type Stack struct { middles []Middleware diff --git a/stack/middleware_test.go b/stack/middleware_test.go index adbb970932..4f88539191 100644 --- a/stack/middleware_test.go +++ b/stack/middleware_test.go @@ -42,8 +42,8 @@ func TestPermissionSandbox(t *testing.T) { for i, tc := range cases { app := New( Recovery{}, // we need this so panics turn to errors - GrantMiddleware{tc.grant}, - CheckMiddleware{tc.require}, + GrantMiddleware{Auth: tc.grant}, + CheckMiddleware{Required: tc.require}, ).Use(EchoHandler{}) res, err := app.CheckTx(ctx, store, raw) diff --git a/stack/multiplexer.go b/stack/multiplexer.go index b0cddfc98e..2000551cb2 100644 --- a/stack/multiplexer.go +++ b/stack/multiplexer.go @@ -14,7 +14,9 @@ const ( NameMultiplexer = "mplx" ) -type Multiplexer struct{} +type Multiplexer struct { + PassOption +} func (_ Multiplexer) Name() string { return NameMultiplexer diff --git a/stack/recovery.go b/stack/recovery.go index b6e84496c6..7dbb10a439 100644 --- a/stack/recovery.go +++ b/stack/recovery.go @@ -3,6 +3,8 @@ package stack import ( "fmt" + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/errors" "github.com/tendermint/basecoin/types" @@ -39,6 +41,15 @@ func (_ Recovery) DeliverTx(ctx basecoin.Context, store types.KVStore, tx baseco return next.DeliverTx(ctx, store, tx) } +func (_ Recovery) SetOption(l log.Logger, store types.KVStore, key, value string, next basecoin.SetOptioner) (log string, err error) { + defer func() { + if r := recover(); r != nil { + err = normalizePanic(r) + } + }() + return next.SetOption(l, store, key, value) +} + // normalizePanic makes sure we can get a nice TMError (with stack) out of it func normalizePanic(p interface{}) error { if err, isErr := p.(error); isErr { diff --git a/stack/signature.go b/stack/signature.go index 049351dd10..d996f921a7 100644 --- a/stack/signature.go +++ b/stack/signature.go @@ -13,7 +13,9 @@ const ( NameSigs = "sigs" ) -type Signatures struct{} +type Signatures struct { + PassOption +} func (_ Signatures) Name() string { return NameSigs diff --git a/stack/signature_test.go b/stack/signature_test.go index 1e6f484ab3..0dbf1a6173 100644 --- a/stack/signature_test.go +++ b/stack/signature_test.go @@ -58,7 +58,7 @@ func TestSignatureChecks(t *testing.T) { app := New( Recovery{}, // we need this so panics turn to errors Signatures{}, - CheckMiddleware{tc.check}, + CheckMiddleware{Required: tc.check}, ).Use(OKHandler{}) var tx basecoin.Tx