Commit e9e8c418 authored by Gyanendra Mishra's avatar Gyanendra Mishra Committed by GitHub

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)
parent 373c6c9b
...@@ -186,7 +186,26 @@ To configure the package behaviour, you can modify your `eth2-package-params.yam ...@@ -186,7 +186,26 @@ To configure the package behaviour, you can modify your `eth2-package-params.yam
// Default: None - no mev boost, mev builder, mev flood or relays are spun up // Default: None - no mev boost, mev builder, mev flood or relays are spun up
// mock - mock-builder & mev-boost are spun up // mock - mock-builder & mev-boost are spun up
// full - mev-boost, relays, flooder and builder are all spun up // full - mev-boost, relays, flooder and builder are all spun up
"mev_type": None "mev_type": None,
// Parameters if MEV is used
"mev_params": {
// The image to use for MEV boot relay
// This uses the h4ck3rk3y image instead of the flashbots image as that isn't published yet
"mev_relay_image": "h4ck3rk3y/mev-boost-relay",
// Extra parameters to send to the API
"mev_relay_api_extra_args": [],
// Extra parameters to send to the housekeeper
"mev_relay_housekeeper_extra_args": [],
// Extra parameters to send to the website
"mev_relay_website_extra_args": [],
// Extra parameters to send to the builder
"mev_builder_extra_args": [],
// Image to use for mev-flood
"mev_flood_image": "flashbots/mev-flood",
// Extra parameters to send to mev-flood
"mev_flood_extra_args": []
}
} }
``` ```
......
...@@ -13,6 +13,8 @@ grafana =import_module("github.com/kurtosis-tech/eth2-package/src/grafana/grafan ...@@ -13,6 +13,8 @@ grafana =import_module("github.com/kurtosis-tech/eth2-package/src/grafana/grafan
testnet_verifier = import_module("github.com/kurtosis-tech/eth2-package/src/testnet_verifier/testnet_verifier.star") testnet_verifier = import_module("github.com/kurtosis-tech/eth2-package/src/testnet_verifier/testnet_verifier.star")
mev_boost_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_boost/mev_boost_launcher.star") mev_boost_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_boost/mev_boost_launcher.star")
mock_mev_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mock_mev/mock_mev_launcher.star") mock_mev_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mock_mev/mock_mev_launcher.star")
mev_relay_launcher_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_relay/mev_relay_launcher.star")
mev_flood_module = import_module("github.com/kurtosis-tech/eth2-package/src/mev_flood/mev_flood_launcher.star")
GRAFANA_USER = "admin" GRAFANA_USER = "admin"
GRAFANA_PASSWORD = "admin" GRAFANA_PASSWORD = "admin"
...@@ -23,12 +25,15 @@ HTTP_PORT_ID_FOR_FACT = "http" ...@@ -23,12 +25,15 @@ HTTP_PORT_ID_FOR_FACT = "http"
MEV_BOOST_SHOULD_CHECK_RELAY = True MEV_BOOST_SHOULD_CHECK_RELAY = True
MOCK_MEV_TYPE = "mock" MOCK_MEV_TYPE = "mock"
FULL_MEV_TYPE = "full"
PATH_TO_PARSED_BEACON_STATE = "/genesis/output/parsedBeaconState.json"
def run(plan, args): def run(plan, args):
args_with_right_defaults, args_with_defaults_dict = parse_input.parse_input(args) args_with_right_defaults, args_with_defaults_dict = parse_input.parse_input(args)
num_participants = len(args_with_right_defaults.participants) num_participants = len(args_with_right_defaults.participants)
network_params = args_with_right_defaults.network_params network_params = args_with_right_defaults.network_params
mev_params = args_with_right_defaults.mev_params
grafana_datasource_config_template = read_file(static_files.GRAFANA_DATASOURCE_CONFIG_TEMPLATE_FILEPATH) grafana_datasource_config_template = read_file(static_files.GRAFANA_DATASOURCE_CONFIG_TEMPLATE_FILEPATH)
grafana_dashboards_config_template = read_file(static_files.GRAFANA_DASHBOARD_PROVIDERS_CONFIG_TEMPLATE_FILEPATH) grafana_dashboards_config_template = read_file(static_files.GRAFANA_DASHBOARD_PROVIDERS_CONFIG_TEMPLATE_FILEPATH)
...@@ -37,7 +42,7 @@ def run(plan, args): ...@@ -37,7 +42,7 @@ def run(plan, args):
plan.print("Read the prometheus, grafana templates") plan.print("Read the prometheus, grafana templates")
plan.print("Launching participant network with {0} participants and the following network params {1}".format(num_participants, network_params)) plan.print("Launching participant network with {0} participants and the following network params {1}".format(num_participants, network_params))
all_participants, cl_genesis_timestamp, _ = eth_network_module.run(plan, args_with_defaults_dict) all_participants, cl_genesis_timestamp, genesis_validators_root = eth_network_module.run(plan, args_with_defaults_dict)
all_el_client_contexts = [] all_el_client_contexts = []
all_cl_client_contexts = [] all_cl_client_contexts = []
...@@ -57,8 +62,29 @@ def run(plan, args): ...@@ -57,8 +62,29 @@ def run(plan, args):
beacon_uri = "{0}:{1}".format(all_cl_client_contexts[0].ip_addr, all_cl_client_contexts[0].http_port_num) beacon_uri = "{0}:{1}".format(all_cl_client_contexts[0].ip_addr, all_cl_client_contexts[0].http_port_num)
jwt_secret = all_el_client_contexts[0].jwt_secret jwt_secret = all_el_client_contexts[0].jwt_secret
endpoint = mock_mev_launcher_module.launch_mock_mev(plan, el_uri, beacon_uri, jwt_secret) endpoint = mock_mev_launcher_module.launch_mock_mev(plan, el_uri, beacon_uri, jwt_secret)
mev_endpoints.append(endpoint)
elif args_with_right_defaults.mev_type and args_with_right_defaults.mev_type == FULL_MEV_TYPE:
el_uri = "http://{0}:{1}".format(all_el_client_contexts[0].ip_addr, all_el_client_contexts[0].rpc_port_num)
builder_uri = "http://{0}:{1}".format(all_el_client_contexts[-1].ip_addr, all_el_client_contexts[-1].rpc_port_num)
beacon_uri = ["http://{0}:{1}".format(context.ip_addr, context.http_port_num) for context in all_cl_client_contexts][-1]
beacon_uris = beacon_uri
first_cl_client = all_cl_client_contexts[0]
first_client_beacon_name = first_cl_client.beacon_service_name
mev_flood_module.launch_mev_flood(plan, mev_params.mev_flood_image, el_uri)
epoch_recipe = GetHttpRequestRecipe(
endpoint = "/eth/v1/beacon/blocks/head",
port_id = HTTP_PORT_ID_FOR_FACT,
extract = {
"epoch": ".data.message.body.attestations[0].data.target.epoch"
}
)
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)
plan.print("epoch 2 reached, can begin mev stuff")
endpoint = mev_relay_launcher_module.launch_mev_relay(plan, mev_params, network_params.network_id, beacon_uris, genesis_validators_root, builder_uri)
mev_flood_module.spam_in_background(plan, el_uri, mev_params.mev_flood_extra_args)
mev_endpoints.append(endpoint) mev_endpoints.append(endpoint)
# spin up the mev boost contexts if some endpoints for relays have been passed # spin up the mev boost contexts if some endpoints for relays have been passed
all_mevboost_contexts = [] all_mevboost_contexts = []
if mev_endpoints: if mev_endpoints:
......
...@@ -34,11 +34,14 @@ def get_config(mev_boost_launcher, network_id): ...@@ -34,11 +34,14 @@ def get_config(mev_boost_launcher, network_id):
ports = USED_PORTS, ports = USED_PORTS,
cmd = command, cmd = command,
env_vars = { env_vars = {
# TODO remove the hardcoding # TODO(maybe) remove the hardcoding
# 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 # 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
# latest-notes
# does this need genesis time to be set as well
"GENESIS_FORK_VERSION": "0x10000038", "GENESIS_FORK_VERSION": "0x10000038",
"BOOST_LISTEN_ADDR": "0.0.0.0:{0}".format(parse_input.FLASHBOTS_MEV_BOOST_PORT), "BOOST_LISTEN_ADDR": "0.0.0.0:{0}".format(parse_input.FLASHBOTS_MEV_BOOST_PORT),
"SKIP_RELAY_SIGNATURE_CHECK": "true", # maybe this is breaking; this isn't verifyign the bid and not sending it to the validator
"SKIP_RELAY_SIGNATURE_CHECK": "1",
"RELAYS": mev_boost_launcher.relay_end_points[0] "RELAYS": mev_boost_launcher.relay_end_points[0]
} }
) )
......
ADMIN_KEY = "0xef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2"
USER_KEY = "0x7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31"
def launch_mev_flood(plan, image, el_uri):
plan.add_service(
name = "mev-flood",
config = ServiceConfig(
image = image,
entrypoint = ["/bin/sh", "-c", "touch main.log && tail -F main.log"]
)
)
plan.exec(
service_name = "mev-flood",
recipe = ExecRecipe(
command = ["/bin/sh", "-c", "./run init -r {0} -k {1} -u {2} -s deployment.json".format(el_uri, ADMIN_KEY, USER_KEY)]
)
)
def spam_in_background(plan, el_uri, mev_flood_extra_args):
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)]
if mev_flood_extra_args:
joined_extra_args = " ".join(mev_flood_extra_args)
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)]
plan.exec(
service_name = "mev-flood",
recipe = ExecRecipe(
command = command
)
)
redis_module = import_module("github.com/kurtosis-tech/redis-package/main.star")
postgres_module = import_module("github.com/kurtosis-tech/postgres-package/main.star")
DUMMY_SECRET_KEY = "0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2"
DUMMY_PUB_KEY = "0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5"
MEV_RELAY_WEBSITE = "mev-relay-website"
MEV_RELAY_ENDPOINT = "mev-relay-api"
MEV_RELAY_HOUSEKEEPER = "mev-relay-housekeeper"
MEV_RELAY_ENDPOINT_PORT = 9062
MEV_RELAY_WEBSITE_PORT = 9060
NETWORK_ID_TO_NAME = {
"5": "goerli",
"11155111": "sepolia",
"3": "ropsten",
}
def launch_mev_relay(plan, mev_params, network_id, beacon_uris, validator_root, builder_uri):
redis = redis_module.run(plan, {})
# making the password postgres as the relay expects it to be postgres
postgres = postgres_module.run(plan, {"password": "postgres", "user": "postgres", "database": "postgres", "name": "postgres"})
network_name = NETWORK_ID_TO_NAME.get(network_id, network_id)
image = mev_params.mev_relay_image
# TODO(maybe) remove hardocded values for the forks
env_vars= {
"GENESIS_FORK_VERSION": "0x10000038",
"BELLATRIX_FORK_VERSION": "0x30000038",
"CAPELLA_FORK_VERSION": "0x40000038",
"DENEB_FORK_VERSION": "0x50000038",
"GENESIS_VALIDATORS_ROOT": validator_root
}
plan.add_service(
name = MEV_RELAY_HOUSEKEEPER,
config = ServiceConfig(
image = image,
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,
env_vars= env_vars
)
)
api = plan.add_service(
name = MEV_RELAY_ENDPOINT,
config = ServiceConfig(
image = image,
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,
ports = {
"api": PortSpec(number = MEV_RELAY_ENDPOINT_PORT, transport_protocol= "TCP")
},
env_vars= env_vars
)
)
plan.add_service(
name = MEV_RELAY_WEBSITE,
config = ServiceConfig(
image = image,
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,
ports = {
"api": PortSpec(number = MEV_RELAY_WEBSITE_PORT, transport_protocol= "TCP", application_protocol="http")
},
env_vars= env_vars
)
)
return "http://{0}@{1}:{2}".format(DUMMY_PUB_KEY, api.ip_address, MEV_RELAY_ENDPOINT_PORT)
...@@ -24,6 +24,8 @@ FLASHBOTS_MEV_BOOST_PORT = 18550 ...@@ -24,6 +24,8 @@ FLASHBOTS_MEV_BOOST_PORT = 18550
MEV_BOOST_SERVICE_NAME_PREFIX = "mev-boost-" MEV_BOOST_SERVICE_NAME_PREFIX = "mev-boost-"
package_io = import_module("github.com/kurtosis-tech/eth-network-package/package_io/constants.star")
def parse_input(input_args): def parse_input(input_args):
result = default_input_args() result = default_input_args()
for attr in input_args: for attr in input_args:
...@@ -35,6 +37,10 @@ def parse_input(input_args): ...@@ -35,6 +37,10 @@ def parse_input(input_args):
for sub_attr in input_args["network_params"]: for sub_attr in input_args["network_params"]:
sub_value = input_args["network_params"][sub_attr] sub_value = input_args["network_params"][sub_attr]
result["network_params"][sub_attr] = sub_value result["network_params"][sub_attr] = sub_value
elif attr == "mev_params":
for sub_attr in input_args["mev_params"]:
sub_value = input_args["mev_params"]["sub_attr"]
result["mev_params"][sub_attr] = sub_value
elif attr == "participants": elif attr == "participants":
participants = [] participants = []
for participant in input_args["participants"]: for participant in input_args["participants"]:
...@@ -139,6 +145,15 @@ def parse_input(input_args): ...@@ -139,6 +145,15 @@ def parse_input(input_args):
deneb_fork_epoch=result["network_params"]["deneb_fork_epoch"], deneb_fork_epoch=result["network_params"]["deneb_fork_epoch"],
genesis_delay=result["network_params"]["genesis_delay"] genesis_delay=result["network_params"]["genesis_delay"]
), ),
mev_params = struct(
mev_relay_image = result["mev_params"]["mev_relay_image"],
mev_relay_api_extra_args = result["mev_params"]["mev_relay_api_extra_args"],
mev_relay_housekeeper_extra_args = result["mev_params"]["mev_relay_housekeeper_extra_args"],
mev_relay_website_extra_args = result["mev_params"]["mev_relay_website_extra_args"],
mev_builder_extra_args = result["mev_params"]["mev_builder_extra_args"],
mev_flood_image = result["mev_params"]["mev_flood_image"],
mev_flood_extra_args = result["mev_params"]["mev_flood_extra_args"]
),
launch_additional_services=result["launch_additional_services"], launch_additional_services=result["launch_additional_services"],
wait_for_finalization=result["wait_for_finalization"], wait_for_finalization=result["wait_for_finalization"],
wait_for_verifications=result["wait_for_verifications"], 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 ...@@ -158,6 +173,7 @@ def get_client_log_level_or_default(participant_log_level, global_log_level, cli
def default_input_args(): def default_input_args():
network_params = default_network_params() network_params = default_network_params()
participants = [default_participant()] participants = [default_participant()]
mev_params = get_default_mev_params()
return { return {
"mev_type": None, "mev_type": None,
"participants": participants, "participants": participants,
...@@ -167,6 +183,7 @@ def default_input_args(): ...@@ -167,6 +183,7 @@ def default_input_args():
"wait_for_verifications": False, "wait_for_verifications": False,
"verifications_epoch_limit": 5, "verifications_epoch_limit": 5,
"global_client_log_level": "info", "global_client_log_level": "info",
"mev_params": mev_params,
"snooper_enabled": False, "snooper_enabled": False,
} }
...@@ -174,19 +191,18 @@ def default_network_params(): ...@@ -174,19 +191,18 @@ def default_network_params():
# this is temporary till we get params working # this is temporary till we get params working
return { return {
"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", "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",
"num_validator_keys_per_node": 64, "num_validator_keys_per_node": 64,
"network_id": "3151908", "network_id": "3151908",
"deposit_contract_address": "0x4242424242424242424242424242424242424242", "deposit_contract_address": "0x4242424242424242424242424242424242424242",
"seconds_per_slot": 12, "seconds_per_slot": 12,
"slots_per_epoch": 32, "slots_per_epoch": 32,
"genesis_delay": 120, "genesis_delay": 120,
"capella_fork_epoch": 1, "capella_fork_epoch": 1,
# arbitrarily large while we sort out https://github.com/kurtosis-tech/eth-network-package/issues/42 "deneb_fork_epoch": 500
# this will take 53~ hoours for now
"deneb_fork_epoch": 500,
} }
def default_participant(): def default_participant():
# TODO(now) add support for mev boost image and extra parameters
return { return {
"el_client_type": "geth", "el_client_type": "geth",
"el_client_image": "", "el_client_image": "",
...@@ -201,6 +217,18 @@ def default_participant(): ...@@ -201,6 +217,18 @@ def default_participant():
"count": 1 "count": 1
} }
def get_default_mev_params():
return {
# TODO fix this when Capella Signature verification works - change it to flashbots/
"mev_relay_image": "h4ck3rk3y/mev-boost-relay",
"mev_relay_api_extra_args": [],
"mev_relay_housekeeper_extra_args": [],
"mev_relay_website_extra_args": [],
"mev_builder_extra_args": [],
"mev_flood_image": "flashbots/mev-flood",
"mev_flood_extra_args": []
}
# TODO perhaps clean this up into a map # TODO perhaps clean this up into a map
def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port): 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): ...@@ -220,4 +248,27 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port):
if participant["cl_client_type"] == "prysm": if participant["cl_client_type"] == "prysm":
participant["validator_extra_params"].append("--enable-builder") participant["validator_extra_params"].append("--enable-builder")
participant["beacon_extra_params"].append("--http-mev-relay={0}".format(mev_url)) participant["beacon_extra_params"].append("--http-mev-relay={0}".format(mev_url))
num_participants = len(parsed_arguments_dict["participants"])
mev_url = "http://{0}{1}:{2}".format(mev_prefix, num_participants, mev_port)
mev_participant = {
"el_client_type": "geth",
# TODO replace with actual when flashbots/builder is published
"el_client_image": "h4ck3rk3y/builder",
"el_client_log_level": "",
"cl_client_type": "lighthouse",
# THIS overrides the beacon image
"cl_client_image": "sigp/lighthouse",
"cl_client_log_level": "",
"beacon_extra_params": ["--builder={0}".format(mev_url), "--always-prepare-payload", "--prepare-payload-lookahead", "12000"],
# TODO(maybe) make parts of this more passable like the mev-relay-endpoint & forks
"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"],
"validator_extra_params": ["--builder-proposals"],
"builder_network_params": None
}
parsed_arguments_dict["participants"].append(mev_participant)
return parsed_arguments_dict return parsed_arguments_dict
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment