Add InitValidate method for setup

This commit is contained in:
Ethan Frey 2017-07-30 17:26:25 -04:00
parent 4b69f1d5ba
commit 37550ca91d
22 changed files with 164 additions and 39 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,9 @@ import (
const NameCoin = "coin"
// Handler includes an accountant
type Handler struct{}
type Handler struct {
stack.PassInitValidate
}
var _ stack.Dispatchable = Handler{}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,8 @@ const (
type Checkpoint struct {
OnCheck bool
OnDeliver bool
PassOption
PassInitState
PassInitValidate
}
// Name of the module - fulfills Middleware interface

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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