From 673b51f3b078672608210b8ce83ac940b3663e53 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 30 Jun 2017 20:26:17 +0200 Subject: [PATCH] More thorough tests on coin module --- modules/coin/handler.go | 12 +++--- modules/coin/handler_test.go | 75 ++++++++++++++++++++++++++++++++++++ modules/coin/tx.go | 10 +++++ modules/coin/tx_test.go | 30 +++++++++++++++ stack/mock.go | 53 +++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 modules/coin/handler_test.go create mode 100644 stack/mock.go diff --git a/modules/coin/handler.go b/modules/coin/handler.go index de66c434a..794728308 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -43,21 +43,21 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoi return basecoin.Result{}, nil } -func checkTx(ctx basecoin.Context, tx basecoin.Tx) (*SendTx, error) { +func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { // check if the tx is proper type and valid - send, ok := tx.Unwrap().(*SendTx) + send, ok := tx.Unwrap().(SendTx) if !ok { - return nil, errors.UnknownTxType(tx) + return send, errors.UnknownTxType(tx) } - err := send.ValidateBasic() + err = send.ValidateBasic() if err != nil { - return nil, err + return send, err } // check if all inputs have permission for _, in := range send.Inputs { if !ctx.HasPermission(in.Address) { - return nil, errors.Unauthorized() + return send, errors.Unauthorized() } } return send, nil diff --git a/modules/coin/handler_test.go b/modules/coin/handler_test.go new file mode 100644 index 000000000..f9a4c490b --- /dev/null +++ b/modules/coin/handler_test.go @@ -0,0 +1,75 @@ +package coin + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/types" +) + +func TestHandlerPermissions(t *testing.T) { + assert := assert.New(t) + // TODO: need to update this when we actually have token store + h := Handler{} + + // these are all valid, except for minusCoins + addr1 := basecoin.Actor{App: "coin", Address: []byte{1, 2}} + addr2 := basecoin.Actor{App: "role", Address: []byte{7, 8}} + someCoins := types.Coins{{"atom", 123}} + minusCoins := types.Coins{{"eth", -34}} + + cases := []struct { + valid bool + tx SendTx + perms []basecoin.Actor + }{ + // auth works with different apps + {true, + SendTx{ + Inputs: []TxInput{NewTxInput(addr1, someCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr2, someCoins)}}, + []basecoin.Actor{addr1}}, + {true, + SendTx{ + Inputs: []TxInput{NewTxInput(addr2, someCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr1, someCoins)}}, + []basecoin.Actor{addr1, addr2}}, + // wrong permissions fail + {false, + SendTx{ + Inputs: []TxInput{NewTxInput(addr1, someCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr2, someCoins)}}, + []basecoin.Actor{}}, + {false, + SendTx{ + Inputs: []TxInput{NewTxInput(addr1, someCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr2, someCoins)}}, + []basecoin.Actor{addr2}}, + // invalid input fails + {false, + SendTx{ + Inputs: []TxInput{NewTxInput(addr1, minusCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr2, minusCoins)}}, + []basecoin.Actor{addr2}}, + } + + for i, tc := range cases { + ctx := stack.MockContext().WithPermissions(tc.perms...) + _, err := h.CheckTx(ctx, nil, tc.tx.Wrap()) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + } else { + assert.NotNil(err, "%d", i) + } + + _, err = h.DeliverTx(ctx, nil, tc.tx.Wrap()) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + } else { + assert.NotNil(err, "%d", i) + } + + } +} diff --git a/modules/coin/tx.go b/modules/coin/tx.go index 6e7116ec0..711362dfc 100644 --- a/modules/coin/tx.go +++ b/modules/coin/tx.go @@ -9,6 +9,16 @@ import ( "github.com/tendermint/basecoin/types" ) +func init() { + basecoin.TxMapper.RegisterImplementation(SendTx{}, TypeSend, ByteSend) +} + +// we reserve the 0x20-0x3f range for standard modules +const ( + ByteSend = 0x20 + TypeSend = "send" +) + //----------------------------------------------------------------------------- type TxInput struct { diff --git a/modules/coin/tx_test.go b/modules/coin/tx_test.go index 0e1b85b90..df43f4bc7 100644 --- a/modules/coin/tx_test.go +++ b/modules/coin/tx_test.go @@ -4,8 +4,10 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" + "github.com/tendermint/go-wire/data" ) // these are some constructs for the test cases @@ -169,5 +171,33 @@ func TestTxValidateTx(t *testing.T) { assert.NotNil(err, "%d", i) } } +} + +func TestTxSerializeTx(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + addr1 := basecoin.Actor{App: "coin", Address: []byte{1, 2}} + addr2 := basecoin.Actor{App: "coin", Address: []byte{3, 4}} + someCoins := types.Coins{{"atom", 123}} + + send := SendTx{ + Inputs: []TxInput{NewTxInput(addr1, someCoins, 2)}, + Outputs: []TxOutput{NewTxOutput(addr2, someCoins)}, + }.Wrap() + + js, err := data.ToJSON(send) + require.Nil(err) + var tx basecoin.Tx + err = data.FromJSON(js, &tx) + require.Nil(err) + assert.Equal(send, tx) + + bin, err := data.ToWire(send) + require.Nil(err) + var tx2 basecoin.Tx + err = data.FromWire(bin, &tx2) + require.Nil(err) + assert.Equal(send, tx2) } diff --git a/stack/mock.go b/stack/mock.go new file mode 100644 index 000000000..dbf8c2df7 --- /dev/null +++ b/stack/mock.go @@ -0,0 +1,53 @@ +package stack + +import ( + "bytes" + + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" +) + +type mockContext struct { + perms []basecoin.Actor + log.Logger +} + +func MockContext() basecoin.Context { + return mockContext{ + Logger: log.NewNopLogger(), + } +} + +var _ basecoin.Context = mockContext{} + +// WithPermissions will panic if they try to set permission without the proper app +func (c mockContext) WithPermissions(perms ...basecoin.Actor) basecoin.Context { + return mockContext{ + perms: append(c.perms, perms...), + Logger: c.Logger, + } +} + +func (c mockContext) HasPermission(perm basecoin.Actor) bool { + for _, p := range c.perms { + if perm.App == p.App && bytes.Equal(perm.Address, p.Address) { + return true + } + } + return false +} + +// IsParent ensures that this is derived from the given secureClient +func (c mockContext) IsParent(other basecoin.Context) bool { + _, ok := other.(mockContext) + return ok +} + +// Reset should clear out all permissions, +// but carry on knowledge that this is a child +func (c mockContext) Reset() basecoin.Context { + return mockContext{ + Logger: c.Logger, + } +}