Add logger and chain middleware, default stack
This commit is contained in:
parent
1df0c9fe5b
commit
82281aa3bb
|
@ -1,6 +1,9 @@
|
||||||
package basecoin
|
package basecoin
|
||||||
|
|
||||||
import "github.com/tendermint/go-wire/data"
|
import (
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
// Actor abstracts any address that can authorize actions, hold funds,
|
// Actor abstracts any address that can authorize actions, hold funds,
|
||||||
// or initiate any sort of transaction.
|
// or initiate any sort of transaction.
|
||||||
|
@ -21,6 +24,7 @@ func NewActor(app string, addr []byte) Actor {
|
||||||
// rely on private fields to control the actions
|
// rely on private fields to control the actions
|
||||||
type Context interface {
|
type Context interface {
|
||||||
// context.Context
|
// context.Context
|
||||||
|
log.Logger
|
||||||
WithPermissions(perms ...Actor) Context
|
WithPermissions(perms ...Actor) Context
|
||||||
HasPermission(perm Actor) bool
|
HasPermission(perm Actor) bool
|
||||||
IsParent(ctx Context) bool
|
IsParent(ctx Context) bool
|
||||||
|
|
|
@ -4,7 +4,11 @@ package errors
|
||||||
* Copyright (C) 2017 Ethan Frey
|
* Copyright (C) 2017 Ethan Frey
|
||||||
**/
|
**/
|
||||||
|
|
||||||
import abci "github.com/tendermint/abci/types"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
msgDecoding = "Error decoding input"
|
msgDecoding = "Error decoding input"
|
||||||
|
@ -20,6 +24,8 @@ const (
|
||||||
msgTooLarge = "Input size too large"
|
msgTooLarge = "Input size too large"
|
||||||
msgMissingSignature = "Signature missing"
|
msgMissingSignature = "Signature missing"
|
||||||
msgTooManySignatures = "Too many signatures"
|
msgTooManySignatures = "Too many signatures"
|
||||||
|
msgNoChain = "No chain id provided"
|
||||||
|
msgWrongChain = "Tx belongs to different chain - %s"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InternalError(msg string) TMError {
|
func InternalError(msg string) TMError {
|
||||||
|
@ -46,6 +52,15 @@ func InvalidSignature() TMError {
|
||||||
return New(msgInvalidSignature, abci.CodeType_Unauthorized)
|
return New(msgInvalidSignature, abci.CodeType_Unauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NoChain() TMError {
|
||||||
|
return New(msgNoChain, abci.CodeType_Unauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WrongChain(chain string) TMError {
|
||||||
|
msg := fmt.Sprintf(msgWrongChain, chain)
|
||||||
|
return New(msg, abci.CodeType_Unauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
func InvalidAddress() TMError {
|
func InvalidAddress() TMError {
|
||||||
return New(msgInvalidAddress, abci.CodeType_BaseInvalidInput)
|
return New(msgInvalidAddress, abci.CodeType_BaseInvalidInput)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/errors"
|
||||||
|
"github.com/tendermint/basecoin/txs"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameChain = "chan"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chain enforces that this tx was bound to the named chain
|
||||||
|
type Chain struct {
|
||||||
|
ChainID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ Chain) Name() string {
|
||||||
|
return NameRecovery
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Middleware = Chain{}
|
||||||
|
|
||||||
|
func (c Chain) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
|
stx, err := c.checkChain(tx)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return next.CheckTx(ctx, store, stx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Chain) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
|
stx, err := c.checkChain(tx)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return next.DeliverTx(ctx, store, stx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkChain makes sure the tx is a txs.Chain and
|
||||||
|
func (c Chain) checkChain(tx basecoin.Tx) (basecoin.Tx, error) {
|
||||||
|
ctx, ok := tx.Unwrap().(*txs.Chain)
|
||||||
|
if !ok {
|
||||||
|
return tx, errors.NoChain()
|
||||||
|
}
|
||||||
|
if ctx.ChainID != c.ChainID {
|
||||||
|
return tx, errors.WrongChain(ctx.ChainID)
|
||||||
|
}
|
||||||
|
return ctx.Tx, nil
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/txs"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChain(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
msg := "got it"
|
||||||
|
chainID := "my-chain"
|
||||||
|
|
||||||
|
raw := txs.NewRaw([]byte{1, 2, 3, 4}).Wrap()
|
||||||
|
cases := []struct {
|
||||||
|
tx basecoin.Tx
|
||||||
|
valid bool
|
||||||
|
errorMsg string
|
||||||
|
}{
|
||||||
|
{txs.NewChain(chainID, raw).Wrap(), true, ""},
|
||||||
|
{txs.NewChain("someone-else", raw).Wrap(), false, "Tx belongs to different chain - someone-else"},
|
||||||
|
{raw, false, "No chain id provided"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic args here...
|
||||||
|
ctx := NewContext(log.NewNopLogger())
|
||||||
|
store := types.NewMemKVStore()
|
||||||
|
|
||||||
|
// build the stack
|
||||||
|
ok := OKHandler{msg}
|
||||||
|
app := New(Chain{chainID}).Use(ok)
|
||||||
|
|
||||||
|
for idx, tc := range cases {
|
||||||
|
i := strconv.Itoa(idx)
|
||||||
|
|
||||||
|
// make sure check returns error, not a panic crash
|
||||||
|
res, err := app.CheckTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
assert.Nil(err, "%d: %+v", idx, err)
|
||||||
|
assert.Equal(msg, res.Log, i)
|
||||||
|
} else {
|
||||||
|
if assert.NotNil(err, i) {
|
||||||
|
assert.Equal(tc.errorMsg, err.Error(), i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure deliver returns error, not a panic crash
|
||||||
|
res, err = app.DeliverTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
assert.Nil(err, "%d: %+v", idx, err)
|
||||||
|
assert.Equal(msg, res.Log, i)
|
||||||
|
} else {
|
||||||
|
if assert.NotNil(err, i) {
|
||||||
|
assert.Equal(tc.errorMsg, err.Error(), i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,9 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
@ -16,11 +19,13 @@ type secureContext struct {
|
||||||
id nonce
|
id nonce
|
||||||
app string
|
app string
|
||||||
perms []basecoin.Actor
|
perms []basecoin.Actor
|
||||||
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext() basecoin.Context {
|
func NewContext(logger log.Logger) basecoin.Context {
|
||||||
return secureContext{
|
return secureContext{
|
||||||
id: nonce(rand.Int63()),
|
id: nonce(rand.Int63()),
|
||||||
|
Logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +42,10 @@ func (c secureContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
return secureContext{
|
return secureContext{
|
||||||
id: c.id,
|
id: c.id,
|
||||||
perms: append(c.perms, perms...),
|
app: c.app,
|
||||||
|
perms: append(c.perms, perms...),
|
||||||
|
Logger: c.Logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +67,14 @@ func (c secureContext) IsParent(other basecoin.Context) bool {
|
||||||
return c.id == so.id
|
return c.id == so.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset should give a fresh context,
|
// Reset should clear out all permissions,
|
||||||
// but carry on knowledge that this is a child
|
// but carry on knowledge that this is a child
|
||||||
func (c secureContext) Reset() basecoin.Context {
|
func (c secureContext) Reset() basecoin.Context {
|
||||||
return secureContext{
|
return secureContext{
|
||||||
id: c.id,
|
id: c.id,
|
||||||
app: c.app,
|
app: c.app,
|
||||||
|
perms: nil,
|
||||||
|
Logger: c.Logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +86,10 @@ func withApp(ctx basecoin.Context, app string) basecoin.Context {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
return secureContext{
|
return secureContext{
|
||||||
id: sc.id,
|
id: sc.id,
|
||||||
app: app,
|
app: app,
|
||||||
perms: sc.perms,
|
perms: sc.perms,
|
||||||
|
Logger: sc.Logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
@ -12,7 +15,7 @@ import (
|
||||||
func TestOK(t *testing.T) {
|
func TestOK(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ctx := NewContext()
|
ctx := NewContext(log.NewNopLogger())
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
data := "this looks okay"
|
data := "this looks okay"
|
||||||
tx := basecoin.Tx{}
|
tx := basecoin.Tx{}
|
||||||
|
@ -30,7 +33,7 @@ func TestOK(t *testing.T) {
|
||||||
func TestFail(t *testing.T) {
|
func TestFail(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ctx := NewContext()
|
ctx := NewContext(log.NewNopLogger())
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
msg := "big problem"
|
msg := "big problem"
|
||||||
tx := basecoin.Tx{}
|
tx := basecoin.Tx{}
|
||||||
|
@ -50,7 +53,7 @@ func TestFail(t *testing.T) {
|
||||||
func TestPanic(t *testing.T) {
|
func TestPanic(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
ctx := NewContext()
|
ctx := NewContext(log.NewNopLogger())
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
msg := "system crash!"
|
msg := "system crash!"
|
||||||
tx := basecoin.Tx{}
|
tx := basecoin.Tx{}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameLogger = "lggr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger catches any panics and returns them as errors instead
|
||||||
|
type Logger struct{}
|
||||||
|
|
||||||
|
func (_ Logger) Name() string {
|
||||||
|
return NameLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Middleware = Logger{}
|
||||||
|
|
||||||
|
func (_ Logger) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
|
start := time.Now()
|
||||||
|
res, err = next.CheckTx(ctx, store, tx)
|
||||||
|
delta := time.Now().Sub(start)
|
||||||
|
// TODO: log some info on the tx itself?
|
||||||
|
l := ctx.With("duration", micros(delta))
|
||||||
|
if err == nil {
|
||||||
|
l.Debug("CheckTx", "log", res.Log)
|
||||||
|
} else {
|
||||||
|
l.Info("CheckTx", "err", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ Logger) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
|
start := time.Now()
|
||||||
|
res, err = next.DeliverTx(ctx, store, tx)
|
||||||
|
delta := time.Now().Sub(start)
|
||||||
|
// TODO: log some info on the tx itself?
|
||||||
|
l := ctx.With("duration", micros(delta))
|
||||||
|
if err == nil {
|
||||||
|
l.Info("DeliverTx", "log", res.Log)
|
||||||
|
} else {
|
||||||
|
l.Error("DeliverTx", "err", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// micros returns how many microseconds passed in a call
|
||||||
|
func micros(d time.Duration) int {
|
||||||
|
return int(d.Seconds() * 1000000)
|
||||||
|
}
|
|
@ -54,6 +54,20 @@ func New(middlewares ...Middleware) *Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDefault sets up the common middlewares before your custom stack.
|
||||||
|
//
|
||||||
|
// This is logger, recovery, signature, and chain
|
||||||
|
func NewDefault(chainID string, middlewares ...Middleware) *Stack {
|
||||||
|
mids := []Middleware{
|
||||||
|
Logger{},
|
||||||
|
Recovery{},
|
||||||
|
SignedHandler{true},
|
||||||
|
Chain{chainID},
|
||||||
|
}
|
||||||
|
mids = append(mids, middlewares...)
|
||||||
|
return New(mids...)
|
||||||
|
}
|
||||||
|
|
||||||
// Use sets the final handler for the stack and prepares it for use
|
// Use sets the final handler for the stack and prepares it for use
|
||||||
func (s *Stack) Use(handler basecoin.Handler) *Stack {
|
func (s *Stack) Use(handler basecoin.Handler) *Stack {
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
|
|
|
@ -8,13 +8,14 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRecovery(t *testing.T) {
|
func TestRecovery(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
// generic args here...
|
// generic args here...
|
||||||
ctx := NewContext()
|
ctx := NewContext(log.NewNopLogger())
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
tx := basecoin.Tx{}
|
tx := basecoin.Tx{}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
package handlers
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/errors"
|
"github.com/tendermint/basecoin/errors"
|
||||||
"github.com/tendermint/basecoin/stack"
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ func (_ SignedHandler) Name() string {
|
||||||
return NameSigs
|
return NameSigs
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ stack.Middleware = SignedHandler{}
|
var _ Middleware = SignedHandler{}
|
||||||
|
|
||||||
func SigPerm(addr []byte) basecoin.Actor {
|
func SigPerm(addr []byte) basecoin.Actor {
|
||||||
return basecoin.NewActor(NameSigs, addr)
|
return basecoin.NewActor(NameSigs, addr)
|
|
@ -121,7 +121,7 @@ type Chain struct {
|
||||||
ChainID string `json:"chain_id"`
|
ChainID string `json:"chain_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChain(tx basecoin.Tx, chainID string) *Chain {
|
func NewChain(chainID string, tx basecoin.Tx) *Chain {
|
||||||
return &Chain{Tx: tx, ChainID: chainID}
|
return &Chain{Tx: tx, ChainID: chainID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue