diff --git a/src/bootnodoor/bootnodoor_launcher.star b/src/bootnodoor/bootnodoor_launcher.star new file mode 100644 index 000000000..dc07f5c59 --- /dev/null +++ b/src/bootnodoor/bootnodoor_launcher.star @@ -0,0 +1,188 @@ +shared_utils = import_module("../shared_utils/shared_utils.star") +constants = import_module("../package_io/constants.star") + +SERVICE_NAME = "bootnodoor" +HTTP_PORT_NUMBER = 8080 +DISCOVERY_PORT_NUMBER = 9000 + +USED_PORTS = { + constants.HTTP_PORT_ID: shared_utils.new_port_spec( + HTTP_PORT_NUMBER, + shared_utils.TCP_PROTOCOL, + shared_utils.HTTP_APPLICATION_PROTOCOL, + ), + constants.UDP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + DISCOVERY_PORT_NUMBER, + shared_utils.UDP_PROTOCOL, + ), +} + + +def launch_bootnodoor( + plan, + bootnodoor_params, + el_cl_genesis_data, + network_params, + global_node_selectors, + global_tolerations, + docker_cache_params, +): + tolerations = shared_utils.get_tolerations(global_tolerations=global_tolerations) + + # Generate a random 32-byte (64 hex char) private key + private_key = generate_random_private_key(plan) + + # Get the genesis validators root from the genesis data + genesis_validators_root = el_cl_genesis_data.genesis_validators_root + + # Get the EL genesis hash + el_genesis_hash = get_el_genesis_hash(plan, network_params, el_cl_genesis_data) + + # Fetch external bootnodes for non-kurtosis networks + # For kurtosis/shadowfork: bootnodoor is the primary bootnode, no external bootnodes needed + # For ephemery/devnets: read bootstrap_nodes.txt from genesis data + external_bootnodes = None + if network_params.network == constants.NETWORK_NAME.ephemery or ( + network_params.network not in constants.PUBLIC_NETWORKS + and network_params.network != constants.NETWORK_NAME.kurtosis + and constants.NETWORK_NAME.shadowfork not in network_params.network + ): + plan.print( + "Fetching external bootnodes for network: {0}".format( + network_params.network + ) + ) + external_bootnodes = shared_utils.get_devnet_enrs_list( + plan, el_cl_genesis_data.files_artifact_uuid + ) + + config = get_config( + bootnodoor_params, + el_cl_genesis_data, + private_key, + genesis_validators_root, + el_genesis_hash, + external_bootnodes, + global_node_selectors, + tolerations, + docker_cache_params, + ) + + plan.add_service(SERVICE_NAME, config) + + # Request ENR from the running bootnodoor service + # The /enr endpoint returns a plain string (not JSON) + enr_recipe = GetHttpRequestRecipe( + endpoint="/enr", + port_id=constants.HTTP_PORT_ID, + ) + + response = plan.request( + recipe=enr_recipe, + service_name=SERVICE_NAME, + ) + + bootnodoor_enr = response["body"] + + # Request ENODE from the running bootnodoor service for EL bootnode + enode_recipe = GetHttpRequestRecipe( + endpoint="/enode", + port_id=constants.HTTP_PORT_ID, + ) + + enode_response = plan.request( + recipe=enode_recipe, + service_name=SERVICE_NAME, + ) + + bootnodoor_enode = enode_response["body"] + + return bootnodoor_enr, bootnodoor_enode + + +def get_config( + bootnodoor_params, + el_cl_genesis_data, + private_key, + genesis_validators_root, + el_genesis_hash, + external_bootnodes, + node_selectors, + tolerations, + docker_cache_params, +): + cmd = [ + "--cl-config", + "{0}/config.yaml".format(constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS), + "--genesis-validators-root", + genesis_validators_root, + "--el-config", + "{0}/genesis.json".format(constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS), + "--el-genesis-hash", + el_genesis_hash, + "--private-key", + private_key, + "--bind-addr", + "0.0.0.0", + "--web-ui", + "--nodedb", + "/nodes.db", + ] + + # Add external bootnodes for non-kurtosis networks (ephemery, devnets) + if external_bootnodes != None: + cmd.append("--bootnodes") + cmd.append(external_bootnodes) + + # Add any extra args from the params + if len(bootnodoor_params.extra_args) > 0: + cmd.extend(bootnodoor_params.extra_args) + + return ServiceConfig( + image=shared_utils.docker_cache_image_calc( + docker_cache_params, + bootnodoor_params.image, + ), + ports=USED_PORTS, + files={ + constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid, + }, + cmd=cmd, + min_cpu=bootnodoor_params.min_cpu, + max_cpu=bootnodoor_params.max_cpu, + min_memory=bootnodoor_params.min_mem, + max_memory=bootnodoor_params.max_mem, + node_selectors=node_selectors, + tolerations=tolerations, + ) + + +def get_el_genesis_hash(plan, network_params, el_cl_genesis_data): + # For mainnet and sepolia, use static genesis hashes + if network_params.network == "mainnet": + return "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + elif network_params.network == "sepolia": + return "0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" + + # For other networks, extract from deposit_contract_block_hash.txt + result = plan.run_sh( + name="read-el-genesis-hash", + description="Reading EL genesis hash from deposit_contract_block_hash.txt", + run="cat /network-configs/deposit_contract_block_hash.txt", + files={ + "/network-configs": el_cl_genesis_data.files_artifact_uuid, + }, + wait=None, + ) + return result.output + + +def generate_random_private_key(plan): + # Generate a random 32-byte (64 hex char) private key using /dev/urandom + result = plan.run_sh( + name="generate-bootnodoor-private-key", + description="Generating random private key for bootnodoor", + run="head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \\n'", + wait=None, + ) + return result.output diff --git a/src/cl/cl_launcher.star b/src/cl/cl_launcher.star index 7d884d570..4e18eb27f 100644 --- a/src/cl/cl_launcher.star +++ b/src/cl/cl_launcher.star @@ -35,6 +35,7 @@ def launch( global_other_index, extra_files_artifacts, backend, + bootnodoor_enr=None, ): plan.print("Launching CL network") @@ -100,6 +101,16 @@ def launch( all_snooper_el_engine_contexts = [] all_cl_contexts = [] blobber_configs_with_contexts = [] + + # Generic bootnode ENR override - can be set from bootnodoor or any other bootnode service + bootnode_enr_override = bootnodoor_enr + if bootnode_enr_override != None: + plan.print( + "Using bootnode ENR override for all CL clients: {0}".format( + bootnode_enr_override + ) + ) + preregistered_validator_keys_for_nodes = ( validator_data.per_node_keystores if network_params.network == constants.NETWORK_NAME.kurtosis @@ -230,6 +241,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) blobber_config = get_blobber_config( @@ -280,6 +292,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) cl_participant_info[cl_service_name] = { diff --git a/src/cl/grandine/grandine_launcher.star b/src/cl/grandine/grandine_launcher.star index ba5493060..4b02af12d 100644 --- a/src/cl/grandine/grandine_launcher.star +++ b/src/cl/grandine/grandine_launcher.star @@ -58,6 +58,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): config = get_beacon_config( plan, @@ -81,6 +82,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, config) @@ -120,6 +122,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -252,6 +255,8 @@ def get_beacon_config( if checkpoint_sync_enabled: cmd.append("--checkpoint-sync-url=" + checkpoint_sync_url) + bootnode_arg = bootnode_enr_override + if network_params.network not in constants.PUBLIC_NETWORKS: cmd.append( "--configuration-directory=" @@ -261,37 +266,28 @@ def get_beacon_config( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): - if bootnode_contexts != None: - cmd.append( - "--boot-nodes=" - + ",".join( - [ - ctx.enr - for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES] - ] - ) + if bootnode_contexts != None and bootnode_arg == None: + bootnode_arg = ",".join( + [ctx.enr for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]] ) elif network_params.network == constants.NETWORK_NAME.ephemery: - cmd.append( - "--boot-nodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) elif constants.NETWORK_NAME.shadowfork in network_params.network: - cmd.append( - "--boot-nodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) else: # Devnets - cmd.append( - "--boot-nodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) + + if bootnode_arg != None: + cmd.append("--boot-nodes=" + bootnode_arg) if len(participant.cl_extra_params) > 0: # we do the list comprehension as the default participant.extra_params is a proto repeated string diff --git a/src/cl/lighthouse/lighthouse_launcher.star b/src/cl/lighthouse/lighthouse_launcher.star index 5ccf0f59a..e47bc1597 100644 --- a/src/cl/lighthouse/lighthouse_launcher.star +++ b/src/cl/lighthouse/lighthouse_launcher.star @@ -60,6 +60,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): # Launch Beacon node beacon_config = get_beacon_config( @@ -84,6 +85,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, beacon_config) @@ -123,6 +125,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -232,39 +235,34 @@ def get_beacon_config( else: cmd.append("--allow-insecure-genesis-sync") + bootnode_arg = bootnode_enr_override + if network_params.network not in constants.PUBLIC_NETWORKS: cmd.append("--testnet-dir=" + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER) if ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): - if bootnode_contexts != None: - cmd.append( - "--boot-nodes=" - + ",".join( - [ - ctx.enr - for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES] - ] - ) + if bootnode_arg == None and bootnode_contexts != None: + bootnode_arg = ",".join( + [ctx.enr for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]] ) elif network_params.network == constants.NETWORK_NAME.ephemery: - cmd.append( - "--boot-nodes=" - + shared_utils.get_devnet_enrs_list( - plan, launcher.el_cl_genesis_data.files_artifact_uuid - ) - ) - else: # Devnets - cmd.append( - "--boot-nodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) + elif bootnode_arg == None: # Devnets + bootnode_arg = shared_utils.get_devnet_enrs_list( + plan, launcher.el_cl_genesis_data.files_artifact_uuid ) else: # Public networks cmd.append("--network=" + network_params.network) + # Add bootnode argument if set + if bootnode_arg != None: + cmd.append("--boot-nodes=" + bootnode_arg) + # Add tempo telemetry integration if tempo is enabled if tempo_otlp_grpc_url != None: cmd.append("--telemetry-collector-url={}".format(tempo_otlp_grpc_url)) diff --git a/src/cl/lodestar/lodestar_launcher.star b/src/cl/lodestar/lodestar_launcher.star index f2d421803..0bbc74b2d 100644 --- a/src/cl/lodestar/lodestar_launcher.star +++ b/src/cl/lodestar/lodestar_launcher.star @@ -49,6 +49,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): # Launch Beacon node beacon_config = get_beacon_config( @@ -73,6 +74,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, beacon_config) @@ -112,6 +114,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -209,6 +212,8 @@ def get_beacon_config( if checkpoint_sync_enabled: cmd.append("--checkpointSyncUrl=" + checkpoint_sync_url) + bootnode_arg = bootnode_enr_override + if network_params.network not in constants.PUBLIC_NETWORKS: cmd.append( "--paramsFile=" @@ -224,33 +229,26 @@ def get_beacon_config( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): - if bootnode_contexts != None: - cmd.append( - "--bootnodes=" - + ",".join( - [ - ctx.enr - for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES] - ] - ) + if bootnode_contexts != None and bootnode_arg == None: + bootnode_arg = ",".join( + [ctx.enr for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]] ) elif network_params.network == constants.NETWORK_NAME.ephemery: - cmd.append( - "--bootnodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) else: # Devnets - cmd.append( - "--bootnodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) else: # Public testnet cmd.append("--network=" + network_params.network) + if bootnode_arg != None: + cmd.append("--bootnodes=" + bootnode_arg) + if len(participant.cl_extra_params) > 0: # this is a repeated, we convert it into Starlark cmd.extend([param for param in participant.cl_extra_params]) diff --git a/src/cl/nimbus/nimbus_launcher.star b/src/cl/nimbus/nimbus_launcher.star index 1547f16d7..61f179e35 100644 --- a/src/cl/nimbus/nimbus_launcher.star +++ b/src/cl/nimbus/nimbus_launcher.star @@ -68,6 +68,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): beacon_config = get_beacon_config( plan, @@ -91,6 +92,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, beacon_config) @@ -130,6 +132,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -267,7 +270,9 @@ def get_beacon_config( if participant.supernode: cmd.extend(supernode_cmd) - if network_params.network not in constants.PUBLIC_NETWORKS: + if bootnode_enr_override != None: + cmd.append("--bootstrap-node=" + bootnode_enr_override) + elif network_params.network not in constants.PUBLIC_NETWORKS: cmd.append( "--bootstrap-file=" + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER diff --git a/src/cl/prysm/prysm_launcher.star b/src/cl/prysm/prysm_launcher.star index c3f929e3c..0f6ded00d 100644 --- a/src/cl/prysm/prysm_launcher.star +++ b/src/cl/prysm/prysm_launcher.star @@ -53,6 +53,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): beacon_config = get_beacon_config( plan, @@ -76,6 +77,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, beacon_config) @@ -115,6 +117,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -244,6 +247,9 @@ def get_beacon_config( if network_params.preset == "minimal": cmd.append("--minimal-config=true") + if bootnode_enr_override != None: + cmd.append("--bootstrap-node=" + bootnode_enr_override) + if network_params.network not in constants.PUBLIC_NETWORKS: cmd.append("--p2p-static-id=true") cmd.append( @@ -261,7 +267,7 @@ def get_beacon_config( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): - if bootnode_contexts != None: + if bootnode_enr_override == None and bootnode_contexts != None: for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]: cmd.append("--bootstrap-node=" + ctx.enr) elif network_params.network == constants.NETWORK_NAME.ephemery: @@ -269,17 +275,19 @@ def get_beacon_config( "--genesis-beacon-api-url=" + constants.CHECKPOINT_SYNC_URL[network_params.network] ) - cmd.append( - "--bootstrap-node=" - + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER - + "/bootstrap_nodes.yaml" - ) + if bootnode_enr_override == None: + cmd.append( + "--bootstrap-node=" + + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER + + "/bootstrap_nodes.yaml" + ) else: # Devnets - cmd.append( - "--bootstrap-node=" - + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER - + "/bootstrap_nodes.yaml" - ) + if bootnode_enr_override == None: + cmd.append( + "--bootstrap-node=" + + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER + + "/bootstrap_nodes.yaml" + ) else: # Public network cmd.append("--{}".format(network_params.network)) diff --git a/src/cl/teku/teku_launcher.star b/src/cl/teku/teku_launcher.star index 874322382..eb97533cb 100644 --- a/src/cl/teku/teku_launcher.star +++ b/src/cl/teku/teku_launcher.star @@ -54,6 +54,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url=None, + bootnode_enr_override=None, ): config = get_beacon_config( plan, @@ -77,6 +78,7 @@ def launch( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override, ) beacon_service = plan.add_service(beacon_service_name, config) @@ -116,6 +118,7 @@ def get_beacon_config( extra_files_artifacts, backend, tempo_otlp_grpc_url, + bootnode_enr_override=None, ): log_level = input_parser.get_client_log_level_or_default( participant.cl_log_level, global_log_level, VERBOSITY_LEVELS @@ -252,6 +255,8 @@ def get_beacon_config( else: cmd.append("--ignore-weak-subjectivity-period-enabled=true") + bootnode_arg = bootnode_enr_override + if network_params.network not in constants.PUBLIC_NETWORKS: cmd.append( "--genesis-state=" @@ -262,37 +267,28 @@ def get_beacon_config( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): - if bootnode_contexts != None: - cmd.append( - "--p2p-discovery-bootnodes=" - + ",".join( - [ - ctx.enr - for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES] - ] - ) + if bootnode_contexts != None and bootnode_arg == None: + bootnode_arg = ",".join( + [ctx.enr for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]] ) elif network_params.network == constants.NETWORK_NAME.ephemery: - cmd.append( - "--p2p-discovery-bootnodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) elif constants.NETWORK_NAME.shadowfork in network_params.network: - cmd.append( - "--p2p-discovery-bootnodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) else: # Devnets - cmd.append( - "--p2p-discovery-bootnodes=" - + shared_utils.get_devnet_enrs_list( + if bootnode_arg == None: + bootnode_arg = shared_utils.get_devnet_enrs_list( plan, launcher.el_cl_genesis_data.files_artifact_uuid ) - ) + + if bootnode_arg != None: + cmd.append("--p2p-discovery-bootnodes=" + bootnode_arg) if len(participant.cl_extra_params) > 0: # we do the list comprehension as the default extra_params is a proto repeated string diff --git a/src/el/besu/besu_launcher.star b/src/el/besu/besu_launcher.star index 3ea32c3e0..0098f1eba 100644 --- a/src/el/besu/besu_launcher.star +++ b/src/el/besu/besu_launcher.star @@ -44,6 +44,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -62,6 +63,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -89,6 +91,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -182,7 +185,10 @@ def get_config( else: cmd.append("--network=" + network_params.network) - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/el/el_launcher.star b/src/el/el_launcher.star index dd2ed0e8f..377c647ad 100644 --- a/src/el/el_launcher.star +++ b/src/el/el_launcher.star @@ -31,6 +31,7 @@ def launch( mev_builder_type, mev_params, extra_files_artifacts={}, + bootnodoor_enode=None, ): el_launchers = { constants.EL_TYPE.geth: { @@ -125,6 +126,14 @@ def launch( el_service_configs = {} el_participant_info = {} + # Generic bootnode ENODE override - can be set from bootnodoor or any other bootnode service + if bootnodoor_enode != None: + plan.print( + "Using bootnode ENODE override for all EL clients: {0}".format( + bootnodoor_enode + ) + ) + for index, participant in enumerate(participants): cl_type = participant.cl_type el_type = participant.el_type @@ -171,6 +180,7 @@ def launch( index, network_params, extra_files_artifacts, + bootnodoor_enode, ) # Add participant el additional prometheus metrics @@ -195,6 +205,7 @@ def launch( index, network_params, extra_files_artifacts, + bootnodoor_enode, ) el_participant_info[el_service_name] = { diff --git a/src/el/erigon/erigon_launcher.star b/src/el/erigon/erigon_launcher.star index 110dbb630..18a13a0cf 100644 --- a/src/el/erigon/erigon_launcher.star +++ b/src/el/erigon/erigon_launcher.star @@ -42,6 +42,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -60,6 +61,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -87,6 +89,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -210,7 +213,10 @@ def get_config( for mount_path, artifact in processed_mounts.items(): files[mount_path] = artifact - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/el/ethereumjs/ethereumjs_launcher.star b/src/el/ethereumjs/ethereumjs_launcher.star index 754bd85c3..d4bf8d75a 100644 --- a/src/el/ethereumjs/ethereumjs_launcher.star +++ b/src/el/ethereumjs/ethereumjs_launcher.star @@ -44,6 +44,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -62,6 +63,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -89,6 +91,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -177,7 +180,10 @@ def get_config( else: cmd.append("--network=" + network_params.network) - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/el/ethrex/ethrex_launcher.star b/src/el/ethrex/ethrex_launcher.star index bb9683458..af76ed7dc 100644 --- a/src/el/ethrex/ethrex_launcher.star +++ b/src/el/ethrex/ethrex_launcher.star @@ -59,6 +59,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -77,6 +78,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -104,6 +106,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): public_ports = {} public_ports_for_component = None @@ -161,7 +164,10 @@ def get_config( "--metrics.addr=0.0.0.0", "--metrics.port={0}".format(METRICS_PORT_NUM), ] - if network_params.network == constants.NETWORK_NAME.kurtosis: + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif network_params.network == constants.NETWORK_NAME.kurtosis: if len(existing_el_clients) > 0: cmd.append( "--bootnodes=" diff --git a/src/el/geth/geth_launcher.star b/src/el/geth/geth_launcher.star index 8b46e831c..e40887c05 100644 --- a/src/el/geth/geth_launcher.star +++ b/src/el/geth/geth_launcher.star @@ -51,6 +51,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -69,6 +70,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -96,6 +98,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -219,7 +222,10 @@ def get_config( if "--ws.api" in arg: cmd[index] = "--ws.api=admin,engine,net,eth,web3,debug,suavex" - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): @@ -232,7 +238,6 @@ def get_config( ] ) ) - elif ( network_params.network not in constants.PUBLIC_NETWORKS and constants.NETWORK_NAME.shadowfork not in network_params.network @@ -244,6 +249,11 @@ def get_config( ) ) + if constants.NETWORK_NAME.shadowfork in network_params.network: # shadowfork + cmd.append("--override.osaka=" + str(launcher.shadowfork_times["osaka_time"])) + cmd.append("--override.bpo1=" + str(launcher.shadowfork_times["bpo_1_time"])) + cmd.append("--override.bpo2=" + str(launcher.shadowfork_times["bpo_2_time"])) + if len(participant.el_extra_params) > 0: # this is a repeated, we convert it into Starlark cmd.extend([param for param in participant.el_extra_params]) diff --git a/src/el/nethermind/nethermind_launcher.star b/src/el/nethermind/nethermind_launcher.star index d7672d85e..0afb3e686 100644 --- a/src/el/nethermind/nethermind_launcher.star +++ b/src/el/nethermind/nethermind_launcher.star @@ -40,6 +40,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -58,6 +59,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -85,6 +87,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -179,7 +182,10 @@ def get_config( else: cmd.append("--config=" + network_params.network) - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--Discovery.Bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/el/nimbus-eth1/nimbus_launcher.star b/src/el/nimbus-eth1/nimbus_launcher.star index aedbe1673..d88512455 100644 --- a/src/el/nimbus-eth1/nimbus_launcher.star +++ b/src/el/nimbus-eth1/nimbus_launcher.star @@ -40,6 +40,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -58,6 +59,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -85,6 +87,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -160,7 +163,10 @@ def get_config( else: cmd.append("--network=" + network_params.network) - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootstrap-node=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/el/reth/reth_launcher.star b/src/el/reth/reth_launcher.star index eb28aff15..1bba190bc 100644 --- a/src/el/reth/reth_launcher.star +++ b/src/el/reth/reth_launcher.star @@ -49,6 +49,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): cl_client_name = service_name.split("-")[3] @@ -67,6 +68,7 @@ def launch( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode, ) service = plan.add_service(service_name, config) @@ -94,6 +96,7 @@ def get_config( participant_index, network_params, extra_files_artifacts, + bootnodoor_enode=None, ): log_level = input_parser.get_client_log_level_or_default( participant.el_log_level, global_log_level, VERBOSITY_LEVELS @@ -204,7 +207,10 @@ def get_config( if network_params.gas_limit > 0: cmd.append("--builder.gaslimit={0}".format(network_params.gas_limit)) - if ( + # Handle bootnode configuration with bootnodoor_enode override + if bootnodoor_enode != None: + cmd.append("--bootnodes=" + bootnodoor_enode) + elif ( network_params.network == constants.NETWORK_NAME.kurtosis or constants.NETWORK_NAME.shadowfork in network_params.network ): diff --git a/src/package_io/constants.star b/src/package_io/constants.star index c9af2c3d7..accd1498d 100644 --- a/src/package_io/constants.star +++ b/src/package_io/constants.star @@ -97,6 +97,7 @@ DEFAULT_CHECKPOINTZ_IMAGE = "ethpandaops/checkpointz:latest" DEFAULT_SPAMOOR_IMAGE = "ethpandaops/spamoor:latest" DEFAULT_ASSERTOOR_IMAGE = "ethpandaops/assertoor:latest" DEFAULT_SNOOPER_IMAGE = "ethpandaops/rpc-snooper:latest" +DEFAULT_BOOTNODOOR_IMAGE = "ethpandaops/bootnodoor:latest" DEFAULT_ETHEREUM_GENESIS_GENERATOR_IMAGE = ( "ethpandaops/ethereum-genesis-generator:5.2.0" ) diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 5b7c1295c..4a9bdc02d 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -81,6 +81,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = ( "xatu_sentry_params", "port_publisher", "spamoor_params", + "bootnodoor_params", "mempool_bridge_params", ) @@ -191,6 +192,10 @@ def input_parser(plan, input_args): for sub_attr in input_args["mempool_bridge_params"]: sub_value = input_args["mempool_bridge_params"][sub_attr] result["mempool_bridge_params"][sub_attr] = sub_value + elif attr == "bootnodoor_params": + for sub_attr in input_args["bootnodoor_params"]: + sub_value = input_args["bootnodoor_params"][sub_attr] + result["bootnodoor_params"][sub_attr] = sub_value elif attr == "ethereum_genesis_generator_params": for sub_attr in input_args["ethereum_genesis_generator_params"]: sub_value = input_args["ethereum_genesis_generator_params"][sub_attr] @@ -784,6 +789,15 @@ def input_parser(plan, input_args): ], other_nat_exit_ip=result["port_publisher"]["other"]["nat_exit_ip"], ), + bootnode=result["bootnode"], + bootnodoor_params=struct( + image=result["bootnodoor_params"]["image"], + min_cpu=result["bootnodoor_params"]["min_cpu"], + max_cpu=result["bootnodoor_params"]["max_cpu"], + min_mem=result["bootnodoor_params"]["min_mem"], + max_mem=result["bootnodoor_params"]["max_mem"], + extra_args=result["bootnodoor_params"]["extra_args"], + ), ) @@ -1202,6 +1216,8 @@ def default_input_args(input_args): "public_port_start": None, }, "spamoor_params": get_default_spamoor_params(), + "bootnode": "client", + "bootnodoor_params": get_default_bootnodoor_params(), } @@ -1693,6 +1709,17 @@ def get_default_mempool_bridge_params(): } +def get_default_bootnodoor_params(): + return { + "image": constants.DEFAULT_BOOTNODOOR_IMAGE, + "min_cpu": 100, + "max_cpu": 1000, + "min_mem": 128, + "max_mem": 512, + "extra_args": [], + } + + def get_port_publisher_params(parameter_type, input_args=None): port_publisher_parameters = { "nat_exit_ip": "KURTOSIS_IP_ADDR_PLACEHOLDER", diff --git a/src/package_io/sanity_check.star b/src/package_io/sanity_check.star index b98e73dba..542a50696 100644 --- a/src/package_io/sanity_check.star +++ b/src/package_io/sanity_check.star @@ -377,6 +377,14 @@ SUBCATEGORY_PARAMS = { "image", "extra_env", ], + "bootnodoor_params": [ + "image", + "min_cpu", + "max_cpu", + "min_mem", + "max_mem", + "extra_args", + ], } ADDITIONAL_SERVICES_PARAMS = [ @@ -422,6 +430,7 @@ ADDITIONAL_CATEGORY_PARAMS = { "keymanager_enabled": "", "checkpoint_sync_enabled": "", "checkpoint_sync_url": "", + "bootnode": "", } diff --git a/src/participant_network.star b/src/participant_network.star index a04f0798b..0f91fd4de 100644 --- a/src/participant_network.star +++ b/src/participant_network.star @@ -32,6 +32,7 @@ beacon_snooper = import_module("./snooper/snooper_beacon_launcher.star") snooper_el_launcher = import_module("./snooper/snooper_el_launcher.star") blobber_launcher = import_module("./blobber/blobber_launcher.star") cl_context_module = import_module("./cl/cl_context.star") +bootnodoor_launcher = import_module("./bootnodoor/bootnodoor_launcher.star") def launch_participant_network( @@ -141,6 +142,23 @@ def launch_participant_network( global_node_selectors, ) + # Launch bootnodoor if configured + bootnodoor_enr = None + bootnodoor_enode = None + if args_with_right_defaults.bootnode == "bootnodoor": + plan.print("Launching bootnodoor as bootnode service") + bootnodoor_enr, bootnodoor_enode = bootnodoor_launcher.launch_bootnodoor( + plan, + args_with_right_defaults.bootnodoor_params, + el_cl_data, + network_params, + global_node_selectors, + global_tolerations, + args_with_right_defaults.docker_cache_params, + ) + plan.print("Bootnodoor launched with ENR: {0}".format(bootnodoor_enr)) + plan.print("Bootnodoor launched with ENODE: {0}".format(bootnodoor_enode)) + # Launch all execution layer clients all_el_contexts = el_client_launcher.launch( plan, @@ -158,6 +176,7 @@ def launch_participant_network( args_with_right_defaults.mev_type, args_with_right_defaults.mev_params, extra_files_artifacts, + bootnodoor_enode, ) # Launch all consensus layer clients @@ -197,6 +216,7 @@ def launch_participant_network( global_other_index, extra_files_artifacts, backend, + bootnodoor_enr, ) # Launch all blobbers after all CLs are up