Raw File
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2023 TriliTech <contact@trili.tech>                         *)
(* Copyright (c) 2023 Functori, <contact@functori.com>                       *)
(* Copyright (c) 2023 Marigold <contact@marigold.dev>                        *)
(*                                                                           *)
(* 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
back to top