diff --git a/.github/tests/nimbus-eth1-all.yaml b/.github/tests/nimbus-eth1-all.yaml new file mode 100644 index 000000000..fe0ee47dc --- /dev/null +++ b/.github/tests/nimbus-eth1-all.yaml @@ -0,0 +1,14 @@ +participants: + - el_client_type: geth + cl_client_type: teku + - el_client_type: nimbus + cl_client_type: teku + - el_client_type: nimbus + cl_client_type: prysm + - el_client_type: nimbus + cl_client_type: nimbus + - el_client_type: nimbus + cl_client_type: lighthouse + - el_client_type: nimbus + cl_client_type: lodestar +additional_services: [] diff --git a/README.md b/README.md index 199a7803d..c04ac0db4 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ To configure the package behaviour, you can modify your `network_params.yaml` fi # Specification of the participants in the network participants: # The type of EL client that should be started - # Valid values are geth, nethermind, erigon, besu, ethereumjs, reth + # Valid values are geth, nethermind, erigon, besu, ethereumjs, reth, nimbus-eth1 - el_client_type: geth # The Docker image that should be used for the EL client; leave blank to use the default for the client type @@ -164,6 +164,7 @@ participants: # - besu: hyperledger/besu:develop # - reth: ghcr.io/paradigmxyz/reth # - ethereumjs: ethpandaops/ethereumjs:master + # - nimbus-eth1: ethpandaops/nimbus-eth1:master el_client_image: "" # The log level string that this participant's EL client should log at diff --git a/src/el/nimbus-eth1/nimbus_launcher.star b/src/el/nimbus-eth1/nimbus_launcher.star new file mode 100644 index 000000000..bed447c00 --- /dev/null +++ b/src/el/nimbus-eth1/nimbus_launcher.star @@ -0,0 +1,270 @@ +shared_utils = import_module("../../shared_utils/shared_utils.star") +input_parser = import_module("../../package_io/input_parser.star") +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") + +WS_RPC_PORT_NUM = 8545 +DISCOVERY_PORT_NUM = 30303 +ENGINE_RPC_PORT_NUM = 8551 +METRICS_PORT_NUM = 9001 + +# The min/max CPU/memory that the execution node can use +EXECUTION_MIN_CPU = 100 +EXECUTION_MIN_MEMORY = 256 + +# Port IDs +WS_RPC_PORT_ID = "ws-rpc" +TCP_DISCOVERY_PORT_ID = "tcp-discovery" +UDP_DISCOVERY_PORT_ID = "udp-discovery" +ENGINE_RPC_PORT_ID = "engine-rpc" +METRICS_PORT_ID = "metrics" + +# Paths +METRICS_PATH = "/metrics" + +# The dirpath of the execution data directory on the client container +EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/data/nimbus/execution-data" + +PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER" + +USED_PORTS = { + WS_RPC_PORT_ID: shared_utils.new_port_spec( + WS_RPC_PORT_NUM, + shared_utils.TCP_PROTOCOL, + ), + TCP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + DISCOVERY_PORT_NUM, + shared_utils.TCP_PROTOCOL, + ), + UDP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + DISCOVERY_PORT_NUM, + shared_utils.UDP_PROTOCOL, + ), + ENGINE_RPC_PORT_ID: shared_utils.new_port_spec( + ENGINE_RPC_PORT_NUM, + shared_utils.TCP_PROTOCOL, + ), + METRICS_PORT_ID: shared_utils.new_port_spec( + METRICS_PORT_NUM, + shared_utils.TCP_PROTOCOL, + ), +} + +VERBOSITY_LEVELS = { + constants.GLOBAL_CLIENT_LOG_LEVEL.error: "ERROR", + constants.GLOBAL_CLIENT_LOG_LEVEL.warn: "WARN", + constants.GLOBAL_CLIENT_LOG_LEVEL.info: "INFO", + constants.GLOBAL_CLIENT_LOG_LEVEL.debug: "DEBUG", + constants.GLOBAL_CLIENT_LOG_LEVEL.trace: "TRACE", +} + + +def launch( + plan, + launcher, + service_name, + image, + participant_log_level, + global_log_level, + # If empty then the node will be launched as a bootnode + existing_el_clients, + el_min_cpu, + el_max_cpu, + el_min_mem, + el_max_mem, + extra_params, + extra_env_vars, + extra_labels, + persistent, + el_volume_size, + tolerations, + node_selectors, +): + log_level = input_parser.get_client_log_level_or_default( + participant_log_level, global_log_level, VERBOSITY_LEVELS + ) + + network_name = shared_utils.get_network_name(launcher.network) + + el_min_cpu = int(el_min_cpu) if int(el_min_cpu) > 0 else EXECUTION_MIN_CPU + el_max_cpu = ( + int(el_max_cpu) + if int(el_max_cpu) > 0 + else constants.RAM_CPU_OVERRIDES[network_name]["nimbus_eth1_max_cpu"] + ) + el_min_mem = int(el_min_mem) if int(el_min_mem) > 0 else EXECUTION_MIN_MEMORY + el_max_mem = ( + int(el_max_mem) + if int(el_max_mem) > 0 + else constants.RAM_CPU_OVERRIDES[network_name]["nimbus_eth1_max_mem"] + ) + + el_volume_size = ( + el_volume_size + if int(el_volume_size) > 0 + else constants.VOLUME_SIZE[network_name]["nimbus_eth1_volume_size"] + ) + + 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, + cl_client_name, + log_level, + el_min_cpu, + el_max_cpu, + el_min_mem, + el_max_mem, + extra_params, + extra_env_vars, + extra_labels, + persistent, + el_volume_size, + tolerations, + node_selectors, + ) + + service = plan.add_service(service_name, config) + + enode = el_admin_node_info.get_enode_for_node(plan, service_name, WS_RPC_PORT_ID) + + metric_url = "{0}:{1}".format(service.ip_address, METRICS_PORT_NUM) + nimbus_metrics_info = node_metrics.new_node_metrics_info( + service_name, METRICS_PATH, metric_url + ) + + return el_client_context.new_el_client_context( + "nimbus", + "", # nimbus has no enr + enode, + service.ip_address, + WS_RPC_PORT_NUM, + WS_RPC_PORT_NUM, + ENGINE_RPC_PORT_NUM, + service_name, + [nimbus_metrics_info], + ) + + +def get_config( + plan, + el_cl_genesis_data, + jwt_file, + network, + image, + service_name, + existing_el_clients, + cl_client_name, + verbosity_level, + el_min_cpu, + el_max_cpu, + el_min_mem, + el_max_mem, + extra_params, + extra_env_vars, + extra_labels, + persistent, + el_volume_size, + tolerations, + node_selectors, +): + cmd = [ + "--log-level={0}".format(verbosity_level), + "--data-dir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER, + "--http-port={0}".format(WS_RPC_PORT_NUM), + "--http-address=0.0.0.0", + "--rpc", + "--rpc-api=eth,debug,exp", + "--ws", + "--ws-api=eth,debug,exp", + "--engine-api", + "--engine-api-address=0.0.0.0", + "--engine-api-port={0}".format(ENGINE_RPC_PORT_NUM), + "--jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER, + "--metrics", + "--metrics-address=0.0.0.0", + "--metrics-port={0}".format(METRICS_PORT_NUM), + ] + if ( + network not in constants.PUBLIC_NETWORKS + or constants.NETWORK_NAME.shadowfork in network + ): + cmd.append( + "--custom-network=" + + constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER + + "/genesis.json" + ) + else: + cmd.append("--network=" + network) + + if network == constants.NETWORK_NAME.kurtosis: + if len(existing_el_clients) > 0: + cmd.append( + "--bootstrap-node=" + + ",".join( + [ + ctx.enode + for ctx in existing_el_clients[: constants.MAX_ENODE_ENTRIES] + ] + ) + ) + elif network not in constants.PUBLIC_NETWORKS: + cmd.append( + "--bootstrap-node=" + + shared_utils.get_devnet_enodes( + plan, el_cl_genesis_data.files_artifact_uuid + ) + ) + + if len(extra_params) > 0: + # this is a repeated, 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.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file, + } + + if persistent: + files[EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER] = Directory( + persistent_key="data-{0}".format(service_name), + size=el_volume_size, + ) + + return ServiceConfig( + image=image, + ports=USED_PORTS, + cmd=cmd, + files=files, + private_ip_address_placeholder=PRIVATE_IP_ADDRESS_PLACEHOLDER, + min_cpu=el_min_cpu, + max_cpu=el_max_cpu, + min_memory=el_min_mem, + max_memory=el_max_mem, + env_vars=extra_env_vars, + labels=shared_utils.label_maker( + constants.EL_CLIENT_TYPE.nimbus, + constants.CLIENT_TYPES.el, + image, + cl_client_name, + extra_labels, + ), + tolerations=tolerations, + node_selectors=node_selectors, + ) + + +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, + ) diff --git a/src/package_io/constants.star b/src/package_io/constants.star index 9d277958f..4c54c7501 100644 --- a/src/package_io/constants.star +++ b/src/package_io/constants.star @@ -6,6 +6,7 @@ EL_CLIENT_TYPE = struct( besu="besu", reth="reth", ethereumjs="ethereumjs", + nimbus="nimbus", ) CL_CLIENT_TYPE = struct( @@ -131,6 +132,7 @@ VOLUME_SIZE = { "besu_volume_size": 1000000, # 1TB "reth_volume_size": 3000000, # 3TB "ethereumjs_volume_size": 1000000, # 1TB + "nimbus_eth1_volume_size": 1000000, # 1TB "prysm_volume_size": 500000, # 500GB "lighthouse_volume_size": 500000, # 500GB "teku_volume_size": 500000, # 500GB @@ -144,6 +146,7 @@ VOLUME_SIZE = { "besu_volume_size": 800000, # 800GB "reth_volume_size": 1200000, # 1200GB "ethereumjs_volume_size": 800000, # 800GB + "nimbus_eth1_volume_size": 800000, # 800GB "prysm_volume_size": 300000, # 300GB "lighthouse_volume_size": 300000, # 300GB "teku_volume_size": 300000, # 300GB @@ -157,6 +160,7 @@ VOLUME_SIZE = { "besu_volume_size": 300000, # 300GB "reth_volume_size": 500000, # 500GB "ethereumjs_volume_size": 300000, # 300GB + "nimbus_eth1_volume_size": 300000, # 300GB "prysm_volume_size": 150000, # 150GB "lighthouse_volume_size": 150000, # 150GB "teku_volume_size": 150000, # 150GB @@ -170,6 +174,7 @@ VOLUME_SIZE = { "besu_volume_size": 100000, # 100GB "reth_volume_size": 200000, # 200GB "ethereumjs_volume_size": 100000, # 100GB + "nimbus_eth1_volume_size": 100000, # 100GB "prysm_volume_size": 100000, # 100GB "lighthouse_volume_size": 100000, # 100GB "teku_volume_size": 100000, # 100GB @@ -183,6 +188,7 @@ VOLUME_SIZE = { "besu_volume_size": 100000, # 100GB "reth_volume_size": 200000, # 200GB "ethereumjs_volume_size": 100000, # 100GB + "nimbus_eth1_volume_size": 100000, # 100GB "prysm_volume_size": 100000, # 100GB "lighthouse_volume_size": 100000, # 100GB "teku_volume_size": 100000, # 100GB @@ -196,6 +202,7 @@ VOLUME_SIZE = { "besu_volume_size": 3000, # 3GB "reth_volume_size": 3000, # 3GB "ethereumjs_volume_size": 3000, # 3GB + "nimbus_eth1_volume_size": 3000, # 3GB "prysm_volume_size": 1000, # 1GB "lighthouse_volume_size": 1000, # 1GB "teku_volume_size": 1000, # 1GB @@ -209,6 +216,7 @@ VOLUME_SIZE = { "besu_volume_size": 3000, # 3GB "reth_volume_size": 3000, # 3GB "ethereumjs_volume_size": 3000, # 3GB + "nimbus_eth1_volume_size": 3000, # 3GB "prysm_volume_size": 1000, # 1GB "lighthouse_volume_size": 1000, # 1GB "teku_volume_size": 1000, # 1GB @@ -231,6 +239,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 4000, # 4 cores "ethereumjs_max_mem": 16384, # 16GB "ethereumjs_max_cpu": 4000, # 4 cores + "nimbus_eth1_max_mem": 16384, # 16GB + "nimbus_eth1_max_cpu": 4000, # 4 cores "prysm_max_mem": 16384, # 16GB "prysm_max_cpu": 4000, # 4 cores "lighthouse_max_mem": 16384, # 16GB @@ -255,6 +265,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 2000, # 2 cores "ethereumjs_max_mem": 8192, # 8GB "ethereumjs_max_cpu": 2000, # 2 cores + "nimbus_eth1_max_mem": 8192, # 8GB + "nimbus_eth1_max_cpu": 2000, # 2 cores "prysm_max_mem": 8192, # 8GB "prysm_max_cpu": 2000, # 2 cores "lighthouse_max_mem": 8192, # 8GB @@ -279,6 +291,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 1000, # 1 core "ethereumjs_max_mem": 4096, # 4GB "ethereumjs_max_cpu": 1000, # 1 core + "nimbus_eth1_max_mem": 4096, # 4GB + "nimbus_eth1_max_cpu": 1000, # 1 core "prysm_max_mem": 4096, # 4GB "prysm_max_cpu": 1000, # 1 core "lighthouse_max_mem": 4096, # 4GB @@ -303,6 +317,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 2000, # 2 cores "ethereumjs_max_mem": 8192, # 8GB "ethereumjs_max_cpu": 2000, # 2 cores + "nimbus_eth1_max_mem": 8192, # 8GB + "nimbus_eth1_max_cpu": 2000, # 2 cores "prysm_max_mem": 8192, # 8GB "prysm_max_cpu": 2000, # 2 cores "lighthouse_max_mem": 8192, # 8GB @@ -327,6 +343,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 1000, # 1 core "ethereumjs_max_mem": 4096, # 4GB "ethereumjs_max_cpu": 1000, # 1 core + "nimbus_eth1_max_mem": 4096, # 4GB + "nimbus_eth1_max_cpu": 1000, # 1 core "prysm_max_mem": 4096, # 4GB "prysm_max_cpu": 1000, # 1 core "lighthouse_max_mem": 4096, # 4GB @@ -351,6 +369,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 1000, # 1 core "ethereumjs_max_mem": 1024, # 1GB "ethereumjs_max_cpu": 1000, # 1 core + "nimbus_eth1_max_mem": 1024, # 1GB + "nimbus_eth1_max_cpu": 1000, # 1 core "prysm_max_mem": 1024, # 1GB "prysm_max_cpu": 1000, # 1 core "lighthouse_max_mem": 1024, # 1GB @@ -375,6 +395,8 @@ RAM_CPU_OVERRIDES = { "reth_max_cpu": 1000, # 1 core "ethereumjs_max_mem": 1024, # 1GB "ethereumjs_max_cpu": 1000, # 1 core + "nimbus_eth1_max_mem": 1024, # 1GB + "nimbus_eth1_max_cpu": 1000, # 1 core "prysm_max_mem": 1024, # 1GB "prysm_max_cpu": 1000, # 1 core "lighthouse_max_mem": 1024, # 1GB diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 5b4a9bb5a..0121de343 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -11,6 +11,7 @@ DEFAULT_EL_IMAGES = { "besu": "hyperledger/besu:latest", "reth": "ghcr.io/paradigmxyz/reth", "ethereumjs": "ethpandaops/ethereumjs:master", + "nimbus": "ethpandaops/nimbus-eth1:master", } DEFAULT_CL_IMAGES = { diff --git a/src/participant_network.star b/src/participant_network.star index aeea46048..9d93ea061 100644 --- a/src/participant_network.star +++ b/src/participant_network.star @@ -21,6 +21,7 @@ erigon = import_module("./el/erigon/erigon_launcher.star") nethermind = import_module("./el/nethermind/nethermind_launcher.star") reth = import_module("./el/reth/reth_launcher.star") ethereumjs = import_module("./el/ethereumjs/ethereumjs_launcher.star") +nimbus_eth1 = import_module("./el/nimbus-eth1/nimbus_launcher.star") lighthouse = import_module("./cl/lighthouse/lighthouse_launcher.star") lodestar = import_module("./cl/lodestar/lodestar_launcher.star") @@ -419,6 +420,14 @@ def launch_participant_network( ), "launch_method": ethereumjs.launch, }, + constants.EL_CLIENT_TYPE.nimbus: { + "launcher": nimbus_eth1.new_nimbus_launcher( + el_cl_data, + jwt_file, + network_params.network, + ), + "launch_method": nimbus_eth1.launch, + }, } all_el_client_contexts = []