![michael](/assets/img/avatar_default.png)
This allows a node to specify that it doesn't want to receive operations that are not included in a block. To do so, one should use the new --disable-mempool option. A disabled mempool node announce its configuration during the ACK exchange. This configuration should be easily expandable with other options. Node will keep track of the ack exchange configuration for each nodes it is connected to and will not propagate its mempool to disabled mempool nodes. A disabled mempool node will ignore non-empty mempool received. In the future, this incident should be punish by a decrement of its score and / or ban.
392 lines
14 KiB
OCaml
392 lines
14 KiB
OCaml
(**************************************************************************)
|
||
(* *)
|
||
(* Copyright (c) 2014 - 2018. *)
|
||
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
||
(* *)
|
||
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
||
(* *)
|
||
(**************************************************************************)
|
||
|
||
open Node_logging
|
||
|
||
let genesis : State.Chain.genesis = {
|
||
time =
|
||
Time.of_notation_exn "2018-04-17T11:46:23Z" ;
|
||
block =
|
||
Block_hash.of_b58check_exn
|
||
"BLockGenesisGenesisGenesisGenesisGenesisa52f8bUWPcg" ;
|
||
protocol =
|
||
Protocol_hash.of_b58check_exn
|
||
"ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im" ;
|
||
}
|
||
|
||
type error += Non_private_sandbox of P2p_addr.t
|
||
type error += RPC_Port_already_in_use of P2p_point.Id.t list
|
||
|
||
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_addr.encoding))
|
||
(function Non_private_sandbox addr -> Some addr | _ -> None)
|
||
(fun addr -> Non_private_sandbox addr);
|
||
register_error_kind
|
||
`Permanent
|
||
~id:"main.run.port_already_in_use"
|
||
~title:"Cannot start sode: RPC port already in use"
|
||
~description:"An other tezos node is probably running on the same RPC port."
|
||
~pp:begin fun ppf addrlist ->
|
||
Format.fprintf ppf
|
||
"An other tezos node is probably running on one of these addresses (%a). \
|
||
Please choose another RPC port."
|
||
(Format.pp_print_list P2p_point.Id.pp) addrlist
|
||
end
|
||
Data_encoding.(obj1 (req "addrlist" (list P2p_point.Id.encoding)))
|
||
(function | RPC_Port_already_in_use addrlist -> Some addrlist | _ -> None)
|
||
(fun addrlist -> RPC_Port_already_in_use addrlist)
|
||
|
||
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 find_log_rules default =
|
||
match Option.try_with (fun () -> Sys.getenv "TEZOS_LOG"),
|
||
Option.try_with (fun () -> Sys.getenv "LWT_LOG")
|
||
with
|
||
| Some rules, None -> "environment variable TEZOS_LOG", Some rules
|
||
| None, Some rules -> "environment variable LWT_LOG", Some rules
|
||
| None, None -> "configuration file", default
|
||
| Some rules, Some _ ->
|
||
warn "Both environment variables TEZOS_LOG and LWT_LOG \
|
||
defined, using TEZOS_LOG." ;
|
||
"environment varible TEZOS_LOG", Some rules
|
||
|
||
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 origin, rules = find_log_rules log_config.rules in
|
||
Option.iter rules ~f:begin fun rules ->
|
||
try Lwt_log_core.load_rules rules ~fail_on_error:true
|
||
with _ ->
|
||
fatal_error "Incorrect log rules defined in %s, exiting." origin ;
|
||
exit 1
|
||
end
|
||
end ;
|
||
Logging_unix.init ~template:log_config.template log_config.output
|
||
|
||
let init_node ?sandbox (config : Node_config_file.t) =
|
||
let patch_context json ctxt =
|
||
begin
|
||
match json with
|
||
| None -> Lwt.return ctxt
|
||
| Some json ->
|
||
Tezos_storage.Context.set ctxt
|
||
["sandbox_parameter"]
|
||
(Data_encoding.Binary.to_bytes_exn Data_encoding.json json)
|
||
end >>= fun ctxt ->
|
||
let module Proto = (val Registered_protocol.get_exn genesis.protocol) in
|
||
Proto.init ctxt {
|
||
level = 0l ;
|
||
proto_level = 0 ;
|
||
predecessor = genesis.block ;
|
||
timestamp = genesis.time ;
|
||
validation_passes = 0 ;
|
||
operations_hash = Operation_list_list_hash.empty ;
|
||
fitness = [] ;
|
||
context = Context_hash.zero ;
|
||
} >>= function
|
||
| Error _ -> assert false (* FIXME error *)
|
||
| Ok { context = ctxt ; _ } ->
|
||
Lwt.return ctxt in
|
||
begin
|
||
match sandbox with
|
||
| None -> Lwt.return_none
|
||
| Some sandbox_param ->
|
||
match sandbox_param with
|
||
| None -> Lwt.return None
|
||
| Some file ->
|
||
Lwt_utils_unix.Json.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 None
|
||
| Ok json ->
|
||
Lwt.return (Some json)
|
||
end >>= fun sandbox_param ->
|
||
(* TODO "WARN" when pow is below our expectation. *)
|
||
begin
|
||
match config.p2p.listen_addr with
|
||
| None ->
|
||
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)
|
||
| None, Some _ -> return None
|
||
| _ ->
|
||
(Node_config_file.resolve_bootstrap_addrs
|
||
config.p2p.bootstrap_peers) >>= fun trusted_points ->
|
||
Node_identity_file.read
|
||
(config.data_dir //
|
||
Node_data_version.default_identity_file_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.p2p.closed ;
|
||
identity ;
|
||
proof_of_work_target =
|
||
Crypto_box.make_target config.p2p.expected_pow ;
|
||
disable_mempool = config.p2p.disable_mempool ;
|
||
}
|
||
in
|
||
return (Some (p2p_config, config.p2p.limits))
|
||
end >>=? fun p2p_config ->
|
||
let node_config : Node.config = {
|
||
genesis ;
|
||
patch_context = Some (patch_context sandbox_param) ;
|
||
store_root = store_dir config.data_dir ;
|
||
context_root = context_dir config.data_dir ;
|
||
p2p = p2p_config ;
|
||
test_chain_max_tll = Some (48 * 3600) ; (* 2 days *)
|
||
} in
|
||
Node.create
|
||
node_config
|
||
config.shell.peer_validator_limits
|
||
config.shell.block_validator_limits
|
||
config.shell.prevalidator_limits
|
||
config.shell.chain_validator_limits
|
||
|
||
(* Add default accepted CORS headers *)
|
||
let sanitize_cors_headers ~default headers =
|
||
List.map String.lowercase_ascii headers |>
|
||
String.Set.of_list |>
|
||
String.Set.(union (of_list default)) |>
|
||
String.Set.elements
|
||
|
||
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 () ->
|
||
let cors_headers =
|
||
sanitize_cors_headers
|
||
~default:["Content-Type"] rpc_config.cors_headers in
|
||
Lwt.catch
|
||
(fun () ->
|
||
RPC_server.launch ~host mode dir
|
||
~media_types:Media_type.all_media_types
|
||
~cors:{ allowed_origins = rpc_config.cors_origins ;
|
||
allowed_headers = cors_headers } >>= fun server ->
|
||
return (Some server))
|
||
(function
|
||
|Unix.Unix_error(Unix.EADDRINUSE, "bind","") ->
|
||
fail (RPC_Port_already_in_use [(addr,port)])
|
||
| exn -> Lwt.return (error_exn exn)
|
||
)
|
||
|
||
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) =
|
||
Node_data_version.ensure_data_dir config.data_dir >>=? fun () ->
|
||
Lwt_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 ~f:RPC_server.shutdown rpc >>= fun () ->
|
||
lwt_log_notice "BYE (%d)" x >>= fun () ->
|
||
Logging_unix.close () >>= fun () ->
|
||
return ()
|
||
|
||
let process sandbox verbosity args =
|
||
let verbosity =
|
||
match verbosity with
|
||
| [] -> None
|
||
| [_] -> Some Logging.Info
|
||
| _ -> Some Logging.Debug in
|
||
let run =
|
||
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_lock_file.is_locked
|
||
(lock_file config.data_dir) >>=? function
|
||
| false ->
|
||
Lwt.catch
|
||
(fun () -> run ?sandbox ?verbosity config)
|
||
(function
|
||
|Unix.Unix_error(Unix.EADDRINUSE, "bind","") ->
|
||
begin match config.rpc.listen_addr with
|
||
| None -> assert false
|
||
| Some addr ->
|
||
Node_config_file.resolve_rpc_listening_addrs addr >>= fun addrlist ->
|
||
fail (RPC_Port_already_in_use addrlist)
|
||
end
|
||
| exn -> Lwt.return (error_exn exn)
|
||
)
|
||
| 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 =
|
||
"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)",
|
||
"$(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
|