Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetTx gRPC endpoint #7688

Merged
merged 25 commits into from
Oct 30, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions baseapp/grpcrouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"google.golang.org/grpc/encoding"
"google.golang.org/grpc/encoding/proto"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/grpc/reflection"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -111,13 +112,14 @@ func (qrt *GRPCQueryRouter) SetInterfaceRegistry(interfaceRegistry codectypes.In
)
}

// RegisterSimulateService registers the simulate service on the gRPC router.
func (qrt *GRPCQueryRouter) RegisterSimulateService(
// RegisterTxService registers the tx service on the gRPC router.
func (qrt *GRPCQueryRouter) RegisterTxService(
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
clientCtx client.Context,
simulateFn tx.BaseAppSimulateFn,
interfaceRegistry codectypes.InterfaceRegistry,
) {
tx.RegisterServiceServer(
qrt,
tx.NewTxServer(simulateFn, interfaceRegistry),
tx.NewTxServer(clientCtx, simulateFn, interfaceRegistry),
)
}
16 changes: 8 additions & 8 deletions proto/cosmos/tx/v1beta1/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ service Service {
option (google.api.http).post = "/cosmos/tx/v1beta1/simulate";
}
// Simulate simulates executing a transaction for estimating gas usage.
rpc TxByHash(TxByHashRequest) returns (TxByHashResponse) {
option (google.api.http).get = "/cosmos/tx/v1beta1/txByHash";
rpc GetTx(GetTxRequest) returns (GetTxResponse) {
option (google.api.http).get = "/cosmos/tx/v1beta1/getTx/{hash}";
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -35,15 +35,15 @@ message SimulateResponse {
cosmos.base.abci.v1beta1.Result result = 2;
}

// TxByHash is the request type for the Service.TxByHash
// GetTx is the request type for the Service.GetTx
// RPC method.
message TxByHashRequest {
// hash is the tx hash to query.
bytes hash = 1;
message GetTxRequest {
// hash is the tx hash to query, encoded as a hex string.
string hash = 1;
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}

// TxByHashResponse is the response type for the Service.TxByHash method.
message TxByHashResponse {
// GetTxResponse is the response type for the Service.GetTx method.
message GetTxResponse {
// tx is the queried transaction.
cosmos.tx.v1beta1.Tx tx = 1;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be including other TxResponse items like height, data, logs, etc.?

4 changes: 4 additions & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App

var grpcSrv *grpc.Server
if config.GRPC.Enable {
// Add the tx service in the gRPC router.
app.RegisterTxService(clientCtx)

// Create a new server, and attach it to the app's gRPC router.
grpcSrv, err = servergrpc.StartGRPCServer(app, config.GRPC.Address)
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions server/types/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
tmtypes "github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server/api"
"github.com/cosmos/cosmos-sdk/server/config"
)
Expand Down Expand Up @@ -37,6 +38,10 @@ type (
// RegisterGRPCServer registers gRPC services directly with the gRPC
// server.
RegisterGRPCServer(grpc.Server)

// RegisterTxService registers the gRPC Query service for tx (such as tx
// simulation, fetching txs by hash...).
RegisterTxService(clientCtx client.Context)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}

// AppCreator is a function that allows us to lazily initialize an
Expand Down
14 changes: 11 additions & 3 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/server/api"
"github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -134,7 +135,10 @@ var (
}
)

var _ App = (*SimApp)(nil)
var (
_ App = (*SimApp)(nil)
_ servertypes.Application = (*SimApp)(nil)
)

// SimApp extends an ABCI application, but with most of its parameters exported.
// They are exported for convenience in creating helper functions, as object
Expand Down Expand Up @@ -204,7 +208,6 @@ func NewSimApp(
bApp.SetCommitMultiStoreTracer(traceStore)
bApp.SetAppVersion(version.Version)
bApp.SetInterfaceRegistry(interfaceRegistry)
bApp.GRPCQueryRouter().RegisterSimulateService(bApp.Simulate, interfaceRegistry)

keys := sdk.NewKVStoreKeys(
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
Expand Down Expand Up @@ -547,14 +550,19 @@ func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICon
authrest.RegisterTxRoutes(clientCtx, apiSvr.Router)

ModuleBasics.RegisterRESTRoutes(clientCtx, apiSvr.Router)
ModuleBasics.RegisterGRPCRoutes(apiSvr.ClientCtx, apiSvr.GRPCRouter)
ModuleBasics.RegisterGRPCRoutes(clientCtx, apiSvr.GRPCRouter)

// register swagger API from root so that other applications can override easily
if apiConfig.Swagger {
RegisterSwaggerAPI(clientCtx, apiSvr.Router)
}
}

// RegisterTxService implements the Application.RegisterTxService method.
func (app *SimApp) RegisterTxService(clientCtx client.Context) {
app.BaseApp.GRPCQueryRouter().RegisterTxService(clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
}

// RegisterSwaggerAPI registers swagger route with API Server
func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) {
statikFS, err := fs.New()
Expand Down
4 changes: 4 additions & 0 deletions testutil/network/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func startInProcess(cfg Config, val *Validator) error {
}

if val.AppConfig.GRPC.Enable {
// Add the tx service in the gRPC router.
app.RegisterTxService(val.ClientCtx)

// Create a new server, and attach it to the app's gRPC router.
grpcSrv, err := servergrpc.StartGRPCServer(app, val.AppConfig.GRPC.Address)
if err != nil {
return err
Expand Down
64 changes: 59 additions & 5 deletions types/tx/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@ package tx

import (
"context"
"encoding/hex"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// BaseAppSimulateFn is the signature of the Baseapp#Simulate function.
type BaseAppSimulateFn func(txBytes []byte) (sdk.GasInfo, *sdk.Result, error)

type txServer struct {
clientCtx client.Context
simulate BaseAppSimulateFn
interfaceRegistry codectypes.InterfaceRegistry
}

// NewTxServer creates a new TxService server.
func NewTxServer(simulate BaseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer {
func NewTxServer(clientCtx client.Context, simulate BaseAppSimulateFn, interfaceRegistry codectypes.InterfaceRegistry) ServiceServer {
return txServer{
clientCtx: clientCtx,
simulate: simulate,
interfaceRegistry: interfaceRegistry,
}
Expand Down Expand Up @@ -54,8 +59,57 @@ func (s txServer) Simulate(ctx context.Context, req *SimulateRequest) (*Simulate
}, nil
}

// TxByHash implements the ServiceServer.TxByHash RPC method.
func (s txServer) TxByHash(ctx context.Context, req *TxByHashRequest) (*TxByHashResponse, error) {
// TODO
return nil, nil
// GetTx implements the ServiceServer.GetTx RPC method.
func (s txServer) GetTx(ctx context.Context, req *GetTxRequest) (*GetTxResponse, error) {
// We get hash as a hex string in the request, convert it to bytes.
hash, err := hex.DecodeString(req.Hash)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

// TODO We should also check the proof flag in gRPC header.
// https://github.com/cosmos/cosmos-sdk/issues/7036.
result, err := s.clientCtx.Client.Tx(ctx, hash, false)
if err != nil {
return nil, err
}

sdkTx, err := s.clientCtx.TxConfig.TxDecoder()(result.Tx)
if err != nil {
return nil, err
}

txBuilder, ok := sdkTx.(client.TxBuilder)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (client.TxBuilder)(nil), sdkTx)
}
// Convert the txBuilder to a tx.Tx.
protoTx, err := TxBuilderToProtoTx(txBuilder)
if err != nil {
return nil, err
}
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

return &GetTxResponse{
Tx: protoTx,
}, nil
}

// TxBuilderToProtoTx convert a txBuilder into a proto tx.Tx.
func TxBuilderToProtoTx(txBuilder client.TxBuilder) (*Tx, error) {
intoAnyTx, ok := txBuilder.(codectypes.IntoAny)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (codectypes.IntoAny)(nil), intoAnyTx)
}

any := intoAnyTx.AsAny().GetCachedValue()
if any == nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "any's cached value is empty")
}
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

protoTx, ok := any.(*Tx)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (codectypes.IntoAny)(nil), intoAnyTx)
}

return protoTx, nil
}
Loading