diff --git a/docs/guide/counter/plugins/counter/counter.go b/docs/guide/counter/plugins/counter/counter.go index 6a17653d30..53ae4f9f5e 100644 --- a/docs/guide/counter/plugins/counter/counter.go +++ b/docs/guide/counter/plugins/counter/counter.go @@ -114,7 +114,8 @@ func NewHandler(feeDenom string) basecoin.Handler { // Handler the counter transaction processing handler type Handler struct { - stack.NopOption + stack.PassInitState + stack.PassInitValidate } var _ stack.Dispatchable = Handler{} diff --git a/handler.go b/handler.go index a7e2b7334f..c15d479fad 100644 --- a/handler.go +++ b/handler.go @@ -10,22 +10,27 @@ import ( // Handler is anything that processes a transaction type Handler interface { + // Checker verifies there are valid fees and estimates work Checker + // Deliver performs the tx once it makes it in the block Deliver - // This is for app options + // InitStater sets state from the genesis file InitStater + // InitValidater sets the initial validator set + InitValidater + // Named ensures there is a name for the item Named - // TODO: for staker - // InitChain(log log.Logger, store state.SimpleDB, vals []*abci.Validator) // TODO???? // BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header) } +// Named ensures there is a name for the item type Named interface { Name() string } +// Checker verifies there are valid fees and estimates work type Checker interface { CheckTx(ctx Context, store state.SimpleDB, tx Tx) (CheckResult, error) } @@ -37,6 +42,7 @@ func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx Tx) (CheckRes return c(ctx, store, tx) } +// Deliver performs the tx once it makes it in the block type Deliver interface { DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (DeliverResult, error) } @@ -48,6 +54,7 @@ func (c DeliverFunc) DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (Delive return c(ctx, store, tx) } +// InitStater sets state from the genesis file type InitStater interface { InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) } @@ -59,6 +66,18 @@ func (c InitStateFunc) InitState(l log.Logger, store state.SimpleDB, module, key return c(l, store, module, key, value) } +// InitValidater sets the initial validator set +type InitValidater interface { + InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator) +} + +// InitValidateFunc (like http.HandlerFunc) is a shortcut for making wrapers +type InitValidateFunc func(log.Logger, state.SimpleDB, []*abci.Validator) + +func (c InitValidateFunc) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator) { + c(l, store, vals) +} + //---------- results and some wrappers -------- type Dataer interface { @@ -119,8 +138,12 @@ type NopDeliver struct{} func (_ NopDeliver) DeliverTx(Context, state.SimpleDB, Tx) (r DeliverResult, e error) { return } -type NopOption struct{} +type NopInitState struct{} -func (_ NopOption) InitState(log.Logger, state.SimpleDB, string, string, string) (string, error) { +func (_ NopInitState) InitState(log.Logger, state.SimpleDB, string, string, string) (string, error) { return "", nil } + +type NopInitValidate struct{} + +func (_ NopInitValidate) InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator) {} diff --git a/modules/auth/signature.go b/modules/auth/signature.go index a10ebd5b98..01ee72bd4c 100644 --- a/modules/auth/signature.go +++ b/modules/auth/signature.go @@ -17,7 +17,8 @@ const ( // Signatures parses out go-crypto signatures and adds permissions to the // context for use inside the application type Signatures struct { - stack.PassOption + stack.PassInitState + stack.PassInitValidate } // Name of the module - fulfills Middleware interface diff --git a/modules/base/chain.go b/modules/base/chain.go index 98391e14b4..b2fa845c48 100644 --- a/modules/base/chain.go +++ b/modules/base/chain.go @@ -13,7 +13,8 @@ const ( // Chain enforces that this tx was bound to the named chain type Chain struct { - stack.PassOption + stack.PassInitState + stack.PassInitValidate } // Name of the module - fulfills Middleware interface diff --git a/modules/base/logger.go b/modules/base/logger.go index 581fcfb42b..4d8d291352 100644 --- a/modules/base/logger.go +++ b/modules/base/logger.go @@ -3,6 +3,7 @@ package base import ( "time" + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -70,6 +71,15 @@ func (Logger) InitState(l log.Logger, store state.SimpleDB, module, key, value s return res, err } +// InitValidate logs time and result - fulfills Middlware interface +func (Logger) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator, next basecoin.InitValidater) { + start := time.Now() + next.InitValidate(l, store, vals) + delta := time.Now().Sub(start) + l = l.With("duration", micros(delta)) + l.Info("InitValidate") +} + // micros returns how many microseconds passed in a call func micros(d time.Duration) int { return int(d.Seconds() * 1000000) diff --git a/modules/base/multiplexer.go b/modules/base/multiplexer.go index 4f2c4713a0..f9eac82070 100644 --- a/modules/base/multiplexer.go +++ b/modules/base/multiplexer.go @@ -18,7 +18,7 @@ package base // // Multiplexer grabs a MultiTx and sends them sequentially down the line // type Multiplexer struct { -// stack.PassOption +// stack.PassInitState // } // // Name of the module - fulfills Middleware interface diff --git a/modules/coin/handler.go b/modules/coin/handler.go index dfaa2fa173..52fa820b09 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -16,7 +16,9 @@ import ( const NameCoin = "coin" // Handler includes an accountant -type Handler struct{} +type Handler struct { + stack.PassInitValidate +} var _ stack.Dispatchable = Handler{} diff --git a/modules/fee/handler.go b/modules/fee/handler.go index ab0e6fbb73..536131648f 100644 --- a/modules/fee/handler.go +++ b/modules/fee/handler.go @@ -24,7 +24,8 @@ type SimpleFeeMiddleware struct { // all fees go here, which could be a dump (Bank) or something reachable // by other app logic Collector basecoin.Actor - stack.PassOption + stack.PassInitState + stack.PassInitValidate } var _ stack.Middleware = SimpleFeeMiddleware{} diff --git a/modules/ibc/handler.go b/modules/ibc/handler.go index 298f62780e..b4df69c224 100644 --- a/modules/ibc/handler.go +++ b/modules/ibc/handler.go @@ -34,7 +34,9 @@ func AllowIBC(app string) basecoin.Actor { } // Handler updates the chain state or creates an ibc packet -type Handler struct{} +type Handler struct { + basecoin.NopInitValidate +} var _ basecoin.Handler = Handler{} diff --git a/modules/ibc/middleware.go b/modules/ibc/middleware.go index 191a533134..c81bcec48a 100644 --- a/modules/ibc/middleware.go +++ b/modules/ibc/middleware.go @@ -9,7 +9,8 @@ import ( // Middleware allows us to verify the IBC proof on a packet and // and if valid, attach this permission to the wrapped packet type Middleware struct { - stack.PassOption + stack.PassInitState + stack.PassInitValidate } var _ stack.Middleware = Middleware{} diff --git a/modules/nonce/replaycheck.go b/modules/nonce/replaycheck.go index 1a125dea9c..742cd348f7 100644 --- a/modules/nonce/replaycheck.go +++ b/modules/nonce/replaycheck.go @@ -13,7 +13,8 @@ const ( // ReplayCheck uses the sequence to check for replay attacks type ReplayCheck struct { - stack.PassOption + stack.PassInitState + stack.PassInitValidate } // Name of the module - fulfills Middleware interface diff --git a/modules/roles/handler.go b/modules/roles/handler.go index 155ba056b7..a3d4161bb9 100644 --- a/modules/roles/handler.go +++ b/modules/roles/handler.go @@ -11,7 +11,8 @@ const NameRole = "role" // Handler allows us to create new roles type Handler struct { - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = Handler{} diff --git a/modules/roles/middleware.go b/modules/roles/middleware.go index c25382a62f..229eb0a55e 100644 --- a/modules/roles/middleware.go +++ b/modules/roles/middleware.go @@ -9,7 +9,8 @@ import ( // Middleware allows us to add a requested role as a permission // if the tx requests it and has sufficient authority type Middleware struct { - stack.PassOption + stack.PassInitState + stack.PassInitValidate } var _ stack.Middleware = Middleware{} diff --git a/stack/checkpoint.go b/stack/checkpoint.go index 66524ae54d..e5426be8a9 100644 --- a/stack/checkpoint.go +++ b/stack/checkpoint.go @@ -14,7 +14,8 @@ const ( type Checkpoint struct { OnCheck bool OnDeliver bool - PassOption + PassInitState + PassInitValidate } // Name of the module - fulfills Middleware interface diff --git a/stack/checkpoint_test.go b/stack/checkpoint_test.go index dd356e3f96..013990649c 100644 --- a/stack/checkpoint_test.go +++ b/stack/checkpoint_test.go @@ -32,12 +32,12 @@ func makeState() state.SimpleDB { func TestCheckpointer(t *testing.T) { assert, require := assert.New(t), require.New(t) - good := writerHand{"foo", []byte{1, 2}, []byte("bar")} + good := writerHand{name: "foo", key: []byte{1, 2}, value: []byte("bar")} bad := FailHandler{Err: errors.New("no go")} app := New( Checkpoint{OnCheck: true}, - writerMid{"bing", []byte{1, 2}, []byte("bang")}, + writerMid{name: "bing", key: []byte{1, 2}, value: []byte("bang")}, Checkpoint{OnDeliver: true}, ).Use( NewDispatcher( diff --git a/stack/dispatcher.go b/stack/dispatcher.go index e4a34c0b7f..54d67f11c9 100644 --- a/stack/dispatcher.go +++ b/stack/dispatcher.go @@ -2,8 +2,10 @@ package stack import ( "fmt" + "sort" "strings" + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -119,6 +121,16 @@ func (d *Dispatcher) InitState(l log.Logger, store state.SimpleDB, module, key, return r.InitState(l, store, module, key, value, cb) } +// InitValidate makes sure all modules are informed +func (d *Dispatcher) InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator) { + for _, mod := range d.sortedModules() { + // no ctx, so secureCheck not needed + cb := d + space := stateSpace(store, mod.Name()) + mod.InitValidate(log, space, vals, cb) + } +} + func (d *Dispatcher) lookupTx(tx basecoin.Tx) (Dispatchable, error) { kind, err := tx.GetKind() if err != nil { @@ -140,3 +152,19 @@ func (d *Dispatcher) lookupModule(name string) (Dispatchable, error) { } return r, nil } + +func (d *Dispatcher) sortedModules() []Dispatchable { + // order all routes names + size := len(d.routes) + names := make([]string, 0, size) + for k := range d.routes { + names = append(names, k) + } + sort.Strings(names) + + res := make([]Dispatchable, size) + for i, k := range names { + res[i] = d.routes[k] + } + return res +} diff --git a/stack/helpers.go b/stack/helpers.go index e91086d35d..ca3c7f586a 100644 --- a/stack/helpers.go +++ b/stack/helpers.go @@ -94,7 +94,8 @@ func (r FailTx) ValidateBasic() error { // OKHandler just used to return okay to everything type OKHandler struct { Log string - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = OKHandler{} @@ -116,7 +117,8 @@ func (ok OKHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx bas // EchoHandler returns success, echoing res.Data = tx bytes type EchoHandler struct { - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = EchoHandler{} @@ -141,7 +143,8 @@ func (EchoHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx base // FailHandler always returns an error type FailHandler struct { Err error - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = FailHandler{} @@ -165,7 +168,8 @@ func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx ba type PanicHandler struct { Msg string Err error - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = PanicHandler{} @@ -193,7 +197,8 @@ func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx b // CheckHandler accepts CheckTx and verifies the permissions type CheckHandler struct { - basecoin.NopOption + basecoin.NopInitState + basecoin.NopInitValidate } var _ basecoin.Handler = CheckHandler{} diff --git a/stack/helperware.go b/stack/helperware.go index 6fd2078a31..003b1156b7 100644 --- a/stack/helperware.go +++ b/stack/helperware.go @@ -16,7 +16,8 @@ const ( // Required Actor, otherwise passes along the call untouched type CheckMiddleware struct { Required basecoin.Actor - PassOption + PassInitState + PassInitValidate } var _ Middleware = CheckMiddleware{} @@ -42,7 +43,8 @@ func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, t // GrantMiddleware tries to set the permission to this Actor, which may be prohibited type GrantMiddleware struct { Auth basecoin.Actor - PassOption + PassInitState + PassInitValidate } var _ Middleware = GrantMiddleware{} diff --git a/stack/interface.go b/stack/interface.go index 173e61d143..35fe35e8df 100644 --- a/stack/interface.go +++ b/stack/interface.go @@ -2,6 +2,7 @@ package stack import ( + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -15,7 +16,8 @@ import ( type Middleware interface { CheckerMiddle DeliverMiddle - InitStateMiddle + InitStaterMiddle + InitValidaterMiddle basecoin.Named } @@ -45,19 +47,31 @@ func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store state.SimpleDB, return d(ctx, store, tx, next) } -type InitStateMiddle interface { +type InitStaterMiddle interface { InitState(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.InitStater) (string, error) } -type InitStateMiddleFunc func(log.Logger, state.SimpleDB, +type InitStaterMiddleFunc func(log.Logger, state.SimpleDB, string, string, string, basecoin.InitStater) (string, error) -func (c InitStateMiddleFunc) InitState(l log.Logger, store state.SimpleDB, +func (c InitStaterMiddleFunc) InitState(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.InitStater) (string, error) { return c(l, store, module, key, value, next) } +type InitValidaterMiddle interface { + InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator, next basecoin.InitValidater) +} + +type InitValidaterMiddleFunc func(log.Logger, state.SimpleDB, + []*abci.Validator, basecoin.InitValidater) + +func (c InitValidaterMiddleFunc) InitValidate(l log.Logger, store state.SimpleDB, + vals []*abci.Validator, next basecoin.InitValidater) { + c(l, store, vals, next) +} + // holders type PassCheck struct{} @@ -73,18 +87,18 @@ func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store state.SimpleDB, return next.DeliverTx(ctx, store, tx) } -type PassOption struct{} +type PassInitState struct{} -func (_ PassOption) InitState(l log.Logger, store state.SimpleDB, module, +func (_ PassInitState) InitState(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.InitStater) (string, error) { return next.InitState(l, store, module, key, value) } -type NopOption struct{} +type PassInitValidate struct{} -func (_ NopOption) InitState(l log.Logger, store state.SimpleDB, module, - key, value string, next basecoin.InitStater) (string, error) { - return "", nil +func (_ PassInitValidate) InitValidate(l log.Logger, store state.SimpleDB, + vals []*abci.Validator, next basecoin.InitValidater) { + next.InitValidate(l, store, vals) } // Dispatchable is like middleware, except the meaning of "next" is different. @@ -126,3 +140,8 @@ func (w wrapped) InitState(l log.Logger, store state.SimpleDB, module, key, value string, _ basecoin.InitStater) (string, error) { return w.h.InitState(l, store, module, key, value) } + +func (w wrapped) InitValidate(l log.Logger, store state.SimpleDB, + vals []*abci.Validator, next basecoin.InitValidater) { + w.h.InitValidate(l, store, vals) +} diff --git a/stack/middleware.go b/stack/middleware.go index 4ba29a2023..ebd2a4bba4 100644 --- a/stack/middleware.go +++ b/stack/middleware.go @@ -1,6 +1,7 @@ package stack import ( + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -59,6 +60,12 @@ func (m *middleware) InitState(l log.Logger, store state.SimpleDB, module, key, return m.middleware.InitState(l, store, module, key, value, m.next) } +func (m *middleware) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator) { + // set the namespace for the app + store = stateSpace(store, m.space) + m.middleware.InitValidate(l, store, vals, m.next) +} + // builder is used to associate info with the middleware, so we can build // it properly type builder struct { diff --git a/stack/recovery.go b/stack/recovery.go index e5b986ef27..cbf9d2d49f 100644 --- a/stack/recovery.go +++ b/stack/recovery.go @@ -3,6 +3,7 @@ package stack import ( "fmt" + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -55,6 +56,21 @@ func (Recovery) InitState(l log.Logger, store state.SimpleDB, module, key, value return next.InitState(l, store, module, key, value) } +// InitValidate catches any panic and logs it +// TODO: return an error??? +func (Recovery) InitValidate(l log.Logger, store state.SimpleDB, + vals []*abci.Validator, next basecoin.InitValidater) { + + defer func() { + if r := recover(); r != nil { + // TODO: return an error??? + err := normalizePanic(r) + l.With("err", err).Error(err.Error()) + } + }() + next.InitValidate(l, store, vals) +} + // 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/state_space_test.go b/stack/state_space_test.go index b18671106d..8f9d302e37 100644 --- a/stack/state_space_test.go +++ b/stack/state_space_test.go @@ -17,6 +17,7 @@ import ( type writerMid struct { name string key, value []byte + PassInitValidate } var _ Middleware = writerMid{} @@ -41,10 +42,11 @@ func (w writerMid) InitState(l log.Logger, store state.SimpleDB, module, return next.InitState(l, store, module, key, value) } -// writerHand is a middleware that writes the given bytes on CheckTx and DeliverTx +// writerHand is a handler that writes the given bytes on CheckTx and DeliverTx type writerHand struct { name string key, value []byte + basecoin.NopInitValidate } var _ basecoin.Handler = writerHand{} @@ -76,9 +78,9 @@ func TestStateSpace(t *testing.T) { expected []data.Bytes }{ { - writerHand{"foo", []byte{1, 2}, []byte("bar")}, + writerHand{name: "foo", key: []byte{1, 2}, value: []byte("bar")}, []Middleware{ - writerMid{"bing", []byte{1, 2}, []byte("bang")}, + writerMid{name: "bing", key: []byte{1, 2}, value: []byte("bang")}, }, []data.Bytes{ {'f', 'o', 'o', 0, 1, 2},