2017-01-30 22:10:16 +04:00
|
|
|
|
(**************************************************************************)
|
|
|
|
|
(* *)
|
|
|
|
|
(* Copyright (c) 2014 - 2016. *)
|
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
|
(* *)
|
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
|
(* *)
|
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
|
|
open Logging.Node.Main
|
|
|
|
|
|
2017-02-24 20:17:53 +04:00
|
|
|
|
let genesis : State.Net.genesis = {
|
|
|
|
|
time =
|
2017-10-13 13:24:22 +04:00
|
|
|
|
Time.of_notation_exn "2017-10-13T00:00:00Z" ;
|
2017-01-30 22:10:16 +04:00
|
|
|
|
block =
|
2017-04-05 11:54:21 +04:00
|
|
|
|
Block_hash.of_b58check_exn
|
2017-10-13 13:24:22 +04:00
|
|
|
|
"BLockGenesisGenesisGenesisGenesisGenesisDDDDDegibDt" ;
|
2017-01-30 22:10:16 +04:00
|
|
|
|
protocol =
|
2017-04-05 11:54:21 +04:00
|
|
|
|
Protocol_hash.of_b58check_exn
|
2017-02-24 18:38:42 +04:00
|
|
|
|
"ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im" ;
|
2017-01-30 22:10:16 +04:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 22:02:40 +04:00
|
|
|
|
type error += Non_private_sandbox of P2p_types.addr
|
|
|
|
|
|
|
|
|
|
let () =
|
|
|
|
|
register_error_kind
|
|
|
|
|
`Permanent
|
|
|
|
|
~id:"main.run.non_private_sandbox"
|
|
|
|
|
~title:"Fordidden public sandbox"
|
|
|
|
|
~description:"A sandboxed node should not listen on public address."
|
|
|
|
|
~pp:begin fun ppf addr ->
|
|
|
|
|
Format.fprintf ppf
|
|
|
|
|
"The node is configured to listen a public addres (%a), \
|
|
|
|
|
while only 'private' network 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
|
|
|
|
|
2017-01-30 22:10:16 +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) =
|
|
|
|
|
let open Logging in
|
|
|
|
|
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 (config : Node_config_file.t) =
|
|
|
|
|
let patch_context json ctxt =
|
2017-10-09 12:55:12 +04:00
|
|
|
|
let module Proto = (val State.Registred_protocol.get_exn genesis.protocol) in
|
2017-01-30 22:10:16 +04:00
|
|
|
|
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. *)
|
2017-01-30 22:10:16 +04:00
|
|
|
|
begin
|
2017-08-09 18:09:41 +04:00
|
|
|
|
match config.net.listen_addr with
|
2017-01-30 22:10:16 +04:00
|
|
|
|
| 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
|
2017-08-28 22:02:40 +04:00
|
|
|
|
| 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))
|
2017-01-30 22:10:16 +04:00
|
|
|
|
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 ;
|
2017-04-10 23:14:17 +04:00
|
|
|
|
test_network_max_tll = Some (48 * 3600) ; (* 2 days *)
|
2017-01-30 22:10:16 +04:00
|
|
|
|
} in
|
|
|
|
|
Node.create node_config
|
|
|
|
|
|
|
|
|
|
let () =
|
|
|
|
|
let old_hook = !Lwt.async_exception_hook in
|
|
|
|
|
Lwt.async_exception_hook := function
|
|
|
|
|
| Ssl.Read_error _ -> ()
|
2017-06-02 20:19:28 +04:00
|
|
|
|
| Ssl.Write_error _ -> ()
|
2017-01-30 22:10:16 +04:00
|
|
|
|
| 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 () =
|
2017-11-08 19:02:19 +04:00
|
|
|
|
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)
|
2017-01-30 22:10:16 +04:00
|
|
|
|
|
|
|
|
|
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 () ->
|
2017-01-30 22:10:16 +04:00
|
|
|
|
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 () ->
|
|
|
|
|
Lwt_utils.may RPC_server.shutdown rpc >>= fun () ->
|
|
|
|
|
lwt_log_notice "BYE (%d)" x >>= fun () ->
|
2017-11-08 19:02:19 +04:00
|
|
|
|
Logging.close () >>= fun () ->
|
2017-01-30 22:10:16 +04:00
|
|
|
|
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 () ->
|
2017-01-30 22:10:16 +04:00
|
|
|
|
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 addressses are disabled, and constants of \
|
2017-03-15 19:00:53 +04:00
|
|
|
|
the economic protocol can be altered with an optional JSON file. \
|
2017-01-30 22:10:16 +04:00
|
|
|
|
$(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 the most prioritary one."
|
|
|
|
|
) ;
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
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 \
|
2017-01-30 22:10:16 +04:00
|
|
|
|
--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
|