From 9f7f17b6679e08572b4ba44194ce8bbf7f000d9f Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:26:13 +0700 Subject: [PATCH] test(server/v2/cometbft): Add Consensus query `p2p`, `app`, `grpc` (#22771) --- server/v2/cometbft/abci_test.go | 195 ++++++++++++++++++++++++++++++-- server/v2/cometbft/utils.go | 8 +- 2 files changed, 194 insertions(+), 9 deletions(-) diff --git a/server/v2/cometbft/abci_test.go b/server/v2/cometbft/abci_test.go index b7bc427d13b..cfc64988466 100644 --- a/server/v2/cometbft/abci_test.go +++ b/server/v2/cometbft/abci_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "io" + "reflect" "strings" "sync" "testing" @@ -15,6 +16,7 @@ import ( abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1" v1 "github.com/cometbft/cometbft/api/cometbft/types/v1" "github.com/cosmos/gogoproto/proto" + gogoproto "github.com/cosmos/gogoproto/proto" gogotypes "github.com/cosmos/gogoproto/types" "github.com/stretchr/testify/require" @@ -33,6 +35,9 @@ import ( "cosmossdk.io/server/v2/stf/branch" "cosmossdk.io/server/v2/stf/mock" consensustypes "cosmossdk.io/x/consensus/types" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" ) var ( @@ -53,7 +58,9 @@ var ( Msg: &gogotypes.BoolValue{Value: true}, GasLimit: 0, } - actorName = []byte("cookies") + actorName = []byte("cookies") + testAcc = sdk.AccAddress([]byte("addr1_______________")) + versionStr = "0.0.0" ) func getQueryRouterBuilder[T any, PT interface { @@ -583,11 +590,7 @@ func TestConsensus_Info(t *testing.T) { require.Equal(t, res.LastBlockHeight, int64(1)) } -// TODO: -// - GRPC request -// - app request -// - p2p request -func TestConsensus_Query(t *testing.T) { +func TestConsensus_QueryStore(t *testing.T) { c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{}) // Write data to state storage @@ -648,11 +651,149 @@ func TestConsensus_Query(t *testing.T) { require.Equal(t, res.Value, []byte(nil)) } +func TestConsensus_GRPCQuery(t *testing.T) { + c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{}) + + _, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{ + Time: time.Now(), + ChainId: "test", + InitialHeight: 1, + }) + require.NoError(t, err) + + _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ + Time: time.Now(), + Height: 1, + Txs: [][]byte{mockTx.Bytes()}, + Hash: emptyHash[:], + }) + require.NoError(t, err) + + // empty request + res, err := c.Query(context.Background(), &abciproto.QueryRequest{}) + require.NoError(t, err) + require.Equal(t, res.Code, uint32(1)) + require.Contains(t, res.Log, "no query path provided") + + // query request not exist in handler map + invalidReq := testdata.EchoRequest{ + Message: "echo", + } + invalidReqBz, err := invalidReq.Marshal() + require.NoError(t, err) + invalidQuery := abci.QueryRequest{ + Data: invalidReqBz, + Path: "testpb.EchoRequest", + } + invalidRes, err := c.Query(context.TODO(), &invalidQuery) + require.Error(t, err) + require.Nil(t, invalidRes) + require.Contains(t, err.Error(), "no query handler found") + + // Valid query + req := testdata.SayHelloRequest{Name: "foo"} + reqBz, err := req.Marshal() + require.NoError(t, err) + reqQuery := abci.QueryRequest{ + Data: reqBz, + Path: "testpb.SayHelloRequest", + } + + resQuery, err := c.Query(context.TODO(), &reqQuery) + require.NoError(t, err) + require.Equal(t, abci.CodeTypeOK, resQuery.Code, resQuery) + + var response testdata.SayHelloResponse + require.NoError(t, response.Unmarshal(resQuery.Value)) + require.Equal(t, "Hello foo!", response.Greeting) +} + +func TestConsensus_P2PQuery(t *testing.T) { + c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{}) + + _, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{ + Time: time.Now(), + ChainId: "test", + InitialHeight: 1, + }) + require.NoError(t, err) + + _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ + Time: time.Now(), + Height: 1, + Txs: [][]byte{mockTx.Bytes()}, + Hash: emptyHash[:], + }) + require.NoError(t, err) + + // empty request + res, err := c.Query(context.Background(), &abciproto.QueryRequest{}) + require.NoError(t, err) + require.Equal(t, res.Code, uint32(1)) + require.Contains(t, res.Log, "no query path provided") + + addrQuery := abci.QueryRequest{ + Path: "/p2p/filter/addr/1.1.1.1:8000", + } + res, err = c.Query(context.TODO(), &addrQuery) + require.NoError(t, err) + require.Equal(t, uint32(3), res.Code) + + idQuery := abci.QueryRequest{ + Path: "/p2p/filter/id/testid", + } + res, err = c.Query(context.TODO(), &idQuery) + require.NoError(t, err) + require.Equal(t, uint32(4), res.Code) +} + +func TestConsensus_AppQuery(t *testing.T) { + c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{}) + + _, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{ + Time: time.Now(), + ChainId: "test", + InitialHeight: 1, + }) + require.NoError(t, err) + + _, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{ + Time: time.Now(), + Height: 1, + Txs: [][]byte{mockTx.Bytes()}, + Hash: emptyHash[:], + }) + require.NoError(t, err) + + tx := mock.Tx{ + Sender: testAcc, + Msg: &gogotypes.BoolValue{Value: true}, + GasLimit: 1000, + } + txBytes := tx.Bytes() + + // simulate by calling Query with encoded tx + query := abci.QueryRequest{ + Path: "/app/simulate", + Data: txBytes, + } + queryResult, err := c.Query(context.TODO(), &query) + require.NoError(t, err) + require.True(t, queryResult.IsOK(), queryResult.Log) + + // Query app version + res, err := c.Query(context.TODO(), &abci.QueryRequest{Path: "app/version"}) + require.NoError(t, err) + require.True(t, res.IsOK()) + require.Equal(t, versionStr, string(res.Value)) +} + func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock.Tx]) *consensus[mock.Tx] { t.Helper() + queryHandler := make(map[string]appmodulev2.Handler) msgRouterBuilder := getMsgRouterBuilder(t, func(ctx context.Context, msg *gogotypes.BoolValue) (*gogotypes.BoolValue, error) { - return nil, nil + return msg, nil }) queryRouterBuilder := getQueryRouterBuilder(t, func(ctx context.Context, q *consensustypes.QueryParamsRequest) (*consensustypes.QueryParamsResponse, error) { @@ -669,6 +810,32 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock. }, nil }) + helloFooHandler := func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) { + typedReq := msg.(*testdata.SayHelloRequest) + handler := testdata.QueryImpl{} + typedResp, err := handler.SayHello(ctx, typedReq) + if err != nil { + return nil, err + } + + return typedResp, nil + } + + queryRouterBuilder.RegisterHandler( + proto.MessageName(&testdata.SayHelloRequest{}), + helloFooHandler, + ) + + queryHandler[proto.MessageName(&testdata.SayHelloRequest{})] = appmodulev2.Handler{ + Func: helloFooHandler, + MakeMsg: func() transaction.Msg { + return reflect.New(gogoproto.MessageType(proto.MessageName(&testdata.SayHelloRequest{})).Elem()).Interface().(transaction.Msg) + }, + MakeMsgResp: func() transaction.Msg { + return reflect.New(gogoproto.MessageType(proto.MessageName(&testdata.SayHelloResponse{})).Elem()).Interface().(transaction.Msg) + }, + } + s, err := stf.New( log.NewNopLogger().With("module", "stf"), msgRouterBuilder, @@ -709,6 +876,16 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock. nil, ) + addrPeerFilter := func(info string) (*abci.QueryResponse, error) { + require.Equal(t, "1.1.1.1:8000", info) + return &abci.QueryResponse{Code: uint32(3)}, nil + } + + idPeerFilter := func(id string) (*abci.QueryResponse, error) { + require.Equal(t, "testid", id) + return &abci.QueryResponse{Code: uint32(4)}, nil + } + return &consensus[mock.Tx]{ logger: log.NewNopLogger(), appName: "testing-app", @@ -719,6 +896,10 @@ func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock. txCodec: mock.TxCodec{}, chainID: "test", getProtoRegistry: sync.OnceValues(proto.MergedRegistry), + queryHandlersMap: queryHandler, + addrPeerFilter: addrPeerFilter, + idPeerFilter: idPeerFilter, + version: versionStr, } } diff --git a/server/v2/cometbft/utils.go b/server/v2/cometbft/utils.go index d4c3ccf86ba..81d47b7f8cb 100644 --- a/server/v2/cometbft/utils.go +++ b/server/v2/cometbft/utils.go @@ -190,6 +190,11 @@ func intoABCISimulationResponse(txRes server.TxResult, indexSet map[string]struc msgResponses[i] = anyMsg } + errMsg := "" + if txRes.Error != nil { + errMsg = txRes.Error.Error() + } + res := &sdk.SimulationResponse{ GasInfo: sdk.GasInfo{ GasWanted: txRes.GasWanted, @@ -197,7 +202,7 @@ func intoABCISimulationResponse(txRes server.TxResult, indexSet map[string]struc }, Result: &sdk.Result{ Data: []byte{}, - Log: txRes.Error.Error(), + Log: errMsg, Events: abciEvents, MsgResponses: msgResponses, }, @@ -284,7 +289,6 @@ func gRPCErrorToSDKError(err error) *abci.QueryResponse { res.Log = err.Error() } return res - } status, ok := grpcstatus.FromError(err)