From 548e21aec2b3f2a1fa295f9a501bd201c5604f5b Mon Sep 17 00:00:00 2001
From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com>
Date: Mon, 5 Aug 2024 16:27:39 +0200
Subject: [PATCH] feat: gRPC server for TxStatus endpoint (backport #3754)
(#3755)
Closes: https://github.com/celestiaorg/celestia-app/issues/3753
This in some ways ties into
https://github.com/celestiaorg/celestia-app/issues/3421
This is an
automatic backport of pull request #3754 done by
[Mergify](https://mergify.com).
---------
Co-authored-by: Callum Waters
---
app/app.go | 3 +
app/grpc/tx/server.go | 81 ++++
app/grpc/tx/tx.pb.go | 703 +++++++++++++++++++++++++++++
app/grpc/tx/tx.pb.gw.go | 189 ++++++++
app/test/std_sdk_test.go | 41 ++
buf.yaml | 6 -
proto/celestia/core/v1/tx/tx.proto | 38 ++
7 files changed, 1055 insertions(+), 6 deletions(-)
create mode 100644 app/grpc/tx/server.go
create mode 100644 app/grpc/tx/tx.pb.go
create mode 100644 app/grpc/tx/tx.pb.gw.go
create mode 100644 proto/celestia/core/v1/tx/tx.proto
diff --git a/app/app.go b/app/app.go
index 735ace785b..d3e9c1ddf7 100644
--- a/app/app.go
+++ b/app/app.go
@@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
+ celestiatx "github.com/celestiaorg/celestia-app/app/grpc/tx"
"github.com/celestiaorg/celestia-app/x/mint"
mintkeeper "github.com/celestiaorg/celestia-app/x/mint/keeper"
minttypes "github.com/celestiaorg/celestia-app/x/mint/types"
@@ -682,11 +683,13 @@ func (app *App) RegisterAPIRoutes(apiSvr *api.Server, _ config.APIConfig) {
// Register the
ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
+ celestiatx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
}
// RegisterTxService implements the Application.RegisterTxService method.
func (app *App) RegisterTxService(clientCtx client.Context) {
authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
+ celestiatx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry)
}
// RegisterTendermintService implements the Application.RegisterTendermintService method.
diff --git a/app/grpc/tx/server.go b/app/grpc/tx/server.go
new file mode 100644
index 0000000000..93b3c9da36
--- /dev/null
+++ b/app/grpc/tx/server.go
@@ -0,0 +1,81 @@
+package tx
+
+import (
+ "context"
+ "encoding/hex"
+
+ "github.com/cosmos/cosmos-sdk/client"
+ codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+ gogogrpc "github.com/gogo/protobuf/grpc"
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// RegisterTxService registers the tx service on the gRPC router.
+func RegisterTxService(
+ qrt gogogrpc.Server,
+ clientCtx client.Context,
+ interfaceRegistry codectypes.InterfaceRegistry,
+) {
+ RegisterTxServer(
+ qrt,
+ NewTxServer(clientCtx, interfaceRegistry),
+ )
+}
+
+// RegisterGRPCGatewayRoutes mounts the tx service's GRPC-gateway routes on the
+// given Mux.
+func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) {
+ err := RegisterTxHandlerClient(context.Background(), mux, NewTxClient(clientConn))
+ if err != nil {
+ panic(err)
+ }
+}
+
+var _ TxServer = &txServer{}
+
+type txServer struct {
+ clientCtx client.Context
+ interfaceRegistry codectypes.InterfaceRegistry
+}
+
+func NewTxServer(clientCtx client.Context, interfaceRegistry codectypes.InterfaceRegistry) TxServer {
+ return &txServer{
+ clientCtx: clientCtx,
+ interfaceRegistry: interfaceRegistry,
+ }
+}
+
+// TxStatus implements the TxServer.TxStatus method proxying to the underlying celestia-core RPC server
+func (s *txServer) TxStatus(ctx context.Context, req *TxStatusRequest) (*TxStatusResponse, error) {
+ if req == nil {
+ return nil, status.Error(codes.InvalidArgument, "request cannot be nil")
+ }
+
+ if len(req.TxId) == 0 {
+ return nil, status.Error(codes.InvalidArgument, "tx id cannot be empty")
+ }
+
+ node, err := s.clientCtx.GetNode()
+ if err != nil {
+ return nil, err
+ }
+
+ txID, err := hex.DecodeString(req.TxId)
+ if err != nil {
+ return nil, status.Errorf(codes.InvalidArgument, "invalid tx id: %s", err)
+ }
+
+ resTx, err := node.TxStatus(ctx, txID)
+ if err != nil {
+ return nil, err
+ }
+
+ return &TxStatusResponse{
+ Height: resTx.Height,
+ Index: resTx.Index,
+ ExecutionCode: resTx.ExecutionCode,
+ Status: resTx.Status,
+ }, nil
+}
diff --git a/app/grpc/tx/tx.pb.go b/app/grpc/tx/tx.pb.go
new file mode 100644
index 0000000000..c962ec2dd5
--- /dev/null
+++ b/app/grpc/tx/tx.pb.go
@@ -0,0 +1,703 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: celestia/core/v1/tx/tx.proto
+
+package tx
+
+import (
+ context "context"
+ fmt "fmt"
+ grpc1 "github.com/gogo/protobuf/grpc"
+ proto "github.com/gogo/protobuf/proto"
+ _ "google.golang.org/genproto/googleapis/api/annotations"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// TxStatusRequest is the request type for the TxStatus gRPC method.
+type TxStatusRequest struct {
+ // this is the hex encoded transaction hash (should be 64 bytes long)
+ TxId string `protobuf:"bytes,1,opt,name=tx_id,json=txId,proto3" json:"tx_id,omitempty"`
+}
+
+func (m *TxStatusRequest) Reset() { *m = TxStatusRequest{} }
+func (m *TxStatusRequest) String() string { return proto.CompactTextString(m) }
+func (*TxStatusRequest) ProtoMessage() {}
+func (*TxStatusRequest) Descriptor() ([]byte, []int) {
+ return fileDescriptor_7d8b070565b0dcb6, []int{0}
+}
+func (m *TxStatusRequest) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *TxStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_TxStatusRequest.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 *TxStatusRequest) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_TxStatusRequest.Merge(m, src)
+}
+func (m *TxStatusRequest) XXX_Size() int {
+ return m.Size()
+}
+func (m *TxStatusRequest) XXX_DiscardUnknown() {
+ xxx_messageInfo_TxStatusRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TxStatusRequest proto.InternalMessageInfo
+
+func (m *TxStatusRequest) GetTxId() string {
+ if m != nil {
+ return m.TxId
+ }
+ return ""
+}
+
+// TxStatusResponse is the response type for the TxStatus gRPC method.
+type TxStatusResponse struct {
+ Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
+ Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"`
+ // execution_code is returned when the transaction has been committed
+ // and returns whether it was successful or errored. A non zero
+ // execution code indicated an error.
+ ExecutionCode uint32 `protobuf:"varint,3,opt,name=execution_code,json=executionCode,proto3" json:"execution_code,omitempty"`
+ // status is the status of the transaction.
+ Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`
+}
+
+func (m *TxStatusResponse) Reset() { *m = TxStatusResponse{} }
+func (m *TxStatusResponse) String() string { return proto.CompactTextString(m) }
+func (*TxStatusResponse) ProtoMessage() {}
+func (*TxStatusResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_7d8b070565b0dcb6, []int{1}
+}
+func (m *TxStatusResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *TxStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_TxStatusResponse.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 *TxStatusResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_TxStatusResponse.Merge(m, src)
+}
+func (m *TxStatusResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *TxStatusResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_TxStatusResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TxStatusResponse proto.InternalMessageInfo
+
+func (m *TxStatusResponse) GetHeight() int64 {
+ if m != nil {
+ return m.Height
+ }
+ return 0
+}
+
+func (m *TxStatusResponse) GetIndex() uint32 {
+ if m != nil {
+ return m.Index
+ }
+ return 0
+}
+
+func (m *TxStatusResponse) GetExecutionCode() uint32 {
+ if m != nil {
+ return m.ExecutionCode
+ }
+ return 0
+}
+
+func (m *TxStatusResponse) GetStatus() string {
+ if m != nil {
+ return m.Status
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*TxStatusRequest)(nil), "celestia.core.v1.tx.TxStatusRequest")
+ proto.RegisterType((*TxStatusResponse)(nil), "celestia.core.v1.tx.TxStatusResponse")
+}
+
+func init() { proto.RegisterFile("celestia/core/v1/tx/tx.proto", fileDescriptor_7d8b070565b0dcb6) }
+
+var fileDescriptor_7d8b070565b0dcb6 = []byte{
+ // 322 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x51, 0xcd, 0x4a, 0x03, 0x31,
+ 0x18, 0x6c, 0xfa, 0x87, 0x06, 0xaa, 0x92, 0x8a, 0x2c, 0xa5, 0x84, 0x52, 0x5a, 0xe9, 0xc5, 0x84,
+ 0xea, 0x1b, 0xe8, 0xa9, 0xd7, 0xb5, 0x27, 0x2f, 0x65, 0xbb, 0x1b, 0xb6, 0x81, 0xba, 0x89, 0x9b,
+ 0x6f, 0x4b, 0x40, 0x8a, 0xa0, 0x2f, 0x20, 0xf8, 0x52, 0x1e, 0x0b, 0x5e, 0x3c, 0x4a, 0xeb, 0x83,
+ 0x48, 0xd3, 0x76, 0x05, 0x29, 0x78, 0x08, 0x64, 0x32, 0x93, 0x6f, 0x32, 0x13, 0xdc, 0x0c, 0xc5,
+ 0x54, 0x18, 0x90, 0x01, 0x0f, 0x55, 0x2a, 0xf8, 0xac, 0xcf, 0xc1, 0x72, 0xb0, 0x4c, 0xa7, 0x0a,
+ 0x14, 0xa9, 0xef, 0x58, 0xb6, 0x66, 0xd9, 0xac, 0xcf, 0xc0, 0x36, 0x9a, 0xb1, 0x52, 0xf1, 0x54,
+ 0xf0, 0x40, 0x4b, 0x1e, 0x24, 0x89, 0x82, 0x00, 0xa4, 0x4a, 0xcc, 0xe6, 0x4a, 0xfb, 0x1c, 0x1f,
+ 0x0f, 0xed, 0x2d, 0x04, 0x90, 0x19, 0x5f, 0x3c, 0x64, 0xc2, 0x00, 0xa9, 0xe3, 0x0a, 0xd8, 0x91,
+ 0x8c, 0x3c, 0xd4, 0x42, 0xbd, 0x43, 0xbf, 0x0c, 0x76, 0x10, 0xb5, 0x9f, 0xf0, 0xc9, 0xaf, 0xce,
+ 0x68, 0x95, 0x18, 0x41, 0xce, 0x70, 0x75, 0x22, 0x64, 0x3c, 0x01, 0xa7, 0x2c, 0xf9, 0x5b, 0x44,
+ 0x4e, 0x71, 0x45, 0x26, 0x91, 0xb0, 0x5e, 0xb1, 0x85, 0x7a, 0x35, 0x7f, 0x03, 0x48, 0x17, 0x1f,
+ 0x09, 0x2b, 0xc2, 0x6c, 0xed, 0x3e, 0x0a, 0x55, 0x24, 0xbc, 0x92, 0xa3, 0x6b, 0xf9, 0xe9, 0x8d,
+ 0x8a, 0xdc, 0x50, 0xe3, 0x6c, 0xbc, 0xb2, 0xb3, 0xdf, 0xa2, 0xcb, 0x17, 0x84, 0x8b, 0x43, 0x4b,
+ 0xe6, 0xf8, 0x60, 0xf7, 0x0e, 0xd2, 0x61, 0x7b, 0xf2, 0xb2, 0x3f, 0x71, 0x1a, 0xdd, 0x7f, 0x54,
+ 0x9b, 0x30, 0xed, 0xce, 0xf3, 0xc7, 0xf7, 0x5b, 0x91, 0x92, 0x26, 0xdf, 0x57, 0xf1, 0xa3, 0x6b,
+ 0x64, 0x7e, 0x3d, 0x78, 0x5f, 0x52, 0xb4, 0x58, 0x52, 0xf4, 0xb5, 0xa4, 0xe8, 0x75, 0x45, 0x0b,
+ 0x8b, 0x15, 0x2d, 0x7c, 0xae, 0x68, 0xe1, 0x8e, 0xc7, 0x12, 0x26, 0xd9, 0x98, 0x85, 0xea, 0x3e,
+ 0x9f, 0xa0, 0xd2, 0x38, 0xdf, 0x5f, 0x04, 0x5a, 0xf3, 0xf5, 0x8a, 0x53, 0x1d, 0x72, 0xb0, 0xe3,
+ 0xaa, 0xfb, 0x80, 0xab, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x2a, 0x51, 0xff, 0xd3, 0x01,
+ 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// TxClient is the client API for Tx service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type TxClient interface {
+ // TxStatus returns the status of a transaction. There are four possible states:
+ // - Committed
+ // - Pending
+ // - Evicted
+ // - Unknown
+ TxStatus(ctx context.Context, in *TxStatusRequest, opts ...grpc.CallOption) (*TxStatusResponse, error)
+}
+
+type txClient struct {
+ cc grpc1.ClientConn
+}
+
+func NewTxClient(cc grpc1.ClientConn) TxClient {
+ return &txClient{cc}
+}
+
+func (c *txClient) TxStatus(ctx context.Context, in *TxStatusRequest, opts ...grpc.CallOption) (*TxStatusResponse, error) {
+ out := new(TxStatusResponse)
+ err := c.cc.Invoke(ctx, "/celestia.core.v1.tx.Tx/TxStatus", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// TxServer is the server API for Tx service.
+type TxServer interface {
+ // TxStatus returns the status of a transaction. There are four possible states:
+ // - Committed
+ // - Pending
+ // - Evicted
+ // - Unknown
+ TxStatus(context.Context, *TxStatusRequest) (*TxStatusResponse, error)
+}
+
+// UnimplementedTxServer can be embedded to have forward compatible implementations.
+type UnimplementedTxServer struct {
+}
+
+func (*UnimplementedTxServer) TxStatus(ctx context.Context, req *TxStatusRequest) (*TxStatusResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method TxStatus not implemented")
+}
+
+func RegisterTxServer(s grpc1.Server, srv TxServer) {
+ s.RegisterService(&_Tx_serviceDesc, srv)
+}
+
+func _Tx_TxStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(TxStatusRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(TxServer).TxStatus(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/celestia.core.v1.tx.Tx/TxStatus",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(TxServer).TxStatus(ctx, req.(*TxStatusRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _Tx_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "celestia.core.v1.tx.Tx",
+ HandlerType: (*TxServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "TxStatus",
+ Handler: _Tx_TxStatus_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "celestia/core/v1/tx/tx.proto",
+}
+
+func (m *TxStatusRequest) 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 *TxStatusRequest) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *TxStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.TxId) > 0 {
+ i -= len(m.TxId)
+ copy(dAtA[i:], m.TxId)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.TxId)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *TxStatusResponse) 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 *TxStatusResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *TxStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ if len(m.Status) > 0 {
+ i -= len(m.Status)
+ copy(dAtA[i:], m.Status)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.Status)))
+ i--
+ dAtA[i] = 0x22
+ }
+ if m.ExecutionCode != 0 {
+ i = encodeVarintTx(dAtA, i, uint64(m.ExecutionCode))
+ i--
+ dAtA[i] = 0x18
+ }
+ if m.Index != 0 {
+ i = encodeVarintTx(dAtA, i, uint64(m.Index))
+ i--
+ dAtA[i] = 0x10
+ }
+ if m.Height != 0 {
+ i = encodeVarintTx(dAtA, i, uint64(m.Height))
+ i--
+ dAtA[i] = 0x8
+ }
+ return len(dAtA) - i, nil
+}
+
+func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
+ offset -= sovTx(v)
+ base := offset
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *TxStatusRequest) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.TxId)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ return n
+}
+
+func (m *TxStatusResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ if m.Height != 0 {
+ n += 1 + sovTx(uint64(m.Height))
+ }
+ if m.Index != 0 {
+ n += 1 + sovTx(uint64(m.Index))
+ }
+ if m.ExecutionCode != 0 {
+ n += 1 + sovTx(uint64(m.ExecutionCode))
+ }
+ l = len(m.Status)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ return n
+}
+
+func sovTx(x uint64) (n int) {
+ return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTx(x uint64) (n int) {
+ return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *TxStatusRequest) 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 ErrIntOverflowTx
+ }
+ 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: TxStatusRequest: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: TxStatusRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field TxId", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthTx
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthTx
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.TxId = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipTx(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthTx
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func (m *TxStatusResponse) 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 ErrIntOverflowTx
+ }
+ 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: TxStatusResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: TxStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType)
+ }
+ m.Height = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.Height |= int64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
+ }
+ m.Index = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.Index |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ExecutionCode", wireType)
+ }
+ m.ExecutionCode = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.ExecutionCode |= uint32(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthTx
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthTx
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Status = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipTx(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthTx
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipTx(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthTx
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupTx
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthTx
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowTx = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/app/grpc/tx/tx.pb.gw.go b/app/grpc/tx/tx.pb.gw.go
new file mode 100644
index 0000000000..8cec56bc0a
--- /dev/null
+++ b/app/grpc/tx/tx.pb.gw.go
@@ -0,0 +1,189 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: celestia/core/v1/tx/tx.proto
+
+/*
+Package tx is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package tx
+
+import (
+ "context"
+ "io"
+ "net/http"
+
+ "github.com/golang/protobuf/descriptor"
+ "github.com/golang/protobuf/proto"
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "github.com/grpc-ecosystem/grpc-gateway/utilities"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+func request_Tx_TxStatus_0(ctx context.Context, marshaler runtime.Marshaler, client TxClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq TxStatusRequest
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["tx_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "tx_id")
+ }
+
+ protoReq.TxId, err = runtime.String(val)
+
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "tx_id", err)
+ }
+
+ msg, err := client.TxStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_Tx_TxStatus_0(ctx context.Context, marshaler runtime.Marshaler, server TxServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq TxStatusRequest
+ var metadata runtime.ServerMetadata
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["tx_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "tx_id")
+ }
+
+ protoReq.TxId, err = runtime.String(val)
+
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "tx_id", err)
+ }
+
+ msg, err := server.TxStatus(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+// RegisterTxHandlerServer registers the http handlers for service Tx to "mux".
+// UnaryRPC :call TxServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterTxHandlerFromEndpoint instead.
+func RegisterTxHandlerServer(ctx context.Context, mux *runtime.ServeMux, server TxServer) error {
+
+ mux.Handle("GET", pattern_Tx_TxStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ 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_Tx_TxStatus_0(rctx, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Tx_TxStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+// RegisterTxHandlerFromEndpoint is same as RegisterTxHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterTxHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+ conn, err := grpc.Dial(endpoint, opts...)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ return
+ }
+ go func() {
+ <-ctx.Done()
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ }()
+ }()
+
+ return RegisterTxHandler(ctx, mux, conn)
+}
+
+// RegisterTxHandler registers the http handlers for service Tx to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterTxHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return RegisterTxHandlerClient(ctx, mux, NewTxClient(conn))
+}
+
+// RegisterTxHandlerClient registers the http handlers for service Tx
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "TxClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "TxClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "TxClient" to call the correct interceptors.
+func RegisterTxHandlerClient(ctx context.Context, mux *runtime.ServeMux, client TxClient) error {
+
+ mux.Handle("GET", pattern_Tx_TxStatus_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_Tx_TxStatus_0(rctx, inboundMarshaler, client, req, pathParams)
+ ctx = runtime.NewServerMetadataContext(ctx, md)
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_Tx_TxStatus_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+var (
+ pattern_Tx_TxStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"celestia", "core", "v1", "tx", "tx_id"}, "", runtime.AssumeColonVerbOpt(false)))
+)
+
+var (
+ forward_Tx_TxStatus_0 = runtime.ForwardResponseMessage
+)
diff --git a/app/test/std_sdk_test.go b/app/test/std_sdk_test.go
index ca85e9ced2..58a0d0e4e1 100644
--- a/app/test/std_sdk_test.go
+++ b/app/test/std_sdk_test.go
@@ -7,7 +7,9 @@ import (
"github.com/celestiaorg/celestia-app/app"
"github.com/celestiaorg/celestia-app/app/encoding"
+ "github.com/celestiaorg/celestia-app/app/grpc/tx"
"github.com/celestiaorg/celestia-app/test/util/testnode"
+ nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/testutil/mock"
@@ -304,3 +306,42 @@ func getAddress(account string, kr keyring.Keyring) sdk.AccAddress {
}
return addr
}
+
+func (s *StandardSDKIntegrationTestSuite) TestGRPCQueries() {
+ t := s.T()
+ t.Run("testnode can query local min gas price", func(t *testing.T) {
+ serviceClient := nodeservice.NewServiceClient(s.cctx.GRPCClient)
+ resp, err := serviceClient.Config(s.cctx.GoContext(), &nodeservice.ConfigRequest{})
+ require.NoError(t, err)
+ want := ""
+ assert.Equal(t, want, resp.MinimumGasPrice)
+ })
+
+ t.Run("testnode can query tx status", func(t *testing.T) {
+ // Create a dummy tx hash
+ dummyTxHash := "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
+
+ // Create a new tx client
+ txClient := tx.NewTxClient(s.cctx.GRPCClient)
+
+ // Query for the tx status
+ resp, err := txClient.TxStatus(s.cctx.GoContext(), &tx.TxStatusRequest{
+ TxId: dummyTxHash,
+ })
+ require.NoError(t, err)
+ assert.Equal(t, resp.Status, "UNKNOWN")
+
+ fromAddr := s.unusedAccount()
+ toAddr := s.unusedAccount()
+
+ msg := banktypes.NewMsgSend(getAddress(fromAddr, s.cctx.Keyring), getAddress(toAddr, s.cctx.Keyring), sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(1))))
+ res, err := testnode.SignAndBroadcastTx(s.ecfg, s.cctx.Context, fromAddr, msg)
+ require.NoError(t, err)
+
+ resp, err = txClient.TxStatus(s.cctx.GoContext(), &tx.TxStatusRequest{
+ TxId: res.TxHash,
+ })
+ require.NoError(t, err)
+ assert.Equal(t, resp.Status, "PENDING")
+ })
+}
diff --git a/buf.yaml b/buf.yaml
index 87c641bedb..4c13e62fc4 100644
--- a/buf.yaml
+++ b/buf.yaml
@@ -31,9 +31,3 @@ lint:
breaking:
use:
- FILE
- ignore:
- - tendermint
- - gogoproto
- - cosmos_proto
- - google
- - confio
diff --git a/proto/celestia/core/v1/tx/tx.proto b/proto/celestia/core/v1/tx/tx.proto
new file mode 100644
index 0000000000..f4a8301606
--- /dev/null
+++ b/proto/celestia/core/v1/tx/tx.proto
@@ -0,0 +1,38 @@
+syntax = "proto3";
+package celestia.core.v1.tx;
+
+import "google/api/annotations.proto";
+
+option go_package = "github.com/celestiaorg/celestia-app/app/grpc/tx";
+
+// Service defines a gRPC service for interacting with transactions.
+service Tx {
+ // TxStatus returns the status of a transaction. There are four possible states:
+ // - Committed
+ // - Pending
+ // - Evicted
+ // - Unknown
+ rpc TxStatus(TxStatusRequest) returns (TxStatusResponse) {
+ option (google.api.http) = {
+ get: "/celestia/core/v1/tx/{tx_id}"
+ };
+ }
+}
+
+// TxStatusRequest is the request type for the TxStatus gRPC method.
+message TxStatusRequest {
+ // this is the hex encoded transaction hash (should be 64 bytes long)
+ string tx_id = 1;
+}
+
+// TxStatusResponse is the response type for the TxStatus gRPC method.
+message TxStatusResponse {
+ int64 height = 1;
+ uint32 index = 2;
+ // execution_code is returned when the transaction has been committed
+ // and returns whether it was successful or errored. A non zero
+ // execution code indicated an error.
+ uint32 execution_code = 3;
+ // status is the status of the transaction.
+ string status = 4;
+}
\ No newline at end of file