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

refactor(server/v2/cometbft): Handle non-module service queries #22803

Merged
merged 17 commits into from
Dec 11, 2024
159 changes: 128 additions & 31 deletions server/v2/cometbft/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (

abci "github.com/cometbft/cometbft/abci/types"
abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
gogoproto "github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"

"cosmossdk.io/collections"
addresscodec "cosmossdk.io/core/address"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/comet"
corecontext "cosmossdk.io/core/context"
Expand All @@ -35,10 +37,11 @@ import (
"cosmossdk.io/store/v2/snapshots"
consensustypes "cosmossdk.io/x/consensus/types"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/std"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
)

const (
Expand Down Expand Up @@ -86,8 +89,10 @@ type consensus[T transaction.Tx] struct {
addrPeerFilter types.PeerFilter // filter peers by address and port
idPeerFilter types.PeerFilter // filter peers by node ID

queryHandlersMap map[string]appmodulev2.Handler
getProtoRegistry func() (*protoregistry.Files, error)
queryHandlersMap map[string]appmodulev2.Handler
getProtoRegistry func() (*protoregistry.Files, error)
consensusAddressCodec addresscodec.Codec
cfgMap server.ConfigMap
}

// CheckTx implements types.Application.
Expand Down Expand Up @@ -184,6 +189,16 @@ func (c *consensus[T]) Query(ctx context.Context, req *abciproto.QueryRequest) (
return resp, err
}

// when a client did not provide a query height, manually inject the latest
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
// for modules queries, AppManager does it automatically
if req.Height == 0 {
latestVersion, err := c.store.GetLatestVersion()
if err != nil {
return nil, err
}
req.Height = int64(latestVersion)
}

// this error most probably means that we can't handle it with a proto message, so
// it must be an app/p2p/store query
path := splitABCIQueryPath(req.Path)
Expand Down Expand Up @@ -238,44 +253,126 @@ func (c *consensus[T]) maybeRunGRPCQuery(ctx context.Context, req *abci.QueryReq
handlerFullName = string(md.Input().FullName())
}

// special case for simulation as it is an external gRPC registered on the grpc server component
// special case for non-module services as they are external gRPC registered on the grpc server component
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
// and not on the app itself, so it won't pass the router afterwards.
if req.Path == "/cosmos.tx.v1beta1.Service/Simulate" {
simulateRequest := &txtypes.SimulateRequest{}
err = gogoproto.Unmarshal(req.Data, simulateRequest)
if err != nil {
return nil, true, fmt.Errorf("unable to decode gRPC request with path %s from ABCI.Query: %w", req.Path, err)

// Handle comet service
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
if strings.Contains(req.Path, "/cosmos.base.tendermint.v1beta1.Service") {
rpcClient, _ := rpchttp.New(c.cfg.AppTomlConfig.Address)
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
cometQServer := cmtservice.NewQueryServer(rpcClient, c.Query, c.consensusAddressCodec)
paths := strings.Split(req.Path, "/")
if len(paths) <= 2 {
return nil, false, fmt.Errorf("invalid request path: %s", req.Path)
}

var resp transaction.Msg
var err error
switch paths[2] {
hieuvubk marked this conversation as resolved.
Show resolved Hide resolved
case "GetNodeInfo":
resp, err = handleExternalService(ctx, req, cometQServer.GetNodeInfo)
case "GetSyncing":
resp, err = handleExternalService(ctx, req, cometQServer.GetSyncing)
case "GetLatestBlock":
resp, err = handleExternalService(ctx, req, cometQServer.GetLatestBlock)
case "GetBlockByHeight":
resp, err = handleExternalService(ctx, req, cometQServer.GetBlockByHeight)
case "GetLatestValidatorSet":
resp, err = handleExternalService(ctx, req, cometQServer.GetLatestValidatorSet)
case "GetValidatorSetByHeight":
resp, err = handleExternalService(ctx, req, cometQServer.GetValidatorSetByHeight)
case "ABCIQuery":
resp, err = handleExternalService(ctx, req, cometQServer.ABCIQuery)
}

tx, err := c.txCodec.Decode(simulateRequest.TxBytes)
if err != nil {
return nil, true, fmt.Errorf("failed to decode tx: %w", err)
return nil, true, err
}

res, err := queryResponse(resp, req.Height)
return res, true, err
}

// Handle node service
if strings.Contains(req.Path, "/cosmos.base.node.v1beta1.Service") {
nodeQService := nodeServer[T]{c.cfgMap, c.cfg.AppTomlConfig, c}
paths := strings.Split(req.Path, "/")
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
if len(paths) <= 2 {
return nil, false, fmt.Errorf("invalid request path: %s", req.Path)
}

var resp transaction.Msg
var err error
switch paths[2] {
case "Config":
resp, err = handleExternalService(ctx, req, nodeQService.Config)
case "Status":
resp, err = handleExternalService(ctx, req, nodeQService.Status)
}

txResult, _, err := c.app.Simulate(ctx, tx)
if err != nil {
return nil, true, fmt.Errorf("failed with gas used: '%d': %w", txResult.GasUsed, err)
return nil, true, err
}

msgResponses := make([]*codectypes.Any, 0, len(txResult.Resp))
// pack the messages into Any
for _, msg := range txResult.Resp {
anyMsg, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, true, fmt.Errorf("failed to pack message response: %w", err)
}
res, err := queryResponse(resp, req.Height)
return res, true, err
}

// Handle tx service
if strings.Contains(req.Path, "/cosmos.tx.v1beta1.Service") {
// init simple client context
amino := codec.NewLegacyAmino()
std.RegisterLegacyAminoCodec(amino)
txConfig := authtx.NewTxConfig(
c.appCodec,
c.appCodec.InterfaceRegistry().SigningContext().AddressCodec(),
c.appCodec.InterfaceRegistry().SigningContext().ValidatorAddressCodec(),
authtx.DefaultSignModes,
)
rpcClient, _ := client.NewClientFromNode(c.cfg.AppTomlConfig.Address)

clientCtx := client.Context{}.
WithLegacyAmino(amino).
WithCodec(c.appCodec).
WithTxConfig(txConfig).
WithNodeURI(c.cfg.AppTomlConfig.Address).
WithClient(rpcClient)

txService := txServer[T]{
clientCtx: clientCtx,
txCodec: c.txCodec,
app: c.app,
consensus: c,
}
paths := strings.Split(req.Path, "/")
if len(paths) <= 2 {
return nil, false, fmt.Errorf("invalid request path: %s", req.Path)
}

msgResponses = append(msgResponses, anyMsg)
var resp transaction.Msg
var err error
switch paths[2] {
case "Simulate":
resp, err = handleExternalService(ctx, req, txService.Simulate)
case "GetTx":
resp, err = handleExternalService(ctx, req, txService.GetTx)
case "BroadcastTx":
return nil, true, errors.New("can't route a broadcast tx message")
case "GetTxsEvent":
resp, err = handleExternalService(ctx, req, txService.GetTxsEvent)
case "GetBlockWithTxs":
resp, err = handleExternalService(ctx, req, txService.GetBlockWithTxs)
case "TxDecode":
resp, err = handleExternalService(ctx, req, txService.TxDecode)
case "TxEncode":
resp, err = handleExternalService(ctx, req, txService.TxEncode)
case "TxEncodeAmino":
resp, err = handleExternalService(ctx, req, txService.TxEncodeAmino)
case "TxDecodeAmino":
resp, err = handleExternalService(ctx, req, txService.Simulate)
}

resp := &txtypes.SimulateResponse{
GasInfo: &sdk.GasInfo{
GasUsed: txResult.GasUsed,
GasWanted: txResult.GasWanted,
},
Result: &sdk.Result{
MsgResponses: msgResponses,
},
if err != nil {
return nil, true, err
}

res, err := queryResponse(resp, req.Height)
Expand Down
Loading
Loading