Cleanup base module -> util
This commit is contained in:
parent
362fbe6fe9
commit
a90741f2df
|
@ -1,68 +0,0 @@
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
|
||||||
"github.com/cosmos/cosmos-sdk/stack"
|
|
||||||
"github.com/cosmos/cosmos-sdk/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint
|
|
||||||
const (
|
|
||||||
NameChain = "chain"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Chain enforces that this tx was bound to the named chain
|
|
||||||
type Chain struct {
|
|
||||||
stack.PassInitState
|
|
||||||
stack.PassInitValidate
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
|
||||||
func (Chain) Name() string {
|
|
||||||
return NameChain
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ stack.Middleware = Chain{}
|
|
||||||
|
|
||||||
// CheckTx makes sure we are on the proper chain - fulfills Middlware interface
|
|
||||||
func (c Chain) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Checker) (res sdk.CheckResult, err error) {
|
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
return next.CheckTx(ctx, store, stx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverTx makes sure we are on the proper chain - fulfills Middlware interface
|
|
||||||
func (c Chain) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, err error) {
|
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
return next.DeliverTx(ctx, store, stx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkChainTx makes sure the tx is a Chain Tx, it is on the proper chain,
|
|
||||||
// and it has not expired.
|
|
||||||
func (c Chain) checkChainTx(chainID string, height uint64, tx sdk.Tx) (sdk.Tx, error) {
|
|
||||||
// make sure it is a chaintx
|
|
||||||
ctx, ok := tx.Unwrap().(ChainTx)
|
|
||||||
if !ok {
|
|
||||||
return tx, ErrNoChain()
|
|
||||||
}
|
|
||||||
|
|
||||||
// basic validation
|
|
||||||
err := ctx.ValidateBasic()
|
|
||||||
if err != nil {
|
|
||||||
return tx, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// compare against state
|
|
||||||
if ctx.ChainID != chainID {
|
|
||||||
return tx, ErrWrongChain(ctx.ChainID)
|
|
||||||
}
|
|
||||||
if ctx.ExpiresAt != 0 && ctx.ExpiresAt <= height {
|
|
||||||
return tx, ErrExpired()
|
|
||||||
}
|
|
||||||
return ctx.Tx, nil
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
|
||||||
"github.com/cosmos/cosmos-sdk/stack"
|
|
||||||
"github.com/cosmos/cosmos-sdk/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
const (
|
|
||||||
NameLogger = "lggr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger catches any panics and returns them as errors instead
|
|
||||||
type Logger struct{}
|
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
|
||||||
func (Logger) Name() string {
|
|
||||||
return NameLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ stack.Middleware = Logger{}
|
|
||||||
|
|
||||||
// CheckTx logs time and result - fulfills Middlware interface
|
|
||||||
func (Logger) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Checker) (res sdk.CheckResult, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverTx logs time and result - fulfills Middlware interface
|
|
||||||
func (Logger) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitState logs time and result - fulfills Middlware interface
|
|
||||||
func (Logger) InitState(l log.Logger, store state.SimpleDB, module, key, value string, next sdk.InitStater) (string, error) {
|
|
||||||
start := time.Now()
|
|
||||||
res, err := next.InitState(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("InitState", "log", res)
|
|
||||||
} else {
|
|
||||||
l.Error("InitState", "err", err)
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitValidate logs time and result - fulfills Middlware interface
|
|
||||||
func (Logger) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator, next sdk.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)
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
|
||||||
"github.com/cosmos/cosmos-sdk/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// nolint
|
|
||||||
const (
|
|
||||||
// for utils...
|
|
||||||
ByteMultiTx = 0x2
|
|
||||||
ByteChainTx = 0x3
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint
|
|
||||||
const (
|
|
||||||
TypeMultiTx = NameMultiplexer + "/tx"
|
|
||||||
TypeChainTx = NameChain + "/tx"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
sdk.TxMapper.
|
|
||||||
RegisterImplementation(MultiTx{}, TypeMultiTx, ByteMultiTx).
|
|
||||||
RegisterImplementation(ChainTx{}, TypeChainTx, ByteChainTx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**** MultiTx ******/
|
|
||||||
|
|
||||||
// MultiTx - a transaction containing multiple transactions
|
|
||||||
type MultiTx struct {
|
|
||||||
Txs []sdk.Tx `json:"txs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ sdk.TxInner = &MultiTx{}
|
|
||||||
|
|
||||||
//nolint - TxInner Functions
|
|
||||||
func NewMultiTx(txs ...sdk.Tx) sdk.Tx {
|
|
||||||
return (MultiTx{Txs: txs}).Wrap()
|
|
||||||
}
|
|
||||||
func (mt MultiTx) Wrap() sdk.Tx {
|
|
||||||
return sdk.Tx{mt}
|
|
||||||
}
|
|
||||||
func (mt MultiTx) ValidateBasic() error {
|
|
||||||
for _, t := range mt.Txs {
|
|
||||||
err := t.ValidateBasic()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** ChainTx ****/
|
|
||||||
|
|
||||||
// ChainTx locks this tx to one chainTx, wrap with this before signing
|
|
||||||
type ChainTx struct {
|
|
||||||
// name of chain, must be [A-Za-z0-9_-]+
|
|
||||||
ChainID string `json:"chain_id"`
|
|
||||||
// block height at which it is no longer valid, 0 means no expiration
|
|
||||||
ExpiresAt uint64 `json:"expires_at"`
|
|
||||||
Tx sdk.Tx `json:"tx"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ sdk.TxInner = &ChainTx{}
|
|
||||||
|
|
||||||
var (
|
|
||||||
chainPattern = regexp.MustCompile("^[A-Za-z0-9_-]+$")
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewChainTx wraps a particular tx with the ChainTx wrapper,
|
|
||||||
// to enforce chain and height
|
|
||||||
func NewChainTx(chainID string, expires uint64, tx sdk.Tx) sdk.Tx {
|
|
||||||
c := ChainTx{
|
|
||||||
ChainID: chainID,
|
|
||||||
ExpiresAt: expires,
|
|
||||||
Tx: tx,
|
|
||||||
}
|
|
||||||
return c.Wrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint - TxInner Functions
|
|
||||||
func (c ChainTx) Wrap() sdk.Tx {
|
|
||||||
return sdk.Tx{c}
|
|
||||||
}
|
|
||||||
func (c ChainTx) ValidateBasic() error {
|
|
||||||
if c.ChainID == "" {
|
|
||||||
return ErrNoChain()
|
|
||||||
}
|
|
||||||
if !chainPattern.MatchString(c.ChainID) {
|
|
||||||
return ErrWrongChain(c.ChainID)
|
|
||||||
}
|
|
||||||
if c.Tx.Empty() {
|
|
||||||
return errors.ErrUnknownTxType(c.Tx)
|
|
||||||
}
|
|
||||||
// TODO: more checks? chainID?
|
|
||||||
return c.Tx.ValidateBasic()
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-wire/data"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
|
||||||
"github.com/cosmos/cosmos-sdk/stack"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncoding(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
raw := stack.NewRawTx([]byte{0x34, 0xa7})
|
|
||||||
// raw2 := stack.NewRawTx([]byte{0x73, 0x86, 0x22})
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
Tx sdk.Tx
|
|
||||||
}{
|
|
||||||
{raw},
|
|
||||||
// {NewMultiTx(raw, raw2)},
|
|
||||||
{NewChainTx("foobar", 0, raw)},
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, tc := range cases {
|
|
||||||
i := strconv.Itoa(idx)
|
|
||||||
tx := tc.Tx
|
|
||||||
|
|
||||||
// test json in and out
|
|
||||||
js, err := data.ToJSON(tx)
|
|
||||||
require.Nil(err, i)
|
|
||||||
var jtx sdk.Tx
|
|
||||||
err = data.FromJSON(js, &jtx)
|
|
||||||
require.Nil(err, i)
|
|
||||||
assert.Equal(tx, jtx, i)
|
|
||||||
|
|
||||||
// test wire in and out
|
|
||||||
bin, err := data.ToWire(tx)
|
|
||||||
require.Nil(err, i)
|
|
||||||
var wtx sdk.Tx
|
|
||||||
err = data.FromWire(bin, &wtx)
|
|
||||||
require.Nil(err, i)
|
|
||||||
assert.Equal(tx, wtx, i)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
Package bonus is a temporary home for various functionalities
|
||||||
|
that were removed for complexity, but may be added back in
|
||||||
|
later.
|
||||||
|
**/
|
||||||
|
package bonus
|
|
@ -1,4 +1,4 @@
|
||||||
package base
|
package bonus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
|
@ -1,4 +1,4 @@
|
||||||
package base
|
package bonus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -1,12 +1,12 @@
|
||||||
package base
|
package bonus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk"
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
"github.com/cosmos/cosmos-sdk/stack"
|
"github.com/cosmos/cosmos-sdk/stack"
|
||||||
"github.com/cosmos/cosmos-sdk/state"
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
|
@ -0,0 +1,73 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ChainPattern must match any valid chain_id
|
||||||
|
ChainPattern = regexp.MustCompile("^[A-Za-z0-9_-]+$")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChainedTx interface should be implemented by any
|
||||||
|
// message to bind it to one chain, with an optional
|
||||||
|
// expiration height
|
||||||
|
type ChainedTx interface {
|
||||||
|
GetChain() ChainData
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainData is info from the message to bind it to
|
||||||
|
// chain and time
|
||||||
|
type ChainData struct {
|
||||||
|
ChainID string `json:"chain_id"`
|
||||||
|
ExpiresAt uint64 `json:"expires_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain enforces that this tx was bound to the named chain
|
||||||
|
type Chain struct{}
|
||||||
|
|
||||||
|
var _ sdk.Decorator = Chain{}
|
||||||
|
|
||||||
|
// CheckTx makes sure we are on the proper chain
|
||||||
|
// - fulfills Decorator interface
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return next.CheckTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx makes sure we are on the proper chain
|
||||||
|
// - fulfills Decorator interface
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return next.DeliverTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkChainTx makes sure the tx is a ChainedTx,
|
||||||
|
// it is on the proper chain, and it has not expired.
|
||||||
|
func (c Chain) checkChainTx(chainID string, height uint64, tx interface{}) error {
|
||||||
|
// make sure it is a chaintx
|
||||||
|
ctx, ok := tx.(ChainedTx)
|
||||||
|
if !ok {
|
||||||
|
return ErrNoChain()
|
||||||
|
}
|
||||||
|
|
||||||
|
data := ctx.GetChain()
|
||||||
|
|
||||||
|
// compare against state
|
||||||
|
if data.ChainID != chainID {
|
||||||
|
return ErrWrongChain(data.ChainID)
|
||||||
|
}
|
||||||
|
if data.ExpiresAt != 0 && data.ExpiresAt <= height {
|
||||||
|
return ErrExpired()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package base
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
|
@ -1,5 +1,5 @@
|
||||||
//nolint
|
//nolint
|
||||||
package base
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -1,4 +1,4 @@
|
||||||
package base
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -0,0 +1,72 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger writes out log messages on every request
|
||||||
|
type Logger struct{}
|
||||||
|
|
||||||
|
var _ sdk.Decorator = Logger{}
|
||||||
|
|
||||||
|
// CheckTx logs time and result - fulfills Middlware interface
|
||||||
|
func (Logger) CheckTx(ctx sdk.Context, store state.SimpleDB, tx interface{}, next sdk.Checker) (res sdk.CheckResult, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx logs time and result - fulfills Middlware interface
|
||||||
|
func (Logger) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx interface{}, next sdk.Deliverer) (res sdk.DeliverResult, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogTicker wraps any ticker and records the time it takes.
|
||||||
|
// Pass in a name to be logged with this to separate out various
|
||||||
|
// tickers
|
||||||
|
func LogTicker(clock sdk.Ticker, name string) sdk.Ticker {
|
||||||
|
res := func(ctx sdk.Context, s state.SimpleDB) ([]*abci.Validator, error) {
|
||||||
|
start := time.Now()
|
||||||
|
vals, err := clock.Tick(ctx, s)
|
||||||
|
delta := time.Now().Sub(start)
|
||||||
|
l := ctx.With("duration", micros(delta))
|
||||||
|
if name != "" {
|
||||||
|
l = l.With("name", name)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
l.Info("Tock")
|
||||||
|
} else {
|
||||||
|
l.Error("Tock", "err", err)
|
||||||
|
}
|
||||||
|
return vals, err
|
||||||
|
}
|
||||||
|
return sdk.TickerFunc(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// micros returns how many microseconds passed in a call
|
||||||
|
func micros(d time.Duration) int {
|
||||||
|
return int(d.Seconds() * 1000000)
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/errors"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Recovery catches any panics and returns them as errors instead
|
||||||
|
type Recovery struct{}
|
||||||
|
|
||||||
|
var _ sdk.Decorator = Recovery{}
|
||||||
|
|
||||||
|
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
|
func (Recovery) CheckTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}, next sdk.Checker) (res sdk.CheckResult, err error) {
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = normalizePanic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return next.CheckTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
|
func (Recovery) DeliverTx(ctx sdk.Context, store state.SimpleDB,
|
||||||
|
tx interface{}, next sdk.Deliverer) (res sdk.DeliverResult, err error) {
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = normalizePanic(r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return next.DeliverTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
msg := fmt.Sprintf("%v", p)
|
||||||
|
return errors.ErrInternal(msg)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk"
|
||||||
|
"github.com/cosmos/cosmos-sdk/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRecovery(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// generic args here...
|
||||||
|
ctx := NewContext("test-chain", 20, log.NewNopLogger())
|
||||||
|
store := state.NewMemKVStore()
|
||||||
|
tx := sdk.Tx{}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
msg string // what to send to panic
|
||||||
|
err error // what to send to panic
|
||||||
|
expected string // expected text in panic
|
||||||
|
}{
|
||||||
|
{"buzz", nil, "buzz"},
|
||||||
|
{"", errors.New("some text"), "some text"},
|
||||||
|
{"text", errors.New("error"), "error"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, tc := range cases {
|
||||||
|
i := strconv.Itoa(idx)
|
||||||
|
fail := PanicHandler{Msg: tc.msg, Err: tc.err}
|
||||||
|
rec := Recovery{}
|
||||||
|
app := New(rec).Use(fail)
|
||||||
|
|
||||||
|
// make sure check returns error, not a panic crash
|
||||||
|
_, err := app.CheckTx(ctx, store, tx)
|
||||||
|
if assert.NotNil(err, i) {
|
||||||
|
assert.Equal(tc.expected, err.Error(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure deliver returns error, not a panic crash
|
||||||
|
_, err = app.DeliverTx(ctx, store, tx)
|
||||||
|
if assert.NotNil(err, i) {
|
||||||
|
assert.Equal(tc.expected, err.Error(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue