diff --git a/devnet/Justfile b/devnet/Justfile index 3f6ba364..bf78f0a0 100644 --- a/devnet/Justfile +++ b/devnet/Justfile @@ -6,12 +6,12 @@ default: # Spawns the devnet devnet-up: build - # ./clean.sh kurtosis run . --args-file network_params.yaml --enclave world-chain # Stops the devnet **This will prune all docker containers** devnet-down: - ./clean.sh + kurtosis enclave rm -f world-chain + kurtosis clean # Builds and tags the world-chain-builder image build: diff --git a/devnet/network_params.yaml b/devnet/network_params.yaml index 1e895c33..3275c6f2 100644 --- a/devnet/network_params.yaml +++ b/devnet/network_params.yaml @@ -1,6 +1,4 @@ optimism_package: - participants: - - el_type: world-chain # - el_type: op-geth-builder # - el_type: op-geth # - el_type: op-reth diff --git a/devnet/src/cl/op-node-builder/op_node_builder_launcher.star b/devnet/src/cl/op-node-builder/op_node_builder_launcher.star new file mode 100644 index 00000000..9fc48228 --- /dev/null +++ b/devnet/src/cl/op-node-builder/op_node_builder_launcher.star @@ -0,0 +1,198 @@ +shared_utils = import_module( + "github.com/ethpandaops/ethereum-package/src/shared_utils/shared_utils.star" +) + +cl_context = import_module( + "github.com/ethpandaops/ethereum-package/src/cl/cl_context.star" +) + +cl_node_ready_conditions = import_module( + "github.com/ethpandaops/ethereum-package/src/cl/cl_node_ready_conditions.star" +) +constants = import_module( + "github.com/ethpandaops/ethereum-package/src/package_io/constants.star" +) + +# ---------------------------------- Beacon client ------------------------------------- + +# The Docker container runs as the "op-node" user so we can't write to root +BEACON_DATA_DIRPATH_ON_SERVICE_CONTAINER = "/data/op-node/op-node-beacon-data" +ROLLUP_CONFIG_MOUNT_PATH_ON_CONTAINER = "/network-configs/rollup.json" +# Port IDs +BEACON_TCP_DISCOVERY_PORT_ID = "tcp-discovery" +BEACON_UDP_DISCOVERY_PORT_ID = "udp-discovery" +BEACON_HTTP_PORT_ID = "http" + +# Port nums +BEACON_DISCOVERY_PORT_NUM = 9003 +BEACON_HTTP_PORT_NUM = 8547 + + +def get_used_ports(discovery_port): + used_ports = { + BEACON_TCP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + discovery_port, shared_utils.TCP_PROTOCOL, wait=None + ), + BEACON_UDP_DISCOVERY_PORT_ID: shared_utils.new_port_spec( + discovery_port, shared_utils.UDP_PROTOCOL, wait=None + ), + BEACON_HTTP_PORT_ID: shared_utils.new_port_spec( + BEACON_HTTP_PORT_NUM, + shared_utils.TCP_PROTOCOL, + shared_utils.HTTP_APPLICATION_PROTOCOL, + ), + } + return used_ports + + +ENTRYPOINT_ARGS = ["sh", "-c"] + +VERBOSITY_LEVELS = { + constants.GLOBAL_LOG_LEVEL.error: "ERROR", + constants.GLOBAL_LOG_LEVEL.warn: "WARN", + constants.GLOBAL_LOG_LEVEL.info: "INFO", + constants.GLOBAL_LOG_LEVEL.debug: "DEBUG", + constants.GLOBAL_LOG_LEVEL.trace: "TRACE", +} + + +def launch( + plan, + launcher, + service_name, + image, + el_context, + existing_cl_clients, + l1_config_env_vars, + gs_sequencer_private_key, + sequencer_enabled, +): + network_name = shared_utils.get_network_name(launcher.network) + + beacon_node_identity_recipe = PostHttpRequestRecipe( + endpoint="/", + content_type="application/json", + body='{"jsonrpc":"2.0","method":"opp2p_self","params":[],"id":1}', + port_id=BEACON_HTTP_PORT_ID, + extract={ + "enr": ".result.ENR", + "multiaddr": ".result.addresses[0]", + "peer_id": ".result.peerID", + }, + ) + + config = get_beacon_config( + plan, + launcher.el_cl_genesis_data, + launcher.jwt_file, + image, + service_name, + el_context, + existing_cl_clients, + l1_config_env_vars, + gs_sequencer_private_key, + beacon_node_identity_recipe, + sequencer_enabled, + ) + + beacon_service = plan.add_service(service_name, config) + + beacon_http_port = beacon_service.ports[BEACON_HTTP_PORT_ID] + beacon_http_url = "http://{0}:{1}".format( + beacon_service.ip_address, beacon_http_port.number + ) + + response = plan.request( + recipe=beacon_node_identity_recipe, service_name=service_name + ) + + beacon_node_enr = response["extract.enr"] + beacon_multiaddr = response["extract.multiaddr"] + beacon_peer_id = response["extract.peer_id"] + + return cl_context.new_cl_context( + client_name="op-node", + enr=beacon_node_enr, + ip_addr=beacon_service.ip_address, + http_port=beacon_http_port.number, + beacon_http_url=beacon_http_url, + cl_nodes_metrics_info=None, + beacon_service_name=service_name, + multiaddr=beacon_multiaddr, + peer_id=beacon_peer_id, + ) + + +def get_beacon_config( + plan, + el_cl_genesis_data, + jwt_file, + image, + service_name, + el_context, + existing_cl_clients, + 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, + el_context.engine_rpc_port_num, + ) + + used_ports = get_used_ports(BEACON_DISCOVERY_PORT_NUM) + + cmd = [ + "op-node", + "--l2={0}".format(EXECUTION_ENGINE_ENDPOINT), + "--l2.jwt-secret=" + constants.JWT_MOUNT_PATH_ON_CONTAINER, + "--verifier.l1-confs=4", + "--rollup.config=" + ROLLUP_CONFIG_MOUNT_PATH_ON_CONTAINER, + "--rpc.addr=0.0.0.0", + "--rpc.port={0}".format(BEACON_HTTP_PORT_NUM), + "--rpc.enable-admin", + "--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"]), + "--l1.trustrpc", + "--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), + "--p2p.ban.peers=true", + "--rpc.enable-admin", + "--syncmode=execution-layer", + ] + + files = { + constants.GENESIS_DATA_MOUNTPOINT_ON_CLIENTS: el_cl_genesis_data, + constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file, + } + ports = {} + ports.update(used_ports) + + return ServiceConfig( + image=image, + ports=ports, + cmd=cmd, + files=files, + private_ip_address_placeholder=constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, + ready_conditions=ReadyCondition( + recipe=beacon_node_identity_recipe, + field="code", + assertion="==", + target_value=200, + timeout="1m", + ), + ) + + +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, + ) diff --git a/devnet/src/el/world-chain/world_chain_launcher.star b/devnet/src/el/world-chain/world_chain_launcher.star index a7ddfa9b..34884834 100644 --- a/devnet/src/el/world-chain/world_chain_launcher.star +++ b/devnet/src/el/world-chain/world_chain_launcher.star @@ -169,11 +169,6 @@ def get_config( "--port={0}".format(discovery_port), ] - 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=" diff --git a/devnet/src/el_cl_launcher.star b/devnet/src/el_cl_launcher.star index 29d0a9ff..7407141b 100644 --- a/devnet/src/el_cl_launcher.star +++ b/devnet/src/el_cl_launcher.star @@ -4,6 +4,7 @@ constants = import_module( shared_utils = import_module( "github.com/ethpandaops/ethereum-package/src/shared_utils/shared_utils.star" ) + # EL op_geth = import_module("./el/op-geth/op_geth_launcher.star") op_geth_builder = import_module("./el/op-geth-builder/op_geth_builder_launcher.star") @@ -12,8 +13,11 @@ world_chain = import_module("./el/world-chain/world_chain_launcher.star") op_erigon = import_module("./el/op-erigon/op_erigon_launcher.star") op_nethermind = import_module("./el/op-nethermind/op_nethermind_launcher.star") op_besu = import_module("./el/op-besu/op_besu_launcher.star") +rollup_boost = import_module("./engine/rollup-boost/rollup_boost_launcher.star") + # CL op_node = import_module("./cl/op-node/op_node_launcher.star") +op_node_builder = import_module("./cl/op-node-builder/op_node_builder_launcher.star") hildr = import_module("./cl/hildr/hildr_launcher.star") @@ -94,6 +98,18 @@ def launch( }, } + engine_relay_launchers = { + "rollup-boost": { + "launcher": rollup_boost.new_rollup_boost_launcher( + el_cl_data, + jwt_file, + network_params.network, + network_params.network_id, + ), + "launch_method": rollup_boost.launch, + } + } + cl_launchers = { "op-node": { "launcher": op_node.new_op_node_launcher( @@ -101,6 +117,12 @@ def launch( ), "launch_method": op_node.launch, }, + "op-node-builder": { + "launcher": op_node_builder.new_op_node_launcher( + el_cl_data, jwt_file, network_params + ), + "launch_method": op_node_builder.launch, + }, "hildr": { "launcher": hildr.new_hildr_launcher(el_cl_data, jwt_file, network_params), "launch_method": hildr.launch, @@ -111,70 +133,221 @@ def launch( all_el_contexts = [] sequencer_enabled = True for index, participant in enumerate(participants): - cl_type = participant.cl_type - el_type = participant.el_type + if participant.admin: + # op-geth EL and op-node CL + el_type = participant.el_type + cl_type = participant.cl_type + + # world-chain-builder EL & op-node CL + el_builder_type = participant.el_builder_type + cl_builder_type = participant.cl_builder_type + + # engine relay + engine_relay_type = participant.engine_relay_type - if el_type not in el_launchers: - fail( - "Unsupported launcher '{0}', need one of '{1}'".format( - el_type, ",".join(el_launchers.keys()) + 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()) + ) + ) + + if el_builder_type not in el_launchers: + fail( + "Unsupported launcher '{0}', need one of '{1}'".format( + el_builder_type, ",".join(el_launchers.keys()) + ) + ) + + if cl_builder_type not in cl_launchers: + fail( + "Unsupported launcher '{0}', need one of '{1}'".format( + cl_builder_type, ",".join(cl_launchers.keys()) + ) + ) + + if engine_relay_type not in engine_relay_launchers: + fail( + "Unsupported launcher '{0}', need one of '{1}'".format( + engine_relay_type, ",".join(engine_relay_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"], + ) + + el_builder_launcher, el_builder_launch_method = ( + el_launchers[el_builder_type]["launcher"], + el_launchers[el_builder_type]["launch_method"], + ) + + cl_builder_launcher, cl_builder_launch_method = ( + cl_launchers[cl_builder_type]["launcher"], + cl_launchers[cl_builder_type]["launch_method"], ) - if cl_type not in cl_launchers: - fail( - "Unsupported launcher '{0}', need one of '{1}'".format( - cl_type, ",".join(cl_launchers.keys()) + + engine_relay_launcher, engine_relay_launch_method = ( + engine_relay_launchers[engine_relay_type]["launcher"], + engine_relay_launchers[engine_relay_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 = "wc-admin-{0}".format(el_type) + + cl_service_name = "wc-admin-{0}".format(cl_type) + + el_builder_service_name = "wc-admin-{0}-builder".format(el_builder_type) + + cl_builder_service_name = "wc-admin-{0}-builder".format(cl_builder_type) + + engine_relayer_service_name = "wc-admin-{0}-engine".format(engine_relay_type) + + # First launch the Sequencer, and the Builder + el_builder_context = el_builder_launch_method( + plan, + el_builder_launcher, + el_builder_service_name, + participant.el_builder_image, + all_el_contexts, + False, # sequencer disabled + None, # sequencer context + ) + + el_context = el_launch_method( + plan, + el_launcher, + el_service_name, + participant.el_image, + all_el_contexts, + True, # sequencer enabled + None, # sequencer context + ) + + # Launch the Engine Relay w/ engine rpc on el_context/el_builder_context + engine_relay_context = engine_relay_launch_method( + plan, + engine_relay_launcher, + engine_relayer_service_name, + participant.engine_relay_image, + all_el_contexts, + el_context, + el_builder_context, + ) + + # Launch op-node pointing to the Engine Relay + cl_context = cl_launch_method( + plan, + cl_launcher, + cl_service_name, + participant.cl_image, + engine_relay_context, + all_cl_contexts, + l1_config_env_vars, + gs_sequencer_private_key, + sequencer_enabled, + ) + + # Launch the CL Builder + # TODO: --p2p.static=/dns//tcp/9003/p2p/ + cl_builder_context = cl_builder_launch_method( + plan, + cl_builder_launcher, + cl_builder_service_name, + participant.cl_builder_image, + el_builder_context, + all_cl_contexts, + l1_config_env_vars, + gs_sequencer_private_key, + sequencer_enabled, + ) + + all_el_contexts.append(el_context) + all_cl_contexts.append(cl_context) + all_el_contexts.append(el_builder_context) + all_cl_contexts.append(cl_builder_context) + all_el_contexts.append(engine_relay_context) + else: + # Launch a standard participant (non sequencer) + 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}{3}".format( + index_str, el_type, cl_type, l2_services_suffix ) + cl_service_name = "op-cl-{0}-{1}-{2}{3}".format( + index_str, cl_type, el_type, l2_services_suffix + ) + + 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 - 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}{3}".format( - index_str, el_type, cl_type, l2_services_suffix - ) - cl_service_name = "op-cl-{0}-{1}-{2}{3}".format( - index_str, cl_type, el_type, l2_services_suffix - ) - - 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) + 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/devnet/src/engine/rollup-boost/rollup_boost_launcher.star b/devnet/src/engine/rollup-boost/rollup_boost_launcher.star new file mode 100644 index 00000000..69fba5f9 --- /dev/null +++ b/devnet/src/engine/rollup-boost/rollup_boost_launcher.star @@ -0,0 +1,119 @@ +shared_utils = import_module( + "github.com/ethpandaops/ethereum-package/src/shared_utils/shared_utils.star" +) +el_context = import_module( + "github.com/ethpandaops/ethereum-package/src/el/el_context.star" +) +el_admin_node_info = import_module( + "github.com/ethpandaops/ethereum-package/src/el/el_admin_node_info.star" +) +constants = import_module( + "github.com/ethpandaops/ethereum-package/src/package_io/constants.star" +) + +RPC_PORT_NUM = 8541 +WS_PORT_NUM = 8546 +DISCOVERY_PORT_NUM = 30303 +RPC_PORT_ID = "rpc" + +def launch( + plan, + launcher, + service_name, + image, + existing_el_clients, + sequencer_context, + builder_context, +): + network_name = shared_utils.get_network_name(launcher.network) + + config = get_config( + plan, + launcher.el_cl_genesis_data, + launcher.jwt_file, + launcher.network, + launcher.network_id, + image, + service_name, + existing_el_clients, + sequencer_context, + builder_context + ) + + service = plan.add_service(service_name, config) + + # enode = el_admin_node_info.get_enode_for_node(plan, service_name, RPC_PORT_ID) + + http_url = "http://{0}:{1}".format(service.ip_address, RPC_PORT_NUM) + + return el_context.new_el_context( + client_name="rollup-boost", + enode=None, + ip_addr=service.ip_address, + rpc_port_num=RPC_PORT_NUM, + ws_port_num=WS_PORT_NUM, + engine_rpc_port_num=RPC_PORT_NUM, + rpc_http_url=http_url, + service_name=service_name, + el_metrics_info=None, + ) + + +def get_config( + plan, + el_cl_genesis_data, + jwt_file, + network, + network_id, + image, + service_name, + existing_el_clients, + sequencer_context, + builder_context, +): + L2_EXECUTION_ENGINE_ENDPOINT = "http://{0}:{1}".format( + sequencer_context.ip_addr, + sequencer_context.engine_rpc_port_num, + ) + + BUILDER_EXECUTION_ENGINE_ENDPOINT = "http://{0}:{1}".format( + builder_context.ip_addr, + builder_context.engine_rpc_port_num, + ) + + public_ports = {} + cmd = [ + "--jwt-path=" + constants.JWT_MOUNT_PATH_ON_CONTAINER, + "--l2-url={0}".format(L2_EXECUTION_ENGINE_ENDPOINT), + "--builder-url={0}".format(BUILDER_EXECUTION_ENGINE_ENDPOINT), + "--rpc-port={0}".format(RPC_PORT_NUM), + "--boost-sync", + "--log-level=debug" + ] + + files = { + constants.JWT_MOUNTPOINT_ON_CLIENTS: jwt_file, + } + + return ServiceConfig( + image=image, + ports={}, + public_ports=public_ports, + cmd=cmd, + files=files, + private_ip_address_placeholder=constants.PRIVATE_IP_ADDRESS_PLACEHOLDER, + ) + + +def new_rollup_boost_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, + ) \ No newline at end of file diff --git a/devnet/src/l2.star b/devnet/src/l2.star index f9bca163..7bcb3150 100644 --- a/devnet/src/l2.star +++ b/devnet/src/l2.star @@ -18,7 +18,6 @@ def launch_l2( plan.print("Parsing the L2 input args") args_with_right_defaults = input_parser.input_parser(plan, l2_args) network_params = args_with_right_defaults.network_params - l2_config_env_vars = {} l2_config_env_vars["L2_CHAIN_ID"] = str(network_params.network_id) l2_config_env_vars["L2_BLOCK_TIME"] = str(network_params.seconds_per_slot) diff --git a/devnet/src/package_io/input_parser.star b/devnet/src/package_io/input_parser.star index f2bb3cb4..a6bfd3f6 100644 --- a/devnet/src/package_io/input_parser.star +++ b/devnet/src/package_io/input_parser.star @@ -14,6 +14,10 @@ DEFAULT_EL_IMAGES = { "op-besu": "ghcr.io/optimism-java/op-besu:latest", } +DEFAULT_ENGINE_IMAGES = { + "rollup-boost": "leytont/rollup-boost:latest", +} + DEFAULT_CL_IMAGES = { "op-node": "us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node:develop", "hildr": "ghcr.io/optimism-java/hildr:latest", @@ -43,10 +47,17 @@ def input_parser(plan, input_args): return struct( participants=[ struct( + admin=participant["admin"], el_type=participant["el_type"], el_image=participant["el_image"], + el_builder_type=participant["el_builder_type"], + el_builder_image=participant["el_builder_image"], cl_type=participant["cl_type"], cl_image=participant["cl_image"], + cl_builder_type=participant["cl_builder_type"], + cl_builder_image=participant["cl_builder_image"], + engine_relay_type=participant["engine_relay_type"], + engine_relay_image=participant["engine_relay_image"], count=participant["count"], ) for participant in result["participants"] @@ -84,6 +95,7 @@ def parse_network_params(plan, input_args): result["network_params"][sub_attr] = sub_value elif attr == "participants": participants = [] + participants.append(world_chain_admin_participant()) for participant in input_args["participants"]: new_participant = default_participant() for sub_attr, sub_value in participant.items(): @@ -129,7 +141,7 @@ def parse_network_params(plan, input_args): def default_input_args(input_args): network_params = default_network_params() - participants = [default_participant()] + participants = [world_chain_admin_participant()] op_contract_deployer_params = default_op_contract_deployer_params() return { "participants": participants, @@ -140,9 +152,9 @@ def default_input_args(input_args): def default_network_params(): return { - "network": "kurtosis", + "network": "world-chain", "network_id": "2151908", - "name": "op-kurtosis", + "name": "world-chain", "seconds_per_slot": 2, "fjord_time_offset": 0, "granite_time_offset": None, @@ -151,8 +163,25 @@ def default_network_params(): } +def world_chain_admin_participant(): + return { + "admin": True, + "el_type": "op-geth", + "el_image": DEFAULT_EL_IMAGES["op-geth"], + "el_builder_type": "world-chain", + "el_builder_image": DEFAULT_EL_IMAGES["world-chain"], + "cl_type": "op-node", + "cl_image": DEFAULT_CL_IMAGES["op-node"], + "cl_builder_type": "op-node-builder", + "cl_builder_image": DEFAULT_CL_IMAGES["op-node"], + "engine_relay_type": "rollup-boost", + "engine_relay_image": DEFAULT_ENGINE_IMAGES["rollup-boost"], + "count": 1, + } + def default_participant(): return { + "admin": False, "el_type": "op-geth", "el_image": "", "cl_type": "op-node",