From dd831301d52b75d74e797dc7c264c9cdd0412086 Mon Sep 17 00:00:00 2001 From: Zach Howard Date: Tue, 21 Jan 2025 22:47:43 -0600 Subject: [PATCH] feat: adds support for permissionless game, challenger interop support --- main.star | 64 +++-- .../op-challenger/op_challenger_launcher.star | 35 ++- src/contracts/contract_deployer.star | 240 +++++++----------- .../op-supervisor/op_supervisor_launcher.star | 9 +- src/l2.star | 8 +- src/package_io/input_parser.star | 4 + src/package_io/sanity_check.star | 1 + src/participant_network.star | 30 +-- src/util.star | 10 + static_files/scripts/fund.sh | 28 +- 10 files changed, 220 insertions(+), 209 deletions(-) diff --git a/main.star b/main.star index 3e29b215..edc44fc6 100644 --- a/main.star +++ b/main.star @@ -4,6 +4,9 @@ l2_launcher = import_module("./src/l2.star") op_supervisor_launcher = import_module( "./src/interop/op-supervisor/op_supervisor_launcher.star" ) +op_challenger_launcher = import_module( + "./src/challenger/op-challenger/op_challenger_launcher.star" +) observability = import_module("./src/observability/observability.star") prometheus = import_module("./src/observability/prometheus/prometheus_launcher.star") @@ -103,24 +106,26 @@ def run(plan, args): name="op_jwt_file", ) - all_participants = [] + l2s = [] for l2_num, chain in enumerate(optimism_args_with_right_defaults.chains): - all_participants += l2_launcher.launch_l2( - plan, - l2_num, - chain.network_params.name, - chain, - jwt_file, - deployment_output, - l1_config_env_vars, - l1_priv_key, - l1_rpc_url, - global_log_level, - global_node_selectors, - global_tolerations, - persistent, - observability_helper, - interop_params, + l2s.append( + l2_launcher.launch_l2( + plan, + l2_num, + chain.network_params.name, + chain, + jwt_file, + deployment_output, + l1_config_env_vars, + l1_priv_key, + l1_rpc_url, + global_log_level, + global_node_selectors, + global_tolerations, + persistent, + observability_helper, + interop_params, + ) ) if interop_params.enabled: @@ -128,12 +133,35 @@ def run(plan, args): plan, l1_config_env_vars, optimism_args_with_right_defaults.chains, - all_participants, + l2s, jwt_file, interop_params.supervisor_params, observability_helper, ) + # challenger must launch after supervisor because it depends on it for interop + for l2_num, l2 in enumerate(l2s): + chain = optimism_args_with_right_defaults.chains[l2_num] + op_challenger_image = ( + chain.challenger_params.image + if chain.challenger_params.image != "" + else input_parser.DEFAULT_CHALLENGER_IMAGES["op-challenger"] + ) + op_challenger_launcher.launch( + plan, + l2_num, + "op-challenger-{0}".format(chain.network_params.name), + chain.challenger_params.image, + l2.participants[0].el_context, + l2.participants[0].cl_context, + l1_config_env_vars, + deployment_output, + chain.network_params, + chain.challenger_params, + interop_params, + observability_helper, + ) + if observability_helper.enabled and len(observability_helper.metrics_jobs) > 0: plan.print("Launching prometheus...") prometheus_private_url = prometheus.launch_prometheus( diff --git a/src/challenger/op-challenger/op_challenger_launcher.star b/src/challenger/op-challenger/op_challenger_launcher.star index e0c53eee..10f51f01 100644 --- a/src/challenger/op-challenger/op_challenger_launcher.star +++ b/src/challenger/op-challenger/op_challenger_launcher.star @@ -9,6 +9,9 @@ ethereum_package_constants = import_module( observability = import_module("../../observability/observability.star") prometheus = import_module("../../observability/prometheus/prometheus_launcher.star") +interop_constants = import_module("../../interop/constants.star") +util = import_module("../../util.star") + # # ---------------------------------- Challenger client ------------------------------------- CHALLENGER_DATA_DIRPATH_ON_SERVICE_CONTAINER = "/data/op-challenger/op-challenger-data" @@ -22,32 +25,32 @@ def get_used_ports(): def launch( plan, + l2_num, service_name, image, el_context, cl_context, l1_config_env_vars, - gs_challenger_private_key, - game_factory_address, deployment_output, network_params, challenger_params, + interop_params, observability_helper, ): challenger_service_name = "{0}".format(service_name) config = get_challenger_config( plan, + l2_num, service_name, image, el_context, cl_context, l1_config_env_vars, - gs_challenger_private_key, - game_factory_address, deployment_output, network_params, challenger_params, + interop_params, observability_helper, ) @@ -62,20 +65,33 @@ def launch( def get_challenger_config( plan, + l2_num, service_name, image, el_context, cl_context, l1_config_env_vars, - gs_challenger_private_key, - game_factory_address, deployment_output, network_params, challenger_params, + interop_params, observability_helper, ): ports = dict(get_used_ports()) + game_factory_address = util.read_network_config_value( + plan, + deployment_output, + "state", + ".opChainDeployments[{0}].disputeGameFactoryProxyAddress".format(l2_num), + ) + challenger_key = util.read_network_config_value( + plan, + deployment_output, + "challenger-{0}".format(network_params.network_id), + ".privateKey", + ) + cmd = [ "op-challenger", "--cannon-l2-genesis=" @@ -93,9 +109,9 @@ def get_challenger_config( "--l1-beacon=" + l1_config_env_vars["CL_RPC_URL"], "--l1-eth-rpc=" + l1_config_env_vars["L1_RPC_URL"], "--l2-eth-rpc=" + el_context.rpc_http_url, - "--private-key=" + gs_challenger_private_key, + "--private-key=" + challenger_key, "--rollup-rpc=" + cl_context.beacon_http_url, - "--trace-type=" + "cannon,permissioned", + "--trace-type=" + ",".join(challenger_params.cannon_trace_types), ] # configure files @@ -109,6 +125,9 @@ def get_challenger_config( if observability_helper.enabled: observability.configure_op_service_metrics(cmd, ports) + if interop_params.enabled: + cmd.append("--supervisor-rpc=" + interop_constants.SUPERVISOR_ENDPOINT) + if ( challenger_params.cannon_prestate_path and challenger_params.cannon_prestates_url diff --git a/src/contracts/contract_deployer.star b/src/contracts/contract_deployer.star index ce7bbb77..7db17680 100644 --- a/src/contracts/contract_deployer.star +++ b/src/contracts/contract_deployer.star @@ -12,12 +12,11 @@ ethereum_package_genesis_constants = import_module( "github.com/ethpandaops/ethereum-package/src/prelaunch_data_generator/genesis_constants/genesis_constants.star" ) - -CANNED_VALUES = ( - ("int", "eip1559Denominator", 50), - ("int", "eip1559DenominatorCanyon", 250), - ("int", "eip1559Elasticity", 6), -) +CANNED_VALUES = { + "eip1559Denominator": 50, + "eip1559DenominatorCanyon": 250, + "eip1559Elasticity": 6, +} def deploy_contracts( @@ -59,7 +58,8 @@ def deploy_contracts( description="Collect keys, and fund addresses", image=utils.DEPLOYMENT_UTILS_IMAGE, env_vars={ - "PRIVATE_KEY": ethereum_package_genesis_constants.PRE_FUNDED_ACCOUNTS[ + "DEPLOYER_PRIVATE_KEY": priv_key, + "FUND_PRIVATE_KEY": ethereum_package_genesis_constants.PRE_FUNDED_ACCOUNTS[ 19 ].private_key, "FUND_VALUE": "10ether", @@ -98,145 +98,92 @@ def deploy_contracts( if activation_timestamp != None: hardfork_schedule.append((index, fork_key, activation_timestamp)) - intent_updates = [ - ( - "bool", - "useInterop", - optimism_args.interop.enabled, - ), - ( - "string", - "l1ContractsLocator", - optimism_args.op_contract_deployer_params.l1_artifacts_locator, - ), - ( - "string", - "l2ContractsLocator", - optimism_args.op_contract_deployer_params.l2_artifacts_locator, - ), - address_update( - "superchainRoles.guardian", "l1ProxyAdmin", l2_chain_ids_list[0] - ), - address_update( - "superchainRoles.protocolVersionsOwner", - "l1ProxyAdmin", - l2_chain_ids_list[0], - ), - address_update( - "superchainRoles.proxyAdminOwner", "l1ProxyAdmin", l2_chain_ids_list[0] - ), - ] + intent = { + "useInterop": optimism_args.interop.enabled, + "l1ContractsLocator": optimism_args.op_contract_deployer_params.l1_artifacts_locator, + "l2ContractsLocator": optimism_args.op_contract_deployer_params.l2_artifacts_locator, + "superchainRoles": { + "guardian": read_chain_cmd("l1ProxyAdmin", l2_chain_ids_list[0]), + "protocolVersionsOwner": read_chain_cmd( + "l1ProxyAdmin", l2_chain_ids_list[0] + ), + "proxyAdminOwner": read_chain_cmd("l1ProxyAdmin", l2_chain_ids_list[0]), + }, + "chains": [], + } + + absolute_prestate = "" if optimism_args.op_contract_deployer_params.global_deploy_overrides[ "faultGameAbsolutePrestate" ]: - intent_updates.extend( - [ - ( - "bool", - "globalDeployOverrides.dangerouslyAllowCustomDisputeParameters", - "true", - ), - ( - "string", - "globalDeployOverrides.faultGameAbsolutePrestate", - optimism_args.op_contract_deployer_params.global_deploy_overrides[ - "faultGameAbsolutePrestate" - ], - ), + absolute_prestate = ( + optimism_args.op_contract_deployer_params.global_deploy_overrides[ + "faultGameAbsolutePrestate" ] ) - intent_updates.extend( - [ - ( - "string", - chain_key(index, "deployOverrides.{0}".format(fork_key)), - "0x%x" % activation_timestamp, - ) - for index, fork_key, activation_timestamp in hardfork_schedule - ] - ) + intent["globalDeployOverrides"] = { + "dangerouslyAllowCustomDisputeParameters": True, + "faultGameAbsolutePrestate": absolute_prestate, + } for i, chain in enumerate(optimism_args.chains): chain_id = str(chain.network_params.network_id) - - intent_updates.extend( - [ - ( - "int", - chain_key(i, "deployOverrides.l2BlockTime"), - str(chain.network_params.seconds_per_slot), - ), - ( - "bool", - chain_key(i, "deployOverrides.fundDevAccounts"), - "true" if chain.network_params.fund_dev_accounts else "false", - ), - address_update( - chain_key(i, "baseFeeVaultRecipient"), - "baseFeeVaultRecipient", - chain_id, - ), - address_update( - chain_key(i, "l1FeeVaultRecipient"), "l1FeeVaultRecipient", chain_id - ), - address_update( - chain_key(i, "sequencerFeeVaultRecipient"), - "sequencerFeeVaultRecipient", - chain_id, - ), - address_update(chain_key(i, "roles.batcher"), "batcher", chain_id), - address_update( - chain_key(i, "roles.challenger"), "challenger", chain_id - ), - address_update( - chain_key(i, "roles.l1ProxyAdminOwner"), "l1ProxyAdmin", chain_id - ), - address_update( - chain_key(i, "roles.l2ProxyAdminOwner"), "l2ProxyAdmin", chain_id - ), - address_update(chain_key(i, "roles.proposer"), "proposer", chain_id), - address_update( - chain_key(i, "roles.systemConfigOwner"), - "systemConfigOwner", - chain_id, + intent_chain = dict(CANNED_VALUES) + intent_chain.update( + { + "deployOverrides": { + "l2BlockTime": chain.network_params.seconds_per_slot, + "fundDevAccounts": True + if chain.network_params.fund_dev_accounts + else False, + }, + "baseFeeVaultRecipient": read_chain_cmd( + "baseFeeVaultRecipient", chain_id ), - address_update( - chain_key(i, "roles.unsafeBlockSigner"), "sequencer", chain_id + "l1FeeVaultRecipient": read_chain_cmd("l1FeeVaultRecipient", chain_id), + "sequencerFeeVaultRecipient": read_chain_cmd( + "sequencerFeeVaultRecipient", chain_id ), - # altda deploy config - ( - "bool", - chain_key(i, "dangerousAltDAConfig.useAltDA"), - altda_args.use_altda, - ), - ( - "string", - chain_key(i, "dangerousAltDAConfig.daCommitmentType"), - altda_args.da_commitment_type, - ), - ( - "int", - chain_key(i, "dangerousAltDAConfig.daChallengeWindow"), - altda_args.da_challenge_window, - ), - ( - "int", - chain_key(i, "dangerousAltDAConfig.daResolveWindow"), - altda_args.da_resolve_window, - ), - ( - "int", - chain_key(i, "dangerousAltDAConfig.daBondSize"), - altda_args.da_bond_size, - ), - ( - "int", - chain_key(i, "dangerousAltDAConfig.daResolverRefundPercentage"), - altda_args.da_resolver_refund_percentage, - ), - ] + "roles": { + "batcher": read_chain_cmd("batcher", chain_id), + "challenger": read_chain_cmd("challenger", chain_id), + "l1ProxyAdminOwner": read_chain_cmd("l1ProxyAdmin", chain_id), + "l2ProxyAdminOwner": read_chain_cmd("l2ProxyAdmin", chain_id), + "proposer": read_chain_cmd("proposer", chain_id), + "systemConfigOwner": read_chain_cmd("systemConfigOwner", chain_id), + "unsafeBlockSigner": read_chain_cmd("sequencer", chain_id), + }, + "dangerousAdditionalDisputeGames": [ + { + "respectedGameType": 0, + "faultGameAbsolutePrestate": absolute_prestate, + "faultGameMaxDepth": 73, + "faultGameSplitDepth": 30, + "faultGameClockExtension": 10800, + "faultGameMaxClockDuration": 302400, + "dangerouslyAllowCustomDisputeParameters": True, + "vmType": "CANNON1", + "useCustomOracle": False, + "oracleMinProposalSize": 0, + "oracleChallengePeriodSeconds": 0, + "makeRespected": False, + } + ], + "dangerousAltDAConfig": { + "useAltDA": altda_args.use_altda, + "daCommitmentType": altda_args.da_commitment_type, + "daChallengeWindow": altda_args.da_challenge_window, + "daResolveWindow": altda_args.da_resolve_window, + "daBondSize": altda_args.da_bond_size, + }, + } ) - intent_updates.extend([(t, chain_key(i, k), v) for t, k, v in CANNED_VALUES]) + for index, fork_key, activation_timestamp in hardfork_schedule: + intent_chain["deployOverrides"][fork_key] = "0x%x" % activation_timestamp + intent["chains"].append(intent_chain) + + intent_json = json.encode(intent) + intent_json_artifact = utils.write_to_file(plan, intent_json, "/tmp", "intent.json") op_deployer_configure = plan.run_sh( name="op-deployer-configure", @@ -250,13 +197,21 @@ def deploy_contracts( ], files={ "/network-data": op_deployer_init.files_artifacts[0], + "/tmp": intent_json_artifact, }, run=" && ".join( [ - "dasel put -r toml -t {0} -v {2} '{1}' -o /network-data/intent.toml < /network-data/intent.toml".format( - t, k, v - ) - for t, k, v in intent_updates + # zhwrd: this mess is temporary until we implement json reading for op-deployer intent file + # convert intent_json to yaml. this is necessary because its unreliable to evaluate command substitutions in json. + """cat /tmp/intent.json | dasel -r json -w yaml > /network-data/intent.yaml""", + # evaluate the command substitutions + "eval \"echo '$(cat /network-data/intent.yaml)'\" | dasel -r yaml -w json > /network-data/intent-b.json", + # convert op-deployer generated intent.toml to json + "dasel -r toml -w json -f /network-data/intent.toml > /network-data/intent-a.json", + # merge the two intent.json files, ensuring that the chains array is merged correctly + "jq -s 'add + {chains: map(.chains) | transpose | map(add)}' /network-data/intent-a.json /network-data/intent-b.json > /network-data/intent-merged.json", + # convert the merged intent.json back to toml + "cat /network-data/intent-merged.json | dasel -r json -w toml > /network-data/intent.toml", ] ), ) @@ -324,10 +279,9 @@ def address_update(key, filename, l2_chain_id): return ( "string", key, - read_address_cmd(filename + "-" + l2_chain_id), + read_chain_cmd(filename, l2_chain_id), ) -def read_address_cmd(filename): - cmd = "jq -r .address /network-data/{0}.json".format(filename) - return "`{0}`".format(cmd) +def read_chain_cmd(filename, l2_chain_id): + return "`jq -r .address /network-data/{0}-{1}.json`".format(filename, l2_chain_id) diff --git a/src/interop/op-supervisor/op_supervisor_launcher.star b/src/interop/op-supervisor/op_supervisor_launcher.star index 457967f4..9c72160b 100644 --- a/src/interop/op-supervisor/op_supervisor_launcher.star +++ b/src/interop/op-supervisor/op_supervisor_launcher.star @@ -47,7 +47,7 @@ def launch( plan, l1_config_env_vars, chains, - all_participants, + l2s, jwt_file, supervisor_params, observability_helper, @@ -64,7 +64,7 @@ def launch( config = get_supervisor_config( plan, l1_config_env_vars, - all_participants, + l2s, jwt_file, dependency_set_artifact, supervisor_params, @@ -85,7 +85,7 @@ def launch( def get_supervisor_config( plan, l1_config_env_vars, - all_participants, + l2s, jwt_file, dependency_set_artifact, supervisor_params, @@ -119,7 +119,8 @@ def get_supervisor_config( participant.cl_context.ip_addr, interop_constants.INTEROP_WS_PORT_NUM, ) - for participant in all_participants + for l2 in l2s + for participant in l2.participants ] ), "OP_SUPERVISOR_L2_CONSENSUS_JWT_SECRET": ethereum_package_constants.JWT_MOUNT_PATH_ON_CONTAINER, diff --git a/src/l2.star b/src/l2.star index ea853f6f..920d77b1 100644 --- a/src/l2.star +++ b/src/l2.star @@ -45,7 +45,7 @@ def launch_l2( ) plan.print("Successfully launched da-server") - all_l2_participants = participant_network.launch_participant_network( + l2 = participant_network.launch_participant_network( plan, l2_args.participants, jwt_file, @@ -70,7 +70,7 @@ def launch_l2( all_el_contexts = [] all_cl_contexts = [] - for participant in all_l2_participants: + for participant in l2.participants: all_el_contexts.append(participant.el_context) all_cl_contexts.append(participant.cl_context) @@ -98,11 +98,11 @@ def launch_l2( ) plan.print("Successfully launched op-blockscout") - plan.print(all_l2_participants) + plan.print(l2.participants) plan.print( "Begin your L2 adventures by depositing some L1 Kurtosis ETH to: {0}".format( l1_bridge_address ) ) - return all_l2_participants + return l2 diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 88dd3b2d..8a6efafd 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -213,6 +213,9 @@ def input_parser(plan, input_args): cannon_prestates_url=result["challenger_params"][ "cannon_prestates_url" ], + cannon_trace_types=result["challenger_params"][ + "cannon_trace_types" + ], ), proposer_params=struct( image=result["proposer_params"]["image"], @@ -525,6 +528,7 @@ def default_challenger_params(): "extra_params": [], "cannon_prestate_path": "", "cannon_prestates_url": "https://storage.googleapis.com/oplabs-network-data/proofs/op-program/cannon", + "cannon_trace_types": ["cannon", "permissioned"], } diff --git a/src/package_io/sanity_check.star b/src/package_io/sanity_check.star index f99a0e3d..928ad556 100644 --- a/src/package_io/sanity_check.star +++ b/src/package_io/sanity_check.star @@ -98,6 +98,7 @@ SUBCATEGORY_PARAMS = { "extra_params", "cannon_prestate_path", "cannon_prestates_url", + "cannon_trace_types", ], "proposer_params": ["image", "extra_params", "game_type", "proposal_interval"], "mev_params": ["rollup_boost_image", "builder_host", "builder_port"], diff --git a/src/participant_network.star b/src/participant_network.star index 9fd0fbfa..bea7c574 100644 --- a/src/participant_network.star +++ b/src/participant_network.star @@ -100,32 +100,6 @@ def launch_participant_network( "state", ".opChainDeployments[{0}].disputeGameFactoryProxyAddress".format(l2_num), ) - challenger_key = util.read_network_config_value( - plan, - deployment_output, - "challenger-{0}".format(network_params.network_id), - ".privateKey", - ) - op_challenger_image = ( - challenger_params.image - if challenger_params.image != "" - else input_parser.DEFAULT_CHALLENGER_IMAGES["op-challenger"] - ) - op_challenger_launcher.launch( - plan, - "op-challenger-{0}".format(l2_services_suffix), - op_challenger_image, - all_el_contexts[0], - all_cl_contexts[0], - l1_config_env_vars, - challenger_key, - game_factory_address, - deployment_output, - network_params, - challenger_params, - observability_helper, - ) - proposer_key = util.read_network_config_value( plan, deployment_output, @@ -149,4 +123,6 @@ def launch_participant_network( observability_helper, ) - return all_participants + return struct( + participants=all_participants, + ) diff --git a/src/util.star b/src/util.star index e72e248c..ff350a97 100644 --- a/src/util.star +++ b/src/util.star @@ -18,6 +18,16 @@ def read_json_value(plan, json_file, json_path, mounts=None): return run.output +def read_file(plan, file_path, mounts=None): + run = plan.run_sh( + description="Read file", + image=DEPLOYMENT_UTILS_IMAGE, + files=mounts, + run="cat {0}".format(file_path), + ) + return run.output + + def write_to_file(plan, contents, directory, file_name): file_path = "{0}/{1}".format(directory, file_name) diff --git a/static_files/scripts/fund.sh b/static_files/scripts/fund.sh index a13694a7..a24239a2 100644 --- a/static_files/scripts/fund.sh +++ b/static_files/scripts/fund.sh @@ -4,10 +4,14 @@ set -euo pipefail export ETH_RPC_URL="$L1_RPC_URL" -addr=$(cast wallet address "$PRIVATE_KEY") +addr=$(cast wallet address "$FUND_PRIVATE_KEY") nonce=$(cast nonce "$addr") + +deployer_addr=$(cast wallet address "$DEPLOYER_PRIVATE_KEY") + mnemonic="test test test test test test test test test test test junk" -roles=("proposer" "batcher" "sequencer" "challenger" "l2ProxyAdmin" "l1ProxyAdmin" "baseFeeVaultRecipient" "l1FeeVaultRecipient" "sequencerFeeVaultRecipient" "systemConfigOwner") +roles=("l2ProxyAdmin" "l1ProxyAdmin" "baseFeeVaultRecipient" "l1FeeVaultRecipient" "sequencerFeeVaultRecipient" "systemConfigOwner") +funded_roles=("proposer" "batcher" "sequencer" "challenger") IFS=',';read -r -a chain_ids <<< "$1" @@ -16,7 +20,7 @@ write_keyfile() { } send() { - cast send $1 --value "$FUND_VALUE" --private-key "$PRIVATE_KEY" --timeout 60 --nonce "$nonce" & + cast send $1 --value "$FUND_VALUE" --private-key "$FUND_PRIVATE_KEY" --timeout 60 --nonce "$nonce" & nonce=$((nonce+1)) } @@ -24,8 +28,9 @@ send() { wallets_json=$(jq -n '{}') for chain_id in "${chain_ids[@]}"; do chain_wallets=$(jq -n '{}') - for index in "${!roles[@]}"; do - role="${roles[$index]}" + + for index in "${!funded_roles[@]}"; do + role="${funded_roles[$index]}" role_idx=$((index+1)) private_key=$(cast wallet private-key "$mnemonic" "m/44'/60'/2'/$chain_id/$role_idx") @@ -40,6 +45,19 @@ for chain_id in "${chain_ids[@]}"; do '.[$role + "PrivateKey"] = $private_key | .[$role + "Address"] = $address') done + for index in "${!roles[@]}"; do + role="${roles[$index]}" + role_idx=$((index+1)) + + write_keyfile "${deployer_addr}" "${DEPLOYER_PRIVATE_KEY}" "${role}-$chain_id" + + chain_wallets=$(echo "$chain_wallets" | jq \ + --arg role "$role" \ + --arg private_key "$DEPLOYER_PRIVATE_KEY" \ + --arg address "$deployer_addr" \ + '.[$role + "PrivateKey"] = $private_key | .[$role + "Address"] = $address') + done + # Add the L1 and L2 faucet information to each chain's wallet data # Use chain 20 from the ethereum_package to prevent conflicts chain_wallets=$(echo "$chain_wallets" | jq \