diff --git a/client/context/broadcast.go b/client/context/broadcast.go index 9c1a720ae7aa..c31162226429 100644 --- a/client/context/broadcast.go +++ b/client/context/broadcast.go @@ -3,6 +3,8 @@ package context import ( "fmt" + "github.com/tendermint/tendermint/mempool" + "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -29,6 +31,27 @@ func (ctx CLIContext) BroadcastTx(txBytes []byte) (res sdk.TxResponse, err error return res, err } +func CheckTendermintError(err error) *sdk.TxResponse { + if err == nil { + return nil + } + + if err == mempool.ErrTxInCache { + return &sdk.TxResponse{Code: uint32(sdk.CodeTxInCache)} + } + + switch err.(type) { + case mempool.ErrMempoolIsFull: + return &sdk.TxResponse{Code: uint32(sdk.CodeMempoolIsFull)} + case mempool.ErrPreCheck: + return &sdk.TxResponse{Code: uint32(sdk.CodeFailedPreCheck)} + case mempool.ErrTxTooLarge: + return &sdk.TxResponse{Code: uint32(sdk.CodeTxTooLarge)} + } + + return nil +} + // BroadcastTxCommit broadcasts transaction bytes to a Tendermint node and // waits for a commit. An error is only returned if there is no RPC node // connection or if broadcasting fails. @@ -44,6 +67,10 @@ func (ctx CLIContext) BroadcastTxCommit(txBytes []byte) (sdk.TxResponse, error) res, err := node.BroadcastTxCommit(txBytes) if err != nil { + if res := CheckTendermintError(err); res != nil { + return *res, err + } + return sdk.NewResponseFormatBroadcastTxCommit(res), err } @@ -67,6 +94,12 @@ func (ctx CLIContext) BroadcastTxSync(txBytes []byte) (sdk.TxResponse, error) { } res, err := node.BroadcastTxSync(txBytes) + if err != nil { + if res := CheckTendermintError(err); res != nil { + return *res, err + } + } + return sdk.NewResponseFormatBroadcastTx(res), err } @@ -79,5 +112,11 @@ func (ctx CLIContext) BroadcastTxAsync(txBytes []byte) (sdk.TxResponse, error) { } res, err := node.BroadcastTxAsync(txBytes) + if err != nil { + if res := CheckTendermintError(err); res != nil { + return *res, err + } + } + return sdk.NewResponseFormatBroadcastTx(res), err } diff --git a/client/context/broadcast_test.go b/client/context/broadcast_test.go new file mode 100644 index 000000000000..d048abe593de --- /dev/null +++ b/client/context/broadcast_test.go @@ -0,0 +1,63 @@ +package context + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/mempool" + "github.com/tendermint/tendermint/rpc/client/mock" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" +) + +type MockClient struct { + mock.Client + err error +} + +func (c MockClient) BroadcastTxCommit(tx tmtypes.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return nil, c.err +} + +func (c MockClient) BroadcastTxAsync(tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { + return nil, c.err +} + +func (c MockClient) BroadcastTxSync(tx tmtypes.Tx) (*ctypes.ResultBroadcastTx, error) { + return nil, c.err +} + +func CreateContextWithErrorAndMode(err error, mode string) CLIContext { + return CLIContext{ + Client: MockClient{err: err}, + BroadcastMode: mode, + } +} + +// Test the correct code is returned when +func TestBroadcastError(t *testing.T) { + errors := map[error]uint32{ + mempool.ErrTxInCache: uint32(types.CodeTxInCache), + mempool.ErrTxTooLarge{}: uint32(types.CodeTxTooLarge), + mempool.ErrPreCheck{}: uint32(types.CodeFailedPreCheck), + mempool.ErrMempoolIsFull{}: uint32(types.CodeMempoolIsFull), + } + + modes := []string{ + flags.BroadcastAsync, + flags.BroadcastBlock, + flags.BroadcastSync, + } + + for _, mode := range modes { + for err, code := range errors { + ctx := CreateContextWithErrorAndMode(err, mode) + resp, returnedErr := ctx.BroadcastTx([]byte{}) + require.Error(t, returnedErr) + require.Equal(t, code, resp.Code) + } + } + +} diff --git a/types/errors.go b/types/errors.go index 7a7a0d4a9dcd..e40e2e5a811b 100644 --- a/types/errors.go +++ b/types/errors.go @@ -46,6 +46,10 @@ const ( CodeTooManySignatures CodeType = 15 CodeGasOverflow CodeType = 16 CodeNoSignatures CodeType = 17 + CodeTxInCache CodeType = 18 + CodeMempoolIsFull CodeType = 19 + CodeFailedPreCheck CodeType = 20 + CodeTxTooLarge CodeType = 21 // CodespaceRoot is a codespace for error codes in this file only. // Notice that 0 is an "unset" codespace, which can be overridden with