Raw File
main_tx_rollup_node_alpha.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Nomadic Labs, <contact@nomadic-labs.com>               *)
(* Copyright (c) 2022 Marigold, <contact@marigold.dev>                       *)
(* Copyright (c) 2022 Oxhead Alpha <info@oxhead-alpha.com>                   *)
(*                                                                           *)
(* 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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

let force_switch =
  Tezos_clic.switch
    ~long:"force"
    ~doc:"Overwrites the configuration file when it exists."
    ()

let data_dir_doc =
  Format.sprintf "The directory path to the transaction rollup node data."

let rpc_addr_doc =
  Format.asprintf
    "The address and port where the node listens to RPCs. The default is %s"

let reconnection_delay_doc =
  Format.asprintf
    "The reconnection delay when the connection is lost. The default delay is \
     %f"

let data_dir_arg =
  let doc = data_dir_doc in
  Tezos_clic.arg
    ~long:"data-dir"
    ~placeholder:"data_dir"
    ~doc
    Client_proto_args.string_parameter

let operator_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"operator"
    ~placeholder:"operator"
    ~doc:"The operator of the rollup"
    ()

let batch_signer_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"batch-signer"
    ~placeholder:"batch-signer"
    ~doc:"The signer for submission of batches"
    ()

let finalize_commitment_signer_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"finalize-commitment-signer"
    ~placeholder:"finalize-commitment-signer"
    ~doc:"The signer for finalization of commitments"
    ()

let remove_commitment_signer_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"remove-commitment-signer"
    ~placeholder:"remove-commitment-signer"
    ~doc:"The signer for removals of commitments"
    ()

let rejection_signer_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"rejection-signer"
    ~placeholder:"rejection-signer"
    ~doc:"The signer for rejections"
    ()

let dispatch_withdrawals_signer_arg =
  Client_keys.Public_key_hash.source_arg
    ~long:"dispatch-withdrawals-signer"
    ~placeholder:"dispatch-withdrawals-signer"
    ~doc:"The signer for dispatch withdrawals"
    ()

let rollup_id_param =
  let open Client_proto_rollups in
  Tezos_clic.parameter ~autocomplete:TxRollupAlias.autocomplete (fun cctxt s ->
      let from_alias s = TxRollupAlias.find cctxt s in
      let from_key s = TxRollupAlias.of_source s in
      Client_aliases.parse_alternatives
        [("alias", from_alias); ("key", from_key)]
        s)

let origination_level_arg =
  Tezos_clic.arg
    ~long:"origination-level"
    ~placeholder:"origination_level"
    ~doc:"The level of the block where the rollup was originated"
    (Tezos_clic.parameter (fun _ str ->
         match Int32.of_string_opt str with
         | None -> failwith "Invalid origination level"
         | Some l -> return l))

let rpc_addr_arg =
  let default = P2p_point.Id.to_string Node_config.default_rpc_addr in
  let doc = rpc_addr_doc default in
  Tezos_clic.arg
    ~long:"rpc-addr"
    ~placeholder:"address:port"
    ~doc
    (Tezos_clic.parameter (fun _ s ->
         P2p_point.Id.of_string s
         |> Result.map_error (fun e -> [Exn (Failure e)])
         |> Lwt.return))

let rpc_addr_opt_arg =
  let default = P2p_point.Id.to_string Node_config.default_rpc_addr in
  let doc = rpc_addr_doc default in
  Tezos_clic.arg
    ~long:"rpc-addr"
    ~placeholder:"address:port"
    ~doc
    (Tezos_clic.parameter (fun _ s ->
         P2p_point.Id.of_string s
         |> Result.map_error (fun e -> [Exn (Failure e)])
         |> Lwt.return))

let cors_origins_arg =
  Tezos_clic.arg
    ~doc:
      "CORS origins allowed by the RPC server via Access-Control-Allow-Origin"
    ~placeholder:"c1, c2, ..."
    ~long:"cors-origins"
  @@ Tezos_clic.parameter (fun _ctxt s ->
         String.split_no_empty ',' s |> List.map String.trim |> return)

let cors_headers_arg =
  Tezos_clic.arg
    ~doc:
      "Header reported by Access-Control-Allow-Headers reported during CORS \
       preflighting"
    ~placeholder:"h1, h2, ..."
    ~long:"cors-headers"
  @@ Tezos_clic.parameter (fun _ctxt s ->
         String.split_no_empty ',' s |> List.map String.trim |> return)

let reconnection_delay_arg =
  let default = Node_config.default_reconnection_delay in
  let doc = reconnection_delay_doc default in
  Tezos_clic.arg
    ~long:"reconnection-delay"
    ~placeholder:"delay"
    ~doc
    (Tezos_clic.parameter (fun _ p ->
         try return (float_of_string p) with _ -> failwith "Cannot read float"))

let possible_modes = List.map Node_config.string_of_mode Node_config.modes

let mode_parameter =
  Tezos_clic.parameter
    ~autocomplete:(fun _ -> return possible_modes)
    (fun _ m -> Lwt.return (Node_config.mode_of_string m))

let mode_param =
  Tezos_clic.param
    ~name:"mode"
    ~desc:
      (Printf.sprintf
         "The mode for the rollup node (%s)"
         (String.concat ", " possible_modes))
    mode_parameter

let allow_deposit_arg =
  Tezos_clic.switch
    ~doc:"Allow the operator to make a first deposit for commitments"
    ~long:"allow-deposit"
    ()

let group =
  Tezos_clic.
    {
      name = "tx_rollup.node";
      title = "Commands related to the transaction rollup node";
    }

let config_from_args data_dir (rollup_id : Client_proto_rollups.TxRollupAlias.t)
    mode operator batch_signer finalize_commitment_signer
    remove_commitment_signer rejection_signer dispatch_withdrawals_signer
    origination_level rpc_addr cors_origins cors_headers allow_deposit
    reconnection_delay =
  let open Lwt_syntax in
  let origination_level =
    Option.either rollup_id.origination_level origination_level
  in
  let rollup_id = rollup_id.rollup in
  let+ data_dir =
    match data_dir with
    | Some d -> return d
    | None -> Node_config.default_data_dir rollup_id
  in
  let rpc_addr = Option.value rpc_addr ~default:Node_config.default_rpc_addr in
  let reconnection_delay =
    Option.value
      reconnection_delay
      ~default:Node_config.default_reconnection_delay
  in
  Node_config.
    {
      data_dir;
      mode;
      signers =
        {
          operator;
          submit_batch = batch_signer;
          finalize_commitment = finalize_commitment_signer;
          remove_commitment = remove_commitment_signer;
          rejection = rejection_signer;
          dispatch_withdrawals = dispatch_withdrawals_signer;
        };
      rollup_id;
      origination_level;
      rpc_addr;
      cors_origins = Option.value cors_origins ~default:[];
      cors_headers = Option.value cors_headers ~default:[];
      reconnection_delay;
      allow_deposit;
      l2_blocks_cache_size = default_l2_blocks_cache_size;
      caps = default_caps;
      batch_burn_limit = None;
    }

let patch_config_from_args config
    (rollup_id : Client_proto_rollups.TxRollupAlias.t) mode operator
    batch_signer finalize_commitment_signer remove_commitment_signer
    rejection_signer dispatch_withdrawals_signer origination_level rpc_addr
    cors_origins cors_headers allow_deposit reconnection_delay =
  let origination_level =
    Option.either rollup_id.origination_level origination_level
  in
  let rollup_id = rollup_id.rollup in
  if
    Protocol.Alpha_context.Tx_rollup.(rollup_id <> config.Node_config.rollup_id)
  then
    error_with
      "Rollup node is configured for rollup %a but asked to run for rollup %a"
      Protocol.Alpha_context.Tx_rollup.pp
      config.Node_config.rollup_id
      Protocol.Alpha_context.Tx_rollup.pp
      rollup_id
  else
    let operator = Option.either operator config.signers.operator in
    let submit_batch = Option.either batch_signer config.signers.submit_batch in
    let finalize_commitment =
      Option.either
        finalize_commitment_signer
        config.signers.finalize_commitment
    in
    let remove_commitment =
      Option.either remove_commitment_signer config.signers.remove_commitment
    in
    let dispatch_withdrawals =
      Option.either
        dispatch_withdrawals_signer
        config.signers.dispatch_withdrawals
    in
    let rejection = Option.either rejection_signer config.signers.rejection in
    let signers =
      {
        Node_config.operator;
        submit_batch;
        finalize_commitment;
        remove_commitment;
        dispatch_withdrawals;
        rejection;
      }
    in
    let origination_level =
      Option.either origination_level config.origination_level
    in
    let rpc_addr = Option.value rpc_addr ~default:config.rpc_addr in
    let cors_origins = Option.value cors_origins ~default:config.cors_origins in
    let cors_headers = Option.value cors_headers ~default:config.cors_headers in
    let reconnection_delay =
      Option.value reconnection_delay ~default:config.reconnection_delay
    in
    let allow_deposit = allow_deposit || config.allow_deposit in
    let config =
      {
        config with
        mode;
        signers;
        origination_level;
        rpc_addr;
        cors_origins;
        cors_headers;
        reconnection_delay;
        allow_deposit;
      }
    in
    ok config

let configuration_init_command =
  let open Tezos_clic in
  command
    ~group
    ~desc:"Configure the transaction rollup daemon."
    (args14
       force_switch
       data_dir_arg
       operator_arg
       batch_signer_arg
       finalize_commitment_signer_arg
       remove_commitment_signer_arg
       rejection_signer_arg
       dispatch_withdrawals_signer_arg
       origination_level_arg
       rpc_addr_arg
       cors_origins_arg
       cors_headers_arg
       allow_deposit_arg
       reconnection_delay_arg)
    (prefix "init" @@ mode_param
    @@ prefixes ["config"; "for"]
    @@ Tezos_clic.param
         ~name:"rollup-id"
         ~desc:"address of the rollup"
         rollup_id_param
    @@ stop)
    (fun ( force,
           data_dir,
           operator,
           batch_signer,
           finalize_commitment_signer,
           remove_commitment_signer,
           rejection_signer,
           dispatch_withdrawals_signer,
           origination_level,
           rpc_addr,
           cors_origins,
           cors_headers,
           allow_deposit,
           reconnection_delay )
         mode
         rollup_id
         cctxt ->
      let open Lwt_result_syntax in
      let*! () = Event.(emit preamble_warning) () in
      let*! config =
        config_from_args
          data_dir
          rollup_id
          mode
          operator
          batch_signer
          finalize_commitment_signer
          remove_commitment_signer
          rejection_signer
          dispatch_withdrawals_signer
          origination_level
          rpc_addr
          cors_origins
          cors_headers
          allow_deposit
          reconnection_delay
      in
      let*? config = Node_config.check_mode config in
      let* file = Node_config.save ~force config in
      (* This is necessary because the node has not yet been launched, so event
         listening can't be used. *)
      let*! () = cctxt#message "Configuration written in %s" file in
      let*! () = Event.(emit configuration_was_written) file in
      return_unit)

let run_command =
  let open Lwt_result_syntax in
  let open Tezos_clic in
  command
    ~group
    ~desc:"Run the transaction rollup daemon."
    (args13
       data_dir_arg
       operator_arg
       batch_signer_arg
       finalize_commitment_signer_arg
       remove_commitment_signer_arg
       rejection_signer_arg
       dispatch_withdrawals_signer_arg
       origination_level_arg
       rpc_addr_opt_arg
       cors_origins_arg
       cors_headers_arg
       allow_deposit_arg
       reconnection_delay_arg)
    (prefix "run" @@ mode_param @@ prefix "for"
    @@ Tezos_clic.param
         ~name:"rollup-id"
         ~desc:"address of the rollup"
         rollup_id_param
    @@ stop)
    (fun ( data_dir,
           operator,
           batch_signer,
           finalize_commitment_signer,
           remove_commitment_signer,
           rejection_signer,
           dispatch_withdrawals_signer,
           origination_level,
           rpc_addr,
           cors_origins,
           cors_headers,
           allow_deposit,
           reconnection_delay )
         mode
         rollup_id
         cctxt ->
      let*! () = Event.(emit preamble_warning) () in
      let*! config_from_args =
        config_from_args
          data_dir
          rollup_id
          mode
          operator
          batch_signer
          finalize_commitment_signer
          remove_commitment_signer
          rejection_signer
          dispatch_withdrawals_signer
          origination_level
          rpc_addr
          cors_origins
          cors_headers
          allow_deposit
          reconnection_delay
      in
      let* config =
        match data_dir with
        | None -> return config_from_args
        | Some data_dir -> (
            let*! disk_config = Node_config.load ~data_dir in
            match disk_config with
            | Error (Error.Tx_rollup_configuration_file_does_not_exists _ :: _)
              ->
                return config_from_args
            | Error _ as err -> Lwt.return err
            | Ok config ->
                let*? config =
                  patch_config_from_args
                    config
                    rollup_id
                    mode
                    operator
                    batch_signer
                    finalize_commitment_signer
                    remove_commitment_signer
                    rejection_signer
                    dispatch_withdrawals_signer
                    origination_level
                    rpc_addr
                    cors_origins
                    cors_headers
                    allow_deposit
                    reconnection_delay
                in
                return config)
      in
      let*? config = Node_config.check_mode config in
      Daemon.run config cctxt)

let tx_rollup_commands () =
  List.map
    (Tezos_clic.map_command (new Protocol_client_context.wrap_full))
    [configuration_init_command; run_command]

let select_commands _ _ = return (tx_rollup_commands ())

let () = Client_main_run.run (module Daemon_config) ~select_commands
back to top