(*****************************************************************************) (* *) (* Open Source License *) (* Copyright (c) 2023 TriliTech *) (* Copyright (c) 2023 Functori, *) (* Copyright (c) 2023 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) (* to deal in the Software without restriction, including without limitation *) (* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) (* and/or sell copies of the Software, and to permit persons to whom the *) (* Software is furnished to do so, subject to the following conditions: *) (* *) (* The above copyright notice and this permission notice shall be included *) (* in all copies or substantial portions of the Software. *) (* *) (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) (* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) (* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) open Node_context let lock ~data_dir = let lockfile_path = global_lockfile_path ~data_dir in let lock_aux ~data_dir = let open Lwt_result_syntax in let*! () = Event.acquiring_lock () in let*! () = Lwt_utils_unix.create_dir data_dir in let* lockfile = protect @@ fun () -> Lwt_unix.openfile lockfile_path [Unix.O_CREAT; O_RDWR; O_CLOEXEC; O_SYNC] 0o644 |> Lwt_result.ok in let* () = protect ~on_error:(fun err -> let*! () = Lwt_unix.close lockfile in fail err) @@ fun () -> let*! () = Lwt_unix.lockf lockfile Unix.F_LOCK 0 in return_unit in return lockfile in trace (Rollup_node_errors.Could_not_acquire_lock lockfile_path) @@ lock_aux ~data_dir let unlock {lockfile; _} = Lwt.finalize (fun () -> Lwt_unix.lockf lockfile Unix.F_ULOCK 0) (fun () -> Lwt_unix.close lockfile) let update_metadata ({Metadata.rollup_address; _} as metadata) ~data_dir = let open Lwt_result_syntax in let* disk_metadata = Metadata.Versioned.read_metadata_file ~dir:data_dir in match disk_metadata with | Some (V1 {rollup_address = saved_address; context_version; _}) -> let*? () = Context.Version.check context_version in fail_unless Address.(rollup_address = saved_address) @@ Rollup_node_errors.Unexpected_rollup {rollup_address; saved_address} | Some (V0 {rollup_address = saved_address; context_version}) -> let*? () = Context.Version.check context_version in let*? () = error_unless Address.(rollup_address = saved_address) @@ Rollup_node_errors.Unexpected_rollup {rollup_address; saved_address} in Metadata.write_metadata_file ~dir:data_dir metadata | None -> Metadata.write_metadata_file ~dir:data_dir metadata let init (cctxt : #Client_context.full) ~data_dir ~irmin_cache_size ~index_buffer_size ?log_kernel_debug_file ?last_whitelist_update mode l1_ctxt genesis_info ~lcc ~lpc kind current_protocol Configuration.( { sc_rollup_address = rollup_address; l2_blocks_cache_size; dal_node_endpoint; _; } as configuration) = let open Lwt_result_syntax in let* lockfile = lock ~data_dir in let metadata = { Metadata.rollup_address; context_version = Context.Version.version; kind; genesis_info; } in let* () = update_metadata metadata ~data_dir in let* () = Store_migration.maybe_run_migration metadata ~storage_dir:(Configuration.default_storage_dir data_dir) ~index_buffer_size:Configuration.default_index_buffer_size in let dal_cctxt = Option.map Dal_node_client.make_unix_cctxt dal_node_endpoint in let* store = Node_context.Node_store.load mode ~index_buffer_size ~l2_blocks_cache_size Configuration.(default_storage_dir data_dir) in let*? (module Plugin : Protocol_plugin_sig.S) = Protocol_plugins.proto_plugin_for_protocol current_protocol.hash in let (module C) = Plugin.Pvm.context kind in let* context = Context.load (module C) ~cache_size:irmin_cache_size mode (Configuration.default_context_dir data_dir) in let* () = Node_context.Node_store.check_and_set_history_mode mode store configuration.history_mode in let*! () = Event.rollup_exists ~addr:rollup_address ~kind in let*! () = if dal_cctxt = None && current_protocol.constants.dal.feature_enable then Event.warn_dal_enabled_no_node () else Lwt.return_unit in let* dac_client = Option.map_es (fun observer_endpoint -> Dac_observer_client.init { observer_endpoint; reveal_data_dir = Filename.concat data_dir (Kind.to_string kind); timeout_seconds = configuration.dac_timeout; }) configuration.dac_observer_endpoint in let*! kernel_debug_logger, kernel_debug_finaliser = let open Lwt_syntax in if configuration.log_kernel_debug then make_kernel_logger Event.kernel_debug ?log_kernel_debug_file data_dir else return (Event.kernel_debug, fun () -> return_unit) in let global_block_watcher = Lwt_watcher.create_input () in let private_info = Option.map (fun (message_index, outbox_level) -> { last_whitelist_update = {outbox_level; message_index = Z.to_int message_index}; last_outbox_level_searched = outbox_level; }) last_whitelist_update in return { config = configuration; cctxt = (cctxt :> Client_context.full); dal_cctxt; dac_client; data_dir; l1_ctxt; genesis_info; lcc = Reference.new_ lcc; lpc = Reference.new_ lpc; private_info = Reference.new_ private_info; kind; injector_retention_period = 0; block_finality_time = 2; lockfile; store; context; kernel_debug_logger; finaliser = kernel_debug_finaliser; current_protocol; global_block_watcher; } let close ({cctxt; store; context; l1_ctxt; finaliser; _} as node_ctxt) = let open Lwt_result_syntax in let message = cctxt#message in let*! () = message "Running finaliser@." in let*! () = finaliser () in let*! () = message "Shutting down L1@." in let*! () = Layer1.shutdown l1_ctxt in let*! () = message "Closing context@." in let*! () = Context.close context in let*! () = message "Closing store@." in let* () = Node_context.Node_store.close store in let*! () = message "Releasing lock@." in let*! () = unlock node_ctxt in return_unit module Internal_for_tests = struct let create_node_context cctxt (current_protocol : current_protocol) ~data_dir kind = let open Lwt_result_syntax in let rollup_address = Address.zero in let mode = Configuration.Observer in let*? operators = Purpose.make_operator ~needed_purposes:(Configuration.purposes_of_mode mode) [] in let loser_mode = Loser_mode.no_failures in let l1_blocks_cache_size = Configuration.default_l1_blocks_cache_size in let l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size in let index_buffer_size = Configuration.default_index_buffer_size in let irmin_cache_size = Configuration.default_irmin_cache_size in let l1_rpc_timeout = Configuration.default_l1_rpc_timeout in let config = Configuration. { sc_rollup_address = rollup_address; boot_sector_file = None; operators; rpc_addr = Configuration.default_rpc_addr; rpc_port = Configuration.default_rpc_port; metrics_addr = None; reconnection_delay = 5.; fee_parameters = Configuration.default_fee_parameters; mode; loser_mode; dal_node_endpoint = None; dac_observer_endpoint = None; dac_timeout = None; pre_images_endpoint = None; batcher = Configuration.default_batcher; injector = Configuration.default_injector; l1_blocks_cache_size; l2_blocks_cache_size; index_buffer_size = Some index_buffer_size; irmin_cache_size = Some irmin_cache_size; prefetch_blocks = None; l1_rpc_timeout; log_kernel_debug = false; no_degraded = false; gc_parameters = Configuration.default_gc_parameters; history_mode = None; cors = Resto_cohttp.Cors.default; } in let* lockfile = lock ~data_dir in let* store = Node_context.Node_store.load Read_write ~index_buffer_size ~l2_blocks_cache_size Configuration.(default_storage_dir data_dir) in let*? (module Plugin : Protocol_plugin_sig.S) = Protocol_plugins.proto_plugin_for_protocol current_protocol.hash in let (module C) = Plugin.Pvm.context kind in let* context = Context.load (module C) Read_write (Configuration.default_context_dir data_dir) ~cache_size:irmin_cache_size in let genesis_info = {level = 0l; commitment_hash = Commitment.Hash.zero} in let l1_ctxt = Layer1.Internal_for_tests.dummy cctxt in let lcc = Reference.new_ {commitment = Commitment.Hash.zero; level = 0l} in let lpc = Reference.new_ None in let* () = Node_context.Internal_for_tests.write_protocols_in_store store [ Store.Protocols. { level = Activation_level 0l; proto_level = current_protocol.proto_level; protocol = current_protocol.hash; }; ] in let global_block_watcher = Lwt_watcher.create_input () in return { config; cctxt = (cctxt :> Client_context.full); dal_cctxt = None; dac_client = None; data_dir; l1_ctxt; genesis_info; lcc; lpc; private_info = Reference.new_ None; kind; injector_retention_period = 0; block_finality_time = 2; current_protocol; lockfile; store; context; kernel_debug_logger = Event.kernel_debug; finaliser = (fun () -> Lwt.return_unit); global_block_watcher; } let openapi_context cctxt protocol = let current_protocol = { hash = protocol; proto_level = 0; constants = Rollup_constants. { minimal_block_delay = 0L; delay_increment_per_round = 0L; sc_rollup = { challenge_window_in_blocks = 0; commitment_period_in_blocks = 0; reveal_activation_level = Some { blake2B = 0l; metadata = 0l; dal_page = 0l; dal_parameters = 0l; }; max_number_of_stored_cemented_commitments = 0; }; dal = { feature_enable = false; attestation_lag = 0; number_of_slots = 0; cryptobox_parameters = { redundancy_factor = 0; page_size = 0; slot_size = 0; number_of_shards = 0; }; }; }; } in Lwt_utils_unix.with_tempdir "smart-rollup-node-openapi" @@ fun data_dir -> let open Lwt_result_syntax in let* node_ctxt = create_node_context cctxt current_protocol ~data_dir Wasm_2_0_0 in let*! () = Context.close node_ctxt.context in let* () = Node_context.Node_store.close node_ctxt.store in return node_ctxt end