Moved the handlers from stack into modules
This commit is contained in:
@@ -1,51 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/errors"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/txs"
|
||||
)
|
||||
|
||||
const (
|
||||
NameChain = "chan"
|
||||
)
|
||||
|
||||
// Chain enforces that this tx was bound to the named chain
|
||||
type Chain struct {
|
||||
PassOption
|
||||
}
|
||||
|
||||
func (_ Chain) Name() string {
|
||||
return NameRecovery
|
||||
}
|
||||
|
||||
var _ Middleware = Chain{}
|
||||
|
||||
func (c Chain) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||
stx, err := c.checkChain(ctx.ChainID(), tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
return next.CheckTx(ctx, store, stx)
|
||||
}
|
||||
|
||||
func (c Chain) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
stx, err := c.checkChain(ctx.ChainID(), 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(chainID string, tx basecoin.Tx) (basecoin.Tx, error) {
|
||||
ctx, ok := tx.Unwrap().(*txs.Chain)
|
||||
if !ok {
|
||||
return tx, errors.ErrNoChain()
|
||||
}
|
||||
if ctx.ChainID != chainID {
|
||||
return tx, errors.ErrWrongChain(ctx.ChainID)
|
||||
}
|
||||
return ctx.Tx, nil
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/txs"
|
||||
)
|
||||
|
||||
func TestChain(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
msg := "got it"
|
||||
chainID := "my-chain"
|
||||
|
||||
raw := txs.NewRaw([]byte{1, 2, 3, 4})
|
||||
cases := []struct {
|
||||
tx basecoin.Tx
|
||||
valid bool
|
||||
errorMsg string
|
||||
}{
|
||||
{txs.NewChain(chainID, raw), true, ""},
|
||||
{txs.NewChain("someone-else", raw), false, "someone-else"},
|
||||
{raw, false, "No chain id provided"},
|
||||
}
|
||||
|
||||
// generic args here...
|
||||
ctx := NewContext(chainID, log.NewNopLogger())
|
||||
store := state.NewMemKVStore()
|
||||
|
||||
// build the stack
|
||||
ok := OKHandler{Log: msg}
|
||||
app := New(Chain{}).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.Contains(err.Error(), tc.errorMsg, 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.Contains(err.Error(), tc.errorMsg, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
)
|
||||
|
||||
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 state.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 state.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
|
||||
}
|
||||
|
||||
func (_ Logger) SetOption(l log.Logger, store state.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||
start := time.Now()
|
||||
res, err := next.SetOption(l, store, module, key, value)
|
||||
delta := time.Now().Sub(start)
|
||||
// TODO: log the value being set also?
|
||||
l = l.With("duration", micros(delta)).With("mod", module).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)
|
||||
}
|
||||
@@ -60,20 +60,6 @@ func New(middlewares ...Middleware) *Stack {
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefault sets up the common middlewares before your custom stack.
|
||||
//
|
||||
// This is logger, recovery, signature, and chain
|
||||
func NewDefault(middlewares ...Middleware) *Stack {
|
||||
mids := []Middleware{
|
||||
Logger{},
|
||||
Recovery{},
|
||||
Signatures{},
|
||||
Chain{},
|
||||
}
|
||||
mids = append(mids, middlewares...)
|
||||
return New(mids...)
|
||||
}
|
||||
|
||||
// Use sets the final handler for the stack and prepares it for use
|
||||
func (s *Stack) Use(handler basecoin.Handler) *Stack {
|
||||
if handler == nil {
|
||||
|
||||
@@ -15,6 +15,10 @@ import (
|
||||
"github.com/tendermint/basecoin/txs"
|
||||
)
|
||||
|
||||
const (
|
||||
nameSigner = "signer"
|
||||
)
|
||||
|
||||
func TestPermissionSandbox(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
@@ -28,7 +32,7 @@ func TestPermissionSandbox(t *testing.T) {
|
||||
// test cases to make sure permissioning is solid
|
||||
grantee := basecoin.Actor{App: NameGrant, Address: []byte{1}}
|
||||
grantee2 := basecoin.Actor{App: NameGrant, Address: []byte{2}}
|
||||
signer := basecoin.Actor{App: NameSigs, Address: []byte{1}}
|
||||
signer := basecoin.Actor{App: nameSigner, Address: []byte{1}}
|
||||
cases := []struct {
|
||||
grant basecoin.Actor
|
||||
require basecoin.Actor
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/txs"
|
||||
)
|
||||
|
||||
const (
|
||||
NameMultiplexer = "mplx"
|
||||
)
|
||||
|
||||
type Multiplexer struct {
|
||||
PassOption
|
||||
}
|
||||
|
||||
func (_ Multiplexer) Name() string {
|
||||
return NameMultiplexer
|
||||
}
|
||||
|
||||
var _ Middleware = Multiplexer{}
|
||||
|
||||
func (_ Multiplexer) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||
if mtx, ok := tx.Unwrap().(*txs.MultiTx); ok {
|
||||
return runAll(ctx, store, mtx.Txs, next.CheckTx)
|
||||
}
|
||||
return next.CheckTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (_ Multiplexer) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
if mtx, ok := tx.Unwrap().(*txs.MultiTx); ok {
|
||||
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
|
||||
}
|
||||
return next.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func runAll(ctx basecoin.Context, store state.KVStore, txs []basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
||||
// store all results, unless anything errors
|
||||
rs := make([]basecoin.Result, len(txs))
|
||||
for i, stx := range txs {
|
||||
rs[i], err = next(ctx, store, stx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// now combine the results into one...
|
||||
return combine(rs), nil
|
||||
}
|
||||
|
||||
// combines all data bytes as a go-wire array.
|
||||
// joins all log messages with \n
|
||||
func combine(all []basecoin.Result) basecoin.Result {
|
||||
datas := make([]data.Bytes, len(all))
|
||||
logs := make([]string, len(all))
|
||||
for i, r := range all {
|
||||
datas[i] = r.Data
|
||||
logs[i] = r.Log
|
||||
}
|
||||
return basecoin.Result{
|
||||
Data: wire.BinaryBytes(datas),
|
||||
Log: strings.Join(logs, "\n"),
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/tendermint/basecoin/state"
|
||||
)
|
||||
|
||||
// nolint
|
||||
const (
|
||||
NameRecovery = "rcvr"
|
||||
)
|
||||
@@ -17,13 +18,15 @@ const (
|
||||
// Recovery catches any panics and returns them as errors instead
|
||||
type Recovery struct{}
|
||||
|
||||
func (_ Recovery) Name() string {
|
||||
// Name of the module - fulfills Middleware interface
|
||||
func (Recovery) Name() string {
|
||||
return NameRecovery
|
||||
}
|
||||
|
||||
var _ Middleware = Recovery{}
|
||||
|
||||
func (_ Recovery) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
||||
func (Recovery) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = normalizePanic(r)
|
||||
@@ -32,7 +35,8 @@ func (_ Recovery) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin
|
||||
return next.CheckTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (_ Recovery) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
||||
func (Recovery) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = normalizePanic(r)
|
||||
@@ -41,7 +45,8 @@ func (_ Recovery) DeliverTx(ctx basecoin.Context, store state.KVStore, tx baseco
|
||||
return next.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
func (_ Recovery) SetOption(l log.Logger, store state.KVStore, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
||||
// SetOption catches any panic and converts to error - fulfills Middlware interface
|
||||
func (Recovery) SetOption(l log.Logger, store state.KVStore, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = normalizePanic(r)
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestRecovery(t *testing.T) {
|
||||
expected string // expected text in panic
|
||||
}{
|
||||
{"buzz", nil, "buzz"},
|
||||
{"", errors.New("owa!"), "owa!"},
|
||||
{"", errors.New("some text"), "some text"},
|
||||
{"text", errors.New("error"), "error"},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/errors"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
)
|
||||
|
||||
// app name for auth
|
||||
const (
|
||||
NameSigs = "sigs"
|
||||
)
|
||||
|
||||
type Signatures struct {
|
||||
PassOption
|
||||
}
|
||||
|
||||
func (_ Signatures) Name() string {
|
||||
return NameSigs
|
||||
}
|
||||
|
||||
var _ Middleware = Signatures{}
|
||||
|
||||
func SigPerm(addr []byte) basecoin.Actor {
|
||||
return basecoin.NewActor(NameSigs, addr)
|
||||
}
|
||||
|
||||
// Signed allows us to use txs.OneSig and txs.MultiSig (and others??)
|
||||
type Signed interface {
|
||||
basecoin.TxLayer
|
||||
Signers() ([]crypto.PubKey, error)
|
||||
}
|
||||
|
||||
func (h Signatures) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||
sigs, tnext, err := getSigners(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ctx2 := addSigners(ctx, sigs)
|
||||
return next.CheckTx(ctx2, store, tnext)
|
||||
}
|
||||
|
||||
func (h Signatures) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||
sigs, tnext, err := getSigners(tx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ctx2 := addSigners(ctx, sigs)
|
||||
return next.DeliverTx(ctx2, store, tnext)
|
||||
}
|
||||
|
||||
func addSigners(ctx basecoin.Context, sigs []crypto.PubKey) basecoin.Context {
|
||||
perms := make([]basecoin.Actor, len(sigs))
|
||||
for i, s := range sigs {
|
||||
perms[i] = SigPerm(s.Address())
|
||||
}
|
||||
// add the signers to the context and continue
|
||||
return ctx.WithPermissions(perms...)
|
||||
}
|
||||
|
||||
func getSigners(tx basecoin.Tx) ([]crypto.PubKey, basecoin.Tx, error) {
|
||||
stx, ok := tx.Unwrap().(Signed)
|
||||
if !ok {
|
||||
return nil, basecoin.Tx{}, errors.ErrUnauthorized()
|
||||
}
|
||||
sig, err := stx.Signers()
|
||||
return sig, stx.Next(), err
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/txs"
|
||||
)
|
||||
|
||||
func TestSignatureChecks(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// generic args
|
||||
ctx := NewContext("test-chain", log.NewNopLogger())
|
||||
store := state.NewMemKVStore()
|
||||
raw := txs.NewRaw([]byte{1, 2, 3, 4})
|
||||
|
||||
// let's make some keys....
|
||||
priv1 := crypto.GenPrivKeyEd25519().Wrap()
|
||||
actor1 := SigPerm(priv1.PubKey().Address())
|
||||
priv2 := crypto.GenPrivKeySecp256k1().Wrap()
|
||||
actor2 := SigPerm(priv2.PubKey().Address())
|
||||
|
||||
// test cases to make sure signature checks are solid
|
||||
cases := []struct {
|
||||
useMultiSig bool
|
||||
keys []crypto.PrivKey
|
||||
check basecoin.Actor
|
||||
valid bool
|
||||
}{
|
||||
// test with single sigs
|
||||
{false, []crypto.PrivKey{priv1}, actor1, true},
|
||||
{false, []crypto.PrivKey{priv1}, actor2, false},
|
||||
{false, []crypto.PrivKey{priv2}, actor2, true},
|
||||
{false, []crypto.PrivKey{}, actor2, false},
|
||||
|
||||
// same with multi sigs
|
||||
{true, []crypto.PrivKey{priv1}, actor1, true},
|
||||
{true, []crypto.PrivKey{priv1}, actor2, false},
|
||||
{true, []crypto.PrivKey{priv2}, actor2, true},
|
||||
{true, []crypto.PrivKey{}, actor2, false},
|
||||
|
||||
// make sure both match on a multisig
|
||||
{true, []crypto.PrivKey{priv1, priv2}, actor1, true},
|
||||
{true, []crypto.PrivKey{priv1, priv2}, actor2, true},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
idx := strconv.Itoa(i)
|
||||
|
||||
// make the stack check for the given permission
|
||||
app := New(
|
||||
Recovery{}, // we need this so panics turn to errors
|
||||
Signatures{},
|
||||
CheckMiddleware{Required: tc.check},
|
||||
).Use(OKHandler{})
|
||||
|
||||
var tx basecoin.Tx
|
||||
// this does the signing as needed
|
||||
if tc.useMultiSig {
|
||||
mtx := txs.NewMulti(raw)
|
||||
for _, k := range tc.keys {
|
||||
err := txs.Sign(mtx, k)
|
||||
assert.Nil(err, "%d: %+v", i, err)
|
||||
}
|
||||
tx = mtx.Wrap()
|
||||
} else {
|
||||
otx := txs.NewSig(raw)
|
||||
for _, k := range tc.keys {
|
||||
err := txs.Sign(otx, k)
|
||||
assert.Nil(err, "%d: %+v", i, err)
|
||||
}
|
||||
tx = otx.Wrap()
|
||||
}
|
||||
|
||||
// this will trivial expose the printing error...
|
||||
// _, err := app.CheckTx(ctx, store, raw)
|
||||
_, err := app.CheckTx(ctx, store, tx)
|
||||
if tc.valid {
|
||||
// TODO: why doen't tmerror print properly???
|
||||
assert.Nil(err, "%d: %+v", i, err)
|
||||
} else {
|
||||
assert.NotNil(err, idx)
|
||||
}
|
||||
|
||||
_, err = app.DeliverTx(ctx, store, tx)
|
||||
if tc.valid {
|
||||
assert.Nil(err, "%d: %+v", i, err)
|
||||
} else {
|
||||
assert.NotNil(err, idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user