diff --git a/decorators.go b/decorators.go new file mode 100644 index 000000000..8dce90281 --- /dev/null +++ b/decorators.go @@ -0,0 +1,96 @@ +package sdk + +import ( + "github.com/cosmos/cosmos-sdk/state" +) + +// Decorator is anything that wraps another handler +// to enhance functionality. +// +// They are usually chained together via ChainDecorators +// before wrapping an interface. +type Decorator interface { + DecorateChecker + DecorateDeliverer +} + +type DecorateChecker interface { + CheckTx(ctx Context, store state.SimpleDB, + tx interface{}, next Checker) (CheckResult, error) +} + +type DecorateDeliverer interface { + DeliverTx(ctx Context, store state.SimpleDB, tx interface{}, + next Deliverer) (DeliverResult, error) +} + +// Stack is the entire application stack +type Stack struct { + decorators []Decorator + handler Handler + Handler // the compiled version, which we expose +} + +var _ Handler = &Stack{} + +// ChainDecorators prepares a stack of decorators, +// you must call `.WithHandler()` before you can execute it. +func ChainDecorators(decorators ...Decorator) *Stack { + s := &Stack{ + decorators: decorators, + } + return s +} + +// WithHandler sets the final handler for the stack and +// prepares it for use +func (s *Stack) WithHandler(handler Handler) *Stack { + if handler == nil { + panic("Cannot have a Stack without an end handler") + } + s.handler = handler + s.Handler = build(s.decorators, s.handler) + return s +} + +// build wraps each decorator around the next, so that +// the last in the list is closest to the handler +func build(stack []Decorator, end Handler) Handler { + if len(stack) == 0 { + return end + } + return wrap(stack[0], build(stack[1:], end)) +} + +// decorator lets us wrap a whole stack up into one Handler +// +// heavily inspired by negroni's design +type decorator struct { + decorator Decorator + next Handler +} + +// ensure it fulfils the interface +var _ Handler = &decorator{} + +// CheckTx fulfils Handler interface +func (m *decorator) CheckTx(ctx Context, store state.SimpleDB, + tx interface{}) (CheckResult, error) { + + return m.decorator.CheckTx(ctx, store, tx, m.next) +} + +// DeliverTx fulfils Handler interface +func (m *decorator) DeliverTx(ctx Context, store state.SimpleDB, + tx interface{}) (res DeliverResult, err error) { + + return m.decorator.DeliverTx(ctx, store, tx, m.next) +} + +// wrap puts one decorator around a handler +func wrap(dec Decorator, next Handler) Handler { + return &decorator{ + decorator: dec, + next: next, + } +} diff --git a/handler.go b/handler.go index f7d663802..97356c4ab 100644 --- a/handler.go +++ b/handler.go @@ -2,7 +2,6 @@ package sdk import ( abci "github.com/tendermint/abci/types" - "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/log" "github.com/cosmos/cosmos-sdk/state" @@ -15,24 +14,44 @@ const ( ChainKey = "chain_id" ) -// Handler is anything that processes a transaction +// Handler is anything that processes a transaction. +// Must handle checktx and delivertx 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 - // 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???? - // BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header) + Deliverer } -// Ticker can be executed every block +// Checker verifies there are valid fees and estimates work +type Checker interface { + CheckTx(ctx Context, store state.SimpleDB, tx interface{}) (CheckResult, error) +} + +// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrappers +type CheckerFunc func(Context, state.SimpleDB, interface{}) (CheckResult, error) + +func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx interface{}) (CheckResult, error) { + return c(ctx, store, tx) +} + +// Deliverer performs the tx once it makes it in the block +type Deliverer interface { + DeliverTx(ctx Context, store state.SimpleDB, tx interface{}) (DeliverResult, error) +} + +// DelivererFunc (like http.HandlerFunc) is a shortcut for making wrappers +type DelivererFunc func(Context, state.SimpleDB, interface{}) (DeliverResult, error) + +func (c DelivererFunc) DeliverTx(ctx Context, store state.SimpleDB, tx interface{}) (DeliverResult, error) { + return c(ctx, store, tx) +} + +///////////////////////////////////////////////// +// Lifecycle actions, not tied to the tx handler + +// Ticker can be executed every block. +// Called from BeginBlock type Ticker interface { Tick(Context, state.SimpleDB) ([]*abci.Validator, error) } @@ -44,139 +63,17 @@ func (t TickerFunc) Tick(ctx Context, store state.SimpleDB) ([]*abci.Validator, return t(ctx, store) } -// 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) -} - -// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrappers -type CheckerFunc func(Context, state.SimpleDB, Tx) (CheckResult, error) - -func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx Tx) (CheckResult, error) { - 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) -} - -// DeliverFunc (like http.HandlerFunc) is a shortcut for making wrappers -type DeliverFunc func(Context, state.SimpleDB, Tx) (DeliverResult, error) - -func (c DeliverFunc) DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (DeliverResult, error) { - return c(ctx, store, tx) +// InitValidator sets the initial validator set. +// Called from InitChain +type InitValidator interface { + InitValidators(logger log.Logger, store state.SimpleDB, + vals []*abci.Validator) } // InitStater sets state from the genesis file +// +// TODO: Think if this belongs here, in genesis, or somewhere else type InitStater interface { - InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) + InitState(logger log.Logger, store state.SimpleDB, + module, key, value string) (string, error) } - -// InitStateFunc (like http.HandlerFunc) is a shortcut for making wrappers -type InitStateFunc func(log.Logger, state.SimpleDB, string, string, string) (string, error) - -func (c InitStateFunc) InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) { - 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 wrappers -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 -------- - -// Result is a common interface of CheckResult and GetResult -type Result interface { - GetData() data.Bytes - GetLog() string -} - -func ToABCI(r Result) abci.Result { - return abci.Result{ - Data: r.GetData(), - Log: r.GetLog(), - } -} - -// CheckResult captures any non-error abci result -// to make sure people use error for error cases -type CheckResult struct { - Data data.Bytes - Log string - // GasAllocated is the maximum units of work we allow this tx to perform - GasAllocated uint64 - // GasPayment is the total fees for this tx (or other source of payment) - GasPayment uint64 -} - -// NewCheck sets the gas used and the response data but no more info -// these are the most common info needed to be set by the Handler -func NewCheck(gasAllocated uint64, log string) CheckResult { - return CheckResult{ - GasAllocated: gasAllocated, - Log: log, - } -} - -var _ Result = CheckResult{} - -func (r CheckResult) GetData() data.Bytes { - return r.Data -} - -func (r CheckResult) GetLog() string { - return r.Log -} - -// DeliverResult captures any non-error abci result -// to make sure people use error for error cases -type DeliverResult struct { - Data data.Bytes - Log string - Diff []*abci.Validator - GasUsed uint64 -} - -var _ Result = DeliverResult{} - -func (r DeliverResult) GetData() data.Bytes { - return r.Data -} - -func (r DeliverResult) GetLog() string { - return r.Log -} - -// placeholders -// holders -type NopCheck struct{} - -func (_ NopCheck) CheckTx(Context, state.SimpleDB, Tx) (r CheckResult, e error) { return } - -type NopDeliver struct{} - -func (_ NopDeliver) DeliverTx(Context, state.SimpleDB, Tx) (r DeliverResult, e error) { return } - -type NopInitState struct{} - -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/results.go b/results.go new file mode 100644 index 000000000..d4ee9f0e8 --- /dev/null +++ b/results.go @@ -0,0 +1,70 @@ +package sdk + +import ( + abci "github.com/tendermint/abci/types" + "github.com/tendermint/go-wire/data" +) + +//---------- results and some wrappers -------- + +// Result is a common interface of CheckResult and GetResult +type Result interface { + GetData() data.Bytes + GetLog() string +} + +func ToABCI(r Result) abci.Result { + return abci.Result{ + Data: r.GetData(), + Log: r.GetLog(), + } +} + +// CheckResult captures any non-error abci result +// to make sure people use error for error cases +type CheckResult struct { + Data data.Bytes + Log string + // GasAllocated is the maximum units of work we allow this tx to perform + GasAllocated uint64 + // GasPayment is the total fees for this tx (or other source of payment) + GasPayment uint64 +} + +// NewCheck sets the gas used and the response data but no more info +// these are the most common info needed to be set by the Handler +func NewCheck(gasAllocated uint64, log string) CheckResult { + return CheckResult{ + GasAllocated: gasAllocated, + Log: log, + } +} + +var _ Result = CheckResult{} + +func (r CheckResult) GetData() data.Bytes { + return r.Data +} + +func (r CheckResult) GetLog() string { + return r.Log +} + +// DeliverResult captures any non-error abci result +// to make sure people use error for error cases +type DeliverResult struct { + Data data.Bytes + Log string + Diff []*abci.Validator + GasUsed uint64 +} + +var _ Result = DeliverResult{} + +func (r DeliverResult) GetData() data.Bytes { + return r.Data +} + +func (r DeliverResult) GetLog() string { + return r.Log +}