Revision 323d1e12d16fb1abcfc16bb85a0085e931c96e49 authored by Raphaƫl Proust on 25 March 2019, 07:04:05 UTC, committed by Vincent Botbol on 13 May 2019, 18:53:39 UTC
1 parent a51e7ad
Raw File
node_config_file.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com>     *)
(* Copyright (c) 2019 Nomadic Labs, <contact@nomadic-labs.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.                                                 *)
(*                                                                           *)
(*****************************************************************************)

[@@@ocaml.warning "-30"]

let (//) = Filename.concat

let home =
  try Sys.getenv "HOME"
  with Not_found -> "/root"

let default_data_dir = home // ".tezos-node"
let default_rpc_port       =  8732
let default_p2p_port       =  9732
let default_discovery_port = 10732

type t = {
  data_dir : string ;
  p2p : p2p ;
  rpc : rpc ;
  log : Lwt_log_sink_unix.cfg ;
  internal_events : Internal_event_unix.Configuration.t ;
  shell : shell ;
}

and p2p = {
  expected_pow : float ;
  bootstrap_peers : string list ;
  listen_addr : string option ;
  discovery_addr : string option ;
  private_mode : bool ;
  limits : P2p.limits ;
  disable_mempool : bool ;
  disable_testchain : bool ;
}

and rpc = {
  listen_addr : string option ;
  cors_origins : string list ;
  cors_headers : string list ;
  tls : tls option ;
}

and tls = {
  cert : string ;
  key : string ;
}

and shell = {
  block_validator_limits : Node.block_validator_limits ;
  prevalidator_limits : Node.prevalidator_limits ;
  peer_validator_limits : Node.peer_validator_limits ;
  chain_validator_limits : Node.chain_validator_limits ;
}

let default_p2p_limits : P2p.limits = {
  connection_timeout = Time.System.Span.of_seconds_exn 10. ;
  authentication_timeout = Time.System.Span.of_seconds_exn 5. ;
  greylist_timeout = Time.System.Span.of_seconds_exn 86400. (* one day *) ;
  maintenance_idle_time = Time.System.Span.of_seconds_exn 120. (* two minutes *) ;
  min_connections = 10 ;
  expected_connections = 50 ;
  max_connections = 100 ;
  backlog = 20 ;
  max_incoming_connections = 20 ;
  max_download_speed = None ;
  max_upload_speed = None ;
  read_buffer_size = 1 lsl 14 ;
  read_queue_size = None ;
  write_queue_size = None ;
  incoming_app_message_queue_size = None ;
  incoming_message_queue_size = None ;
  outgoing_message_queue_size = None ;
  known_points_history_size = 500 ;
  known_peer_ids_history_size = 500 ;
  max_known_points = Some (400, 300) ;
  max_known_peer_ids = Some (400, 300) ;
  swap_linger = Time.System.Span.of_seconds_exn 30. ;
  binary_chunks_size = None ;
}

let default_p2p = {
  expected_pow = 26. ;
  bootstrap_peers  = [] ;
  listen_addr = Some ("[::]:" ^ string_of_int default_p2p_port) ;
  discovery_addr = None ;
  private_mode = false ;
  limits = default_p2p_limits ;
  disable_mempool = false ;
  disable_testchain = false ;
}

let default_rpc = {
  listen_addr = None ;
  cors_origins = [] ;
  cors_headers = [] ;
  tls = None ;
}

let default_shell = {
  block_validator_limits = Node.default_block_validator_limits ;
  prevalidator_limits = Node.default_prevalidator_limits ;
  peer_validator_limits = Node.default_peer_validator_limits ;
  chain_validator_limits = Node.default_chain_validator_limits ;
}

let default_config = {
  data_dir = default_data_dir ;
  p2p = default_p2p ;
  rpc = default_rpc ;
  log = Lwt_log_sink_unix.default_cfg ;
  internal_events = Internal_event_unix.Configuration.default ;
  shell = default_shell ;
}

let limit : P2p.limits Data_encoding.t =
  let open Data_encoding in
  conv
    (fun { P2p.connection_timeout ; authentication_timeout ; greylist_timeout ;
           maintenance_idle_time ;
           min_connections ; expected_connections ; max_connections ;
           backlog ; max_incoming_connections ;
           max_download_speed ; max_upload_speed ;
           read_buffer_size ; read_queue_size ; write_queue_size ;
           incoming_app_message_queue_size ;
           incoming_message_queue_size ; outgoing_message_queue_size ;
           known_points_history_size ; known_peer_ids_history_size ;
           max_known_points ; max_known_peer_ids ;
           swap_linger ; binary_chunks_size
         } ->
      (((( connection_timeout, authentication_timeout,
           min_connections, expected_connections,
           max_connections, backlog, max_incoming_connections,
           max_download_speed, max_upload_speed, swap_linger),
         ( binary_chunks_size, read_buffer_size, read_queue_size, write_queue_size,
           incoming_app_message_queue_size,
           incoming_message_queue_size, outgoing_message_queue_size,
           known_points_history_size, known_peer_ids_history_size,
           max_known_points)),
        (  max_known_peer_ids, greylist_timeout, maintenance_idle_time))))
    (fun (((( connection_timeout, authentication_timeout,
              min_connections, expected_connections,
              max_connections, backlog, max_incoming_connections,
              max_download_speed, max_upload_speed, swap_linger),
            ( binary_chunks_size, read_buffer_size, read_queue_size, write_queue_size,
              incoming_app_message_queue_size,
              incoming_message_queue_size, outgoing_message_queue_size,
              known_points_history_size, known_peer_ids_history_size,
              max_known_points)),
           (  max_known_peer_ids, greylist_timeout, maintenance_idle_time))) ->
      { connection_timeout ; authentication_timeout ; greylist_timeout ;
        maintenance_idle_time ;
        min_connections ; expected_connections ;
        max_connections ; backlog ; max_incoming_connections ;
        max_download_speed ; max_upload_speed ;
        read_buffer_size ; read_queue_size ; write_queue_size ;
        incoming_app_message_queue_size ;
        incoming_message_queue_size ; outgoing_message_queue_size ;
        known_points_history_size ; known_peer_ids_history_size ;
        max_known_points ; max_known_peer_ids ; swap_linger ;
        binary_chunks_size
      })
    (merge_objs
       (merge_objs
          (obj10
             (dft "connection-timeout"
                ~description: "Delay acceptable when initiating a \
                               connection to a new peer, in seconds."
                Time.System.Span.encoding default_p2p_limits.authentication_timeout)
             (dft "authentication-timeout"
                ~description: "Delay granted to a peer to perform authentication, \
                               in seconds."
                Time.System.Span.encoding default_p2p_limits.authentication_timeout)
             (dft "min-connections"
                ~description: "Strict minimum number of connections (triggers an \
                               urgent maintenance)."
                uint16
                default_p2p_limits.min_connections)
             (dft "expected-connections"
                ~description: "Targeted number of connections to reach when \
                               bootstrapping / maintaining."
                uint16
                default_p2p_limits.expected_connections)
             (dft "max-connections"
                ~description: "Maximum number of connections (exceeding peers are \
                               disconnected)."
                uint16
                default_p2p_limits.max_connections)
             (dft "backlog"
                ~description: "Number above which pending incoming connections are \
                               immediately rejected."
                uint8
                default_p2p_limits.backlog)
             (dft "max-incoming-connections"
                ~description: "Number above which pending incoming connections are \
                               immediately rejected."
                uint8
                default_p2p_limits.max_incoming_connections)
             (opt "max-download-speed"
                ~description: "Max download speeds in KiB/s."
                int31)
             (opt "max-upload-speed"
                ~description: "Max upload speeds in KiB/s."
                int31)
             (dft "swap-linger" Time.System.Span.encoding default_p2p_limits.swap_linger))
          (obj10
             (opt "binary-chunks-size" uint8)
             (dft "read-buffer-size"
                ~description: "Size of the buffer passed to read(2)."
                int31
                default_p2p_limits.read_buffer_size)
             (opt "read-queue-size" int31)
             (opt "write-queue-size" int31)
             (opt "incoming-app-message-queue-size" int31)
             (opt "incoming-message-queue-size" int31)
             (opt "outgoing-message-queue-size" int31)
             (dft "known_points_history_size" uint16
                default_p2p_limits.known_points_history_size)
             (dft "known_peer_ids_history_size" uint16
                default_p2p_limits.known_points_history_size)
             (opt "max_known_points" (tup2 uint16 uint16))
          ))
       (obj3
          (opt "max_known_peer_ids" (tup2 uint16 uint16))
          (dft "greylist-timeout"
             ~description: "GC delay for the greylists tables, in seconds."
             Time.System.Span.encoding default_p2p_limits.greylist_timeout)
          (dft "maintenance-idle-time"
             ~description: "How long to wait at most, in seconds, \
                            before running a maintenance loop."
             Time.System.Span.encoding default_p2p_limits.maintenance_idle_time)
       )
    )

let p2p =
  let open Data_encoding in
  conv
    (fun { expected_pow ; bootstrap_peers ;
           listen_addr ; discovery_addr ; private_mode ;
           limits ; disable_mempool ; disable_testchain } ->
      (expected_pow, bootstrap_peers,
       listen_addr, discovery_addr, private_mode, limits,
       disable_mempool, disable_testchain))
    (fun (expected_pow, bootstrap_peers,
          listen_addr, discovery_addr, private_mode, limits,
          disable_mempool, disable_testchain) ->
      { expected_pow ; bootstrap_peers ;
        listen_addr ; discovery_addr ; private_mode ; limits ;
        disable_mempool ; disable_testchain })
    (obj8
       (dft "expected-proof-of-work"
          ~description: "Floating point number between 0 and 256 that represents a \
                         difficulty, 24 signifies for example that at least 24 leading \
                         zeroes are expected in the hash."
          float default_p2p.expected_pow)
       (dft "bootstrap-peers"
          ~description: "List of hosts. Tezos can connect to both IPv6 and IPv4 hosts. \
                         If the port is not specified, default port 9732 will be assumed."
          (list string) default_p2p.bootstrap_peers)
       (opt "listen-addr"
          ~description: "Host to listen to. If the port is not \
                         specified, the default port 8732 will be \
                         assumed."
          string)
       (dft "discovery-addr"
          ~description: "Host for local peer discovery. If the port is not \
                         specified, the default port 10732 will be \
                         assumed."
          (option string) default_p2p.discovery_addr)
       (dft "private-mode"
          ~description: "Specify if the node is in private mode or \
                         not. A node in private mode rejects incoming \
                         connections from untrusted peers and only \
                         opens outgoing connections to peers listed in \
                         'bootstrap-peers' or provided with '--peer' \
                         option. Moreover, these peers will keep the \
                         identity and the address of the private node \
                         secret."
          bool false)
       (dft "limits"
          ~description: "Network limits"
          limit default_p2p_limits)
       (dft "disable_mempool"
          ~description: "If set to [true], the node will not participate in \
                         the propagation of pending operations (mempool). \
                         Default value is [false]. \
                         It can be used to decrease the memory and \
                         computation footprints of the node."
          bool false)
       (dft "disable_testchain"
          ~description: "If set to [true], the node will not spawn a testchain during \
                         the protocol's testing voting period. \
                         Default value is [false]. It may be used used to decrease the \
                         node storage usage and computation by droping the validation \
                         of the test network blocks."
          bool false)
    )

let rpc : rpc Data_encoding.t =
  let open Data_encoding in
  conv
    (fun { cors_origins ; cors_headers ; listen_addr ; tls } ->
       let cert, key =
         match tls with
         | None -> None, None
         | Some { cert ; key } -> Some cert, Some key in
       (listen_addr, cors_origins, cors_headers, cert, key ))
    (fun (listen_addr, cors_origins, cors_headers, cert, key ) ->
       let tls =
         match cert, key with
         | None, _ | _, None -> None
         | Some cert, Some key -> Some { cert ; key } in
       { listen_addr ; cors_origins ; cors_headers ; tls })
    (obj5
       (opt "listen-addr"
          ~description: "Host to listen to. If the port is not specified, \
                         the default port 8732 will be assumed."
          string)
       (dft "cors-origin"
          ~description: "Cross Origin Resource Sharing parameters, see \
                         https://en.wikipedia.org/wiki/Cross-origin_resource_sharing."
          (list string) default_rpc.cors_origins)
       (dft "cors-headers"
          ~description: "Cross Origin Resource Sharing parameters, see \
                         https://en.wikipedia.org/wiki/Cross-origin_resource_sharing."
          (list string) default_rpc.cors_headers)
       (opt "crt"
          ~description: "Certificate file (necessary when TLS is used)."
          string)
       (opt "key"
          ~description: "Key file (necessary when TLS is used)."
          string)
    )

let worker_limits_encoding
    default_size
    default_level =
  let open Data_encoding in
  conv
    (fun { Worker_types.backlog_size ; backlog_level ;} ->
       (backlog_size, backlog_level))
    (fun (backlog_size, backlog_level) ->
       { backlog_size ; backlog_level })
    (obj2
       (dft "worker_backlog_size" uint16 default_size)
       (dft "worker_backlog_level"
          Internal_event.Level.encoding default_level))

let timeout_encoding =
  Data_encoding.ranged_float 0. 500.

let block_validator_limits_encoding =
  let open Data_encoding in
  conv
    (fun { Node.protocol_timeout ; worker_limits } ->
       (protocol_timeout, worker_limits))
    (fun (protocol_timeout, worker_limits) ->
       { protocol_timeout ; worker_limits})
    (merge_objs
       (obj1
          (dft "protocol_request_timeout" timeout_encoding
             default_shell.block_validator_limits.protocol_timeout))
       (worker_limits_encoding
          default_shell.block_validator_limits.worker_limits.backlog_size
          default_shell.block_validator_limits.worker_limits.backlog_level))

let prevalidator_limits_encoding =
  let open Data_encoding in
  conv
    (fun { Node.operation_timeout ; max_refused_operations ; worker_limits } ->
       ((operation_timeout, max_refused_operations), worker_limits))
    (fun ((operation_timeout, max_refused_operations), worker_limits) ->
       { operation_timeout ; max_refused_operations ; worker_limits})
    (merge_objs
       (obj2
          (dft "operations_request_timeout" timeout_encoding
             default_shell.prevalidator_limits.operation_timeout)
          (dft "max_refused_operations" uint16
             default_shell.prevalidator_limits.max_refused_operations))
       (worker_limits_encoding
          default_shell.prevalidator_limits.worker_limits.backlog_size
          default_shell.prevalidator_limits.worker_limits.backlog_level
       ))

let peer_validator_limits_encoding =
  let open Data_encoding in
  let default_limits = default_shell.peer_validator_limits in
  conv
    (fun { Node.block_header_timeout ; block_operations_timeout ;
           protocol_timeout ; new_head_request_timeout ; worker_limits } ->
      ((block_header_timeout, block_operations_timeout,
        protocol_timeout, new_head_request_timeout), worker_limits))
    (fun ((block_header_timeout, block_operations_timeout,
           protocol_timeout, new_head_request_timeout), worker_limits) ->
      { block_header_timeout ; block_operations_timeout ;
        protocol_timeout ; new_head_request_timeout ; worker_limits })
    (merge_objs
       (obj4
          (dft "block_header_request_timeout" timeout_encoding default_limits.block_header_timeout)
          (dft "block_operations_request_timeout" timeout_encoding default_limits.block_operations_timeout)
          (dft "protocol_request_timeout" timeout_encoding default_limits.protocol_timeout)
          (dft "new_head_request_timeout" timeout_encoding default_limits.new_head_request_timeout))
       (worker_limits_encoding
          default_limits.worker_limits.backlog_size
          default_limits.worker_limits.backlog_level
       ))

let chain_validator_limits_encoding =
  let open Data_encoding in
  conv
    (fun { Node.bootstrap_threshold ; worker_limits } ->
       (bootstrap_threshold, worker_limits))
    (fun (bootstrap_threshold, worker_limits) ->
       { bootstrap_threshold ; worker_limits})
    (merge_objs
       (obj1
          (dft "bootstrap_threshold"
             ~description:
               "Set the number of peers with whom a chain synchronization must \
                be completed to bootstrap the node."
             uint8
             default_shell.chain_validator_limits.bootstrap_threshold))
       (worker_limits_encoding
          default_shell.chain_validator_limits.worker_limits.backlog_size
          default_shell.chain_validator_limits.worker_limits.backlog_level))

let shell =
  let open Data_encoding in
  conv
    (fun { peer_validator_limits ; block_validator_limits ;
           prevalidator_limits ; chain_validator_limits } ->
      (peer_validator_limits, block_validator_limits,
       prevalidator_limits, chain_validator_limits))
    (fun (peer_validator_limits, block_validator_limits,
          prevalidator_limits, chain_validator_limits) ->
      { peer_validator_limits ; block_validator_limits ;
        prevalidator_limits ; chain_validator_limits })
    (obj4
       (dft "peer_validator" peer_validator_limits_encoding default_shell.peer_validator_limits)
       (dft "block_validator" block_validator_limits_encoding default_shell.block_validator_limits)
       (dft "prevalidator" prevalidator_limits_encoding default_shell.prevalidator_limits)
       (dft "chain_validator" chain_validator_limits_encoding default_shell.chain_validator_limits)
    )

let encoding =
  let open Data_encoding in
  conv
    (fun { data_dir ; rpc ; p2p ; log ; internal_events ; shell } ->
       (data_dir, rpc, p2p, log, internal_events, shell))
    (fun (data_dir, rpc, p2p, log, internal_events, shell) ->
       { data_dir ; rpc ; p2p ; log ; internal_events ; shell })
    (obj6
       (dft "data-dir"
          ~description: "Location of the data dir on disk."
          string default_data_dir)
       (dft "rpc"
          ~description: "Configuration of rpc parameters"
          rpc default_rpc)
       (req "p2p"
          ~description: "Configuration of network parameters" p2p)
       (dft "log"
          ~description:
            "Configuration of the Lwt-log sink (part of the logging framework)"
          Lwt_log_sink_unix.cfg_encoding Lwt_log_sink_unix.default_cfg)
       (dft "internal-events"
          ~description: "Configuration of the structured logging framework"
          Internal_event_unix.Configuration.encoding
          Internal_event_unix.Configuration.default)
       (dft "shell"
          ~description: "Configuration of network parameters"
          shell default_shell))

let read fp =
  if Sys.file_exists fp then begin
    Lwt_utils_unix.Json.read_file fp >>=? fun json ->
    try return (Data_encoding.Json.destruct encoding json)
    with exn -> fail (Exn exn)
  end else
    return default_config

let write fp cfg =
  Node_data_version.ensure_data_dir (Filename.dirname fp) >>=? fun () ->
  Lwt_utils_unix.Json.write_file fp
    (Data_encoding.Json.construct encoding cfg)

let to_string cfg =
  Data_encoding.Json.to_string
    (Data_encoding.Json.construct encoding cfg)

let update
    ?data_dir
    ?min_connections
    ?expected_connections
    ?max_connections
    ?max_download_speed
    ?max_upload_speed
    ?binary_chunks_size
    ?peer_table_size
    ?expected_pow
    ?bootstrap_peers
    ?listen_addr
    ?discovery_addr
    ?rpc_listen_addr
    ?(private_mode = false)
    ?(disable_mempool = false)
    ?(disable_testchain = false)
    ?(cors_origins = [])
    ?(cors_headers = [])
    ?rpc_tls
    ?log_output
    ?bootstrap_threshold
    cfg = let data_dir = Option.unopt ~default:cfg.data_dir data_dir in
  Node_data_version.ensure_data_dir data_dir >>=? fun () ->
  let peer_table_size =
    Option.map peer_table_size ~f:(fun i -> i, i / 4 * 3) in
  let unopt_list ~default = function
    | [] -> default
    | l -> l in
  let limits : P2p.limits = {
    cfg.p2p.limits with
    min_connections =
      Option.unopt
        ~default:cfg.p2p.limits.min_connections
        min_connections ;
    expected_connections =
      Option.unopt
        ~default:cfg.p2p.limits.expected_connections
        expected_connections ;
    max_connections =
      Option.unopt
        ~default:cfg.p2p.limits.max_connections
        max_connections ;
    max_download_speed =
      Option.first_some
        max_download_speed cfg.p2p.limits.max_download_speed ;
    max_upload_speed =
      Option.first_some
        max_upload_speed cfg.p2p.limits.max_upload_speed ;
    max_known_points =
      Option.first_some
        peer_table_size cfg.p2p.limits.max_known_points ;
    max_known_peer_ids =
      Option.first_some
        peer_table_size cfg.p2p.limits.max_known_peer_ids ;
    binary_chunks_size =
      Option.map ~f:(fun x -> x lsl 10) binary_chunks_size ;
  } in
  let p2p : p2p = {
    expected_pow =
      Option.unopt ~default:cfg.p2p.expected_pow expected_pow ;
    bootstrap_peers =
      Option.unopt ~default:cfg.p2p.bootstrap_peers bootstrap_peers ;
    listen_addr =
      Option.first_some listen_addr cfg.p2p.listen_addr ;
    discovery_addr =
      Option.first_some discovery_addr cfg.p2p.discovery_addr ;
    private_mode = cfg.p2p.private_mode || private_mode ;
    limits ;
    disable_mempool = cfg.p2p.disable_mempool || disable_mempool ;
    disable_testchain = cfg.p2p.disable_testchain || disable_testchain ;
  }
  and rpc : rpc = {
    listen_addr =
      Option.first_some rpc_listen_addr cfg.rpc.listen_addr ;
    cors_origins =
      unopt_list ~default:cfg.rpc.cors_origins cors_origins ;
    cors_headers =
      unopt_list ~default:cfg.rpc.cors_headers cors_headers ;
    tls =
      Option.first_some rpc_tls cfg.rpc.tls ;
  }
  and log : Lwt_log_sink_unix.cfg = {
    cfg.log with
    output = Option.unopt ~default:cfg.log.output log_output ;
  }
  and shell : shell = {
    peer_validator_limits = cfg.shell.peer_validator_limits ;
    block_validator_limits = cfg.shell.block_validator_limits ;
    prevalidator_limits = cfg.shell.prevalidator_limits ;
    chain_validator_limits =
      Option.unopt_map
        ~default:cfg.shell.chain_validator_limits
        ~f:(fun bootstrap_threshold ->
            { cfg.shell.chain_validator_limits
              with bootstrap_threshold })
        bootstrap_threshold
  }
  in
  let internal_events = cfg.internal_events in
  return { data_dir ; p2p ; rpc ; log ; internal_events ; shell }

let resolve_addr ~default_addr ?default_port ?(passive = false) peer =
  let addr, port = P2p_point.Id.parse_addr_port peer in
  let node = if addr = "" || addr = "_" then default_addr else addr
  and service =
    match port, default_port with
    | "", None -> invalid_arg ""
    | "", Some default_port -> string_of_int default_port
    | port, _ -> port in
  Lwt_utils_unix.getaddrinfo ~passive ~node ~service

let resolve_addrs ~default_addr ?default_port ?passive peers =
  Lwt_list.fold_left_s begin fun a peer ->
    resolve_addr ~default_addr ?default_port ?passive peer >>= fun points ->
    Lwt.return (List.rev_append points a)
  end [] peers

let resolve_discovery_addrs discovery_addr =
  resolve_addr
    ~default_addr:Ipaddr.V4.(to_string broadcast)
    ~default_port:default_discovery_port
    ~passive:true
    discovery_addr
  >>= fun addrs ->
  let rec to_ipv4 acc = function
    | [] -> Lwt.return (List.rev acc)
    | (ip, port) :: xs -> begin match Ipaddr.v4_of_v6 ip with
        | Some v -> to_ipv4 ((v, port) :: acc) xs
        | None ->
            Format.eprintf
              "Warning: failed to convert %S to an ipv4 address@."
              (Ipaddr.V6.to_string ip) ;
            to_ipv4 acc xs
      end
  in to_ipv4 [] addrs

let resolve_listening_addrs listen_addr =
  resolve_addr
    ~default_addr:"::"
    ~default_port:default_p2p_port
    ~passive:true
    listen_addr

let resolve_rpc_listening_addrs listen_addr =
  resolve_addr
    ~default_addr:"::"
    ~default_port:default_rpc_port
    ~passive:true
    listen_addr

let resolve_bootstrap_addrs peers =
  resolve_addrs
    ~default_addr:"::"
    ~default_port:default_p2p_port
    peers

let check_listening_addr config =
  match config.p2p.listen_addr with
  | None -> Lwt.return_unit
  | Some addr ->
      Lwt.catch begin fun () ->
        resolve_listening_addrs addr >>= function
        | [] ->
            Format.eprintf "Warning: failed to resolve %S\n@." addr ;
            Lwt.return_unit
        | _ :: _ ->
            Lwt.return_unit
      end begin function
        | (Invalid_argument msg) ->
            Format.eprintf "Warning: failed to parse %S:\   %s\n@." addr msg ;
            Lwt.return_unit
        | exn -> Lwt.fail exn
      end

let check_discovery_addr config =
  match config.p2p.discovery_addr with
  | None -> Lwt.return_unit
  | Some addr ->
      Lwt.catch begin fun () ->
        resolve_discovery_addrs addr >>= function
        | [] ->
            Format.eprintf "Warning: failed to resolve %S\n@." addr ;
            Lwt.return_unit
        | _ :: _ ->
            Lwt.return_unit
      end begin function
        | (Invalid_argument msg) ->
            Format.eprintf "Warning: failed to parse %S:\   %s\n@." addr msg ;
            Lwt.return_unit
        | exn -> Lwt.fail exn
      end

let check_rpc_listening_addr config =
  match config.rpc.listen_addr with
  | None -> Lwt.return_unit
  | Some addr ->
      Lwt.catch begin fun () ->
        resolve_rpc_listening_addrs addr >>= function
        | [] ->
            Format.eprintf "Warning: failed to resolve %S\n@." addr ;
            Lwt.return_unit
        | _ :: _ ->
            Lwt.return_unit
      end begin function
        | (Invalid_argument msg) ->
            Format.eprintf "Warning: failed to parse %S:\   %s\n@." addr msg ;
            Lwt.return_unit
        | exn -> Lwt.fail exn
      end

let check_bootstrap_peer addr =
  Lwt.catch begin fun () ->
    resolve_bootstrap_addrs [addr] >>= function
    | [] ->
        Format.eprintf "Warning: cannot resolve %S\n@." addr ;
        Lwt.return_unit
    | _ :: _ ->
        Lwt.return_unit
  end begin function
    | (Invalid_argument msg) ->
        Format.eprintf "Warning: failed to parse %S:\   %s\n@." addr msg ;
        Lwt.return_unit
    | exn -> Lwt.fail exn
  end


let check_bootstrap_peers config =
  Lwt_list.iter_p check_bootstrap_peer config.p2p.bootstrap_peers


let fail fmt =
  Format.kasprintf (fun s -> prerr_endline s ; exit 1) fmt

let check_connections config =
  if config.p2p.limits.min_connections > config.p2p.limits.expected_connections then
    fail "Error: The minumum number of connections is greater than \
          the expected number of connections"
      config.p2p.limits.min_connections
      config.p2p.limits.expected_connections ;
  if config.p2p.limits.expected_connections > config.p2p.limits.max_connections then
    fail "Error: The expected number of connections is greater than \
          the maximum number of connections"
      config.p2p.limits.expected_connections
      config.p2p.limits.max_connections ;
  begin
    match config.p2p.limits.max_known_peer_ids with
    | None -> ()
    | Some (max_known_peer_ids, target_known_peer_ids) ->
        if target_known_peer_ids > max_known_peer_ids then
          fail "Error: The target number of known peer ids is greater than \
                the maximum number of known peer ids."
            target_known_peer_ids max_known_peer_ids ;
        if config.p2p.limits.max_connections > target_known_peer_ids then
          fail "Error: The target number of known peer ids is lower than \
                the maximum number of connections."
            target_known_peer_ids max_known_peer_ids ;
  end ;
  begin
    match config.p2p.limits.max_known_points with
    | None -> ()
    | Some (max_known_points, target_known_points) ->
        if target_known_points > max_known_points then
          fail "Error: The target number of known points is greater than \
                the maximum number of known points."
            target_known_points max_known_points ;
        if config.p2p.limits.max_connections > target_known_points then
          fail "Error: The target number of known points is lower than \
                the maximum number of connections."
            target_known_points max_known_points ;
  end


let check config =
  check_listening_addr config >>= fun () ->
  check_rpc_listening_addr config >>= fun () ->
  check_discovery_addr config >>= fun () ->
  check_bootstrap_peers config >>= fun () ->
  check_connections config ;
  Lwt.return_unit
back to top