diff --git a/Makefile b/Makefile index baace1ee14b0..f9011d13d3b8 100644 --- a/Makefile +++ b/Makefile @@ -357,7 +357,7 @@ devdoc-update: ### Protobuf ### ############################################################################### -proto-all: proto-format proto-lint proto-check-breaking proto-gen +proto-all: proto-format proto-lint proto-gen proto-gen: @echo "Generating Protobuf files" diff --git a/client/broadcast.go b/client/broadcast.go index e28d76c54f3a..e21bc080c286 100644 --- a/client/broadcast.go +++ b/client/broadcast.go @@ -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" + } +} diff --git a/client/grpc_query.go b/client/grpc_query.go index 979333f64137..fc8cdeeb80d5 100644 --- a/client/grpc_query.go +++ b/client/grpc_query.go @@ -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" +} diff --git a/docs/migrations/rest.md b/docs/migrations/rest.md index d418587d0866..f27635bf350c 100644 --- a/docs/migrations/rest.md +++ b/docs/migrations/rest.md @@ -31,7 +31,7 @@ Some modules expose legacy `POST` endpoints to generate unsigned transactions fo | Legacy REST Endpoint | Description | New gGPC-gateway REST Endpoint | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -| `GET /txs/{hash}` | Query tx by hash | `GET /cosmos/tx/v1beta1/tx/{hash}` | +| `GET /txs/{hash}` | Query tx by hash | `GET /cosmos/tx/v1beta1/txs/{hash}` | | `GET /txs` | Query tx by events | `GET /cosmos/tx/v1beta1/txs` | | `POST /txs` | Broadcast tx | `POST /cosmos/tx/v1beta1/txs` | | `POST /txs/encode` | Encodes an Amino JSON tx to an Amino binary tx | N/A, use Protobuf directly | diff --git a/proto/cosmos/tx/v1beta1/service.proto b/proto/cosmos/tx/v1beta1/service.proto index 68eaaae83395..59df75bab12b 100644 --- a/proto/cosmos/tx/v1beta1/service.proto +++ b/proto/cosmos/tx/v1beta1/service.proto @@ -4,6 +4,7 @@ package cosmos.tx.v1beta1; import "google/api/annotations.proto"; import "cosmos/base/abci/v1beta1/abci.proto"; import "cosmos/tx/v1beta1/tx.proto"; +import "gogoproto/gogo.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; @@ -12,13 +13,22 @@ option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; service Service { // Simulate simulates executing a transaction for estimating gas usage. rpc Simulate(SimulateRequest) returns (SimulateResponse) { - option (google.api.http).post = "/cosmos/tx/v1beta1/simulate"; + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/simulate" + body: "*" + }; } // GetTx fetches a tx by hash. rpc GetTx(GetTxRequest) returns (GetTxResponse) { - option (google.api.http).get = "/cosmos/tx/v1beta1/tx/{hash}"; + option (google.api.http).get = "/cosmos/tx/v1beta1/txs/{hash}"; + } + // BroadcastTx broadcast transaction. + rpc BroadcastTx(BroadcastTxRequest) returns (BroadcastTxResponse) { + option (google.api.http) = { + post: "/cosmos/tx/v1beta1/txs" + body: "*" + }; } - // GetTxsEvent fetches txs by event. rpc GetTxsEvent(GetTxsEventRequest) returns (GetTxsEventResponse) { option (google.api.http).get = "/cosmos/tx/v1beta1/txs"; @@ -45,6 +55,36 @@ message GetTxsEventResponse { cosmos.base.query.v1beta1.PageResponse pagination = 3; } +// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +// RPC method. +message BroadcastTxRequest { + // tx_bytes is the raw transaction. + bytes tx_bytes = 1; + BroadcastMode mode = 2; +} + +// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +enum BroadcastMode { + // zero-value for mode ordering + BROADCAST_MODE_UNSPECIFIED = 0; + // BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BROADCAST_MODE_BLOCK = 1; + // BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BROADCAST_MODE_SYNC = 2; + // BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + // immediately. + BROADCAST_MODE_ASYNC = 3; +} + +// BroadcastTxResponse is the response type for the +// Service.BroadcastTx method. +message BroadcastTxResponse { + // tx_response is the queried TxResponses. + cosmos.base.abci.v1beta1.TxResponse tx_response = 1; +} + // SimulateRequest is the request type for the Service.Simulate // RPC method. message SimulateRequest { diff --git a/server/grpc/server_test.go b/server/grpc/server_test.go index 59c726c6abaf..5648233f8f32 100644 --- a/server/grpc/server_test.go +++ b/server/grpc/server_test.go @@ -13,54 +13,64 @@ import ( "google.golang.org/grpc/metadata" rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" + clienttx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/cosmos/cosmos-sdk/types/tx" txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { suite.Suite + cfg network.Config network *network.Network + conn *grpc.ClientConn } func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - s.network = network.New(s.T(), network.DefaultConfig()) + s.cfg = network.DefaultConfig() + s.network = network.New(s.T(), s.cfg) s.Require().NotNil(s.network) _, err := s.network.WaitForHeight(2) s.Require().NoError(err) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} -func (s *IntegrationTestSuite) TestGRPCServer() { val0 := s.network.Validators[0] - conn, err := grpc.Dial( + s.conn, err = grpc.Dial( val0.AppConfig.GRPC.Address, grpc.WithInsecure(), // Or else we get "no transport security set" ) s.Require().NoError(err) - defer conn.Close() +} +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.conn.Close() + s.network.Cleanup() +} + +func (s *IntegrationTestSuite) TestGRPCServer_TestService() { // gRPC query to test service should work - testClient := testdata.NewQueryClient(conn) + testClient := testdata.NewQueryClient(s.conn) testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) s.Require().NoError(err) s.Require().Equal("hello", testRes.Message) +} + +func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() { + val0 := s.network.Validators[0] // gRPC query to bank service should work denom := fmt.Sprintf("%stoken", val0.Moniker) - bankClient := banktypes.NewQueryClient(conn) + bankClient := banktypes.NewQueryClient(s.conn) var header metadata.MD bankRes, err := bankClient.Balance( context.Background(), @@ -83,9 +93,11 @@ func (s *IntegrationTestSuite) TestGRPCServer() { ) blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) s.Require().Equal([]string{"1"}, blockHeight) +} +func (s *IntegrationTestSuite) TestGRPCServer_Reflection() { // Test server reflection - reflectClient := rpb.NewServerReflectionClient(conn) + reflectClient := rpb.NewServerReflectionClient(s.conn) stream, err := reflectClient.ServerReflectionInfo(context.Background(), grpc.WaitForReady(true)) s.Require().NoError(err) s.Require().NoError(stream.Send(&rpb.ServerReflectionRequest{ @@ -100,11 +112,13 @@ func (s *IntegrationTestSuite) TestGRPCServer() { } // Make sure the following services are present s.Require().True(servicesMap["cosmos.bank.v1beta1.Query"]) +} +func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() { // Query the tx via gRPC without pagination. This used to panic, see // https://github.com/cosmos/cosmos-sdk/issues/8038. - txServiceClient := txtypes.NewServiceClient(conn) - _, err = txServiceClient.GetTxsEvent( + txServiceClient := txtypes.NewServiceClient(s.conn) + _, err := txServiceClient.GetTxsEvent( context.Background(), &tx.GetTxsEventRequest{ Events: []string{"message.action=send"}, @@ -115,6 +129,50 @@ func (s *IntegrationTestSuite) TestGRPCServer() { s.Require().NoError(err) } +func (s *IntegrationTestSuite) TestGRPCServer_BroadcastTx() { + val0 := s.network.Validators[0] + + // prepare txBuilder with msg + txBuilder := val0.ClientCtx.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + s.Require().NoError( + txBuilder.SetMsgs(&banktypes.MsgSend{ + FromAddress: val0.Address.String(), + ToAddress: val0.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, + }), + ) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + + // setup txFactory + txFactory := clienttx.Factory{}. + WithChainID(val0.ClientCtx.ChainID). + WithKeybase(val0.ClientCtx.Keyring). + WithTxConfig(val0.ClientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err := authclient.SignTx(txFactory, val0.ClientCtx, val0.Moniker, txBuilder, false) + s.Require().NoError(err) + + txBytes, err := val0.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + // Broadcast the tx via gRPC. + queryClient := tx.NewServiceClient(s.conn) + grpcRes, err := queryClient.BroadcastTx( + context.Background(), + &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, + ) + s.Require().NoError(err) + s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) +} + // Test and enforce that we upfront reject any connections to baseapp containing // invalid initial x-cosmos-block-height that aren't positive and in the range [0, max(int64)] // See issue https://github.com/cosmos/cosmos-sdk/issues/7662. diff --git a/types/tx/service.pb.go b/types/tx/service.pb.go index 44242b03116d..57f1f627635e 100644 --- a/types/tx/service.pb.go +++ b/types/tx/service.pb.go @@ -8,6 +8,7 @@ import ( fmt "fmt" types "github.com/cosmos/cosmos-sdk/types" query "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" @@ -30,6 +31,45 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +type BroadcastMode int32 + +const ( + // zero-value for mode ordering + BroadcastMode_BROADCAST_MODE_UNSPECIFIED BroadcastMode = 0 + // BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + // the tx to be committed in a block. + BroadcastMode_BROADCAST_MODE_BLOCK BroadcastMode = 1 + // BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + // a CheckTx execution response only. + BroadcastMode_BROADCAST_MODE_SYNC BroadcastMode = 2 + // BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + // immediately. + BroadcastMode_BROADCAST_MODE_ASYNC BroadcastMode = 3 +) + +var BroadcastMode_name = map[int32]string{ + 0: "BROADCAST_MODE_UNSPECIFIED", + 1: "BROADCAST_MODE_BLOCK", + 2: "BROADCAST_MODE_SYNC", + 3: "BROADCAST_MODE_ASYNC", +} + +var BroadcastMode_value = map[string]int32{ + "BROADCAST_MODE_UNSPECIFIED": 0, + "BROADCAST_MODE_BLOCK": 1, + "BROADCAST_MODE_SYNC": 2, + "BROADCAST_MODE_ASYNC": 3, +} + +func (x BroadcastMode) String() string { + return proto.EnumName(BroadcastMode_name, int32(x)) +} + +func (BroadcastMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{0} +} + // GetTxsEventRequest is the request type for the Service.TxsByEvents // RPC method. type GetTxsEventRequest struct { @@ -151,6 +191,108 @@ func (m *GetTxsEventResponse) GetPagination() *query.PageResponse { return nil } +// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +// RPC method. +type BroadcastTxRequest struct { + // tx_bytes is the raw transaction. + TxBytes []byte `protobuf:"bytes,1,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` + Mode BroadcastMode `protobuf:"varint,2,opt,name=mode,proto3,enum=cosmos.tx.v1beta1.BroadcastMode" json:"mode,omitempty"` +} + +func (m *BroadcastTxRequest) Reset() { *m = BroadcastTxRequest{} } +func (m *BroadcastTxRequest) String() string { return proto.CompactTextString(m) } +func (*BroadcastTxRequest) ProtoMessage() {} +func (*BroadcastTxRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{2} +} +func (m *BroadcastTxRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BroadcastTxRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BroadcastTxRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BroadcastTxRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_BroadcastTxRequest.Merge(m, src) +} +func (m *BroadcastTxRequest) XXX_Size() int { + return m.Size() +} +func (m *BroadcastTxRequest) XXX_DiscardUnknown() { + xxx_messageInfo_BroadcastTxRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_BroadcastTxRequest proto.InternalMessageInfo + +func (m *BroadcastTxRequest) GetTxBytes() []byte { + if m != nil { + return m.TxBytes + } + return nil +} + +func (m *BroadcastTxRequest) GetMode() BroadcastMode { + if m != nil { + return m.Mode + } + return BroadcastMode_BROADCAST_MODE_UNSPECIFIED +} + +// BroadcastTxResponse is the response type for the +// Service.BroadcastTx method. +type BroadcastTxResponse struct { + // tx_response is the queried TxResponses. + TxResponse *types.TxResponse `protobuf:"bytes,1,opt,name=tx_response,json=txResponse,proto3" json:"tx_response,omitempty"` +} + +func (m *BroadcastTxResponse) Reset() { *m = BroadcastTxResponse{} } +func (m *BroadcastTxResponse) String() string { return proto.CompactTextString(m) } +func (*BroadcastTxResponse) ProtoMessage() {} +func (*BroadcastTxResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_e0b00a618705eca7, []int{3} +} +func (m *BroadcastTxResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BroadcastTxResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BroadcastTxResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BroadcastTxResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_BroadcastTxResponse.Merge(m, src) +} +func (m *BroadcastTxResponse) XXX_Size() int { + return m.Size() +} +func (m *BroadcastTxResponse) XXX_DiscardUnknown() { + xxx_messageInfo_BroadcastTxResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_BroadcastTxResponse proto.InternalMessageInfo + +func (m *BroadcastTxResponse) GetTxResponse() *types.TxResponse { + if m != nil { + return m.TxResponse + } + return nil +} + // SimulateRequest is the request type for the Service.Simulate // RPC method. type SimulateRequest struct { @@ -162,7 +304,7 @@ func (m *SimulateRequest) Reset() { *m = SimulateRequest{} } func (m *SimulateRequest) String() string { return proto.CompactTextString(m) } func (*SimulateRequest) ProtoMessage() {} func (*SimulateRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e0b00a618705eca7, []int{2} + return fileDescriptor_e0b00a618705eca7, []int{4} } func (m *SimulateRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -211,7 +353,7 @@ func (m *SimulateResponse) Reset() { *m = SimulateResponse{} } func (m *SimulateResponse) String() string { return proto.CompactTextString(m) } func (*SimulateResponse) ProtoMessage() {} func (*SimulateResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e0b00a618705eca7, []int{3} + return fileDescriptor_e0b00a618705eca7, []int{5} } func (m *SimulateResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -265,7 +407,7 @@ func (m *GetTxRequest) Reset() { *m = GetTxRequest{} } func (m *GetTxRequest) String() string { return proto.CompactTextString(m) } func (*GetTxRequest) ProtoMessage() {} func (*GetTxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e0b00a618705eca7, []int{4} + return fileDescriptor_e0b00a618705eca7, []int{6} } func (m *GetTxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -313,7 +455,7 @@ func (m *GetTxResponse) Reset() { *m = GetTxResponse{} } func (m *GetTxResponse) String() string { return proto.CompactTextString(m) } func (*GetTxResponse) ProtoMessage() {} func (*GetTxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e0b00a618705eca7, []int{5} + return fileDescriptor_e0b00a618705eca7, []int{7} } func (m *GetTxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -357,8 +499,11 @@ func (m *GetTxResponse) GetTxResponse() *types.TxResponse { } func init() { + proto.RegisterEnum("cosmos.tx.v1beta1.BroadcastMode", BroadcastMode_name, BroadcastMode_value) proto.RegisterType((*GetTxsEventRequest)(nil), "cosmos.tx.v1beta1.GetTxsEventRequest") proto.RegisterType((*GetTxsEventResponse)(nil), "cosmos.tx.v1beta1.GetTxsEventResponse") + proto.RegisterType((*BroadcastTxRequest)(nil), "cosmos.tx.v1beta1.BroadcastTxRequest") + proto.RegisterType((*BroadcastTxResponse)(nil), "cosmos.tx.v1beta1.BroadcastTxResponse") proto.RegisterType((*SimulateRequest)(nil), "cosmos.tx.v1beta1.SimulateRequest") proto.RegisterType((*SimulateResponse)(nil), "cosmos.tx.v1beta1.SimulateResponse") proto.RegisterType((*GetTxRequest)(nil), "cosmos.tx.v1beta1.GetTxRequest") @@ -368,43 +513,54 @@ func init() { func init() { proto.RegisterFile("cosmos/tx/v1beta1/service.proto", fileDescriptor_e0b00a618705eca7) } var fileDescriptor_e0b00a618705eca7 = []byte{ - // 563 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xae, 0x1d, 0x48, 0xdb, 0x49, 0x11, 0xb0, 0x88, 0x2a, 0x32, 0xc5, 0x0d, 0x4e, 0xd3, 0x56, - 0x48, 0xd8, 0x6a, 0xb8, 0xf4, 0x80, 0x84, 0x84, 0x54, 0x22, 0x6e, 0xc8, 0xed, 0x89, 0x4b, 0xb5, - 0x09, 0x5b, 0xc7, 0x22, 0xf1, 0xba, 0xd9, 0x49, 0xb4, 0x15, 0xf4, 0xc2, 0x91, 0x13, 0x12, 0x2f, - 0xc5, 0x31, 0x12, 0x17, 0x8e, 0x28, 0xe1, 0x0d, 0x78, 0x01, 0xe4, 0xf5, 0x3a, 0x71, 0x68, 0x4c, - 0x7b, 0xca, 0x8e, 0xf2, 0xfd, 0xcc, 0x37, 0xa3, 0x31, 0x6c, 0x77, 0xb8, 0xe8, 0x73, 0xe1, 0xa1, - 0xf4, 0x46, 0x07, 0x6d, 0x86, 0xf4, 0xc0, 0x13, 0x6c, 0x30, 0x0a, 0x3b, 0xcc, 0x8d, 0x07, 0x1c, - 0x39, 0xb9, 0x9f, 0x02, 0x5c, 0x94, 0xae, 0x06, 0x58, 0x5b, 0x01, 0xe7, 0x41, 0x8f, 0x79, 0x34, - 0x0e, 0x3d, 0x1a, 0x45, 0x1c, 0x29, 0x86, 0x3c, 0x12, 0x29, 0xc1, 0xaa, 0x6b, 0xc5, 0x36, 0x15, - 0xcc, 0xa3, 0xed, 0x4e, 0x38, 0x13, 0x4e, 0x0a, 0x0d, 0xb2, 0xae, 0xda, 0xa2, 0xd4, 0xff, 0x3d, - 0xcd, 0x0b, 0x9c, 0x0f, 0xd9, 0xe0, 0x62, 0x86, 0x89, 0x69, 0x10, 0x46, 0xca, 0x2d, 0xc5, 0x3a, - 0x08, 0xa4, 0xc5, 0xf0, 0x44, 0x8a, 0xa3, 0x11, 0x8b, 0xd0, 0x67, 0xe7, 0x43, 0x26, 0x90, 0x6c, - 0x42, 0x99, 0x25, 0xb5, 0xa8, 0x1a, 0xb5, 0xd2, 0xfe, 0xba, 0xaf, 0x2b, 0xf2, 0x1a, 0x60, 0xae, - 0x50, 0x35, 0x6b, 0xc6, 0x7e, 0xa5, 0xb9, 0xeb, 0xea, 0x80, 0x89, 0x9d, 0xab, 0xec, 0xb2, 0xa0, - 0xee, 0x5b, 0x1a, 0x30, 0xad, 0xe9, 0xe7, 0x98, 0xce, 0xd8, 0x80, 0x07, 0x0b, 0xb6, 0x22, 0xe6, - 0x91, 0x60, 0x64, 0x0f, 0x4a, 0x28, 0x53, 0xd3, 0x4a, 0xf3, 0xa1, 0x7b, 0x65, 0x72, 0xee, 0x89, - 0xf4, 0x13, 0x04, 0x69, 0xc1, 0x06, 0xca, 0xd3, 0x81, 0xe6, 0x89, 0xaa, 0xa9, 0x18, 0x3b, 0x0b, - 0xad, 0xa8, 0x69, 0xe5, 0x88, 0x1a, 0xec, 0x57, 0x70, 0xf6, 0x4e, 0x84, 0xf2, 0x89, 0x4a, 0x2a, - 0xd1, 0xde, 0xb5, 0x89, 0xb4, 0x52, 0x3e, 0xd2, 0x21, 0xdc, 0x3d, 0x0e, 0xfb, 0xc3, 0x1e, 0xc5, - 0x2c, 0x31, 0x69, 0x80, 0x89, 0xb2, 0x6a, 0x28, 0xcd, 0x82, 0x30, 0x26, 0x4a, 0xe7, 0x8b, 0x01, - 0xf7, 0xe6, 0x54, 0x3d, 0x89, 0x17, 0xb0, 0x16, 0x50, 0x71, 0x1a, 0x46, 0x67, 0x5c, 0x2b, 0x3c, - 0x29, 0x0e, 0xd7, 0xa2, 0xe2, 0x4d, 0x74, 0xc6, 0xfd, 0xd5, 0x20, 0x7d, 0x90, 0x43, 0x28, 0x0f, - 0x98, 0x18, 0xf6, 0x50, 0xef, 0xa8, 0x56, 0xcc, 0xf5, 0x15, 0xce, 0xd7, 0x78, 0xc7, 0x81, 0x0d, - 0xb5, 0x98, 0x2c, 0x03, 0x81, 0x5b, 0x5d, 0x2a, 0xba, 0xaa, 0x87, 0x75, 0x5f, 0xbd, 0x9d, 0x4b, - 0xb8, 0xa3, 0x31, 0xba, 0xd9, 0x9b, 0x05, 0x25, 0x47, 0x50, 0xc9, 0x2d, 0x4d, 0xb7, 0x76, 0xb3, - 0x9d, 0xc1, 0x7c, 0x67, 0xcd, 0x3f, 0x26, 0xac, 0x1e, 0xa7, 0x27, 0x46, 0x24, 0xac, 0x65, 0xa3, - 0x23, 0xce, 0x12, 0xe7, 0x7f, 0x56, 0x62, 0xd5, 0xff, 0x8b, 0x49, 0x0d, 0x9c, 0xfa, 0xe7, 0x1f, - 0xbf, 0xbf, 0x99, 0x8f, 0x9d, 0x47, 0xde, 0x92, 0xdb, 0xce, 0xdc, 0x62, 0xb8, 0xad, 0x86, 0x40, - 0xb6, 0x97, 0x48, 0xe6, 0x47, 0x68, 0xd5, 0x8a, 0x01, 0xda, 0x70, 0x47, 0x19, 0xda, 0x64, 0xcb, - 0x5b, 0x76, 0xd5, 0xde, 0xc7, 0x64, 0xea, 0x97, 0xe4, 0x13, 0x54, 0x72, 0x37, 0x43, 0x1a, 0x45, - 0xb2, 0x0b, 0xa7, 0x6c, 0xed, 0x5e, 0x07, 0xd3, 0x3d, 0xd8, 0xaa, 0x87, 0x2a, 0xd9, 0x5c, 0xda, - 0x83, 0x78, 0xf5, 0xf2, 0xfb, 0xc4, 0x36, 0xc6, 0x13, 0xdb, 0xf8, 0x35, 0xb1, 0x8d, 0xaf, 0x53, - 0x7b, 0x65, 0x3c, 0xb5, 0x57, 0x7e, 0x4e, 0xed, 0x95, 0x77, 0x8d, 0x20, 0xc4, 0xee, 0xb0, 0xed, - 0x76, 0x78, 0x3f, 0xe3, 0xa6, 0x3f, 0xcf, 0xc4, 0xfb, 0x0f, 0x1e, 0x5e, 0xc4, 0x2c, 0x11, 0x6b, - 0x97, 0xd5, 0x07, 0xe7, 0xf9, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x62, 0x59, 0xdc, 0x70, 0x31, - 0x05, 0x00, 0x00, + // 737 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4f, 0x13, 0x41, + 0x14, 0xef, 0xb6, 0xc8, 0xc7, 0x2b, 0x68, 0x1d, 0x10, 0x6b, 0xd1, 0xa5, 0x2c, 0x16, 0x08, 0x89, + 0xbb, 0xa1, 0x7a, 0x20, 0xc6, 0xc4, 0xd0, 0x52, 0x08, 0x51, 0x3e, 0xb2, 0xc5, 0x83, 0xc6, 0xa4, + 0x99, 0xb6, 0xc3, 0xb2, 0x91, 0xee, 0x94, 0xce, 0x94, 0x2c, 0x01, 0x62, 0xe2, 0xd1, 0x93, 0x89, + 0xff, 0x94, 0x47, 0x12, 0x2f, 0x1e, 0x0d, 0xf8, 0x47, 0x78, 0x34, 0x3b, 0x3b, 0x6d, 0xb7, 0x65, + 0x0b, 0xc4, 0x13, 0x33, 0xcc, 0xef, 0xfd, 0x3e, 0xde, 0x9b, 0x9d, 0xc2, 0x74, 0x85, 0xb2, 0x1a, + 0x65, 0x06, 0x77, 0x8d, 0xa3, 0xa5, 0x32, 0xe1, 0x78, 0xc9, 0x60, 0xa4, 0x71, 0x64, 0x57, 0x88, + 0x5e, 0x6f, 0x50, 0x4e, 0xd1, 0x7d, 0x1f, 0xa0, 0x73, 0x57, 0x97, 0x80, 0xd4, 0x63, 0x8b, 0x52, + 0xeb, 0x80, 0x18, 0xb8, 0x6e, 0x1b, 0xd8, 0x71, 0x28, 0xc7, 0xdc, 0xa6, 0x0e, 0xf3, 0x0b, 0x52, + 0xb3, 0x92, 0xb1, 0x8c, 0x19, 0x31, 0x70, 0xb9, 0x62, 0xb7, 0x89, 0xbd, 0x8d, 0x04, 0xa5, 0xae, + 0xca, 0x72, 0x57, 0x9e, 0x4d, 0x58, 0xd4, 0xa2, 0x62, 0x69, 0x78, 0x2b, 0xf9, 0xdf, 0xc5, 0x20, + 0xed, 0x61, 0x93, 0x34, 0x8e, 0xdb, 0x95, 0x75, 0x6c, 0xd9, 0x8e, 0xf0, 0xe0, 0x63, 0x35, 0x0e, + 0x68, 0x9d, 0xf0, 0x5d, 0x97, 0x15, 0x8e, 0x88, 0xc3, 0x4d, 0x72, 0xd8, 0x24, 0x8c, 0xa3, 0x49, + 0x18, 0x24, 0xde, 0x9e, 0x25, 0x95, 0x74, 0x6c, 0x61, 0xc4, 0x94, 0x3b, 0xb4, 0x06, 0xd0, 0x61, + 0x48, 0x46, 0xd3, 0xca, 0x42, 0x3c, 0x3b, 0xa7, 0xcb, 0xd8, 0x9e, 0x9c, 0x2e, 0xe4, 0x5a, 0xf1, + 0xf5, 0x1d, 0x6c, 0x11, 0xc9, 0x69, 0x06, 0x2a, 0xb5, 0x73, 0x05, 0xc6, 0xbb, 0x64, 0x59, 0x9d, + 0x3a, 0x8c, 0xa0, 0x79, 0x88, 0x71, 0xd7, 0x17, 0x8d, 0x67, 0x1f, 0xe8, 0x57, 0xfa, 0xa9, 0xef, + 0xba, 0xa6, 0x87, 0x40, 0xeb, 0x30, 0xca, 0xdd, 0x52, 0x43, 0xd6, 0xb1, 0x64, 0x54, 0x54, 0x3c, + 0xed, 0xb2, 0x22, 0x7a, 0x18, 0x28, 0x94, 0x60, 0x33, 0xce, 0xdb, 0x6b, 0x8f, 0x28, 0x98, 0x28, + 0x26, 0x12, 0xcd, 0xdf, 0x98, 0x48, 0x32, 0x05, 0x23, 0x11, 0x40, 0xb9, 0x06, 0xc5, 0xd5, 0x0a, + 0x66, 0xdc, 0x13, 0xf3, 0x1b, 0xf9, 0x08, 0x86, 0xb9, 0x5b, 0x2a, 0x1f, 0x73, 0xe2, 0xa5, 0x52, + 0x16, 0x46, 0xcd, 0x21, 0xee, 0xe6, 0xbc, 0x2d, 0x7a, 0x01, 0x03, 0x35, 0x5a, 0x25, 0xa2, 0x8b, + 0x77, 0xb3, 0xe9, 0x90, 0xb0, 0x6d, 0xbe, 0x4d, 0x5a, 0x25, 0xa6, 0x40, 0x6b, 0x1f, 0x61, 0xbc, + 0x4b, 0x46, 0x36, 0xae, 0x00, 0xf1, 0x40, 0x3f, 0x84, 0xd4, 0x6d, 0xdb, 0x01, 0x9d, 0x76, 0x68, + 0xcb, 0x70, 0xaf, 0x68, 0xd7, 0x9a, 0x07, 0x98, 0xb7, 0xc6, 0x86, 0x32, 0x10, 0xe5, 0xae, 0x24, + 0xec, 0x33, 0x91, 0x28, 0x77, 0xb5, 0xaf, 0x0a, 0x24, 0x3a, 0xa5, 0xd2, 0xd5, 0x2b, 0x18, 0xb6, + 0x30, 0x2b, 0xd9, 0xce, 0x1e, 0x95, 0x0c, 0x33, 0xfd, 0x2d, 0xad, 0x63, 0xb6, 0xe1, 0xec, 0x51, + 0x73, 0xc8, 0xf2, 0x17, 0x68, 0x19, 0x06, 0x1b, 0x84, 0x35, 0x0f, 0xb8, 0xbc, 0x68, 0xe9, 0xfe, + 0xb5, 0xa6, 0xc0, 0x99, 0x12, 0xaf, 0x69, 0x30, 0x2a, 0x6e, 0x57, 0x2b, 0x03, 0x82, 0x81, 0x7d, + 0xcc, 0xf6, 0x85, 0x87, 0x11, 0x53, 0xac, 0xb5, 0x33, 0x18, 0x93, 0x18, 0x69, 0xf6, 0x76, 0x41, + 0x7b, 0x3b, 0x1d, 0xfd, 0xbf, 0x4e, 0x2f, 0x9e, 0xc2, 0x58, 0xd7, 0x78, 0x91, 0x0a, 0xa9, 0x9c, + 0xb9, 0xbd, 0xb2, 0x9a, 0x5f, 0x29, 0xee, 0x96, 0x36, 0xb7, 0x57, 0x0b, 0xa5, 0x77, 0x5b, 0xc5, + 0x9d, 0x42, 0x7e, 0x63, 0x6d, 0xa3, 0xb0, 0x9a, 0x88, 0xa0, 0x24, 0x4c, 0xf4, 0x9c, 0xe7, 0xde, + 0x6e, 0xe7, 0xdf, 0x24, 0x14, 0xf4, 0x10, 0xc6, 0x7b, 0x4e, 0x8a, 0xef, 0xb7, 0xf2, 0x89, 0x68, + 0x48, 0xc9, 0x8a, 0x38, 0x89, 0x65, 0xff, 0xc6, 0x60, 0xa8, 0xe8, 0xbf, 0x5d, 0xe8, 0x04, 0x86, + 0x5b, 0x83, 0x43, 0x5a, 0x48, 0xee, 0x9e, 0x0b, 0x91, 0x9a, 0xbd, 0x16, 0x23, 0x2f, 0xd2, 0xdc, + 0x97, 0x9f, 0x7f, 0xbe, 0x47, 0xd3, 0xda, 0x94, 0x11, 0xf2, 0x68, 0x4a, 0xf0, 0x4b, 0x65, 0x11, + 0x1d, 0xc2, 0x1d, 0x31, 0x05, 0x34, 0x1d, 0xc2, 0x1a, 0x9c, 0x61, 0x2a, 0xdd, 0x1f, 0x20, 0x35, + 0x33, 0x42, 0x73, 0x1a, 0x3d, 0x31, 0xc2, 0x5e, 0x4c, 0x66, 0x9c, 0x78, 0x73, 0x3f, 0x43, 0x9f, + 0x21, 0x1e, 0xf8, 0x82, 0x50, 0xe6, 0xba, 0x0f, 0xaf, 0x23, 0x3f, 0x77, 0x13, 0x4c, 0x9a, 0x98, + 0x11, 0x26, 0xa6, 0xb4, 0xc9, 0x70, 0x13, 0x5e, 0xe6, 0x53, 0x88, 0x07, 0xde, 0xbe, 0x50, 0x03, + 0x57, 0x9f, 0xe4, 0x50, 0x03, 0x21, 0x4f, 0xa8, 0xa6, 0x0a, 0x03, 0x49, 0xd4, 0xc7, 0x40, 0xee, + 0xf5, 0x8f, 0x0b, 0x55, 0x39, 0xbf, 0x50, 0x95, 0xdf, 0x17, 0xaa, 0xf2, 0xed, 0x52, 0x8d, 0x9c, + 0x5f, 0xaa, 0x91, 0x5f, 0x97, 0x6a, 0xe4, 0x43, 0xc6, 0xb2, 0xf9, 0x7e, 0xb3, 0xac, 0x57, 0x68, + 0xad, 0x55, 0xeb, 0xff, 0x79, 0xc6, 0xaa, 0x9f, 0x0c, 0x7e, 0x5c, 0x27, 0x1e, 0x59, 0x79, 0x50, + 0xfc, 0x70, 0x3c, 0xff, 0x17, 0x00, 0x00, 0xff, 0xff, 0x96, 0xba, 0xfb, 0xcb, 0x0f, 0x07, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -423,6 +579,8 @@ type ServiceClient interface { Simulate(ctx context.Context, in *SimulateRequest, opts ...grpc.CallOption) (*SimulateResponse, error) // GetTx fetches a tx by hash. GetTx(ctx context.Context, in *GetTxRequest, opts ...grpc.CallOption) (*GetTxResponse, error) + // BroadcastTx broadcast transaction. + BroadcastTx(ctx context.Context, in *BroadcastTxRequest, opts ...grpc.CallOption) (*BroadcastTxResponse, error) // GetTxsEvent fetches txs by event. GetTxsEvent(ctx context.Context, in *GetTxsEventRequest, opts ...grpc.CallOption) (*GetTxsEventResponse, error) } @@ -453,6 +611,15 @@ func (c *serviceClient) GetTx(ctx context.Context, in *GetTxRequest, opts ...grp return out, nil } +func (c *serviceClient) BroadcastTx(ctx context.Context, in *BroadcastTxRequest, opts ...grpc.CallOption) (*BroadcastTxResponse, error) { + out := new(BroadcastTxResponse) + err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/BroadcastTx", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *serviceClient) GetTxsEvent(ctx context.Context, in *GetTxsEventRequest, opts ...grpc.CallOption) (*GetTxsEventResponse, error) { out := new(GetTxsEventResponse) err := c.cc.Invoke(ctx, "/cosmos.tx.v1beta1.Service/GetTxsEvent", in, out, opts...) @@ -468,6 +635,8 @@ type ServiceServer interface { Simulate(context.Context, *SimulateRequest) (*SimulateResponse, error) // GetTx fetches a tx by hash. GetTx(context.Context, *GetTxRequest) (*GetTxResponse, error) + // BroadcastTx broadcast transaction. + BroadcastTx(context.Context, *BroadcastTxRequest) (*BroadcastTxResponse, error) // GetTxsEvent fetches txs by event. GetTxsEvent(context.Context, *GetTxsEventRequest) (*GetTxsEventResponse, error) } @@ -482,6 +651,9 @@ func (*UnimplementedServiceServer) Simulate(ctx context.Context, req *SimulateRe func (*UnimplementedServiceServer) GetTx(ctx context.Context, req *GetTxRequest) (*GetTxResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTx not implemented") } +func (*UnimplementedServiceServer) BroadcastTx(ctx context.Context, req *BroadcastTxRequest) (*BroadcastTxResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BroadcastTx not implemented") +} func (*UnimplementedServiceServer) GetTxsEvent(ctx context.Context, req *GetTxsEventRequest) (*GetTxsEventResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTxsEvent not implemented") } @@ -526,6 +698,24 @@ func _Service_GetTx_Handler(srv interface{}, ctx context.Context, dec func(inter return interceptor(ctx, in, info, handler) } +func _Service_BroadcastTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BroadcastTxRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).BroadcastTx(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.tx.v1beta1.Service/BroadcastTx", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).BroadcastTx(ctx, req.(*BroadcastTxRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Service_GetTxsEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetTxsEventRequest) if err := dec(in); err != nil { @@ -556,6 +746,10 @@ var _Service_serviceDesc = grpc.ServiceDesc{ MethodName: "GetTx", Handler: _Service_GetTx_Handler, }, + { + MethodName: "BroadcastTx", + Handler: _Service_BroadcastTx_Handler, + }, { MethodName: "GetTxsEvent", Handler: _Service_GetTxsEvent_Handler, @@ -672,6 +866,76 @@ func (m *GetTxsEventResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *BroadcastTxRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BroadcastTxRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BroadcastTxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Mode != 0 { + i = encodeVarintService(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x10 + } + if len(m.TxBytes) > 0 { + i -= len(m.TxBytes) + copy(dAtA[i:], m.TxBytes) + i = encodeVarintService(dAtA, i, uint64(len(m.TxBytes))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *BroadcastTxResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BroadcastTxResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BroadcastTxResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TxResponse != nil { + { + size, err := m.TxResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintService(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *SimulateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -886,6 +1150,35 @@ func (m *GetTxsEventResponse) Size() (n int) { return n } +func (m *BroadcastTxRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TxBytes) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + if m.Mode != 0 { + n += 1 + sovService(uint64(m.Mode)) + } + return n +} + +func (m *BroadcastTxResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.TxResponse != nil { + l = m.TxResponse.Size() + n += 1 + l + sovService(uint64(l)) + } + return n +} + func (m *SimulateRequest) Size() (n int) { if m == nil { return 0 @@ -1230,6 +1523,201 @@ func (m *GetTxsEventResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *BroadcastTxRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BroadcastTxRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BroadcastTxRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TxBytes = append(m.TxBytes[:0], dAtA[iNdEx:postIndex]...) + if m.TxBytes == nil { + m.TxBytes = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) + } + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= BroadcastMode(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BroadcastTxResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BroadcastTxResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BroadcastTxResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TxResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TxResponse == nil { + m.TxResponse = &types.TxResponse{} + } + if err := m.TxResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipService(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthService + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *SimulateRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/types/tx/service.pb.gw.go b/types/tx/service.pb.gw.go index a7d5b56c5e05..6d94c423ecbd 100644 --- a/types/tx/service.pb.gw.go +++ b/types/tx/service.pb.gw.go @@ -31,18 +31,15 @@ var _ = runtime.String var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage -var ( - filter_Service_Simulate_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} -) - func request_Service_Simulate_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq SimulateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_Simulate_0); err != nil { + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -55,10 +52,11 @@ func local_request_Service_Simulate_0(ctx context.Context, marshaler runtime.Mar var protoReq SimulateRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Service_Simulate_0); err != nil { + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -121,6 +119,40 @@ func local_request_Service_GetTx_0(ctx context.Context, marshaler runtime.Marsha } +func request_Service_BroadcastTx_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BroadcastTxRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.BroadcastTx(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_BroadcastTx_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq BroadcastTxRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.BroadcastTx(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_Service_GetTxsEvent_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} ) @@ -203,6 +235,26 @@ func RegisterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, se }) + mux.Handle("POST", pattern_Service_BroadcastTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Service_BroadcastTx_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_BroadcastTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Service_GetTxsEvent_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -304,6 +356,26 @@ func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, cl }) + mux.Handle("POST", pattern_Service_BroadcastTx_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Service_BroadcastTx_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_BroadcastTx_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Service_GetTxsEvent_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -330,7 +402,9 @@ func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, cl var ( pattern_Service_Simulate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "simulate"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_Service_GetTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1, 1, 0, 4, 1, 5, 3}, []string{"cosmos", "tx", "v1beta1", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Service_GetTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "tx", "v1beta1", "txs", "hash"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_Service_BroadcastTx_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) pattern_Service_GetTxsEvent_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "tx", "v1beta1", "txs"}, "", runtime.AssumeColonVerbOpt(true))) ) @@ -340,5 +414,7 @@ var ( forward_Service_GetTx_0 = runtime.ForwardResponseMessage + forward_Service_BroadcastTx_0 = runtime.ForwardResponseMessage + forward_Service_GetTxsEvent_0 = runtime.ForwardResponseMessage ) diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index f9abae6b71fe..6fe54404beb7 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -151,7 +151,7 @@ func QueryTxRequestHandlerFn(clientCtx client.Context) http.HandlerFunc { rest.WriteErrorResponse(w, http.StatusNotFound, fmt.Sprintf("no transaction found with hash %s", hashHexStr)) } - err = checkSignModeError(clientCtx, output, "/cosmos/tx/v1beta1/tx/{txhash}") + err = checkSignModeError(clientCtx, output, "/cosmos/tx/v1beta1/txs/{txhash}") if err != nil { rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) diff --git a/x/auth/client/rest/rest_test.go b/x/auth/client/rest/rest_test.go index 671df87a8b9c..67c3cfc3fc1d 100644 --- a/x/auth/client/rest/rest_test.go +++ b/x/auth/client/rest/rest_test.go @@ -345,7 +345,7 @@ func (s *IntegrationTestSuite) broadcastReq(stdTx legacytx.StdTx, mode string) ( // testQueryIBCTx is a helper function to test querying txs which: // - show an error message on legacy REST endpoints // - succeed using gRPC -// In practise, we call this function on IBC txs. +// In practice, we call this function on IBC txs. func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.Command, args []string) { val := s.network.Validators[0] @@ -381,7 +381,7 @@ func (s *IntegrationTestSuite) testQueryIBCTx(txRes sdk.TxResponse, cmd *cobra.C } // try fetching the txn using gRPC req, it will fetch info since it has proto codec. - grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/tx/%s", val.APIAddress, txRes.TxHash)) + grpcJSON, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, txRes.TxHash)) s.Require().NoError(err) var getTxRes txtypes.GetTxResponse diff --git a/x/auth/tx/service.go b/x/auth/tx/service.go index 751c8fbb22b2..853d401beccc 100644 --- a/x/auth/tx/service.go +++ b/x/auth/tx/service.go @@ -49,6 +49,10 @@ const ( // TxsByEvents implements the ServiceServer.TxsByEvents RPC method. func (s txServer) GetTxsEvent(ctx context.Context, req *txtypes.GetTxsEventRequest) (*txtypes.GetTxsEventResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "request cannot be nil") + } + page, limit, err := pagination.ParsePagination(req.Pagination) if err != nil { return nil, err @@ -113,7 +117,7 @@ func (s txServer) GetTxsEvent(ctx context.Context, req *txtypes.GetTxsEventReque // Simulate implements the ServiceServer.Simulate RPC method. func (s txServer) Simulate(ctx context.Context, req *txtypes.SimulateRequest) (*txtypes.SimulateResponse, error) { - if req.Tx == nil { + if req == nil || req.Tx == nil { return nil, status.Error(codes.InvalidArgument, "invalid empty tx") } @@ -139,6 +143,10 @@ func (s txServer) Simulate(ctx context.Context, req *txtypes.SimulateRequest) (* // GetTx implements the ServiceServer.GetTx RPC method. func (s txServer) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtypes.GetTxResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "request cannot be nil") + } + // We get hash as a hex string in the request, convert it to bytes. hash, err := hex.DecodeString(req.Hash) if err != nil { @@ -170,6 +178,10 @@ func (s txServer) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtype }, nil } +func (s txServer) BroadcastTx(ctx context.Context, req *txtypes.BroadcastTxRequest) (*txtypes.BroadcastTxResponse, error) { + return client.TxServiceBroadcast(ctx, s.clientCtx, req) +} + // RegisterTxService registers the tx service on the gRPC router. func RegisterTxService( qrt gogogrpc.Server, diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go index 480529541794..341684acbe26 100644 --- a/x/auth/tx/service_test.go +++ b/x/auth/tx/service_test.go @@ -31,6 +31,7 @@ type IntegrationTestSuite struct { network *network.Network queryClient tx.ServiceClient + txRes sdk.TxResponse } func (s *IntegrationTestSuite) SetupSuite() { @@ -41,67 +42,14 @@ func (s *IntegrationTestSuite) SetupSuite() { s.cfg = cfg s.network = network.New(s.T(), cfg) - s.Require().NotNil(s.network) - _, err := s.network.WaitForHeight(1) - s.Require().NoError(err) - - s.queryClient = tx.NewServiceClient(s.network.Validators[0].ClientCtx) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} - -func (s IntegrationTestSuite) TestSimulate() { val := s.network.Validators[0] - // prepare txBuilder with msg - txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() - feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError( - txBuilder.SetMsgs(&banktypes.MsgSend{ - FromAddress: val.Address.String(), - ToAddress: val.Address.String(), - Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, - }), - ) - txBuilder.SetFeeAmount(feeAmount) - txBuilder.SetGasLimit(gasLimit) - txBuilder.SetMemo("foobar") - - // setup txFactory - txFactory := clienttx.Factory{}. - WithChainID(val.ClientCtx.ChainID). - WithKeybase(val.ClientCtx.Keyring). - WithTxConfig(val.ClientCtx.TxConfig). - WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) - - // Sign Tx. - err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false) - s.Require().NoError(err) - - // Convert the txBuilder to a tx.Tx. - protoTx, err := txBuilderToProtoTx(txBuilder) - s.Require().NoError(err) - - // Run the simulate gRPC query. - res, err := s.queryClient.Simulate( - context.Background(), - &tx.SimulateRequest{Tx: protoTx}, - ) + _, err := s.network.WaitForHeight(1) s.Require().NoError(err) - // Check the result and gas used are correct. - s.Require().Equal(len(res.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. - s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. -} - -func (s IntegrationTestSuite) TestGetTxEvents() { - val := s.network.Validators[0] + s.queryClient = tx.NewServiceClient(val.ClientCtx) // Create a new MsgSend tx from val to itself. out, err := bankcli.MsgSendExec( @@ -118,51 +66,161 @@ func (s IntegrationTestSuite) TestGetTxEvents() { fmt.Sprintf("--%s=foobar", flags.FlagMemo), ) s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) - s.Require().Equal(uint32(0), txRes.Code) + s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &s.txRes)) + s.Require().Equal(uint32(0), s.txRes.Code) s.Require().NoError(s.network.WaitForNextBlock()) +} - // Query the tx via gRPC empty params. - _, err = s.queryClient.GetTxsEvent( - context.Background(), - &tx.GetTxsEventRequest{}, - ) - s.Require().Error(err) - - // Query the tx via gRPC. - grpcRes, err := s.queryClient.GetTxsEvent( - context.Background(), - &tx.GetTxsEventRequest{ - Events: []string{"message.action=send"}, - Pagination: &query.PageRequest{ - CountTotal: false, - Offset: 0, - Limit: 1, +func (s *IntegrationTestSuite) TearDownSuite() { + s.T().Log("tearing down integration test suite") + s.network.Cleanup() +} + +func (s IntegrationTestSuite) TestSimulateTx_GRPC() { + txBuilder := s.mkTxBuilder() + // Convert the txBuilder to a tx.Tx. + protoTx, err := txBuilderToProtoTx(txBuilder) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.SimulateRequest{}, true, "invalid empty tx"}, + {"valid request", &tx.SimulateRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + res, err := s.queryClient.Simulate(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + // Check the result and gas used are correct. + s.Require().Equal(len(res.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } +} + +func (s IntegrationTestSuite) TestSimulateTx_GRPCGateway() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + // Convert the txBuilder to a tx.Tx. + protoTx, err := txBuilderToProtoTx(txBuilder) + s.Require().NoError(err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.SimulateRequest{}, true, "invalid empty tx"}, + {"valid request", &tx.SimulateRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + req, err := val.ClientCtx.JSONMarshaler.MarshalJSON(tc.req) + s.Require().NoError(err) + res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", val.APIAddress), "application/json", req) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tx.SimulateResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + // Check the result and gas used are correct. + s.Require().Equal(len(result.GetResult().GetEvents()), 4) // 1 transfer, 3 messages. + s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { + testCases := []struct { + name string + req *tx.GetTxsEventRequest + expErr bool + expErrMsg string + }{ + { + "nil request", + nil, + true, "request cannot be nil", + }, + { + "empty request", + &tx.GetTxsEventRequest{}, + true, "must declare at least one event to search", + }, + { + "request with dummy event", + &tx.GetTxsEventRequest{Events: []string{"foobar"}}, + true, "event foobar should be of the format: {eventType}.{eventAttribute}={value}", + }, + { + "without pagination", + &tx.GetTxsEventRequest{ + Events: []string{"message.action=send"}, }, + false, "", }, - ) - s.Require().NoError(err) - s.Require().Equal(len(grpcRes.Txs), 1) - s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) - - // Query the tx via gRPC without pagination. This used to panic, see - // https://github.com/cosmos/cosmos-sdk/issues/8038. - grpcRes, err = s.queryClient.GetTxsEvent( - context.Background(), - &tx.GetTxsEventRequest{ - Events: []string{"message.action=send"}, + { + "with pagination", + &tx.GetTxsEventRequest{ + Events: []string{"message.action=send"}, + Pagination: &query.PageRequest{ + CountTotal: false, + Offset: 0, + Limit: 1, + }, + }, + false, "", }, - ) - // TODO Once https://github.com/cosmos/cosmos-sdk/pull/8029 is merged, this - // should not error anymore. - s.Require().NoError(err) + { + "with multi events", + &tx.GetTxsEventRequest{ + Events: []string{"message.action=send", "message.module=bank"}, + }, + false, "", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + // Query the tx via gRPC. + grpcRes, err := s.queryClient.GetTxsEvent(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().GreaterOrEqual(len(grpcRes.Txs), 1) + s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) + } + }) + } +} - rpcTests := []struct { +func (s IntegrationTestSuite) TestGetTxEvents_GRPCGateway() { + val := s.network.Validators[0] + testCases := []struct { name string url string - expectErr bool + expErr bool expErrMsg string }{ { @@ -196,15 +254,16 @@ func (s IntegrationTestSuite) TestGetTxEvents() { "", }, } - for _, tc := range rpcTests { + for _, tc := range testCases { s.Run(tc.name, func() { res, err := rest.GetRequest(tc.url) s.Require().NoError(err) - if tc.expectErr { + if tc.expErr { s.Require().Contains(string(res), tc.expErrMsg) } else { var result tx.GetTxsEventResponse - val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) s.Require().GreaterOrEqual(len(result.Txs), 1) s.Require().Equal("foobar", result.Txs[0].Body.Memo) s.Require().NotZero(result.TxResponses[0].Height) @@ -213,52 +272,188 @@ func (s IntegrationTestSuite) TestGetTxEvents() { } } -func (s IntegrationTestSuite) TestGetTx() { +func (s IntegrationTestSuite) TestGetTx_GRPC() { + testCases := []struct { + name string + req *tx.GetTxRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.GetTxRequest{}, true, "transaction hash cannot be empty"}, + {"request with dummy hash", &tx.GetTxRequest{Hash: "deadbeef"}, true, "tx (DEADBEEF) not found"}, + {"good request", &tx.GetTxRequest{Hash: s.txRes.TxHash}, false, ""}, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + // Query the tx via gRPC. + grpcRes, err := s.queryClient.GetTx(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().Equal("foobar", grpcRes.Tx.Body.Memo) + } + }) + } +} + +func (s IntegrationTestSuite) TestGetTx_GRPCGateway() { val := s.network.Validators[0] + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", val.APIAddress), + true, "transaction hash cannot be empty", + }, + { + "dummy hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "deadbeef"), + true, "tx (DEADBEEF) not found", + }, + { + "good hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, s.txRes.TxHash), + false, "", + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + res, err := rest.GetRequest(tc.url) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tx.GetTxResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal("foobar", result.Tx.Body.Memo) + s.Require().NotZero(result.TxResponse.Height) + } + }) + } +} - // Create a new MsgSend tx from val to itself. - out, err := bankcli.MsgSendExec( - val.ClientCtx, - val.Address, - val.Address, - sdk.NewCoins( - sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), - ), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=foobar", flags.FlagMemo), - ) +func (s IntegrationTestSuite) TestBroadcastTx_GRPC() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) s.Require().NoError(err) - var txRes sdk.TxResponse - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txRes)) - s.Require().Equal(uint32(0), txRes.Code) - s.Require().NoError(s.network.WaitForNextBlock()) + testCases := []struct { + name string + req *tx.BroadcastTxRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, + {"no mode", &tx.BroadcastTxRequest{ + TxBytes: txBytes, + }, true, "supported types: sync, async, block"}, + {"valid request", &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, false, ""}, + } - // Query the tx via gRPC. - grpcRes, err := s.queryClient.GetTx( - context.Background(), - &tx.GetTxRequest{Hash: txRes.TxHash}, - ) - s.Require().NoError(err) - s.Require().Equal("foobar", grpcRes.Tx.Body.Memo) - s.Require().NotZero(grpcRes.TxResponse.Height) + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + grpcRes, err := s.queryClient.BroadcastTx(context.Background(), tc.req) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) + } + }) + } +} - // Query the tx via grpc-gateway. - restRes, err := rest.GetRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/tx/%s", val.APIAddress, txRes.TxHash)) +func (s IntegrationTestSuite) TestBroadcastTx_GRPCGateway() { + val := s.network.Validators[0] + txBuilder := s.mkTxBuilder() + txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) s.Require().NoError(err) - var getTxRes tx.GetTxResponse - s.Require().NoError(val.ClientCtx.JSONMarshaler.UnmarshalJSON(restRes, &getTxRes)) - s.Require().Equal("foobar", grpcRes.Tx.Body.Memo) - s.Require().NotZero(grpcRes.TxResponse.Height) + + testCases := []struct { + name string + req *tx.BroadcastTxRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, + {"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async, block"}, + {"valid request", &tx.BroadcastTxRequest{ + Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, false, ""}, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + req, err := val.ClientCtx.JSONMarshaler.MarshalJSON(tc.req) + s.Require().NoError(err) + res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), "application/json", req) + s.Require().NoError(err) + if tc.expErr { + s.Require().Contains(string(res), tc.expErrMsg) + } else { + var result tx.BroadcastTxResponse + err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res, &result) + s.Require().NoError(err) + s.Require().Equal(uint32(0), result.TxResponse.Code) + } + }) + } } func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } +func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder { + val := s.network.Validators[0] + s.Require().NoError(s.network.WaitForNextBlock()) + + // prepare txBuilder with msg + txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + s.Require().NoError( + txBuilder.SetMsgs(&banktypes.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, + }), + ) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + + // setup txFactory + txFactory := clienttx.Factory{}. + WithChainID(val.ClientCtx.ChainID). + WithKeybase(val.ClientCtx.Keyring). + WithTxConfig(val.ClientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false) + s.Require().NoError(err) + + return txBuilder +} + // txBuilderToProtoTx converts a txBuilder into a proto tx.Tx. func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) { // nolint intoAnyTx, ok := txBuilder.(codectypes.IntoAny)