Commit 2bae0993 authored by Barnabas Busa's avatar Barnabas Busa Committed by GitHub

feat: add devnets support (#384)

parent 8d400566
participants:
- el_client_type: besu
cl_client_type: lighthouse
additional_services: []
persistent: true
participants:
- el_client_type: besu
cl_client_type: teku
- el_client_type: erigon
cl_client_type: lighthouse
cl_client_type: teku
additional_services: []
persistent: true
participants:
- el_client_type: geth
el_client_image: ethpandaops/geth:master
cl_client_type: teku
cl_client_image: ethpandaops/teku:master
- el_client_type: nethermind
el_client_image: ethpandaops/nethermind:master
cl_client_type: prysm
cl_client_image: ethpandaops/prysm:develop,ethpandaops/prysm-validator:develop
- el_client_type: erigon
el_client_image: ethpandaops/erigon:devel
cl_client_type: nimbus
cl_client_image: ethpandaops/nimbus:unstable
- el_client_type: besu
el_client_image: ethpandaops/besu:main
cl_client_type: lighthouse
cl_client_image: ethpandaops/lighthouse:unstable
- el_client_type: reth
el_client_image: ethpandaops/reth:main
cl_client_type: lodestar
cl_client_image: ethpandaops/lodestar:unstable
- el_client_type: ethereumjs
el_client_image: ethpandaops/ethereumjs:master
cl_client_type: teku
cl_client_image: ethpandaops/teku:master
network_params:
network: "dencun-devnet-12"
additional_services: []
participants:
- el_client_type: geth
cl_client_type: teku
additional_services: []
persistent: true
......@@ -69,6 +69,11 @@ def run(plan, args={}):
)
prometheus_additional_metrics_jobs = []
raw_jwt_secret = read_file(static_files.JWT_PATH_FILEPATH)
jwt_file = plan.upload_files(
src=static_files.JWT_PATH_FILEPATH,
name="jwt_file",
)
plan.print("Read the prometheus, grafana templates")
plan.print(
......@@ -86,6 +91,7 @@ def run(plan, args={}):
args_with_right_defaults.participants,
network_params,
args_with_right_defaults.global_client_log_level,
jwt_file,
persistent,
parallel_keystore_generation,
)
......@@ -117,11 +123,12 @@ def run(plan, args={}):
all_cl_client_contexts,
args_with_right_defaults.participants,
)
if network_params.network == "kurtosis":
if network_params.deneb_fork_epoch != 0:
plan.print("Launching 4788 contract deployer")
el_uri = "http://{0}:{1}".format(
all_el_client_contexts[0].ip_addr, all_el_client_contexts[0].rpc_port_num
all_el_client_contexts[0].ip_addr,
all_el_client_contexts[0].rpc_port_num,
)
eip4788_deployment.deploy_eip4788_contract_in_background(
plan,
......@@ -165,17 +172,11 @@ def run(plan, args={}):
beacon_uri = "{0}:{1}".format(
all_cl_client_contexts[0].ip_addr, all_cl_client_contexts[0].http_port_num
)
jwt_secret = plan.run_sh(
run="cat " + constants.JWT_AUTH_PATH + " | tr -d '\n'",
image="busybox",
files={"/data": el_cl_data_files_artifact_uuid},
wait=None,
)
endpoint = mock_mev.launch_mock_mev(
plan,
el_uri,
beacon_uri,
jwt_secret.output,
raw_jwt_secret,
args_with_right_defaults.global_client_log_level,
)
mev_endpoints.append(endpoint)
......
......@@ -3,7 +3,6 @@ input_parser = import_module("../../package_io/input_parser.star")
cl_client_context = import_module("../../cl/cl_client_context.star")
node_metrics = import_module("../../node_metrics_info.star")
cl_node_ready_conditions = import_module("../../cl/cl_node_ready_conditions.star")
constants = import_module("../../package_io/constants.star")
blobber_launcher = import_module("../../blobber/blobber_launcher.star")
......@@ -139,7 +138,10 @@ def launch(
# Launch Beacon node
beacon_config = get_beacon_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
beacon_service_name,
bootnode_contexts,
......@@ -263,7 +265,10 @@ def launch(
def get_beacon_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
boot_cl_client_ctxs,
......@@ -325,7 +330,7 @@ def get_beacon_config(
# and the option says it's "useful for testing in smaller networks" (unclear what happens in larger networks)
"--disable-packet-filter",
"--execution-endpoints=" + EXECUTION_ENGINE_ENDPOINT,
"--jwt-secrets=" + constants.JWT_AUTH_PATH,
"--jwt-secrets=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--suggested-fee-recipient=" + constants.VALIDATING_REWARDS_ACCOUNT,
# Set per Paris' recommendation to reduce noise in the logs
"--subscribe-all-subnets",
......@@ -337,11 +342,15 @@ def get_beacon_config(
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if network == "kurtosis":
if boot_cl_client_ctxs != None:
cmd.append(
"--boot-nodes="
+ ",".join(
[ctx.enr for ctx in boot_cl_client_ctxs[: constants.MAX_ENR_ENTRIES]]
[
ctx.enr
for ctx in boot_cl_client_ctxs[: constants.MAX_ENR_ENTRIES]
]
)
)
cmd.append(
......@@ -353,6 +362,13 @@ def get_beacon_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--boot-nodes="
+ shared_utils.get_devnet_enrs_list(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -362,7 +378,8 @@ def get_beacon_config(
endpoint="/eth/v1/node/identity", port_id=BEACON_HTTP_PORT_ID
)
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -476,7 +493,9 @@ def get_validator_config(
)
def new_lighthouse_launcher(el_cl_genesis_data):
def new_lighthouse_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -116,7 +116,10 @@ def launch(
# Launch Beacon node
beacon_config = get_beacon_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
beacon_service_name,
bootnode_contexts,
......@@ -232,7 +235,10 @@ def launch(
def get_beacon_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
bootnode_contexts,
......@@ -293,14 +299,14 @@ def get_beacon_config(
"--enr.udp={0}".format(DISCOVERY_PORT_NUM),
# Set per Pari's recommendation to reduce noise in the logs
"--subscribeAllSubnets=true",
"--jwt-secret=" + constants.JWT_AUTH_PATH,
"--jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics",
"--metrics.address=0.0.0.0",
"--metrics.port={0}".format(METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if network == "kurtosis":
if bootnode_contexts != None:
cmd.append(
"--bootnodes="
......@@ -308,12 +314,20 @@ def get_beacon_config(
[ctx.enr for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enrs_list(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
cmd.extend([param for param in extra_params])
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -424,7 +438,9 @@ def get_validator_config(
)
def new_lodestar_launcher(el_cl_genesis_data):
def new_lodestar_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -155,7 +155,10 @@ def launch(
bn_max_mem = int(bn_max_mem) if int(bn_max_mem) > 0 else BEACON_MAX_MEMORY
beacon_config = get_beacon_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
beacon_service_name,
bootnode_contexts,
......@@ -261,7 +264,10 @@ def launch(
def get_beacon_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
bootnode_contexts,
......@@ -325,7 +331,7 @@ def get_beacon_config(
"--subscribe-all-subnets=true",
# Nimbus can handle a max of 256 threads, if the host has more then nimbus crashes. Setting it to 4 so it doesn't crash on build servers
"--num-threads=4",
"--jwt-secret=" + constants.JWT_AUTH_PATH,
"--jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics",
"--metrics-address=0.0.0.0",
......@@ -346,6 +352,7 @@ def get_beacon_config(
if node_keystore_files != None and not split_mode_enabled:
cmd.extend(validator_flags)
if network == "kurtosis":
if bootnode_contexts == None:
# Copied from https://github.com/status-im/nimbus-eth2/blob/67ab477a27e358d605e99bffeb67f98d18218eca/scripts/launch_local_testnet.sh#L417
# See explanation there
......@@ -354,12 +361,18 @@ def get_beacon_config(
for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]:
cmd.append("--bootstrap-node=" + ctx.enr)
cmd.append("--direct-peer=" + ctx.multiaddr)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootstrap-node="
+ shared_utils.get_devnet_enr(plan, el_cl_genesis_data.files_artifact_uuid)
)
if len(extra_params) > 0:
cmd.extend([param for param in extra_params])
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if node_keystore_files != None and not split_mode_enabled:
files[
......@@ -465,7 +478,9 @@ def get_validator_config(
)
def new_nimbus_launcher(el_cl_genesis_data):
def new_nimbus_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -142,7 +142,10 @@ def launch(
bn_max_mem = int(bn_max_mem) if int(bn_max_mem) > 0 else BEACON_MAX_MEMORY
beacon_config = get_beacon_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
beacon_image,
beacon_service_name,
bootnode_contexts,
......@@ -250,7 +253,10 @@ def launch(
def get_beacon_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
beacon_image,
service_name,
bootnode_contexts,
......@@ -302,7 +308,7 @@ def get_beacon_config(
"--suggested-fee-recipient=" + constants.VALIDATING_REWARDS_ACCOUNT,
# Set per Pari's recommendation to reduce noise
"--subscribe-all-subnets=true",
"--jwt-secret=" + constants.JWT_AUTH_PATH,
"--jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--enable-debug-rpc-endpoints=true",
# vvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--disable-monitoring=false",
......@@ -310,19 +316,26 @@ def get_beacon_config(
"--monitoring-port={0}".format(BEACON_MONITORING_PORT_NUM)
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if network == "kurtosis":
if bootnode_contexts != None:
for ctx in bootnode_contexts[: constants.MAX_ENR_ENTRIES]:
cmd.append("--peer=" + ctx.multiaddr)
cmd.append("--bootstrap-node=" + ctx.enr)
cmd.append("--p2p-static-id=true")
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootstrap-node="
+ shared_utils.get_devnet_enr(plan, el_cl_genesis_data.files_artifact_uuid)
)
cmd.append("--p2p-static-id=true")
if len(extra_params) > 0:
# we do the for loop as otherwise its a proto repeated array
cmd.extend([param for param in extra_params])
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -394,7 +407,6 @@ def get_validator_config(
"--monitoring-port={0}".format(VALIDATOR_MONITORING_PORT_NUM),
"--verbosity=" + log_level,
"--suggested-fee-recipient=" + constants.VALIDATING_REWARDS_ACCOUNT,
# TODO(old) SOMETHING ABOUT JWT
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--disable-monitoring=false",
"--monitoring-host=0.0.0.0",
......@@ -440,10 +452,16 @@ def get_validator_config(
def new_prysm_launcher(
el_cl_genesis_data, prysm_password_relative_filepath, prysm_password_artifact_uuid
el_cl_genesis_data,
jwt_file,
network,
prysm_password_relative_filepath,
prysm_password_artifact_uuid,
):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
prysm_password_artifact_uuid=prysm_password_artifact_uuid,
prysm_password_relative_filepath=prysm_password_relative_filepath,
)
......@@ -3,7 +3,6 @@ input_parser = import_module("../../package_io/input_parser.star")
cl_client_context = import_module("../../cl/cl_client_context.star")
node_metrics = import_module("../../node_metrics_info.star")
cl_node_ready_conditions = import_module("../../cl/cl_node_ready_conditions.star")
constants = import_module("../../package_io/constants.star")
TEKU_BINARY_FILEPATH_IN_IMAGE = "/opt/teku/bin/teku"
......@@ -145,7 +144,10 @@ def launch(
bn_max_mem = int(bn_max_mem) if int(bn_max_mem) > 0 else BEACON_MAX_MEMORY
config = get_beacon_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
beacon_service_name,
bootnode_context,
......@@ -254,7 +256,10 @@ def launch(
def get_beacon_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
bootnode_contexts,
......@@ -319,7 +324,7 @@ def get_beacon_config(
"--rest-api-port={0}".format(BEACON_HTTP_PORT_NUM),
"--rest-api-host-allowlist=*",
"--data-storage-non-canonical-blocks-enabled=true",
"--ee-jwt-secret-file=" + constants.JWT_AUTH_PATH,
"--ee-jwt-secret-file=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--ee-endpoint=" + EXECUTION_ENGINE_ENDPOINT,
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics-enabled",
......@@ -328,6 +333,8 @@ def get_beacon_config(
"--metrics-categories=BEACON,PROCESS,LIBP2P,JVM,NETWORK,PROCESS",
"--metrics-port={0}".format(BEACON_METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
# To enable syncing other networks too without checkpoint syncing
"--ignore-weak-subjectivity-period-enabled=true",
]
validator_flags = [
"--validator-keys={0}:{1}".format(
......@@ -344,7 +351,7 @@ def get_beacon_config(
if node_keystore_files != None and not split_mode_enabled:
cmd.extend(validator_flags)
if network == "kurtosis":
if bootnode_contexts != None:
cmd.append(
"--p2p-discovery-bootnodes="
......@@ -361,6 +368,13 @@ def get_beacon_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--p2p-discovery-bootnodes="
+ shared_utils.get_devnet_enrs_list(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# we do the list comprehension as the default extra_params is a proto repeated string
......@@ -368,6 +382,7 @@ def get_beacon_config(
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if node_keystore_files != None and not split_mode_enabled:
files[
......@@ -485,5 +500,7 @@ def get_validator_config(
)
def new_teku_launcher(el_cl_genesis_data):
return struct(el_cl_genesis_data=el_cl_genesis_data)
def new_teku_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data, jwt_file=jwt_file, network=network
)
......@@ -4,7 +4,6 @@ el_client_context = import_module("../../el/el_client_context.star")
el_admin_node_info = import_module("../../el/el_admin_node_info.star")
node_metrics = import_module("../../node_metrics_info.star")
constants = import_module("../../package_io/constants.star")
# The dirpath of the execution data directory on the client container
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/opt/besu/execution-data"
......@@ -89,8 +88,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
launcher.network_id,
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -129,8 +130,10 @@ def launch(
def get_config(
network_id,
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -152,7 +155,6 @@ def get_config(
"--genesis-file="
+ constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER
+ "/besu.json",
"--network-id=" + network_id,
"--host-allowlist=*",
"--rpc-http-enabled=true",
"--rpc-http-host=0.0.0.0",
......@@ -167,7 +169,7 @@ def get_config(
"--p2p-host=" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--p2p-port={0}".format(DISCOVERY_PORT_NUM),
"--engine-rpc-enabled=true",
"--engine-jwt-secret=" + constants.JWT_AUTH_PATH,
"--engine-jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--engine-host-allowlist=*",
"--engine-rpc-port={0}".format(ENGINE_HTTP_RPC_PORT_NUM),
"--sync-mode=FULL",
......@@ -176,7 +178,7 @@ def get_config(
"--metrics-host=0.0.0.0",
"--metrics-port={0}".format(METRICS_PORT_NUM),
]
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--bootnodes="
......@@ -187,6 +189,13 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# we do this as extra_params isn't a normal [] but a proto repeated array
......@@ -198,6 +207,7 @@ def get_config(
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -226,5 +236,9 @@ def get_config(
)
def new_besu_launcher(network_id, el_cl_genesis_data):
return struct(network_id=network_id, el_cl_genesis_data=el_cl_genesis_data)
def new_besu_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -90,8 +90,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
launcher.network_id,
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -132,8 +134,10 @@ def launch(
def get_config(
network_id,
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -148,8 +152,6 @@ def get_config(
extra_labels,
persistent,
):
network_id = network_id
init_datadir_cmd_str = "erigon init --datadir={0} {1}".format(
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER + "/genesis.json",
......@@ -160,7 +162,6 @@ def get_config(
"--log.console.verbosity=" + verbosity_level,
"--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--port={0}".format(DISCOVERY_PORT_NUM),
"--networkid=" + network_id,
"--http.api=eth,erigon,engine,web3,net,debug,trace,txpool,admin",
"--http.vhosts=*",
"--ws",
......@@ -170,15 +171,17 @@ def get_config(
"--http.addr=0.0.0.0",
"--http.corsdomain=*",
"--http.port={0}".format(WS_RPC_PORT_NUM),
"--authrpc.jwtsecret=" + constants.JWT_AUTH_PATH,
"--authrpc.jwtsecret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--authrpc.addr=0.0.0.0",
"--authrpc.port={0}".format(ENGINE_RPC_PORT_NUM),
"--authrpc.vhosts=*",
"--metrics",
"--metrics.addr=0.0.0.0",
"--metrics.port={0}".format(METRICS_PORT_NUM),
"--db.size.limit=100GB",
]
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--bootnodes="
......@@ -198,6 +201,19 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
cmd.append(
"--staticpeers="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -208,6 +224,7 @@ def get_config(
command_arg_str = " && ".join(command_arg)
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -236,8 +253,9 @@ def get_config(
)
def new_erigon_launcher(network_id, el_cl_genesis_data):
def new_erigon_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
network_id=network_id,
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -6,7 +6,6 @@ el_admin_node_info = import_module("../../el/el_admin_node_info.star")
node_metrics = import_module("../../node_metrics_info.star")
constants = import_module("../../package_io/constants.star")
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
WS_PORT_ENGINE_NUM = 8547
......@@ -94,7 +93,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -132,7 +134,10 @@ def launch(
def get_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -165,13 +170,13 @@ def get_config(
"--wsPort={0}".format(WS_PORT_NUM),
"--wsEnginePort={0}".format(WS_PORT_ENGINE_NUM),
"--wsEngineAddr=0.0.0.0",
"--jwt-secret=" + constants.JWT_AUTH_PATH,
"--jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--extIP={0}".format(PRIVATE_IP_ADDRESS_PLACEHOLDER),
"--sync=full",
"--isSingleNode=true",
"--logLevel={0}".format(verbosity_level),
]
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--bootnodes="
......@@ -182,6 +187,13 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -189,6 +201,7 @@ def get_config(
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -217,7 +230,9 @@ def get_config(
)
def new_ethereumjs_launcher(el_cl_genesis_data):
def new_ethereumjs_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -99,8 +99,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
launcher.network_id,
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -144,8 +146,10 @@ def launch(
def get_config(
network_id,
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -211,7 +215,6 @@ def get_config(
),
"--verbosity=" + verbosity_level,
"--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--networkid=" + network_id,
"--http",
"--http.addr=0.0.0.0",
"--http.vhosts=*",
......@@ -230,7 +233,7 @@ def get_config(
"--authrpc.port={0}".format(ENGINE_RPC_PORT_NUM),
"--authrpc.addr=0.0.0.0",
"--authrpc.vhosts=*",
"--authrpc.jwtsecret=" + constants.JWT_AUTH_PATH,
"--authrpc.jwtsecret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--syncmode=full",
"--rpc.allow-unprotected-txs",
"--metrics",
......@@ -245,6 +248,7 @@ def get_config(
if "--ws.api" in arg:
cmd[index] = "--ws.api=admin,engine,net,eth,web3,debug,mev,flashbots"
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--bootnodes="
......@@ -255,6 +259,13 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -269,7 +280,8 @@ def get_config(
command_str = " && ".join(subcommand_strs)
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
files[EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER] = Directory(
......@@ -298,15 +310,17 @@ def get_config(
def new_geth_launcher(
network_id,
el_cl_genesis_data,
jwt_file,
network,
final_genesis_timestamp,
capella_fork_epoch,
electra_fork_epoch=None,
):
return struct(
network_id=network_id,
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
final_genesis_timestamp=final_genesis_timestamp,
capella_fork_epoch=capella_fork_epoch,
electra_fork_epoch=electra_fork_epoch,
......
......@@ -88,7 +88,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -128,7 +131,10 @@ def launch(
def get_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -161,12 +167,12 @@ def get_config(
"--Network.ExternalIp={0}".format(PRIVATE_IP_ADDRESS_PLACEHOLDER),
"--Network.DiscoveryPort={0}".format(DISCOVERY_PORT_NUM),
"--Network.P2PPort={0}".format(DISCOVERY_PORT_NUM),
"--JsonRpc.JwtSecretFile=" + constants.JWT_AUTH_PATH,
"--JsonRpc.JwtSecretFile=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--Network.OnlyStaticPeers=true",
"--Metrics.Enabled=true",
"--Metrics.ExposePort={0}".format(METRICS_PORT_NUM),
]
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--Network.StaticPeers="
......@@ -177,6 +183,13 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--Network.StaticPeers="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -184,6 +197,7 @@ def get_config(
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -212,5 +226,7 @@ def get_config(
)
def new_nethermind_launcher(el_cl_genesis_data):
return struct(el_cl_genesis_data=el_cl_genesis_data)
def new_nethermind_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data, jwt_file=jwt_file, network=network
)
......@@ -5,7 +5,6 @@ el_admin_node_info = import_module("../../el/el_admin_node_info.star")
node_metrics = import_module("../../node_metrics_info.star")
constants = import_module("../../package_io/constants.star")
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
DISCOVERY_PORT_NUM = 30303
......@@ -92,7 +91,10 @@ def launch(
cl_client_name = service_name.split("-")[3]
config = get_config(
plan,
launcher.el_cl_genesis_data,
launcher.jwt_file,
launcher.network,
image,
service_name,
existing_el_clients,
......@@ -131,7 +133,10 @@ def launch(
def get_config(
plan,
el_cl_genesis_data,
jwt_file,
network,
image,
service_name,
existing_el_clients,
......@@ -171,11 +176,11 @@ def get_config(
"--ws.origins=*",
"--nat=extip:" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--authrpc.port={0}".format(ENGINE_RPC_PORT_NUM),
"--authrpc.jwtsecret=" + constants.JWT_AUTH_PATH,
"--authrpc.jwtsecret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER,
"--authrpc.addr=0.0.0.0",
"--metrics=0.0.0.0:{0}".format(METRICS_PORT_NUM),
]
if network == "kurtosis":
if len(existing_el_clients) > 0:
cmd.append(
"--bootnodes="
......@@ -186,6 +191,13 @@ def get_config(
]
)
)
elif network not in constants.PUBLIC_NETWORKS:
cmd.append(
"--bootnodes="
+ shared_utils.get_devnet_enodes(
plan, el_cl_genesis_data.files_artifact_uuid
)
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
......@@ -201,6 +213,7 @@ def get_config(
files = {
constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data.files_artifact_uuid,
constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file,
}
if persistent:
......@@ -229,7 +242,9 @@ def get_config(
)
def new_reth_launcher(el_cl_genesis_data):
def new_reth_launcher(el_cl_genesis_data, jwt_file, network):
return struct(
el_cl_genesis_data=el_cl_genesis_data,
jwt_file=jwt_file,
network=network,
)
......@@ -38,15 +38,24 @@ GENESIS_VALIDATORS_ROOT_PLACEHOLDER = "GENESIS_VALIDATORS_ROOT_PLACEHOLDER"
ARCHIVE_MODE = True
GENESIS_DATA_MOUNTPOINT_ON_CLIENTS = "/data"
GENESIS_DATA_MOUNTPOINT_ON_CLIENTS = "/network-configs"
GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER = (
GENESIS_DATA_MOUNTPOINT_ON_CLIENTS + "/data/custom_config_data"
GENESIS_DATA_MOUNTPOINT_ON_CLIENTS + "/network-configs"
)
JWT_AUTH_PATH = GENESIS_DATA_MOUNTPOINT_ON_CLIENTS + "/data/jwt/jwtsecret"
JWT_MOUNTPOINT_ON_CLIENTS = "/jwt"
JWT_MOUNT_PATH_ON_CONTAINER = JWT_MOUNTPOINT_ON_CLIENTS + "/jwtsecret"
GENESIS_FORK_VERSION = "0x10000038"
BELLATRIX_FORK_VERSION = "0x30000038"
CAPELLA_FORK_VERSION = "0x40000038"
DENEB_FORK_VERSION = "0x50000038"
ELECTRA_FORK_VERSION = "0x60000038"
PUBLIC_NETWORKS = (
"mainnet",
"goerli",
"sepolia",
"holesky",
)
......@@ -64,7 +64,10 @@ def input_parser(plan, input_args):
# add default eth2 input params
result["mev_type"] = None
result["mev_params"] = get_default_mev_params()
if result["network_params"]["network"] == "kurtosis":
result["additional_services"] = DEFAULT_ADDITIONAL_SERVICES
else:
result["additional_services"] = []
result["grafana_additional_dashboards"] = []
result["tx_spammer_params"] = get_default_tx_spammer_params()
result["custom_flood_params"] = get_default_custom_flood_params()
......@@ -181,6 +184,7 @@ def input_parser(plan, input_args):
capella_fork_epoch=result["network_params"]["capella_fork_epoch"],
deneb_fork_epoch=result["network_params"]["deneb_fork_epoch"],
electra_fork_epoch=result["network_params"]["electra_fork_epoch"],
network=result["network_params"]["network"],
),
mev_params=struct(
mev_relay_image=result["mev_params"]["mev_relay_image"],
......@@ -346,7 +350,11 @@ def parse_network_params(input_args):
"deposit_contract_address is empty or spaces it needs to be of non zero length"
)
if result["network_params"]["preregistered_validator_keys_mnemonic"].strip() == "":
if result["network_params"]["network"] == "kurtosis":
if (
result["network_params"]["preregistered_validator_keys_mnemonic"].strip()
== ""
):
fail(
"preregistered_validator_keys_mnemonic is empty or spaces it needs to be of non zero length"
)
......@@ -367,12 +375,17 @@ def parse_network_params(input_args):
):
fail("electra can only happen with capella genesis not bellatrix")
if result["network_params"]["network"] == "kurtosis":
if MIN_VALIDATORS > actual_num_validators:
fail(
"We require at least {0} validators but got {1}".format(
MIN_VALIDATORS, actual_num_validators
)
)
else:
# Don't allow validators on non-kurtosis networks
for participant in result["participants"]:
participant["validator_count"] = 0
return result
......@@ -423,6 +436,7 @@ def default_network_params():
"capella_fork_epoch": 0,
"deneb_fork_epoch": 500,
"electra_fork_epoch": None,
"network": "kurtosis",
}
......
......@@ -5,6 +5,9 @@ validator_keystores = import_module(
el_cl_genesis_data_generator = import_module(
"./prelaunch_data_generator/el_cl_genesis/el_cl_genesis_generator.star"
)
el_cl_genesis_data = import_module(
"./prelaunch_data_generator/el_cl_genesis/el_cl_genesis_data.star"
)
shared_utils = import_module("./shared_utils/shared_utils.star")
static_files = import_module("./static_files/static_files.star")
......@@ -56,11 +59,12 @@ def launch_participant_network(
participants,
network_params,
global_log_level,
jwt_file,
persistent,
parallel_keystore_generation=False,
):
num_participants = len(participants)
if network_params.network == "kurtosis":
plan.print("Generating cl validator key stores")
validator_data = None
if not parallel_keystore_generation:
......@@ -68,15 +72,20 @@ def launch_participant_network(
plan, network_params.preregistered_validator_keys_mnemonic, participants
)
else:
validator_data = validator_keystores.generate_valdiator_keystores_in_parallel(
plan, network_params.preregistered_validator_keys_mnemonic, participants
validator_data = (
validator_keystores.generate_valdiator_keystores_in_parallel(
plan,
network_params.preregistered_validator_keys_mnemonic,
participants,
)
)
plan.print(json.indent(json.encode(validator_data)))
# We need to send the same genesis time to both the EL and the CL to ensure that timestamp based forking works as expected
final_genesis_timestamp = get_final_genesis_timestamp(
plan, CL_GENESIS_DATA_GENERATION_TIME + num_participants * CL_NODE_STARTUP_TIME
plan,
CL_GENESIS_DATA_GENERATION_TIME + num_participants * CL_NODE_STARTUP_TIME,
)
# if preregistered validator count is 0 (default) then calculate the total number of validators from the participants
......@@ -107,7 +116,7 @@ def launch_participant_network(
elif network_params.electra_fork_epoch != None:
if network_params.electra_fork_epoch == 0:
ethereum_genesis_generator_image = (
"ethpandaops/ethereum-genesis-generator:4.0.0-rc.4"
"ethpandaops/ethereum-genesis-generator:4.0.0-rc.3"
)
else:
ethereum_genesis_generator_image = (
......@@ -140,12 +149,34 @@ def launch_participant_network(
network_params.deneb_fork_epoch,
network_params.electra_fork_epoch,
)
else:
# split up the name from dencun-devnet-12 to dencun and devnet-12
devnet_name = network_params.network.split("-")[0]
devnet_number = network_params.network.split("-")[-1]
el_cl_genesis_uuid = plan.upload_files(
src="github.com/ethpandaops/{0}-devnets/network-configs/devnet-{1}".format(
devnet_name, devnet_number
),
name="el_cl_genesis",
)
el_cl_genesis_data_uuid = plan.run_sh(
run="mkdir -p /network-configs/ && mv /opt/* /network-configs/",
store=[StoreSpec(src="/network-configs/", name="el_cl_genesis_data")],
files={"/opt": el_cl_genesis_uuid},
)
el_cl_data = el_cl_genesis_data.new_el_cl_genesis_data(
el_cl_genesis_data_uuid.files_artifacts[0],
"0",
)
final_genesis_timestamp = 0
validator_data = None
el_launchers = {
constants.EL_CLIENT_TYPE.geth: {
"launcher": geth.new_geth_launcher(
network_params.network_id,
el_cl_data,
jwt_file,
network_params.network,
final_genesis_timestamp,
network_params.capella_fork_epoch,
network_params.electra_fork_epoch,
......@@ -154,8 +185,9 @@ def launch_participant_network(
},
constants.EL_CLIENT_TYPE.gethbuilder: {
"launcher": geth.new_geth_launcher(
network_params.network_id,
el_cl_data,
jwt_file,
network_params.network,
final_genesis_timestamp,
network_params.capella_fork_epoch,
network_params.electra_fork_epoch,
......@@ -163,25 +195,43 @@ def launch_participant_network(
"launch_method": geth.launch,
},
constants.EL_CLIENT_TYPE.besu: {
"launcher": besu.new_besu_launcher(network_params.network_id, el_cl_data),
"launcher": besu.new_besu_launcher(
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": besu.launch,
},
constants.EL_CLIENT_TYPE.erigon: {
"launcher": erigon.new_erigon_launcher(
network_params.network_id, el_cl_data
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": erigon.launch,
},
constants.EL_CLIENT_TYPE.nethermind: {
"launcher": nethermind.new_nethermind_launcher(el_cl_data),
"launcher": nethermind.new_nethermind_launcher(
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": nethermind.launch,
},
constants.EL_CLIENT_TYPE.reth: {
"launcher": reth.new_reth_launcher(el_cl_data),
"launcher": reth.new_reth_launcher(
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": reth.launch,
},
constants.EL_CLIENT_TYPE.ethereumjs: {
"launcher": ethereumjs.new_ethereumjs_launcher(el_cl_data),
"launcher": ethereumjs.new_ethereumjs_launcher(
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": ethereumjs.launch,
},
}
......@@ -239,30 +289,51 @@ def launch_participant_network(
plan.print("Successfully added {0} EL participants".format(num_participants))
plan.print("Launching CL network")
prysm_password_relative_filepath = (
validator_data.prysm_password_relative_filepath
if network_params.network == "kurtosis"
else None
)
prysm_password_artifact_uuid = (
validator_data.prysm_password_artifact_uuid
if network_params.network == "kurtosis"
else None
)
cl_launchers = {
constants.CL_CLIENT_TYPE.lighthouse: {
"launcher": lighthouse.new_lighthouse_launcher(el_cl_data),
"launcher": lighthouse.new_lighthouse_launcher(
el_cl_data, jwt_file, network_params.network
),
"launch_method": lighthouse.launch,
},
constants.CL_CLIENT_TYPE.lodestar: {
"launcher": lodestar.new_lodestar_launcher(el_cl_data),
"launcher": lodestar.new_lodestar_launcher(
el_cl_data, jwt_file, network_params.network
),
"launch_method": lodestar.launch,
},
constants.CL_CLIENT_TYPE.nimbus: {
"launcher": nimbus.new_nimbus_launcher(el_cl_data),
"launcher": nimbus.new_nimbus_launcher(
el_cl_data, jwt_file, network_params.network
),
"launch_method": nimbus.launch,
},
constants.CL_CLIENT_TYPE.prysm: {
"launcher": prysm.new_prysm_launcher(
el_cl_data,
validator_data.prysm_password_relative_filepath,
validator_data.prysm_password_artifact_uuid,
jwt_file,
network_params.network,
prysm_password_relative_filepath,
prysm_password_artifact_uuid,
),
"launch_method": prysm.launch,
},
constants.CL_CLIENT_TYPE.teku: {
"launcher": teku.new_teku_launcher(el_cl_data),
"launcher": teku.new_teku_launcher(
el_cl_data,
jwt_file,
network_params.network,
),
"launch_method": teku.launch,
},
}
......@@ -270,7 +341,11 @@ def launch_participant_network(
all_snooper_engine_contexts = []
all_cl_client_contexts = []
all_ethereum_metrics_exporter_contexts = []
preregistered_validator_keys_for_nodes = validator_data.per_node_keystores
preregistered_validator_keys_for_nodes = (
validator_data.per_node_keystores
if network_params.network == "kurtosis"
else None
)
for index, participant in enumerate(participants):
cl_client_type = participant.cl_client_type
......
......@@ -56,16 +56,22 @@ def generate_el_cl_genesis_data(
)
genesis = plan.run_sh(
run="cp /opt/values.env /config/values.env && ./entrypoint.sh all",
run="cp /opt/values.env /config/values.env && ./entrypoint.sh all && mkdir /network-configs && mv /data/custom_config_data/* /network-configs/",
image=image,
files={GENESIS_VALUES_PATH: genesis_generation_config_artifact_name},
store=[StoreSpec(src="/data", name="el-cl-genesis-data")],
store=[
StoreSpec(src="/network-configs/", name="el_cl_genesis_data"),
StoreSpec(
src="/network-configs/genesis_validators_root.txt",
name="genesis_validators_root",
),
],
wait=None,
)
genesis_validators_root = plan.run_sh(
run="cat /data/data/custom_config_data/genesis_validators_root.txt",
files={"/data": genesis.files_artifacts[0]},
run="cat /data/genesis_validators_root.txt",
files={"/data": genesis.files_artifacts[1]},
wait=None,
)
......
constants = import_module("../package_io/constants.star")
TCP_PROTOCOL = "TCP"
UDP_PROTOCOL = "UDP"
HTTP_APPLICATION_PROTOCOL = "http"
......@@ -71,3 +73,51 @@ def label_maker(client, client_type, image, connected_client, extra_labels):
}
labels.update(extra_labels) # Add extra_labels to the labels dictionary
return labels
def get_devnet_enodes(plan, filename):
enode_list = plan.run_python(
files={constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: filename},
wait=None,
run="""
with open("/network-configs/network-configs/bootnode.txt") as bootnode_file:
bootnodes = []
for line in bootnode_file:
line = line.strip()
bootnodes.append(line)
print(",".join(bootnodes), end="")
""",
)
return enode_list.output
def get_devnet_enrs_list(plan, filename):
enr_list = plan.run_python(
files={constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: filename},
wait=None,
run="""
with open("/network-configs/network-configs/bootstrap_nodes.txt") as bootnode_file:
bootnodes = []
for line in bootnode_file:
line = line.strip()
bootnodes.append(line)
print(",".join(bootnodes), end="")
""",
)
return enr_list.output
# Prysm and Nimbus needs to have the enrs in a list format
# Can't figure out how to pass each item as a list, as I can't return an array from the starlark function
# So for now I'm just returning the last item in the list
def get_devnet_enr(plan, filename):
enr_items = plan.run_python(
files={constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: filename},
wait=None,
run="""
with open("/network-configs/network-configs/bootstrap_nodes.txt") as bootnode_file:
last_enr = bootnode_file.read().splitlines()[-1]
print(last_enr, end="")
""",
)
return enr_items.output
......@@ -51,3 +51,5 @@ CL_GENESIS_GENERATION_CONFIG_TEMPLATE_FILEPATH = (
CL_GENESIS_GENERATION_MNEMONICS_TEMPLATE_FILEPATH = (
CL_GENESIS_GENERATION_CONFIG_DIRPATH + "/mnemonics.yaml.tmpl"
)
JWT_PATH_FILEPATH = STATIC_FILES_DIRPATH + "/jwt/jwtsecret"
......@@ -5,7 +5,7 @@ logging:
# Chain network configuration
chain:
name: "kurtosis-testnet"
configPath: "/data/data/custom_config_data/config.yaml"
configPath: "/network-configs/network-configs/config.yaml"
displayName: "Kurtosis Testnet"
# HTTP Server configuration
......
0xdc49981516e8e72b401a63e6405495a32dafc3939b5d6d83cc319ac0388bca1b
\ No newline at end of file
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