Skip to content

Commit e9e8c41

Browse files
authored
feat: support full MEV (#115)
There are some immediate FLUPs here 1. Replace `h4ck3rk3y/builder` with `flashbots/builder` ( pending publishing of builder) 2. Replace `h4ck3rk3y/mev-boost-relay` with `flashbots/mev-boost-relay` (pending Capella signature fix)
1 parent 373c6c9 commit e9e8c41

6 files changed

Lines changed: 214 additions & 14 deletions

File tree

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,26 @@ To configure the package behaviour, you can modify your `eth2-package-params.yam
186186
// Default: None - no mev boost, mev builder, mev flood or relays are spun up
187187
// mock - mock-builder & mev-boost are spun up
188188
// full - mev-boost, relays, flooder and builder are all spun up
189-
"mev_type": None
189+
"mev_type": None,
190+
191+
// Parameters if MEV is used
192+
"mev_params": {
193+
// The image to use for MEV boot relay
194+
// This uses the h4ck3rk3y image instead of the flashbots image as that isn't published yet
195+
"mev_relay_image": "h4ck3rk3y/mev-boost-relay",
196+
// Extra parameters to send to the API
197+
"mev_relay_api_extra_args": [],
198+
// Extra parameters to send to the housekeeper
199+
"mev_relay_housekeeper_extra_args": [],
200+
// Extra parameters to send to the website
201+
"mev_relay_website_extra_args": [],
202+
// Extra parameters to send to the builder
203+
"mev_builder_extra_args": [],
204+
// Image to use for mev-flood
205+
"mev_flood_image": "flashbots/mev-flood",
206+
// Extra parameters to send to mev-flood
207+
"mev_flood_extra_args": []
208+
}
190209
}
191210
```
192211

main.star

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ grafana =import_module("github.com/kurtosis-tech/eth2-package/src/grafana/grafan
1313
testnet_verifier = import_module("github.com/kurtosis-tech/eth2-package/src/testnet_verifier/testnet_verifier.star")
1414
mev_boost_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_boost/mev_boost_launcher.star")
1515
mock_mev_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mock_mev/mock_mev_launcher.star")
16+
mev_relay_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_relay/mev_relay_launcher.star")
17+
mev_flood_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_flood/mev_flood_launcher.star")
1618

1719
GRAFANA_USER = "admin"
1820
GRAFANA_PASSWORD = "admin"
@@ -23,12 +25,15 @@ HTTP_PORT_ID_FOR_FACT = "http"
2325

2426
MEV_BOOST_SHOULD_CHECK_RELAY = True
2527
MOCK_MEV_TYPE = "mock"
28+
FULL_MEV_TYPE = "full"
29+
PATH_TO_PARSED_BEACON_STATE = "/genesis/output/parsedBeaconState.json"
2630

2731
def run(plan, args):
2832
args_with_right_defaults, args_with_defaults_dict = parse_input.parse_input(args)
2933

3034
num_participants = len(args_with_right_defaults.participants)
3135
network_params = args_with_right_defaults.network_params
36+
mev_params = args_with_right_defaults.mev_params
3237

3338
grafana_datasource_config_template = read_file(static_files.GRAFANA_DATASOURCE_CONFIG_TEMPLATE_FILEPATH)
3439
grafana_dashboards_config_template = read_file(static_files.GRAFANA_DASHBOARD_PROVIDERS_CONFIG_TEMPLATE_FILEPATH)
@@ -37,7 +42,7 @@ def run(plan, args):
3742
plan.print("Read the prometheus, grafana templates")
3843

3944
plan.print("Launching participant network with {0} participants and the following network params {1}".format(num_participants, network_params))
40-
all_participants, cl_genesis_timestamp, _ = eth_network_module.run(plan, args_with_defaults_dict)
45+
all_participants, cl_genesis_timestamp, genesis_validators_root = eth_network_module.run(plan, args_with_defaults_dict)
4146

4247
all_el_client_contexts = []
4348
all_cl_client_contexts = []
@@ -57,8 +62,29 @@ def run(plan, args):
5762
beacon_uri = "{0}:{1}".format(all_cl_client_contexts[0].ip_addr, all_cl_client_contexts[0].http_port_num)
5863
jwt_secret = all_el_client_contexts[0].jwt_secret
5964
endpoint = mock_mev_launcher_module.launch_mock_mev(plan, el_uri, beacon_uri, jwt_secret)
65+
mev_endpoints.append(endpoint)
66+
elif args_with_right_defaults.mev_type and args_with_right_defaults.mev_type == FULL_MEV_TYPE:
67+
el_uri = "http://{0}:{1}".format(all_el_client_contexts[0].ip_addr, all_el_client_contexts[0].rpc_port_num)
68+
builder_uri = "http://{0}:{1}".format(all_el_client_contexts[-1].ip_addr, all_el_client_contexts[-1].rpc_port_num)
69+
beacon_uri = ["http://{0}:{1}".format(context.ip_addr, context.http_port_num) for context in all_cl_client_contexts][-1]
70+
beacon_uris = beacon_uri
71+
first_cl_client = all_cl_client_contexts[0]
72+
first_client_beacon_name = first_cl_client.beacon_service_name
73+
mev_flood_module.launch_mev_flood(plan, mev_params.mev_flood_image, el_uri)
74+
epoch_recipe = GetHttpRequestRecipe(
75+
endpoint = "/eth/v1/beacon/blocks/head",
76+
port_id = HTTP_PORT_ID_FOR_FACT,
77+
extract = {
78+
"epoch": ".data.message.body.attestations[0].data.target.epoch"
79+
}
80+
)
81+
plan.wait(recipe = epoch_recipe, field = "extract.epoch", assertion = ">=", target_value = str(network_params.capella_fork_epoch), timeout = "20m", service_name = first_client_beacon_name)
82+
plan.print("epoch 2 reached, can begin mev stuff")
83+
endpoint = mev_relay_launcher_module.launch_mev_relay(plan, mev_params, network_params.network_id, beacon_uris, genesis_validators_root, builder_uri)
84+
mev_flood_module.spam_in_background(plan, el_uri, mev_params.mev_flood_extra_args)
6085
mev_endpoints.append(endpoint)
6186

87+
6288
# spin up the mev boost contexts if some endpoints for relays have been passed
6389
all_mevboost_contexts = []
6490
if mev_endpoints:

src/mev_boost/mev_boost_launcher.star

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ def get_config(mev_boost_launcher, network_id):
3434
ports = USED_PORTS,
3535
cmd = command,
3636
env_vars = {
37-
# TODO remove the hardcoding
37+
# TODO(maybe) remove the hardcoding
3838
# This is set to match this file https://github.com/kurtosis-tech/eth-network-package/blob/main/static_files/genesis-generation-config/cl/config.yaml.tmpl#L11
39+
# latest-notes
40+
# does this need genesis time to be set as well
3941
"GENESIS_FORK_VERSION": "0x10000038",
4042
"BOOST_LISTEN_ADDR": "0.0.0.0:{0}".format(parse_input.FLASHBOTS_MEV_BOOST_PORT),
41-
"SKIP_RELAY_SIGNATURE_CHECK": "true",
43+
# maybe this is breaking; this isn't verifyign the bid and not sending it to the validator
44+
"SKIP_RELAY_SIGNATURE_CHECK": "1",
4245
"RELAYS": mev_boost_launcher.relay_end_points[0]
4346
}
4447
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
ADMIN_KEY = "0xef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2"
2+
USER_KEY = "0x7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31"
3+
4+
def launch_mev_flood(plan, image, el_uri):
5+
plan.add_service(
6+
name = "mev-flood",
7+
config = ServiceConfig(
8+
image = image,
9+
entrypoint = ["/bin/sh", "-c", "touch main.log && tail -F main.log"]
10+
)
11+
)
12+
13+
plan.exec(
14+
service_name = "mev-flood",
15+
recipe = ExecRecipe(
16+
command = ["/bin/sh", "-c", "./run init -r {0} -k {1} -u {2} -s deployment.json".format(el_uri, ADMIN_KEY, USER_KEY)]
17+
)
18+
)
19+
20+
def spam_in_background(plan, el_uri, mev_flood_extra_args):
21+
command = ["/bin/sh", "-c", "nohup ./run spam -r {0} -k {1} -u {2} -l deployment.json --secondsPerBundle 15 >main.log 2>&1 &".format(el_uri, ADMIN_KEY, USER_KEY)]
22+
if mev_flood_extra_args:
23+
joined_extra_args = " ".join(mev_flood_extra_args)
24+
command = ["/bin/sh", "-c", "nohup ./run spam -r {0} -k {1} -u {2} -l deployment.json --secondsPerBundle 15 {3} >main.log 2>&1 &".format(el_uri, ADMIN_KEY, USER_KEY, joined_extra_args)]
25+
plan.exec(
26+
service_name = "mev-flood",
27+
recipe = ExecRecipe(
28+
command = command
29+
)
30+
)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
redis_module = import_module("github.com/kurtosis-tech/redis-package/main.star")
2+
postgres_module = import_module("github.com/kurtosis-tech/postgres-package/main.star")
3+
4+
DUMMY_SECRET_KEY = "0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2"
5+
DUMMY_PUB_KEY = "0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5"
6+
7+
MEV_RELAY_WEBSITE = "mev-relay-website"
8+
MEV_RELAY_ENDPOINT = "mev-relay-api"
9+
MEV_RELAY_HOUSEKEEPER = "mev-relay-housekeeper"
10+
11+
MEV_RELAY_ENDPOINT_PORT = 9062
12+
MEV_RELAY_WEBSITE_PORT = 9060
13+
14+
NETWORK_ID_TO_NAME = {
15+
"5": "goerli",
16+
"11155111": "sepolia",
17+
"3": "ropsten",
18+
}
19+
20+
def launch_mev_relay(plan, mev_params, network_id, beacon_uris, validator_root, builder_uri):
21+
redis = redis_module.run(plan, {})
22+
# making the password postgres as the relay expects it to be postgres
23+
postgres = postgres_module.run(plan, {"password": "postgres", "user": "postgres", "database": "postgres", "name": "postgres"})
24+
25+
network_name = NETWORK_ID_TO_NAME.get(network_id, network_id)
26+
27+
image = mev_params.mev_relay_image
28+
29+
# TODO(maybe) remove hardocded values for the forks
30+
env_vars= {
31+
"GENESIS_FORK_VERSION": "0x10000038",
32+
"BELLATRIX_FORK_VERSION": "0x30000038",
33+
"CAPELLA_FORK_VERSION": "0x40000038",
34+
"DENEB_FORK_VERSION": "0x50000038",
35+
"GENESIS_VALIDATORS_ROOT": validator_root
36+
}
37+
38+
plan.add_service(
39+
name = MEV_RELAY_HOUSEKEEPER,
40+
config = ServiceConfig(
41+
image = image,
42+
cmd = ["housekeeper", "--network", "custom", "--db", "postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable", "--redis-uri", "redis:6379", "--beacon-uris", beacon_uris] + mev_params.mev_relay_housekeeper_extra_args,
43+
env_vars= env_vars
44+
)
45+
)
46+
47+
api = plan.add_service(
48+
name = MEV_RELAY_ENDPOINT,
49+
config = ServiceConfig(
50+
image = image,
51+
cmd = ["api", "--network", "custom", "--db", "postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable", "--secret-key", DUMMY_SECRET_KEY, "--listen-addr", "0.0.0.0:{0}".format(MEV_RELAY_ENDPOINT_PORT), "--redis-uri", "redis:6379", "--beacon-uris", beacon_uris, "--blocksim", builder_uri] + mev_params.mev_relay_api_extra_args,
52+
ports = {
53+
"api": PortSpec(number = MEV_RELAY_ENDPOINT_PORT, transport_protocol= "TCP")
54+
},
55+
env_vars= env_vars
56+
)
57+
)
58+
59+
plan.add_service(
60+
name = MEV_RELAY_WEBSITE,
61+
config = ServiceConfig(
62+
image = image,
63+
cmd = ["website", "--network", "custom", "--db", "postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable", "--listen-addr", "0.0.0.0:{0}".format(MEV_RELAY_WEBSITE_PORT), "--redis-uri", "redis:6379", "https://{0}@{1}".format(DUMMY_PUB_KEY, MEV_RELAY_ENDPOINT)] + mev_params.mev_relay_website_extra_args,
64+
ports = {
65+
"api": PortSpec(number = MEV_RELAY_WEBSITE_PORT, transport_protocol= "TCP", application_protocol="http")
66+
},
67+
env_vars= env_vars
68+
)
69+
)
70+
71+
return "http://{0}@{1}:{2}".format(DUMMY_PUB_KEY, api.ip_address, MEV_RELAY_ENDPOINT_PORT)

src/package_io/parse_input.star

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ FLASHBOTS_MEV_BOOST_PORT = 18550
2424
MEV_BOOST_SERVICE_NAME_PREFIX = "mev-boost-"
2525

2626

27+
package_io = import_module("github.com/kurtosis-tech/eth-network-package/package_io/constants.star")
28+
2729
def parse_input(input_args):
2830
result = default_input_args()
2931
for attr in input_args:
@@ -35,6 +37,10 @@ def parse_input(input_args):
3537
for sub_attr in input_args["network_params"]:
3638
sub_value = input_args["network_params"][sub_attr]
3739
result["network_params"][sub_attr] = sub_value
40+
elif attr == "mev_params":
41+
for sub_attr in input_args["mev_params"]:
42+
sub_value = input_args["mev_params"]["sub_attr"]
43+
result["mev_params"][sub_attr] = sub_value
3844
elif attr == "participants":
3945
participants = []
4046
for participant in input_args["participants"]:
@@ -139,6 +145,15 @@ def parse_input(input_args):
139145
deneb_fork_epoch=result["network_params"]["deneb_fork_epoch"],
140146
genesis_delay=result["network_params"]["genesis_delay"]
141147
),
148+
mev_params = struct(
149+
mev_relay_image = result["mev_params"]["mev_relay_image"],
150+
mev_relay_api_extra_args = result["mev_params"]["mev_relay_api_extra_args"],
151+
mev_relay_housekeeper_extra_args = result["mev_params"]["mev_relay_housekeeper_extra_args"],
152+
mev_relay_website_extra_args = result["mev_params"]["mev_relay_website_extra_args"],
153+
mev_builder_extra_args = result["mev_params"]["mev_builder_extra_args"],
154+
mev_flood_image = result["mev_params"]["mev_flood_image"],
155+
mev_flood_extra_args = result["mev_params"]["mev_flood_extra_args"]
156+
),
142157
launch_additional_services=result["launch_additional_services"],
143158
wait_for_finalization=result["wait_for_finalization"],
144159
wait_for_verifications=result["wait_for_verifications"],
@@ -158,6 +173,7 @@ def get_client_log_level_or_default(participant_log_level, global_log_level, cli
158173
def default_input_args():
159174
network_params = default_network_params()
160175
participants = [default_participant()]
176+
mev_params = get_default_mev_params()
161177
return {
162178
"mev_type": None,
163179
"participants": participants,
@@ -167,26 +183,26 @@ def default_input_args():
167183
"wait_for_verifications": False,
168184
"verifications_epoch_limit": 5,
169185
"global_client_log_level": "info",
186+
"mev_params": mev_params,
170187
"snooper_enabled": False,
171188
}
172189

173190
def default_network_params():
174191
# this is temporary till we get params working
175192
return {
176193
"preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete",
177-
"num_validator_keys_per_node": 64,
178-
"network_id": "3151908",
179-
"deposit_contract_address": "0x4242424242424242424242424242424242424242",
180-
"seconds_per_slot": 12,
181-
"slots_per_epoch": 32,
182-
"genesis_delay": 120,
183-
"capella_fork_epoch": 1,
184-
# arbitrarily large while we sort out https://github.com/kurtosis-tech/eth-network-package/issues/42
185-
# this will take 53~ hoours for now
186-
"deneb_fork_epoch": 500,
194+
"num_validator_keys_per_node": 64,
195+
"network_id": "3151908",
196+
"deposit_contract_address": "0x4242424242424242424242424242424242424242",
197+
"seconds_per_slot": 12,
198+
"slots_per_epoch": 32,
199+
"genesis_delay": 120,
200+
"capella_fork_epoch": 1,
201+
"deneb_fork_epoch": 500
187202
}
188203

189204
def default_participant():
205+
# TODO(now) add support for mev boost image and extra parameters
190206
return {
191207
"el_client_type": "geth",
192208
"el_client_image": "",
@@ -201,6 +217,18 @@ def default_participant():
201217
"count": 1
202218
}
203219

220+
def get_default_mev_params():
221+
return {
222+
# TODO fix this when Capella Signature verification works - change it to flashbots/
223+
"mev_relay_image": "h4ck3rk3y/mev-boost-relay",
224+
"mev_relay_api_extra_args": [],
225+
"mev_relay_housekeeper_extra_args": [],
226+
"mev_relay_website_extra_args": [],
227+
"mev_builder_extra_args": [],
228+
"mev_flood_image": "flashbots/mev-flood",
229+
"mev_flood_extra_args": []
230+
}
231+
204232

205233
# TODO perhaps clean this up into a map
206234
def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port):
@@ -220,4 +248,27 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port):
220248
if participant["cl_client_type"] == "prysm":
221249
participant["validator_extra_params"].append("--enable-builder")
222250
participant["beacon_extra_params"].append("--http-mev-relay={0}".format(mev_url))
251+
252+
num_participants = len(parsed_arguments_dict["participants"])
253+
254+
mev_url = "http://{0}{1}:{2}".format(mev_prefix, num_participants, mev_port)
255+
256+
mev_participant = {
257+
"el_client_type": "geth",
258+
# TODO replace with actual when flashbots/builder is published
259+
"el_client_image": "h4ck3rk3y/builder",
260+
"el_client_log_level": "",
261+
"cl_client_type": "lighthouse",
262+
# THIS overrides the beacon image
263+
"cl_client_image": "sigp/lighthouse",
264+
"cl_client_log_level": "",
265+
"beacon_extra_params": ["--builder={0}".format(mev_url), "--always-prepare-payload", "--prepare-payload-lookahead", "12000"],
266+
# TODO(maybe) make parts of this more passable like the mev-relay-endpoint & forks
267+
"el_extra_params": ["--builder", "--builder.remote_relay_endpoint=http://mev-relay-api:9062", "--builder.beacon_endpoints=http://cl-{0}-lighthouse-geth:4000".format(num_participants+1), "--builder.bellatrix_fork_version=0x30000038", "--builder.genesis_fork_version=0x10000038", "--builder.genesis_validators_root={0}".format(package_io.GENESIS_VALIDATORS_ROOT_PLACEHOLDER), "--miner.extradata=\"Illuminate Dmocratize Dstribute\"", "--miner.algotype=greedy"] + parsed_arguments_dict["mev_params"]["mev_builder_extra_args"],
268+
"validator_extra_params": ["--builder-proposals"],
269+
"builder_network_params": None
270+
}
271+
272+
parsed_arguments_dict["participants"].append(mev_participant)
273+
223274
return parsed_arguments_dict

0 commit comments

Comments
 (0)