diff --git a/README.md b/README.md index 433ee3895..55d991134 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Running the node in a sandbox To run a single instance of a Tezos node in sandbox mode: ``` -./tezos-node --sandbox /path/to/a/custom/data/dir --rpc-addr :::8732 +./tezos-node --sandbox --rpc-addr :::8732 ``` This "sandboxed" node will not participate in the P2P network, but will accept @@ -77,20 +77,22 @@ test network. Use the following command to run a node that will accept incoming connections: ``` -./tezos-node +./tezos-node --generate-identity --expected-pow 24. ``` -The node will listen to connections coming in on `0.0.0.0:9732` (and -`[::]:9732`). All used data is stored at `$HOME/.tezos-node/`. For example, -the default configuration file is at `$HOME/.tezos-node/config`. +This will first generate a new node identity and compute the associated stamp +of proof-of-work. Then, the node will listen to connections coming in on +`0.0.0.0:9732` (and`[::]:9732`). All used data is stored at +`$HOME/.tezos-node/`. For example, the default configuration file is +at `$HOME/.tezos-node/config.json`. To run multiple nodes on the same machine, you can duplicate and edit -`$HOME/.tezos-node/config` while making sure they don't share paths to the +`$HOME/.tezos-node/config.json` while making sure they don't share paths to the database or any other data file (cf. options `db.store` ; `db.context` ; -`net.peers` and `protocol.dir`). +`db.protocol`, `net.peers-metadata` and `net.identity`). You could also let Tezos generate a config file by specifying options on the -command line. For instance, if `$dir/config` does not exist, the following +command line. For instance, if `$dir/config.json` does not exist, the following command will generate it and replace the default values with the values from the command line: @@ -102,20 +104,23 @@ The Tezos server has a built-in mechanism to discover peers on the local network (using UDP packets broadcasted on port 7732). If this mechanism is not sufficient, one can provide Tezos with a list of -initial peers, either by editing the option `net.bootstrap.peers` in the -`config` file, or by specifying a command line parameter: +initial peers, either by editing the option `net.bootstrap-peers` in the +`config.json` file, or by specifying a command line parameter: ``` ./tezos-node --base-dir "$dir" --net-addr 127.0.0.1:2023 \ --peer 127.0.0.1:2021 --peer 127.0.0.1:2022 ``` -If `"$dir"/config` exists, the command line options override those read in the -config file. Tezos won't modify the content of an existing `"$dir"/config` -file. +If `"$dir"/config.json` exists, the command line options override those +read in the config file. By default, Tezos won't modify the content of an +existing `"$dir"/config.json` file. But, you may explicit ask the node +to reset or to update the file according to the command line parameters +with the following commands line: ``` -./tezos-node --config-file "$dir"/config +./tezos-node --reset-config --base-dir "$dir" --net-addr 127.0.0.1:9733 +./tezos-node --update-config --base-dir "$dir" --net-addr 127.0.0.1:9734 ``` @@ -129,7 +134,7 @@ Typically, if you are not trying to run a local network and just want to explore the RPC, you would run: ``` -./tezos-node --sandbox /path/to/a/custom/data/dir --rpc-addr :::8732 +./tezos-node --sandbox --rpc-addr :::8732 ``` The RPC interface is self-documented and the `tezos-client` executable is able @@ -151,7 +156,7 @@ You might also want the JSON schema describing the expected input and output of a RPC. For instance: ``` -./tezos-client rpc schema /block/genesis/hash +./tezos-client rpc schema /blocks/genesis/hash ``` Note: you can get the same information, but as a raw JSON object, with a simple @@ -170,4 +175,4 @@ The minimal CLI client Work in progress. -See `./tezos-client -help` for available commands. \ No newline at end of file +See `./tezos-client -help` for available commands. diff --git a/src/node/net/p2p_connection_pool_types.ml b/src/node/net/p2p_connection_pool_types.ml index d2d0bec6c..dabbc5a96 100644 --- a/src/node/net/p2p_connection_pool_types.ml +++ b/src/node/net/p2p_connection_pool_types.ml @@ -449,7 +449,7 @@ module Gid_info = struct let load path metadata_encoding = let enc = Data_encoding.list (encoding metadata_encoding) in - if Sys.file_exists path then + if path <> "/dev/null" && Sys.file_exists path then Data_encoding_ezjsonm.read_file path >>=? fun json -> return (Data_encoding.Json.destruct enc json) else diff --git a/src/node/shell/node.ml b/src/node/shell/node.ml index aa8d16c55..6adccc162 100644 --- a/src/node/shell/node.ml +++ b/src/node/shell/node.ml @@ -215,9 +215,17 @@ let init_p2p net_params = Lwt.async (fun () -> Tezos_p2p.maintain p2p) ; Lwt.return p2p +type config = { + genesis: Store.genesis ; + store_root: string ; + context_root: string ; + test_protocol: Protocol_hash.t option ; + patch_context: (Context.t -> Context.t Lwt.t) option ; + p2p: (P2p.config * P2p.limits) option ; +} -let create - ~genesis ~store_root ~context_root ?test_protocol ?patch_context net_params = +let create { genesis ; store_root ; context_root ; + test_protocol ; patch_context ; p2p = net_params } = lwt_debug "-> Node.create" >>= fun () -> init_p2p net_params >>= fun p2p -> lwt_log_info "reading state..." >>= fun () -> diff --git a/src/node/shell/node.mli b/src/node/shell/node.mli index 997b71ca9..c21398043 100644 --- a/src/node/shell/node.mli +++ b/src/node/shell/node.mli @@ -9,14 +9,16 @@ type t -val create: - genesis:Store.genesis -> - store_root:string -> - context_root:string -> - ?test_protocol:Protocol_hash.t -> - ?patch_context:(Context.t -> Context.t Lwt.t) -> - (P2p.config * P2p.limits) option -> - t tzresult Lwt.t +type config = { + genesis: Store.genesis ; + store_root: string ; + context_root: string ; + test_protocol: Protocol_hash.t option ; + patch_context: (Context.t -> Context.t Lwt.t) option ; + p2p: (P2p.config * P2p.limits) option ; +} + +val create: config -> t tzresult Lwt.t module RPC : sig diff --git a/src/node_main.ml b/src/node_main.ml index 0d6ea52ad..8fd695584 100644 --- a/src/node_main.ml +++ b/src/node_main.ml @@ -9,9 +9,18 @@ module V6 = Ipaddr.V6 +open Hash open Error_monad open Logging.Node.Main +let (//) = Filename.concat + +let home = + try Sys.getenv "HOME" + with Not_found -> "/root" + +let default_base_dir = home // ".tezos-node" + let genesis_block = Block_hash.of_b48check "grHGHkVfgJb5gPaRd5AtQsa65g9GyLcXgQsHbSnQ5SD5DEp2ctqck" @@ -33,357 +42,557 @@ let genesis = { protocol = genesis_protocol ; } -let (//) = Filename.concat +module Sockaddr = struct -let home = - try Sys.getenv "HOME" - with Not_found -> "/root" + type t = V6.t * int -let default_base_dir = home // ".tezos-node" + let of_string str = + match String.rindex str ':' with + | exception Not_found -> `Error "not a sockaddr" + | pos -> + let len = String.length str in + let addr, port = + String.sub str 0 pos, String.sub str (pos+1) (len - pos - 1) in + match Ipaddr.of_string_exn addr, int_of_string port with + | exception Failure _ -> `Error "not a sockaddr" + | V4 ipv4, port -> `Ok (Ipaddr.v6_of_v4 ipv4, port) + | V6 ipv6, port -> `Ok (ipv6, port) -type cfg = { - (* cli *) - base_dir : string ; - sandbox : string option ; - sandbox_param : string option ; + let of_string_exn str = + match of_string str with + | `Ok saddr -> saddr + | `Error msg -> invalid_arg msg - (* db *) + let pp fmt (ip, port) = Format.fprintf fmt "%a:%d" V6.pp_hum ip port + let to_string saddr = Format.asprintf "%a" pp saddr + + let encoding = + Data_encoding.conv to_string of_string_exn Data_encoding.string + + let converter : t Cmdliner.Arg.converter = of_string, pp + +end + +module Cfg_file = struct + + open Data_encoding + + type t = { + db : db ; + net : net ; + rpc : rpc ; + log : log ; + } + + and db = { store : string ; context : string ; protocol : string ; - - (* net *) - min_connections : int ; - max_connections : int ; - expected_connections : int ; - net_addr : V6.t ; - net_port : int ; - (* local_discovery : (string * int) option ; *) - peers : (V6.t * int) list ; - peers_cache : string ; - closed : bool ; - - (* rpc *) - rpc_addr : (V6.t * int) option ; - cors_origins : string list ; - cors_headers : string list ; - rpc_crt : string option ; - rpc_key : string option ; - - (* log *) - log_output : [`Stderr | `File of string | `Syslog | `Null] ; - log_level : Lwt_log.level option ; } -let default_cfg_of_base_dir base_dir = { - (* cli *) - base_dir ; - sandbox = None ; - sandbox_param = None ; + and net = { + identity : string ; + expected_pow : float ; + bootstrap_peers : Sockaddr.t list ; + peers_metadata : string ; + listen_addr : Sockaddr.t option ; + closed : bool ; + limits : P2p.limits ; + } - (* db *) - store = base_dir // "store" ; - context = base_dir // "context" ; - protocol = base_dir // "protocol" ; + and rpc = { + listen_addr : Sockaddr.t option ; + cors_origins : string list ; + cors_headers : string list ; + tls : tls option ; + } - (* net *) - min_connections = 4 ; - max_connections = 400 ; - expected_connections = 20 ; - net_addr = V6.unspecified ; - net_port = 9732 ; - (* local_discovery = None ; *) - peers = [] ; - closed = false ; - peers_cache = base_dir // "peers_cache" ; + and tls = { + cert : string ; + key : string ; + } - (* rpc *) - rpc_addr = None ; - cors_origins = [] ; - cors_headers = ["content-type"] ; - rpc_crt = None ; - rpc_key = None ; + and log = { + output : Logging.kind ; + default_level : Logging.level ; + rules : string option ; + template : Logging.template ; + } - (* log *) - log_output = `Stderr ; - log_level = None ; -} + let default_net_limits : P2p.limits = { + authentification_timeout = 5. ; + min_connections = 50 ; + expected_connections = 100 ; + max_connections = 200 ; + 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 ; + } -let default_cfg = default_cfg_of_base_dir default_base_dir + let default_net base_dir = { + identity = base_dir // "identity.json" ; + expected_pow = 24. ; + bootstrap_peers = [] ; + peers_metadata = base_dir // "peers.json" ; + listen_addr = Some (V6.unspecified, 8732) ; + closed = false ; + limits = default_net_limits ; + } -let log_of_string s = match Utils.split ':' ~limit:2 s with - | ["stderr"] -> `Stderr - | ["file"; fn] -> `File fn - | ["syslog"] -> `Syslog - | ["null"] -> `Null - | _ -> invalid_arg "log_of_string" + let default_rpc = { + listen_addr = None ; + cors_origins = [] ; + cors_headers = [] ; + tls = None ; + } -let string_of_log = function - | `Stderr -> "stderr" - | `File fn -> "file:" ^ fn - | `Syslog -> "syslog" - | `Null -> "null" + let default_log = { + output = Stderr ; + default_level = Notice ; + rules = None ; + template = Logging.default_template ; + } -let sockaddr_of_string str = - match String.rindex str ':' with - | exception Not_found -> `Error "not a sockaddr" - | pos -> - let len = String.length str in - let addr, port = String.sub str 0 pos, String.sub str (pos+1) (len - pos - 1) in - match Ipaddr.of_string_exn addr, int_of_string port with - | exception Failure _ -> `Error "not a sockaddr" - | V4 ipv4, port -> `Ok (Ipaddr.v6_of_v4 ipv4, port) - | V6 ipv6, port -> `Ok (ipv6, port) + let default_db base_dir = { + store = base_dir // "store" ; + context = base_dir // "context" ; + protocol = base_dir // "protocol" ; + } -let sockaddr_of_string_exn str = - match sockaddr_of_string str with - | `Ok saddr -> saddr - | `Error msg -> invalid_arg msg - -let pp_sockaddr fmt (ip, port) = Format.fprintf fmt "%a:%d" V6.pp_hum ip port -let string_of_sockaddr saddr = Format.asprintf "%a" pp_sockaddr saddr - -let mcast_params_of_string s = match Utils.split ':' s with - | [iface; port] -> iface, int_of_string port - | _ -> invalid_arg "mcast_params_of_string" - -module Cfg_file = struct - open Data_encoding + let default_config base_dir = { + db = default_db base_dir ; + net = default_net base_dir ; + rpc = default_rpc ; + log = default_log ; + } let db = - obj3 - (opt "store" string) - (opt "context" string) - (opt "protocol" string) + let default = default_db default_base_dir in + conv + (fun { store ; context ; protocol } -> + (store, context, protocol)) + (fun (store, context, protocol) -> + { store ; context ; protocol }) + (obj3 + (dft "store" string default.store) + (dft "context" string default.context) + (dft "protocol" string default.protocol)) + + let limit : P2p.limits Data_encoding.t = + conv + (fun { P2p.authentification_timeout ; + 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 } -> + ( ( authentification_timeout, 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 ))) + (fun ( ( authentification_timeout, 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 ) ) -> + { authentification_timeout ; 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 }) + (merge_objs + (obj8 + (dft "authentification_timeout" + float default_net_limits.authentification_timeout) + (dft "min_connections" int31 + default_net_limits.min_connections) + (dft "expected_connections" int31 + default_net_limits.expected_connections) + (dft "max_connections" int31 + default_net_limits.max_connections) + (dft "backlog" int31 + default_net_limits.backlog) + (dft "max_incoming_connections" int31 + default_net_limits.max_incoming_connections) + (opt "max_download_speed" int31) + (opt "max_upload_speed" int31)) + (obj6 + (dft "read_buffer_size" int31 + default_net_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))) let net = - obj7 - (opt "min-connections" uint16) - (opt "max-connections" uint16) - (opt "expected-connections" uint16) - (opt "addr" string) - (* (opt "local-discovery" string) *) - (opt "peers" (list string)) - (dft "closed" bool false) - (opt "peers-cache" string) + let default = default_net default_base_dir in + conv + (fun { identity ; expected_pow ; bootstrap_peers ; peers_metadata ; + listen_addr ; closed ; limits } -> + ( identity, expected_pow, bootstrap_peers, peers_metadata, + listen_addr, closed, limits )) + (fun ( identity, expected_pow, bootstrap_peers, peers_metadata, + listen_addr, closed, limits ) -> + { identity ; expected_pow ; bootstrap_peers ; peers_metadata ; + listen_addr ; closed ; limits }) + (obj7 + (dft "identity" string default.identity) + (dft "expected-proof-or-work" float default.expected_pow) + (dft "bootstrap_peers" + (list Sockaddr.encoding) default.bootstrap_peers) + (dft "peers-metadata" string default.peers_metadata) + (opt "listen-addr" Sockaddr.encoding) + (dft "closed" bool false) + (dft "limits" limit default_net_limits)) - let rpc = - obj3 - (opt "addr" string) - (dft "cors-origin" (list string) []) - (dft "cors-header" (list string) []) + let rpc : rpc Data_encoding.t = + 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" Sockaddr.encoding) + (dft "cors-origin" (list string) default_rpc.cors_origins) + (dft "cors-headers" (list string) default_rpc.cors_headers) + (opt "crt" string) + (opt "key" string)) let log = - obj1 - (opt "output" string) - - let t = conv - (fun { store ; context ; protocol ; - min_connections ; max_connections ; expected_connections ; - net_addr ; net_port ; - (* local_discovery ; *) - peers ; - closed ; peers_cache ; rpc_addr ; cors_origins ; cors_headers ; log_output } -> - let net_addr = string_of_sockaddr (net_addr, net_port) in - (* let local_discovery = Utils.map_option local_discovery *) - (* ~f:(fun (iface, port) -> iface ^ ":" ^ string_of_int port) *) - (* in *) - let rpc_addr = Utils.map_option string_of_sockaddr rpc_addr in - let peers = ListLabels.map peers ~f:string_of_sockaddr in - let log_output = string_of_log log_output in - ((Some store, Some context, Some protocol), - (Some min_connections, Some max_connections, Some expected_connections, - Some net_addr, - (* local_discovery, *) - Some peers, closed, Some peers_cache), - (rpc_addr, cors_origins, cors_headers), - Some log_output)) - (fun ( - (store, context, protocol), - (min_connections, max_connections, expected_connections, net_addr, - (* local_discovery, *) - peers, closed, peers_cache), - (rpc_addr, cors_origins, cors_headers), - log_output) -> - let open Utils in - let store = unopt ~default:default_cfg.store store in - let context = unopt ~default:default_cfg.context context in - let protocol = unopt ~default:default_cfg.protocol protocol in - let net_addr = map_option sockaddr_of_string_exn net_addr in - let net_addr, net_port = unopt ~default:(default_cfg.net_addr, default_cfg.net_port) net_addr in - let rpc_addr = map_option sockaddr_of_string_exn rpc_addr in - let peers = unopt ~default:[] peers in - let peers = ListLabels.map peers ~f:sockaddr_of_string_exn in - let peers_cache = unopt ~default:default_cfg.peers_cache peers_cache in - let log_output = unopt ~default:default_cfg.log_output (map_option log_of_string log_output) in - let min_connections = unopt ~default:default_cfg.min_connections min_connections in - let max_connections = unopt ~default:default_cfg.max_connections max_connections in - let expected_connections = unopt ~default:default_cfg.expected_connections expected_connections in - (* let local_discovery = map_option local_discovery ~f:mcast_params_of_string in *) - { default_cfg with - store ; context ; protocol ; - min_connections ; max_connections ; expected_connections ; - net_addr ; net_port ; - (* local_discovery ; *) - peers ; closed ; peers_cache ; - rpc_addr ; cors_origins ; cors_headers ; log_output ; - } - ) + (fun {output ; default_level ; rules ; template } -> + (output, default_level, rules, template)) + (fun (output, default_level, rules, template) -> + { output ; default_level ; rules ; template }) (obj4 - (req "db" db) + (dft "output" Logging.kind_encoding default_log.output) + (dft "level" Logging.level_encoding default_log.default_level) + (opt "rules" string) + (dft "template" string default_log.template)) + + let encoding = + conv + (fun { db ; rpc ; net ; log } -> (db, rpc, net, log)) + (fun (db, rpc, net, log) -> { db ; rpc ; net ; log }) + (obj4 + (dft "db" db (default_db default_base_dir)) + (dft "rpc" rpc default_rpc) (req "net" net) - (req "rpc" rpc) - (req "log" log)) + (dft "log" log default_log)) let read fp = - Data_encoding_ezjsonm.read_file fp >|= function - | Error _ -> None - | Ok json -> Some (Data_encoding.Json.destruct t json) + Data_encoding_ezjsonm.read_file fp >>=? fun json -> + try return (Data_encoding.Json.destruct encoding json) + with exn -> fail (Exn exn) + + let write fp cfg = + Data_encoding_ezjsonm.write_file fp + (Data_encoding.Json.construct encoding cfg) - let from_json json = Data_encoding.Json.destruct t json - let write out cfg = - Utils.write_file ~bin:false out - (Data_encoding.Json.construct t cfg |> - Data_encoding_ezjsonm.to_string) end module Cmdline = struct - open Cmdliner - (* custom converters *) - let sockaddr_converter = sockaddr_of_string, pp_sockaddr + type t = { + sandbox : string option option ; + verbosity : Logging.level option ; + generate_identity : bool ; + write_cfg : 'a 'b 'c 'd. (string * (string -> 'a, 'b, 'c, 'a) format4) option ; + } + + open Cmdliner (* cli args *) let misc_sect = "MISC" + let base_dir = - let doc = "The directory where the Tezos node will store all its data." in - Arg.(value & opt (some string) None & info ~docs:"CONFIG" ~doc ~docv:"DIR" ["base-dir"]) + let doc = + "The directory where the Tezos node will store all its data." in + Arg.(value & opt (some string) None & + info ~docs:"CONFIG" ~doc ~docv:"DIR" ["base-dir"]) + let config_file = let doc = "The main configuration file." in - Arg.(value & opt (some string) None & info ~docs:"CONFIG" ~doc ~docv:"FILE" ["config-file"]) + Arg.(value & opt (some string) None & + info ~docs:"CONFIG" ~doc ~docv:"FILE" ["config-file"]) + let sandbox = - let doc = "Run the daemon in a sandbox (P2P is disabled, data is stored in a custom directory)." in - Arg.(value & opt (some string) None & info ~docs:"NETWORK" ~doc ~docv:"DIR" ["sandbox"]) - let sandbox_param = - let doc = "Custom parameter for the economical protocol." in - Arg.(value & opt (some string) None & info ~docs:"NETWORK" ~doc ["sandbox-param"]) - let v = - let doc = "Increase log level. Use several times to increase log level, e.g. `-vv'." in + let doc = + "Run the daemon in a sandbox: P2P is disabled, and constants of \ + the economical protocol might be altered by the optionnal JSON file." + in + Arg.(value & opt ~vopt:(Some None) (some (some string)) None & + info ~docs:"NETWORK" ~doc ~docv:"FILE.json" ["sandbox"]) + + let verbosity = + let doc = + "Increase log level. \ + Use several times to increase log level, e.g. `-vv'." in Arg.(value & flag_all & info ~docs:misc_sect ~doc ["v"]) - (* net args *) - let min_connections = - let doc = "The number of connections below which aggressive peer discovery mode is entered." in - Arg.(value & opt (some int) None & info ~docs:"NETWORK" ~doc ~docv:"NUM" ["min-connections"]) - let max_connections = - let doc = "The number of connections above which some connections will be closed." in - Arg.(value & opt (some int) None & info ~docs:"NETWORK" ~doc ~docv:"NUM" ["max-connections"]) - let expected_connections = - let doc = "The minimum number of connections to be ensured by the cruise control." in - Arg.(value & opt (some int) None & info ~docs:"NETWORK" ~doc ~docv:"NUM" ["expected-connections"]) - let net_addr = - let doc = "The TCP address and port at which this instance can be reached." in - Arg.(value & opt (some sockaddr_converter) None & info ~docs:"NETWORK" ~doc ~docv:"ADDR:PORT" ["net-addr"]) - (* let local_discovery = *) - (* let doc = "Automatic discovery of peers on the local network." in *) - (* Arg.(value & opt (some @@ pair string int) None & info ~docs:"NETWORK" ~doc ~docv:"IFACE:PORT" ["local-discovery"]) *) - let peers = - let doc = "A peer to bootstrap the network from. Can be used several times to add several peers." in - Arg.(value & opt_all sockaddr_converter [] & info ~docs:"NETWORK" ~doc ~docv:"ADDR:PORT" ["peer"]) - let closed = - let doc = "Only accept connections from the bootstrap peers." in - Arg.(value & flag & info ~docs:"NETWORK" ~doc ["closed"]) + let reset_config = let doc = "Overwrite config file with factory defaults." in Arg.(value & flag & info ~docs:"CONFIG" ~doc ["reset-config"]) + let update_config = let doc = "Update config file with values from the command line." in Arg.(value & flag & info ~docs:"CONFIG" ~doc ["update-config"]) + let generate_identity = + let doc = + "Generate a new cryptographic identity for the node. \ + It also generates the associated stamp of proof-of-work. \ + See `--expected-pow` for adjusting the required amount of \ + proof-of-work" in + Arg.(value & flag & info ~docs:"CONFIG" ~doc ["generate-identity"]) + + (* net args *) + let expected_connections = + let doc = + "The number of running connections that we must try to maintain + (approximativaly)." in + Arg.(value & opt (some int) None & + info ~docs:"NETWORK" ~doc ~docv:"NUM" ["expected-connections"]) + + let max_download_speed = + let doc = + "The maximum number of bytes read per second." in + Arg.(value & opt (some int) None & + info ~docs:"NETWORK" ~doc ~docv:"NUM" ["max-download-speed"]) + + let max_upload_speed = + let doc = + "The maximum number of bytes sent per second." in + Arg.(value & opt (some int) None & + info ~docs:"NETWORK" ~doc ~docv:"NUM" ["max-upload-speed"]) + + let listen_addr = + let doc = + "The TCP address and port at which this instance can be reached." in + Arg.(value & opt (some Sockaddr.converter) None & + info ~docs:"NETWORK" ~doc ~docv:"ADDR:PORT" ["net-addr"]) + + let peers = + let doc = + "A peer to bootstrap the network from. \ + Can be used several times to add several peers." in + Arg.(value & opt_all Sockaddr.converter [] & + info ~docs:"NETWORK" ~doc ~docv:"ADDR:PORT" ["peer"]) + + let expected_pow = + let doc = + "Expected level of proof-of-work for peers identity." in + Arg.(value & opt (some float) None & + info ~docs:"NETWORK" ~doc ~docv:"FLOAT" ["expected-pow"]) + + let closed = + let doc = + "Only accept connections from the configured bootstrap peers." in + Arg.(value & flag & info ~docs:"NETWORK" ~doc ["closed"]) + (* rpc args *) - let rpc_addr = - let doc = "The TCP socket address at which this RPC server instance can be reached." in - Arg.(value & opt (some sockaddr_converter) None & info ~docs:"RPC" ~doc ~docv:"ADDR:PORT" ["rpc-addr"]) + let rpc_listen_addr = + let doc = + "The TCP socket address at which this RPC server \ + instance can be reached." in + Arg.(value & opt (some Sockaddr.converter) None & + info ~docs:"RPC" ~doc ~docv:"ADDR:PORT" ["rpc-addr"]) + let rpc_tls = - let doc = "Enable TLS for this RPC server with the provided certificate and key." in - Arg.(value & opt (some (pair string string)) None & info ~docs:"RPC" ~doc ~docv:"crt,key" ["rpc-tls"]) + let doc = + "Enable TLS for this RPC server \ + with the provided certificate and key." in + Arg.(value & opt (some (pair string string)) None & + info ~docs:"RPC" ~doc ~docv:"crt,key" ["rpc-tls"]) + let cors_origins = - let doc = "CORS origin allowed by the RPC server via Access-Control-Allow-Origin; may be used multiple times" in - Arg.(value & opt_all string [] & info ~docs:"RPC" ~doc ~docv:"ORIGIN" ["cors-origin"]) + let doc = + "CORS origin allowed by the RPC server \ + via Access-Control-Allow-Origin; may be used multiple times" in + Arg.(value & opt_all string [] & + info ~docs:"RPC" ~doc ~docv:"ORIGIN" ["cors-origin"]) + let cors_headers = - let doc = "Header reported by Access-Control-Allow-Headers reported during CORS preflighting; may be used multiple times" in - Arg.(value & opt_all string [] & info ~docs:"RPC" ~doc ~docv:"HEADER" ["cors-header"]) + let doc = + "Header reported by Access-Control-Allow-Headers \ + reported during CORS preflighting; may be used multiple times" in + Arg.(value & opt_all string [] & + info ~docs:"RPC" ~doc ~docv:"HEADER" ["cors-header"]) - let parse base_dir config_file sandbox sandbox_param log_level - min_connections max_connections expected_connections - net_saddr - (* local_discovery *) - peers closed rpc_addr tls cors_origins cors_headers reset_cfg update_cfg = + exception Fail of string + let fail fmt = + Format.kasprintf (fun msg -> Lwt.fail (Fail msg)) fmt - let base_dir = Utils.(unopt ~default:(unopt ~default:default_cfg.base_dir base_dir) sandbox) in - let config_file = Utils.(unopt ~default:((unopt ~default:base_dir sandbox) // "config")) config_file in - let no_config () = - warn "Found no config file at %s" config_file; - warn "Using factory defaults"; - default_cfg_of_base_dir base_dir - in - let corrupted_config msg = - log_error "Config file %s corrupted: %s" config_file msg; - warn "Using factory defaults"; - default_cfg_of_base_dir base_dir - in - let cfg = - match Utils.read_file ~bin:false config_file |> Data_encoding_ezjsonm.from_string with - | exception _ -> no_config () - | Error msg -> corrupted_config msg - | Ok cfg -> try Cfg_file.from_json cfg with - | Invalid_argument msg - | Failure msg -> corrupted_config msg - in - let log_level = match List.length log_level with - | 0 -> None - | 1 -> Some Lwt_log.Info - | _ -> Some Lwt_log.Debug - in - let rpc_crt, rpc_key = match tls with - | None -> None, None - | Some (crt, key) -> Some crt, Some key - in - let cfg = - { cfg with - base_dir ; - sandbox = Utils.first_some sandbox cfg.sandbox ; - sandbox_param = Utils.first_some sandbox_param cfg.sandbox_param ; - log_level = Utils.first_some log_level cfg.log_level ; - min_connections = Utils.unopt ~default:cfg.min_connections min_connections ; - max_connections = Utils.unopt ~default:cfg.max_connections max_connections ; - expected_connections = Utils.unopt ~default:cfg.expected_connections expected_connections ; - net_addr = (match net_saddr with None -> cfg.net_addr | Some (addr, _) -> addr) ; - net_port = (match net_saddr with None -> cfg.net_port | Some (_, port) -> port) ; - (* local_discovery = Utils.first_some local_discovery cfg.local_discovery ; *) - peers = (match peers with [] -> cfg.peers | _ -> peers) ; - closed = closed || cfg.closed ; - rpc_addr = Utils.first_some rpc_addr cfg.rpc_addr ; - cors_origins = (match cors_origins with [] -> cfg.cors_origins | _ -> cors_origins) ; - cors_headers = (match cors_headers with [] -> cfg.cors_headers | _ -> cors_headers) ; - rpc_crt ; - rpc_key ; - log_output = cfg.log_output ; - } - in - if update_cfg then Cfg_file.write config_file cfg; - `Ok (config_file, reset_cfg, update_cfg, cfg) + let parse + base_dir config_file + sandbox verbosity + expected_connections + max_download_speed max_upload_speed + listen_addr bootstrap_peers closed expected_pow + rpc_listen_addr rpc_tls cors_origins cors_headers + reset_cfg update_cfg generate_identity = + + let actual_base_dir = + match base_dir with + | None -> default_base_dir + | Some dir -> dir in + + let config_file = + match config_file with + | None -> actual_base_dir // "config.json" + | Some file -> file in + + (* When --base-dir is provided, we ignore the `db`, `net.identity` + and `net.peers_metadata` of the configuration file. *) + let db = Utils.map_option Cfg_file.default_db base_dir in + let identity, peers_metadata = + let default_net = Utils.map_option Cfg_file.default_net base_dir in + Utils.map_option + ~f:(fun net -> net.Cfg_file.identity) default_net, + Utils.map_option + ~f:(fun net -> net.Cfg_file.peers_metadata) default_net in + + let read () = + if reset_cfg && update_cfg then + fail "The options --reset-config and --update-config \ + cannot be used together" + else if reset_cfg then + Lwt.return + (Cfg_file.default_config actual_base_dir, true) + else if update_cfg && not (Sys.file_exists config_file) then + fail "Cannot update a non-existant configuration file." + else if not (Sys.file_exists config_file) then + Lwt.return + (Cfg_file.default_config actual_base_dir, true) + else + Cfg_file.read config_file >>= function + | Error err -> + fail + "@[Corrupted configuration file, \ + fix it or use --reset-config.@ %a@]" + pp_print_error err + | Ok cfg -> Lwt.return (cfg, update_cfg) + in + + let verbosity = + match verbosity with + | [] -> None + | [_] -> Some Logging.Info + | _ -> Some Logging.Debug + in + + let rpc_tls = + Utils.map_option + (fun (cert, key) -> { Cfg_file.cert ; key }) + rpc_tls in + + let unopt_list ~default = function + | [] -> default + | l -> l in + + (* when `--expected-connections` is used, + override all the bounds defined in the configuration file. *) + let min_connections, expected_connections, max_connections = + match expected_connections with + | None -> None, None, None + | Some x -> Some (x/2), Some x, Some (3*x/2) in + + try + Lwt_main.run begin + Lwt_utils.create_dir ~perm:0o700 actual_base_dir >>= fun () -> + read () >>= fun (cfg, write_cfg) -> + let db = Utils.unopt ~default:cfg.db db in + let limits : P2p.limits = { + cfg.net.limits with + min_connections = + Utils.unopt + ~default:cfg.net.limits.min_connections + min_connections ; + expected_connections = + Utils.unopt + ~default:cfg.net.limits.expected_connections + expected_connections ; + max_connections = + Utils.unopt + ~default:cfg.net.limits.max_connections + max_connections ; + max_download_speed = + Utils.first_some + max_download_speed cfg.net.limits.max_download_speed ; + max_upload_speed = + Utils.first_some + max_upload_speed cfg.net.limits.max_upload_speed ; + } in + let net : Cfg_file.net = { + identity = + Utils.unopt ~default:cfg.net.identity identity ; + expected_pow = + Utils.unopt ~default:cfg.net.expected_pow expected_pow ; + bootstrap_peers = + unopt_list ~default:cfg.net.bootstrap_peers bootstrap_peers ; + peers_metadata = + Utils.unopt ~default:cfg.net.peers_metadata peers_metadata ; + listen_addr = + Utils.first_some listen_addr cfg.net.listen_addr ; + closed = cfg.net.closed || closed ; + limits ; + } + and rpc : Cfg_file.rpc = { + listen_addr = + Utils.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 = + Utils.first_some rpc_tls cfg.rpc.tls ; + } in + let cfg_file = { Cfg_file.db ; net ; rpc ; log = cfg.log } in + let write_cfg : (string * _ format6) option = + if not write_cfg then None + else if reset_cfg then + Some (config_file, "Reseting configuration file '%s'.") + else if update_cfg then + Some (config_file, "Updating configuration file '%s'.") + else + Some (config_file, "Writing initial configuration file '%s'.") + in + let cmdline = + { sandbox ; verbosity ; generate_identity ; write_cfg } in + Lwt.return (`Ok (cfg_file, cmdline)) + end + with Fail msg -> `Error (false, msg) let cmd = let open Term in ret (const parse $ base_dir $ config_file - $ sandbox $ sandbox_param $ v - $ min_connections $ max_connections $ expected_connections - $ net_addr - (* $ local_discovery *) - $ peers $ closed - $ rpc_addr $ rpc_tls $ cors_origins $ cors_headers - $ reset_config $ update_config + $ sandbox $ verbosity + $ expected_connections + $ max_download_speed $ max_upload_speed + $ listen_addr $ peers $ closed $ expected_pow + $ rpc_listen_addr $ rpc_tls $ cors_origins $ cors_headers + $ reset_config $ update_config $ generate_identity ), let doc = "The Tezos daemon" in let man = [ @@ -392,7 +601,9 @@ module Cmdline = struct `S "CONFIG"; `S misc_sect; `S "EXAMPLES" ; - `P "Use `$(mname) --sandbox /path/to/a/custom/data/dir --rpc-addr :::8732' \ + `P "Use `$(mname) --sandbox \ + --base-dir /path/to/a/custom/data/dir \ + --rpc-addr :::8732' \ to run a single instance in sandbox mode, \ listening to RPC commands at localhost port 8732."; `P "Use `$(mname)' for a node that accepts network connections."; @@ -402,172 +613,232 @@ module Cmdline = struct info ~sdocs:misc_sect ~man ~doc "tezos-node" let parse () = Term.eval cmd + end -let init_logger { log_output ; log_level } = +let init_logger ?verbosity (log_config : Cfg_file.log) = let open Logging in - Utils.iter_option log_level ~f:(Lwt_log_core.add_rule "*") ; - match log_output with - | `Stderr -> Logging.init Stderr - | `File fp -> Logging.init (File fp) - | `Null -> Logging.init Null - | `Syslog -> Logging.init (Syslog `Local1) + begin + match verbosity with + | Some level -> + Lwt_log_core.add_rule "*" level + | None -> + Lwt_log_core.add_rule "*" log_config.default_level ; + let rules = + match Sys.getenv "TEZOS_LOG" with + | rules -> Some rules + | exception Not_found -> + match Sys.getenv "LWT_LOG" with + | rules -> Some rules + | exception Not_found -> log_config.rules in + Utils.iter_option Lwt_log_core.load_rules rules + end ; + Logging.init ~template:log_config.template log_config.output -let init_node - { sandbox ; sandbox_param ; - store ; context ; - min_connections ; max_connections ; expected_connections ; - net_port ; peers ; peers_cache ; closed } = +type error += No_identity +type error += Existent_identity_file + +let read_identity target file = + Lwt_unix.file_exists file >>= function + | true -> + Data_encoding_ezjsonm.read_file file >>=? fun json -> + let id = Data_encoding.Json.destruct P2p.Identity.encoding json in + Lwt_utils.unless + (Crypto_box.check_proof_of_work + id.public_key id.proof_of_work_stamp target) + (fun () -> + lwt_warn "The amount of proof-of-work stamp in the node's identity \ + is below your own expectations.") >>= fun () -> + return id + | false -> + fail No_identity + +let init_node ?sandbox (config : Cfg_file.t) = let patch_context json ctxt = let module Proto = (val Updater.get_exn genesis_protocol) in - Lwt.catch - (fun () -> - Proto.configure_sandbox ctxt json >|= function - | Error _ -> - warn "Error while configuring ecoproto for the sandboxed mode." ; - ctxt - | Ok ctxt -> ctxt) - (fun exn -> - warn "Error while configuring ecoproto for the sandboxed mode. (%s)" - (Printexc.to_string exn) ; - Lwt.return ctxt) in + Lwt_utils.protect begin fun () -> + Proto.configure_sandbox ctxt json + end >|= function + | Error err -> + warn + "@[Error while configuring ecoproto for the sandboxed mode:@ %a@]" + pp_print_error err ; + ctxt + | Ok ctxt -> ctxt in begin match sandbox with | None -> Lwt.return_none - | Some _ -> + | Some sandbox_param -> match sandbox_param with | None -> Lwt.return (Some (patch_context None)) | Some file -> Data_encoding_ezjsonm.read_file file >>= function - | Error _ -> + | Error err -> lwt_warn - "Can't parse sandbox parameters. (%s)" file >>= fun () -> + "Can't parse sandbox parameters: %s" file >>= fun () -> + lwt_debug "%a" pp_print_error err >>= fun () -> Lwt.return (Some (patch_context None)) | Ok json -> Lwt.return (Some (patch_context (Some json))) end >>= fun patch_context -> - let net_params = + begin let open P2p in match sandbox with - | Some _ -> None + | Some _ -> return None | None -> - (* TODO add parameters... *) - let authentification_timeout = 5. - and backlog = 20 - and max_incoming_connections = 20 - and max_download_speed = None - and max_upload_speed = None - and read_buffer_size = 1 lsl 14 - and read_queue_size = None - and write_queue_size = None - and incoming_app_message_queue_size = None - and incoming_message_queue_size = None - and outgoing_message_queue_size = None in - let limits = - { authentification_timeout ; - 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 ; - } - in - (* TODO add parameters... *) - let identity = P2p.Identity.generate Crypto_box.default_target - and listening_addr = None - and proof_of_work_target = Crypto_box.default_target in - let config = - { listening_port = Some net_port ; - listening_addr ; + let proof_of_work_target = + Crypto_box.make_target config.net.expected_pow in + read_identity + proof_of_work_target config.net.identity >>=? fun identity -> + lwt_log_notice "Peers' id: %a" P2p.Gid.pp identity.gid >>= fun () -> + let p2p_config : P2p.config = + { listening_port = Utils.map_option snd config.net.listen_addr ; + listening_addr = Utils.map_option fst config.net.listen_addr ; + trusted_points = config.net.bootstrap_peers ; + peers_file = config.net.peers_metadata ; + closed_network = config.net.closed ; identity ; - trusted_points = peers ; - peers_file = peers_cache ; - closed_network = closed ; proof_of_work_target ; } in - Some (config, limits) in - Node.create - ~genesis - ~store_root:store - ~context_root:context - ?test_protocol - ?patch_context - net_params + return (Some (p2p_config, config.net.limits)) + end >>=? fun p2p_config -> + let node_config : Node.config = { + genesis ; + test_protocol ; + patch_context ; + store_root = config.db.store ; + context_root = config.db.context ; + p2p = p2p_config ; + } in + Node.create node_config -let init_rpc { rpc_addr ; rpc_crt; rpc_key ; cors_origins ; cors_headers } node = - match rpc_addr, rpc_crt, rpc_key with - | Some (addr, port), Some crt, Some key -> - lwt_log_notice "Starting the RPC server listening on port %d (TLS enabled)." port >>= fun () -> - let dir = Node_rpc.build_rpc_directory node in - let mode = `TLS (`Crt_file_path crt, `Key_file_path key, `No_password, `Port port) in - let host = Ipaddr.V6.to_string addr in - let () = - let old_hook = !Lwt.async_exception_hook in - Lwt.async_exception_hook := function - | Ssl.Read_error _ -> () - | exn -> old_hook exn in - RPC_server.launch ~host mode dir cors_origins cors_headers >>= fun server -> - Lwt.return (Some server) - | Some (_addr, port), _, _ -> - lwt_log_notice "Starting the RPC server listening on port %d (TLS disabled)." port >>= fun () -> - let dir = Node_rpc.build_rpc_directory node in - RPC_server.launch (`TCP (`Port port)) dir cors_origins cors_headers >>= fun server -> - Lwt.return (Some server) - | _ -> +let () = + let old_hook = !Lwt.async_exception_hook in + Lwt.async_exception_hook := function + | Ssl.Read_error _ -> () + | exn -> old_hook exn + +let init_rpc (rpc_config: Cfg_file.rpc) node = + match rpc_config.listen_addr with + | None -> lwt_log_notice "Not listening to RPC calls." >>= fun () -> - Lwt.return None + Lwt.return_none + | Some (addr, port) -> + let host = Ipaddr.V6.to_string addr in + let dir = Node_rpc.build_rpc_directory node in + let mode = + match rpc_config.tls with + | None -> `TCP (`Port port) + | Some { cert ; key } -> + `TLS (`Crt_file_path cert, `Key_file_path key, + `No_password, `Port port) in + lwt_log_notice + "Starting the RPC server listening on port %d%s." + port + (if rpc_config.tls = None then "" else " (TLS enabled)") >>= fun () -> + RPC_server.launch ~host mode dir + rpc_config.cors_origins rpc_config.cors_headers >>= fun server -> + Lwt.return (Some server) + let init_signal () = let handler id = try Lwt_exit.exit id with _ -> () in ignore (Lwt_unix.on_signal Sys.sigint handler : Lwt_unix.signal_handler_id) -let main cfg = +module Identity = struct + + let generate (command : Cmdline.t) (config : Cfg_file.t) = + let file = config.net.identity in + if not command.generate_identity then + return () + else if Sys.file_exists file then + fail Existent_identity_file + else + let target = Crypto_box.make_target config.net.expected_pow in + Format.eprintf "Generating a new identity... " ; + let identity = + P2p.Identity.generate_with_animation Format.err_formatter target in + Data_encoding_ezjsonm.write_file file + (Data_encoding.Json.construct P2p.Identity.encoding identity) + >>=? fun () -> + Format.eprintf + "Stored the new identity (%a) into '%s'@." + P2p.Gid.pp identity.gid file ; + return () + +end + +module Node = struct + + let may_write_config (command : Cmdline.t) (config : Cfg_file.t) = + match command.write_cfg with + | None -> return () + | Some (file, fmt) -> + Format.eprintf "%(%s%)@." fmt file ; + Cfg_file.write file config + + let run (command : Cmdline.t) (config : Cfg_file.t) = + may_write_config command config >>=? fun () -> + init_signal () ; + init_logger ?verbosity:command.verbosity config.log >>= fun () -> + Updater.init config.db.protocol ; + lwt_log_notice "Starting the Tezos node..." >>= fun () -> + init_node ?sandbox:command.sandbox config >>=? fun node -> + init_rpc config.rpc node >>= fun rpc -> + lwt_log_notice "The Tezos node is now running!" >>= fun () -> + Lwt_exit.termination_thread >>= fun x -> + lwt_log_notice "Shutting down the Tezos node..." >>= fun () -> + Node.shutdown node >>= fun () -> + lwt_log_notice "Shutting down the RPC server..." >>= fun () -> + Lwt_utils.may RPC_server.shutdown rpc >>= fun () -> + lwt_log_notice "BYE (%d)" x >>= fun () -> + return () + +end + +let main (command : Cmdline.t) (config : Cfg_file.t) = Random.self_init () ; Sodium.Random.stir () ; - init_logger cfg >>= fun () -> - Updater.init cfg.protocol; - lwt_log_notice "Starting the Tezos node..." >>= fun () -> - init_node cfg >>=? fun node -> - init_rpc cfg node >>= fun rpc -> - init_signal (); - lwt_log_notice "The Tezos node is now running!" >>= fun () -> - Lwt_exit.termination_thread >>= fun x -> - lwt_log_notice "Shutting down the Tezos node..." >>= fun () -> - Node.shutdown node >>= fun () -> - lwt_log_notice "Shutting down the RPC server..." >>= fun () -> - Lwt_utils.may RPC_server.shutdown rpc >>= fun () -> - lwt_log_notice "BYE (%d)" x >>= fun () -> - return () + Identity.generate command config >>=? fun () -> + Node.run command config let () = match Cmdline.parse () with | `Error _ -> exit 1 | `Help -> exit 1 | `Version -> exit 1 - | `Ok (config_file, was_reset, updated, cfg) -> - if was_reset then log_notice "Overwriting %s with factory defaults." config_file; - if updated then log_notice "Updated %s from command line arguments." config_file; + | `Ok (config, command) -> Lwt_main.run begin - if not @@ Sys.file_exists cfg.base_dir then begin - Unix.mkdir cfg.base_dir 0o700; - log_notice "Created base directory %s." cfg.base_dir - end; - log_notice "Using config file %s." config_file; - if not @@ Sys.file_exists config_file then begin - Cfg_file.write config_file cfg; - log_notice "Created config file %s." config_file - end; - main cfg >>= function + main command config >>= function | Ok () -> Lwt.return_unit + | Error [No_identity] -> + Format.eprintf + "Cannot find the identity file '%s'!\n%a@." + config.net.identity + Utils.display_paragraph + (Format.sprintf + "In order to proceed, Tezos needs a cryptographic identity. \ + You may generate a new identity by running:\n\ + \n\ +    %s --generate-identity --expected-pow %.1f\n\ + where `%.1f` is the expected level of proof-of-work in \ + the stamp associated to the new identity. \ + For quick testing, you may use '--expected-pow 0'." + Sys.argv.(0) + config.net.expected_pow + config.net.expected_pow) ; + exit 2 + | Error [Existent_identity_file] -> + Format.eprintf + "Error: Cannot implicitely overwrite an existing identity.\n\ + \n\ + \ Please remove the old identity file '%s'.@." + config.net.identity ; + exit 2 | Error err -> - lwt_log_error "%a@." Error_monad.pp_print_error err + lwt_log_error + "@[Unexpected error while initializing the node:@ %a@]@." + pp_print_error err >>= fun () -> + exit 1 end diff --git a/test/test-basic.sh b/test/test-basic.sh index 5ca301f7b..301a1bd49 100755 --- a/test/test-basic.sh +++ b/test/test-basic.sh @@ -5,8 +5,8 @@ set -e DIR=$(dirname "$0") cd "${DIR}" -DATA_DIR=$(mktemp -d /tmp/tezos_node.XXXXXXXXXX) -CLIENT_DIR=$(mktemp -d /tmp/tezos_client.XXXXXXXXXX) +DATA_DIR="$(mktemp -td tezos_node.XXXXXXXXXX)" +CLIENT_DIR="$(mktemp -td tezos_client.XXXXXXXXXX)" cleanup() { rm -fr ${DATA_DIR} ${CLIENT_DIR} @@ -17,8 +17,8 @@ trap cleanup EXIT QUIT INT NODE=../tezos-node CLIENT="../tezos-client -base-dir ${CLIENT_DIR}" -CUSTOM_PARAM="--sandbox-param ./sandbox.json" -${NODE} --sandbox "${DATA_DIR}" ${CUSTOM_PARAM} --rpc-addr :::8732 > LOG 2>&1 & +CUSTOM_PARAM="--sandbox ./sandbox.json" +${NODE} --base-dir "${DATA_DIR}" ${CUSTOM_PARAM} --rpc-addr :::8732 > LOG 2>&1 & NODE_PID="$!" sleep 3 diff --git a/test/test_basic.ml b/test/test_basic.ml index 2337ce546..e4d5895ba 100644 --- a/test/test_basic.ml +++ b/test/test_basic.ml @@ -50,8 +50,8 @@ let fork_node () = Unix.create_process Filename.(concat (dirname (Sys.getcwd ())) "tezos-node") [| "tezos-node" ; - "--sandbox"; data_dir ; - "--sandbox-param"; "./sandbox.json"; + "--base-dir"; data_dir ; + "--sandbox"; "./sandbox.json"; "--rpc-addr"; ":::8732" |] null_fd log_fd log_fd in Printf.printf "Created node, pid: %d, log: %s\n%!" pid log_file_name ;