Merge pull request #429 from cosmos/feature/remove_attec

Removes the _attic folder from x/
This commit is contained in:
Ethan Buchman 2018-02-13 07:49:12 -05:00 committed by GitHub
commit 4412c49546
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 5 additions and 1629 deletions

View File

@ -1,7 +1,7 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/' | grep -v '_attic')
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=`git rev-parse --short HEAD`"
all: check_tools get_vendor_deps build test
all: check_tools get_vendor_deps build test
########################################
### CI

View File

@ -1,7 +1,7 @@
PACKAGES=$(shell go list ./... | grep -v '/vendor/' | grep -v '_attic')
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`"
all: check_tools get_vendor_deps build test
all: check_tools get_vendor_deps build test
########################################
### Build
@ -38,4 +38,4 @@ benchmark:
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: build check_tools get_tools get_vendor_deps test benchmark
.PHONY: build check_tools get_tools get_vendor_deps test benchmark

View File

@ -1,6 +0,0 @@
package main
import (
_ "github.com/tendermint/go-wire/gen"
_ "github.com/clipperhouse/stringer"
)

View File

@ -1,44 +0,0 @@
package commands
import (
"encoding/hex"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmn "github.com/tendermint/tmlibs/common"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/modules/eyes"
"github.com/cosmos/cosmos-sdk/util"
)
// EyesQueryCmd - command to query raw data
var EyesQueryCmd = &cobra.Command{
Use: "eyes [key]",
Short: "Get data stored under key in eyes",
RunE: commands.RequireInit(eyesQueryCmd),
}
func eyesQueryCmd(cmd *cobra.Command, args []string) error {
var res eyes.Data
arg, err := commands.GetOneArg(args, "key")
if err != nil {
return err
}
key, err := hex.DecodeString(cmn.StripHex(arg))
if err != nil {
return err
}
key = util.PrefixedKey(eyes.Name, key)
prove := !viper.GetBool(commands.FlagTrustNode)
height, err := query.GetParsed(key, &res, query.GetHeight(), prove)
if err != nil {
return err
}
return query.OutputProof(res, height)
}

View File

@ -1,63 +0,0 @@
package commands
import (
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/txs"
"github.com/cosmos/cosmos-sdk/modules/eyes"
)
// SetTxCmd is CLI command to set data
var SetTxCmd = &cobra.Command{
Use: "set",
Short: "Sets a key value pair",
RunE: commands.RequireInit(setTxCmd),
}
// RemoveTxCmd is CLI command to remove data
var RemoveTxCmd = &cobra.Command{
Use: "remove",
Short: "Removes a key value pair",
RunE: commands.RequireInit(removeTxCmd),
}
const (
// FlagKey is the cli flag to set the key
FlagKey = "key"
// FlagValue is the cli flag to set the value
FlagValue = "value"
)
func init() {
SetTxCmd.Flags().String(FlagKey, "", "Key to store data under (hex)")
SetTxCmd.Flags().String(FlagValue, "", "Data to store (hex)")
RemoveTxCmd.Flags().String(FlagKey, "", "Key under which to remove data (hex)")
}
// setTxCmd creates a SetTx, wraps, signs, and delivers it
func setTxCmd(cmd *cobra.Command, args []string) error {
key, err := commands.ParseHexFlag(FlagKey)
if err != nil {
return err
}
value, err := commands.ParseHexFlag(FlagValue)
if err != nil {
return err
}
tx := eyes.NewSetTx(key, value)
return txs.DoTx(tx)
}
// removeTxCmd creates a RemoveTx, wraps, signs, and delivers it
func removeTxCmd(cmd *cobra.Command, args []string) error {
key, err := commands.ParseHexFlag(FlagKey)
if err != nil {
return err
}
tx := eyes.NewRemoveTx(key)
return txs.DoTx(tx)
}

View File

@ -1,23 +0,0 @@
package eyes
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/errors"
)
var (
errMissingData = fmt.Errorf("All tx fields must be filled")
malformed = abci.CodeType_EncodingError
)
//nolint
func ErrMissingData() errors.TMError {
return errors.WithCode(errMissingData, malformed)
}
func IsMissingDataErr(err error) bool {
return errors.IsSameError(errMissingData, err)
}

View File

@ -1,62 +0,0 @@
// Generated by: main
// TypeWriter: wrapper
// Directive: +gen on EyesTxInner
package eyes
import (
"github.com/tendermint/go-wire/data"
)
// Auto-generated adapters for happily unmarshaling interfaces
// Apache License 2.0
// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com)
type EyesTx struct {
EyesTxInner "json:\"unwrap\""
}
var EyesTxMapper = data.NewMapper(EyesTx{})
func (h EyesTx) MarshalJSON() ([]byte, error) {
return EyesTxMapper.ToJSON(h.EyesTxInner)
}
func (h *EyesTx) UnmarshalJSON(data []byte) (err error) {
parsed, err := EyesTxMapper.FromJSON(data)
if err == nil && parsed != nil {
h.EyesTxInner = parsed.(EyesTxInner)
}
return err
}
// Unwrap recovers the concrete interface safely (regardless of levels of embeds)
func (h EyesTx) Unwrap() EyesTxInner {
hi := h.EyesTxInner
for wrap, ok := hi.(EyesTx); ok; wrap, ok = hi.(EyesTx) {
hi = wrap.EyesTxInner
}
return hi
}
func (h EyesTx) Empty() bool {
return h.EyesTxInner == nil
}
/*** below are bindings for each implementation ***/
func init() {
EyesTxMapper.RegisterImplementation(SetTx{}, "set", 0x1)
}
func (hi SetTx) Wrap() EyesTx {
return EyesTx{hi}
}
func init() {
EyesTxMapper.RegisterImplementation(RemoveTx{}, "remove", 0x2)
}
func (hi RemoveTx) Wrap() EyesTx {
return EyesTx{hi}
}

View File

@ -1,104 +0,0 @@
package eyes
import (
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
)
const (
// Name is used to register this module
Name = "eyes"
// CostSet is the gas needed for the set operation
CostSet uint64 = 10
// CostRemove is the gas needed for the remove operation
CostRemove = 10
)
// Handler allows us to set and remove data
type Handler struct{}
var _ sdk.Handler = Handler{}
// NewHandler makes a role handler to modify data
func NewHandler() Handler {
return Handler{}
}
// InitState - sets the genesis state - implements InitStater
func (h Handler) InitState(l log.Logger, store sdk.SimpleDB,
module, key, value string) (log string, err error) {
if module != Name {
return "", errors.ErrUnknownModule(module)
}
store.Set([]byte(key), []byte(value))
return key, nil
}
// CheckTx verifies if the transaction is properly formated
func (h Handler) CheckTx(ctx sdk.Context, store sdk.SimpleDB,
msg interface{}) (res sdk.CheckResult, err error) {
tx := sdk.MustGetTx(msg).(EyesTx)
if err := tx.ValidateBasic(); err != nil {
return res, err
}
switch tx.Unwrap().(type) {
case SetTx:
res = sdk.NewCheck(CostSet, "")
case RemoveTx:
res = sdk.NewCheck(CostRemove, "")
default:
err = errors.ErrUnknownTxType(tx)
}
return
}
// DeliverTx tries to create a new role.
//
// Returns an error if the role already exists
func (h Handler) DeliverTx(ctx sdk.Context, store sdk.SimpleDB,
msg interface{}) (res sdk.DeliverResult, err error) {
tx := sdk.MustGetTx(msg).(EyesTx)
if err := tx.ValidateBasic(); err != nil {
return res, err
}
switch t := tx.Unwrap().(type) {
case SetTx:
res, err = h.doSetTx(ctx, store, t)
case RemoveTx:
res, err = h.doRemoveTx(ctx, store, t)
default:
err = errors.ErrUnknownTxType(tx)
}
return
}
// doSetTx writes to the store, overwriting any previous value
// note that an empty response in DeliverTx is OK with no log or data returned
func (h Handler) doSetTx(ctx sdk.Context, store sdk.SimpleDB,
tx SetTx) (res sdk.DeliverResult, err error) {
data := NewData(tx.Value, ctx.BlockHeight())
store.Set(tx.Key, wire.BinaryBytes(data))
return
}
// doRemoveTx deletes the value from the store and returns the last value
// here we let res.Data to return the value over abci
func (h Handler) doRemoveTx(ctx sdk.Context, store sdk.SimpleDB,
tx RemoveTx) (res sdk.DeliverResult, err error) {
// we set res.Data so it gets returned to the client over the abci interface
res.Data = store.Get(tx.Key)
if len(res.Data) != 0 {
store.Remove(tx.Key)
}
return
}

View File

@ -1,64 +0,0 @@
package eyes
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wire "github.com/tendermint/go-wire"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/state"
"github.com/cosmos/cosmos-sdk/util"
)
func TestHandler(t *testing.T) {
assert, require := assert.New(t), require.New(t)
key := []byte("one")
val := []byte("foo")
var height uint64 = 123
h := NewHandler()
ctx := util.MockContext("role-chain", height)
store := state.NewMemKVStore()
set := sdk.WrapTx(NewSetTx(key, val))
remove := sdk.WrapTx(NewRemoveTx(key))
invalid := sdk.WrapTx(NewSetTx(nil, nil))
// make sure pricing makes sense
cres, err := h.CheckTx(ctx, store, set)
require.Nil(err, "%+v", err)
require.True(cres.GasAllocated > 5, "%#v", cres)
// set the value, no error
dres, err := h.DeliverTx(ctx, store, set)
require.Nil(err, "%+v", err)
// get the data
var data Data
bs := store.Get(key)
require.NotEmpty(bs)
err = wire.ReadBinaryBytes(bs, &data)
require.Nil(err, "%+v", err)
assert.Equal(height, data.SetAt)
assert.EqualValues(val, data.Value)
// make sure pricing makes sense
cres, err = h.CheckTx(ctx, store, remove)
require.Nil(err, "%+v", err)
require.True(cres.GasAllocated > 5, "%#v", cres)
// remove the data returns the same as the above query
dres, err = h.DeliverTx(ctx, store, remove)
require.Nil(err, "%+v", err)
require.EqualValues(bs, dres.Data)
// make sure invalid fails both ways
_, err = h.CheckTx(ctx, store, invalid)
require.NotNil(err)
_, err = h.DeliverTx(ctx, store, invalid)
require.NotNil(err)
}

View File

@ -1,20 +0,0 @@
package eyes
import "github.com/tendermint/go-wire/data"
// Data is the struct we use to store in the merkle tree
type Data struct {
// SetAt is the block height this was set at
SetAt uint64 `json:"set_at"`
// Value is the data that was stored.
// data.Bytes is like []byte but json encodes as hex not base64
Value data.Bytes `json:"value"`
}
// NewData creates a new Data item
func NewData(value []byte, setAt uint64) Data {
return Data{
SetAt: setAt,
Value: value,
}
}

View File

@ -1,53 +0,0 @@
package eyes
import (
"github.com/tendermint/go-wire/data"
)
// DO NOT USE THIS INTERFACE.
// You probably want to use EyesTx
// +gen wrapper:"EyesTx,Impl[SetTx,RemoveTx],set,remove"
type EyesTxInner interface {
ValidateBasic() error
}
// func init() {
// sdk.TxMapper.
// RegisterImplementation(SetTx{}, TypeSet, ByteSet).
// RegisterImplementation(RemoveTx{}, TypeRemove, ByteRemove)
// }
// SetTx sets a key-value pair
type SetTx struct {
Key data.Bytes `json:"key"`
Value data.Bytes `json:"value"`
}
func NewSetTx(key, value []byte) EyesTx {
return SetTx{Key: key, Value: value}.Wrap()
}
// ValidateBasic makes sure it is valid
func (t SetTx) ValidateBasic() error {
if len(t.Key) == 0 || len(t.Value) == 0 {
return ErrMissingData()
}
return nil
}
// RemoveTx deletes the value at this key, returns old value
type RemoveTx struct {
Key data.Bytes `json:"key"`
}
func NewRemoveTx(key []byte) EyesTx {
return RemoveTx{Key: key}.Wrap()
}
// ValidateBasic makes sure it is valid
func (t RemoveTx) ValidateBasic() error {
if len(t.Key) == 0 {
return ErrMissingData()
}
return nil
}

View File

@ -1,59 +0,0 @@
package commands
import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/fee"
)
//nolint
const (
FlagFee = "fee"
FlagPayer = "payer"
)
// FeeWrapper wraps a tx with an optional fee payment
type FeeWrapper struct{}
var _ txcmd.Wrapper = FeeWrapper{}
// Wrap checks for FlagFee and if present wraps the tx with a
// FeeTx of the given amount, paid by the signer
func (FeeWrapper) Wrap(tx sdk.Tx) (res sdk.Tx, err error) {
//parse the fee and amounts into coin types
toll, err := coin.ParseCoin(viper.GetString(FlagFee))
if err != nil {
return res, err
}
// if no fee, do nothing, otherwise wrap it
if toll.IsZero() {
return tx, nil
}
payer, err := readPayer()
if err != nil {
return res, err
}
res = fee.NewFee(tx, toll, payer)
return
}
// Register adds the sequence flags to the cli
func (FeeWrapper) Register(fs *pflag.FlagSet) {
fs.String(FlagFee, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
fs.String(FlagPayer, "", "Account to pay fee if not current signer (for multisig)")
}
func readPayer() (sdk.Actor, error) {
payer := viper.GetString(FlagPayer)
if payer == "" {
return txcmd.GetSignerAct(), nil
}
return commands.ParseActor(payer)
}

View File

@ -1,19 +0,0 @@
package fee
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestErrors(t *testing.T) {
assert := assert.New(t)
e := ErrInsufficientFees()
assert.True(IsInsufficientFeesErr(e))
assert.False(IsWrongFeeDenomErr(e))
e2 := ErrWrongFeeDenom("atom")
assert.False(IsInsufficientFeesErr(e2))
assert.True(IsWrongFeeDenomErr(e2))
}

View File

@ -1,39 +0,0 @@
//nolint
package fee
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/errors"
)
var (
errInsufficientFees = fmt.Errorf("Insufficient fees")
errWrongFeeDenom = fmt.Errorf("Required fee denomination")
errSkipFees = fmt.Errorf("Skip fees")
invalidInput = abci.CodeType_BaseInvalidInput
)
func ErrInsufficientFees() errors.TMError {
return errors.WithCode(errInsufficientFees, invalidInput)
}
func IsInsufficientFeesErr(err error) bool {
return errors.IsSameError(errInsufficientFees, err)
}
func ErrWrongFeeDenom(denom string) errors.TMError {
return errors.WithMessage(denom, errWrongFeeDenom, invalidInput)
}
func IsWrongFeeDenomErr(err error) bool {
return errors.IsSameError(errWrongFeeDenom, err)
}
func ErrSkipFees() errors.TMError {
return errors.WithCode(errSkipFees, invalidInput)
}
func IsSkipFeesErr(err error) bool {
return errors.IsSameError(errSkipFees, err)
}

View File

@ -1,117 +0,0 @@
package fee
import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
// NameFee - namespace for the fee module
const NameFee = "fee"
// Bank is a default location for the fees, but pass anything into
// the middleware constructor
var Bank = sdk.Actor{App: NameFee, Address: []byte("bank")}
// SimpleFeeMiddleware - middleware for fee checking, constant amount
// It used modules.coin to move the money
type SimpleFeeMiddleware struct {
// the fee must be the same denomination and >= this amount
// if the amount is 0, then the fee tx wrapper is optional
MinFee coin.Coin
// all fees go here, which could be a dump (Bank) or something reachable
// by other app logic
Collector sdk.Actor
stack.PassInitState
stack.PassInitValidate
}
var _ stack.Middleware = SimpleFeeMiddleware{}
// NewSimpleFeeMiddleware returns a fee handler with a fixed minimum fee.
//
// If minFee is 0, then the FeeTx is optional
func NewSimpleFeeMiddleware(minFee coin.Coin, collector sdk.Actor) SimpleFeeMiddleware {
return SimpleFeeMiddleware{
MinFee: minFee,
Collector: collector,
}
}
// Name - return the namespace for the fee module
func (SimpleFeeMiddleware) Name() string {
return NameFee
}
// CheckTx - check the transaction
func (h SimpleFeeMiddleware) CheckTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Checker) (res sdk.CheckResult, err error) {
fee, err := h.verifyFee(ctx, tx)
if err != nil {
if IsSkipFeesErr(err) {
return next.CheckTx(ctx, store, tx)
}
return res, err
}
var paid, used uint64
if !fee.Fee.IsZero() { // now, try to make a IPC call to coins...
send := coin.NewSendOneTx(fee.Payer, h.Collector, coin.Coins{fee.Fee})
sendRes, err := next.CheckTx(ctx, store, send)
if err != nil {
return res, err
}
paid = uint64(fee.Fee.Amount)
used = sendRes.GasAllocated
}
res, err = next.CheckTx(ctx, store, fee.Tx)
// add the given fee to the price for gas, plus one query
if err == nil {
res.GasPayment += paid
res.GasAllocated += used
}
return res, err
}
// DeliverTx - send the fee handler transaction
func (h SimpleFeeMiddleware) DeliverTx(ctx sdk.Context, store state.SimpleDB, tx sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, err error) {
fee, err := h.verifyFee(ctx, tx)
if IsSkipFeesErr(err) {
return next.DeliverTx(ctx, store, tx)
}
if err != nil {
return res, err
}
if !fee.Fee.IsZero() { // now, try to make a IPC call to coins...
send := coin.NewSendOneTx(fee.Payer, h.Collector, coin.Coins{fee.Fee})
_, err = next.DeliverTx(ctx, store, send)
if err != nil {
return res, err
}
}
return next.DeliverTx(ctx, store, fee.Tx)
}
func (h SimpleFeeMiddleware) verifyFee(ctx sdk.Context, tx sdk.Tx) (Fee, error) {
feeTx, ok := tx.Unwrap().(Fee)
if !ok {
// the fee wrapper is not required if there is no minimum
if h.MinFee.IsZero() {
return feeTx, ErrSkipFees()
}
return feeTx, errors.ErrInvalidFormat(TypeFees, tx)
}
// see if it is the proper denom and big enough
fee := feeTx.Fee
if fee.Denom != h.MinFee.Denom {
return feeTx, ErrWrongFeeDenom(h.MinFee.Denom)
}
if !fee.IsGTE(h.MinFee) {
return feeTx, ErrInsufficientFees()
}
return feeTx, nil
}

View File

@ -1,141 +0,0 @@
package fee_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tmlibs/log"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/fee"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
func TestFeeChecks(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
atom := func(i int64) coin.Coin { return coin.Coin{"atom", i} }
eth := func(i int64) coin.Coin { return coin.Coin{"eth", i} }
atoms := func(i int64) coin.Coins { return coin.Coins{{"atom", i}} }
wallet := func(i, j int64) coin.Coins { return coin.Coins{atom(i), eth(j)} }
// some coin amounts...
zero := coin.Coin{}
mixed := wallet(1200, 55)
pure := atoms(46657)
// these are some accounts
collector := sdk.Actor{App: fee.NameFee, Address: []byte("mine")}
key1 := coin.NewAccountWithKey(mixed)
key2 := coin.NewAccountWithKey(pure)
act1, act2 := key1.Actor(), key2.Actor()
// set up the apps....
disp := stack.NewDispatcher(
// OKHandler will just return success to a RawTx
stack.WrapHandler(stack.OKHandler{}),
// coin is needed to handle the IPC call from Fee middleware
coin.NewHandler(),
)
// app1 requires no fees
app1 := stack.New(fee.NewSimpleFeeMiddleware(atom(0), collector)).Use(disp)
// app2 requires 2 atom
app2 := stack.New(fee.NewSimpleFeeMiddleware(atom(2), collector)).Use(disp)
// set up the store and init the accounts
store := state.NewMemKVStore()
l := log.NewNopLogger()
_, err := app1.InitState(l, store, "coin", "account", key1.MakeOption())
require.Nil(err, "%+v", err)
_, err = app2.InitState(l, store, "coin", "account", key2.MakeOption())
require.Nil(err, "%+v", err)
// feeCost is what we expect if the fee is properly paid
feeCost := coin.CostSend * 2
cases := []struct {
valid bool
// this is the middleware stack to test
app sdk.Handler
// they sign the tx
signer sdk.Actor
// wrap with the given fee if hasFee is true
hasFee bool
payer sdk.Actor
fee coin.Coin
// expected balance after the tx
left coin.Coins
collected coin.Coins
// expected gas allocated
expectedCost uint64
}{
// make sure it works with no fee (control group)
{true, app1, act1, false, act1, zero, mixed, nil, 0},
{true, app1, act2, false, act2, zero, pure, nil, 0},
// no fee or too low is rejected
{false, app2, act1, false, act1, zero, mixed, nil, 0},
{false, app2, act2, false, act2, zero, pure, nil, 0},
{false, app2, act1, true, act1, zero, mixed, nil, 0},
{false, app2, act2, true, act2, atom(1), pure, nil, 0},
// proper fees are transfered in both cases
{true, app1, act1, true, act1, atom(1), wallet(1199, 55), atoms(1), feeCost},
{true, app2, act2, true, act2, atom(57), atoms(46600), atoms(58), feeCost},
// // fee must be the proper type...
{false, app1, act1, true, act1, eth(5), wallet(1199, 55), atoms(58), 0},
// signature must match
{false, app2, act1, true, act2, atom(5), atoms(46600), atoms(58), 0},
// send only works within limits
{true, app2, act1, true, act1, atom(1100), wallet(99, 55), atoms(1158), feeCost},
{false, app2, act1, true, act1, atom(500), wallet(99, 55), atoms(1158), 0},
}
for i, tc := range cases {
// build the tx
tx := stack.NewRawTx([]byte{7, 8, 9})
if tc.hasFee {
tx = fee.NewFee(tx, tc.fee, tc.payer)
}
// set up the permissions
ctx := stack.MockContext("x", 1).WithPermissions(tc.signer)
// call checktx...
cres, err := tc.app.CheckTx(ctx, store, tx)
if tc.valid {
assert.Nil(err, "%d: %+v", i, err)
assert.EqualValues(tc.fee.Amount, cres.GasPayment)
assert.EqualValues(tc.expectedCost, cres.GasAllocated)
} else {
assert.NotNil(err, "%d", i)
}
// call delivertx...
_, err = tc.app.DeliverTx(ctx, store, tx)
if tc.valid {
assert.Nil(err, "%d: %+v", i, err)
} else {
assert.NotNil(err, "%d", i)
}
// check the account balance afterwards....
cspace := stack.PrefixedStore(coin.NameCoin, store)
acct, err := coin.GetAccount(cspace, tc.payer)
require.Nil(err, "%d: %+v", i, err)
assert.Equal(tc.left, acct.Coins, "%d", i)
// check the collected balance afterwards....
acct, err = coin.GetAccount(cspace, collector)
require.Nil(err, "%d: %+v", i, err)
assert.Equal(tc.collected, acct.Coins, "%d", i)
}
}

View File

@ -1,44 +0,0 @@
package fee
import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/modules/coin"
)
// nolint
const (
ByteFees = 0x28
TypeFees = NameFee + "/tx"
)
func init() {
sdk.TxMapper.
RegisterImplementation(Fee{}, TypeFees, ByteFees)
}
/**** Fee ****/
// Fee attaches a fee payment to the embedded tx
type Fee struct {
// Gas coin.Coin `json:"gas"` // ?????
Fee coin.Coin `json:"fee"`
Payer sdk.Actor `json:"payer"` // the address who pays the fee
Tx sdk.Tx `json:"tx"`
}
// NewFee wraps a tx with a promised fee from this actor
func NewFee(tx sdk.Tx, fee coin.Coin, payer sdk.Actor) sdk.Tx {
return Fee{Tx: tx, Fee: fee, Payer: payer}.Wrap()
}
// nolint - TxInner Functions
func (f Fee) ValidateBasic() error {
// TODO: more checks
return f.Tx.ValidateBasic()
}
func (f Fee) Wrap() sdk.Tx {
return sdk.Tx{f}
}
func (f Fee) Next() sdk.Tx {
return f.Tx
}

View File

@ -1,71 +0,0 @@
package util
import (
"time"
abci "github.com/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk"
)
// 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 sdk.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 sdk.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 sdk.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)
}

View File

@ -1,53 +0,0 @@
package commands
import (
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/stack"
)
// NonceQueryCmd - command to query an nonce account
var NonceQueryCmd = &cobra.Command{
Use: "nonce [address]",
Short: "Get details of a nonce sequence number, with proof",
RunE: commands.RequireInit(nonceQueryCmd),
}
func nonceQueryCmd(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("Missing required argument [address]")
}
addr := strings.Join(args, ",")
signers, err := commands.ParseActors(addr)
if err != nil {
return err
}
seq, height, err := doNonceQuery(signers)
if err != nil {
return err
}
return query.OutputProof(seq, height)
}
func doNonceQuery(signers []sdk.Actor) (sequence uint32, height uint64, err error) {
key := stack.PrefixedKey(nonce.NameNonce, nonce.GetSeqKey(signers))
prove := !viper.GetBool(commands.FlagTrustNode)
height, err = query.GetParsed(key, &sequence, query.GetHeight(), prove)
if client.IsNoDataErr(err) {
// no data, return sequence 0
return 0, 0, nil
}
return
}

View File

@ -1,83 +0,0 @@
package commands
import (
"fmt"
"github.com/spf13/pflag"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client/commands"
txcmd "github.com/cosmos/cosmos-sdk/client/commands/txs"
"github.com/cosmos/cosmos-sdk/modules/nonce"
)
// nolint
const (
FlagSequence = "sequence"
FlagNonceKey = "nonce-key"
)
// NonceWrapper wraps a tx with a nonce
type NonceWrapper struct{}
var _ txcmd.Wrapper = NonceWrapper{}
// Wrap grabs the sequence number from the flag and wraps
// the tx with this nonce. Grabs the permission from the signer,
// as we still only support single sig on the cli
func (NonceWrapper) Wrap(tx sdk.Tx) (res sdk.Tx, err error) {
signers, err := readNonceKey()
if err != nil {
return res, err
}
seq, err := readSequence(signers)
if err != nil {
return res, err
}
res = nonce.NewTx(seq, signers, tx)
return
}
// Register adds the sequence flags to the cli
func (NonceWrapper) Register(fs *pflag.FlagSet) {
fs.Int(FlagSequence, -1, "Sequence number for this transaction")
fs.String(FlagNonceKey, "", "Set of comma-separated addresses for the nonce (for multisig)")
}
func readNonceKey() ([]sdk.Actor, error) {
nonce := viper.GetString(FlagNonceKey)
if nonce == "" {
return []sdk.Actor{txcmd.GetSignerAct()}, nil
}
return commands.ParseActors(nonce)
}
// read the sequence from the flag or query for it if flag is -1
func readSequence(signers []sdk.Actor) (seq uint32, err error) {
//add the nonce tx layer to the tx
seqFlag := viper.GetInt(FlagSequence)
switch {
case seqFlag > 0:
seq = uint32(seqFlag)
case seqFlag == -1:
//autocalculation for default sequence
seq, _, err = doNonceQuery(signers)
if err != nil {
return
}
//increase the sequence by 1!
seq++
default:
err = fmt.Errorf("sequence must be either greater than 0, or -1 for autocalculation")
}
return
}

View File

@ -1,41 +0,0 @@
//nolint
package nonce
import (
"fmt"
abci "github.com/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/errors"
)
var (
errNoNonce = fmt.Errorf("Tx doesn't contain nonce")
errNotMember = fmt.Errorf("Nonce contains non-permissioned member")
errZeroSequence = fmt.Errorf("Sequence number cannot be zero")
errNoSigners = fmt.Errorf("There are no signers")
errTxEmpty = fmt.Errorf("The provided Tx is empty")
unauthorized = abci.CodeType_Unauthorized
badNonce = abci.CodeType_BadNonce
invalidInput = abci.CodeType_BaseInvalidInput
)
func ErrBadNonce(got, expected uint32) errors.TMError {
return errors.WithCode(fmt.Errorf("Bad nonce sequence, got %d, expected %d", got, expected), badNonce)
}
func ErrNoNonce() errors.TMError {
return errors.WithCode(errNoNonce, badNonce)
}
func ErrNotMember() errors.TMError {
return errors.WithCode(errNotMember, unauthorized)
}
func ErrZeroSequence() errors.TMError {
return errors.WithCode(errZeroSequence, invalidInput)
}
func ErrNoSigners() errors.TMError {
return errors.WithCode(errNoSigners, invalidInput)
}
func ErrTxEmpty() errors.TMError {
return errors.WithCode(errTxEmpty, invalidInput)
}

View File

@ -1,77 +0,0 @@
package nonce
import (
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
//nolint
const (
NameNonce = "nonce"
CostNonce = 10
)
// ReplayCheck uses the sequence to check for replay attacks
type ReplayCheck struct {
stack.PassInitState
stack.PassInitValidate
}
// Name of the module - fulfills Middleware interface
func (ReplayCheck) Name() string {
return NameNonce
}
var _ stack.Middleware = ReplayCheck{}
// CheckTx verifies tx is not being replayed - fulfills Middlware interface
func (r ReplayCheck) CheckTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx, next sdk.Checker) (res sdk.CheckResult, err error) {
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
if err != nil {
return res, err
}
res, err = next.CheckTx(ctx, store, stx)
res.GasAllocated += CostNonce
return res, err
}
// DeliverTx verifies tx is not being replayed - fulfills Middlware interface
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
// wrapped Tx fails, the state changes are not applied
func (r ReplayCheck) DeliverTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx, next sdk.Deliver) (res sdk.DeliverResult, err error) {
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
if err != nil {
return res, err
}
return next.DeliverTx(ctx, store, stx)
}
// checkNonceTx varifies the nonce sequence, an increment sequence number
func (r ReplayCheck) checkIncrementNonceTx(ctx sdk.Context, store state.SimpleDB,
tx sdk.Tx) (sdk.Tx, error) {
// make sure it is a the nonce Tx (Tx from this package)
nonceTx, ok := tx.Unwrap().(Tx)
if !ok {
return tx, ErrNoNonce()
}
err := nonceTx.ValidateBasic()
if err != nil {
return tx, err
}
// check the nonce sequence number
err = nonceTx.CheckIncrementSeq(ctx, store)
if err != nil {
return tx, err
}
return nonceTx.Tx, nil
}

View File

@ -1,84 +0,0 @@
package rest
import (
"fmt"
"net/http"
"strconv"
"github.com/gorilla/mux"
"github.com/spf13/viper"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/commands"
"github.com/cosmos/cosmos-sdk/client/commands/query"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/modules/coin"
"github.com/cosmos/cosmos-sdk/modules/nonce"
"github.com/cosmos/cosmos-sdk/stack"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/common"
)
// doQueryNonce is the HTTP handlerfunc to query a nonce
func doQueryNonce(w http.ResponseWriter, r *http.Request) {
args := mux.Vars(r)
signature := args["signature"]
actor, err := commands.ParseActor(signature)
if err != nil {
common.WriteError(w, err)
return
}
var h int
qHeight := r.URL.Query().Get("height")
if qHeight != "" {
h, err = strconv.Atoi(qHeight)
if err != nil {
common.WriteError(w, err)
return
}
}
actor = coin.ChainAddr(actor)
key := nonce.GetSeqKey([]sdk.Actor{actor})
key = stack.PrefixedKey(nonce.NameNonce, key)
prove := !viper.GetBool(commands.FlagTrustNode)
// query sequence number
data, height, err := query.Get(key, h, prove)
if client.IsNoDataErr(err) {
err = fmt.Errorf("nonce empty for address: %q", signature)
common.WriteError(w, err)
return
} else if err != nil {
common.WriteError(w, err)
return
}
// unmarshal sequence number
var seq uint32
err = wire.ReadBinaryBytes(data, &seq)
if err != nil {
msg := fmt.Sprintf("Error reading sequence for %X", key)
common.WriteError(w, errors.ErrInternal(msg))
return
}
// write result to client
if err := query.FoutputProof(w, seq, height); err != nil {
common.WriteError(w, err)
}
}
// mux.Router registrars
// RegisterQueryNonce is a mux.Router handler that exposes GET
// method access on route /query/nonce/{signature} to query nonces
func RegisterQueryNonce(r *mux.Router) error {
r.HandleFunc("/query/nonce/{signature}", doQueryNonce).Methods("GET")
return nil
}
// End of mux.Router registrars

View File

@ -1,30 +0,0 @@
package nonce
import (
"fmt"
wire "github.com/tendermint/go-wire"
"github.com/cosmos/cosmos-sdk/errors"
"github.com/cosmos/cosmos-sdk/state"
)
func getSeq(store state.SimpleDB, key []byte) (seq uint32, err error) {
data := store.Get(key)
if len(data) == 0 {
//if the key is not stored, its a new key with a sequence of zero!
return 0, nil
}
err = wire.ReadBinaryBytes(data, &seq)
if err != nil {
msg := fmt.Sprintf("Error reading sequence for %X", key)
return seq, errors.ErrInternal(msg)
}
return seq, nil
}
func setSeq(store state.SimpleDB, key []byte, seq uint32) error {
bin := wire.BinaryBytes(seq)
store.Set(key, bin)
return nil // real stores can return error...
}

View File

@ -1,111 +0,0 @@
/*
Package nonce - This module allows replay protection to be added to process stack.
This is achieved through the use of a sequence number for each unique set of signers.
Note that the sequence number for the single signing account "foo" will be unique
from the sequence number for a multi-sig account {"foo", "bar"} which would also be
unique from a different multi-sig account {"foo", "soup"}
*/
package nonce
import (
"sort"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/state"
)
// nolint
const (
ByteNonce = 0x69 //TODO overhaul byte assign system don't make no sense!
TypeNonce = "nonce"
)
func init() {
sdk.TxMapper.RegisterImplementation(Tx{}, TypeNonce, ByteNonce)
}
// Tx - Nonce transaction structure, contains list of signers and current sequence number
type Tx struct {
Sequence uint32 `json:"sequence"`
Signers []sdk.Actor `json:"signers"`
Tx sdk.Tx `json:"tx"`
}
var _ sdk.TxInner = &Tx{}
// NewTx wraps the tx with a signable nonce
func NewTx(sequence uint32, signers []sdk.Actor, tx sdk.Tx) sdk.Tx {
return (Tx{
Sequence: sequence,
Signers: signers,
Tx: tx,
}).Wrap()
}
//nolint
func (n Tx) Wrap() sdk.Tx {
return sdk.Tx{n}
}
func (n Tx) ValidateBasic() error {
switch {
case n.Tx.Empty():
return ErrTxEmpty()
case n.Sequence == 0:
return ErrZeroSequence()
case len(n.Signers) == 0:
return ErrNoSigners()
}
return n.Tx.ValidateBasic()
}
// CheckIncrementSeq - Check that the sequence number is one more than the state sequence number
// and further increment the sequence number
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
// wrapped Tx fails, the state changes are not applied
func (n Tx) CheckIncrementSeq(ctx sdk.Context, store state.SimpleDB) error {
seqKey := n.getSeqKey()
// check the current state
cur, err := getSeq(store, seqKey)
if err != nil {
return err
}
if n.Sequence != cur+1 {
return ErrBadNonce(n.Sequence, cur+1)
}
// make sure they all signed
for _, s := range n.Signers {
if !ctx.HasPermission(s) {
return ErrNotMember()
}
}
// increment the sequence by 1
err = setSeq(store, seqKey, cur+1)
if err != nil {
return err
}
return nil
}
func (n Tx) getSeqKey() (seqKey []byte) {
return GetSeqKey(n.Signers)
}
// GetSeqKey - Generate the sequence key as the concatenated list of signers, sorted by address.
func GetSeqKey(signers []sdk.Actor) (seqKey []byte) {
// First copy the list of signers to sort as sort is done in place
signers2sort := make([]sdk.Actor, len(signers))
copy(signers2sort, signers)
sort.Sort(sdk.ByAll(signers))
for _, signer := range signers {
seqKey = append(seqKey, signer.Bytes()...)
}
return
}

View File

@ -1,119 +0,0 @@
package nonce
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/stack"
"github.com/cosmos/cosmos-sdk/state"
)
func TestNonce(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
// generic args here...
chainID := "my-chain"
chain2ID := "woohoo"
height := uint64(100)
ctx := stack.MockContext(chainID, height)
store := state.NewMemKVStore()
appName1 := "fooz"
appName2 := "foot"
//root actors for the tests
act1 := sdk.Actor{ChainID: chainID, App: appName1, Address: []byte{1, 2, 3, 4}}
act2 := sdk.Actor{ChainID: chainID, App: appName1, Address: []byte{1, 1, 1, 1}}
act3 := sdk.Actor{ChainID: chainID, App: appName1, Address: []byte{3, 3, 3, 3}}
act1DiffChain := sdk.Actor{ChainID: chain2ID, App: appName1, Address: []byte{1, 2, 3, 4}}
act2DiffChain := sdk.Actor{ChainID: chain2ID, App: appName1, Address: []byte{1, 1, 1, 1}}
act3DiffChain := sdk.Actor{ChainID: chain2ID, App: appName1, Address: []byte{3, 3, 3, 3}}
act1DiffApp := sdk.Actor{ChainID: chainID, App: appName2, Address: []byte{1, 2, 3, 4}}
act2DiffApp := sdk.Actor{ChainID: chainID, App: appName2, Address: []byte{1, 1, 1, 1}}
act3DiffApp := sdk.Actor{ChainID: chainID, App: appName2, Address: []byte{3, 3, 3, 3}}
// let's construct some tests to make the table a bit less verbose
set0 := []sdk.Actor{}
set1 := []sdk.Actor{act1}
set2 := []sdk.Actor{act2}
set12 := []sdk.Actor{act1, act2}
set21 := []sdk.Actor{act2, act1}
set123 := []sdk.Actor{act1, act2, act3}
set321 := []sdk.Actor{act3, act2, act1}
//some more test cases for different chains and apps for each actor
set123Chain2 := []sdk.Actor{act1DiffChain, act2DiffChain, act3DiffChain}
set123App2 := []sdk.Actor{act1DiffApp, act2DiffApp, act3DiffApp}
set123MixedChains := []sdk.Actor{act1, act2DiffChain, act3}
set123MixedApps := []sdk.Actor{act1, act2DiffApp, act3}
testList := []struct {
valid bool
seq uint32
actors []sdk.Actor
signers []sdk.Actor
}{
// one signer
{false, 0, set1, set1}, // seq 0 is no good
{false, 1, set1, set0}, // sig is required
{true, 1, set1, set1}, // sig and seq are good
{true, 2, set1, set1}, // increments each time
{false, 777, set1, set1}, // seq is too high
// independent from second signer
{false, 1, set2, set1}, // sig must match
{true, 1, set2, set2}, // seq of set2 independent from set1
{true, 2, set2, set321}, // extra sigs don't change the situation
// multisig has same requirements
{false, 0, set12, set12}, // need valid sequence number
{false, 1, set12, set2}, // they all must sign
{true, 1, set12, set12}, // this is proper, independent of act1 and act2
{true, 2, set21, set21}, // order of actors doesn't matter
{false, 2, set12, set12}, // but can't repeat sequence
{true, 3, set12, set321}, // no effect from extra sigs
// triple sigs also work
{false, 2, set123, set123}, // must start with seq=1
{false, 1, set123, set12}, // all must sign
{true, 1, set123, set321}, // this works
{true, 2, set321, set321}, // other order is the same
{false, 2, set321, set321}, // no repetition
// signers with different chain-IDs and apps from actors
{false, 3, set123, set123Chain2}, // sign with different chain actors
{false, 3, set123, set123App2}, // sign with different app actors
{false, 3, set123, set123MixedChains}, // sign with mixed chain actor
{false, 3, set123, set123MixedApps}, // sign with mixed app actors
// signers from different chain-IDs and apps, working
{true, 1, set123Chain2, set123Chain2},
{true, 1, set123App2, set123App2},
{true, 1, set123MixedChains, set123MixedChains},
{true, 1, set123MixedApps, set123MixedApps},
}
raw := stack.NewRawTx([]byte{42})
for i, test := range testList {
// set the permissions
myCtx := ctx.WithPermissions(test.signers...)
tx := NewTx(test.seq, test.actors, raw)
nonceTx, ok := tx.Unwrap().(Tx)
require.True(ok)
err := nonceTx.CheckIncrementSeq(myCtx, store)
if test.valid {
assert.Nil(err, "%d: %+v", i, err)
} else {
assert.NotNil(err, "%d", i)
}
}
}

View File

@ -1,46 +0,0 @@
package util
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk"
"github.com/cosmos/cosmos-sdk/errors"
)
// 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 sdk.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 sdk.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

@ -1,51 +0,0 @@
package util
import (
"errors"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
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 := MockContext("test-chain", 20)
store := state.NewMemKVStore()
tx := 0 // we ignore it, so it can be anything
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 := sdk.ChainDecorators(rec).WithHandler(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)
}
}
}