Cleanup base module -> util

This commit is contained in:
Ethan Frey 2017-10-23 21:16:10 +02:00
parent 362fbe6fe9
commit a90741f2df
16 changed files with 258 additions and 311 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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)
}
}

6
modules/bonus/doc.go Normal file
View File

@ -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

View File

@ -1,4 +1,4 @@
package base
package bonus
import (
abci "github.com/tendermint/abci/types"

View File

@ -1,4 +1,4 @@
package base
package bonus
import (
"strings"

View File

@ -1,12 +1,12 @@
package base
package bonus
import (
"testing"
"github.com/stretchr/testify/assert"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
"github.com/stretchr/testify/assert"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
"github.com/tendermint/tmlibs/log"

73
modules/util/chain.go Normal file
View File

@ -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
}

View File

@ -1,4 +1,4 @@
package base
package util
import (
"strconv"

View File

@ -1,5 +1,5 @@
//nolint
package base
package util
import (
"fmt"

View File

@ -1,4 +1,4 @@
package base
package util
import (
"testing"

72
modules/util/logger.go Normal file
View File

@ -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)
}

47
modules/util/recovery.go Normal file
View File

@ -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)
}

View File

@ -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)
}
}
}