Merge branch 'cmdliner' into 'master'

Cmdliner

See merge request !120
This commit is contained in:
Grégoire Henry 2016-12-01 13:39:52 +01:00
commit 3cb307eeff
13 changed files with 388 additions and 250 deletions

View File

@ -61,7 +61,7 @@ Running the node in a sandbox
To run a single instance of a Tezos node in sandbox mode: To run a single instance of a Tezos node in sandbox mode:
``` ```
./tezos-node -sandbox /path/to/a/custom/data/dir -rpc-port 8732 ./tezos-node --sandbox /path/to/a/custom/data/dir --rpc-addr :::8732
``` ```
This "sandboxed" node will not participate in the P2P network, but will accept This "sandboxed" node will not participate in the P2P network, but will accept
@ -81,11 +81,11 @@ connections:
``` ```
The node will listen to connections coming in on `0.0.0.0:9732` (and 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, `[::]:9732`). All used data is stored at `$HOME/.tezos-node/`. For example,
the default configuration file is at `${HOME}/.tezos-node/config`. the default configuration file is at `$HOME/.tezos-node/config`.
To run multiple nodes on the same machine, you can duplicate and edit 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` while making sure they don't share paths to the
database or any other data file (cf. options `db.store` ; `db.context` ; database or any other data file (cf. options `db.store` ; `db.context` ;
`net.peers` and `protocol.dir`). `net.peers` and `protocol.dir`).
@ -95,7 +95,7 @@ command will generate it and replace the default values with the values from
the command line: the command line:
``` ```
./tezos-node -base-dir "$dir" -net-port 9733 -net-addr 127.0.0.1 ./tezos-node --base-dir "$dir" --net-addr 127.0.0.1:9733
``` ```
The Tezos server has a built-in mechanism to discover peers on the local The Tezos server has a built-in mechanism to discover peers on the local
@ -106,8 +106,8 @@ initial peers, either by editing the option `net.bootstrap.peers` in the
`config` file, or by specifying a command line parameter: `config` file, or by specifying a command line parameter:
``` ```
./tezos-node -base-dir "$dir" -net-port 2023 -net-addr 127.0.0.1 \ ./tezos-node --base-dir "$dir" --net-addr 127.0.0.1:2023 \
-net-bootstrap-peers '[("127.0.0.1", 2021);("127.0.0.1", 2022)]' --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 If `"$dir"/config` exists, the command line options override those read in the
@ -115,7 +115,7 @@ config file. Tezos won't modify the content of an existing `"$dir"/config`
file. file.
``` ```
./tezos-node -config-file "$dir"/config ./tezos-node --config-file "$dir"/config
``` ```
@ -124,12 +124,12 @@ JSON/RPC interface
The Tezos node provides a JSON/RPC interface. Note that it is an RPC, and it is The Tezos node provides a JSON/RPC interface. Note that it is an RPC, and it is
JSON based, but it does not follow the "JSON-RPC" protocol. It is not active by JSON based, but it does not follow the "JSON-RPC" protocol. It is not active by
default and it must be explicitely activated with the `-rpc-port` option. default and it must be explicitely activated with the `--rpc-addr` option.
Typically, if you are not trying to run a local network and just want to Typically, if you are not trying to run a local network and just want to
explore the RPC, you would run: explore the RPC, you would run:
``` ```
./tezos-node -sandbox /path/to/a/custom/data/dir -rpc-port 8732 ./tezos-node --sandbox /path/to/a/custom/data/dir --rpc-addr :::8732
``` ```
The RPC interface is self-documented and the `tezos-client` executable is able The RPC interface is self-documented and the `tezos-client` executable is able

View File

@ -37,3 +37,4 @@ PKG result
PKG sodium PKG sodium
PKG unix PKG unix
PKG zarith PKG zarith
PKG cmdliner

View File

@ -274,6 +274,7 @@ NODE_PACKAGES := \
git \ git \
irmin.unix \ irmin.unix \
ocplib-resto.directory \ ocplib-resto.directory \
cmdliner \
EMBEDDED_NODE_PROTOCOLS := \ EMBEDDED_NODE_PROTOCOLS := \

View File

@ -35,8 +35,8 @@ module ConnectionMap = Map.Make(Cohttp.Connection)
exception Invalid_method exception Invalid_method
exception Cannot_parse_body of string exception Cannot_parse_body of string
(* Promise a running RPC server. Takes the address and port. *) (* Promise a running RPC server. Takes the port. *)
let launch addr port root = let launch port root =
(* launch the worker *) (* launch the worker *)
let cancelation, canceler, _ = Lwt_utils.canceler () in let cancelation, canceler, _ = Lwt_utils.canceler () in
let open Cohttp_lwt_unix in let open Cohttp_lwt_unix in
@ -119,7 +119,7 @@ let launch addr port root =
and conn_closed (_, con) = and conn_closed (_, con) =
log_info "connection close %s" (Cohttp.Connection.to_string con) ; log_info "connection close %s" (Cohttp.Connection.to_string con) ;
shutdown_stream con in shutdown_stream con in
lwt_log_info "create server %s:%d" addr port >>= fun () -> lwt_log_info "create server listening on port %d" port >>= fun () ->
let ctx = Cohttp_lwt_unix_net.init () in let ctx = Cohttp_lwt_unix_net.init () in
let mode = `TCP (`Port port) in let mode = `TCP (`Port port) in
let stop = cancelation () in let stop = cancelation () in

View File

@ -282,14 +282,14 @@ val register_describe_directory_service:
(** A handle on the server worker. *) (** A handle on the server worker. *)
type server type server
(** Promise a running RPC serve ; takes the address and port. To call (** Promise a running RPC serve ; takes the port. To call
an RPX at /p/a/t/h/ in the provided service, one must call the URI an RPX at /p/a/t/h/ in the provided service, one must call the URI
/call/p/a/t/h/. Calling /list/p/a/t/h/ will list the services /call/p/a/t/h/. Calling /list/p/a/t/h/ will list the services
prefixed by /p/a/t/h/, if any. Calling /schema/p/a/t/h/ will prefixed by /p/a/t/h/, if any. Calling /schema/p/a/t/h/ will
describe the input and output of the service, if it is describe the input and output of the service, if it is
callable. Calling /pipe will read a sequence of services to call in callable. Calling /pipe will read a sequence of services to call in
sequence from the request body, see {!pipe_encoding}. *) sequence from the request body, see {!pipe_encoding}. *)
val launch : string -> int -> unit directory -> server Lwt.t val launch : int -> unit directory -> server Lwt.t
(** Kill an RPC server. *) (** Kill an RPC server. *)
val shutdown : server -> unit Lwt.t val shutdown : server -> unit Lwt.t

View File

@ -31,221 +31,337 @@ let genesis = {
protocol = genesis_protocol ; protocol = genesis_protocol ;
} }
module Globals = struct let (//) = Filename.concat
open Config_file let home =
let (//) = Filename.concat
let home =
try Sys.getenv "HOME" try Sys.getenv "HOME"
with Not_found -> "/root" with Not_found -> "/root"
class string_option_cp ?group name ?short_name default help = let default_base_dir = home // ".tezos-node"
object (self)
inherit [string] option_cp
string_wrappers ?group name ?short_name default help
method get_spec =
let set = function
| ""
| "none" -> self#set None | s -> self#set (Some s) in
Arg.String set
end
let addr_wrappers = { type cfg = {
to_raw = (fun v -> Raw.String (Ipaddr.to_string v)); (* cli *)
of_raw = function base_dir : string ;
| Raw.String v -> Ipaddr.of_string_exn v sandbox : string option ;
| r -> raise (Wrong_type (fun outchan -> Printf.fprintf outchan sandbox_param : string option ;
"Raw.Int expected, got %a\n%!" Raw.to_channel r))}
class addr_cp = [Ipaddr.t] cp_custom_type addr_wrappers
(** Command line options *) (* db *)
store : string ;
context : string ;
protocol : string ;
let cli_group = new group (* net *)
min_connections : int ;
max_connections : int ;
expected_connections : int ;
net_addr : Ipaddr.t ;
net_port : int ;
local_discovery : int option ;
peers : (Ipaddr.t * int) list ;
peers_cache : string ;
closed : bool ;
let base_dir = (* rpc *)
new filename_cp ~group:cli_group ["base-dir"] (home // ".tezos-node") rpc_addr : (Ipaddr.t * int) option ;
"The directory where the tezos node will store all its data."
let config_file = (* log *)
new filename_cp ~group:cli_group ["config-file"] (base_dir#get // "config") log_output : [`Stderr | `File of string | `Syslog | `Null] ;
"The main configuration file." log_level : Lwt_log.level option ;
}
let () = let default_cfg_of_base_dir base_dir = {
let config_file_forced = ref false in (* cli *)
let update_config _old_file _new_file = config_file_forced := true in base_dir ;
let update_base_dir old_dir new_dir = sandbox = None ;
if new_dir <> old_dir then sandbox_param = None ;
if not !config_file_forced then begin
config_file#set (new_dir // "config");
config_file_forced := false
end
in
config_file#add_hook update_config;
base_dir#add_hook update_base_dir
let sandbox = (* db *)
new string_option_cp ~group:cli_group ["sandbox"] None store = base_dir // "store" ;
"Run a sandboxed daemon \ context = base_dir // "context" ;
\ (P2P is disabled, \ protocol = base_dir // "protocol" ;
\ data are stored in custom directory)."
let sandbox_param = (* net *)
new string_option_cp ~group:cli_group ["sandbox-param"] None min_connections = 4 ;
"Custom paramater for the ecoproto." max_connections = 400 ;
expected_connections = 20 ;
net_addr = Ipaddr.(V6 V6.unspecified) ;
net_port = 9732 ;
local_discovery = None ;
peers = [] ;
closed = false ;
peers_cache = base_dir // "peers_cache" ;
let () = (* rpc *)
let sandboxed _ = function rpc_addr = None ;
| None -> base_dir#reset
| Some dir -> base_dir#set dir in
sandbox#add_hook sandboxed
let verbose_param = (* log *)
new string_option_cp ~group:cli_group ["verbosity"] ~short_name:"v" None log_output = `Stderr ;
"Verbosity level (fatal, error, warning, notice, info, debug)" log_level = None ;
}
(** File options *) let default_cfg = default_cfg_of_base_dir default_base_dir
let file_group = new group 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 store_root = let string_of_log = function
new filename_cp ~group:file_group ["db"; "store"] | `Stderr -> "stderr"
"DUMMY" (* See update default *) "TODO" | `File fn -> "file:" ^ fn
| `Syslog -> "syslog"
| `Null -> "null"
let context_root = let sockaddr_of_string str =
new filename_cp ~group:file_group ["db"; "context"] match String.rindex str ':' with
"DUMMY" (* See update default *) "TODO" | 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"
| ip, port -> `Ok (ip, port)
let protocol_dir = let sockaddr_of_string_exn str =
new filename_cp ~group:file_group ["protocol"; "dir"] match sockaddr_of_string str with
"DUMMY" (* See update default *) "TODO" | `Ok saddr -> saddr
| `Error msg -> invalid_arg msg
let peers_file = let pp_sockaddr fmt (ip, port) = Format.fprintf fmt "%a:%d" Ipaddr.pp_hum ip port
new filename_cp ~group:file_group ["net"; "peers"] let string_of_sockaddr saddr = Format.asprintf "%a" pp_sockaddr saddr
"DUMMY" (* See update default *)
"A file storing information about known peers"
(** Network options *) module Cfg_file = struct
open Data_encoding
let in_both_groups cp = let db =
file_group # add cp ; cli_group # add cp ; cp obj3
(opt "store" string)
(opt "context" string)
(opt "protocol" string)
let min_connections = in_both_groups @@ let net =
new int_cp [ "net" ; "min-connections" ] 4 obj8
"The number of connections under which aggressive peer discovery mode must be entered" (opt "min-connections" uint16)
(opt "max-connections" uint16)
(opt "expected-connections" uint16)
(opt "addr" string)
(opt "local-discovery" uint16)
(opt "peers" (list string))
(dft "closed" bool false)
(opt "peers-cache" string)
let max_connections = in_both_groups @@ let rpc =
new int_cp [ "net" ; "max-connections" ] 400 obj1
"The number of connections over which some have to be closed" (opt "addr" string)
let expected_connections = in_both_groups @@ let log =
new int_cp [ "net" ; "expected-connections" ] 20 obj1
"The minimum number of connections to be ensured by the cruise control" (opt "output" string)
let incoming_port = in_both_groups @@ let t =
new option_cp int_wrappers [ "net" ; "port" ] ~short_name:"P" (Some 9732) conv
"The TCP address at which this instance can be reached" (fun { store ; context ; protocol ;
min_connections ; max_connections ; expected_connections;
net_addr ; net_port ; local_discovery ; peers;
closed ; peers_cache ; rpc_addr; log_output } ->
let net_addr = string_of_sockaddr (net_addr, net_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, Some log_output))
(fun (
(store, context, protocol),
(min_connections, max_connections, expected_connections,
net_addr, local_discovery, peers, closed, peers_cache), rpc_addr, log_output) ->
let open Utils in
let store = unopt default_cfg.store store in
let context = unopt default_cfg.context context in
let protocol = unopt default_cfg.protocol protocol in
let net_addr = map_option sockaddr_of_string_exn net_addr in
let net_addr, net_port = unopt (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 [] peers in
let peers = ListLabels.map peers ~f:sockaddr_of_string_exn in
let peers_cache = unopt default_cfg.peers_cache peers_cache in
let log_output = unopt default_cfg.log_output (map_option log_of_string log_output) in
let min_connections = unopt default_cfg.min_connections min_connections in
let max_connections = unopt default_cfg.max_connections max_connections in
let expected_connections = unopt default_cfg.expected_connections expected_connections 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; log_output
}
)
(obj4
(req "db" db)
(req "net" net)
(req "rpc" rpc)
(req "log" log))
let discovery_port = in_both_groups @@ let read fp =
new bool_cp [ "net" ; "local-discovery" ] ~short_name:"D" false let open Data_encoding.Json in
"Automatic discovery of peers on the local network" read_file fp >|= function
| None -> None
let bootstrap_peers = in_both_groups @@ | Some json -> Some (destruct t json)
new list_cp (tuple2_wrappers addr_wrappers int_wrappers)
[ "net" ; "bootstrap-peers" ] ~short_name:"B" [ ]
"The peers to bootstrap the networks from"
let closed_network = in_both_groups @@
new bool_cp
[ "net" ; "closed" ] ~short_name:"X" false
"Only accept connections from the bootstrap peers"
(** Logging *)
let log_kind =
new string_cp ~group:file_group [ "log" ; "kind" ] "stderr"
"Which logger to use: 'stderr', 'stdout', 'file', 'null' or 'syslog'."
let log_file =
new filename_cp ~group:file_group ["log"; "file"]
"DUMMY" (* See update default *)
"The log-file path when 'log_kind = file'."
(** RPC *)
let rpc_listening_port = in_both_groups @@
new option_cp int_wrappers [ "rpc" ; "port" ] ~short_name:"P" None
"The TCP port at which this RPC-server instance can be reached"
let rpc_listening_addr = in_both_groups @@
new string_option_cp [ "rpc" ; "addr" ] ~short_name:"A" None
"The TCP address at which this RPC-server instance can be reached"
(** Entry point *)
let update_defaults () =
(* Set default path relatively to [base_dir]. *)
store_root#set (base_dir#get // "store");
context_root#set (base_dir#get // "context");
protocol_dir#set (base_dir#get // "protocol");
peers_file#set (base_dir#get // "peers-cache");
log_file#set (base_dir#get // "tezos-node.log")
let parse_args () =
let args = cli_group#command_line_args "-" in
let anon_fun str =
Arg.usage args
(Printf.sprintf
"\nError: Unknown command line argument %S.\n\nUsage:" str);
Utils.exit 1
in
Arg.parse args anon_fun "Usage:";
update_defaults ();
if Sys.file_exists config_file#get then begin
try
file_group#read config_file#get ;
(* parse once again to overwrite file options by cli ones *)
Arg.parse_argv ~current:(ref 0) Sys.argv args anon_fun "Usage:"
with Sys_error msg ->
Printf.eprintf "Error: can't read the configuration file: %s\n%!" msg;
Utils.exit 1
end else begin
try
Lwt_main.run (Utils.create_dir (Filename.dirname config_file#get));
file_group#write config_file#get
with Sys_error msg ->
Printf.eprintf
"Warning: can't create the default configuration file: %s\n%!" msg
end
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 |> to_string)
end end
let init_logger () = module Cmdline = struct
let open Logging in open Cmdliner
begin
let open Lwt_log_core in
match Globals.verbose_param#get with
| Some "fatal" -> add_rule "*" Fatal
| Some "error" -> add_rule "*" Error
| Some "warning" -> add_rule "*" Warning
| Some "notice" -> add_rule "*" Notice
| Some "info" -> add_rule "*" Info
| Some "debug" -> add_rule "*" Debug
| Some level ->
Printf.eprintf "Warning: unknown verbosity level \"%s\".\n%!" level
| None -> ()
end;
match Globals.log_kind#get with
| "" | "stderr" -> Logging.init Stderr
| "stdout" -> Logging.init Stdout
| "file" -> Logging.init (File Globals.log_file#get)
| "null" -> Logging.init Null
| "syslog" -> Logging.init Syslog
| kind -> Printf.eprintf "Warning: unknown log_kind \"%s\".\n%!" kind
let init_node () = (* custom converters *)
let sockaddr_converter = sockaddr_of_string, pp_sockaddr
(* 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 config_file =
let doc = "The main configuration file." in
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
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 int default_cfg.min_connections & 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 int default_cfg.max_connections & 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 int default_cfg.expected_connections & 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 sockaddr_converter (default_cfg.net_addr, default_cfg.net_port) & 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 int) default_cfg.local_discovery & info ~docs:"NETWORK" ~doc ~docv:"ADDR: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"])
(* 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 parse base_dir config_file sandbox sandbox_param log_level
min_connections max_connections expected_connections
(net_addr, net_port) local_discovery peers closed rpc_addr reset_cfg update_cfg =
let base_dir = Utils.(unopt (unopt default_cfg.base_dir base_dir) sandbox) in
let config_file = Utils.(unopt ((unopt 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.Json.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 cfg =
{ cfg with
base_dir ;
sandbox ;
sandbox_param ;
log_level ;
min_connections ;
max_connections ;
expected_connections ;
net_addr ;
net_port ;
local_discovery ;
peers = List.rev_append peers cfg.peers ;
closed ;
rpc_addr = Utils.first_some rpc_addr cfg.rpc_addr ;
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 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
$ reset_config $ update_config
),
let doc = "The Tezos daemon" in
let man = [
`S "NETWORK";
`S "RPC";
`S "CONFIG";
`S misc_sect;
`S "EXAMPLES" ;
`P "Use `$(mname) --sandbox /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.";
`S "BUGS"; `P "Check bug reports at https://github.com/tezos/tezos/issues.";
]
in
info ~sdocs:misc_sect ~man ~doc "tezos-node"
let parse () = Term.eval cmd
end
let init_logger { log_output ; log_level } =
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
let init_node { sandbox ; sandbox_param ;
store ; context ;
min_connections ; max_connections ; expected_connections ;
net_port ; peers ; peers_cache ; local_discovery ; closed } =
let patch_context json ctxt = let patch_context json ctxt =
let module Proto = (val Updater.get_exn genesis_protocol) in let module Proto = (val Updater.get_exn genesis_protocol) in
Lwt.catch Lwt.catch
@ -260,92 +376,101 @@ let init_node () =
(Printexc.to_string exn) ; (Printexc.to_string exn) ;
Lwt.return ctxt) in Lwt.return ctxt) in
begin begin
match Globals.sandbox#get with match sandbox with
| None -> Lwt.return_none | None -> Lwt.return_none
| Some _ -> | Some _ ->
match Globals.sandbox_param#get with match sandbox_param with
| None -> Lwt.return (Some (patch_context None)) | None -> Lwt.return (Some (patch_context None))
| Some file -> | Some file ->
Data_encoding.Json.read_file file >>= function Data_encoding.Json.read_file file >>= function
| None -> | None ->
lwt_warn lwt_warn
"Can't parse sandbox parameters (%s)" file >>= fun () -> "Can't parse sandbox parameters. (%s)" file >>= fun () ->
Lwt.return (Some (patch_context None)) Lwt.return (Some (patch_context None))
| Some _ as json -> | Some _ as json ->
Lwt.return (Some (patch_context json)) Lwt.return (Some (patch_context json))
end >>= fun patch_context -> end >>= fun patch_context ->
let net_params = let net_params =
let open P2p in let open P2p in
match Globals.sandbox#get with match sandbox with
| Some _ -> None | Some _ -> None
| None -> | None ->
let limits = let limits =
{ max_packet_size = 10_000 ; { max_packet_size = 10_000 ;
peer_answer_timeout = 5. ; peer_answer_timeout = 5. ;
expected_connections = Globals.expected_connections#get ; expected_connections ;
min_connections = Globals.min_connections#get ; min_connections ;
max_connections = Globals.max_connections#get ; max_connections ;
blacklist_time = 30. } blacklist_time = 30. }
and config = in
{ incoming_port = Globals.incoming_port#get ; let config =
discovery_port = { incoming_port = Some net_port ;
if Globals.discovery_port#get then Some 7732 else None ; discovery_port = local_discovery ;
known_peers = Globals.bootstrap_peers#get ; known_peers = peers ;
peers_file = Globals.peers_file#get ; peers_file = peers_cache ;
closed_network = Globals.closed_network#get } closed_network = closed }
in in
Some (config, limits) in Some (config, limits) in
Node.create Node.create
~genesis ~genesis
~store_root:Globals.store_root#get ~store_root:store
~context_root:Globals.context_root#get ~context_root:context
?test_protocol ?test_protocol
?patch_context ?patch_context
net_params net_params
let init_rpc node = let init_rpc { rpc_addr } node =
match Globals.rpc_listening_port#get, Globals.rpc_listening_addr#get with match rpc_addr with
| None, None -> | None ->
lwt_log_notice "Not listening to RPC calls." >>= fun () -> lwt_log_notice "Not listening to RPC calls." >>= fun () ->
Lwt.return None Lwt.return None
| port, addr -> | Some (_addr, port) ->
let addr = match addr with Some a -> a | None -> "127.0.0.1" in lwt_log_notice "Starting the RPC server listening on port %d." port >>= fun () ->
let port = match port with Some p -> p | None -> 8732 in
lwt_log_notice "Starting the RPC server at %s:%d." addr port >>= fun () ->
let dir = Node_rpc.build_rpc_directory node in let dir = Node_rpc.build_rpc_directory node in
RPC.(launch addr port dir) >>= fun server -> RPC.(launch port dir) >>= fun server ->
Lwt.return (Some server) Lwt.return (Some server)
let may f = function
| None -> Lwt.return_unit
| Some x -> f x
let init_signal () = let init_signal () =
let handler id = try Utils.exit id with _ -> () in let handler id = try Utils.exit id with _ -> () in
ignore (Lwt_unix.on_signal Sys.sigint handler : Lwt_unix.signal_handler_id) ignore (Lwt_unix.on_signal Sys.sigint handler : Lwt_unix.signal_handler_id)
let main () = let main cfg =
Random.self_init () ; Random.self_init () ;
Sodium.Random.stir () ; Sodium.Random.stir () ;
Globals.parse_args (); init_logger cfg;
init_logger (); Updater.init cfg.protocol;
Updater.init Globals.protocol_dir#get;
lwt_log_notice "Starting the Tezos node..." >>= fun () -> lwt_log_notice "Starting the Tezos node..." >>= fun () ->
init_node () >>=? fun node -> init_node cfg >>=? fun node ->
init_rpc node >>= fun rpc -> init_rpc cfg node >>= fun rpc ->
init_signal (); init_signal ();
lwt_log_notice "The Tezos node is now running!" >>= fun () -> lwt_log_notice "The Tezos node is now running!" >>= fun () ->
Utils.termination_thread >>= fun x -> Utils.termination_thread >>= fun x ->
lwt_log_notice "Shutting down the Tezos node..." >>= fun () -> lwt_log_notice "Shutting down the Tezos node..." >>= fun () ->
Node.shutdown node >>= fun () -> Node.shutdown node >>= fun () ->
lwt_log_notice "Shutting down the RPC server..." >>= fun () -> lwt_log_notice "Shutting down the RPC server..." >>= fun () ->
may RPC.shutdown rpc >>= fun () -> Lwt_utils.may RPC.shutdown rpc >>= fun () ->
lwt_log_notice "BYE (%d)" x >>= fun () -> lwt_log_notice "BYE (%d)" x >>= fun () ->
return () return ()
let () = 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;
Lwt_main.run begin Lwt_main.run begin
main () >>= function 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
| Ok () -> Lwt.return_unit | Ok () -> Lwt.return_unit
| Error err -> | Error err ->
lwt_log_error "%a@." Error_monad.pp_print_error err lwt_log_error "%a@." Error_monad.pp_print_error err

View File

@ -8,10 +8,13 @@
(**************************************************************************) (**************************************************************************)
module LC = Lwt_condition module LC = Lwt_condition
open Lwt.Infix
open Logging.Core open Logging.Core
let (>>=) = Lwt.(>>=) let may f = function
let (>|=) = Lwt.(>|=) | None -> Lwt.return_unit
| Some x -> f x
let never_ending = fst (Lwt.wait ()) let never_ending = fst (Lwt.wait ())

View File

@ -7,6 +7,8 @@
(* *) (* *)
(**************************************************************************) (**************************************************************************)
val may : ('a -> unit Lwt.t) -> 'a option -> unit Lwt.t
val never_ending: 'a Lwt.t val never_ending: 'a Lwt.t
val canceler : unit -> val canceler : unit ->

View File

@ -112,6 +112,11 @@ let unopt_list l =
let may_cons xs x = match x with None -> xs | Some x -> x :: xs in let may_cons xs x = match x with None -> xs | Some x -> x :: xs in
List.rev @@ List.fold_left may_cons [] l List.rev @@ List.fold_left may_cons [] l
let first_some a b = match a, b with
| None, None -> None
| None, Some v -> Some v
| Some v, _ -> Some v
let filter_map f l = let filter_map f l =
let may_cons xs x = match f x with None -> xs | Some x -> x :: xs in let may_cons xs x = match f x with None -> xs | Some x -> x :: xs in
List.rev @@ List.fold_left may_cons [] l List.rev @@ List.fold_left may_cons [] l

View File

@ -31,6 +31,7 @@ val apply_option: f:('a -> 'b option) -> 'a option -> 'b option
val iter_option: f:('a -> unit) -> 'a option -> unit val iter_option: f:('a -> unit) -> 'a option -> unit
val unopt: 'a -> 'a option -> 'a val unopt: 'a -> 'a option -> 'a
val unopt_list: 'a option list -> 'a list val unopt_list: 'a option list -> 'a list
val first_some: 'a option -> 'a option -> 'a option
val display_paragraph: Format.formatter -> string -> unit val display_paragraph: Format.formatter -> string -> unit

View File

@ -2,7 +2,7 @@ COMMAND='gnome-terminal'
COUNT=2 COUNT=2
for i in $(seq 1 $COUNT) for i in $(seq 1 $COUNT)
do do
SUBCOMMAND="./tezos-node -net-port $((9900 + i)) -net-local-discovery true -rpc-port $((8800 + i)) -net-expected-connections $(($COUNT - 1)) -base-dir /tmp/tezos_$i" SUBCOMMAND="./tezos-node --net-addr :::$((9900 + i)) --local-discovery :::7732 --rpc-addr :::$((8800 + i)) --expected-connections $(($COUNT - 1)) --base-dir /tmp/tezos_$i"
COMMAND="$COMMAND --tab -e '$SUBCOMMAND'" COMMAND="$COMMAND --tab -e '$SUBCOMMAND'"
done done
echo $COMMAND echo $COMMAND

View File

@ -17,8 +17,8 @@ trap cleanup EXIT QUIT INT
NODE=../tezos-node NODE=../tezos-node
CLIENT="../tezos-client -base-dir ${CLIENT_DIR}" CLIENT="../tezos-client -base-dir ${CLIENT_DIR}"
CUSTOM_PARAM="-sandbox-param ./sandbox.json " CUSTOM_PARAM="--sandbox-param ./sandbox.json"
${NODE} -sandbox ${DATA_DIR} ${CUSTOM_PARAM} -rpc-port 8732 > LOG 2>&1 & ${NODE} --sandbox "${DATA_DIR}" ${CUSTOM_PARAM} --rpc-addr :::8732 > LOG 2>&1 &
NODE_PID="$!" NODE_PID="$!"
sleep 3 sleep 3

View File

@ -38,9 +38,9 @@ let fork_node () =
Unix.create_process Unix.create_process
Filename.(concat (dirname (Sys.getcwd ())) "tezos-node") Filename.(concat (dirname (Sys.getcwd ())) "tezos-node")
[| "tezos-node" ; [| "tezos-node" ;
"-sandbox"; data_dir ; "--sandbox"; data_dir ;
"-sandbox-param"; "./sandbox.json"; "--sandbox-param"; "./sandbox.json";
"-rpc-port"; "8732" |] "--rpc-addr"; ":::8732" |]
null_fd log_fd log_fd in null_fd log_fd log_fd in
Printf.printf "Created node, pid: %d, log: %s\n%!" pid log_file_name ; Printf.printf "Created node, pid: %d, log: %s\n%!" pid log_file_name ;
at_exit at_exit