Skip to content

Commit 9525d91

Browse files
authored
fix(simapp/v2): register extra gRPC gateway routes (cosmos#22786)
1 parent 7e02d59 commit 9525d91

File tree

2 files changed

+166
-60
lines changed

2 files changed

+166
-60
lines changed

server/v2/cometbft/grpc.go

Lines changed: 142 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cometbft
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
abci "github.com/cometbft/cometbft/abci/types"
89
abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1"
@@ -24,6 +25,8 @@ import (
2425
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
2526
sdk "github.com/cosmos/cosmos-sdk/types"
2627
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
28+
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
29+
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
2730
)
2831

2932
type appSimulator[T transaction.Tx] interface {
@@ -50,51 +53,6 @@ func gRPCServiceRegistrar[T transaction.Tx](
5053
}
5154
}
5255

53-
// CometBFTAutoCLIDescriptor is the auto-generated CLI descriptor for the CometBFT service
54-
var CometBFTAutoCLIDescriptor = &autocliv1.ServiceCommandDescriptor{
55-
Service: cmtv1beta1.Service_ServiceDesc.ServiceName,
56-
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
57-
{
58-
RpcMethod: "GetNodeInfo",
59-
Use: "node-info",
60-
Short: "Query the current node info",
61-
},
62-
{
63-
RpcMethod: "GetSyncing",
64-
Use: "syncing",
65-
Short: "Query node syncing status",
66-
},
67-
{
68-
RpcMethod: "GetLatestBlock",
69-
Use: "block-latest",
70-
Short: "Query for the latest committed block",
71-
},
72-
{
73-
RpcMethod: "GetBlockByHeight",
74-
Use: "block-by-height <height>",
75-
Short: "Query for a committed block by height",
76-
Long: "Query for a specific committed block using the CometBFT RPC `block_by_height` method",
77-
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
78-
},
79-
{
80-
RpcMethod: "GetLatestValidatorSet",
81-
Use: "validator-set",
82-
Alias: []string{"validator-set-latest", "comet-validator-set", "cometbft-validator-set", "tendermint-validator-set"},
83-
Short: "Query for the latest validator set",
84-
},
85-
{
86-
RpcMethod: "GetValidatorSetByHeight",
87-
Use: "validator-set-by-height <height>",
88-
Short: "Query for a validator set by height",
89-
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
90-
},
91-
{
92-
RpcMethod: "ABCIQuery",
93-
Skip: true,
94-
},
95-
},
96-
}
97-
9856
type txServer[T transaction.Tx] struct {
9957
clientCtx client.Context
10058
txCodec transaction.Codec[T]
@@ -112,8 +70,33 @@ func (t txServer[T]) GetBlockWithTxs(context.Context, *txtypes.GetBlockWithTxsRe
11270
}
11371

11472
// GetTx implements tx.ServiceServer.
115-
func (t txServer[T]) GetTx(context.Context, *txtypes.GetTxRequest) (*txtypes.GetTxResponse, error) {
116-
return nil, status.Error(codes.Unimplemented, "not implemented")
73+
func (t txServer[T]) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtypes.GetTxResponse, error) {
74+
if req == nil {
75+
return nil, status.Error(codes.InvalidArgument, "request cannot be nil")
76+
}
77+
78+
if len(req.Hash) == 0 {
79+
return nil, status.Error(codes.InvalidArgument, "tx hash cannot be empty")
80+
}
81+
82+
result, err := authtx.QueryTx(t.clientCtx, req.Hash)
83+
if err != nil {
84+
if strings.Contains(err.Error(), "not found") {
85+
return nil, status.Errorf(codes.NotFound, "tx not found: %s", req.Hash)
86+
}
87+
88+
return nil, err
89+
}
90+
91+
protoTx, ok := result.Tx.GetCachedValue().(*txtypes.Tx)
92+
if !ok {
93+
return nil, status.Errorf(codes.Internal, "expected %T, got %T", txtypes.Tx{}, result.Tx.GetCachedValue())
94+
}
95+
96+
return &txtypes.GetTxResponse{
97+
Tx: protoTx,
98+
TxResponse: result,
99+
}, nil
117100
}
118101

119102
// GetTxsEvent implements tx.ServiceServer.
@@ -181,18 +164,79 @@ func (t txServer[T]) TxDecode(context.Context, *txtypes.TxDecodeRequest) (*txtyp
181164
}
182165

183166
// TxDecodeAmino implements tx.ServiceServer.
184-
func (t txServer[T]) TxDecodeAmino(context.Context, *txtypes.TxDecodeAminoRequest) (*txtypes.TxDecodeAminoResponse, error) {
185-
return nil, status.Error(codes.Unimplemented, "not implemented")
167+
func (t txServer[T]) TxDecodeAmino(_ context.Context, req *txtypes.TxDecodeAminoRequest) (*txtypes.TxDecodeAminoResponse, error) {
168+
if req.AminoBinary == nil {
169+
return nil, status.Error(codes.InvalidArgument, "invalid empty tx bytes")
170+
}
171+
172+
var stdTx legacytx.StdTx
173+
err := t.clientCtx.LegacyAmino.Unmarshal(req.AminoBinary, &stdTx)
174+
if err != nil {
175+
return nil, err
176+
}
177+
178+
res, err := t.clientCtx.LegacyAmino.MarshalJSON(stdTx)
179+
if err != nil {
180+
return nil, err
181+
}
182+
183+
return &txtypes.TxDecodeAminoResponse{
184+
AminoJson: string(res),
185+
}, nil
186186
}
187187

188188
// TxEncode implements tx.ServiceServer.
189-
func (t txServer[T]) TxEncode(context.Context, *txtypes.TxEncodeRequest) (*txtypes.TxEncodeResponse, error) {
190-
return nil, status.Error(codes.Unimplemented, "not implemented")
189+
func (t txServer[T]) TxEncode(_ context.Context, req *txtypes.TxEncodeRequest) (*txtypes.TxEncodeResponse, error) {
190+
if req.Tx == nil {
191+
return nil, status.Error(codes.InvalidArgument, "invalid empty tx")
192+
}
193+
194+
bodyBytes, err := t.clientCtx.Codec.Marshal(req.Tx.Body)
195+
if err != nil {
196+
return nil, err
197+
}
198+
199+
authInfoBytes, err := t.clientCtx.Codec.Marshal(req.Tx.AuthInfo)
200+
if err != nil {
201+
return nil, err
202+
}
203+
204+
raw := &txtypes.TxRaw{
205+
BodyBytes: bodyBytes,
206+
AuthInfoBytes: authInfoBytes,
207+
Signatures: req.Tx.Signatures,
208+
}
209+
210+
encodedBytes, err := t.clientCtx.Codec.Marshal(raw)
211+
if err != nil {
212+
return nil, err
213+
}
214+
215+
return &txtypes.TxEncodeResponse{
216+
TxBytes: encodedBytes,
217+
}, nil
191218
}
192219

193220
// TxEncodeAmino implements tx.ServiceServer.
194-
func (t txServer[T]) TxEncodeAmino(context.Context, *txtypes.TxEncodeAminoRequest) (*txtypes.TxEncodeAminoResponse, error) {
195-
return nil, status.Error(codes.Unimplemented, "not implemented")
221+
func (t txServer[T]) TxEncodeAmino(_ context.Context, req *txtypes.TxEncodeAminoRequest) (*txtypes.TxEncodeAminoResponse, error) {
222+
if req.AminoJson == "" {
223+
return nil, status.Error(codes.InvalidArgument, "invalid empty tx json")
224+
}
225+
226+
var stdTx legacytx.StdTx
227+
err := t.clientCtx.LegacyAmino.UnmarshalJSON([]byte(req.AminoJson), &stdTx)
228+
if err != nil {
229+
return nil, err
230+
}
231+
232+
encodedBytes, err := t.clientCtx.LegacyAmino.Marshal(stdTx)
233+
if err != nil {
234+
return nil, err
235+
}
236+
237+
return &txtypes.TxEncodeAminoResponse{
238+
AminoBinary: encodedBytes,
239+
}, nil
196240
}
197241

198242
var _ txtypes.ServiceServer = txServer[transaction.Tx]{}
@@ -236,3 +280,48 @@ func (s nodeServer[T]) Status(ctx context.Context, _ *nodeservice.StatusRequest)
236280
ValidatorHash: nodeInfo.LastBlockAppHash,
237281
}, nil
238282
}
283+
284+
// CometBFTAutoCLIDescriptor is the auto-generated CLI descriptor for the CometBFT service
285+
var CometBFTAutoCLIDescriptor = &autocliv1.ServiceCommandDescriptor{
286+
Service: cmtv1beta1.Service_ServiceDesc.ServiceName,
287+
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
288+
{
289+
RpcMethod: "GetNodeInfo",
290+
Use: "node-info",
291+
Short: "Query the current node info",
292+
},
293+
{
294+
RpcMethod: "GetSyncing",
295+
Use: "syncing",
296+
Short: "Query node syncing status",
297+
},
298+
{
299+
RpcMethod: "GetLatestBlock",
300+
Use: "block-latest",
301+
Short: "Query for the latest committed block",
302+
},
303+
{
304+
RpcMethod: "GetBlockByHeight",
305+
Use: "block-by-height <height>",
306+
Short: "Query for a committed block by height",
307+
Long: "Query for a specific committed block using the CometBFT RPC `block_by_height` method",
308+
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
309+
},
310+
{
311+
RpcMethod: "GetLatestValidatorSet",
312+
Use: "validator-set",
313+
Alias: []string{"validator-set-latest", "comet-validator-set", "cometbft-validator-set", "tendermint-validator-set"},
314+
Short: "Query for the latest validator set",
315+
},
316+
{
317+
RpcMethod: "GetValidatorSetByHeight",
318+
Use: "validator-set-by-height <height>",
319+
Short: "Query for a validator set by height",
320+
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "height"}},
321+
},
322+
{
323+
RpcMethod: "ABCIQuery",
324+
Skip: true,
325+
},
326+
},
327+
}

simapp/v2/simdv2/cmd/commands.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"context"
45
"io"
56

67
"github.com/spf13/cobra"
@@ -23,11 +24,14 @@ import (
2324
"github.com/cosmos/cosmos-sdk/client"
2425
"github.com/cosmos/cosmos-sdk/client/config"
2526
"github.com/cosmos/cosmos-sdk/client/debug"
27+
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
28+
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
2629
"github.com/cosmos/cosmos-sdk/client/keys"
2730
"github.com/cosmos/cosmos-sdk/client/rpc"
2831
sdktelemetry "github.com/cosmos/cosmos-sdk/telemetry"
2932
sdk "github.com/cosmos/cosmos-sdk/types"
3033
"github.com/cosmos/cosmos-sdk/types/module"
34+
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
3135
"github.com/cosmos/cosmos-sdk/version"
3236
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
3337
"github.com/cosmos/cosmos-sdk/x/genutil"
@@ -153,13 +157,7 @@ func InitRootCmd[T transaction.Tx](
153157
if err != nil {
154158
return nil, err
155159
}
156-
157-
for _, mod := range deps.ModuleManager.Modules() {
158-
if gmod, ok := mod.(module.HasGRPCGateway); ok {
159-
// TODO(@julienrbrt) https://github.com/cosmos/cosmos-sdk/pull/22701#pullrequestreview-2470651390
160-
gmod.RegisterGRPCGatewayRoutes(deps.ClientContext, grpcgatewayServer.GRPCGatewayRouter)
161-
}
162-
}
160+
registerGRPCGatewayRoutes[T](deps, grpcgatewayServer)
163161

164162
// wire server commands
165163
return serverv2.AddCommands[T](
@@ -264,3 +262,22 @@ func RootCommandPersistentPreRun(clientCtx client.Context) func(*cobra.Command,
264262
return nil
265263
}
266264
}
265+
266+
// registerGRPCGatewayRoutes registers the gRPC gateway routes for all modules and other components
267+
// TODO(@julienrbrt): Eventually, this should removed and directly done within the grpcgateway.Server
268+
// ref: https://github.com/cosmos/cosmos-sdk/pull/22701#pullrequestreview-2470651390
269+
func registerGRPCGatewayRoutes[T transaction.Tx](
270+
deps CommandDependencies[T],
271+
server *grpcgateway.Server[T],
272+
) {
273+
// those are the extra services that the CometBFT server implements (server/v2/cometbft/grpc.go)
274+
cmtservice.RegisterGRPCGatewayRoutes(deps.ClientContext, server.GRPCGatewayRouter)
275+
_ = nodeservice.RegisterServiceHandlerClient(context.Background(), server.GRPCGatewayRouter, nodeservice.NewServiceClient(deps.ClientContext))
276+
_ = txtypes.RegisterServiceHandlerClient(context.Background(), server.GRPCGatewayRouter, txtypes.NewServiceClient(deps.ClientContext))
277+
278+
for _, mod := range deps.ModuleManager.Modules() {
279+
if gmod, ok := mod.(module.HasGRPCGateway); ok {
280+
gmod.RegisterGRPCGatewayRoutes(deps.ClientContext, server.GRPCGatewayRouter)
281+
}
282+
}
283+
}

0 commit comments

Comments
 (0)