Add tx broadcast gRPC endpoint (#7852)
* WIP tx/broadcast grpc endpoint
* fix lint
* fix proto lint
* Update service.proto
* resolve conflicts
* update service.proto
* Update service.proto
* review changes
* proto lint
* Switch to txraw
* Add check breaking at the end
* Fix broadcast
* Send Msg on SetupSuite
* Remove proto-check-breaking
* 1 validator in test
* Add grpc server tests for broadcast
* Fix grpc server tests
* Add some changes
* Add ress comments
* Add table tests for tx service
* Add test for mode
* Add simulate tests
* Add build flag back
* Revert custom stringer for enum
* Remove stray logs
* Use /txs/{hash}
Co-authored-by: Amaury Martiny <amaury.martiny@protonmail.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
This commit is contained in:
co-authored by
Amaury Martiny
Aleksandr Bezobchuk
parent
f57828c091
commit
b6c8d5ea9f
@@ -7,10 +7,13 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/mempool"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx"
|
||||
)
|
||||
|
||||
// BroadcastTx broadcasts a transactions either synchronously or asynchronously
|
||||
@@ -142,3 +145,36 @@ func (ctx Context) BroadcastTxAsync(txBytes []byte) (*sdk.TxResponse, error) {
|
||||
|
||||
return sdk.NewResponseFormatBroadcastTx(res), err
|
||||
}
|
||||
|
||||
// TxServiceBroadcast is a helper function to broadcast a Tx with the correct gRPC types
|
||||
// from the tx service. Calls `clientCtx.BroadcastTx` under the hood.
|
||||
func TxServiceBroadcast(grpcCtx context.Context, clientCtx Context, req *tx.BroadcastTxRequest) (*tx.BroadcastTxResponse, error) {
|
||||
if req == nil || req.TxBytes == nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "invalid empty tx")
|
||||
}
|
||||
|
||||
clientCtx = clientCtx.WithBroadcastMode(normalizeBroadcastMode(req.Mode))
|
||||
resp, err := clientCtx.BroadcastTx(req.TxBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tx.BroadcastTxResponse{
|
||||
TxResponse: resp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// normalizeBroadcastMode converts a broadcast mode into a normalized string
|
||||
// to be passed into the clientCtx.
|
||||
func normalizeBroadcastMode(mode tx.BroadcastMode) string {
|
||||
switch mode {
|
||||
case tx.BroadcastMode_BROADCAST_MODE_ASYNC:
|
||||
return "async"
|
||||
case tx.BroadcastMode_BROADCAST_MODE_BLOCK:
|
||||
return "block"
|
||||
case tx.BroadcastMode_BROADCAST_MODE_SYNC:
|
||||
return "sync"
|
||||
default:
|
||||
return "unspecified"
|
||||
}
|
||||
}
|
||||
|
||||
+38
-3
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
gocontext "context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
gogogrpc "github.com/gogo/protobuf/grpc"
|
||||
@@ -12,10 +13,10 @@ import (
|
||||
"google.golang.org/grpc/encoding/proto"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx"
|
||||
)
|
||||
|
||||
var _ gogogrpc.ClientConn = Context{}
|
||||
@@ -23,7 +24,37 @@ var _ gogogrpc.ClientConn = Context{}
|
||||
var protoCodec = encoding.GetCodec(proto.Name)
|
||||
|
||||
// Invoke implements the grpc ClientConn.Invoke method
|
||||
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply interface{}, opts ...grpc.CallOption) error {
|
||||
func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply interface{}, opts ...grpc.CallOption) (err error) {
|
||||
// Two things can happen here:
|
||||
// 1. either we're broadcasting a Tx, in which call we call Tendermint's broadcast endpoint directly,
|
||||
// 2. or we are querying for state, in which case we call ABCI's Query.
|
||||
|
||||
// In both cases, we don't allow empty request args (it will panic unexpectedly).
|
||||
if reflect.ValueOf(args).IsNil() {
|
||||
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "request cannot be nil")
|
||||
}
|
||||
|
||||
// Case 1. Broadcasting a Tx.
|
||||
if isBroadcast(method) {
|
||||
req, ok := args.(*tx.BroadcastTxRequest)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxRequest)(nil), args)
|
||||
}
|
||||
res, ok := reply.(*tx.BroadcastTxResponse)
|
||||
if !ok {
|
||||
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "expected %T, got %T", (*tx.BroadcastTxResponse)(nil), args)
|
||||
}
|
||||
|
||||
broadcastRes, err := TxServiceBroadcast(grpcCtx, ctx, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*res = *broadcastRes
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Case 2. Querying state.
|
||||
reqBz, err := protoCodec.Marshal(args)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -86,3 +117,7 @@ func (ctx Context) Invoke(grpcCtx gocontext.Context, method string, args, reply
|
||||
func (Context) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
return nil, fmt.Errorf("streaming rpc not supported")
|
||||
}
|
||||
|
||||
func isBroadcast(method string) bool {
|
||||
return method == "/cosmos.tx.v1beta1.Service/BroadcastTx"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user