diff --git a/etherlink/CHANGES_NODE.md b/etherlink/CHANGES_NODE.md index eaf752727cc180c06a10c306f85e82f84f6529d1..493b8426dd2523f7df00c37777d631dbaefcf595 100644 --- a/etherlink/CHANGES_NODE.md +++ b/etherlink/CHANGES_NODE.md @@ -7,6 +7,9 @@ ### Configuration changes - Fix encodings for sequencer key when using a remote key on GCP KMS. (!18642) +- Add a new configuration option for the sequencer to stop block production + ahead of a sequencer upgrade. See `describe config` or `man run sequencer`. + (!18605) ### RPCs changes @@ -18,6 +21,8 @@ - Add a new command `show gcp key` which can be used to retrieve Tezos-specific (b58-encoded public key, Tezos implicit account) and Ethereum-specific (EOA address) information about a key stored in a GCP KMS. (!18617 !18618) +- Halt blocks production five minutes before a planned upgrade of the sequencer + operator. (!18605) ### Storage changes diff --git a/etherlink/bin_node/config/configuration.ml b/etherlink/bin_node/config/configuration.ml index 5b912addb47eee46750a968c4a4f927b515982cf..26c767548040fd6a3242564d9eccf7120fd89cc8 100644 --- a/etherlink/bin_node/config/configuration.ml +++ b/etherlink/bin_node/config/configuration.ml @@ -13,8 +13,13 @@ let pp_supported_network fmt network = fmt (match network with Mainnet -> "mainnet" | Testnet -> "testnet") +let positive_encoding = Data_encoding.ranged_int 0 ((1 lsl 30) - 1) + let strictly_positive_encoding = Data_encoding.ranged_int 1 ((1 lsl 30) - 1) +let positive_i64_encoding = + Data_encoding.(conv Int64.to_int Int64.of_int positive_encoding) + type log_filter_config = { max_nb_blocks : int; max_nb_logs : int; @@ -194,6 +199,7 @@ type sequencer = { max_number_of_chunks : int; sequencer : sequencer_key option; blueprints_publisher_config : blueprints_publisher_config; + sunset_sec : int64; } type gcp_authentication_method = Gcloud_auth | Metadata_server @@ -519,9 +525,11 @@ let kernel_execution_config_dft ~data_dir ?preimages ?preimages_endpoint native_execution_policy; } +let default_sequencer_sunset_sec = 300L + let sequencer_config_dft ?time_between_blocks ?max_number_of_chunks ?sequencer ?max_blueprints_lag ?max_blueprints_ahead ?max_blueprints_catchup - ?catchup_cooldown ?dal_slots () = + ?catchup_cooldown ?dal_slots ?sunset_sec () = let default_blueprints_publisher_config = if Option.is_some dal_slots then default_blueprints_publisher_config_with_dal @@ -555,6 +563,7 @@ let sequencer_config_dft ?time_between_blocks ?max_number_of_chunks ?sequencer Option.value ~default:default_max_number_of_chunks max_number_of_chunks; sequencer; blueprints_publisher_config; + sunset_sec = Option.value ~default:default_sequencer_sunset_sec sunset_sec; } let observer_evm_node_endpoint = function @@ -774,22 +783,26 @@ let sequencer_encoding = max_number_of_chunks; sequencer; blueprints_publisher_config; + sunset_sec; } -> ( time_between_blocks, max_number_of_chunks, sequencer, - blueprints_publisher_config )) + blueprints_publisher_config, + sunset_sec )) (fun ( time_between_blocks, max_number_of_chunks, sequencer, - blueprints_publisher_config ) -> + blueprints_publisher_config, + sunset_sec ) -> { time_between_blocks; max_number_of_chunks; sequencer; blueprints_publisher_config; + sunset_sec; }) - (obj4 + (obj5 time_between_blocks_field max_number_of_chunks_field (opt @@ -799,7 +812,14 @@ let sequencer_encoding = (dft "blueprints_publisher_config" blueprints_publisher_config_encoding - default_blueprints_publisher_config)) + default_blueprints_publisher_config) + (dft + ~description: + "Number of seconds prior to a sequencer operator upgrade before \ + which the current sequencer stops producing blocks" + "sunset_sec" + positive_i64_encoding + default_sequencer_sunset_sec)) let observer_encoding ?network () = let open Data_encoding in @@ -1934,7 +1954,7 @@ module Cli = struct ?log_filter_max_nb_logs ?log_filter_chunk_size ?max_blueprints_lag ?max_blueprints_ahead ?max_blueprints_catchup ?catchup_cooldown ?restricted_rpcs ?finalized_view ?proxy_ignore_block_param ?history_mode - ?dal_slots configuration = + ?dal_slots ?sunset_sec configuration = let public_rpc = patch_rpc ?rpc_addr @@ -1994,6 +2014,9 @@ module Cli = struct Option.either dal_slots blueprints_publisher_config.dal_slots; } in + let sunset_sec = + Option.value ~default:sequencer_config.sunset_sec sunset_sec + in { time_between_blocks = Option.value @@ -2005,6 +2028,7 @@ module Cli = struct max_number_of_chunks; sequencer = Option.either sequencer_key sequencer_config.sequencer; blueprints_publisher_config; + sunset_sec; } in let observer = @@ -2121,7 +2145,8 @@ module Cli = struct ?log_filter_max_nb_blocks ?log_filter_max_nb_logs ?log_filter_chunk_size ?max_blueprints_lag ?max_blueprints_ahead ?max_blueprints_catchup ?catchup_cooldown ?restricted_rpcs ?finalized_view - ?proxy_ignore_block_param ?dal_slots ?network ?history_mode () = + ?proxy_ignore_block_param ?dal_slots ?network ?history_mode ?sunset_sec () + = default ~data_dir ?network ?evm_node_endpoint () |> patch_configuration_from_args ?rpc_addr @@ -2158,6 +2183,7 @@ module Cli = struct ?proxy_ignore_block_param ?dal_slots ?history_mode + ?sunset_sec let create_or_read_config ~data_dir ?rpc_addr ?rpc_port ?rpc_batch_limit ?cors_origins ?cors_headers ?enable_websocket ?tx_pool_timeout_limit @@ -2169,7 +2195,7 @@ module Cli = struct ?max_blueprints_ahead ?max_blueprints_catchup ?catchup_cooldown ?log_filter_max_nb_blocks ?log_filter_max_nb_logs ?log_filter_chunk_size ?restricted_rpcs ?finalized_view ?proxy_ignore_block_param ?dal_slots - ?network ?history_mode config_file = + ?network ?history_mode ?sunset_sec config_file = let open Lwt_result_syntax in let open Filename.Infix in (* Check if the data directory of the evm node is not the one of Octez @@ -2225,6 +2251,7 @@ module Cli = struct ?proxy_ignore_block_param ?history_mode ?dal_slots + ?sunset_sec configuration in return configuration @@ -2267,6 +2294,7 @@ module Cli = struct ?dal_slots ?network ?history_mode + ?sunset_sec () in return config diff --git a/etherlink/bin_node/config/configuration.mli b/etherlink/bin_node/config/configuration.mli index 3e199196cb0fa9a8887350cb78d481eb8a6c2e5e..20a53c47b5979d70e6eadfa8d59c75b1d9471275 100644 --- a/etherlink/bin_node/config/configuration.mli +++ b/etherlink/bin_node/config/configuration.mli @@ -147,6 +147,7 @@ type sequencer = { (** The maximum number of chunks per blueprints. *) sequencer : sequencer_key option; (** The key used to sign the blueprints. *) blueprints_publisher_config : blueprints_publisher_config; + sunset_sec : int64; } type observer = {evm_node_endpoint : Uri.t; rollup_node_tracking : bool} @@ -332,6 +333,7 @@ module Cli : sig ?dal_slots:int list -> ?network:supported_network -> ?history_mode:history_mode -> + ?sunset_sec:int64 -> unit -> t @@ -370,6 +372,7 @@ module Cli : sig ?proxy_ignore_block_param:bool -> ?history_mode:history_mode -> ?dal_slots:int list -> + ?sunset_sec:int64 -> t -> t @@ -410,6 +413,7 @@ module Cli : sig ?dal_slots:int list -> ?network:supported_network -> ?history_mode:history_mode -> + ?sunset_sec:int64 -> string -> t tzresult Lwt.t end diff --git a/etherlink/bin_node/lib_dev/block_producer.ml b/etherlink/bin_node/lib_dev/block_producer.ml index 814a0d4d6fd99de7063bb145cb9ab5a9d834836f..067ec9c169f645ad77f4444d2c0a02ecb197e2d9 100644 --- a/etherlink/bin_node/lib_dev/block_producer.ml +++ b/etherlink/bin_node/lib_dev/block_producer.ml @@ -10,6 +10,7 @@ type parameters = { smart_rollup_address : string; maximum_number_of_chunks : int; tx_container : Services_backend_sig.ex_tx_container; + sequencer_sunset_sec : int64; } (* The size of a delayed transaction is overapproximated to the maximum size @@ -60,7 +61,14 @@ let minimum_ethereum_transaction_size = module Types = struct type nonrec parameters = parameters - type state = parameters + type state = { + signer : Signer.t; + smart_rollup_address : string; + maximum_number_of_chunks : int; + tx_container : Services_backend_sig.ex_tx_container; + sequencer_sunset_sec : int64; + mutable sunset : bool; + } end module Name = struct @@ -364,55 +372,69 @@ let head_info_and_delayed_transactions ~with_delayed_transactions Sequencer_blueprint.maximum_usable_space_in_blueprint maximum_number_of_chunks ) in - let*! head_info = Evm_context.head_info () in - return (head_info, delayed_hashes, remaining_cumulative_size) + return (delayed_hashes, remaining_cumulative_size) -let produce_block (type f) ~signer ~smart_rollup_address ~force ~timestamp - ~maximum_number_of_chunks ~with_delayed_transactions - ~(tx_container : f Services_backend_sig.tx_container) = +let produce_block (type f) (state : Types.state) ~force ~timestamp + ~with_delayed_transactions = let open Lwt_result_syntax in - let (module Tx_container) = - Services_backend_sig.tx_container_module tx_container - in - let* is_locked = Tx_container.is_locked () in - if is_locked then - let*! () = Block_producer_events.production_locked () in - return `No_block - else - let* head_info, delayed_hashes, remaining_cumulative_size = - head_info_and_delayed_transactions - ~with_delayed_transactions - maximum_number_of_chunks - in - let is_going_to_upgrade = - match head_info.pending_upgrade with - | Some Evm_events.Upgrade.{hash = _; timestamp = upgrade_timestamp} -> - timestamp >= upgrade_timestamp - | None -> false - in - if is_going_to_upgrade then - let* hashes = - produce_block_with_transactions - ~signer - ~timestamp - ~smart_rollup_address - ~transactions_and_objects:None - ~delayed_hashes:[] - ~tx_container - head_info + match state.tx_container with + | Ex_tx_container tx_container -> + let (module Tx_container) = + Services_backend_sig.tx_container_module tx_container in - (* (Seq.length hashes) is always zero, this is only to be "future" proof *) - return (`Block_produced (Seq.length hashes)) - else - produce_block_if_needed - ~signer - ~timestamp - ~smart_rollup_address - ~force - ~delayed_hashes - ~remaining_cumulative_size - ~tx_container - head_info + let*! head_info = Evm_context.head_info () in + let* () = + when_ (not state.sunset) @@ fun () -> + match head_info.pending_sequencer_upgrade with + | Some Evm_events.Sequencer_upgrade.{timestamp = upgrade_timestamp; _} + when Time.Protocol.( + add timestamp state.sequencer_sunset_sec >= upgrade_timestamp + && timestamp < upgrade_timestamp) -> + (* We stop producing blocks ahead of the upgrade *) + let*! () = Block_producer_events.sunset () in + state.sunset <- true ; + Tx_container.lock_transactions () + | _ -> return_unit + in + let* is_locked = Tx_container.is_locked () in + if is_locked then + let*! () = Block_producer_events.production_locked () in + return `No_block + else + let* delayed_hashes, remaining_cumulative_size = + head_info_and_delayed_transactions + ~with_delayed_transactions + state.maximum_number_of_chunks + in + let is_going_to_upgrade_kernel = + match head_info.pending_upgrade with + | Some Evm_events.Upgrade.{hash = _; timestamp = upgrade_timestamp} -> + timestamp >= upgrade_timestamp + | None -> false + in + if is_going_to_upgrade_kernel then + let* hashes = + produce_block_with_transactions + ~signer:state.signer + ~timestamp + ~smart_rollup_address:state.smart_rollup_address + ~transactions_and_objects:None + ~delayed_hashes:[] + ~tx_container + head_info + in + (* (Seq.length hashes) is always zero, this is only to be "future" proof *) + return (`Block_produced (Seq.length hashes)) + else + produce_block_if_needed + ~signer:state.signer + ~timestamp + ~smart_rollup_address:state.smart_rollup_address + ~force + ~delayed_hashes + ~remaining_cumulative_size + ~tx_container + head_info module Handlers = struct type self = worker @@ -426,27 +448,21 @@ module Handlers = struct match request with | Request.Produce_block (with_delayed_transactions, timestamp, force) -> protect @@ fun () -> - let { - signer; - smart_rollup_address; - maximum_number_of_chunks; - tx_container = Ex_tx_container tx_container; - } = - state - in - produce_block - ~signer - ~smart_rollup_address - ~force - ~timestamp - ~maximum_number_of_chunks - ~with_delayed_transactions - ~tx_container + produce_block state ~force ~timestamp ~with_delayed_transactions type launch_error = error trace let on_launch _w () (parameters : Types.parameters) = - Lwt_result_syntax.return parameters + Lwt_result_syntax.return + Types. + { + sunset = false; + signer = parameters.signer; + smart_rollup_address = parameters.smart_rollup_address; + maximum_number_of_chunks = parameters.maximum_number_of_chunks; + tx_container = parameters.tx_container; + sequencer_sunset_sec = parameters.sequencer_sunset_sec; + } let on_error (type a b) _w _st (_r : (a, b) Request.t) (_errs : b) : [`Continue | `Shutdown] tzresult Lwt.t = diff --git a/etherlink/bin_node/lib_dev/block_producer.mli b/etherlink/bin_node/lib_dev/block_producer.mli index e8ac69f6215083026e2dea959969af5a4d95d5ac..e6a81acc88d5aebc6e47970cf7c392ebe655cbae 100644 --- a/etherlink/bin_node/lib_dev/block_producer.mli +++ b/etherlink/bin_node/lib_dev/block_producer.mli @@ -10,6 +10,7 @@ type parameters = { smart_rollup_address : string; maximum_number_of_chunks : int; tx_container : Services_backend_sig.ex_tx_container; + sequencer_sunset_sec : int64; } (** [start parameters] starts the events follower. *) diff --git a/etherlink/bin_node/lib_dev/block_producer_events.ml b/etherlink/bin_node/lib_dev/block_producer_events.ml index 2cb27a80a61fa84ceca431b9750436d036b284ed..923b803e8277f3599221632710992d50029218b8 100644 --- a/etherlink/bin_node/lib_dev/block_producer_events.ml +++ b/etherlink/bin_node/lib_dev/block_producer_events.ml @@ -53,6 +53,14 @@ module Event = struct Format.fprintf fmt "%10s" h) ("tx_hash", Ethereum_types.hash_encoding) ("error", Data_encoding.string) + + let sunset = + declare_0 + ~section + ~name:"block_production_sunset" + ~msg:"block production is sunset ahead of the change of sequencer" + ~level:Notice + () end let transaction_selected ~hash = @@ -66,3 +74,5 @@ let production_locked () = Internal_event.Simple.emit Event.production_locked () let transaction_rejected tx_hash error = Internal_event.Simple.emit Event.transaction_rejected (tx_hash, error) + +let sunset () = Internal_event.Simple.emit Event.sunset () diff --git a/etherlink/bin_node/lib_dev/sequencer.ml b/etherlink/bin_node/lib_dev/sequencer.ml index 394530ca0e0c73834704b50208d71bbffca774a4..fd1fb58a59f62cd169b33b02d613e325bec828fb 100644 --- a/etherlink/bin_node/lib_dev/sequencer.ml +++ b/etherlink/bin_node/lib_dev/sequencer.ml @@ -405,6 +405,7 @@ let main ~data_dir ~cctxt ?signer ?(genesis_timestamp = Misc.now ()) smart_rollup_address = smart_rollup_address_b58; maximum_number_of_chunks = sequencer_config.max_number_of_chunks; tx_container = Ex_tx_container tx_container; + sequencer_sunset_sec = sequencer_config.sunset_sec; } in let* () = diff --git a/etherlink/bin_node/main.ml b/etherlink/bin_node/main.ml index ee3a177c8a611b230e31488b8622afe0714c6f0a..d89ccadfa2de9033c182770bed6f4effee457448 100644 --- a/etherlink/bin_node/main.ml +++ b/etherlink/bin_node/main.ml @@ -324,6 +324,16 @@ let catchup_cooldown_arg = resending its blueprints before trying to catch-up again." Params.int +let sunset_sec_arg = + Tezos_clic.arg + ~env:(config_env "SEQUENCER_SUNSET_SEC") + ~long:"sunset-sec" + ~placeholder:"SEC" + ~doc: + "Number of seconds prior to a sequencer operator upgrade before which \ + the current sequencer stops producing blocks" + Params.int64 + let cors_allowed_headers_arg = Tezos_clic.arg ~long:"cors-headers" @@ -1090,7 +1100,7 @@ let start_sequencer ~wallet_ctxt ~data_dir ?sequencer_key ?rpc_addr ?rpc_port ?max_blueprints_ahead ?max_blueprints_catchup ?catchup_cooldown ?log_filter_max_nb_blocks ?log_filter_max_nb_logs ?log_filter_chunk_size ?genesis_timestamp ?restricted_rpcs ?kernel ?dal_slots ?sandbox_config - ~finalized_view config_file = + ~finalized_view ?sunset_sec config_file = let open Lwt_result_syntax in let* configuration = Cli.create_or_read_config @@ -1125,6 +1135,7 @@ let start_sequencer ~wallet_ctxt ~data_dir ?sequencer_key ?rpc_addr ?rpc_port ?restricted_rpcs ?dal_slots ~finalized_view + ?sunset_sec config_file in let*! () = init_logs ~daily_logs:true ~data_dir configuration in @@ -1872,7 +1883,7 @@ let init_config_command = then adds the configuration for the observer mode." (merge_options common_config_args - (args18 + (args19 (* sequencer and observer config*) preimages_arg preimages_endpoint_arg @@ -1900,7 +1911,8 @@ let init_config_command = ~why: "If set, some configuration options defaults to well-known \ values for the selected network." - ()))) + ()) + sunset_sec_arg)) (prefixes ["init"; "config"] @@ stop) (fun ( ( data_dir, config_file, @@ -1941,7 +1953,8 @@ let init_config_command = wallet_dir, force, dal_slots, - network ) ) + network, + sequencer_sunset_sec ) ) () -> let config_file = config_filename ~data_dir config_file in Evm_node_lib_dev.Data_dir.use ~data_dir @@ fun () -> @@ -1995,6 +2008,7 @@ let init_config_command = ~finalized_view ?network ?history_mode + ?sunset_sec:sequencer_sunset_sec config_file in let*! () = init_logs ~daily_logs:false ~data_dir config in @@ -2403,7 +2417,7 @@ let proxy_command = ()) let sequencer_config_args = - Tezos_clic.args15 + Tezos_clic.args16 preimages_arg preimages_endpoint_arg time_between_blocks_arg @@ -2419,6 +2433,7 @@ let sequencer_config_args = wallet_dir_arg (Client_config.password_filename_arg ()) dal_slots_arg + sunset_sec_arg let fund_arg = let long = "fund" in @@ -2494,7 +2509,8 @@ let sequencer_command = kernel, wallet_dir, password_filename, - dal_slots ) ) + dal_slots, + sunset_sec ) ) () -> let open Lwt_result_syntax in let* restricted_rpcs = @@ -2541,6 +2557,7 @@ let sequencer_command = ?kernel ?dal_slots ~finalized_view + ?sunset_sec config_file) let sandbox_command = diff --git a/etherlink/tezt/lib/evm_node.ml b/etherlink/tezt/lib/evm_node.ml index 996d6c62db9cb29e0afd30da318371f87a2605bd..b01cd705be746e743901a02b5f1fcc6fd183816f 100644 --- a/etherlink/tezt/lib/evm_node.ml +++ b/etherlink/tezt/lib/evm_node.ml @@ -90,6 +90,7 @@ type mode = tx_pool_addr_limit : int option; tx_pool_tx_per_addr_limit : int option; dal_slots : int list option; + sequencer_sunset_sec : int option; } | Sandbox of { initial_kernel : string; @@ -729,6 +730,7 @@ let mode_with_new_private_rpc (mode : mode) = tx_pool_addr_limit; tx_pool_tx_per_addr_limit; dal_slots; + sequencer_sunset_sec; } -> Sequencer { @@ -748,6 +750,7 @@ let mode_with_new_private_rpc (mode : mode) = tx_pool_addr_limit; tx_pool_tx_per_addr_limit; dal_slots; + sequencer_sunset_sec; } | Sandbox { @@ -1045,6 +1048,7 @@ let spawn_init_config ?(extra_arguments = []) evm_node = tx_pool_addr_limit; tx_pool_tx_per_addr_limit; dal_slots; + sequencer_sunset_sec; } -> [ "--rollup-node-endpoint"; @@ -1095,6 +1099,7 @@ let spawn_init_config ?(extra_arguments = []) evm_node = "dal-slots" (fun l -> String.concat "," (List.map string_of_int l)) dal_slots + @ Cli_arg.optional_arg "sunset-sec" string_of_int sequencer_sunset_sec | Sandbox { initial_kernel = _; diff --git a/etherlink/tezt/lib/evm_node.mli b/etherlink/tezt/lib/evm_node.mli index 843e7ec73b7207776a8f7a853f2f4b131e7b8d09..2a7ba4e20f33c3c18a096f70d56403e17aaff0b7 100644 --- a/etherlink/tezt/lib/evm_node.mli +++ b/etherlink/tezt/lib/evm_node.mli @@ -90,6 +90,7 @@ type mode = (** --tx-pool-tx-per-addr-limit: maximum transaction per address allowed simultaneously inside the pool. *) dal_slots : int list option; + sequencer_sunset_sec : int option; } | Sandbox of { initial_kernel : string; diff --git a/etherlink/tezt/lib/setup.ml b/etherlink/tezt/lib/setup.ml index 63d208139e2c2923a5c385a43d0e9c7f4f38b933..bd941cdf6310733d5bf6141b3d6a69988f2ef71d 100644 --- a/etherlink/tezt/lib/setup.ml +++ b/etherlink/tezt/lib/setup.ml @@ -480,7 +480,7 @@ let setup_sequencer_internal ?max_delayed_inbox_blueprint_length ?(blueprints_publisher_order_enabled = true) ?rollup_history_mode ~enable_dal ?dal_slots ~enable_multichain ~l2_chains ?rpc_server ?websockets ?history_mode ?enable_tx_queue ?spawn_rpc ?periodic_snapshot_path - ?(signatory = false) protocol = + ?(signatory = false) ?(sequencer_sunset_sec = 0) protocol = let* node, client = setup_l1 ?commitment_period @@ -653,6 +653,7 @@ let setup_sequencer_internal ?max_delayed_inbox_blueprint_length tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots; + sequencer_sunset_sec = Some sequencer_sunset_sec; } in let* sequencer = @@ -727,7 +728,8 @@ let setup_sequencer ?max_delayed_inbox_blueprint_length ?next_wasm_runtime ?drop_duplicate_when_injection ?blueprints_publisher_order_enabled ?rollup_history_mode ~enable_dal ?dal_slots ~enable_multichain ?rpc_server ?websockets ?history_mode ?enable_tx_queue ?spawn_rpc - ?periodic_snapshot_path ?signatory ?l2_chains protocol = + ?periodic_snapshot_path ?signatory ?l2_chains ?sequencer_sunset_sec protocol + = (* Note that the chain_id is not important (it will become important later) *) let l2_chains = Option.value @@ -786,6 +788,7 @@ let setup_sequencer ?max_delayed_inbox_blueprint_length ?next_wasm_runtime ?spawn_rpc ?periodic_snapshot_path ?signatory + ?sequencer_sunset_sec protocol in return (multichain_setup_to_single ~setup:sequencer_setup) @@ -805,8 +808,8 @@ let register_multichain_test ~__FILE__ ?max_delayed_inbox_blueprint_length ?(uses = uses) ?(additional_uses = []) ?rollup_history_mode ~enable_dal ?(dal_slots = if enable_dal then Some [0; 1; 2; 3] else None) ~enable_multichain ~l2_setups ?rpc_server ?websockets ?history_mode - ?enable_tx_queue ?spawn_rpc ?periodic_snapshot_path ?signatory body ~title - ~tags protocols = + ?enable_tx_queue ?spawn_rpc ?periodic_snapshot_path ?signatory + ?sequencer_sunset_sec body ~title ~tags protocols = let kernel_tag, kernel_use = Kernel.to_uses_and_tags kernel in let tags = kernel_tag :: tags in let additional_uses = @@ -876,6 +879,7 @@ let register_multichain_test ~__FILE__ ?max_delayed_inbox_blueprint_length ?spawn_rpc ?periodic_snapshot_path ?signatory + ?sequencer_sunset_sec protocol in body sequencer_setup protocol @@ -929,7 +933,7 @@ let register_test ~__FILE__ ?max_delayed_inbox_blueprint_length ?challenge_window ?uses ?additional_uses ?rollup_history_mode ~enable_dal ?dal_slots ~enable_multichain ?rpc_server ?websockets ?history_mode ?enable_tx_queue ?spawn_rpc ?periodic_snapshot_path ?signatory ?l2_setups - body ~title ~tags protocols = + ?sequencer_sunset_sec body ~title ~tags protocols = let body sequencer_setup = body (multichain_setup_to_single ~setup:sequencer_setup) in @@ -978,6 +982,7 @@ let register_test ~__FILE__ ?max_delayed_inbox_blueprint_length ?periodic_snapshot_path ?signatory ~l2_setups + ?sequencer_sunset_sec body ~title ~tags @@ -997,7 +1002,7 @@ let register_test_for_kernels ~__FILE__ ?max_delayed_inbox_blueprint_length ?challenge_window ?additional_uses ~enable_dal ?dal_slots ~enable_multichain ?rpc_server ?websockets ?enable_fast_withdrawal ?enable_fast_fa_withdrawal ?history_mode ?enable_tx_queue ?spawn_rpc ?periodic_snapshot_path ?signatory - ?l2_setups ~title ~tags body protocols = + ?l2_setups ?sequencer_sunset_sec ~title ~tags body protocols = List.iter (fun kernel -> register_test @@ -1044,6 +1049,7 @@ let register_test_for_kernels ~__FILE__ ?max_delayed_inbox_blueprint_length ?periodic_snapshot_path ?signatory ?l2_setups + ?sequencer_sunset_sec ~title ~tags body @@ -1092,7 +1098,8 @@ let register_all ~__FILE__ ?max_delayed_inbox_blueprint_length ?(use_dal = default_dal_registration) ?(use_multichain = default_multichain_registration) ?(use_revm = default_revm_registration) ?enable_tx_queue ?spawn_rpc - ?periodic_snapshot_path ?signatory ?l2_setups ~title ~tags body protocols = + ?periodic_snapshot_path ?signatory ?l2_setups ?sequencer_sunset_sec ~title + ~tags body protocols = let register_cases = function | Register_both {additional_tags_with; additional_tags_without} -> [(false, additional_tags_without); (true, additional_tags_with)] @@ -1160,6 +1167,7 @@ let register_all ~__FILE__ ?max_delayed_inbox_blueprint_length ?periodic_snapshot_path ?signatory ?l2_setups + ?sequencer_sunset_sec ~title ~tags:(dal_tags @ multichain_tags @ revm_tags @ tags) body diff --git a/etherlink/tezt/lib/setup.mli b/etherlink/tezt/lib/setup.mli index a98b9fd9a6ab620db45cf3c8a0ba8693bdfa7f17..29023d69b2eb246daddbb4aec6606a16078f934a 100644 --- a/etherlink/tezt/lib/setup.mli +++ b/etherlink/tezt/lib/setup.mli @@ -123,6 +123,7 @@ val register_test : ?periodic_snapshot_path:string -> ?signatory:bool -> ?l2_setups:Evm_node.l2_setup list -> + ?sequencer_sunset_sec:int -> (sequencer_setup -> Protocol.t -> unit Lwt.t) -> title:string -> tags:string list -> @@ -175,6 +176,7 @@ val register_multichain_test : ?spawn_rpc:int -> ?periodic_snapshot_path:string -> ?signatory:bool -> + ?sequencer_sunset_sec:int -> (multichain_sequencer_setup -> Protocol.t -> unit Lwt.t) -> title:string -> tags:string list -> @@ -228,6 +230,7 @@ val register_test_for_kernels : ?periodic_snapshot_path:string -> ?signatory:bool -> ?l2_setups:Evm_node.l2_setup list -> + ?sequencer_sunset_sec:int -> title:string -> tags:string list -> (sequencer_setup -> Protocol.t -> unit Lwt.t) -> @@ -280,6 +283,7 @@ val setup_sequencer : ?periodic_snapshot_path:string -> ?signatory:bool -> ?l2_chains:Evm_node.l2_setup list -> + ?sequencer_sunset_sec:int -> Protocol.t -> sequencer_setup Lwt.t @@ -345,6 +349,7 @@ val register_all : ?periodic_snapshot_path:string -> ?signatory:bool -> ?l2_setups:Evm_node.l2_setup list -> + ?sequencer_sunset_sec:int -> title:string -> tags:string list -> (sequencer_setup -> Protocol.t -> unit Lwt.t) -> diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index 955dc466a203c7e5c3d17115104226e19ac65c42..e3b3d5daeaca2f25d100561242a3237eba93cabc 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -481,6 +481,7 @@ let setup_evm_kernel ?additional_config ?(setup_kernel_root_hash = true) tx_pool_addr_limit; tx_pool_tx_per_addr_limit; dal_slots; + sequencer_sunset_sec = None; } in let* sequencer = @@ -5050,6 +5051,7 @@ let test_migrate_proxy_to_sequencer_future = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; } in Evm_node.create ~mode (Sc_rollup_node.endpoint sc_rollup_node) @@ -5220,6 +5222,7 @@ let test_migrate_proxy_to_sequencer_past = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; } in Evm_node.create ~mode (Sc_rollup_node.endpoint sc_rollup_node) diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 75f77e180f0db24eed4a1bee245d8a721da15ccc..505d6e6162993b8572f6c5e0300abf8e18ff95eb 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -345,6 +345,7 @@ let test_make_l2_kernel_installer_config chain_family = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; } in let* sequencer = @@ -520,6 +521,7 @@ let test_observer_reset = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; }) (Sc_rollup_node.endpoint sc_rollup_node) in @@ -563,6 +565,7 @@ let test_observer_reset = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; }) (Sc_rollup_node.endpoint temp_sc_rollup_node) in @@ -5897,6 +5900,69 @@ let test_timestamp_from_the_future = unit +let test_sequencer_sunset = + let sequencer_key = Constant.bootstrap1 in + let new_sequencer_key = Constant.bootstrap2 in + let timestamp dt = Format.sprintf "2020-01-01T00:00:%02dZ" dt in + let activation_delay = 30 in + let genesis_timestamp = timestamp 0 in + let activation_timestamp = timestamp activation_delay in + let sunset = 10 in + + register_all + ~__FILE__ + ~title: + "The sequencer locks its transaction queue ahead of the sequencer upgrade" + ~tags:["evm"; "sequencer"; "sequencer_upgrade"; "sunset"; "lock"] + ~sequencer:sequencer_key + ~time_between_blocks:Nothing + ~sequencer_sunset_sec:sunset + ~use_multichain: + (* TODO #7843: Adapt this test to multichain context *) + Register_without_feature + ~genesis_timestamp:Client.(At (Time.of_notation_exn genesis_timestamp)) + @@ fun {sequencer; sc_rollup_address; l1_contracts; client; sc_rollup_node; _} + _protocol -> + (* Check we can create a block *) + let*@ _txns_count = produce_block ~timestamp:(timestamp 1) sequencer in + + let* () = + sequencer_upgrade + ~sc_rollup_address + ~sequencer_admin:Constant.bootstrap2.alias + ~sequencer_governance_contract:l1_contracts.sequencer_governance + ~pool_address:Eth_account.bootstrap_accounts.(0).address + ~client + ~upgrade_to:new_sequencer_key.alias + ~activation_timestamp + in + + let upgrade_info = Evm_node.wait_for_evm_event Sequencer_upgrade sequencer in + let* () = + repeat 2 (fun () -> + let* _ = next_rollup_node_level ~client ~sc_rollup_node in + unit) + and* _upgrade_info = upgrade_info in + + (* Check we can still create a block before the sunset *) + let*@ _txns_count = + produce_block + ~timestamp:(timestamp (activation_delay - sunset - 1)) + sequencer + in + let*@? _err = + produce_block + ~timestamp:(timestamp (activation_delay - sunset + 1)) + sequencer + in + let*@? _err = + produce_block + ~timestamp:(timestamp (activation_delay - sunset + 2)) + sequencer + in + + unit + (** This tests the situation where the kernel has an upgrade and the sequencer upgrade by following the event of the kernel. Observer must upgrade as well, even the one that is not connected to a @@ -13240,6 +13306,7 @@ let () = test_non_increasing_timestamp protocols ; test_timestamp_from_the_future protocols ; test_sequencer_upgrade protocols ; + test_sequencer_sunset protocols ; test_sequencer_diverge protocols ; test_sequencer_can_catch_up_on_event protocols ; test_sequencer_dont_read_level_twice protocols ; diff --git a/etherlink/tezt/tests/expected/evm_rollup.ml/EVM node- list events regression.out b/etherlink/tezt/tests/expected/evm_rollup.ml/EVM node- list events regression.out index 9c8681808fc6e93fc25f93559ba75ccb54213733..296b3b2687720a7e076e82d68368928d5e8d2dd1 100644 --- a/etherlink/tezt/tests/expected/evm_rollup.ml/EVM node- list events regression.out +++ b/etherlink/tezt/tests/expected/evm_rollup.ml/EVM node- list events regression.out @@ -85,6 +85,14 @@ block_producer_transaction_rejected: contain invalid byte sequences. */ string || { "invalid_utf8_string": [ integer ∈ [0, 255] ... ] } +block_production_sunset: + description: block production is sunset ahead of the change of sequencer + level: notice + section: evm_node.dev + json format: + { /* block_production_sunset version 0 */ + "block_production_sunset.v0": any } + blueprint_application: description: head is now {level}, applied in {process_time}{timestamp} level: notice diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Configuration RPC.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Configuration RPC.out index 0ca0eb8090b8c43c06d3b911dc3ecce8f96d72b7..332b9e3ec5b5d400fb3358e4ecbd046ad2dd1204 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Configuration RPC.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Configuration RPC.out @@ -12,7 +12,8 @@ "max_blueprints_ahead": 100, "max_blueprints_catchup": 50, "catchup_cooldown": 1 - } + }, + "sunset_sec": 300 }, "tx_pool_timeout_limit": "3600", "tx_pool_addr_limit": "4000", @@ -91,7 +92,8 @@ "max_blueprints_ahead": 100, "max_blueprints_catchup": 50, "catchup_cooldown": 1 - } + }, + "sunset_sec": 0 }, "tx_pool_timeout_limit": "3600", "tx_pool_addr_limit": "4000", @@ -170,7 +172,8 @@ "max_blueprints_ahead": 100, "max_blueprints_catchup": 50, "catchup_cooldown": 1 - } + }, + "sunset_sec": 300 }, "observer": { "evm_node_endpoint": "hidden", diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- describe config.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- describe config.out index b7ca8475be0104a43ca670ed53830489d0eefb6f..106d2d05e0bc494dfc57a97d845fda082257b9e8 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- describe config.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- describe config.out @@ -59,7 +59,11 @@ /* The number of Layer 1 blocks the sequencer awaits before sending another batch of blueprints, as part of its catchup mechanism. */, - "dal_slots"?: [ integer ∈ [-128, 127] ... ] } }, + "dal_slots"?: [ integer ∈ [-128, 127] ... ] }, + "sunset_sec"?: + integer ∈ [0, 2^30] + /* Number of seconds prior to a sequencer operator upgrade before + which the current sequencer stops producing blocks */ }, "observer"?: { "evm_node_endpoint": $unistring diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- man.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- man.out index 491f6a33d65180b3c15ab71e8cb4d69db4dd1abc..2727ad79bedc5f81f2a3580b14c464c02e8dc098 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- man.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/EVM Node- man.out @@ -316,6 +316,7 @@ Run commands: [--genesis-timestamp <[TIMESTAMP]>] [--initial-kernel ] [-d --wallet-dir ] [-f --password-filename ] [--dal-slots ] + [--sunset-sec ] Start the EVM node in sequencer mode. --data-dir : The path to the EVM node data directory. Defaults to the value of the environment variable `$EVM_NODE_DATA_DIR` @@ -393,6 +394,9 @@ Run commands: -f --password-filename : path to the password filename --dal-slots : The DAL slots indices on which the sequencer is allowed to send blueprints. + --sunset-sec : Number of seconds prior to a sequencer operator + upgrade before which the current sequencer stops producing blocks + If set, defaults to the value of EVM_NODE_SEQUENCER_SUNSET_SEC. run observer [--data-dir ] [--config-file ] [--rpc-addr ] [--rpc-port ] [--rpc-batch-limit ] @@ -596,7 +600,7 @@ Configuration commands: [--catch-up-cooldown ] [--evm-node-endpoint ] [--history ] [--dont-track-rollup-node] [-d --wallet-dir ] [-f --force] [--dal-slots ] - [--network ] + [--network ] [--sunset-sec ] Create an initial config with default value. If the is set then adds the configuration for the proxy mode. If the is set,then adds the configuration @@ -687,6 +691,9 @@ Configuration commands: be `mainnet` or `testnet`. If set, some configuration options defaults to well-known values for the selected network. If set, defaults to the value of EVM_NODE_NETWORK. + --sunset-sec : Number of seconds prior to a sequencer operator + upgrade before which the current sequencer stops producing blocks + If set, defaults to the value of EVM_NODE_SEQUENCER_SUNSET_SEC. check config [--data-dir ] [--config-file ] [-p --print] [--network ] diff --git a/src/bin_testnet_scenarios/upgrade_etherlink.ml b/src/bin_testnet_scenarios/upgrade_etherlink.ml index c08f7870412e677d44fa37881d2447f4b7455a4a..1561981196d113a394597c0581154fdf4afe80a3 100644 --- a/src/bin_testnet_scenarios/upgrade_etherlink.ml +++ b/src/bin_testnet_scenarios/upgrade_etherlink.ml @@ -180,6 +180,7 @@ let prepare_and_run_sequencer rollup_node = tx_pool_addr_limit = None; tx_pool_tx_per_addr_limit = None; dal_slots = None; + sequencer_sunset_sec = None; }) (Sc_rollup_node.endpoint rollup_node) in diff --git a/tezt/tests/cloud/dal.ml b/tezt/tests/cloud/dal.ml index 453fcb4ccd8aec57bb598bcefbde2eda5dd4df4e..83b134a826ef14de7818281a17408e882c0e1aa5 100644 --- a/tezt/tests/cloud/dal.ml +++ b/tezt/tests/cloud/dal.ml @@ -3677,6 +3677,7 @@ let init_etherlink_operator_setup cloud configuration etherlink_configuration 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