From 2be456a298fab740a3c35986c856efa8e1762dee Mon Sep 17 00:00:00 2001 From: Sambhav Jain <136801346+DarkLord017@users.noreply.github.com> Date: Fri, 2 Jan 2026 01:57:06 +0530 Subject: [PATCH 1/4] fix: support custom CL for MEV builder (issue #627) - Infer CL type from mev_builder_cl_image instead of hardcoding Lighthouse - Set CL-specific builder flags for all supported clients - Use correct beacon HTTP port in builder config CLEndpoint - Add robust cl_type inference in cl_launcher based on image --- src/cl/cl_launcher.star | 28 ++- .../mev_builder/mev_builder_launcher.star | 43 ++++- src/package_io/input_parser.star | 161 ++++++++++++++++-- 3 files changed, 212 insertions(+), 20 deletions(-) diff --git a/src/cl/cl_launcher.star b/src/cl/cl_launcher.star index 84731ad03..658ae0a38 100644 --- a/src/cl/cl_launcher.star +++ b/src/cl/cl_launcher.star @@ -122,8 +122,34 @@ def launch( cl_service_configs = {} cl_participant_info = {} for index, participant in enumerate(args_with_right_defaults.participants): - cl_type = participant.cl_type + # The declared client type from the participant YAML + declared_cl = participant.cl_type el_type = participant.el_type + # If the participant image indicates a different CL than the declared + # `cl_type`, prefer the image-detected type so we select the correct + # launcher (prevents running lighthouse commands in a lodestar image). + cl_image = participant.cl_image + # Try to infer from image if mismatch or launcher missing. Prefer the + # image-inferred type but do not mutate the participant struct here — + # instead compute a single `effective_cl_type` used for all launcher + # selection and naming below. This avoids side-effects and makes the + # decision deterministic for this function's execution. + inferred_cl = None + for candidate in cl_launchers.keys(): + if candidate in cl_image: + inferred_cl = candidate + break + effective_cl_type = inferred_cl if inferred_cl != None else declared_cl + if inferred_cl != None and inferred_cl != declared_cl: + plan.print( + "CL type mismatch detected for participant {0}: declared '{1}', image suggests '{2}'. Using image-inferred type.".format( + index + 1, declared_cl, inferred_cl + ) + ) + + # Use the effective client type for all subsequent launcher selection + # and naming within this function. + cl_type = effective_cl_type node_selectors = input_parser.get_client_node_selectors( participant.node_selectors, global_node_selectors, diff --git a/src/mev/flashbots/mev_builder/mev_builder_launcher.star b/src/mev/flashbots/mev_builder/mev_builder_launcher.star index 49c35c2fc..6788384a8 100644 --- a/src/mev/flashbots/mev_builder/mev_builder_launcher.star +++ b/src/mev/flashbots/mev_builder/mev_builder_launcher.star @@ -5,6 +5,12 @@ constants = import_module("../../../package_io/constants.star") flashbots_relay = import_module("../mev_relay/mev_relay_launcher.star") helix_relay = import_module("../../helix/helix_relay_launcher.star") lighthouse = import_module("../../../cl/lighthouse/lighthouse_launcher.star") +lodestar = import_module("../../../cl/lodestar/lodestar_launcher.star") +nimbus = import_module("../../../cl/nimbus/nimbus_launcher.star") +prysm = import_module("../../../cl/prysm/prysm_launcher.star") +teku = import_module("../../../cl/teku/teku_launcher.star") +grandine = import_module("../../../cl/grandine/grandine_launcher.star") + # MEV Builder flags MEV_BUILDER_CONFIG_FILENAME = "config.toml" @@ -28,6 +34,34 @@ def new_builder_config( num_of_participants = shared_utils.zfill_custom( len(participants), len(str(len(participants))) ) + # Infer the builder CL type from the provided image so CLEndpoint is correct + mev_cl_image = mev_params.mev_builder_cl_image + mev_cl_type = constants.CL_TYPE.lighthouse + for candidate in [ + constants.CL_TYPE.lighthouse, + constants.CL_TYPE.lodestar, + constants.CL_TYPE.prysm, + constants.CL_TYPE.teku, + constants.CL_TYPE.nimbus, + constants.CL_TYPE.grandine, + ]: + if candidate in mev_cl_image: + mev_cl_type = candidate + break + + # Map CL type to its beacon HTTP port constant + mev_cl_port = lighthouse.BEACON_HTTP_PORT_NUM + if mev_cl_type == constants.CL_TYPE.lodestar: + mev_cl_port = lodestar.BEACON_HTTP_PORT_NUM + elif mev_cl_type == constants.CL_TYPE.prysm: + mev_cl_port = prysm.BEACON_HTTP_PORT_NUM + elif mev_cl_type == constants.CL_TYPE.teku: + mev_cl_port = teku.BEACON_HTTP_PORT_NUM + elif mev_cl_type == constants.CL_TYPE.nimbus: + mev_cl_port = nimbus.BEACON_HTTP_PORT_NUM + elif mev_cl_type == constants.CL_TYPE.grandine: + mev_cl_port = grandine.BEACON_HTTP_PORT_NUM + builder_template_data = new_builder_config_template_data( network_params, constants.DEFAULT_MEV_PUBKEY, @@ -38,6 +72,8 @@ def new_builder_config( num_of_participants, mev_params.mev_builder_subsidy, mev_type, + mev_cl_type, + mev_cl_port, ) flashbots_builder_config_template = read_file( static_files.FLASHBOTS_RBUILDER_CONFIG_FILEPATH @@ -63,6 +99,7 @@ def new_builder_config( return config_files_artifact_name + def new_builder_config_template_data( network_params, pubkey, @@ -73,6 +110,8 @@ def new_builder_config_template_data( num_of_participants, subsidy, mev_type, + builder_cl_type, + builder_cl_port, ): # Determine relay service name and port based on MEV type if mev_type == constants.HELIX_MEV_TYPE: @@ -91,9 +130,9 @@ def new_builder_config_template_data( "DataDir": "/data/reth/execution-data", "CLEndpoint": "http://cl-{0}-{1}-{2}:{3}".format( num_of_participants, - constants.CL_TYPE.lighthouse, + builder_cl_type, constants.EL_TYPE.reth_builder, - lighthouse.BEACON_HTTP_PORT_NUM, + builder_cl_port, ), "GenesisForkVersion": constants.GENESIS_FORK_VERSION, "Relay": relay_service, diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index eb7a138f2..33ba2d9f3 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -1561,10 +1561,7 @@ def get_default_docker_cache_params(): def get_default_mev_params(mev_type, preset): mev_relay_image = constants.DEFAULT_FLASHBOTS_RELAY_IMAGE mev_builder_image = constants.DEFAULT_FLASHBOTS_BUILDER_IMAGE - if preset == "minimal": - mev_builder_cl_image = DEFAULT_CL_IMAGES_MINIMAL[constants.CL_TYPE.lighthouse] - else: - mev_builder_cl_image = DEFAULT_CL_IMAGES[constants.CL_TYPE.lighthouse] + mev_builder_cl_image = DEFAULT_CL_IMAGES[constants.CL_TYPE.lighthouse] mev_builder_extra_data = None mev_builder_subsidy = 0 mev_boost_image = constants.DEFAULT_FLASHBOTS_MEV_BOOST_IMAGE @@ -1943,13 +1940,6 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ "el_image": parsed_arguments_dict["mev_params"]["mev_builder_image"], "cl_image": parsed_arguments_dict["mev_params"]["mev_builder_cl_image"], "cl_log_level": parsed_arguments_dict["global_log_level"], - "cl_extra_params": [ - "--always-prepare-payload", - "--prepare-payload-lookahead", - "8000", - "--disable-peer-scoring", - "--supernode", - ], "el_extra_params": parsed_arguments_dict["mev_params"][ "mev_builder_extra_args" ], @@ -1960,6 +1950,79 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ } ) + mev_cl_image = parsed_arguments_dict["mev_params"]["mev_builder_cl_image"] + mev_cl_type = constants.CL_TYPE.lighthouse + for candidate in [ + constants.CL_TYPE.lighthouse, + constants.CL_TYPE.lodestar, + constants.CL_TYPE.prysm, + constants.CL_TYPE.teku, + constants.CL_TYPE.nimbus, + constants.CL_TYPE.grandine, + ]: + if candidate in mev_cl_image: + mev_cl_type = candidate + break + mev_participant["cl_type"] = mev_cl_type + mev_participant["vc_type"] = mev_cl_type + + # Compute mev_url for this mev participant so we can set CL-specific + # extra params (different CLs accept different flags). + if mev_type == constants.COMMIT_BOOST_MEV_TYPE: + prefix = constants.COMMIT_BOOST_SERVICE_NAME_PREFIX + else: + prefix = constants.MEV_BOOST_SERVICE_NAME_PREFIX + num_participants = len(parsed_arguments_dict["participants"]) + index_str = shared_utils.zfill_custom( + num_participants + 1, len(str(num_participants + 1)) + ) + mev_url = "http://{0}-{1}-{2}-{3}:{4}".format( + prefix, + index_str, + mev_participant["cl_type"], + mev_participant["el_type"], + constants.MEV_BOOST_PORT, + ) + + mev_participant["cl_extra_params"] = [] + # CL-specific builder flags + if mev_cl_type == constants.CL_TYPE.lighthouse: + mev_participant["cl_extra_params"].extend( + [ + "--builder={0}".format(mev_url), + "--always-prepare-payload", + "--prepare-payload-lookahead", + "8000", + "--disable-peer-scoring", + "--supernode", + ] + ) + elif mev_cl_type == constants.CL_TYPE.lodestar: + mev_participant["cl_extra_params"].append("--builder") + mev_participant["cl_extra_params"].append( + "--builder.urls={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.nimbus: + mev_participant["cl_extra_params"].append("--payload-builder=true") + mev_participant["cl_extra_params"].append( + "--payload-builder-url={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.teku: + mev_participant["cl_extra_params"].append( + "--builder-endpoint={0}".format(mev_url) + ) + mev_participant["cl_extra_params"].append( + "--validators-builder-registration-default-enabled=true" + ) + elif mev_cl_type == constants.CL_TYPE.prysm: + mev_participant["cl_extra_params"].append( + "--http-mev-relay={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.grandine: + mev_participant["cl_extra_params"].append( + "--builder-url={0}".format(mev_url) + ) + parsed_arguments_dict["participants"].append(mev_participant) if mev_type == constants.MEV_RS_MEV_TYPE: @@ -1970,18 +2033,82 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ "el_image": parsed_arguments_dict["mev_params"]["mev_builder_image"], "cl_image": parsed_arguments_dict["mev_params"]["mev_builder_cl_image"], "cl_log_level": parsed_arguments_dict["global_log_level"], - "cl_extra_params": [ - "--always-prepare-payload", - "--prepare-payload-lookahead", - "8000", - "--disable-peer-scoring", - ], "el_extra_params": parsed_arguments_dict["mev_params"][ "mev_builder_extra_args" ], "validator_count": 0, } ) + # Infer CL type from provided MEV builder CL image for MEV-RS case as well + mev_cl_image = parsed_arguments_dict["mev_params"]["mev_builder_cl_image"] + mev_cl_type = constants.CL_TYPE.lighthouse + for candidate in [ + constants.CL_TYPE.lighthouse, + constants.CL_TYPE.lodestar, + constants.CL_TYPE.prysm, + constants.CL_TYPE.teku, + constants.CL_TYPE.nimbus, + constants.CL_TYPE.grandine, + ]: + if candidate in mev_cl_image: + mev_cl_type = candidate + break + mev_participant["cl_type"] = mev_cl_type + mev_participant["vc_type"] = mev_cl_type + + if mev_type == constants.COMMIT_BOOST_MEV_TYPE: + prefix = constants.COMMIT_BOOST_SERVICE_NAME_PREFIX + else: + prefix = constants.MEV_BOOST_SERVICE_NAME_PREFIX + num_participants = len(parsed_arguments_dict["participants"]) + index_str = shared_utils.zfill_custom( + num_participants + 1, len(str(num_participants + 1)) + ) + mev_url = "http://{0}-{1}-{2}-{3}:{4}".format( + prefix, + index_str, + mev_participant["cl_type"], + mev_participant["el_type"], + constants.MEV_BOOST_PORT, + ) + + mev_participant["cl_extra_params"] = [] + if mev_cl_type == constants.CL_TYPE.lighthouse: + mev_participant["cl_extra_params"].extend( + [ + "--builder={0}".format(mev_url), + "--always-prepare-payload", + "--prepare-payload-lookahead", + "8000", + "--disable-peer-scoring", + ] + ) + elif mev_cl_type == constants.CL_TYPE.lodestar: + mev_participant["cl_extra_params"].append("--builder") + mev_participant["cl_extra_params"].append( + "--builder.urls={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.nimbus: + mev_participant["cl_extra_params"].append("--payload-builder=true") + mev_participant["cl_extra_params"].append( + "--payload-builder-url={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.teku: + mev_participant["cl_extra_params"].append( + "--builder-endpoint={0}".format(mev_url) + ) + mev_participant["cl_extra_params"].append( + "--validators-builder-registration-default-enabled=true" + ) + elif mev_cl_type == constants.CL_TYPE.prysm: + mev_participant["cl_extra_params"].append( + "--http-mev-relay={0}".format(mev_url) + ) + elif mev_cl_type == constants.CL_TYPE.grandine: + mev_participant["cl_extra_params"].append( + "--builder-url={0}".format(mev_url) + ) + parsed_arguments_dict["participants"].append(mev_participant) if mev_type == constants.MOCK_MEV_TYPE: parsed_arguments_dict["mev_params"]["mock_mev_image"] = parsed_arguments_dict[ From b5d286e326b5456bf13155a5fb1b3b7347e8db37 Mon Sep 17 00:00:00 2001 From: Sambhav Jain <136801346+DarkLord017@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:48:58 +0530 Subject: [PATCH 2/4] added proper config and removed duplication --- .../mev_builder/mev_builder_launcher.star | 1 - src/package_io/input_parser.star | 177 ++++++++---------- 2 files changed, 73 insertions(+), 105 deletions(-) diff --git a/src/mev/flashbots/mev_builder/mev_builder_launcher.star b/src/mev/flashbots/mev_builder/mev_builder_launcher.star index 6788384a8..00129c000 100644 --- a/src/mev/flashbots/mev_builder/mev_builder_launcher.star +++ b/src/mev/flashbots/mev_builder/mev_builder_launcher.star @@ -99,7 +99,6 @@ def new_builder_config( return config_files_artifact_name - def new_builder_config_template_data( network_params, pubkey, diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 33ba2d9f3..5e2ebbbe4 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -1561,7 +1561,10 @@ def get_default_docker_cache_params(): def get_default_mev_params(mev_type, preset): mev_relay_image = constants.DEFAULT_FLASHBOTS_RELAY_IMAGE mev_builder_image = constants.DEFAULT_FLASHBOTS_BUILDER_IMAGE - mev_builder_cl_image = DEFAULT_CL_IMAGES[constants.CL_TYPE.lighthouse] + if preset == "minimal": + mev_builder_cl_image = DEFAULT_CL_IMAGES_MINIMAL[constants.CL_TYPE.lighthouse] + else: + mev_builder_cl_image = DEFAULT_CL_IMAGES[constants.CL_TYPE.lighthouse] mev_builder_extra_data = None mev_builder_subsidy = 0 mev_boost_image = constants.DEFAULT_FLASHBOTS_MEV_BOOST_IMAGE @@ -1862,6 +1865,62 @@ def enrich_disable_peer_scoring(parsed_arguments_dict): return parsed_arguments_dict +def infer_cl_type_from_image(cl_image): + """Infer CL client type from given config.""" + for candidate in [ + constants.CL_TYPE.lighthouse, + constants.CL_TYPE.lodestar, + constants.CL_TYPE.prysm, + constants.CL_TYPE.teku, + constants.CL_TYPE.nimbus, + constants.CL_TYPE.grandine, + ]: + if candidate in cl_image: + return candidate + return constants.CL_TYPE.lighthouse # default + + +def get_mev_cl_extra_params(cl_type, mev_url, include_supernode=False): + """Return CL-specific builder flags for MEV participant. + + For MEV builder nodes, the CL needs to prepare payloads for EVERY slot, + not just slots where the validator is proposing. This requires special flags. + """ + params = [] + if cl_type == constants.CL_TYPE.lighthouse: + params.extend( + [ + "--builder={0}".format(mev_url), + "--always-prepare-payload", + "--prepare-payload-lookahead", + "8000", + "--disable-peer-scoring", + ] + ) + if include_supernode: + params.append("--supernode") + elif cl_type == constants.CL_TYPE.lodestar: + params.append("--builder") + params.append("--builder.urls={0}".format(mev_url)) + # emitPayloadAttributes sends payload attributes before every slot for builders + params.append("--emitPayloadAttributes") + elif cl_type == constants.CL_TYPE.nimbus: + params.append("--payload-builder=true") + params.append("--payload-builder-url={0}".format(mev_url)) + elif cl_type == constants.CL_TYPE.teku: + params.append("--builder-endpoint={0}".format(mev_url)) + params.append("--validators-builder-registration-default-enabled=true") + # BUILDER_ALWAYS ensures builder is always used for payload construction + params.append("--builder-bid-compare-factor=BUILDER_ALWAYS") + elif cl_type == constants.CL_TYPE.prysm: + params.append("--http-mev-relay={0}".format(mev_url)) + # prepare-all-payloads tells Prysm to prepare payloads for every slot (required for builders) + params.append("--prepare-all-payloads") + elif cl_type == constants.CL_TYPE.grandine: + params.append("--builder-url={0}".format(mev_url)) + return params + + # TODO perhaps clean this up into a map def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_type): for index, participant in enumerate(parsed_arguments_dict["participants"]): @@ -1951,18 +2010,7 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ ) mev_cl_image = parsed_arguments_dict["mev_params"]["mev_builder_cl_image"] - mev_cl_type = constants.CL_TYPE.lighthouse - for candidate in [ - constants.CL_TYPE.lighthouse, - constants.CL_TYPE.lodestar, - constants.CL_TYPE.prysm, - constants.CL_TYPE.teku, - constants.CL_TYPE.nimbus, - constants.CL_TYPE.grandine, - ]: - if candidate in mev_cl_image: - mev_cl_type = candidate - break + mev_cl_type = infer_cl_type_from_image(mev_cl_image) mev_participant["cl_type"] = mev_cl_type mev_participant["vc_type"] = mev_cl_type @@ -1984,44 +2032,10 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ constants.MEV_BOOST_PORT, ) - mev_participant["cl_extra_params"] = [] - # CL-specific builder flags - if mev_cl_type == constants.CL_TYPE.lighthouse: - mev_participant["cl_extra_params"].extend( - [ - "--builder={0}".format(mev_url), - "--always-prepare-payload", - "--prepare-payload-lookahead", - "8000", - "--disable-peer-scoring", - "--supernode", - ] - ) - elif mev_cl_type == constants.CL_TYPE.lodestar: - mev_participant["cl_extra_params"].append("--builder") - mev_participant["cl_extra_params"].append( - "--builder.urls={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.nimbus: - mev_participant["cl_extra_params"].append("--payload-builder=true") - mev_participant["cl_extra_params"].append( - "--payload-builder-url={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.teku: - mev_participant["cl_extra_params"].append( - "--builder-endpoint={0}".format(mev_url) - ) - mev_participant["cl_extra_params"].append( - "--validators-builder-registration-default-enabled=true" - ) - elif mev_cl_type == constants.CL_TYPE.prysm: - mev_participant["cl_extra_params"].append( - "--http-mev-relay={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.grandine: - mev_participant["cl_extra_params"].append( - "--builder-url={0}".format(mev_url) - ) + # Use helper to get CL-specific builder flags (with supernode for Flashbots/CommitBoost/Helix) + mev_participant["cl_extra_params"] = get_mev_cl_extra_params( + mev_cl_type, mev_url, include_supernode=True + ) parsed_arguments_dict["participants"].append(mev_participant) @@ -2039,27 +2053,13 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ "validator_count": 0, } ) - # Infer CL type from provided MEV builder CL image for MEV-RS case as well + mev_cl_image = parsed_arguments_dict["mev_params"]["mev_builder_cl_image"] - mev_cl_type = constants.CL_TYPE.lighthouse - for candidate in [ - constants.CL_TYPE.lighthouse, - constants.CL_TYPE.lodestar, - constants.CL_TYPE.prysm, - constants.CL_TYPE.teku, - constants.CL_TYPE.nimbus, - constants.CL_TYPE.grandine, - ]: - if candidate in mev_cl_image: - mev_cl_type = candidate - break + mev_cl_type = infer_cl_type_from_image(mev_cl_image) mev_participant["cl_type"] = mev_cl_type mev_participant["vc_type"] = mev_cl_type - if mev_type == constants.COMMIT_BOOST_MEV_TYPE: - prefix = constants.COMMIT_BOOST_SERVICE_NAME_PREFIX - else: - prefix = constants.MEV_BOOST_SERVICE_NAME_PREFIX + prefix = constants.MEV_BOOST_SERVICE_NAME_PREFIX num_participants = len(parsed_arguments_dict["participants"]) index_str = shared_utils.zfill_custom( num_participants + 1, len(str(num_participants + 1)) @@ -2072,44 +2072,13 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ constants.MEV_BOOST_PORT, ) - mev_participant["cl_extra_params"] = [] - if mev_cl_type == constants.CL_TYPE.lighthouse: - mev_participant["cl_extra_params"].extend( - [ - "--builder={0}".format(mev_url), - "--always-prepare-payload", - "--prepare-payload-lookahead", - "8000", - "--disable-peer-scoring", - ] - ) - elif mev_cl_type == constants.CL_TYPE.lodestar: - mev_participant["cl_extra_params"].append("--builder") - mev_participant["cl_extra_params"].append( - "--builder.urls={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.nimbus: - mev_participant["cl_extra_params"].append("--payload-builder=true") - mev_participant["cl_extra_params"].append( - "--payload-builder-url={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.teku: - mev_participant["cl_extra_params"].append( - "--builder-endpoint={0}".format(mev_url) - ) - mev_participant["cl_extra_params"].append( - "--validators-builder-registration-default-enabled=true" - ) - elif mev_cl_type == constants.CL_TYPE.prysm: - mev_participant["cl_extra_params"].append( - "--http-mev-relay={0}".format(mev_url) - ) - elif mev_cl_type == constants.CL_TYPE.grandine: - mev_participant["cl_extra_params"].append( - "--builder-url={0}".format(mev_url) - ) + # Use helper to get CL-specific builder flags (no supernode for MEV-RS) + mev_participant["cl_extra_params"] = get_mev_cl_extra_params( + mev_cl_type, mev_url, include_supernode=False + ) parsed_arguments_dict["participants"].append(mev_participant) + if mev_type == constants.MOCK_MEV_TYPE: parsed_arguments_dict["mev_params"]["mock_mev_image"] = parsed_arguments_dict[ "mev_params" From 5317cdb41dace54b33de9c4670f47bb6fd580ded Mon Sep 17 00:00:00 2001 From: Sambhav Jain <136801346+DarkLord017@users.noreply.github.com> Date: Sun, 11 Jan 2026 02:00:59 +0530 Subject: [PATCH 3/4] removing grandine , nimbus and lodestar --- src/el/reth/reth_launcher.star | 14 ++++++++++- .../mev_builder/mev_builder_launcher.star | 16 +++---------- src/package_io/input_parser.star | 24 ++++++++----------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/el/reth/reth_launcher.star b/src/el/reth/reth_launcher.star index 54a3f5b57..5b021f881 100644 --- a/src/el/reth/reth_launcher.star +++ b/src/el/reth/reth_launcher.star @@ -7,6 +7,8 @@ node_metrics = import_module("../../node_metrics_info.star") constants = import_module("../../package_io/constants.star") mev_rs_builder = import_module("../../mev/mev-rs/mev_builder/mev_builder_launcher.star") lighthouse = import_module("../../cl/lighthouse/lighthouse_launcher.star") +prysm = import_module("../../cl/prysm/prysm_launcher.star") +teku = import_module("../../cl/teku/teku_launcher.star") flashbots_rbuilder = import_module( "../../mev/flashbots/mev_builder/mev_builder_launcher.star" ) @@ -289,13 +291,23 @@ def get_config( files[ flashbots_rbuilder.MEV_BUILDER_MOUNT_DIRPATH_ON_SERVICE ] = flashbots_rbuilder.MEV_BUILDER_FILES_ARTIFACT_NAME + + # Infer builder CL type from image and get correct port + # Note: Only Lighthouse, Lodestar, Prysm, and Teku are supported as builder CLs. + mev_cl_image = launcher.mev_params.mev_builder_cl_image + mev_cl_port = lighthouse.BEACON_HTTP_PORT_NUM # default + if constants.CL_TYPE.prysm in mev_cl_image: + mev_cl_port = prysm.BEACON_HTTP_PORT_NUM + elif constants.CL_TYPE.teku in mev_cl_image: + mev_cl_port = teku.BEACON_HTTP_PORT_NUM + env_vars.update( { "CL_ENDPOINT": "http://cl-{0}-{1}-{2}:{3}".format( participant_index + 1, cl_client_name, constants.EL_TYPE.reth_builder, - lighthouse.BEACON_HTTP_PORT_NUM, + mev_cl_port, ), } ) diff --git a/src/mev/flashbots/mev_builder/mev_builder_launcher.star b/src/mev/flashbots/mev_builder/mev_builder_launcher.star index 00129c000..74dfb5019 100644 --- a/src/mev/flashbots/mev_builder/mev_builder_launcher.star +++ b/src/mev/flashbots/mev_builder/mev_builder_launcher.star @@ -5,11 +5,8 @@ constants = import_module("../../../package_io/constants.star") flashbots_relay = import_module("../mev_relay/mev_relay_launcher.star") helix_relay = import_module("../../helix/helix_relay_launcher.star") lighthouse = import_module("../../../cl/lighthouse/lighthouse_launcher.star") -lodestar = import_module("../../../cl/lodestar/lodestar_launcher.star") -nimbus = import_module("../../../cl/nimbus/nimbus_launcher.star") prysm = import_module("../../../cl/prysm/prysm_launcher.star") teku = import_module("../../../cl/teku/teku_launcher.star") -grandine = import_module("../../../cl/grandine/grandine_launcher.star") # MEV Builder flags @@ -35,15 +32,14 @@ def new_builder_config( len(participants), len(str(len(participants))) ) # Infer the builder CL type from the provided image so CLEndpoint is correct + # Note: Only Lighthouse, Prysm, and Teku are supported as builder CLs. + # Lodestar, Nimbus, and Grandine have incompatible payload_attributes SSE event formats. mev_cl_image = mev_params.mev_builder_cl_image mev_cl_type = constants.CL_TYPE.lighthouse for candidate in [ constants.CL_TYPE.lighthouse, - constants.CL_TYPE.lodestar, constants.CL_TYPE.prysm, constants.CL_TYPE.teku, - constants.CL_TYPE.nimbus, - constants.CL_TYPE.grandine, ]: if candidate in mev_cl_image: mev_cl_type = candidate @@ -51,16 +47,10 @@ def new_builder_config( # Map CL type to its beacon HTTP port constant mev_cl_port = lighthouse.BEACON_HTTP_PORT_NUM - if mev_cl_type == constants.CL_TYPE.lodestar: - mev_cl_port = lodestar.BEACON_HTTP_PORT_NUM - elif mev_cl_type == constants.CL_TYPE.prysm: + if mev_cl_type == constants.CL_TYPE.prysm: mev_cl_port = prysm.BEACON_HTTP_PORT_NUM elif mev_cl_type == constants.CL_TYPE.teku: mev_cl_port = teku.BEACON_HTTP_PORT_NUM - elif mev_cl_type == constants.CL_TYPE.nimbus: - mev_cl_port = nimbus.BEACON_HTTP_PORT_NUM - elif mev_cl_type == constants.CL_TYPE.grandine: - mev_cl_port = grandine.BEACON_HTTP_PORT_NUM builder_template_data = new_builder_config_template_data( network_params, diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 5e2ebbbe4..a83c2526a 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -1866,14 +1866,15 @@ def enrich_disable_peer_scoring(parsed_arguments_dict): def infer_cl_type_from_image(cl_image): - """Infer CL client type from given config.""" + """Infer CL client type from given config. + + Note: Only Lighthouse, Prysm, and Teku are supported as builder CLs. + Lodestar, Nimbus, and Grandine have incompatible payload_attributes SSE event formats. + """ for candidate in [ constants.CL_TYPE.lighthouse, - constants.CL_TYPE.lodestar, constants.CL_TYPE.prysm, constants.CL_TYPE.teku, - constants.CL_TYPE.nimbus, - constants.CL_TYPE.grandine, ]: if candidate in cl_image: return candidate @@ -1885,6 +1886,9 @@ def get_mev_cl_extra_params(cl_type, mev_url, include_supernode=False): For MEV builder nodes, the CL needs to prepare payloads for EVERY slot, not just slots where the validator is proposing. This requires special flags. + + Note: Only Lighthouse, Prysm, and Teku are supported as builder CLs. + Lodestar, Nimbus, and Grandine have incompatible payload_attributes SSE event formats. """ params = [] if cl_type == constants.CL_TYPE.lighthouse: @@ -1899,25 +1903,17 @@ def get_mev_cl_extra_params(cl_type, mev_url, include_supernode=False): ) if include_supernode: params.append("--supernode") - elif cl_type == constants.CL_TYPE.lodestar: - params.append("--builder") - params.append("--builder.urls={0}".format(mev_url)) - # emitPayloadAttributes sends payload attributes before every slot for builders - params.append("--emitPayloadAttributes") - elif cl_type == constants.CL_TYPE.nimbus: - params.append("--payload-builder=true") - params.append("--payload-builder-url={0}".format(mev_url)) elif cl_type == constants.CL_TYPE.teku: params.append("--builder-endpoint={0}".format(mev_url)) params.append("--validators-builder-registration-default-enabled=true") # BUILDER_ALWAYS ensures builder is always used for payload construction params.append("--builder-bid-compare-factor=BUILDER_ALWAYS") + # This flag makes Teku send payload_attributes on every slot (required for block builders) + params.append("--Xfork-choice-updated-always-send-payload-attributes=true") elif cl_type == constants.CL_TYPE.prysm: params.append("--http-mev-relay={0}".format(mev_url)) # prepare-all-payloads tells Prysm to prepare payloads for every slot (required for builders) params.append("--prepare-all-payloads") - elif cl_type == constants.CL_TYPE.grandine: - params.append("--builder-url={0}".format(mev_url)) return params From c9c86966a4a0af49e4b423c05c2e21fe8d84f3d5 Mon Sep 17 00:00:00 2001 From: Sambhav Jain <136801346+DarkLord017@users.noreply.github.com> Date: Sun, 11 Jan 2026 02:28:58 +0530 Subject: [PATCH 4/4] remove unnecessary code --- src/cl/cl_launcher.star | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/cl/cl_launcher.star b/src/cl/cl_launcher.star index 658ae0a38..84731ad03 100644 --- a/src/cl/cl_launcher.star +++ b/src/cl/cl_launcher.star @@ -122,34 +122,8 @@ def launch( cl_service_configs = {} cl_participant_info = {} for index, participant in enumerate(args_with_right_defaults.participants): - # The declared client type from the participant YAML - declared_cl = participant.cl_type + cl_type = participant.cl_type el_type = participant.el_type - # If the participant image indicates a different CL than the declared - # `cl_type`, prefer the image-detected type so we select the correct - # launcher (prevents running lighthouse commands in a lodestar image). - cl_image = participant.cl_image - # Try to infer from image if mismatch or launcher missing. Prefer the - # image-inferred type but do not mutate the participant struct here — - # instead compute a single `effective_cl_type` used for all launcher - # selection and naming below. This avoids side-effects and makes the - # decision deterministic for this function's execution. - inferred_cl = None - for candidate in cl_launchers.keys(): - if candidate in cl_image: - inferred_cl = candidate - break - effective_cl_type = inferred_cl if inferred_cl != None else declared_cl - if inferred_cl != None and inferred_cl != declared_cl: - plan.print( - "CL type mismatch detected for participant {0}: declared '{1}', image suggests '{2}'. Using image-inferred type.".format( - index + 1, declared_cl, inferred_cl - ) - ) - - # Use the effective client type for all subsequent launcher selection - # and naming within this function. - cl_type = effective_cl_type node_selectors = input_parser.get_client_node_selectors( participant.node_selectors, global_node_selectors,