diff --git a/.gitignore b/.gitignore index b9d175a5..15fa95d4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ profile.out # Output of the go coverage tool, specifically when used with LiteIDE *.out build/ +tests/.testappd # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/Makefile b/Makefile index 1bcdc684..fa864d2d 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,13 @@ BUILD_DIR ?= $(CURDIR)/build PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git) HTTPS_GIT := https://github.com/skip-mev/block-sdk.git DOCKER := $(shell which docker) +<<<<<<< HEAD +======= +HOMEDIR ?= $(CURDIR)/tests/.testappd +GENESIS ?= $(HOMEDIR)/config/genesis.json +GENESIS_TMP ?= $(HOMEDIR)/config/genesis_tmp.json +COVER_FILE ?= "cover.out" +>>>>>>> 7d8a695 (feat: gRPC mempool service (#275)) ############################################################################### ### Test App ### @@ -75,13 +82,15 @@ $(BUILD_DIR)/: # other addresses using "genesis add-genesis-account address 10000000000000000000000000stake". # This will allow users to bootstrap their wallet with a balance. build-and-start-app: build-test-app - ./build/testappd init validator1 --chain-id chain-id-0 - ./build/testappd keys add validator1 - ./build/testappd genesis add-genesis-account validator1 10000000000000000000000000stake - ./build/testappd genesis add-genesis-account cosmos1see0htr47uapjvcvh0hu6385rp8lw3em24hysg 10000000000000000000000000stake - ./build/testappd genesis gentx validator1 1000000000stake --chain-id chain-id-0 - ./build/testappd genesis collect-gentxs - ./build/testappd start --api.enable true --api.enabled-unsafe-cors true --log_level info + rm -rf $(HOMEDIR) + + ./build/testappd init validator1 --chain-id chain-id-0 --home $(HOMEDIR) + ./build/testappd keys add validator1 --home $(HOMEDIR) --keyring-backend test + ./build/testappd genesis add-genesis-account validator1 10000000000000000000000000stake --home $(HOMEDIR) --keyring-backend test + ./build/testappd genesis add-genesis-account cosmos1see0htr47uapjvcvh0hu6385rp8lw3em24hysg 10000000000000000000000000stake --home $(HOMEDIR) --keyring-backend test + ./build/testappd genesis gentx validator1 1000000000stake --chain-id chain-id-0 --home $(HOMEDIR) --keyring-backend test + ./build/testappd genesis collect-gentxs --home $(HOMEDIR) + ./build/testappd start --api.enable false --api.enabled-unsafe-cors false --log_level info --home $(HOMEDIR) .PHONY: build-test-app build-and-start-app diff --git a/block/mempool.go b/block/mempool.go index 1eda523c..1ae0d5dd 100644 --- a/block/mempool.go +++ b/block/mempool.go @@ -24,7 +24,7 @@ type ( Contains(tx sdk.Tx) bool // GetTxDistribution returns the number of transactions in each lane. - GetTxDistribution() map[string]int + GetTxDistribution() map[string]uint64 } // LanedMempool defines the Block SDK mempool implementation. It contains a registry @@ -72,11 +72,11 @@ func (m *LanedMempool) CountTx() int { } // GetTxDistribution returns the number of transactions in each lane. -func (m *LanedMempool) GetTxDistribution() map[string]int { - counts := make(map[string]int, len(m.registry)) +func (m *LanedMempool) GetTxDistribution() map[string]uint64 { + counts := make(map[string]uint64, len(m.registry)) for _, lane := range m.registry { - counts[lane.Name()] = lane.CountTx() + counts[lane.Name()] = uint64(lane.CountTx()) } return counts diff --git a/block/mempool_test.go b/block/mempool_test.go index 46d2ffa0..1f49cd96 100644 --- a/block/mempool_test.go +++ b/block/mempool_test.go @@ -121,43 +121,43 @@ func (suite *BlockBusterTestSuite) SetupTest() { func (suite *BlockBusterTestSuite) TestInsert() { cases := []struct { name string - insertDistribution map[string]int + insertDistribution map[string]uint64 }{ { "insert 1 mev tx", - map[string]int{ + map[string]uint64{ suite.mevLane.Name(): 1, }, }, { "insert 10 mev txs", - map[string]int{ + map[string]uint64{ suite.mevLane.Name(): 10, }, }, { "insert 1 base tx", - map[string]int{ + map[string]uint64{ suite.baseLane.Name(): 1, }, }, { "insert 10 base txs and 10 mev txs", - map[string]int{ + map[string]uint64{ suite.baseLane.Name(): 10, suite.mevLane.Name(): 10, }, }, { "insert 100 base txs and 100 mev txs", - map[string]int{ + map[string]uint64{ suite.baseLane.Name(): 100, suite.mevLane.Name(): 100, }, }, { "insert 100 base txs, 100 mev txs, and 100 free txs", - map[string]int{ + map[string]uint64{ suite.baseLane.Name(): 100, suite.mevLane.Name(): 100, suite.freeLane.Name(): 100, @@ -165,20 +165,20 @@ func (suite *BlockBusterTestSuite) TestInsert() { }, { "insert 10 free txs", - map[string]int{ + map[string]uint64{ suite.freeLane.Name(): 10, }, }, { "insert 10 free txs and 10 base txs", - map[string]int{ + map[string]uint64{ suite.freeLane.Name(): 10, suite.baseLane.Name(): 10, }, }, { "insert 10 mev txs and 10 free txs", - map[string]int{ + map[string]uint64{ suite.mevLane.Name(): 10, suite.freeLane.Name(): 10, }, @@ -198,18 +198,18 @@ func (suite *BlockBusterTestSuite) TestInsert() { // Fill the Free lane with numFreeTxs transactions suite.fillFreeLane(tc.insertDistribution[suite.freeLane.Name()]) - sum := 0 + sum := uint64(0) for _, v := range tc.insertDistribution { sum += v } // Validate the mempool - suite.Require().Equal(sum, suite.mempool.CountTx()) + suite.Require().Equal(int(sum), suite.mempool.CountTx()) // Validate the lanes - suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], suite.mevLane.CountTx()) - suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], suite.baseLane.CountTx()) - suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], suite.freeLane.CountTx()) + suite.Require().Equal(tc.insertDistribution[suite.mevLane.Name()], uint64(suite.mevLane.CountTx())) + suite.Require().Equal(tc.insertDistribution[suite.baseLane.Name()], uint64(suite.baseLane.CountTx())) + suite.Require().Equal(tc.insertDistribution[suite.freeLane.Name()], uint64(suite.freeLane.CountTx())) // Validate the lane counts laneCounts := suite.mempool.GetTxDistribution() @@ -225,8 +225,8 @@ func (suite *BlockBusterTestSuite) TestInsert() { func (suite *BlockBusterTestSuite) TestRemove() { cases := []struct { name string - numTobTxs int - numBaseTxs int + numTobTxs uint64 + numBaseTxs uint64 }{ { "insert 1 mev tx", @@ -279,7 +279,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { // Ensure the number of transactions in the lane is correct baseCount-- - suite.Require().Equal(suite.baseLane.CountTx(), baseCount) + suite.Require().Equal(suite.baseLane.CountTx(), int(baseCount)) distribution := suite.mempool.GetTxDistribution() suite.Require().Equal(distribution[suite.baseLane.Name()], baseCount) @@ -288,7 +288,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { } suite.Require().Equal(0, suite.baseLane.CountTx()) - suite.Require().Equal(mevCount, suite.mevLane.CountTx()) + suite.Require().Equal(int(mevCount), suite.mevLane.CountTx()) // Remove all transactions from the lanes for iterator := suite.mevLane.Select(suite.ctx, nil); iterator != nil; { @@ -302,7 +302,7 @@ func (suite *BlockBusterTestSuite) TestRemove() { // Ensure the number of transactions in the lane is correct mevCount-- - suite.Require().Equal(suite.mevLane.CountTx(), mevCount) + suite.Require().Equal(suite.mevLane.CountTx(), int(mevCount)) distribution := suite.mempool.GetTxDistribution() suite.Require().Equal(distribution[suite.mevLane.Name()], mevCount) @@ -318,15 +318,15 @@ func (suite *BlockBusterTestSuite) TestRemove() { distribution := suite.mempool.GetTxDistribution() // Ensure that the lane counts are correct - suite.Require().Equal(distribution[suite.mevLane.Name()], 0) - suite.Require().Equal(distribution[suite.baseLane.Name()], 0) + suite.Require().Equal(distribution[suite.mevLane.Name()], uint64(0)) + suite.Require().Equal(distribution[suite.baseLane.Name()], uint64(0)) }) } } // fillBaseLane fills the base lane with numTxs transactions that are randomly created. -func (suite *BlockBusterTestSuite) fillBaseLane(numTxs int) { - for i := 0; i < numTxs; i++ { +func (suite *BlockBusterTestSuite) fillBaseLane(numTxs uint64) { + for i := uint64(0); i < numTxs; i++ { // randomly select an account to create the tx randomIndex := suite.random.Intn(len(suite.accounts)) acc := suite.accounts[randomIndex] @@ -345,8 +345,8 @@ func (suite *BlockBusterTestSuite) fillBaseLane(numTxs int) { } // fillTOBLane fills the TOB lane with numTxs transactions that are randomly created. -func (suite *BlockBusterTestSuite) fillTOBLane(numTxs int) { - for i := 0; i < numTxs; i++ { +func (suite *BlockBusterTestSuite) fillTOBLane(numTxs uint64) { + for i := uint64(0); i < numTxs; i++ { // randomly select a bidder to create the tx randomIndex := suite.random.Intn(len(suite.accounts)) acc := suite.accounts[randomIndex] @@ -365,8 +365,8 @@ func (suite *BlockBusterTestSuite) fillTOBLane(numTxs int) { } // filleFreeLane fills the free lane with numTxs transactions that are randomly created. -func (suite *BlockBusterTestSuite) fillFreeLane(numTxs int) { - for i := 0; i < numTxs; i++ { +func (suite *BlockBusterTestSuite) fillFreeLane(numTxs uint64) { + for i := uint64(0); i < numTxs; i++ { // randomly select an account to create the tx randomIndex := suite.random.Intn(len(suite.accounts)) acc := suite.accounts[randomIndex] diff --git a/block/service/README.md b/block/service/README.md new file mode 100644 index 00000000..0b4f0821 --- /dev/null +++ b/block/service/README.md @@ -0,0 +1,56 @@ +# Block SDK Mempool Service + +The Block SDK mempool service is a service that allows you to query the current state of the application side mempool. + +## Usage + +The mempool service is a standard gRPC service that can be paired with http or grpc clients. + +### HTTP Clients + +To make requests to the mempool service using HTTP, you have to use the grpc-gateway defined on your application's server. This is usually hosted on port 1317. + +### gRPC Clients + +To query the mempool service using gRPC, you can use the Mempool `ServiceClient` defined in [types](./types/query.pb.go): + +```golang +type serviceClient struct { + cc grpc1.ClientConn +} + +func NewServiceClient(cc grpc1.ClientConn) ServiceClient { + return &serviceClient{cc} +} + +func (c *serviceClient) GetTxDistribution(ctx context.Context, in *GetTxDistributionRequest, opts ...grpc.CallOption) (*GetTxDistributionResponse, error) { + out := new(GetTxDistributionResponse) + err := c.cc.Invoke(ctx, "/sdk.mempool.v1.Service/GetTxDistribution", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} +``` + +## Endpoints + +### GetTxDistribution + +GetTxDistribution returns the current distribution of transactions in the mempool. The response is a map of the lane name to the number of transactions in that lane. + +```golang +type GetTxDistributionRequest struct {} + +type GetTxDistributionResponse struct { + Distribution map[string]uint64 +} +``` + +### HTTP Requests + +To query the mempool service using HTTP, you can use the following endpoint: + +```bash +curl http://localhost:1317/block-sdk/mempool/v1/distribution +``` diff --git a/block/service/service.go b/block/service/service.go new file mode 100644 index 00000000..20f277f1 --- /dev/null +++ b/block/service/service.go @@ -0,0 +1,54 @@ +package service + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + + "github.com/skip-mev/block-sdk/block" + "github.com/skip-mev/block-sdk/block/service/types" +) + +var _ types.ServiceServer = (*QueryService)(nil) + +// QueryService defines the service used by the gRPC query server to query the +// Block SDK mempool. +type QueryService struct { + types.UnimplementedServiceServer + + // mempool is the mempool instance to query. + mempool block.Mempool +} + +// NewQueryService creates a new QueryService instance. +func NewQueryService(mempool block.Mempool) *QueryService { + return &QueryService{ + mempool: mempool, + } +} + +// GetTxDistribution returns the current distribution of transactions in the +// mempool. +func (s *QueryService) GetTxDistribution( + _ context.Context, + _ *types.GetTxDistributionRequest, +) (*types.GetTxDistributionResponse, error) { + distribution := s.mempool.GetTxDistribution() + return &types.GetTxDistributionResponse{Distribution: distribution}, nil +} + +// RegisterMempoolService registers the Block SDK mempool queries on the gRPC server. + +func RegisterMempoolService( + server gogogrpc.Server, + mempool block.Mempool, +) { + types.RegisterServiceServer(server, NewQueryService(mempool)) +} + +// RegisterGRPCGatewayRoutes mounts the Block SDK mempool service's GRPC-gateway routes on the +// given Mux. +func RegisterGRPCGatewayRoutes(clientConn gogogrpc.ClientConn, mux *runtime.ServeMux) { + _ = types.RegisterServiceHandlerClient(context.Background(), mux, types.NewServiceClient(clientConn)) +} diff --git a/block/service/service_test.go b/block/service/service_test.go new file mode 100644 index 00000000..44e23719 --- /dev/null +++ b/block/service/service_test.go @@ -0,0 +1,192 @@ +package service_test + +import ( + "context" + "math/rand" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/skip-mev/block-sdk/block" + "github.com/skip-mev/block-sdk/block/service" + "github.com/skip-mev/block-sdk/block/service/types" + "github.com/skip-mev/block-sdk/lanes/base" + "github.com/skip-mev/block-sdk/lanes/free" + "github.com/skip-mev/block-sdk/lanes/mev" + "github.com/skip-mev/block-sdk/testutils" + "github.com/stretchr/testify/require" +) + +func TestGetTxDistribution(t *testing.T) { + config := testutils.CreateTestEncodingConfig() + accounts := testutils.RandomAccounts(rand.New(rand.NewSource(1)), 3) + ctx := testutils.CreateBaseSDKContext(t) + + testCases := []struct { + name string + mempool func() *block.LanedMempool + expectedDistribution map[string]uint64 + }{ + { + name: "returns correct distribution with no transactions", + mempool: testutils.CreateMempool, + expectedDistribution: map[string]uint64{ + mev.LaneName: 0, + free.LaneName: 0, + base.LaneName: 0, + }, + }, + { + name: "only default lane has transactions", + mempool: func() *block.LanedMempool { + tx1, err := testutils.CreateRandomTx( + config.TxConfig, + accounts[0], + 0, + 1, + 0, + 0, + sdk.NewCoin("skip", math.NewInt(1)), + ) + require.NoError(t, err) + + tx2, err := testutils.CreateRandomTx( + config.TxConfig, + accounts[1], + 0, + 1, + 0, + 0, + sdk.NewCoin("skip", math.NewInt(1)), + ) + require.NoError(t, err) + + mempool := testutils.CreateMempool() + err = mempool.Insert(ctx, tx1) + require.NoError(t, err) + err = mempool.Insert(ctx, tx2) + require.NoError(t, err) + + return mempool + }, + expectedDistribution: map[string]uint64{ + mev.LaneName: 0, + free.LaneName: 0, + base.LaneName: 2, + }, + }, + { + name: "only free lane has transactions", + mempool: func() *block.LanedMempool { + tx1, err := testutils.CreateFreeTx( + config.TxConfig, + accounts[0], + 0, + 1, + "skip", + sdk.NewCoin("skip", math.NewInt(1)), + ) + require.NoError(t, err) + + mempool := testutils.CreateMempool() + err = mempool.Insert(ctx, tx1) + require.NoError(t, err) + + return mempool + }, + expectedDistribution: map[string]uint64{ + mev.LaneName: 0, + free.LaneName: 1, + base.LaneName: 0, + }, + }, + { + name: "only mev lane has transactions", + mempool: func() *block.LanedMempool { + tx1, err := testutils.CreateAuctionTxWithSigners( + config.TxConfig, + accounts[0], + sdk.NewCoin("skip", math.NewInt(1)), + 0, + 0, + accounts, + ) + require.NoError(t, err) + + mempool := testutils.CreateMempool() + err = mempool.Insert(ctx, tx1) + require.NoError(t, err) + + return mempool + }, + expectedDistribution: map[string]uint64{ + mev.LaneName: 1, + free.LaneName: 0, + base.LaneName: 0, + }, + }, + { + name: "all lanes have transactions", + mempool: func() *block.LanedMempool { + mevTx, err := testutils.CreateAuctionTxWithSigners( + config.TxConfig, + accounts[0], + sdk.NewCoin("skip", math.NewInt(1)), + 0, + 0, + accounts, + ) + require.NoError(t, err) + + freeTx, err := testutils.CreateFreeTx( + config.TxConfig, + accounts[0], + 0, + 1, + "skip", + sdk.NewCoin("skip", math.NewInt(1)), + ) + require.NoError(t, err) + + baseTx, err := testutils.CreateRandomTx( + config.TxConfig, + accounts[0], + 0, + 1, + 0, + 0, + sdk.NewCoin("skip", math.NewInt(1)), + ) + require.NoError(t, err) + + mempool := testutils.CreateMempool() + err = mempool.Insert(ctx, mevTx) + require.NoError(t, err) + err = mempool.Insert(ctx, freeTx) + require.NoError(t, err) + err = mempool.Insert(ctx, baseTx) + require.NoError(t, err) + + return mempool + }, + expectedDistribution: map[string]uint64{ + mev.LaneName: 1, + free.LaneName: 1, + base.LaneName: 1, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + mempool := tc.mempool() + queryService := service.NewQueryService(mempool) + ctx := context.Background() + + distributionResponse, err := queryService.GetTxDistribution(ctx, &types.GetTxDistributionRequest{}) + require.NoError(t, err) + require.Equal(t, tc.expectedDistribution, distributionResponse.Distribution) + }) + } +} diff --git a/block/service/types/query.pb.go b/block/service/types/query.pb.go new file mode 100644 index 00000000..61f7593f --- /dev/null +++ b/block/service/types/query.pb.go @@ -0,0 +1,633 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: sdk/mempool/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/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 + +// GetTxDistributionRequest is the request type for the Service.GetTxDistribution +// RPC method. +type GetTxDistributionRequest struct { +} + +func (m *GetTxDistributionRequest) Reset() { *m = GetTxDistributionRequest{} } +func (m *GetTxDistributionRequest) String() string { return proto.CompactTextString(m) } +func (*GetTxDistributionRequest) ProtoMessage() {} +func (*GetTxDistributionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_2b59d6882c9c3543, []int{0} +} +func (m *GetTxDistributionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxDistributionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxDistributionRequest.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 *GetTxDistributionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxDistributionRequest.Merge(m, src) +} +func (m *GetTxDistributionRequest) XXX_Size() int { + return m.Size() +} +func (m *GetTxDistributionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxDistributionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxDistributionRequest proto.InternalMessageInfo + +// GetTxDistributionResponse is the response type for the Service.GetTxDistribution +// RPC method. +type GetTxDistributionResponse struct { + // Distribution is a map of lane to the number of transactions in the mempool for that lane. + Distribution map[string]uint64 `protobuf:"bytes,1,rep,name=distribution,proto3" json:"distribution,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (m *GetTxDistributionResponse) Reset() { *m = GetTxDistributionResponse{} } +func (m *GetTxDistributionResponse) String() string { return proto.CompactTextString(m) } +func (*GetTxDistributionResponse) ProtoMessage() {} +func (*GetTxDistributionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2b59d6882c9c3543, []int{1} +} +func (m *GetTxDistributionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTxDistributionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTxDistributionResponse.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 *GetTxDistributionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTxDistributionResponse.Merge(m, src) +} +func (m *GetTxDistributionResponse) XXX_Size() int { + return m.Size() +} +func (m *GetTxDistributionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTxDistributionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTxDistributionResponse proto.InternalMessageInfo + +func (m *GetTxDistributionResponse) GetDistribution() map[string]uint64 { + if m != nil { + return m.Distribution + } + return nil +} + +func init() { + proto.RegisterType((*GetTxDistributionRequest)(nil), "sdk.mempool.v1.GetTxDistributionRequest") + proto.RegisterType((*GetTxDistributionResponse)(nil), "sdk.mempool.v1.GetTxDistributionResponse") + proto.RegisterMapType((map[string]uint64)(nil), "sdk.mempool.v1.GetTxDistributionResponse.DistributionEntry") +} + +func init() { proto.RegisterFile("sdk/mempool/v1/query.proto", fileDescriptor_2b59d6882c9c3543) } + +var fileDescriptor_2b59d6882c9c3543 = []byte{ + // 326 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2a, 0x4e, 0xc9, 0xd6, + 0xcf, 0x4d, 0xcd, 0x2d, 0xc8, 0xcf, 0xcf, 0xd1, 0x2f, 0x33, 0xd4, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, + 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2b, 0x4e, 0xc9, 0xd6, 0x83, 0xca, 0xe9, 0x95, + 0x19, 0x4a, 0xc9, 0xa4, 0xe7, 0xe7, 0xa7, 0xe7, 0xa4, 0xea, 0x27, 0x16, 0x64, 0xea, 0x27, 0xe6, + 0xe5, 0xe5, 0x97, 0x24, 0x96, 0x64, 0xe6, 0xe7, 0x15, 0x43, 0x54, 0x2b, 0x49, 0x71, 0x49, 0xb8, + 0xa7, 0x96, 0x84, 0x54, 0xb8, 0x64, 0x16, 0x97, 0x14, 0x65, 0x26, 0x95, 0x82, 0xe4, 0x82, 0x52, + 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x94, 0xf6, 0x32, 0x72, 0x49, 0x62, 0x91, 0x2c, 0x2e, 0xc8, 0xcf, + 0x2b, 0x4e, 0x15, 0x8a, 0xe7, 0xe2, 0x49, 0x41, 0x12, 0x97, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, + 0xb2, 0xd6, 0x43, 0xb5, 0x5e, 0x0f, 0xa7, 0x01, 0x7a, 0xc8, 0x82, 0xae, 0x79, 0x25, 0x45, 0x95, + 0x41, 0x28, 0x06, 0x4a, 0xd9, 0x73, 0x09, 0x62, 0x28, 0x11, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, + 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, + 0x4a, 0x53, 0x25, 0x98, 0x14, 0x18, 0x35, 0x58, 0x82, 0x20, 0x1c, 0x2b, 0x26, 0x0b, 0x46, 0xa3, + 0x05, 0x8c, 0x5c, 0xec, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0x53, 0x18, 0xb9, 0x04, + 0x31, 0x9c, 0x22, 0xa4, 0x41, 0x84, 0x6b, 0xc1, 0x61, 0x21, 0xa5, 0x49, 0xb4, 0xbf, 0x94, 0xb4, + 0x9a, 0x2e, 0x3f, 0x99, 0xcc, 0xa4, 0x22, 0xa4, 0xa4, 0x9f, 0x94, 0x93, 0x9f, 0x9c, 0xad, 0x8b, + 0x16, 0x57, 0xc8, 0x7e, 0x74, 0xf2, 0x3e, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, + 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, + 0x28, 0xc3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xe2, 0xec, 0xcc, + 0x02, 0xdd, 0xdc, 0xd4, 0x32, 0x24, 0x03, 0xc1, 0x2c, 0xfd, 0x62, 0x88, 0xef, 0xf4, 0x4b, 0x2a, + 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x51, 0x6a, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xfb, 0xaf, + 0x0d, 0xf5, 0x1e, 0x02, 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 + +// ServiceClient is the client API for Service service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ServiceClient interface { + // GetTxDistribution returns the distribution of transactions in the mempool. + GetTxDistribution(ctx context.Context, in *GetTxDistributionRequest, opts ...grpc.CallOption) (*GetTxDistributionResponse, error) +} + +type serviceClient struct { + cc grpc1.ClientConn +} + +func NewServiceClient(cc grpc1.ClientConn) ServiceClient { + return &serviceClient{cc} +} + +func (c *serviceClient) GetTxDistribution(ctx context.Context, in *GetTxDistributionRequest, opts ...grpc.CallOption) (*GetTxDistributionResponse, error) { + out := new(GetTxDistributionResponse) + err := c.cc.Invoke(ctx, "/sdk.mempool.v1.Service/GetTxDistribution", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ServiceServer is the server API for Service service. +type ServiceServer interface { + // GetTxDistribution returns the distribution of transactions in the mempool. + GetTxDistribution(context.Context, *GetTxDistributionRequest) (*GetTxDistributionResponse, error) +} + +// UnimplementedServiceServer can be embedded to have forward compatible implementations. +type UnimplementedServiceServer struct { +} + +func (*UnimplementedServiceServer) GetTxDistribution(ctx context.Context, req *GetTxDistributionRequest) (*GetTxDistributionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTxDistribution not implemented") +} + +func RegisterServiceServer(s grpc1.Server, srv ServiceServer) { + s.RegisterService(&_Service_serviceDesc, srv) +} + +func _Service_GetTxDistribution_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTxDistributionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetTxDistribution(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sdk.mempool.v1.Service/GetTxDistribution", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetTxDistribution(ctx, req.(*GetTxDistributionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Service_serviceDesc = grpc.ServiceDesc{ + ServiceName: "sdk.mempool.v1.Service", + HandlerType: (*ServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetTxDistribution", + Handler: _Service_GetTxDistribution_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "sdk/mempool/v1/query.proto", +} + +func (m *GetTxDistributionRequest) 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 *GetTxDistributionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxDistributionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *GetTxDistributionResponse) 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 *GetTxDistributionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTxDistributionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Distribution) > 0 { + for k := range m.Distribution { + v := m.Distribution[k] + baseI := i + i = encodeVarintQuery(dAtA, i, uint64(v)) + i-- + dAtA[i] = 0x10 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintQuery(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintQuery(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GetTxDistributionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetTxDistributionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Distribution) > 0 { + for k, v := range m.Distribution { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovQuery(uint64(len(k))) + 1 + sovQuery(uint64(v)) + n += mapEntrySize + 1 + sovQuery(uint64(mapEntrySize)) + } + } + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GetTxDistributionRequest) 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 ErrIntOverflowQuery + } + 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: GetTxDistributionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxDistributionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTxDistributionResponse) 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 ErrIntOverflowQuery + } + 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: GetTxDistributionResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTxDistributionResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Distribution", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Distribution == nil { + m.Distribution = make(map[string]uint64) + } + var mapkey string + var mapvalue uint64 + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthQuery + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthQuery + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + } else { + iNdEx = entryPreIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Distribution[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrIntOverflowQuery + } + 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, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/block/service/types/query.pb.gw.go b/block/service/types/query.pb.gw.go new file mode 100644 index 00000000..5f21b0b9 --- /dev/null +++ b/block/service/types/query.pb.gw.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: sdk/mempool/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +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_Service_GetTxDistribution_0(ctx context.Context, marshaler runtime.Marshaler, client ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxDistributionRequest + var metadata runtime.ServerMetadata + + msg, err := client.GetTxDistribution(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Service_GetTxDistribution_0(ctx context.Context, marshaler runtime.Marshaler, server ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTxDistributionRequest + var metadata runtime.ServerMetadata + + msg, err := server.GetTxDistribution(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterServiceHandlerServer registers the http handlers for service Service to "mux". +// UnaryRPC :call ServiceServer 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 RegisterServiceHandlerFromEndpoint instead. +func RegisterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ServiceServer) error { + + mux.Handle("GET", pattern_Service_GetTxDistribution_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_Service_GetTxDistribution_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_Service_GetTxDistribution_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterServiceHandlerFromEndpoint is same as RegisterServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterServiceHandlerFromEndpoint(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 RegisterServiceHandler(ctx, mux, conn) +} + +// RegisterServiceHandler registers the http handlers for service Service to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterServiceHandlerClient(ctx, mux, NewServiceClient(conn)) +} + +// RegisterServiceHandlerClient registers the http handlers for service Service +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "ServiceClient" to call the correct interceptors. +func RegisterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ServiceClient) error { + + mux.Handle("GET", pattern_Service_GetTxDistribution_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_Service_GetTxDistribution_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Service_GetTxDistribution_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Service_GetTxDistribution_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"block-sdk", "mempool", "v1", "distribution"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Service_GetTxDistribution_0 = runtime.ForwardResponseMessage +) diff --git a/proto/sdk/mempool/v1/query.proto b/proto/sdk/mempool/v1/query.proto new file mode 100644 index 00000000..a89d6038 --- /dev/null +++ b/proto/sdk/mempool/v1/query.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package sdk.mempool.v1; + +option go_package = "github.com/skip-mev/block-sdk/block/service/types"; + +import "google/api/annotations.proto"; + +// Service defines the gRPC querier service for the Block SDK mempool. +service Service { + // GetTxDistribution returns the distribution of transactions in the mempool. + rpc GetTxDistribution(GetTxDistributionRequest) returns (GetTxDistributionResponse) { + option (google.api.http) = { + get: "/block-sdk/mempool/v1/distribution" + }; + } +} + +// GetTxDistributionRequest is the request type for the Service.GetTxDistribution +// RPC method. +message GetTxDistributionRequest {} + +// GetTxDistributionResponse is the response type for the Service.GetTxDistribution +// RPC method. +message GetTxDistributionResponse { + // Distribution is a map of lane to the number of transactions in the mempool for that lane. + map distribution = 1; +} diff --git a/tests/app/app.go b/tests/app/app.go index 71aba235..e9842a6a 100644 --- a/tests/app/app.go +++ b/tests/app/app.go @@ -63,6 +63,7 @@ import ( "github.com/skip-mev/block-sdk/abci" "github.com/skip-mev/block-sdk/block" "github.com/skip-mev/block-sdk/block/base" + service "github.com/skip-mev/block-sdk/block/service" "github.com/skip-mev/block-sdk/lanes/mev" auctionmodule "github.com/skip-mev/block-sdk/x/auction" auctionkeeper "github.com/skip-mev/block-sdk/x/auction/keeper" @@ -428,13 +429,31 @@ func (app *TestApp) SimulationManager() *module.SimulationManager { // RegisterAPIRoutes registers all application module routes with the provided // API server. func (app *TestApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + // Register the base app API routes. app.App.RegisterAPIRoutes(apiSvr, apiConfig) + + // Register the Block SDK mempool API routes. + service.RegisterGRPCGatewayRoutes(apiSvr.ClientCtx, apiSvr.GRPCGatewayRouter) + // register swagger API in app.go so that other applications can override easily if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { panic(err) } } +// RegisterTxService implements the Application.RegisterTxService method. +func (app *TestApp) RegisterTxService(clientCtx client.Context) { + // Register the base app transaction service. + app.App.RegisterTxService(clientCtx) + + // Register the Block SDK mempool transaction service. + mempool, ok := app.App.Mempool().(block.Mempool) + if !ok { + panic("mempool is not a block.Mempool") + } + service.RegisterMempoolService(app.GRPCQueryRouter(), mempool) +} + // GetMaccPerms returns a copy of the module account permissions // // NOTE: This is solely to be used for testing purposes. diff --git a/tests/integration/block_sdk_suite.go b/tests/integration/block_sdk_suite.go index 4c8a4e7d..72499985 100644 --- a/tests/integration/block_sdk_suite.go +++ b/tests/integration/block_sdk_suite.go @@ -12,6 +12,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/skip-mev/block-sdk/lanes/base" + "github.com/skip-mev/block-sdk/lanes/free" interchaintest "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -121,6 +123,12 @@ func (s *IntegrationTestSuite) TestQueryParams() { require.NoError(s.T(), params.Validate()) } +func (s *IntegrationTestSuite) TestMempoolService() { + resp, err := QueryMempool(s.T(), s.chain) + s.Require().NoError(err) + s.Require().Len(resp.Distribution, 3) +} + // TestValidBids tests the execution of various valid auction bids. There are a few // invariants that are tested: // @@ -1298,6 +1306,10 @@ func (s *IntegrationTestSuite) TestNetwork() { s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{normalTx}) } } + + resp, err := QueryMempool(s.T(), s.chain) + s.NoError(err) + s.Require().True(resp.Distribution[base.LaneName] > 0) } } }) @@ -1322,6 +1334,10 @@ func (s *IntegrationTestSuite) TestNetwork() { s.BroadcastTxs(context.Background(), s.chain.(*cosmos.CosmosChain), []Tx{freeTx}) } } + + resp, err := QueryMempool(s.T(), s.chain) + s.NoError(err) + s.Require().True(resp.Distribution[free.LaneName] > 0) } } }) diff --git a/tests/integration/chain_setup.go b/tests/integration/chain_setup.go index 1d2d9c9b..e4bc41ee 100644 --- a/tests/integration/chain_setup.go +++ b/tests/integration/chain_setup.go @@ -37,6 +37,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + servicetypes "github.com/skip-mev/block-sdk/block/service/types" auctiontypes "github.com/skip-mev/block-sdk/x/auction/types" ) @@ -367,6 +368,18 @@ func QueryValidators(t *testing.T, chain *cosmos.CosmosChain) []sdk.ValAddress { return addrs } +// QueryMempool queries the mempool of the given chain +func QueryMempool(t *testing.T, chain ibc.Chain) (*servicetypes.GetTxDistributionResponse, error) { + // get grpc client of the node + grpcAddr := chain.GetHostGRPCAddress() + cc, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + + require.NoError(t, err) + + client := servicetypes.NewServiceClient(cc) + return client.GetTxDistribution(context.Background(), &servicetypes.GetTxDistributionRequest{}) +} + // QueryAccountBalance queries a given account's balance on the chain func QueryAccountBalance(t *testing.T, chain ibc.Chain, address, denom string) int64 { // cast the chain to a cosmos-chain diff --git a/testutils/utils.go b/testutils/utils.go index b0a2ce40..ccdd7be4 100644 --- a/testutils/utils.go +++ b/testutils/utils.go @@ -2,7 +2,11 @@ package testutils import ( "math/rand" + "testing" + "cosmossdk.io/log" + "cosmossdk.io/math" + storetypes "cosmossdk.io/store/types" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" @@ -12,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" @@ -20,6 +25,13 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmos/gogoproto/proto" + signerextraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter" + "github.com/skip-mev/block-sdk/block" + "github.com/skip-mev/block-sdk/block/base" + "github.com/skip-mev/block-sdk/block/mocks" + defaultlane "github.com/skip-mev/block-sdk/lanes/base" + "github.com/skip-mev/block-sdk/lanes/free" + "github.com/skip-mev/block-sdk/lanes/mev" auctiontypes "github.com/skip-mev/block-sdk/x/auction/types" ) @@ -30,6 +42,66 @@ type EncodingConfig struct { Amino *codec.LegacyAmino } +// CreateBaseSDKContext creates a base sdk context with the default store key and transient key. +func CreateBaseSDKContext(t *testing.T) sdk.Context { + key := storetypes.NewKVStoreKey(auctiontypes.StoreKey) + + testCtx := testutil.DefaultContextWithDB( + t, + key, + storetypes.NewTransientStoreKey("transient_test"), + ) + + return testCtx.Ctx +} + +func CreateMempool() *block.LanedMempool { + encodingConfig := CreateTestEncodingConfig() + signerExtractor := signerextraction.NewDefaultAdapter() + + mevConfig := base.LaneConfig{ + SignerExtractor: signerExtractor, + Logger: log.NewNopLogger(), + TxEncoder: encodingConfig.TxConfig.TxEncoder(), + TxDecoder: encodingConfig.TxConfig.TxDecoder(), + AnteHandler: nil, + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, // unlimited + } + factory := mev.NewDefaultAuctionFactory(encodingConfig.TxConfig.TxDecoder(), signerExtractor) + mevLane := mev.NewMEVLane(mevConfig, factory, factory.MatchHandler()) + + freeConfig := base.LaneConfig{ + SignerExtractor: signerExtractor, + Logger: log.NewNopLogger(), + TxEncoder: encodingConfig.TxConfig.TxEncoder(), + TxDecoder: encodingConfig.TxConfig.TxDecoder(), + AnteHandler: nil, + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, // unlimited + } + freeLane := free.NewFreeLane[string](freeConfig, base.DefaultTxPriority(), free.DefaultMatchHandler()) + + defaultConfig := base.LaneConfig{ + SignerExtractor: signerExtractor, + Logger: log.NewNopLogger(), + TxEncoder: encodingConfig.TxConfig.TxEncoder(), + TxDecoder: encodingConfig.TxConfig.TxDecoder(), + AnteHandler: nil, + MaxBlockSpace: math.LegacyZeroDec(), + MaxTxs: 0, // unlimited + } + defaultLane := defaultlane.NewDefaultLane(defaultConfig, base.DefaultMatchHandler()) + + lanes := []block.Lane{mevLane, freeLane, defaultLane} + mempool, err := block.NewLanedMempool(log.NewNopLogger(), lanes, mocks.MockLaneFetcher{}) + if err != nil { + panic(err) + } + + return mempool +} + func CreateTestEncodingConfig() EncodingConfig { interfaceRegistry, err := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{ ProtoFiles: proto.HybridResolver,