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

API v0.2 (external), builder file config #5

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions builders.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
builders:
- name: builder
api: v0.1
url: http://127.0.0.1:8545
internal: true
23 changes: 8 additions & 15 deletions cmd/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,11 @@ var (
defaultRedisEndpoint = getEnvOrDefault("REDIS_ENDPOINT", "redis://localhost:6379")
defaultSimulationsEndpoint = getEnvOrDefault("SIMULATION_ENDPOINTS", "http://127.0.0.1:8545")
defaultWorkersPerNode = getEnvOrDefault("WORKERS_PER_SIM_ENDPOINT", "2")
defaultBuildersEndpoint = getEnvOrDefault("BUILDER_ENDPOINTS", "http://127.0.0.1:8545")
defaultPostgresDSN = getEnvOrDefault("POSTGRES_DSN", "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable")
defaultEthEndpoint = getEnvOrDefault("ETH_ENDPOINT", "http://127.0.0.1:8545")
defaultMevSimBundleRateLimit = getEnvOrDefault("MEV_SIM_BUNDLE_RATE_LIMIT", "5")
// See `ParseExternalBuilders` external_builders.go for more info
defaultExternalBuilders = getEnvOrDefault("EXTERNAL_BUILDERS", "")
// See `LoadBuilderConfig` builders.go for more info
defaultBuildersConfig = getEnvOrDefault("BUILDERS_CONFIG", "builders.yaml")
defaultShareGasUsed = getEnvOrDefault("SHARE_GAS_USED", "0")
defaultShareMevGasPrice = getEnvOrDefault("SHARE_MEV_GAS_PRICE", "1")

Expand All @@ -55,11 +54,10 @@ var (
redisPtr = flag.String("redis", defaultRedisEndpoint, "redis url string")
simEndpointPtr = flag.String("sim-endpoint", defaultSimulationsEndpoint, "simulation endpoints (comma separated)")
workersPerNodePtr = flag.String("workers-per-node", defaultWorkersPerNode, "number of workers per simulation node")
buildersEndpointPtr = flag.String("builder-endpoint", defaultBuildersEndpoint, "builder endpoint")
postgresDSNPtr = flag.String("postgres-dsn", defaultPostgresDSN, "postgres dsn")
ethPtr = flag.String("eth", defaultEthEndpoint, "eth endpoint")
meVSimBundleRateLimitPtr = flag.String("mev-sim-bundle-rate-limit", defaultMevSimBundleRateLimit, "mev sim bundle rate limit for external users (calls per second)")
externalBuildersPtr = flag.String("external-builders", defaultExternalBuilders, "external builders (e.g. name,rpc1,api;name,rpc2,api)")
buildersPtr = flag.String("external-builders", defaultBuildersConfig, "external builders YAML config file")
shareGasUsedPtr = flag.String("share-gas-used", defaultShareGasUsed, "share gas used in hints (0-1)")
shareMevGasPricePtr = flag.String("share-mev-gas-price", defaultShareMevGasPrice, "share mev gas price in hints (0-1)")
)
Expand Down Expand Up @@ -108,12 +106,6 @@ func main() {
logger.Fatal("Failed to create redis hint backend", zap.Error(err))
}

var builderBackends []mevshare.BuilderBackend //nolint:prealloc
for _, builderEndpoint := range strings.Split(*buildersEndpointPtr, ",") {
builderBackend := mevshare.NewJSONRPCBuilder(builderEndpoint)
builderBackends = append(builderBackends, builderBackend)
}

ethBackend, err := ethclient.Dial(*ethPtr)
if err != nil {
logger.Fatal("Failed to connect to ethBackend endpoint", zap.Error(err))
Expand All @@ -124,14 +116,14 @@ func main() {
logger.Fatal("Failed to create postgres backend", zap.Error(err))
}

externalBuilders, err := mevshare.ParseExternalBuilders(*externalBuildersPtr)
builders, err := mevshare.LoadBuilderConfig(*buildersPtr)
if err != nil {
logger.Fatal("Failed to parse external builders", zap.Error(err))
logger.Fatal("Failed to load builder config", zap.Error(err))
}

shareGasUsed := *shareGasUsedPtr == "1"
shareMevGasPrice := *shareMevGasPricePtr == "1"
simResultBackend := mevshare.NewSimulationResultBackend(logger, hintBackend, builderBackends, ethBackend, dbBackend, externalBuilders, shareGasUsed, shareMevGasPrice)
simResultBackend := mevshare.NewSimulationResultBackend(logger, hintBackend, builders, ethBackend, dbBackend, shareGasUsed, shareMevGasPrice)

redisQueue := simqueue.NewRedisQueue(logger, redisClient, "node")

Expand Down Expand Up @@ -159,10 +151,11 @@ func main() {

cachingEthBackend := mevshare.NewCachingEthClient(ethBackend)

api := mevshare.NewAPI(logger, simQueue, dbBackend, cachingEthBackend, signer, simBackends, rate.Limit(rateLimit), builderBackends)
api := mevshare.NewAPI(logger, simQueue, dbBackend, cachingEthBackend, signer, simBackends, rate.Limit(rateLimit), builders)

jsonRPCServer, err := jsonrpcserver.NewHandler(jsonrpcserver.Methods{
"mev_sendBundle": api.SendBundle,
"mev_sendMergedBundle": api.SendMergedBundle,
"mev_simBundle": api.SimBundle,
"mev_cancelBundleByHash": api.CancelBundleByHash,
})
Expand Down
51 changes: 36 additions & 15 deletions mevshare/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import (
)

var (
ErrInvalidSendBundleArgument = errors.New("invalid mev_sendBundle argument")
ErrInvalidSendMergedBundleArgument = errors.New("invalid mev_sendMergedBundle argument")

ErrInvalidInclusion = errors.New("invalid inclusion")
ErrInvalidBundleBodySize = errors.New("invalid bundle body size")
ErrInvalidBundleBody = errors.New("invalid bundle body")
Expand All @@ -32,11 +35,11 @@ var (
)

type SimScheduler interface {
ScheduleBundleSimulation(ctx context.Context, bundle *SendMevBundleArgs, highPriority bool) error
ScheduleBundleSimulation(ctx context.Context, bundle *SendMevBundleArgsV1, highPriority bool) error
}

type BundleStorage interface {
GetBundle(ctx context.Context, hash common.Hash) (*SendMevBundleArgs, error)
GetBundle(ctx context.Context, hash common.Hash) (*SendMevBundleArgsV1, error)
CancelBundleByHash(ctx context.Context, hash common.Hash, signer common.Address) error
}

Expand All @@ -53,12 +56,12 @@ type API struct {
signer types.Signer
simBackends []SimulationBackend
simRateLimiter *rate.Limiter
builders []BuilderBackend
builders BuildersBackend

knownBundleCache *lru.Cache[common.Hash, struct{}]
}

func NewAPI(log *zap.Logger, scheduler SimScheduler, bundleStorage BundleStorage, eth EthClient, signer types.Signer, simBackends []SimulationBackend, simRateLimit rate.Limit, builders []BuilderBackend) *API {
func NewAPI(log *zap.Logger, scheduler SimScheduler, bundleStorage BundleStorage, eth EthClient, signer types.Signer, simBackends []SimulationBackend, simRateLimit rate.Limit, builders BuildersBackend) *API {
return &API{
log: log,

Expand All @@ -74,16 +77,30 @@ func NewAPI(log *zap.Logger, scheduler SimScheduler, bundleStorage BundleStorage
}
}

func (m *API) SendBundle(ctx context.Context, bundle SendMevBundleArgs) (SendMevBundleResponse, error) {
func (m *API) SendBundle(ctx context.Context, union SendBundleUnion) (SendMevBundleResponse, error) {
logger := m.log

var bundle *SendMevBundleArgsV1
if union.v1bundle != nil {
bundle = union.v1bundle
} else if union.v2bundle != nil {
b, err := union.v2bundle.ToV1Bundle()
if err != nil {
logger.Warn("failed to convert bundle", zap.Error(err))
return SendMevBundleResponse{}, ErrInvalidSendBundleArgument
}
bundle = b
} else {
return SendMevBundleResponse{}, ErrInvalidSendBundleArgument
}

currentBlock, err := m.eth.BlockNumber(ctx)
if err != nil {
logger.Error("failed to get current block", zap.Error(err))
return SendMevBundleResponse{}, ErrInternalServiceError
}

hash, hasUnmatchedHash, err := ValidateBundle(&bundle, currentBlock, m.signer)
hash, hasUnmatchedHash, err := ValidateBundle(bundle, currentBlock, m.signer)
if err != nil {
logger.Warn("failed to validate bundle", zap.Error(err))
return SendMevBundleResponse{}, err
Expand Down Expand Up @@ -129,15 +146,15 @@ func (m *API) SendBundle(ctx context.Context, bundle SendMevBundleArgs) (SendMev
refundPercent = *unmatchedBundle.Privacy.WantRefund
}
bundle.Validity.Refund = []RefundConstraint{{0, refundPercent}}
MergePrivacyBuilders(&bundle)
MergePrivacyBuilders(bundle)
err = MergeInclusionIntervals(&bundle.Inclusion, &unmatchedBundle.Inclusion)
if err != nil {
return SendMevBundleResponse{}, ErrBackrunInclusion
}
}

highPriority := jsonrpcserver.GetPriority(ctx)
err = m.scheduler.ScheduleBundleSimulation(ctx, &bundle, highPriority)
err = m.scheduler.ScheduleBundleSimulation(ctx, bundle, highPriority)
if err != nil {
logger.Error("Failed to schedule bundle simulation", zap.Error(err))
return SendMevBundleResponse{}, ErrInternalServiceError
Expand All @@ -148,7 +165,16 @@ func (m *API) SendBundle(ctx context.Context, bundle SendMevBundleArgs) (SendMev
}, nil
}

func (m *API) SimBundle(ctx context.Context, bundle SendMevBundleArgs, aux SimMevBundleAuxArgs) (*SimMevBundleResponse, error) {
func (m *API) SendMergedBundle(ctx context.Context, union SendMergedBundleArgsV2) (SendMevBundleResponse, error) {
bundle, err := union.ToV1Bundle()
if err != nil {
return SendMevBundleResponse{}, ErrInvalidSendMergedBundleArgument
}

return m.SendBundle(ctx, SendBundleUnion{v1bundle: bundle, v2bundle: nil})
}

func (m *API) SimBundle(ctx context.Context, bundle SendMevBundleArgsV1, aux SimMevBundleAuxArgs) (*SimMevBundleResponse, error) {
if len(m.simBackends) == 0 {
return nil, ErrInternalServiceError
}
Expand Down Expand Up @@ -181,12 +207,7 @@ func (m *API) CancelBundleByHash(ctx context.Context, hash common.Hash) error {
return ErrBundleNotCancelled
}

for _, builder := range m.builders {
err := builder.CancelBundleByHash(ctx, hash)
if err != nil {
m.log.Warn("Failed to cancel bundle by hash", zap.Error(err), zap.String("builder", builder.String()))
}
}
m.builders.CancelBundleByHash(ctx, m.log, hash)
m.log.Info("Bundle cancelled", zap.String("hash", hash.Hex()))
return nil
}
89 changes: 44 additions & 45 deletions mevshare/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"encoding/json"

"github.com/ethereum/go-ethereum/common"
"github.com/redis/go-redis/v9"
"github.com/ybbus/jsonrpc/v3"
)
Expand All @@ -13,16 +12,16 @@ type HintBackend interface {
NotifyHint(ctx context.Context, hint *Hint) error
}

type BuilderBackend interface {
String() string
SendMatchedShareBundle(ctx context.Context, bundle *SendMevBundleArgs) error
CancelBundleByHash(ctx context.Context, hash common.Hash) error
}
//type BuilderBackend interface {
// String() string
// SendMatchedShareBundle(ctx context.Context, bundle *SendMevBundleArgsV1) error
// CancelBundleByHash(ctx context.Context, hash common.Hash) error
//}

// SimulationBackend is an interface for simulating transactions
// There should be one simulation backend per worker node
type SimulationBackend interface {
SimulateBundle(ctx context.Context, bundle *SendMevBundleArgs, aux *SimMevBundleAuxArgs) (*SimMevBundleResponse, error)
SimulateBundle(ctx context.Context, bundle *SendMevBundleArgsV1, aux *SimMevBundleAuxArgs) (*SimMevBundleResponse, error)
}

type JSONRPCSimulationBackend struct {
Expand All @@ -35,7 +34,7 @@ func NewJSONRPCSimulationBackend(url string) *JSONRPCSimulationBackend {
}
}

func (b *JSONRPCSimulationBackend) SimulateBundle(ctx context.Context, bundle *SendMevBundleArgs, aux *SimMevBundleAuxArgs) (*SimMevBundleResponse, error) {
func (b *JSONRPCSimulationBackend) SimulateBundle(ctx context.Context, bundle *SendMevBundleArgsV1, aux *SimMevBundleAuxArgs) (*SimMevBundleResponse, error) {
var result SimMevBundleResponse
err := b.client.CallFor(ctx, &result, "mev_simBundle", bundle, aux)
return &result, err
Expand All @@ -61,40 +60,40 @@ func (b *RedisHintBackend) NotifyHint(ctx context.Context, hint *Hint) error {
return b.client.Publish(ctx, b.pubChannel, data).Err()
}

type JSONRPCBuilder struct {
url string
client jsonrpc.RPCClient
}

func NewJSONRPCBuilder(url string) *JSONRPCBuilder {
return &JSONRPCBuilder{
url: url,
client: jsonrpc.NewClient(url),
}
}

func (b *JSONRPCBuilder) String() string {
return b.url
}

func (b *JSONRPCBuilder) SendMatchedShareBundle(ctx context.Context, bundle *SendMevBundleArgs) error {
res, err := b.client.Call(ctx, "mev_sendBundle", []*SendMevBundleArgs{bundle})
if err != nil {
return err
}
if res.Error != nil {
return res.Error
}
return nil
}

func (b *JSONRPCBuilder) CancelBundleByHash(ctx context.Context, hash common.Hash) error {
res, err := b.client.Call(ctx, "mev_cancelBundleByHash", []common.Hash{hash})
if err != nil {
return err
}
if res.Error != nil {
return res.Error
}
return nil
}
//type JSONRPCBuilder struct {
// url string
// client jsonrpc.RPCClient
//}
//
//func NewJSONRPCBuilder(url string) *JSONRPCBuilder {
// return &JSONRPCBuilder{
// url: url,
// client: jsonrpc.NewClient(url),
// }
//}
//
//func (b *JSONRPCBuilder) String() string {
// return b.url
//}
//
//func (b *JSONRPCBuilder) SendMatchedShareBundle(ctx context.Context, bundle *SendMevBundleArgsV1) error {
// res, err := b.client.Call(ctx, "mev_sendBundle", []*SendMevBundleArgsV1{bundle})
// if err != nil {
// return err
// }
// if res.Error != nil {
// return res.Error
// }
// return nil
//}
//
//func (b *JSONRPCBuilder) CancelBundleByHash(ctx context.Context, hash common.Hash) error {
// res, err := b.client.Call(ctx, "mev_cancelBundleByHash", []common.Hash{hash})
// if err != nil {
// return err
// }
// if res.Error != nil {
// return res.Error
// }
// return nil
//}
Loading