diff --git a/TODO.md b/TODO.md index eb4a4ab39d..9a215a0785 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,3 @@ -# TODO for rewrite - -* Add tests for new CheckTx -* Test Multiplexer - - Alexis: * merkle - proof (non-existence - maybe range) @@ -12,8 +6,7 @@ light-client proofs: * make this sensible -> very tied to merkle proofs and API * support new proof types -* abci add range suppprt - +* expose more proof types in basecoin.Query * merkle - api cleanup (also Bonsai) diff --git a/app/app_val_test.go b/app/app_val_test.go index cf2cdbfe0e..d1697168dd 100644 --- a/app/app_val_test.go +++ b/app/app_val_test.go @@ -6,60 +6,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" + "github.com/tendermint/basecoin/modules/base" wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" - - "github.com/tendermint/basecoin" - "github.com/tendermint/basecoin/errors" - "github.com/tendermint/basecoin/state" ) -//-------------------------------- -// Setup tx and handler for validation test cases - -const ( - ValName = "val" - TypeValChange = ValName + "/change" - ByteValChange = 0xfe -) - -func init() { - basecoin.TxMapper.RegisterImplementation(ValChangeTx{}, TypeValChange, ByteValChange) -} - -type ValSetHandler struct { - basecoin.NopCheck - basecoin.NopInitState - basecoin.NopInitValidate -} - -var _ basecoin.Handler = ValSetHandler{} - -func (ValSetHandler) Name() string { - return ValName -} - -func (ValSetHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, - tx basecoin.Tx) (res basecoin.DeliverResult, err error) { - change, ok := tx.Unwrap().(ValChangeTx) - if !ok { - return res, errors.ErrUnknownTxType(tx) - } - res.Diff = change.Diff - return -} - -type ValChangeTx struct { - Diff []*abci.Validator -} - -func (v ValChangeTx) Wrap() basecoin.Tx { - return basecoin.Tx{v} -} - -func (v ValChangeTx) ValidateBasic() error { return nil } - //----------------------------------- // Test cases start here @@ -90,7 +42,7 @@ func TestEndBlock(t *testing.T) { logger := log.NewNopLogger() store := MockStore() - handler := ValSetHandler{} + handler := base.ValSetHandler{} app := NewBasecoin(handler, store, logger) val1 := makeVal() @@ -125,7 +77,7 @@ func TestEndBlock(t *testing.T) { for i, tc := range cases { app.BeginBlock(nil, nil) for _, c := range tc.changes { - tx := ValChangeTx{c}.Wrap() + tx := base.ValChangeTx{c}.Wrap() txBytes := wire.BinaryBytes(tx) res := app.DeliverTx(txBytes) require.True(res.IsOK(), "%#v", res) diff --git a/modules/base/helpers.go b/modules/base/helpers.go new file mode 100644 index 0000000000..953e903fff --- /dev/null +++ b/modules/base/helpers.go @@ -0,0 +1,118 @@ +package base + +import ( + abci "github.com/tendermint/abci/types" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/state" +) + +//nolint +const ( + NameVal = "val" + NamePrice = "price" + + TypeValChange = NameVal + "/change" + ByteValChange = 0xfe + + TypePriceShow = NamePrice + "/show" + BytePriceShow = 0xfd +) + +func init() { + basecoin.TxMapper. + RegisterImplementation(ValChangeTx{}, TypeValChange, ByteValChange). + RegisterImplementation(PriceShowTx{}, TypePriceShow, BytePriceShow) +} + +//-------------------------------- +// Setup tx and handler for validation test cases + +type ValSetHandler struct { + basecoin.NopCheck + basecoin.NopInitState + basecoin.NopInitValidate +} + +var _ basecoin.Handler = ValSetHandler{} + +func (ValSetHandler) Name() string { + return NameVal +} + +func (ValSetHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, + tx basecoin.Tx) (res basecoin.DeliverResult, err error) { + change, ok := tx.Unwrap().(ValChangeTx) + if !ok { + return res, errors.ErrUnknownTxType(tx) + } + res.Diff = change.Diff + return +} + +type ValChangeTx struct { + Diff []*abci.Validator +} + +func (v ValChangeTx) Wrap() basecoin.Tx { + return basecoin.Tx{v} +} + +func (v ValChangeTx) ValidateBasic() error { return nil } + +//-------------------------------- +// Setup tx and handler for testing checktx fees/gas + +// PriceData is the data we ping back +var PriceData = []byte{0xCA, 0xFE} + +// PriceHandler returns checktx results based on the input +type PriceHandler struct { + basecoin.NopInitState + basecoin.NopInitValidate +} + +var _ basecoin.Handler = PriceHandler{} + +func (PriceHandler) Name() string { + return NamePrice +} + +func (PriceHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, + tx basecoin.Tx) (res basecoin.CheckResult, err error) { + price, ok := tx.Unwrap().(PriceShowTx) + if !ok { + return res, errors.ErrUnknownTxType(tx) + } + res.GasAllocated = price.GasAllocated + res.GasPayment = price.GasPayment + res.Data = PriceData + return +} + +func (PriceHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, + tx basecoin.Tx) (res basecoin.DeliverResult, err error) { + _, ok := tx.Unwrap().(PriceShowTx) + if !ok { + return res, errors.ErrUnknownTxType(tx) + } + res.Data = PriceData + return +} + +// PriceShowTx lets us bounce back a given fee/gas on CheckTx +type PriceShowTx struct { + GasAllocated uint + GasPayment uint +} + +func NewPriceShowTx(gasAllocated, gasPayment uint) basecoin.Tx { + return PriceShowTx{GasAllocated: gasAllocated, GasPayment: gasPayment}.Wrap() +} + +func (p PriceShowTx) Wrap() basecoin.Tx { + return basecoin.Tx{p} +} + +func (v PriceShowTx) ValidateBasic() error { return nil } diff --git a/modules/base/multiplexer.go b/modules/base/multiplexer.go index 4088129012..f71b37cf9d 100644 --- a/modules/base/multiplexer.go +++ b/modules/base/multiplexer.go @@ -32,7 +32,7 @@ var _ stack.Middleware = Multiplexer{} // CheckTx splits the input tx and checks them all - fulfills Middlware interface func (Multiplexer) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.CheckResult, err error) { - if mtx, ok := tx.Unwrap().(*MultiTx); ok { + if mtx, ok := tx.Unwrap().(MultiTx); ok { return runAllChecks(ctx, store, mtx.Txs, next) } return next.CheckTx(ctx, store, tx) @@ -40,7 +40,7 @@ func (Multiplexer) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx baseco // DeliverTx splits the input tx and checks them all - fulfills Middlware interface func (Multiplexer) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.DeliverResult, err error) { - if mtx, ok := tx.Unwrap().(*MultiTx); ok { + if mtx, ok := tx.Unwrap().(MultiTx); ok { return runAllDelivers(ctx, store, mtx.Txs, next) } return next.DeliverTx(ctx, store, tx) diff --git a/modules/base/multiplexer_test.go b/modules/base/multiplexer_test.go new file mode 100644 index 0000000000..a3862bb687 --- /dev/null +++ b/modules/base/multiplexer_test.go @@ -0,0 +1,87 @@ +package base + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/state" + wire "github.com/tendermint/go-wire" + "github.com/tendermint/go-wire/data" + "github.com/tendermint/tmlibs/log" +) + +func TestMultiplexer(t *testing.T) { + assert := assert.New(t) + msg := "diddly" + chainID := "multi-verse" + height := uint64(100) + + // Generic args here... + store := state.NewMemKVStore() + ctx := stack.NewContext(chainID, height, log.NewNopLogger()) + + // Build the stack + app := stack. + New(Multiplexer{}). + Dispatch( + stack.WrapHandler(stack.OKHandler{Log: msg}), + stack.WrapHandler(PriceHandler{}), + ) + + raw := stack.NewRawTx([]byte{1, 2, 3, 4}) + fail := stack.NewFailTx() + price1 := NewPriceShowTx(123, 456) + price2 := NewPriceShowTx(1000, 2000) + price3 := NewPriceShowTx(11, 0) + + join := func(data ...[]byte) []byte { + return wire.BinaryBytes(data) + } + + cases := [...]struct { + tx basecoin.Tx + valid bool + gasAllocated uint + gasPayment uint + log string + data data.Bytes + }{ + // test the components without multiplexer (no effect) + 0: {raw, true, 0, 0, msg, nil}, + 1: {price1, true, 123, 456, "", PriceData}, + 2: {fail, false, 0, 0, "", nil}, + // test multiplexer on error + 3: {NewMultiTx(raw, fail, price1), false, 0, 0, "", nil}, + // test combining info on multiplexer + 4: {NewMultiTx(price1, raw), true, 123, 456, "\n" + msg, join(PriceData, nil)}, + // add lots of prices + 5: {NewMultiTx(price1, price2, price3), true, 1134, 2456, "\n\n", join(PriceData, PriceData, PriceData)}, + // combine multiple logs + 6: {NewMultiTx(raw, price3, raw), true, 11, 0, msg + "\n\n" + msg, join(nil, PriceData, nil)}, + } + + for i, tc := range cases { + cres, err := app.CheckTx(ctx, store, tc.tx) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + assert.Equal(tc.log, cres.Log, "%d", i) + assert.Equal(tc.data, cres.Data, "%d", i) + assert.Equal(tc.gasAllocated, cres.GasAllocated, "%d", i) + assert.Equal(tc.gasPayment, cres.GasPayment, "%d", i) + } else { + assert.NotNil(err, "%d", i) + } + + // make sure deliver returns error, not a panic crash + dres, err := app.DeliverTx(ctx, store, tc.tx) + if tc.valid { + assert.Nil(err, "%d: %+v", i, err) + assert.Equal(tc.log, dres.Log, "%d", i) + assert.Equal(tc.data, dres.Data, "%d", i) + } else { + assert.NotNil(err, "%d", i) + } + } +} diff --git a/modules/base/tx.go b/modules/base/tx.go index b2f23db2aa..d365c1b7b8 100644 --- a/modules/base/tx.go +++ b/modules/base/tx.go @@ -16,13 +16,13 @@ const ( //nolint const ( - // TypeMultiTx = NameMultiplexer + "/tx" + TypeMultiTx = NameMultiplexer + "/tx" TypeChainTx = NameChain + "/tx" ) func init() { basecoin.TxMapper. - // RegisterImplementation(MultiTx{}, TypeMultiTx, ByteMultiTx). + RegisterImplementation(MultiTx{}, TypeMultiTx, ByteMultiTx). RegisterImplementation(ChainTx{}, TypeChainTx, ByteChainTx) }