Util handlers compile and pass tests
This commit is contained in:
parent
a90741f2df
commit
2a3c072cc5
15
handler.go
15
handler.go
|
@ -77,3 +77,18 @@ type InitStater interface {
|
||||||
InitState(logger log.Logger, store state.SimpleDB,
|
InitState(logger log.Logger, store state.SimpleDB,
|
||||||
module, key, value string) (string, error)
|
module, key, value string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// Helper methods
|
||||||
|
|
||||||
|
// Msg allows us to get the actual tx from a structure with lots of
|
||||||
|
// decorator information. This is usually what should be passed to Handlers.
|
||||||
|
type Msg interface {
|
||||||
|
GetTx() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustGetTx forces the msg to the interface and extracts the tx
|
||||||
|
func MustGetTx(msg interface{}) interface{} {
|
||||||
|
m := msg.(Msg)
|
||||||
|
return m.GetTx()
|
||||||
|
}
|
||||||
|
|
|
@ -33,7 +33,9 @@ var _ sdk.Decorator = Chain{}
|
||||||
|
|
||||||
// CheckTx makes sure we are on the proper chain
|
// CheckTx makes sure we are on the proper chain
|
||||||
// - fulfills Decorator interface
|
// - fulfills Decorator interface
|
||||||
func (c Chain) CheckTx(ctx sdk.Context, store state.SimpleDB, tx interface{}, next sdk.Checker) (res sdk.CheckResult, err error) {
|
func (c Chain) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}, next sdk.Checker) (res sdk.CheckResult, err error) {
|
||||||
|
|
||||||
err = c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
err = c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -43,7 +45,9 @@ func (c Chain) CheckTx(ctx sdk.Context, store state.SimpleDB, tx interface{}, ne
|
||||||
|
|
||||||
// DeliverTx makes sure we are on the proper chain
|
// DeliverTx makes sure we are on the proper chain
|
||||||
// - fulfills Decorator interface
|
// - fulfills Decorator interface
|
||||||
func (c Chain) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx interface{}, next sdk.Deliverer) (res sdk.DeliverResult, err error) {
|
func (c Chain) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}, next sdk.Deliverer) (res sdk.DeliverResult, err error) {
|
||||||
|
|
||||||
err = c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
err = c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
|
@ -6,42 +6,28 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
"github.com/cosmos/cosmos-sdk/stack"
|
|
||||||
"github.com/cosmos/cosmos-sdk/state"
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestChainValidate(t *testing.T) {
|
func NewChainTx(name string, height uint64, data []byte) ChainedTx {
|
||||||
assert := assert.New(t)
|
return chainTx{
|
||||||
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
|
ChainData: ChainData{name, height},
|
||||||
|
Data: data,
|
||||||
cases := []struct {
|
|
||||||
name string
|
|
||||||
expires uint64
|
|
||||||
valid bool
|
|
||||||
}{
|
|
||||||
{"hello", 0, true},
|
|
||||||
{"one-2-three", 123, true},
|
|
||||||
{"super!@#$%@", 0, false},
|
|
||||||
{"WISH_2_be", 14, true},
|
|
||||||
{"öhhh", 54, false},
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
type chainTx struct {
|
||||||
tx := NewChainTx(tc.name, tc.expires, raw)
|
ChainData
|
||||||
err := tx.ValidateBasic()
|
Data []byte
|
||||||
if tc.valid {
|
}
|
||||||
assert.Nil(err, "%s: %+v", tc.name, err)
|
|
||||||
} else {
|
|
||||||
assert.NotNil(err, tc.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
empty := NewChainTx("okay", 0, sdk.Tx{})
|
func (c chainTx) GetChain() ChainData {
|
||||||
err := empty.ValidateBasic()
|
return c.ChainData
|
||||||
assert.NotNil(err)
|
}
|
||||||
|
|
||||||
|
func (c chainTx) GetTx() interface{} {
|
||||||
|
return RawTx{c.Data}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChain(t *testing.T) {
|
func TestChain(t *testing.T) {
|
||||||
|
@ -50,9 +36,9 @@ func TestChain(t *testing.T) {
|
||||||
chainID := "my-chain"
|
chainID := "my-chain"
|
||||||
height := uint64(100)
|
height := uint64(100)
|
||||||
|
|
||||||
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
|
raw := []byte{1, 2, 3, 4}
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
tx sdk.Tx
|
tx interface{}
|
||||||
valid bool
|
valid bool
|
||||||
errorMsg string
|
errorMsg string
|
||||||
}{
|
}{
|
||||||
|
@ -71,12 +57,12 @@ func TestChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generic args here...
|
// generic args here...
|
||||||
ctx := stack.NewContext(chainID, height, log.NewNopLogger())
|
ctx := MockContext(chainID, height)
|
||||||
store := state.NewMemKVStore()
|
store := state.NewMemKVStore()
|
||||||
|
|
||||||
// build the stack
|
// build the stack
|
||||||
ok := stack.OKHandler{Log: msg}
|
ok := OKHandler{Log: msg}
|
||||||
app := stack.New(Chain{}).Use(ok)
|
app := sdk.ChainDecorators(Chain{}).WithHandler(ok)
|
||||||
|
|
||||||
for idx, tc := range cases {
|
for idx, tc := range cases {
|
||||||
i := strconv.Itoa(idx)
|
i := strconv.Itoa(idx)
|
|
@ -0,0 +1,127 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/errors"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
const (
|
||||||
|
// ByteRawTx = 0xF0
|
||||||
|
// ByteCheckTx = 0xF1
|
||||||
|
// ByteFailTx = 0xF2
|
||||||
|
|
||||||
|
// TypeRawTx = NameOK + "/raw" // this will just say a-ok to RawTx
|
||||||
|
// TypeCheckTx = NameCheck + "/tx"
|
||||||
|
// TypeFailTx = NameFail + "/tx"
|
||||||
|
|
||||||
|
rawMaxSize = 2000 * 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// sdk.TxMapper.
|
||||||
|
// RegisterImplementation(RawTx{}, TypeRawTx, ByteRawTx).
|
||||||
|
// RegisterImplementation(CheckTx{}, TypeCheckTx, ByteCheckTx).
|
||||||
|
// RegisterImplementation(FailTx{}, TypeFailTx, ByteFailTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawTx just contains bytes that can be hex-ified
|
||||||
|
type RawTx struct {
|
||||||
|
Data data.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateBasic can ensure a limited size of tx
|
||||||
|
func (r RawTx) ValidateBasic() error {
|
||||||
|
if len(r.Data) > rawMaxSize {
|
||||||
|
return errors.ErrTooLarge()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OKHandler just used to return okay to everything
|
||||||
|
type OKHandler struct {
|
||||||
|
Log string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdk.Handler = OKHandler{}
|
||||||
|
|
||||||
|
// CheckTx always returns an empty success tx
|
||||||
|
func (ok OKHandler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.CheckResult, err error) {
|
||||||
|
return sdk.CheckResult{Log: ok.Log}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx always returns an empty success tx
|
||||||
|
func (ok OKHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.DeliverResult, err error) {
|
||||||
|
return sdk.DeliverResult{Log: ok.Log}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EchoHandler returns success, echoing res.Data = tx bytes
|
||||||
|
type EchoHandler struct{}
|
||||||
|
|
||||||
|
var _ sdk.Handler = EchoHandler{}
|
||||||
|
|
||||||
|
// CheckTx returns input if RawTx comes in, otherwise panic
|
||||||
|
func (EchoHandler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
msg interface{}) (res sdk.CheckResult, err error) {
|
||||||
|
raw := sdk.MustGetTx(msg).(RawTx)
|
||||||
|
return sdk.CheckResult{Data: raw.Data}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx returns input if RawTx comes in, otherwise panic
|
||||||
|
func (EchoHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
msg interface{}) (res sdk.DeliverResult, err error) {
|
||||||
|
raw := sdk.MustGetTx(msg).(RawTx)
|
||||||
|
return sdk.DeliverResult{Data: raw.Data}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailHandler always returns an error
|
||||||
|
type FailHandler struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdk.Handler = FailHandler{}
|
||||||
|
|
||||||
|
// CheckTx always returns the given error
|
||||||
|
func (f FailHandler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.CheckResult, err error) {
|
||||||
|
return res, errors.Wrap(f.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx always returns the given error
|
||||||
|
func (f FailHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.DeliverResult, err error) {
|
||||||
|
return res, errors.Wrap(f.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicHandler always panics, using the given error (first choice) or msg (fallback)
|
||||||
|
type PanicHandler struct {
|
||||||
|
Msg string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdk.Handler = PanicHandler{}
|
||||||
|
|
||||||
|
// CheckTx always panics
|
||||||
|
func (p PanicHandler) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.CheckResult, err error) {
|
||||||
|
|
||||||
|
if p.Err != nil {
|
||||||
|
panic(p.Err)
|
||||||
|
}
|
||||||
|
panic(p.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx always panics
|
||||||
|
func (p PanicHandler) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}) (res sdk.DeliverResult, err error) {
|
||||||
|
|
||||||
|
if p.Err != nil {
|
||||||
|
panic(p.Err)
|
||||||
|
}
|
||||||
|
panic(p.Msg)
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOK(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ctx := MockContext("test-chain", 20)
|
||||||
|
store := state.NewMemKVStore()
|
||||||
|
data := "this looks okay"
|
||||||
|
tx := sdk.Tx{}
|
||||||
|
|
||||||
|
ok := OKHandler{Log: data}
|
||||||
|
res, err := ok.CheckTx(ctx, store, tx)
|
||||||
|
assert.Nil(err, "%+v", err)
|
||||||
|
assert.Equal(data, res.Log)
|
||||||
|
|
||||||
|
dres, err := ok.DeliverTx(ctx, store, tx)
|
||||||
|
assert.Nil(err, "%+v", err)
|
||||||
|
assert.Equal(data, dres.Log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFail(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ctx := MockContext("test-chain", 20)
|
||||||
|
store := state.NewMemKVStore()
|
||||||
|
msg := "big problem"
|
||||||
|
tx := sdk.Tx{}
|
||||||
|
|
||||||
|
fail := FailHandler{Err: errors.New(msg)}
|
||||||
|
_, err := fail.CheckTx(ctx, store, tx)
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal(msg, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fail.DeliverTx(ctx, store, tx)
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal(msg, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPanic(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ctx := MockContext("test-chain", 20)
|
||||||
|
store := state.NewMemKVStore()
|
||||||
|
msg := "system crash!"
|
||||||
|
tx := sdk.Tx{}
|
||||||
|
|
||||||
|
fail := PanicHandler{Msg: msg}
|
||||||
|
assert.Panics(func() { fail.CheckTx(ctx, store, tx) })
|
||||||
|
assert.Panics(func() { fail.DeliverTx(ctx, store, tx) })
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// store nonce as it's own type so no one can even try to fake it
|
||||||
|
type nonce int64
|
||||||
|
|
||||||
|
type naiveContext struct {
|
||||||
|
id nonce
|
||||||
|
chain string
|
||||||
|
height uint64
|
||||||
|
perms []sdk.Actor
|
||||||
|
log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockContext returns a simple, non-checking context for test cases.
|
||||||
|
//
|
||||||
|
// Always use NewContext() for production code to sandbox malicious code better
|
||||||
|
func MockContext(chain string, height uint64) sdk.Context {
|
||||||
|
return naiveContext{
|
||||||
|
id: nonce(rand.Int63()),
|
||||||
|
chain: chain,
|
||||||
|
height: height,
|
||||||
|
Logger: log.NewNopLogger(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdk.Context = naiveContext{}
|
||||||
|
|
||||||
|
func (c naiveContext) ChainID() string {
|
||||||
|
return c.chain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c naiveContext) BlockHeight() uint64 {
|
||||||
|
return c.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPermissions will panic if they try to set permission without the proper app
|
||||||
|
func (c naiveContext) WithPermissions(perms ...sdk.Actor) sdk.Context {
|
||||||
|
return naiveContext{
|
||||||
|
id: c.id,
|
||||||
|
chain: c.chain,
|
||||||
|
height: c.height,
|
||||||
|
perms: append(c.perms, perms...),
|
||||||
|
Logger: c.Logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c naiveContext) HasPermission(perm sdk.Actor) bool {
|
||||||
|
for _, p := range c.perms {
|
||||||
|
if p.Equals(perm) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c naiveContext) GetPermissions(chain, app string) (res []sdk.Actor) {
|
||||||
|
for _, p := range c.perms {
|
||||||
|
if chain == p.ChainID {
|
||||||
|
if app == "" || app == p.App {
|
||||||
|
res = append(res, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsParent ensures that this is derived from the given secureClient
|
||||||
|
func (c naiveContext) IsParent(other sdk.Context) bool {
|
||||||
|
nc, ok := other.(naiveContext)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return c.id == nc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset should clear out all permissions,
|
||||||
|
// but carry on knowledge that this is a child
|
||||||
|
func (c naiveContext) Reset() sdk.Context {
|
||||||
|
return naiveContext{
|
||||||
|
id: c.id,
|
||||||
|
chain: c.chain,
|
||||||
|
height: c.height,
|
||||||
|
Logger: c.Logger,
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
"github.com/cosmos/cosmos-sdk/state"
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
)
|
)
|
||||||
|
@ -17,9 +15,9 @@ func TestRecovery(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
// generic args here...
|
// generic args here...
|
||||||
ctx := NewContext("test-chain", 20, log.NewNopLogger())
|
ctx := MockContext("test-chain", 20)
|
||||||
store := state.NewMemKVStore()
|
store := state.NewMemKVStore()
|
||||||
tx := sdk.Tx{}
|
tx := 0 // we ignore it, so it can be anything
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
msg string // what to send to panic
|
msg string // what to send to panic
|
||||||
|
@ -35,7 +33,7 @@ func TestRecovery(t *testing.T) {
|
||||||
i := strconv.Itoa(idx)
|
i := strconv.Itoa(idx)
|
||||||
fail := PanicHandler{Msg: tc.msg, Err: tc.err}
|
fail := PanicHandler{Msg: tc.msg, Err: tc.err}
|
||||||
rec := Recovery{}
|
rec := Recovery{}
|
||||||
app := New(rec).Use(fail)
|
app := sdk.ChainDecorators(rec).WithHandler(fail)
|
||||||
|
|
||||||
// make sure check returns error, not a panic crash
|
// make sure check returns error, not a panic crash
|
||||||
_, err := app.CheckTx(ctx, store, tx)
|
_, err := app.CheckTx(ctx, store, tx)
|
Loading…
Reference in New Issue