Skip to content

Commit

Permalink
feat: gRPC mempool service (#275)
Browse files Browse the repository at this point in the history
* make file nit

* base app set up

* make file update

* makefile nit

* adding service test to e2e

* lint

* type fix

* nit

* unit test + readme

(cherry picked from commit 7d8a695)

# Conflicts:
#	Makefile
  • Loading branch information
davidterpay authored and mergify[bot] committed Dec 8, 2023
1 parent 8fcd596 commit ede4010
Show file tree
Hide file tree
Showing 14 changed files with 1,284 additions and 39 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
23 changes: 16 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 ###
Expand Down Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions block/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
56 changes: 28 additions & 28 deletions block/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,64 +121,64 @@ 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,
},
},
{
"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,
},
Expand All @@ -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()
Expand All @@ -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",
Expand Down Expand Up @@ -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)
Expand All @@ -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; {
Expand All @@ -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)
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand Down
56 changes: 56 additions & 0 deletions block/service/README.md
Original file line number Diff line number Diff line change
@@ -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
```
54 changes: 54 additions & 0 deletions block/service/service.go
Original file line number Diff line number Diff line change
@@ -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))
}
Loading

0 comments on commit ede4010

Please sign in to comment.