From ec641dbc454f9c4f7ae6e7f7906730bbcc02e46f Mon Sep 17 00:00:00 2001 From: Barnabas Busa Date: Fri, 7 Jun 2024 13:38:25 +0200 Subject: [PATCH] feat: add op-reth --- network_params.yaml | 2 + src/cl/cl_launcher.star | 72 --------- src/cl/op-node/op_node_launcher.star | 24 ++- src/el/el_launcher.star | 65 -------- src/el/op-geth/op_geth_launcher.star | 52 ++----- src/el/op-reth/op_reth_launcher.star | 216 +++++++++++++++++++++++++++ src/el_cl_launcher.star | 120 +++++++++++++++ src/package_io/input_parser.star | 1 + src/participant_network.star | 18 +-- 9 files changed, 368 insertions(+), 202 deletions(-) delete mode 100644 src/cl/cl_launcher.star delete mode 100644 src/el/el_launcher.star create mode 100644 src/el/op-reth/op_reth_launcher.star create mode 100644 src/el_cl_launcher.star diff --git a/network_params.yaml b/network_params.yaml index d75d524..4e69e27 100644 --- a/network_params.yaml +++ b/network_params.yaml @@ -2,6 +2,8 @@ optimism_package: participants: - el_type: op-geth cl_type: op-node + count: 3 + - el_type: op-reth ethereum_package: participants: - el_type: geth diff --git a/src/cl/cl_launcher.star b/src/cl/cl_launcher.star deleted file mode 100644 index d874af2..0000000 --- a/src/cl/cl_launcher.star +++ /dev/null @@ -1,72 +0,0 @@ -constants = import_module( - "github.com/kurtosis-tech/ethereum-package/src/package_io/constants.star" -) -shared_utils = import_module( - "github.com/kurtosis-tech/ethereum-package/src/shared_utils/shared_utils.star" -) - -op_node = import_module("./op-node/op_node_launcher.star") - - -def launch( - plan, - jwt_file, - network_params, - el_cl_data, - participants, - num_participants, - all_el_contexts, - l1_config_env_vars, - gs_sequencer_private_key, -): - plan.print("Launching CL network") - - cl_launchers = { - "op-node": { - "launcher": op_node.new_op_node_launcher( - el_cl_data, jwt_file, network_params - ), - "launch_method": op_node.launch, - }, - } - all_cl_contexts = [] - - for index, participant in enumerate(participants): - cl_type = participant.cl_type - el_type = participant.el_type - - if cl_type not in cl_launchers: - fail( - "Unsupported launcher '{0}', need one of '{1}'".format( - cl_type, ",".join(cl_launchers.keys()) - ) - ) - - cl_launcher, launch_method = ( - cl_launchers[cl_type]["launcher"], - cl_launchers[cl_type]["launch_method"], - ) - - index_str = shared_utils.zfill_custom(index + 1, len(str(len(participants)))) - - cl_service_name = "op-cl-{0}-{1}-{2}".format(index_str, cl_type, el_type) - - el_context = all_el_contexts[index] - - cl_context = None - - full_name = "{0}-{1}-{2}".format(index_str, el_type, cl_type) - - cl_context = launch_method( - plan, - cl_launcher, - cl_service_name, - participant.cl_image, - el_context, - all_cl_contexts, - l1_config_env_vars, - gs_sequencer_private_key, - ) - - all_cl_contexts.append(cl_context) - return all_cl_contexts diff --git a/src/cl/op-node/op_node_launcher.star b/src/cl/op-node/op_node_launcher.star index 30d0c35..fef01ea 100644 --- a/src/cl/op-node/op_node_launcher.star +++ b/src/cl/op-node/op_node_launcher.star @@ -65,6 +65,7 @@ def launch( existing_cl_clients, l1_config_env_vars, gs_sequencer_private_key, + sequencer_enabled, ): network_name = shared_utils.get_network_name(launcher.network) @@ -91,6 +92,7 @@ def launch( l1_config_env_vars, gs_sequencer_private_key, beacon_node_identity_recipe, + sequencer_enabled, ) beacon_service = plan.add_service(service_name, config) @@ -132,6 +134,7 @@ def get_beacon_config( l1_config_env_vars, gs_sequencer_private_key, beacon_node_identity_recipe, + sequencer_enabled, ): EXECUTION_ENGINE_ENDPOINT = "http://{0}:{1}".format( el_context.ip_addr, @@ -148,27 +151,27 @@ def get_beacon_config( "--rpc.addr=0.0.0.0", "--rpc.port={0}".format(BEACON_HTTP_PORT_NUM), "--rpc.enable-admin", - "--p2p.sequencer.key=" + gs_sequencer_private_key, "--l1={0}".format(l1_config_env_vars["L1_RPC_URL"]), "--l1.rpckind={0}".format(l1_config_env_vars["L1_RPC_KIND"]), "--l1.beacon={0}".format(l1_config_env_vars["CL_RPC_URL"]), + "--p2p.advertise.ip=" + constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, + "--p2p.advertise.tcp={0}".format(BEACON_DISCOVERY_PORT_NUM), + "--p2p.advertise.udp={0}".format(BEACON_DISCOVERY_PORT_NUM), "--p2p.listen.ip=0.0.0.0", "--p2p.listen.tcp={0}".format(BEACON_DISCOVERY_PORT_NUM), "--p2p.listen.udp={0}".format(BEACON_DISCOVERY_PORT_NUM), ] - if len(existing_cl_clients) == 0: + if sequencer_enabled: + cmd.append("--p2p.sequencer.key=" + gs_sequencer_private_key) cmd.append("--sequencer.enabled") cmd.append("--sequencer.l1-confs=5") if len(existing_cl_clients) > 0: cmd.append( - "--p2p.static=" + "--p2p.bootnodes=" + ",".join( - [ - ctx.beacon_multiaddr - for ctx in existing_cl_clients[: constants.MAX_ENR_ENTRIES] - ] + [ctx.enr for ctx in existing_cl_clients[: constants.MAX_ENR_ENTRIES]] ) ) @@ -195,9 +198,4 @@ def get_beacon_config( ) -def new_op_node_launcher(el_cl_genesis_data, jwt_file, network_params): - return struct( - el_cl_genesis_data=el_cl_genesis_data, - jwt_file=jwt_file, - network=network_params.network, - ) +de diff --git a/src/el/el_launcher.star b/src/el/el_launcher.star deleted file mode 100644 index 7d00804..0000000 --- a/src/el/el_launcher.star +++ /dev/null @@ -1,65 +0,0 @@ -constants = import_module( - "github.com/kurtosis-tech/ethereum-package/src/package_io/constants.star" -) -shared_utils = import_module( - "github.com/kurtosis-tech/ethereum-package/src/shared_utils/shared_utils.star" -) - -op_geth = import_module("./op-geth/op_geth_launcher.star") - - -def launch( - plan, - jwt_file, - network_params, - el_cl_data, - participants, - num_participants, -): - el_launchers = { - "op-geth": { - "launcher": op_geth.new_op_geth_launcher( - el_cl_data, - jwt_file, - network_params.network, - network_params.network_id, - ), - "launch_method": op_geth.launch, - }, - } - - all_el_contexts = [] - - for index, participant in enumerate(participants): - cl_type = participant.cl_type - el_type = participant.el_type - - if el_type not in el_launchers: - fail( - "Unsupported launcher '{0}', need one of '{1}'".format( - el_type, ",".join(el_launchers.keys()) - ) - ) - - el_launcher, launch_method = ( - el_launchers[el_type]["launcher"], - el_launchers[el_type]["launch_method"], - ) - - # Zero-pad the index using the calculated zfill value - index_str = shared_utils.zfill_custom(index + 1, len(str(len(participants)))) - - el_service_name = "op-el-{0}-{1}-{2}".format(index_str, el_type, cl_type) - - el_context = launch_method( - plan, - el_launcher, - el_service_name, - participant.el_image, - all_el_contexts, - ) - - all_el_contexts.append(el_context) - - plan.print("Successfully added {0} EL participants".format(num_participants)) - return all_el_contexts diff --git a/src/el/op-geth/op_geth_launcher.star b/src/el/op-geth/op_geth_launcher.star index 4c0fdde..7619363 100644 --- a/src/el/op-geth/op_geth_launcher.star +++ b/src/el/op-geth/op_geth_launcher.star @@ -89,34 +89,11 @@ def launch( service_name, image, existing_el_clients, + sequencer_enabled, + sequencer_context, ): - # 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]["geth_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]["geth_max_mem"] - # ) - - # el_volume_size = ( - # el_volume_size - # if int(el_volume_size) > 0 - # else constants.VOLUME_SIZE[network_name]["geth_volume_size"] - # ) - - # cl_client_name = service_name.split("-")[3] - config = get_config( plan, launcher.el_cl_genesis_data, @@ -126,6 +103,8 @@ def launch( image, service_name, existing_el_clients, + sequencer_enabled, + sequencer_context, ) service = plan.add_service(service_name, config) @@ -164,6 +143,8 @@ def get_config( image, service_name, existing_el_clients, + sequencer_enabled, + sequencer_context, ): init_datadir_cmd_str = "geth init --datadir={0} {1}".format( EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER, @@ -175,8 +156,6 @@ def get_config( cmd = [ "geth", - # Disable path based storage scheme for electra fork and verkle - # TODO: REMOVE Once geth default db is path based, and builder rebased "--networkid={0}".format(network_id), # "--verbosity=" + verbosity_level, "--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER, @@ -185,8 +164,6 @@ def get_config( "--http.addr=0.0.0.0", "--http.vhosts=*", "--http.corsdomain=*", - # WARNING: The admin info endpoint is enabled so that we can easily get ENR/enode, which means - # that users should NOT store private information in these Kurtosis nodes! "--http.api=admin,engine,net,eth,web3,debug", "--ws", "--ws.addr=0.0.0.0", @@ -199,6 +176,7 @@ def get_config( "--authrpc.vhosts=*", "--authrpc.jwtsecret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER, "--syncmode=full", + "--nat=extip:" + constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, "--rpc.allow-unprotected-txs", "--metrics", "--metrics.addr=0.0.0.0", @@ -208,6 +186,11 @@ def get_config( "--rollup.disabletxpoolgossip=true", ] + if not sequencer_enabled: + cmd.append( + "--rollup.sequencerhttp={0}".format(sequencer_context.beacon_http_url) + ) + if len(existing_el_clients) > 0: cmd.append( "--bootnodes=" @@ -219,10 +202,6 @@ def get_config( ) ) - # if len(extra_params) > 0: - # # this is a repeated, we convert it into Starlark - # cmd.extend([param for param in extra_params]) - cmd_str = " ".join(cmd) if network not in constants.PUBLIC_NETWORKS: subcommand_strs = [ @@ -237,17 +216,14 @@ def get_config( constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data, 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=[command_str], files=files, entrypoint=ENTRYPOINT_ARGS, + private_ip_address_placeholder=constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, ) diff --git a/src/el/op-reth/op_reth_launcher.star b/src/el/op-reth/op_reth_launcher.star new file mode 100644 index 0000000..fff627e --- /dev/null +++ b/src/el/op-reth/op_reth_launcher.star @@ -0,0 +1,216 @@ +shared_utils = import_module( + "github.com/kurtosis-tech/ethereum-package/src/shared_utils/shared_utils.star" +) +# input_parser = import_module("../../package_io/input_parser.star") +el_context = import_module( + "github.com/kurtosis-tech/ethereum-package/src/el/el_context.star" +) +el_admin_node_info = import_module( + "github.com/kurtosis-tech/ethereum-package/src/el/el_admin_node_info.star" +) + +node_metrics = import_module( + "github.com/kurtosis-tech/ethereum-package/src/node_metrics_info.star" +) +constants = import_module( + "github.com/kurtosis-tech/ethereum-package/src/package_io/constants.star" +) + +RPC_PORT_NUM = 8545 +WS_PORT_NUM = 8546 +DISCOVERY_PORT_NUM = 30303 +ENGINE_RPC_PORT_NUM = 9551 +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 +RPC_PORT_ID = "rpc" +WS_PORT_ID = "ws" +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/op-reth/execution-data" + + +def get_used_ports(discovery_port=DISCOVERY_PORT_NUM): + used_ports = { + RPC_PORT_ID: shared_utils.new_port_spec( + RPC_PORT_NUM, + shared_utils.TCP_PROTOCOL, + shared_utils.HTTP_APPLICATION_PROTOCOL, + ), + WS_PORT_ID: shared_utils.new_port_spec(WS_PORT_NUM, shared_utils.TCP_PROTOCOL), + TCP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + discovery_port, shared_utils.TCP_PROTOCOL + ), + UDP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + discovery_port, 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 + ), + } + return used_ports + + +VERBOSITY_LEVELS = { + constants.GLOBAL_LOG_LEVEL.error: "v", + constants.GLOBAL_LOG_LEVEL.warn: "vv", + constants.GLOBAL_LOG_LEVEL.info: "vvv", + constants.GLOBAL_LOG_LEVEL.debug: "vvvv", + constants.GLOBAL_LOG_LEVEL.trace: "vvvvv", +} + + +def launch( + plan, + launcher, + service_name, + image, + existing_el_clients, + sequencer_enabled, + sequencer_context, +): + network_name = shared_utils.get_network_name(launcher.network) + + cl_client_name = service_name.split("-")[3] + + config = get_config( + plan, + launcher.el_cl_genesis_data, + launcher.jwt_file, + launcher.network, + launcher.network_id, + image, + service_name, + existing_el_clients, + sequencer_enabled, + sequencer_context, + ) + + service = plan.add_service(service_name, config) + + enode = el_admin_node_info.get_enode_for_node(plan, service_name, RPC_PORT_ID) + + metric_url = "{0}:{1}".format(service.ip_address, METRICS_PORT_NUM) + op_reth_metrics_info = node_metrics.new_node_metrics_info( + service_name, METRICS_PATH, metric_url + ) + + http_url = "http://{0}:{1}".format(service.ip_address, RPC_PORT_NUM) + + return el_context.new_el_context( + "reth", + "", # reth has no enr + enode, + service.ip_address, + RPC_PORT_NUM, + WS_PORT_NUM, + ENGINE_RPC_PORT_NUM, + http_url, + service_name, + [op_reth_metrics_info], + ) + + +def get_config( + plan, + el_cl_genesis_data, + jwt_file, + network, + network_id, + image, + service_name, + existing_el_clients, + sequencer_enabled, + sequencer_context, +): + public_ports = {} + discovery_port = DISCOVERY_PORT_NUM + used_ports = get_used_ports(discovery_port) + + cmd = [ + "node", + "--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER, + "--chain={0}".format( + network + if network in constants.PUBLIC_NETWORKS + else constants.GENESIS_CONFIG_MOUNT_PATH_ON_CONTAINER + "/genesis.json" + ), + "--http", + "--http.port={0}".format(RPC_PORT_NUM), + "--http.addr=0.0.0.0", + "--http.corsdomain=*", + # WARNING: The admin info endpoint is enabled so that we can easily get ENR/enode, which means + # that users should NOT store private information in these Kurtosis nodes! + "--http.api=admin,net,eth,web3,debug,trace", + "--ws", + "--ws.addr=0.0.0.0", + "--ws.port={0}".format(WS_PORT_NUM), + "--ws.api=net,eth", + "--ws.origins=*", + "--nat=extip:" + constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, + "--authrpc.port={0}".format(ENGINE_RPC_PORT_NUM), + "--authrpc.jwtsecret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER, + "--authrpc.addr=0.0.0.0", + "--metrics=0.0.0.0:{0}".format(METRICS_PORT_NUM), + "--discovery.port={0}".format(discovery_port), + "--port={0}".format(discovery_port), + "--rollup.disabletxpoolgossip=true", + ] + + if not sequencer_enabled: + cmd.append( + "--rollup.sequencer-http={0}".format(sequencer_context.beacon_http_url) + ) + + if len(existing_el_clients) > 0: + cmd.append( + "--bootnodes=" + + ",".join( + [ + ctx.enode + for ctx in existing_el_clients[: constants.MAX_ENODE_ENTRIES] + ] + ) + ) + + files = { + constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data, + constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file, + } + + return ServiceConfig( + image=image, + ports=used_ports, + public_ports=public_ports, + cmd=cmd, + files=files, + private_ip_address_placeholder=constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, + ) + + +def new_op_reth_launcher( + el_cl_genesis_data, + jwt_file, + network, + network_id, +): + return struct( + el_cl_genesis_data=el_cl_genesis_data, + jwt_file=jwt_file, + network=network, + network_id=network_id, + ) diff --git a/src/el_cl_launcher.star b/src/el_cl_launcher.star new file mode 100644 index 0000000..ac2412e --- /dev/null +++ b/src/el_cl_launcher.star @@ -0,0 +1,120 @@ +constants = import_module( + "github.com/kurtosis-tech/ethereum-package/src/package_io/constants.star" +) +shared_utils = import_module( + "github.com/kurtosis-tech/ethereum-package/src/shared_utils/shared_utils.star" +) +# EL +op_geth = import_module("./el/op-geth/op_geth_launcher.star") +op_reth = import_module("./el/op-reth/op_reth_launcher.star") +# CL +op_node = import_module("./cl/op-node/op_node_launcher.star") + + +def launch( + plan, + jwt_file, + network_params, + el_cl_data, + participants, + num_participants, + l1_config_env_vars, + gs_sequencer_private_key, +): + el_launchers = { + "op-geth": { + "launcher": op_geth.new_op_geth_launcher( + el_cl_data, + jwt_file, + network_params.network, + network_params.network_id, + ), + "launch_method": op_geth.launch, + }, + "op-reth": { + "launcher": op_reth.new_op_reth_launcher( + el_cl_data, + jwt_file, + network_params.network, + network_params.network_id, + ), + "launch_method": op_reth.launch, + }, + } + + cl_launchers = { + "op-node": { + "launcher": op_node.new_op_node_launcher( + el_cl_data, jwt_file, network_params + ), + "launch_method": op_node.launch, + }, + } + + all_cl_contexts = [] + all_el_contexts = [] + sequencer_enabled = True + for index, participant in enumerate(participants): + cl_type = participant.cl_type + el_type = participant.el_type + + if el_type not in el_launchers: + fail( + "Unsupported launcher '{0}', need one of '{1}'".format( + el_type, ",".join(el_launchers.keys()) + ) + ) + if cl_type not in cl_launchers: + fail( + "Unsupported launcher '{0}', need one of '{1}'".format( + cl_type, ",".join(cl_launchers.keys()) + ) + ) + + el_launcher, el_launch_method = ( + el_launchers[el_type]["launcher"], + el_launchers[el_type]["launch_method"], + ) + + cl_launcher, cl_launch_method = ( + cl_launchers[cl_type]["launcher"], + cl_launchers[cl_type]["launch_method"], + ) + + # Zero-pad the index using the calculated zfill value + index_str = shared_utils.zfill_custom(index + 1, len(str(len(participants)))) + + el_service_name = "op-el-{0}-{1}-{2}".format(index_str, el_type, cl_type) + cl_service_name = "op-cl-{0}-{1}-{2}".format(index_str, cl_type, el_type) + + el_context = el_launch_method( + plan, + el_launcher, + el_service_name, + participant.el_image, + all_el_contexts, + sequencer_enabled, + all_cl_contexts[0] + if len(all_cl_contexts) > 0 + else None, # sequencer context + ) + + cl_context = cl_launch_method( + plan, + cl_launcher, + cl_service_name, + participant.cl_image, + el_context, + all_cl_contexts, + l1_config_env_vars, + gs_sequencer_private_key, + sequencer_enabled, + ) + + sequencer_enabled = False + + all_el_contexts.append(el_context) + all_cl_contexts.append(cl_context) + + plan.print("Successfully added {0} EL/CL participants".format(num_participants)) + return all_el_contexts, all_cl_contexts diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index c535bab..fe9958b 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -4,6 +4,7 @@ ethereum_package_input_parser = import_module( DEFAULT_EL_IMAGES = { "op-geth": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth:latest", + "op-reth": "parithoshj/op-reth:v0.2.0-beta.9", } DEFAULT_CL_IMAGES = { diff --git a/src/participant_network.star b/src/participant_network.star index 0b47007..3f4d1a3 100644 --- a/src/participant_network.star +++ b/src/participant_network.star @@ -1,5 +1,4 @@ -el_client_launcher = import_module("./el/el_launcher.star") -cl_client_launcher = import_module("./cl/cl_launcher.star") +el_cl_client_launcher = import_module("./el_cl_launcher.star") participant_module = import_module("./participant.star") input_parser = import_module("./package_io/input_parser.star") op_batcher_launcher = import_module("./batcher/op-batcher/op_batcher_launcher.star") @@ -17,24 +16,15 @@ def launch_participant_network( l2oo_address, ): num_participants = len(participants) - # Launch all execution layer clients - all_el_contexts = el_client_launcher.launch( + sequencer_enabled = True + # First EL and sequencer CL + all_el_contexts, all_cl_contexts = el_cl_client_launcher.launch( plan, jwt_file, network_params, el_cl_data, participants, num_participants, - ) - - all_cl_contexts = cl_client_launcher.launch( - plan, - jwt_file, - network_params, - el_cl_data, - participants, - num_participants, - all_el_contexts, l1_config_env_vars, gs_private_keys["GS_SEQUENCER_PRIVATE_KEY"], )