Skip to content
This repository has been archived by the owner on Jan 31, 2025. It is now read-only.

feat: gRPC mempool service #275

Merged
merged 10 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
19 changes: 12 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ 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)
HOMEDIR ?= $(CURDIR)/tests/.testappd
GENESIS ?= $(HOMEDIR)/config/genesis.json
GENESIS_TMP ?= $(HOMEDIR)/config/genesis_tmp.json
COVER_FILE ?= "cover.out"

###############################################################################
Expand Down Expand Up @@ -76,13 +79,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 @@ -32,7 +32,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 @@ -86,11 +86,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())
aljo242 marked this conversation as resolved.
Show resolved Hide resolved
}

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 @@ -288,64 +288,64 @@ func (suite *BlockBusterTestSuite) TestNewMempool() {
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 @@ -365,18 +365,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 @@ -392,8 +392,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 @@ -446,7 +446,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 @@ -455,7 +455,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 @@ -469,7 +469,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 @@ -485,15 +485,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 @@ -512,8 +512,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 @@ -532,8 +532,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))

Check warning on line 47 in block/service/service.go

View check run for this annotation

Codecov / codecov/patch

block/service/service.go#L46-L47

Added lines #L46 - L47 were not covered by tests
}

// 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))

Check warning on line 53 in block/service/service.go

View check run for this annotation

Codecov / codecov/patch

block/service/service.go#L52-L53

Added lines #L52 - L53 were not covered by tests
}
Loading
Loading