Merge PR #4996: Expose precheck errors from tx
This commit is contained in:
parent
849e2fb638
commit
5bcab79e8a
|
@ -60,6 +60,11 @@ longer panics if the store to load contains substores that we didn't explicitly
|
|||
* [\#4979](https://github.com/cosmos/cosmos-sdk/issues/4979) Introduce a new `halt-time` config and
|
||||
CLI option to the `start` command. When provided, an application will halt during `Commit` when the
|
||||
block time is >= the `halt-time`.
|
||||
* [\#4972](https://github.com/cosmos/cosmos-sdk/issues/4972) A `TxResponse` with a corresponding code
|
||||
and tx hash will be returned for specific Tendermint errors:
|
||||
* `CodeTxInMempoolCache`
|
||||
* `CodeMempoolIsFull`
|
||||
* `CodeTxTooLarge`
|
||||
|
||||
### Improvements
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@ package context
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -29,6 +33,46 @@ func (ctx CLIContext) BroadcastTx(txBytes []byte) (res sdk.TxResponse, err error
|
|||
return res, err
|
||||
}
|
||||
|
||||
// CheckTendermintError checks if the error returned from BroadcastTx is a
|
||||
// Tendermint error that is returned before the tx is submitted due to
|
||||
// precondition checks that failed. If an Tendermint error is detected, this
|
||||
// function returns the correct code back in TxResponse.
|
||||
//
|
||||
// TODO: Avoid brittle string matching in favor of error matching. This requires
|
||||
// a change to Tendermint's RPCError type to allow retrieval or matching against
|
||||
// a concrete error type.
|
||||
func CheckTendermintError(err error, txBytes []byte) *sdk.TxResponse {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errStr := strings.ToLower(err.Error())
|
||||
txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes))
|
||||
|
||||
switch {
|
||||
case strings.Contains(errStr, strings.ToLower(mempool.ErrTxInCache.Error())):
|
||||
return &sdk.TxResponse{
|
||||
Code: uint32(sdk.CodeTxInMempoolCache),
|
||||
TxHash: txHash,
|
||||
}
|
||||
|
||||
case strings.Contains(errStr, "mempool is full"):
|
||||
return &sdk.TxResponse{
|
||||
Code: uint32(sdk.CodeMempoolIsFull),
|
||||
TxHash: txHash,
|
||||
}
|
||||
|
||||
case strings.Contains(errStr, "tx too large"):
|
||||
return &sdk.TxResponse{
|
||||
Code: uint32(sdk.CodeTxTooLarge),
|
||||
TxHash: txHash,
|
||||
}
|
||||
|
||||
default:
|
||||
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 +88,10 @@ func (ctx CLIContext) BroadcastTxCommit(txBytes []byte) (sdk.TxResponse, error)
|
|||
|
||||
res, err := node.BroadcastTxCommit(txBytes)
|
||||
if err != nil {
|
||||
if errRes := CheckTendermintError(err, txBytes); errRes != nil {
|
||||
return *errRes, nil
|
||||
}
|
||||
|
||||
return sdk.NewResponseFormatBroadcastTxCommit(res), err
|
||||
}
|
||||
|
||||
|
@ -67,6 +115,10 @@ func (ctx CLIContext) BroadcastTxSync(txBytes []byte) (sdk.TxResponse, error) {
|
|||
}
|
||||
|
||||
res, err := node.BroadcastTxSync(txBytes)
|
||||
if errRes := CheckTendermintError(err, txBytes); errRes != nil {
|
||||
return *errRes, nil
|
||||
}
|
||||
|
||||
return sdk.NewResponseFormatBroadcastTx(res), err
|
||||
}
|
||||
|
||||
|
@ -79,5 +131,9 @@ func (ctx CLIContext) BroadcastTxAsync(txBytes []byte) (sdk.TxResponse, error) {
|
|||
}
|
||||
|
||||
res, err := node.BroadcastTxAsync(txBytes)
|
||||
if errRes := CheckTendermintError(err, txBytes); errRes != nil {
|
||||
return *errRes, nil
|
||||
}
|
||||
|
||||
return sdk.NewResponseFormatBroadcastTx(res), err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/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.CodeTxInMempoolCache),
|
||||
mempool.ErrTxTooLarge{}: uint32(types.CodeTxTooLarge),
|
||||
mempool.ErrMempoolIsFull{}: uint32(types.CodeMempoolIsFull),
|
||||
}
|
||||
|
||||
modes := []string{
|
||||
flags.BroadcastAsync,
|
||||
flags.BroadcastBlock,
|
||||
flags.BroadcastSync,
|
||||
}
|
||||
|
||||
txBytes := []byte{0xA, 0xB}
|
||||
txHash := fmt.Sprintf("%X", tmhash.Sum(txBytes))
|
||||
|
||||
for _, mode := range modes {
|
||||
for err, code := range errors {
|
||||
ctx := CreateContextWithErrorAndMode(err, mode)
|
||||
resp, returnedErr := ctx.BroadcastTx(txBytes)
|
||||
require.NoError(t, returnedErr)
|
||||
require.Equal(t, code, resp.Code)
|
||||
require.Equal(t, txHash, resp.TxHash)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
1
go.mod
1
go.mod
|
@ -28,6 +28,7 @@ require (
|
|||
github.com/tendermint/iavl v0.12.4
|
||||
github.com/tendermint/tendermint v0.32.3
|
||||
github.com/tendermint/tm-db v0.1.1
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
||||
|
|
8
go.sum
8
go.sum
|
@ -87,8 +87,6 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
|
@ -98,8 +96,6 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
|
@ -214,8 +210,6 @@ github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoM
|
|||
github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8=
|
||||
github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o=
|
||||
github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU=
|
||||
github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXHwyrV1QY=
|
||||
github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE=
|
||||
github.com/tendermint/tendermint v0.32.3 h1:GEnWpGQ795h5oTFNbfBLsY0LW/CW2j6p6HtiYNfxsgg=
|
||||
github.com/tendermint/tendermint v0.32.3/go.mod h1:ZK2c29jl1QRYznIRyRWRDsmm1yvtPzBRT00x4t1JToY=
|
||||
github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0=
|
||||
|
@ -274,6 +268,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
|
|
@ -46,6 +46,9 @@ const (
|
|||
CodeTooManySignatures CodeType = 15
|
||||
CodeGasOverflow CodeType = 16
|
||||
CodeNoSignatures CodeType = 17
|
||||
CodeTxInMempoolCache CodeType = 18
|
||||
CodeMempoolIsFull CodeType = 19
|
||||
CodeTxTooLarge CodeType = 20
|
||||
|
||||
// CodespaceRoot is a codespace for error codes in this file only.
|
||||
// Notice that 0 is an "unset" codespace, which can be overridden with
|
||||
|
|
Loading…
Reference in New Issue