From 06b2fea89387ee021dd6ec802fcc0add65b2f059 Mon Sep 17 00:00:00 2001 From: Gabriel Moise Date: Fri, 18 Jul 2025 21:23:47 +0100 Subject: [PATCH 1/3] Tezt_cloud: Refactor echo_rollup --- tezt/tests/cloud/dal.ml | 241 ++++--------------------------- tezt/tests/cloud/echo_rollup.ml | 214 +++++++++++++++++++++++++++ tezt/tests/cloud/echo_rollup.mli | 42 ++++++ 3 files changed, 282 insertions(+), 215 deletions(-) create mode 100644 tezt/tests/cloud/echo_rollup.ml create mode 100644 tezt/tests/cloud/echo_rollup.mli diff --git a/tezt/tests/cloud/dal.ml b/tezt/tests/cloud/dal.ml index 8f523aef1e99..efd95a28814e 100644 --- a/tezt/tests/cloud/dal.ml +++ b/tezt/tests/cloud/dal.ml @@ -165,14 +165,6 @@ type baker = { stake : int; } -type echo_operator = { - node : Node.t; - client : Client.t; - sc_rollup_node : Sc_rollup_node.t; - sc_rollup_address : string; - operator : Account.key; -} - type etherlink_operator_setup = { node : Node.t; client : Client.t; @@ -259,7 +251,7 @@ type t = { (* NOTE: they have the observer profile*) observers : Dal_node_helpers.observer list; etherlink : etherlink option; - echo_rollup : echo_operator option; + echo_rollup : Echo_rollup.operator option; time_between_blocks : int; parameters : Dal_common.Parameters.t; infos : (int, per_level_info) Hashtbl.t; @@ -1961,19 +1953,6 @@ let init_teztale (configuration : configuration) cloud agent = if configuration.teztale then init_teztale cloud agent |> Lwt.map Option.some else Lwt.return_none -let init_echo_rollup_account (bootstrap : bootstrap) - (configuration : configuration) = - if configuration.echo_rollup then - let () = toplog "Initializing the echo rollup key" in - let* key = - Client.stresstest_gen_keys - ~alias_prefix:"echo_operator" - 1 - bootstrap.client - in - Lwt.return_some (List.hd key) - else Lwt.return_none - let init_etherlink_operators (bootstrap : bootstrap) etherlink_configuration = let* etherlink_rollup_operator_key = if etherlink_configuration <> None then @@ -2144,7 +2123,12 @@ let init_public_network cloud (configuration : configuration) let* etherlink_rollup_operator_key, etherlink_batching_operator_keys = init_etherlink_operators bootstrap etherlink_configuration in - let* echo_rollup_key = init_echo_rollup_account bootstrap configuration in + let* echo_rollup_key = + Echo_rollup.init_echo_rollup_account + ~client:bootstrap.client + ~echo_rollup:configuration.echo_rollup + ~alias_prefix:"echo_operator" + in let accounts_to_fund = (if configuration.producer_key = None then List.map (fun producer -> (producer, 10 * 1_000_000)) producer_accounts @@ -2420,14 +2404,11 @@ let init_sandbox_and_activate_protocol cloud (configuration : configuration) else Lwt.return [] in let* echo_rollup_key = - if configuration.echo_rollup then - let* keys = - Client.stresstest_gen_keys ~alias_prefix:"echo_rollup_key" 1 client - in - Lwt.return_some (List.hd keys) - else Lwt.return_none + Echo_rollup.init_echo_rollup_account + ~client + ~echo_rollup:configuration.echo_rollup + ~alias_prefix:"echo_rollup_key" in - let* () = if configuration.simulate_network = Disabled then let* parameter_file = @@ -3199,173 +3180,6 @@ let init_etherlink cloud configuration etherlink_configuration ~bootstrap in return {configuration = etherlink_configuration; operator; accounts} -let init_echo_rollup cloud configuration ~bootstrap operator dal_slots - next_agent = - let name = name_of Echo_rollup_operator in - let* agent = next_agent ~name in - let data_dir = - configuration.data_dir |> Option.map (fun data_dir -> data_dir // name) - in - let env, with_yes_crypto = - may_set_yes_crypto_env configuration.simulate_network - in - let* node = - Node_helpers.init - ?env - ?data_dir - ~name - ~arguments: - [ - Peer bootstrap.node_p2p_endpoint; - refutation_game_minimal_rolling_history_mode; - ] - ~rpc_external:configuration.external_rpc - configuration.network - ~snapshot:configuration.snapshot - ~with_yes_crypto - cloud - agent - in - let endpoint = Client.Node node in - let* client = Client.Agent.create ~endpoint agent in - let () = toplog "Init Echo rollup: importing the operator secret key" in - let* () = - Client.import_secret_key - client - ~endpoint - operator.Account.secret_key - ~alias:operator.Account.alias - in - - let l = Node.get_last_seen_level node in - let () = toplog "Init Echo rollup: revealing the operator account" in - let*! () = Client.reveal client ~endpoint ~src:operator.Account.alias in - let () = toplog "Init Echo rollup: waiting for level %d" (l + 2) in - let* _ = Node.wait_for_level node (l + 2) in - let () = toplog "Init Echo rollup: waiting for level %d: done" (l + 2) in - - let otel = Cloud.open_telemetry_endpoint cloud in - let* dal_node = - match dal_slots with - | [] -> - toplog "Echo rollup doesn't follow any slot" ; - none - | [slot_index] -> - let name = name_of (Echo_rollup_dal_observer {slot_index}) in - let* agent = next_agent ~name in - let* dal_node = Dal_node.Agent.create ~name ~node cloud agent in - let* () = - Dal_node.init_config - ~expected_pow:(Network.expected_pow configuration.network) - ~observer_profiles:dal_slots - ~peers:(Option.to_list bootstrap.dal_node_p2p_endpoint) - dal_node - in - let* () = - Dal_node.Agent.run - ?otel - ~ppx_profiling:configuration.ppx_profiling - ~ppx_profiling_backends:configuration.ppx_profiling_backends - dal_node - in - some dal_node - | _ -> - let* dal_reverse_proxy_with_observers = - Dal_reverse_proxy.init_dal_reverse_proxy_observers - ~external_rpc:configuration.external_rpc - ~network:configuration.network - ~snapshot:configuration.snapshot - ~ppx_profiling:configuration.ppx_profiling - ~ppx_profiling_backends:configuration.ppx_profiling_backends - ~memtrace:configuration.memtrace - ~simulate_network:configuration.simulate_network - ~name_of:(fun slot_index -> - name_of (Echo_rollup_dal_observer {slot_index})) - ~default_endpoint:None - ~node_p2p_endpoint:bootstrap.node_p2p_endpoint - ~dal_node_p2p_endpoint:bootstrap.dal_node_p2p_endpoint - ~dal_slots - ~next_agent - ~otel - ~cloud - in - some dal_reverse_proxy_with_observers - in - let* sc_rollup_node = - Sc_rollup_node.Agent.create - ~name:(Format.asprintf "%s-rollup-node" name) - ~base_dir:(Client.base_dir client) - ~default_operator:operator.alias - ~operators:[(Sc_rollup_node.Operating, operator.Account.alias)] - ?dal_node - cloud - agent - Operator - node - in - let preimages_dir = - Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" - in - let slots_bitvector = - List.fold_left - (fun vec slot -> Z.logor vec (Z.of_int (1 lsl slot))) - Z.zero - dal_slots - in - let config = - Format.sprintf - {|instructions: -- set: - value: %s - to: /slots - |} - (Z.to_bits slots_bitvector |> Hex.of_string |> Hex.show) - in - let output_config = Temp.file "config.yaml" in - write_file output_config ~contents:config ; - let* remote_output_config = Agent.copy agent ~source:output_config in - let* {output; _} = - Sc_rollup_helpers.Agent.prepare_installer_kernel - ~config:(`Path remote_output_config) - ~preimages_dir - Constant.WASM.dal_echo_kernel_bandwidth - agent - in - let pvm_kind = "wasm_2_0_0" in - let l = Node.get_last_seen_level node in - let () = toplog "Init Echo rollup: originating the rollup" in - let* sc_rollup_address = - Sc_rollup_helpers.Agent.originate_sc_rollup - ~kind:pvm_kind - ~boot_sector:output - ~parameters_ty:"unit" - ~src:operator.alias - client - in - let () = toplog "Init Echo rollup: waiting again, for level %d" (l + 2) in - let* _ = Node.wait_for_level node (l + 2) in - let () = - toplog "Init Echo rollup: waiting again, for level %d: done" (l + 2) - in - let () = toplog "Init Echo rollup: launching the rollup node" in - let* () = - Sc_rollup_node.run sc_rollup_node sc_rollup_address [Log_kernel_debug] - in - let () = toplog "Init Echo rollup: launching the rollup node: done" in - let operator : echo_operator = - {node; client; sc_rollup_node; operator; sc_rollup_address} - in - let* () = - add_prometheus_source - ?dal_node - ~node - ~sc_rollup_node - cloud - agent - (Format.asprintf "echo-%s" name) - in - return operator - let obtain_some_node_rpc_endpoint agent network (bootstrap : bootstrap) (bakers : baker list) (producers : Dal_node_helpers.producer list) (observers : Dal_node_helpers.observer list) etherlink = @@ -3591,25 +3405,22 @@ let init ~(configuration : configuration) etherlink_configuration cloud in let () = toplog "Init: all producers and observers have been initialized" in let* echo_rollup = - match echo_rollup_key with - | Some operator -> - let dal_slots = - List.map (fun p -> p.Dal_node_helpers.slot_index) producers - in - let* echo_rollup = - init_echo_rollup - cloud - configuration - ~bootstrap - operator - dal_slots - next_agent - in - Lwt.return_some echo_rollup - | _ -> Lwt.return_none + Echo_rollup.init_echo_rollup + cloud + ~data_dir:configuration.data_dir + ~simulate_network:configuration.simulate_network + ~external_rpc:configuration.external_rpc + ~network:configuration.network + ~snapshot:configuration.snapshot + ~ppx_profiling:configuration.ppx_profiling + ~ppx_profiling_backends:configuration.ppx_profiling_backends + ~memtrace:configuration.memtrace + ~node_p2p_endpoint:bootstrap.node_p2p_endpoint + ~dal_node_p2p_endpoint:bootstrap.dal_node_p2p_endpoint + ~next_agent + producers + echo_rollup_key in - - let () = toplog "Init: Echo rollup has been initialized" in let* etherlink = match etherlink_configuration with | Some etherlink_configuration -> diff --git a/tezt/tests/cloud/echo_rollup.ml b/tezt/tests/cloud/echo_rollup.ml new file mode 100644 index 000000000000..0c70309baa94 --- /dev/null +++ b/tezt/tests/cloud/echo_rollup.ml @@ -0,0 +1,214 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Trilitech *) +(* *) +(*****************************************************************************) + +open Agent_kind +open Scenarios_helpers +open Tezos +open Yes_crypto + +type operator = { + node : Node.t; + client : Client.t; + sc_rollup_node : Sc_rollup_node.t; + sc_rollup_address : string; + operator : Account.key; +} + +let init_echo_rollup_account ~client ~echo_rollup ~alias_prefix = + if echo_rollup then + let () = toplog "Initializing the echo rollup key" in + let* key = Client.stresstest_gen_keys ~alias_prefix 1 client in + Lwt.return_some (List.hd key) + else Lwt.return_none + +let init_echo_rollup cloud ~data_dir ~simulate_network ~external_rpc ~network + ~snapshot ~ppx_profiling ~ppx_profiling_backends ~memtrace + ~node_p2p_endpoint ~dal_node_p2p_endpoint operator dal_slots next_agent = + let name = name_of Echo_rollup_operator in + let* agent = next_agent ~name in + let data_dir = data_dir |> Option.map (fun data_dir -> data_dir // name) in + let with_yes_crypto = should_enable_yes_crypto simulate_network in + let* node = + Node_helpers.init + ?data_dir + ~name + ~arguments: + [Peer node_p2p_endpoint; refutation_game_minimal_rolling_history_mode] + ~rpc_external:external_rpc + network + ~snapshot + ~with_yes_crypto + cloud + agent + in + let endpoint = Client.Node node in + let* client = Client.Agent.create ~endpoint agent in + let () = toplog "Init Echo rollup: importing the operator secret key" in + let* () = + Client.import_secret_key + client + ~endpoint + operator.Account.secret_key + ~alias:operator.Account.alias + in + + let l = Node.get_last_seen_level node in + let () = toplog "Init Echo rollup: revealing the operator account" in + let*! () = Client.reveal client ~endpoint ~src:operator.Account.alias in + let () = toplog "Init Echo rollup: waiting for level %d" (l + 2) in + let* _ = Node.wait_for_level node (l + 2) in + let () = toplog "Init Echo rollup: waiting for level %d: done" (l + 2) in + + let otel = Cloud.open_telemetry_endpoint cloud in + let* dal_node = + match dal_slots with + | [] -> + toplog "Echo rollup doesn't follow any slot" ; + none + | [slot_index] -> + let name = name_of (Echo_rollup_dal_observer {slot_index}) in + let* agent = next_agent ~name in + let* dal_node = Dal_node.Agent.create ~name ~node cloud agent in + let* () = + Dal_node.init_config + ~expected_pow:(Network.expected_pow network) + ~observer_profiles:dal_slots + ~peers:(Option.to_list dal_node_p2p_endpoint) + dal_node + in + let* () = + Dal_node.Agent.run + ?otel + ~ppx_profiling + ~ppx_profiling_backends + dal_node + in + some dal_node + | _ -> + let* dal_reverse_proxy_with_observers = + Dal_reverse_proxy.init_dal_reverse_proxy_observers + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~simulate_network + ~name_of:(fun slot_index -> + name_of (Echo_rollup_dal_observer {slot_index})) + ~default_endpoint:None + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + ~dal_slots + ~next_agent + ~otel + ~cloud + in + some dal_reverse_proxy_with_observers + in + let* sc_rollup_node = + Sc_rollup_node.Agent.create + ~name:(Format.asprintf "%s-rollup-node" name) + ~base_dir:(Client.base_dir client) + ~default_operator:operator.alias + ~operators:[(Sc_rollup_node.Operating, operator.Account.alias)] + ?dal_node + cloud + agent + Operator + node + in + let preimages_dir = + Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" + in + let slots_bitvector = + List.fold_left + (fun vec slot -> Z.logor vec (Z.of_int (1 lsl slot))) + Z.zero + dal_slots + in + let config = + Format.sprintf + {|instructions: +- set: + value: %s + to: /slots + |} + (Z.to_bits slots_bitvector |> Hex.of_string |> Hex.show) + in + let output_config = Temp.file "config.yaml" in + write_file output_config ~contents:config ; + let* remote_output_config = Agent.copy agent ~source:output_config in + let* {output; _} = + Sc_rollup_helpers.Agent.prepare_installer_kernel + ~config:(`Path remote_output_config) + ~preimages_dir + Constant.WASM.dal_echo_kernel_bandwidth + agent + in + let pvm_kind = "wasm_2_0_0" in + let l = Node.get_last_seen_level node in + let () = toplog "Init Echo rollup: originating the rollup" in + let* sc_rollup_address = + Sc_rollup_helpers.Agent.originate_sc_rollup + ~kind:pvm_kind + ~boot_sector:output + ~parameters_ty:"unit" + ~src:operator.alias + client + in + let () = toplog "Init Echo rollup: waiting again, for level %d" (l + 2) in + let* _ = Node.wait_for_level node (l + 2) in + let () = + toplog "Init Echo rollup: waiting again, for level %d: done" (l + 2) + in + let () = toplog "Init Echo rollup: launching the rollup node" in + let* () = + Sc_rollup_node.run sc_rollup_node sc_rollup_address [Log_kernel_debug] + in + let () = toplog "Init Echo rollup: launching the rollup node: done" in + let operator = {node; client; sc_rollup_node; operator; sc_rollup_address} in + let* () = + add_prometheus_source + ?dal_node + ~node + ~sc_rollup_node + cloud + agent + (Format.asprintf "echo-%s" name) + in + return operator + +let init_echo_rollup cloud ~data_dir ~simulate_network ~external_rpc ~network + ~snapshot ~ppx_profiling ~ppx_profiling_backends ~memtrace + ~node_p2p_endpoint ~dal_node_p2p_endpoint ~next_agent producers + echo_rollup_key = + match echo_rollup_key with + | Some operator -> + let dal_slots = + List.map (fun p -> p.Dal_node_helpers.slot_index) producers + in + let* echo_rollup = + init_echo_rollup + cloud + ~data_dir + ~simulate_network + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + operator + dal_slots + next_agent + in + let () = toplog "Init: Echo rollup has been initialized" in + Lwt.return_some echo_rollup + | _ -> Lwt.return_none diff --git a/tezt/tests/cloud/echo_rollup.mli b/tezt/tests/cloud/echo_rollup.mli new file mode 100644 index 000000000000..666200989975 --- /dev/null +++ b/tezt/tests/cloud/echo_rollup.mli @@ -0,0 +1,42 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Trilitech *) +(* *) +(*****************************************************************************) + +(** This module handles the initialization and orchestration of an Echo rollup + within a Tezt-cloud scenario. + + An Echo rollup is a smart rollup running a WASM kernel that echoes back + messages published via DAL slots. +*) + +type operator + +(** [init_echo_rollup_account ~client ~echo_rollup ~alias_prefix] generates + a new rollup operator key if [~echo_rollup] is [true]. The key alias will + start with [~alias_prefix]. *) +val init_echo_rollup_account : + client:Client.t -> + echo_rollup:bool -> + alias_prefix:string -> + Account.key option Lwt.t + +(** Initialize an Echo rollup. *) +val init_echo_rollup : + Cloud.t -> + data_dir:string option -> + simulate_network:Scenarios_cli.network_simulation_config -> + external_rpc:bool -> + network:Network.t -> + snapshot:Snapshot_helpers.t -> + ppx_profiling:bool -> + ppx_profiling_backends:string list -> + memtrace:bool -> + node_p2p_endpoint:string -> + dal_node_p2p_endpoint:string option -> + next_agent:(name:string -> Agent.t Lwt.t) -> + Dal_node_helpers.producer list -> + Account.key option -> + operator option Lwt.t -- GitLab From b3018f332aeba19c3b734acda5fc5892b3610aac Mon Sep 17 00:00:00 2001 From: Gabriel Moise Date: Mon, 21 Jul 2025 11:44:49 +0100 Subject: [PATCH 2/3] Tezt_cloud: Refactor etherlink_helpers --- tezt/tests/cloud/dal.ml | 663 ++----------------------- tezt/tests/cloud/etherlink_helpers.ml | 627 +++++++++++++++++++++++ tezt/tests/cloud/etherlink_helpers.mli | 63 +++ 3 files changed, 726 insertions(+), 627 deletions(-) create mode 100644 tezt/tests/cloud/etherlink_helpers.ml create mode 100644 tezt/tests/cloud/etherlink_helpers.mli diff --git a/tezt/tests/cloud/dal.ml b/tezt/tests/cloud/dal.ml index efd95a28814e..8dc152bae7ab 100644 --- a/tezt/tests/cloud/dal.ml +++ b/tezt/tests/cloud/dal.ml @@ -99,15 +99,6 @@ module Disconnect = struct Lwt.return {t with disconnected_bakers = bakers_to_keep_disconnected} end -type etherlink_configuration = { - etherlink_sequencer : bool; - etherlink_producers : int; - (* Empty list means DAL FF is set to false. *) - etherlink_dal_slots : int list; - chain_id : int option; - tezlink : bool; -} - type configuration = { with_dal : bool; stake : int list Lwt.t; @@ -165,23 +156,6 @@ type baker = { stake : int; } -type etherlink_operator_setup = { - node : Node.t; - client : Client.t; - sc_rollup_node : Sc_rollup_node.t; - evm_node : Tezt_etherlink.Evm_node.t; - is_sequencer : bool; - sc_rollup_address : string; - account : Account.key; - batching_operators : Account.key list; -} - -type etherlink = { - configuration : etherlink_configuration; - operator : etherlink_operator_setup; - accounts : Tezt_etherlink.Eth_account.t Array.t; -} - type public_key_hash = PKH of string type commitment_info = {commitment : string; publisher_pkh : string} @@ -250,7 +224,7 @@ type t = { producers : Dal_node_helpers.producer list; (* NOTE: they have the observer profile*) observers : Dal_node_helpers.observer list; - etherlink : etherlink option; + etherlink : Etherlink_helpers.etherlink option; echo_rollup : Echo_rollup.operator option; time_between_blocks : int; parameters : Dal_common.Parameters.t; @@ -1913,15 +1887,9 @@ let get_infos_per_level t ~level ~metadata = dal_attestations) in let* etherlink_operator_balance_sum = - List.fold_left - (fun acc (operator : Account.key) -> - let* acc in - let* balance = - Client.get_full_balance_for ~account:operator.public_key_hash client - in - return Tez.(acc + balance)) - (return Tez.zero) - etherlink_operators + Etherlink_helpers.total_operator_balance + ~client + ~operators:etherlink_operators in (* None of these actions are performed if `--dal-slack-webhook` is not provided. *) @@ -1953,26 +1921,6 @@ let init_teztale (configuration : configuration) cloud agent = if configuration.teztale then init_teztale cloud agent |> Lwt.map Option.some else Lwt.return_none -let init_etherlink_operators (bootstrap : bootstrap) etherlink_configuration = - let* etherlink_rollup_operator_key = - if etherlink_configuration <> None then - let () = toplog "Generating a key pair for Etherlink operator" in - Client.stresstest_gen_keys - ~alias_prefix:"etherlink_operator" - 1 - bootstrap.client - else Lwt.return_nil - in - let* etherlink_batching_operator_keys = - if etherlink_configuration <> None then - Client.stresstest_gen_keys - ~alias_prefix:"etherlink_batching" - 20 - bootstrap.client - else Lwt.return [] - in - return (etherlink_rollup_operator_key, etherlink_batching_operator_keys) - let init_public_network cloud (configuration : configuration) etherlink_configuration teztale agent network = toplog "Init public network" ; @@ -2121,7 +2069,9 @@ let init_public_network cloud (configuration : configuration) ~dal_node_producers:configuration.dal_node_producers in let* etherlink_rollup_operator_key, etherlink_batching_operator_keys = - init_etherlink_operators bootstrap etherlink_configuration + Etherlink_helpers.init_etherlink_operators + ~client:bootstrap.client + etherlink_configuration in let* echo_rollup_key = Echo_rollup.init_echo_rollup_account @@ -2174,7 +2124,8 @@ let round_robin_split m lst = Array.to_list buckets |> List.rev let init_sandbox_and_activate_protocol cloud (configuration : configuration) - ?(etherlink_configuration : etherlink_configuration option) agent = + ?(etherlink_configuration : + Etherlink_helpers.etherlink_configuration option) agent = let dal_bootstrap_node_net_port = Agent.next_available_port agent in let dal_config : Cryptobox.Config.t = { @@ -2393,15 +2344,8 @@ let init_sandbox_and_activate_protocol cloud (configuration : configuration) (List.length configuration.dal_node_producers) client in - let* etherlink_rollup_operator_key = - if etherlink_configuration <> None then - Client.stresstest_gen_keys ~alias_prefix:"etherlink_operator" 1 client - else Lwt.return_nil - in - let* etherlink_batching_operator_keys = - if etherlink_configuration <> None then - Client.stresstest_gen_keys ~alias_prefix:"etherlink_batching" 20 client - else Lwt.return [] + let* etherlink_rollup_operator_key, etherlink_batching_operator_keys = + Etherlink_helpers.init_etherlink_operators ~client etherlink_configuration in let* echo_rollup_key = Echo_rollup.init_echo_rollup_account @@ -2667,519 +2611,6 @@ let init_baker ?stake cloud (configuration : configuration) ~bootstrap teztale in Lwt.return {node; dal_node; baker; accounts = baker_accounts; stake} -let init_etherlink_dal_node - ({ - external_rpc; - network; - snapshot; - ppx_profiling; - ppx_profiling_backends; - memtrace; - simulate_network; - _; - } as configuration) ~bootstrap ~dal_slots ~next_agent ~otel ~cloud = - match dal_slots with - | [] -> - toplog "Etherlink will run without DAL support" ; - none - | [_] -> - (* On a single DAL slot index, we launch a single DAL node for - this index on a dedicated VM and give it directly as endpoint - to the rollup node. *) - toplog "Etherlink sequencer will run its own DAL node" ; - let name = name_of Etherlink_dal_operator in - let* agent = next_agent ~name in - let env, with_yes_crypto = may_set_yes_crypto_env simulate_network in - let* node = - Node_helpers.init - ?env - ~name - ~arguments: - [ - Peer bootstrap.node_p2p_endpoint; - refutation_game_minimal_rolling_history_mode; - ] - ~rpc_external:external_rpc - network - ~with_yes_crypto - ~snapshot - cloud - agent - in - let* dal_node = Dal_node.Agent.create ~name ~node cloud agent in - let* () = - Dal_node.init_config - ~expected_pow:(Network.expected_pow network) - ~operator_profiles:dal_slots - ~peers:(Option.to_list bootstrap.dal_node_p2p_endpoint) - dal_node - in - let* () = - Dal_node.Agent.run ?otel ~ppx_profiling ~ppx_profiling_backends dal_node - in - some dal_node - | _ :: _ :: _ -> - (* On several slot indices, we launch one observer DAL node per - slot index + an operator DAL node on no index + a reverse proxy - which is passed as endpoint to the rollup node. The operator - DAL node runs on the same VM than the reverse proxy, the - observer DAL nodes run on dedicated VMs. *) - toplog - "Etherlink will run with DAL support on indices %a" - (Format.pp_print_list - ~pp_sep:(fun out () -> Format.fprintf out ",") - Format.pp_print_int) - dal_slots ; - toplog "Etherlink sequencer will use a reverse proxy" ; - let name = name_of Etherlink_dal_operator in - let* agent = next_agent ~name in - let env, with_yes_crypto = may_set_yes_crypto_env simulate_network in - let* node = - Node_helpers.init - ?env - ~name - ~arguments: - [ - Peer bootstrap.node_p2p_endpoint; - refutation_game_minimal_rolling_history_mode; - ] - ~rpc_external:external_rpc - network - ~with_yes_crypto - ~snapshot - cloud - agent - in - let* default_dal_node = Dal_node.Agent.create ~name ~node cloud agent in - let* () = - Dal_node.init_config - ~expected_pow:(Network.expected_pow network) - ~peers:(Option.to_list bootstrap.dal_node_p2p_endpoint) - default_dal_node - in - let* () = - Dal_node.Agent.run - ?otel - ~memtrace - ~ppx_profiling - ~ppx_profiling_backends - default_dal_node - in - let default_endpoint = Dal_node.rpc_endpoint default_dal_node in - let* reverse_proxy_dal_node = - Dal_reverse_proxy.init_dal_reverse_proxy_observers - ~external_rpc:configuration.external_rpc - ~network:configuration.network - ~snapshot:configuration.snapshot - ~ppx_profiling:configuration.ppx_profiling - ~ppx_profiling_backends:configuration.ppx_profiling_backends - ~memtrace:configuration.memtrace - ~simulate_network:configuration.simulate_network - ~name_of:(fun slot_index -> - name_of (Etherlink_dal_observer {slot_index})) - ~default_endpoint:(Some default_endpoint) - ~node_p2p_endpoint:bootstrap.node_p2p_endpoint - ~dal_node_p2p_endpoint:bootstrap.dal_node_p2p_endpoint - ~dal_slots - ~next_agent - ~otel - ~cloud - in - some reverse_proxy_dal_node - -let init_etherlink_operator_setup cloud configuration etherlink_configuration - name ~bootstrap ~dal_slots ~tezlink account batching_operators agent - next_agent = - let chain_id = Option.value ~default:1 etherlink_configuration.chain_id in - let is_sequencer = etherlink_configuration.etherlink_sequencer in - let data_dir = - configuration.data_dir |> Option.map (fun data_dir -> data_dir // name) - in - let env, with_yes_crypto = - may_set_yes_crypto_env configuration.simulate_network - in - let* node = - Node_helpers.init - ?env - ?data_dir - ~name - ~arguments: - [ - Peer bootstrap.node_p2p_endpoint; - refutation_game_minimal_rolling_history_mode; - ] - ~rpc_external:configuration.external_rpc - configuration.network - ~with_yes_crypto - ~snapshot:configuration.snapshot - cloud - agent - in - let endpoint = Client.Node node in - let* client = Client.Agent.create ~endpoint agent in - let () = toplog "Init Etherlink: importing the sequencer secret key" in - let* () = - Lwt_list.iter_s - (fun account -> - Client.import_secret_key - client - ~endpoint - account.Account.secret_key - ~alias:account.Account.alias) - (account :: batching_operators) - in - let l = Node.get_last_seen_level node in - let () = toplog "Init Etherlink: revealing the sequencer account" in - let* () = - Lwt_list.iter_s - (fun account -> - let*! () = Client.reveal client ~endpoint ~src:account.Account.alias in - unit) - (account :: batching_operators) - in - let () = toplog "Init Etherlink operator: waiting for level %d" (l + 2) in - let* _ = Node.wait_for_level node (l + 2) in - let () = toplog "Init Etherlink: waiting for level %d: done" (l + 2) in - (* A configuration is generated locally by the orchestrator. The resulting - kernel will be pushed to Etherlink. *) - let tezlink_config = Temp.file "l2-tezlink-config.yaml" in - let tez_bootstrap_accounts = Account.Bootstrap.keys |> Array.to_list in - let* () = - if tezlink then - let*! () = - Evm_node.make_l2_kernel_installer_config - ~chain_id - ~chain_family:"Michelson" - ~eth_bootstrap_accounts:[] - ~tez_bootstrap_accounts - ~output:tezlink_config - () - in - let* () = Process.spawn "cat" [tezlink_config] |> Process.check in - unit - else unit - in - let rollup_config = Temp.file "rollup-config.yaml" in - let eth_bootstrap_accounts = - Tezt_etherlink.Eth_account.bootstrap_accounts |> Array.to_list - |> List.map (fun account -> account.Tezt_etherlink.Eth_account.address) - in - let*! () = - let sequencer = if is_sequencer then Some account.public_key else None in - let () = toplog "Init Etherlink: configuring the kernel" in - Tezt_etherlink.Evm_node.make_kernel_installer_config - ?sequencer - ~eth_bootstrap_accounts - ~output:rollup_config - ~enable_dal:(Option.is_some dal_slots) - ~chain_id - ?dal_slots - ~enable_multichain:tezlink - ?l2_chain_ids:(if tezlink then Some [chain_id] else None) - () - in - let* () = Process.spawn "cat" [rollup_config] |> Process.check in - let otel = Cloud.open_telemetry_endpoint cloud in - let* dal_node = - init_etherlink_dal_node - configuration - ~bootstrap - ~next_agent - ~dal_slots:etherlink_configuration.etherlink_dal_slots - ~otel - ~cloud - in - let operators = - List.map - (fun account -> (Sc_rollup_node.Batching, account.Account.alias)) - batching_operators - in - let* sc_rollup_node = - Sc_rollup_node.Agent.create - ~name:(Format.asprintf "etherlink-%s-rollup-node" name) - ~base_dir:(Client.base_dir client) - ~default_operator:account.alias - ~operators - ?dal_node - cloud - agent - Operator - node - in - let preimages_dir = - Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" - in - let* output = - if tezlink then - let* remote_rollup_config = Agent.copy agent ~source:rollup_config in - let* remote_tezlink_config = Agent.copy agent ~source:tezlink_config in - let* {output; _} = - Sc_rollup_helpers.Agent - .prepare_installer_kernel_with_multiple_setup_file - ~configs:[remote_rollup_config; remote_tezlink_config] - ~preimages_dir - (Uses.path Constant.WASM.evm_kernel) - agent - in - return output - else - let* remote_rollup_config = Agent.copy agent ~source:rollup_config in - let* {output; _} = - Sc_rollup_helpers.Agent.prepare_installer_kernel - ~config:(`Path remote_rollup_config) - ~preimages_dir - Constant.WASM.evm_kernel - agent - in - return output - in - let pvm_kind = "wasm_2_0_0" in - let l = Node.get_last_seen_level node in - let () = toplog "Init Etherlink: originating the rollup" in - let* sc_rollup_address = - Sc_rollup_helpers.Agent.originate_sc_rollup - ~kind:pvm_kind - ~boot_sector:output - ~parameters_ty:Tezt_etherlink.Test_helpers.evm_type - ~src:account.alias - client - in - let () = toplog "Init Etherlink: waiting again, for level %d" (l + 2) in - let* _ = Node.wait_for_level node (l + 2) in - let () = toplog "Init Etherlink: waiting again, for level %d: done" (l + 2) in - let () = toplog "Init Etherlink: launching the rollup node" in - let* () = - Sc_rollup_node.run sc_rollup_node sc_rollup_address [Log_kernel_debug] - in - let () = toplog "Init Etherlink: launching the rollup node: done" in - let private_rpc_port = Agent.next_available_port agent |> Option.some in - let time_between_blocks = Some (Evm_node.Time_between_blocks 10.) in - let sequencer_mode = - Evm_node.Sequencer - { - initial_kernel = output; - preimage_dir = Some preimages_dir; - private_rpc_port; - time_between_blocks; - sequencer = account.alias; - genesis_timestamp = None; - max_blueprints_lag = Some 300; - max_blueprints_ahead = Some 2000; - max_blueprints_catchup = None; - catchup_cooldown = None; - max_number_of_chunks = None; - wallet_dir = Some (Client.base_dir client); - tx_pool_timeout_limit = None; - tx_pool_addr_limit = None; - tx_pool_tx_per_addr_limit = None; - dal_slots; - sequencer_sunset_sec = None; - } - in - let endpoint = Sc_rollup_node.endpoint sc_rollup_node in - let mode = if is_sequencer then sequencer_mode else Evm_node.Proxy in - let () = toplog "Init Etherlink: launching the EVM node" in - let* evm_node = - Tezos.Evm_node.Agent.init - ~patch_config:(fun json -> - JSON.update - "public_rpc" - (fun json -> - JSON.update - "cors_headers" - (fun _ -> - JSON.annotate ~origin:"patch-config:cors_headers" - @@ `A [`String "*"]) - json - |> JSON.update "cors_origins" (fun _ -> - JSON.annotate ~origin:"patch-config:cors_origins" - @@ `A [`String "*"])) - json - |> Evm_node.patch_config_with_experimental_feature - ~drop_duplicate_when_injection:true - ~blueprints_publisher_order_enabled:true - ~rpc_server:Resto - ~spawn_rpc:(Port.fresh ()) - ?l2_chains: - (if tezlink then - Some - [ - { - (Evm_node.default_l2_setup ~l2_chain_id:chain_id) with - l2_chain_family = "Michelson"; - tez_bootstrap_accounts = Some tez_bootstrap_accounts; - }; - ] - else None) - ()) - ~name:(Format.asprintf "etherlink-%s-evm-node" name) - ~mode - endpoint - cloud - agent - in - let () = toplog "Init Etherlink: launching the EVM node: done" in - let operator = - { - node; - client; - sc_rollup_node; - evm_node; - is_sequencer; - account; - batching_operators; - sc_rollup_address; - } - in - let* () = - add_prometheus_source - ?dal_node - ~node - ~sc_rollup_node - ~evm_node - cloud - agent - (Format.asprintf "etherlink-%s" name) - in - return operator - -let init_etherlink_producer_setup operator name ~bootstrap ~rpc_external cloud - agent = - let* node = - Node.Agent.init - ~rpc_external - ~name:(Format.asprintf "etherlink-%s-node" name) - ~arguments:[Peer bootstrap.node_p2p_endpoint; Synchronisation_threshold 0] - cloud - agent - in - let endpoint = Client.Node node in - let* client = Client.Agent.create ~endpoint agent in - let l = Node.get_last_seen_level node in - let* _ = Node.wait_for_level node (l + 2) in - (* A configuration is generated locally by the orchestrator. The resulting - kernel will be pushed to Etherlink. *) - let output_config = Temp.file "config.yaml" in - let eth_bootstrap_accounts = - Tezt_etherlink.Eth_account.bootstrap_accounts |> Array.to_list - |> List.map (fun account -> account.Tezt_etherlink.Eth_account.address) - in - let*! () = - let sequencer = - if operator.is_sequencer then Some operator.account.public_key else None - in - Tezt_etherlink.Evm_node.make_kernel_installer_config - ?sequencer - ~eth_bootstrap_accounts - ~output:output_config - () - in - let* sc_rollup_node = - Sc_rollup_node.Agent.create - ~name:(Format.asprintf "etherlink-%s-rollup-node" name) - ~base_dir:(Client.base_dir client) - cloud - agent - Observer - node - in - let preimages_dir = - Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" - in - let* remote_output_config = Agent.copy agent ~source:output_config in - let* {output; _} = - Sc_rollup_helpers.Agent.prepare_installer_kernel - ~config:(`Path remote_output_config) - ~preimages_dir - Constant.WASM.evm_kernel - agent - in - let* () = - Sc_rollup_node.run - sc_rollup_node - operator.sc_rollup_address - [Log_kernel_debug] - in - let mode = - Evm_node.Observer - { - private_rpc_port = None; - initial_kernel = output; - preimages_dir = Some preimages_dir; - rollup_node_endpoint = Sc_rollup_node.endpoint sc_rollup_node; - } - in - let () = toplog "Init Etherlink: init producer %s" name in - let endpoint = Evm_node.endpoint operator.evm_node in - (* TODO: try using this local EVM node for Floodgate confirmations. *) - let* evm_node = - Evm_node.Agent.init - ~name:(Format.asprintf "etherlink-%s-evm-node" name) - ~mode - endpoint - cloud - agent - in - let* () = - (* This is to avoid producing operations too soon. *) - Evm_node.wait_for_blueprint_applied evm_node 1 - in - (* Launch floodgate *) - let* () = - Floodgate.Agent.run - ~rpc_endpoint:endpoint - ~max_active_eoa:210 - ~max_transaction_batch_length:70 - ~tick_interval:1.0 - ~controller:Tezt_etherlink.Eth_account.bootstrap_accounts.(0) - ~base_fee_factor:1000.0 - cloud - agent - in - return () - -let init_etherlink cloud configuration etherlink_configuration ~bootstrap - etherlink_rollup_operator_key batching_operators ~dal_slots ~tezlink - next_agent = - let () = toplog "Initializing an Etherlink operator" in - let name = name_of Etherlink_operator in - let* operator_agent = next_agent ~name in - let* operator = - init_etherlink_operator_setup - cloud - configuration - etherlink_configuration - ~dal_slots - "operator" - ~bootstrap - ~tezlink - etherlink_rollup_operator_key - batching_operators - operator_agent - next_agent - in - let accounts = Tezt_etherlink.Eth_account.bootstrap_accounts in - let* producers_agents = - List.init etherlink_configuration.etherlink_producers (fun i -> - let name = name_of (Etherlink_producer i) in - next_agent ~name) - |> Lwt.all - in - let* () = - producers_agents - |> List.mapi (fun i agent -> - assert (i < Array.length accounts) ; - init_etherlink_producer_setup - operator - (Format.asprintf "producer-%d" i) - ~bootstrap - ~rpc_external:configuration.external_rpc - cloud - agent) - |> Lwt.join - in - return {configuration = etherlink_configuration; operator; accounts} - let obtain_some_node_rpc_endpoint agent network (bootstrap : bootstrap) (bakers : baker list) (producers : Dal_node_helpers.producer list) (observers : Dal_node_helpers.observer list) etherlink = @@ -3190,7 +2621,7 @@ let obtain_some_node_rpc_endpoint agent network (bootstrap : bootstrap) | [], producer :: _, _, _ -> Node.as_rpc_endpoint producer.node | [], [], observer :: _, _ -> Node.as_rpc_endpoint observer.node | [], [], [], Some etherlink -> - Node.as_rpc_endpoint etherlink.operator.node + Node.as_rpc_endpoint etherlink.Etherlink_helpers.operator.node | [], [], [], None -> bootstrap.node_rpc_endpoint) | _ -> bootstrap.node_rpc_endpoint @@ -3422,45 +2853,22 @@ let init ~(configuration : configuration) etherlink_configuration cloud echo_rollup_key in let* etherlink = - match etherlink_configuration with - | Some etherlink_configuration -> - let () = toplog "Init: initializing Etherlink" in - let () = toplog "Init: Getting Etherlink operator key" in - let etherlink_rollup_operator_key = - Option.get etherlink_rollup_operator_key - in - let () = toplog "Init: Getting allowed DAL slot indices" in - let dal_slots = - match etherlink_configuration.etherlink_dal_slots with - | [] -> None - | slots -> Some slots - in - let () = - toplog - "Init: Etherlink+DAL feature flag: %b" - (Option.is_some dal_slots) - in - let* etherlink = - let () = toplog "Init: calling init_etherlink" in - init_etherlink - cloud - configuration - etherlink_configuration - ~bootstrap - etherlink_rollup_operator_key - etherlink_batching_operator_keys - next_agent - ~dal_slots - ~tezlink:etherlink_configuration.tezlink - in - some etherlink - | None -> - let () = - toplog - "Init: skipping Etherlink initialization because --etherlink was \ - not given on the CLI" - in - none + Etherlink_helpers.init_etherlink + ~data_dir:configuration.data_dir + ~simulate_network:configuration.simulate_network + ~external_rpc:configuration.external_rpc + ~network:configuration.network + ~snapshot:configuration.snapshot + ~ppx_profiling:configuration.ppx_profiling + ~ppx_profiling_backends:configuration.ppx_profiling_backends + ~memtrace:configuration.memtrace + ~node_p2p_endpoint:bootstrap.node_p2p_endpoint + ~dal_node_p2p_endpoint:bootstrap.dal_node_p2p_endpoint + ~next_agent + ~cloud + etherlink_rollup_operator_key + etherlink_batching_operator_keys + etherlink_configuration in let some_node_rpc_endpoint = obtain_some_node_rpc_endpoint @@ -3907,13 +3315,14 @@ let register (module Cli : Scenarios_cli.Dal) = let etherlink = if etherlink then Some - { - etherlink_sequencer; - etherlink_producers; - etherlink_dal_slots; - chain_id = etherlink_chain_id; - tezlink; - } + Etherlink_helpers. + { + etherlink_sequencer; + etherlink_producers; + etherlink_dal_slots; + chain_id = etherlink_chain_id; + tezlink; + } else None in let blocks_history = Cli.blocks_history in diff --git a/tezt/tests/cloud/etherlink_helpers.ml b/tezt/tests/cloud/etherlink_helpers.ml new file mode 100644 index 000000000000..86ead99e14da --- /dev/null +++ b/tezt/tests/cloud/etherlink_helpers.ml @@ -0,0 +1,627 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Trilitech *) +(* *) +(*****************************************************************************) + +open Agent_kind +open Scenarios_helpers +open Tezos +open Yes_crypto + +type etherlink_configuration = { + etherlink_sequencer : bool; + etherlink_producers : int; + (* Empty list means DAL FF is set to false. *) + etherlink_dal_slots : int list; + chain_id : int option; + tezlink : bool; +} + +type etherlink_operator_setup = { + node : Node.t; + client : Client.t; + sc_rollup_node : Sc_rollup_node.t; + evm_node : Tezt_etherlink.Evm_node.t; + is_sequencer : bool; + sc_rollup_address : string; + account : Account.key; + batching_operators : Account.key list; +} + +type etherlink = { + configuration : etherlink_configuration; + operator : etherlink_operator_setup; + accounts : Tezt_etherlink.Eth_account.t Array.t; +} + +let total_operator_balance ~client ~operators = + List.fold_left + (fun acc operator -> + let* acc in + let* balance = + Client.get_full_balance_for + ~account:operator.Account.public_key_hash + client + in + return Tez.(acc + balance)) + (return Tez.zero) + operators + +let init_etherlink_operators ~client = function + | None -> Lwt.return ([], []) + | Some _ -> + let* etherlink_rollup_operator_key = + let () = toplog "Generating keys for Etherlink operator and batching" in + Client.stresstest_gen_keys ~alias_prefix:"etherlink_operator" 1 client + in + let* etherlink_batching_operator_keys = + Client.stresstest_gen_keys ~alias_prefix:"etherlink_batching" 20 client + in + return (etherlink_rollup_operator_key, etherlink_batching_operator_keys) + +let init_etherlink_dal_node ~external_rpc ~network ~snapshot ~ppx_profiling + ~ppx_profiling_backends ~memtrace ~simulate_network ~node_p2p_endpoint + ~dal_node_p2p_endpoint ~dal_slots ~next_agent ~otel ~cloud = + match dal_slots with + | [] -> + toplog "Etherlink will run without DAL support" ; + none + | [_] -> + (* On a single DAL slot index, we launch a single DAL node for + this index on a dedicated VM and give it directly as endpoint + to the rollup node. *) + toplog "Etherlink sequencer will run its own DAL node" ; + let name = name_of Etherlink_dal_operator in + let* agent = next_agent ~name in + let env, with_yes_crypto = may_set_yes_crypto_env simulate_network in + let* node = + Node_helpers.init + ?env + ~name + ~arguments: + [ + Peer node_p2p_endpoint; + refutation_game_minimal_rolling_history_mode; + ] + ~rpc_external:external_rpc + network + ~with_yes_crypto + ~snapshot + cloud + agent + in + let* dal_node = Dal_node.Agent.create ~name ~node cloud agent in + let* () = + Dal_node.init_config + ~expected_pow:(Network.expected_pow network) + ~operator_profiles:dal_slots + ~peers:(Option.to_list dal_node_p2p_endpoint) + dal_node + in + let* () = + Dal_node.Agent.run ?otel ~ppx_profiling ~ppx_profiling_backends dal_node + in + some dal_node + | _ :: _ :: _ -> + (* On several slot indices, we launch one observer DAL node per + slot index + an operator DAL node on no index + a reverse proxy + which is passed as endpoint to the rollup node. The operator + DAL node runs on the same VM than the reverse proxy, the + observer DAL nodes run on dedicated VMs. *) + toplog + "Etherlink will run with DAL support on indices %a" + (Format.pp_print_list + ~pp_sep:(fun out () -> Format.fprintf out ",") + Format.pp_print_int) + dal_slots ; + toplog "Etherlink sequencer will use a reverse proxy" ; + let name = name_of Etherlink_dal_operator in + let* agent = next_agent ~name in + let env, with_yes_crypto = may_set_yes_crypto_env simulate_network in + let* node = + Node_helpers.init + ?env + ~name + ~arguments: + [ + Peer node_p2p_endpoint; + refutation_game_minimal_rolling_history_mode; + ] + ~rpc_external:external_rpc + network + ~with_yes_crypto + ~snapshot + cloud + agent + in + let* default_dal_node = Dal_node.Agent.create ~name ~node cloud agent in + let* () = + Dal_node.init_config + ~expected_pow:(Network.expected_pow network) + ~peers:(Option.to_list dal_node_p2p_endpoint) + default_dal_node + in + let* () = + Dal_node.Agent.run + ?otel + ~memtrace + ~ppx_profiling + ~ppx_profiling_backends + default_dal_node + in + let default_endpoint = Dal_node.rpc_endpoint default_dal_node in + let* reverse_proxy_dal_node = + Dal_reverse_proxy.init_dal_reverse_proxy_observers + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~simulate_network + ~name_of:(fun slot_index -> + name_of (Etherlink_dal_observer {slot_index})) + ~default_endpoint:(Some default_endpoint) + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + ~dal_slots + ~next_agent + ~otel + ~cloud + in + some reverse_proxy_dal_node + +let init_etherlink_operator_setup cloud ~data_dir ~external_rpc ~network + ~snapshot ~ppx_profiling ~ppx_profiling_backends ~memtrace ~simulate_network + etherlink_configuration name ~node_p2p_endpoint ~dal_node_p2p_endpoint + ~dal_slots ~tezlink account batching_operators agent next_agent = + let chain_id = Option.value ~default:1 etherlink_configuration.chain_id in + let is_sequencer = etherlink_configuration.etherlink_sequencer in + let data_dir = data_dir |> Option.map (fun data_dir -> data_dir // name) in + let env, with_yes_crypto = may_set_yes_crypto_env simulate_network in + let* node = + Node_helpers.init + ?env + ?data_dir + ~name + ~arguments: + [Peer node_p2p_endpoint; refutation_game_minimal_rolling_history_mode] + ~rpc_external:external_rpc + network + ~with_yes_crypto + ~snapshot + cloud + agent + in + let endpoint = Client.Node node in + let* client = Client.Agent.create ~endpoint agent in + let () = toplog "Init Etherlink: importing the sequencer secret key" in + let* () = + Lwt_list.iter_s + (fun account -> + Client.import_secret_key + client + ~endpoint + account.Account.secret_key + ~alias:account.Account.alias) + (account :: batching_operators) + in + let l = Node.get_last_seen_level node in + let () = toplog "Init Etherlink: revealing the sequencer account" in + let* () = + Lwt_list.iter_s + (fun account -> + let*! () = Client.reveal client ~endpoint ~src:account.Account.alias in + unit) + (account :: batching_operators) + in + let () = toplog "Init Etherlink operator: waiting for level %d" (l + 2) in + let* _ = Node.wait_for_level node (l + 2) in + let () = toplog "Init Etherlink: waiting for level %d: done" (l + 2) in + (* A configuration is generated locally by the orchestrator. The resulting + kernel will be pushed to Etherlink. *) + let tezlink_config = Temp.file "l2-tezlink-config.yaml" in + let tez_bootstrap_accounts = Account.Bootstrap.keys |> Array.to_list in + let* () = + if tezlink then + let*! () = + Evm_node.make_l2_kernel_installer_config + ~chain_id + ~chain_family:"Michelson" + ~eth_bootstrap_accounts:[] + ~tez_bootstrap_accounts + ~output:tezlink_config + () + in + let* () = Process.spawn "cat" [tezlink_config] |> Process.check in + unit + else unit + in + let rollup_config = Temp.file "rollup-config.yaml" in + let eth_bootstrap_accounts = + Tezt_etherlink.Eth_account.bootstrap_accounts |> Array.to_list + |> List.map (fun account -> account.Tezt_etherlink.Eth_account.address) + in + let*! () = + let sequencer = if is_sequencer then Some account.public_key else None in + let () = toplog "Init Etherlink: configuring the kernel" in + Tezt_etherlink.Evm_node.make_kernel_installer_config + ?sequencer + ~eth_bootstrap_accounts + ~output:rollup_config + ~enable_dal:(Option.is_some dal_slots) + ~chain_id + ?dal_slots + ~enable_multichain:tezlink + ?l2_chain_ids:(if tezlink then Some [chain_id] else None) + () + in + let* () = Process.spawn "cat" [rollup_config] |> Process.check in + let otel = Cloud.open_telemetry_endpoint cloud in + let* dal_node = + init_etherlink_dal_node + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~simulate_network + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + ~next_agent + ~dal_slots:etherlink_configuration.etherlink_dal_slots + ~otel + ~cloud + in + let operators = + List.map + (fun account -> (Sc_rollup_node.Batching, account.Account.alias)) + batching_operators + in + let* sc_rollup_node = + Sc_rollup_node.Agent.create + ~name:(Format.asprintf "etherlink-%s-rollup-node" name) + ~base_dir:(Client.base_dir client) + ~default_operator:account.alias + ~operators + ?dal_node + cloud + agent + Operator + node + in + let preimages_dir = + Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" + in + let* output = + if tezlink then + let* remote_rollup_config = Agent.copy agent ~source:rollup_config in + let* remote_tezlink_config = Agent.copy agent ~source:tezlink_config in + let* {output; _} = + Sc_rollup_helpers.Agent + .prepare_installer_kernel_with_multiple_setup_file + ~configs:[remote_rollup_config; remote_tezlink_config] + ~preimages_dir + (Uses.path Constant.WASM.evm_kernel) + agent + in + return output + else + let* remote_rollup_config = Agent.copy agent ~source:rollup_config in + let* {output; _} = + Sc_rollup_helpers.Agent.prepare_installer_kernel + ~config:(`Path remote_rollup_config) + ~preimages_dir + Constant.WASM.evm_kernel + agent + in + return output + in + let pvm_kind = "wasm_2_0_0" in + let l = Node.get_last_seen_level node in + let () = toplog "Init Etherlink: originating the rollup" in + let* sc_rollup_address = + Sc_rollup_helpers.Agent.originate_sc_rollup + ~kind:pvm_kind + ~boot_sector:output + ~parameters_ty:Tezt_etherlink.Test_helpers.evm_type + ~src:account.alias + client + in + let () = toplog "Init Etherlink: waiting again, for level %d" (l + 2) in + let* _ = Node.wait_for_level node (l + 2) in + let () = toplog "Init Etherlink: waiting again, for level %d: done" (l + 2) in + let () = toplog "Init Etherlink: launching the rollup node" in + let* () = + Sc_rollup_node.run sc_rollup_node sc_rollup_address [Log_kernel_debug] + in + let () = toplog "Init Etherlink: launching the rollup node: done" in + let private_rpc_port = Agent.next_available_port agent |> Option.some in + let time_between_blocks = Some (Evm_node.Time_between_blocks 10.) in + let sequencer_mode = + Evm_node.Sequencer + { + initial_kernel = output; + preimage_dir = Some preimages_dir; + private_rpc_port; + time_between_blocks; + sequencer = account.alias; + genesis_timestamp = None; + max_blueprints_lag = Some 300; + max_blueprints_ahead = Some 2000; + max_blueprints_catchup = None; + catchup_cooldown = None; + max_number_of_chunks = None; + wallet_dir = Some (Client.base_dir client); + tx_pool_timeout_limit = None; + tx_pool_addr_limit = None; + tx_pool_tx_per_addr_limit = None; + dal_slots; + sequencer_sunset_sec = None; + } + in + let endpoint = Sc_rollup_node.endpoint sc_rollup_node in + let mode = if is_sequencer then sequencer_mode else Evm_node.Proxy in + let () = toplog "Init Etherlink: launching the EVM node" in + let* evm_node = + Tezos.Evm_node.Agent.init + ~patch_config:(fun json -> + JSON.update + "public_rpc" + (fun json -> + JSON.update + "cors_headers" + (fun _ -> + JSON.annotate ~origin:"patch-config:cors_headers" + @@ `A [`String "*"]) + json + |> JSON.update "cors_origins" (fun _ -> + JSON.annotate ~origin:"patch-config:cors_origins" + @@ `A [`String "*"])) + json + |> Evm_node.patch_config_with_experimental_feature + ~drop_duplicate_when_injection:true + ~blueprints_publisher_order_enabled:true + ~rpc_server:Resto + ~spawn_rpc:(Port.fresh ()) + ?l2_chains: + (if tezlink then + Some + [ + { + (Evm_node.default_l2_setup ~l2_chain_id:chain_id) with + l2_chain_family = "Michelson"; + tez_bootstrap_accounts = Some tez_bootstrap_accounts; + }; + ] + else None) + ()) + ~name:(Format.asprintf "etherlink-%s-evm-node" name) + ~mode + endpoint + cloud + agent + in + let () = toplog "Init Etherlink: launching the EVM node: done" in + let operator = + { + node; + client; + sc_rollup_node; + evm_node; + is_sequencer; + account; + batching_operators; + sc_rollup_address; + } + in + let* () = + add_prometheus_source + ?dal_node + ~node + ~sc_rollup_node + ~evm_node + cloud + agent + (Format.asprintf "etherlink-%s" name) + in + return operator + +let init_etherlink_producer_setup operator name ~node_p2p_endpoint ~rpc_external + cloud agent = + let* node = + Node.Agent.init + ~rpc_external + ~name:(Format.asprintf "etherlink-%s-node" name) + ~arguments:[Peer node_p2p_endpoint; Synchronisation_threshold 0] + cloud + agent + in + let endpoint = Client.Node node in + let* client = Client.Agent.create ~endpoint agent in + let l = Node.get_last_seen_level node in + let* _ = Node.wait_for_level node (l + 2) in + (* A configuration is generated locally by the orchestrator. The resulting + kernel will be pushed to Etherlink. *) + let output_config = Temp.file "config.yaml" in + let eth_bootstrap_accounts = + Tezt_etherlink.Eth_account.bootstrap_accounts |> Array.to_list + |> List.map (fun account -> account.Tezt_etherlink.Eth_account.address) + in + let*! () = + let sequencer = + if operator.is_sequencer then Some operator.account.public_key else None + in + Tezt_etherlink.Evm_node.make_kernel_installer_config + ?sequencer + ~eth_bootstrap_accounts + ~output:output_config + () + in + let* sc_rollup_node = + Sc_rollup_node.Agent.create + ~name:(Format.asprintf "etherlink-%s-rollup-node" name) + ~base_dir:(Client.base_dir client) + cloud + agent + Observer + node + in + let preimages_dir = + Filename.concat (Sc_rollup_node.data_dir sc_rollup_node) "wasm_2_0_0" + in + let* remote_output_config = Agent.copy agent ~source:output_config in + let* {output; _} = + Sc_rollup_helpers.Agent.prepare_installer_kernel + ~config:(`Path remote_output_config) + ~preimages_dir + Constant.WASM.evm_kernel + agent + in + let* () = + Sc_rollup_node.run + sc_rollup_node + operator.sc_rollup_address + [Log_kernel_debug] + in + let mode = + Evm_node.Observer + { + private_rpc_port = None; + initial_kernel = output; + preimages_dir = Some preimages_dir; + rollup_node_endpoint = Sc_rollup_node.endpoint sc_rollup_node; + } + in + let () = toplog "Init Etherlink: init producer %s" name in + let endpoint = Evm_node.endpoint operator.evm_node in + (* TODO: try using this local EVM node for Floodgate confirmations. *) + let* evm_node = + Evm_node.Agent.init + ~name:(Format.asprintf "etherlink-%s-evm-node" name) + ~mode + endpoint + cloud + agent + in + let* () = + (* This is to avoid producing operations too soon. *) + Evm_node.wait_for_blueprint_applied evm_node 1 + in + (* Launch floodgate *) + let* () = + Floodgate.Agent.run + ~rpc_endpoint:endpoint + ~max_active_eoa:210 + ~max_transaction_batch_length:70 + ~tick_interval:1.0 + ~controller:Tezt_etherlink.Eth_account.bootstrap_accounts.(0) + ~base_fee_factor:1000.0 + cloud + agent + in + Lwt.return_unit + +let init_etherlink cloud ~data_dir ~external_rpc ~network ~snapshot + ~ppx_profiling ~ppx_profiling_backends ~memtrace ~simulate_network + etherlink_configuration ~node_p2p_endpoint ~dal_node_p2p_endpoint + etherlink_rollup_operator_key batching_operators ~dal_slots ~tezlink + next_agent = + let () = toplog "Initializing an Etherlink operator" in + let name = name_of Etherlink_operator in + let* operator_agent = next_agent ~name in + let* operator = + init_etherlink_operator_setup + cloud + ~data_dir + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~simulate_network + etherlink_configuration + "operator" + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + ~dal_slots + ~tezlink + etherlink_rollup_operator_key + batching_operators + operator_agent + next_agent + in + let accounts = Tezt_etherlink.Eth_account.bootstrap_accounts in + let* producers_agents = + List.init etherlink_configuration.etherlink_producers (fun i -> + let name = name_of (Etherlink_producer i) in + next_agent ~name) + |> Lwt.all + in + let* () = + producers_agents + |> List.mapi (fun i agent -> + assert (i < Array.length accounts) ; + init_etherlink_producer_setup + operator + (Format.asprintf "producer-%d" i) + ~node_p2p_endpoint + ~rpc_external:external_rpc + cloud + agent) + |> Lwt.join + in + return {configuration = etherlink_configuration; operator; accounts} + +let init_etherlink ~data_dir ~simulate_network ~external_rpc ~network ~snapshot + ~ppx_profiling ~ppx_profiling_backends ~memtrace ~node_p2p_endpoint + ~dal_node_p2p_endpoint ~next_agent ~cloud etherlink_rollup_operator_key + etherlink_batching_operator_keys = function + | Some etherlink_configuration -> + let () = toplog "Init: initializing Etherlink" in + let () = toplog "Init: Getting Etherlink operator key" in + let etherlink_rollup_operator_key = + Option.get etherlink_rollup_operator_key + in + let () = toplog "Init: Getting allowed DAL slot indices" in + let dal_slots = + match etherlink_configuration.etherlink_dal_slots with + | [] -> None + | slots -> Some slots + in + let () = + toplog "Init: Etherlink+DAL feature flag: %b" (Option.is_some dal_slots) + in + let* etherlink = + let () = toplog "Init: calling init_etherlink" in + init_etherlink + cloud + ~data_dir + ~external_rpc + ~network + ~snapshot + ~ppx_profiling + ~ppx_profiling_backends + ~memtrace + ~simulate_network + etherlink_configuration + ~node_p2p_endpoint + ~dal_node_p2p_endpoint + etherlink_rollup_operator_key + etherlink_batching_operator_keys + next_agent + ~dal_slots + ~tezlink:etherlink_configuration.tezlink + in + some etherlink + | None -> + let () = + toplog + "Init: skipping Etherlink initialization because --etherlink was not \ + given on the CLI" + in + none diff --git a/tezt/tests/cloud/etherlink_helpers.mli b/tezt/tests/cloud/etherlink_helpers.mli new file mode 100644 index 000000000000..c4fc3dcf5370 --- /dev/null +++ b/tezt/tests/cloud/etherlink_helpers.mli @@ -0,0 +1,63 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Trilitech *) +(* *) +(*****************************************************************************) + +(** This module provides helpers for initializing Etherlink rollup operators, producers + and DAL integration within Tezt-cloud scenarios. +*) + +type etherlink_configuration = { + etherlink_sequencer : bool; + etherlink_producers : int; + etherlink_dal_slots : int list; + chain_id : int option; + tezlink : bool; +} + +type etherlink_operator_setup = { + node : Node.t; + client : Client.t; + sc_rollup_node : Sc_rollup_node.t; + evm_node : Tezt_etherlink.Evm_node.t; + is_sequencer : bool; + sc_rollup_address : string; + account : Account.key; + batching_operators : Account.key list; +} + +type etherlink = { + configuration : etherlink_configuration; + operator : etherlink_operator_setup; + accounts : Tezt_etherlink.Eth_account.t Array.t; +} + +(** Compute the combined Tez balance of all the given Etherlink operator accounts. *) +val total_operator_balance : + client:Client.t -> operators:Account.key list -> Tez.t Lwt.t + +(** Initialize the operator and batching accounts required for Etherlink. *) +val init_etherlink_operators : + client:Client.t -> 'a option -> (Account.key list * Account.key list) Lwt.t + +(** Initialize the Etherlink rollup stack which includes the EVM rollup node, + DAL if enabled and a configurable number of producers. *) +val init_etherlink : + data_dir:string option -> + simulate_network:Scenarios_cli.network_simulation_config -> + external_rpc:bool -> + network:Network.t -> + snapshot:Snapshot_helpers.t -> + ppx_profiling:bool -> + ppx_profiling_backends:string list -> + memtrace:bool -> + node_p2p_endpoint:string -> + dal_node_p2p_endpoint:string option -> + next_agent:(name:string -> Agent.t Lwt.t) -> + cloud:Cloud.t -> + Account.key option -> + Account.key list -> + etherlink_configuration option -> + etherlink option Lwt.t -- GitLab From 4becc6d4d046d3a1b7f852b238958f1a4a2b9267 Mon Sep 17 00:00:00 2001 From: Gabriel Moise Date: Mon, 21 Jul 2025 11:45:29 +0100 Subject: [PATCH 3/3] Tezt_cloud: Update copyrights --- tezt/tests/cloud/echo_rollup.ml | 1 + tezt/tests/cloud/echo_rollup.mli | 1 + tezt/tests/cloud/etherlink_helpers.ml | 1 + tezt/tests/cloud/etherlink_helpers.mli | 1 + 4 files changed, 4 insertions(+) diff --git a/tezt/tests/cloud/echo_rollup.ml b/tezt/tests/cloud/echo_rollup.ml index 0c70309baa94..2071b47f5308 100644 --- a/tezt/tests/cloud/echo_rollup.ml +++ b/tezt/tests/cloud/echo_rollup.ml @@ -1,6 +1,7 @@ (*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) (* Copyright (c) 2025 Trilitech *) (* *) (*****************************************************************************) diff --git a/tezt/tests/cloud/echo_rollup.mli b/tezt/tests/cloud/echo_rollup.mli index 666200989975..89e779ba7adb 100644 --- a/tezt/tests/cloud/echo_rollup.mli +++ b/tezt/tests/cloud/echo_rollup.mli @@ -1,6 +1,7 @@ (*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) (* Copyright (c) 2025 Trilitech *) (* *) (*****************************************************************************) diff --git a/tezt/tests/cloud/etherlink_helpers.ml b/tezt/tests/cloud/etherlink_helpers.ml index 86ead99e14da..69d30a81cc8d 100644 --- a/tezt/tests/cloud/etherlink_helpers.ml +++ b/tezt/tests/cloud/etherlink_helpers.ml @@ -1,6 +1,7 @@ (*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) (* Copyright (c) 2025 Trilitech *) (* *) (*****************************************************************************) diff --git a/tezt/tests/cloud/etherlink_helpers.mli b/tezt/tests/cloud/etherlink_helpers.mli index c4fc3dcf5370..5360f417c845 100644 --- a/tezt/tests/cloud/etherlink_helpers.mli +++ b/tezt/tests/cloud/etherlink_helpers.mli @@ -1,6 +1,7 @@ (*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) (* Copyright (c) 2025 Trilitech *) (* *) (*****************************************************************************) -- GitLab