Skip to content
Merged
188 changes: 188 additions & 0 deletions src/bootnodoor/bootnodoor_launcher.star
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions src/cl/cl_launcher.star
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def launch(
global_other_index,
extra_files_artifacts,
backend,
bootnodoor_enr=None,
):
plan.print("Launching CL network")

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -230,6 +241,7 @@ def launch(
extra_files_artifacts,
backend,
tempo_otlp_grpc_url,
bootnode_enr_override,
)

blobber_config = get_blobber_config(
Expand Down Expand Up @@ -280,6 +292,7 @@ def launch(
extra_files_artifacts,
backend,
tempo_otlp_grpc_url,
bootnode_enr_override,
)

cl_participant_info[cl_service_name] = {
Expand Down
38 changes: 17 additions & 21 deletions src/cl/grandine/grandine_launcher.star
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def launch(
extra_files_artifacts,
backend,
tempo_otlp_grpc_url=None,
bootnode_enr_override=None,
):
config = get_beacon_config(
plan,
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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="
Expand All @@ -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
Expand Down
36 changes: 17 additions & 19 deletions src/cl/lighthouse/lighthouse_launcher.star
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down
Loading