ligo/src/node/main/node_run_command.ml

325 lines
12 KiB
OCaml
Raw Normal View History

(**************************************************************************)
(* *)
2017-11-14 03:36:14 +04:00
(* Copyright (c) 2014 - 2017. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
open Logging.Node.Main
let genesis : State.Net.genesis = {
time =
2017-10-13 13:24:22 +04:00
Time.of_notation_exn "2017-10-13T00:00:00Z" ;
block =
Block_hash.of_b58check_exn
2017-10-13 13:24:22 +04:00
"BLockGenesisGenesisGenesisGenesisGenesisDDDDDegibDt" ;
protocol =
Protocol_hash.of_b58check_exn
2017-02-24 18:38:42 +04:00
"ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im" ;
}
type error += Non_private_sandbox of P2p_types.addr
let () =
register_error_kind
`Permanent
~id:"main.run.non_private_sandbox"
~title:"Forbidden public sandbox"
~description:"A sandboxed node should not listen on a public address."
~pp:begin fun ppf addr ->
Format.fprintf ppf
"The node is configured to listen on a public address (%a), \
while only 'private' networks are authorised with `--sandbox`.
See `%s run --help` on how to change the listening address."
Ipaddr.V6.pp_hum addr Sys.argv.(0)
end
Data_encoding.(obj1 (req "addr" P2p_types.addr_encoding))
(function Non_private_sandbox addr -> Some addr | _ -> None)
(fun addr -> Non_private_sandbox addr)
2017-08-09 18:09:41 +04:00
let (//) = Filename.concat
let store_dir data_dir = data_dir // "store"
let context_dir data_dir = data_dir // "context"
let protocol_dir data_dir = data_dir // "protocol"
let lock_file data_dir = data_dir // "lock"
let init_logger ?verbosity (log_config : Node_config_file.log) =
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
2017-11-13 17:29:28 +04:00
Utils.iter_option ~f:Lwt_log_core.load_rules rules
end ;
Logging.init ~template:log_config.template log_config.output
let init_node ?sandbox (config : Node_config_file.t) =
let patch_context json ctxt =
let module Proto = (val State.Registred_protocol.get_exn genesis.protocol) 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 sandbox_param ->
match sandbox_param with
| None -> Lwt.return (Some (patch_context None))
| Some file ->
Data_encoding_ezjsonm.read_file file >>= function
| Error err ->
lwt_warn
"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 ->
2017-08-09 18:09:41 +04:00
(* TODO "WARN" when pow is below our expectation. *)
begin
2017-08-09 18:09:41 +04:00
match config.net.listen_addr with
| None ->
2017-08-09 18:09:41 +04:00
lwt_log_notice "Not listening to P2P calls." >>= fun () ->
return (None, None)
| Some addr ->
Node_config_file.resolve_listening_addrs addr >>= function
| [] ->
failwith "Cannot resolve P2P listening address: %S" addr
| (addr, port) :: _ -> return (Some addr, Some port)
end >>=? fun (listening_addr, listening_port) ->
begin
match listening_addr, sandbox with
| Some addr, Some _
when Ipaddr.V6.(compare addr unspecified) = 0 ->
return None
| Some addr, Some _ when not (Ipaddr.V6.is_private addr) ->
fail (Non_private_sandbox addr)
2017-08-09 18:09:41 +04:00
| None, Some _ -> return None
| _ ->
(Node_config_file.resolve_bootstrap_addrs
config.net.bootstrap_peers) >>= fun trusted_points ->
Node_identity_file.read
(config.data_dir //
Node_identity_file.default_name) >>=? fun identity ->
lwt_log_notice
"Peer's global id: %a"
P2p.Peer_id.pp identity.peer_id >>= fun () ->
let p2p_config : P2p.config =
{ listening_addr ;
listening_port ;
trusted_points ;
peers_file =
(config.data_dir // "peers.json") ;
closed_network = config.net.closed ;
identity ;
proof_of_work_target =
Crypto_box.make_target config.net.expected_pow ;
}
in
return (Some (p2p_config, config.net.limits))
end >>=? fun p2p_config ->
let node_config : Node.config = {
genesis ;
patch_context ;
store_root = store_dir config.data_dir ;
context_root = context_dir config.data_dir ;
p2p = p2p_config ;
test_network_max_tll = Some (48 * 3600) ; (* 2 days *)
} in
Node.create node_config
let () =
let old_hook = !Lwt.async_exception_hook in
Lwt.async_exception_hook := function
| Ssl.Read_error _ -> ()
| Ssl.Write_error _ -> ()
| exn -> old_hook exn
let init_rpc (rpc_config: Node_config_file.rpc) node =
match rpc_config.listen_addr with
| None ->
lwt_log_notice "Not listening to RPC calls." >>= fun () ->
return None
| Some addr ->
Node_config_file.resolve_rpc_listening_addrs addr >>= function
| [] ->
failwith "Cannot resolve listening address: %S" addr
| (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 ->
return (Some server)
let init_signal () =
let handler name id = try
fatal_error "Received the %s signal, triggering shutdown." name ;
Lwt_exit.exit id
with _ -> () in
ignore (Lwt_unix.on_signal Sys.sigint (handler "INT") : Lwt_unix.signal_handler_id) ;
ignore (Lwt_unix.on_signal Sys.sigterm (handler "TERM") : Lwt_unix.signal_handler_id)
let run ?verbosity ?sandbox (config : Node_config_file.t) =
2017-09-14 12:26:05 +04:00
Node_data_version.ensure_data_dir config.data_dir >>=? fun () ->
Lwt_utils.Lock_file.create
~unlink_on_exit:true (lock_file config.data_dir) >>=? fun () ->
init_signal () ;
init_logger ?verbosity config.log >>= fun () ->
Updater.init (protocol_dir config.data_dir) ;
lwt_log_notice "Starting the Tezos node..." >>= fun () ->
init_node ?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 () ->
2017-11-13 17:29:28 +04:00
Lwt_utils.may ~f:RPC_server.shutdown rpc >>= fun () ->
lwt_log_notice "BYE (%d)" x >>= fun () ->
Logging.close () >>= fun () ->
return ()
let process sandbox verbosity args =
let verbosity =
match verbosity with
| [] -> None
| [_] -> Some Logging.Info
| _ -> Some Logging.Debug in
let run =
2017-08-09 18:09:41 +04:00
Node_shared_arg.read_and_patch_config_file
~ignore_bootstrap_peers:(match sandbox with
| Some _ -> true
| None -> false)
args >>=? fun config ->
begin match sandbox with
| Some _ ->
if config.data_dir = Node_config_file.default_data_dir
then failwith "Cannot use default data directory while in sandbox mode"
else return ()
| None -> return ()
end >>=? fun () ->
Lwt_utils.Lock_file.is_locked
(lock_file config.data_dir) >>=? function
| false ->
run ?sandbox ?verbosity config
| true -> failwith "Data directory is locked by another process" in
match Lwt_main.run run with
| Ok () -> `Ok ()
| Error err -> `Error (false, Format.asprintf "%a" pp_print_error err)
module Term = struct
let verbosity =
let open Cmdliner in
let doc =
"Increase log level. Using $(b,-v) is equivalent to \
using $(b,TEZOS_LOG='* -> info'), and $(b,-vv) is equivalent to using \
$(b,TEZOS_LOG='* -> debug')." in
Arg.(value & flag_all &
info ~docs:Node_shared_arg.Manpage.misc_section ~doc ["v"])
let sandbox =
let open Cmdliner in
let doc =
2017-08-09 18:09:41 +04:00
"Run the daemon in sandbox mode. \
P2P to non-localhost addresses are disabled, and constants of \
the economic protocol can be altered with an optional JSON file. \
$(b,IMPORTANT): Using sandbox mode affects the node state and \
subsequent runs of Tezos node must also use sandbox mode. \
In order to run the node in normal mode afterwards, a full reset \
must be performed (by removing the node's data directory)."
in
Arg.(value & opt ~vopt:(Some None) (some (some string)) None &
info ~docs:Node_shared_arg.Manpage.misc_section
~doc ~docv:"FILE.json" ["sandbox"])
let term =
Cmdliner.Term.(ret (const process $ sandbox $ verbosity $
Node_shared_arg.Term.args))
end
module Manpage = struct
let command_description =
"The $(b,run) command is meant to run the Tezos node. \
Most of its command line arguments corresponds to config file \
entries, and will have priority over the latter if used."
let description = [
`S "DESCRIPTION" ;
`P command_description ;
]
let debug =
let log_sections = String.concat " " (List.rev !Logging.sections) in
[
`S "DEBUG" ;
`P ("The environment variable $(b,TEZOS_LOG) is used to fine-tune \
what is going to be logged. The syntax is \
$(b,TEZOS_LOG='<section> -> <level> [ ; ...]') \
where section is one of $(i,"
^ log_sections ^
") and level is one of $(i,fatal), $(i,error), $(i,warn), \
$(i,notice), $(i,info) or $(i,debug). \
A $(b,*) can be used as a wildcard \
in sections, i.e. $(b, client* -> debug). \
The rules are matched left to right, \
therefore the leftmost rule is highest priority ."
) ;
]
let examples =
[
`S "EXAMPLES" ;
`I ("$(b,Run in sandbox mode listening to RPC commands \
at localhost port 8732)",
2017-07-10 14:32:07 +04:00
"$(mname) run --sandbox --data-dir /custom/data/dir \
--rpc-addr localhost:8732" ) ;
`I ("$(b,Run a node that accepts network connections)",
"$(mname) run" ) ;
]
let man =
description @
Node_shared_arg.Manpage.args @
examples @
Node_shared_arg.Manpage.bugs
let info =
Cmdliner.Term.info
~doc:"Run the Tezos node"
~man
"run"
end
let cmd = Term.term, Manpage.info