Add SetOption to all middleware and handlers

This commit is contained in:
Ethan Frey 2017-07-03 18:10:46 +02:00
parent 159574db89
commit fa1a300943
18 changed files with 169 additions and 35 deletions

View File

@ -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

View File

@ -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}),
}

View File

@ -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
}

View File

@ -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)

View File

@ -23,6 +23,7 @@ type AccountChecker interface {
type SimpleFeeHandler struct {
AccountChecker
MinFee types.Coins
stack.PassOption
}
func (_ SimpleFeeHandler) Name() string {

View File

@ -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

View File

@ -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 {

View File

@ -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{}

View File

@ -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())

View File

@ -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{}

View File

@ -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)
}

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -14,7 +14,9 @@ const (
NameMultiplexer = "mplx"
)
type Multiplexer struct{}
type Multiplexer struct {
PassOption
}
func (_ Multiplexer) Name() string {
return NameMultiplexer

View File

@ -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 {

View File

@ -13,7 +13,9 @@ const (
NameSigs = "sigs"
)
type Signatures struct{}
type Signatures struct {
PassOption
}
func (_ Signatures) Name() string {
return NameSigs

View File

@ -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