diff --git a/CHANGES.rst b/CHANGES.rst index 14c1d9d1d53eb52fa7916618390859e19bc7e719..06fafb3824563b5fb6349287dfc8d73dc9c9a631 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -34,6 +34,13 @@ Client Baker ----- +- **Deprecation:** For Paris and Quebec protocols, launching a + baker daemon without specifying a DAL node endpoint is deprecated. + To opt out of this requirement, use the newly introduced + ``--without-dal`` option (MR :gl:`!16213`). + The CLI argument ``--dal-node `` or ``--without-dal`` will be mandatory + in the next version of Octez. + Accuser ------- diff --git a/src/lib_dal_node_services/types.ml b/src/lib_dal_node_services/types.ml index cdfd69bc07a466e098434cbcbbb4200f62797a12..4fbcdd922fbfde2bea99615e58ded4f2b604c436 100644 --- a/src/lib_dal_node_services/types.ml +++ b/src/lib_dal_node_services/types.ml @@ -690,4 +690,26 @@ module Health = struct (fun {status; checks} -> (status, checks)) (fun (status, checks) -> {status; checks}) (obj2 (req "status" status_encoding) (req "checks" checks_encoding)) + + let pp_status fmt status = + Format.pp_print_string + fmt + (match status with + | Up -> "up" + | Degraded -> "degraded" + | Down -> "down" + | Ok -> "ok" + | Ko -> "ko") + + let pp fmt {status; checks} = + Format.fprintf + fmt + "status: %a; checks: %a" + pp_status + status + (Format.pp_print_list + ~pp_sep:Format.pp_print_cut + (fun fmt (name, status) -> + Format.fprintf fmt "(%s: %a)" name pp_status status)) + checks end diff --git a/src/lib_dal_node_services/types.mli b/src/lib_dal_node_services/types.mli index f9de61cfd5b90f622b51048bcd8a5cf4a099a32e..41001d39c2f5af9b9a8c59bdc88e630fbb81149b 100644 --- a/src/lib_dal_node_services/types.mli +++ b/src/lib_dal_node_services/types.mli @@ -372,4 +372,6 @@ module Health : sig type t = {status : status; checks : (string * status) list} val encoding : t Data_encoding.t + + val pp : Format.formatter -> t -> unit end diff --git a/src/proto_020_PsParisC/lib_delegate/baking_actions.ml b/src/proto_020_PsParisC/lib_delegate/baking_actions.ml index 917dd451db2ff7330c62ad14bd3748042be94b98..e4fabc66b9661e64306a9d9dff4d47adee1c7dc8 100644 --- a/src/proto_020_PsParisC/lib_delegate/baking_actions.ml +++ b/src/proto_020_PsParisC/lib_delegate/baking_actions.ml @@ -403,7 +403,7 @@ let only_if_dal_feature_enabled = incr no_dal_node_warning_counter ; let* () = if !no_dal_node_warning_counter mod 10 = 1 then - Events.(emit no_dal_node ()) + Events.(emit no_dal_node_running ()) else return_unit in return default_value diff --git a/src/proto_020_PsParisC/lib_delegate/baking_commands.ml b/src/proto_020_PsParisC/lib_delegate/baking_commands.ml index d0ec936ce26715043039d45c1337c87c98a49739..b12ca1f7fce6fc6aaacaf7d0c934f4a7f9894f48 100644 --- a/src/proto_020_PsParisC/lib_delegate/baking_commands.ml +++ b/src/proto_020_PsParisC/lib_delegate/baking_commands.ml @@ -355,6 +355,14 @@ let endpoint_arg = ~doc:"endpoint of the DAL node, e.g. 'http://localhost:8933'" (Tezos_clic.parameter (fun _ s -> return @@ Uri.of_string s)) +let without_dal_arg = + Tezos_clic.switch + ~long:"without-dal" + ~doc: + "If without-dal flag is set, the daemon will not try to connect to a DAL \ + node." + () + let block_count_arg = Tezos_clic.default_arg ~long:"count" @@ -680,10 +688,36 @@ let lookup_default_vote_file_path (cctxt : Protocol_client_context.full) = let base_dir_file = Filename.Infix.(cctxt#get_base_dir // default_filename) in when_s file_exists base_dir_file @@ fun () -> return_none +(* This function checks that a DAL node endpoint was given, + and that the specified DAL node is "healthy", + (the DAL's nodes 'health' RPC is used for that). *) +let check_dal_node without_dal dal_node_rpc_ctxt = + let open Lwt_result_syntax in + let result_emit f x = + let*! () = Events.emit f x in + return_unit + in + match (dal_node_rpc_ctxt, without_dal) with + | None, true -> + (* The user is aware that no DAL node is running, since they explicitly + used the [--without-dal] option. However, we do not want to reduce the + exposition of bakers to warnings about DAL, so we keep it. *) + result_emit Events.no_dal_node_running () + | None, false -> result_emit Events.no_dal_deprecation () + | Some _, true -> tzfail Incompatible_dal_options + | Some ctxt, false -> ( + let*! health = Node_rpc.get_dal_health ctxt in + match health with + | Ok health -> ( + match health.status with + | Tezos_dal_node_services.Types.Health.Up -> return_unit + | _ -> result_emit Events.unhealthy_dal_node (ctxt#base, health)) + | Error _ -> result_emit Events.unreachable_dal_node ctxt#base) + type baking_mode = Local of {local_data_dir_path : string} | Remote let baker_args = - Tezos_clic.args15 + Tezos_clic.args16 pidfile_arg node_version_check_bypass_arg node_version_allowed_arg @@ -697,6 +731,7 @@ let baker_args = per_block_vote_file_arg operations_arg endpoint_arg + without_dal_arg state_recorder_switch_arg pre_emptive_forge_time_arg @@ -714,6 +749,7 @@ let run_baker per_block_vote_file, extra_operations, dal_node_endpoint, + without_dal, state_recorder, pre_emptive_forge_time ) baking_mode sources cctxt = let open Lwt_result_syntax in @@ -737,6 +773,10 @@ let run_baker ~default_adaptive_issuance_vote:adaptive_issuance_vote ~per_block_vote_file in + let dal_node_rpc_ctxt = + Option.map Baking_scheduling.create_dal_node_rpc_ctxt dal_node_endpoint + in + let* () = check_dal_node without_dal dal_node_rpc_ctxt in let* delegates = get_delegates cctxt sources in let context_path = match baking_mode with diff --git a/src/proto_020_PsParisC/lib_delegate/baking_errors.ml b/src/proto_020_PsParisC/lib_delegate/baking_errors.ml index 2fa448def8faa76bd5346eea62c02ae1cb8ba740..6f3eb86c26aeae4afd7799a71f8b2423fc5ba333 100644 --- a/src/proto_020_PsParisC/lib_delegate/baking_errors.ml +++ b/src/proto_020_PsParisC/lib_delegate/baking_errors.ml @@ -332,3 +332,36 @@ let () = | _ -> None) (fun (chain, block_hash, length) -> Unexpected_empty_block_list {chain; block_hash; length}) + +(* DAL node related errors *) + +type error += No_dal_node_endpoint | Incompatible_dal_options + +let () = + register_error_kind + `Permanent + ~id:"Client_commands.no_dal_node_endpoint" + ~title:"Missing_dal_node_argument" + ~description:"Explicit DAL node configuration is required." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Please connect a running DAL node using '--dal-node '. If \ + you do not want to run a DAL node, you have to opt-out using \ + '--without-dal'.") + Data_encoding.unit + (function No_dal_node_endpoint -> Some () | _ -> None) + (fun () -> No_dal_node_endpoint) ; + register_error_kind + `Permanent + ~id:"Client_commands.incompatible_dal_options" + ~title:"Incompatible_dal_options" + ~description:"'--dal-node' and '--without-dal' are incompatible." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "'--dal-node ' and '--without-dal' are incompatible. Please \ + do not pass '--without-dal' option.") + Data_encoding.unit + (function Incompatible_dal_options -> Some () | _ -> None) + (fun () -> Incompatible_dal_options) diff --git a/src/proto_020_PsParisC/lib_delegate/baking_events.ml b/src/proto_020_PsParisC/lib_delegate/baking_events.ml index f1e1f60cf144b702cfaa19f56650b1af2d868dd3..96d553d9c642280f94677af5ee6bad86ced6d512 100644 --- a/src/proto_020_PsParisC/lib_delegate/baking_events.ml +++ b/src/proto_020_PsParisC/lib_delegate/baking_events.ml @@ -71,6 +71,55 @@ module Commands = struct ( "baker_commit", Data_encoding.option Tezos_version.Octez_node_version.commit_info_encoding ) + + let no_dal_node_running = + declare_0 + ~section + ~name:"no_dal_node_running" + ~level:Warning + ~msg: + "No DAL node endpoint has been provided.\n\ + It will soon be required to launch a DAL node before running the \ + baker. For instructions on running a DAL node, please visit \ + https://docs.tezos.com/tutorials/join-dal-baker." + () + + let unhealthy_dal_node = + declare_2 + ~section + ~name:"unhealthy_dal_node" + ~level:Error + ~msg: + "The DAL node running on {endpoint} is not healthy. DAL attestations \ + cannot be sent.\n\ + Its health is {health}.\n\ + Please check your DAL node and possibly restart it." + ~pp1:Uri.pp + ("endpoint", Tezos_rpc.Encoding.uri_encoding) + ~pp2:Tezos_dal_node_services.Types.Health.pp + ("health", Tezos_dal_node_services.Types.Health.encoding) + + let unreachable_dal_node = + declare_1 + ~section + ~name:"unreachable_dal_node" + ~level:Error + ~msg: + "The DAL node cannot be reached on endpoint: {endpoint}.\n\ + Please check your DAL node and possibly restart it." + ~pp1:Uri.pp + ("endpoint", Tezos_rpc.Encoding.uri_encoding) + + let no_dal_deprecation = + declare_0 + ~section + ~name:"no_dal_deprecation" + ~level:Warning + ~msg: + "DEPRECATED: Please use a DAL node to launch a baker. If you purposely \ + do not run a DAL node, the option --without-dal will soon be \ + mandatory." + () end module State_transitions = struct @@ -1042,15 +1091,7 @@ module Actions = struct Protocol.Alpha_context.Per_block_votes.adaptive_issuance_vote_encoding ) - let no_dal_node = - declare_0 - ~section - ~name:"no_dal_node" - ~level:Notice - ~msg: - "DAL feature enabled, but no DAL node specified: cannot fetch \ - attestations" - () + let no_dal_node_running = Commands.no_dal_node_running end module VDF = struct diff --git a/src/proto_020_PsParisC/lib_delegate/baking_scheduling.mli b/src/proto_020_PsParisC/lib_delegate/baking_scheduling.mli index 948cc995a19a1fa93ba99267839b736f58256b7d..ba9f10403d66af6f419522786dabb71c18fa4d02 100644 --- a/src/proto_020_PsParisC/lib_delegate/baking_scheduling.mli +++ b/src/proto_020_PsParisC/lib_delegate/baking_scheduling.mli @@ -109,3 +109,6 @@ val run : Baking_configuration.t -> consensus_key list -> unit tzresult Lwt.t + +val create_dal_node_rpc_ctxt : + Uri.t -> Tezos_rpc_http_client_unix.RPC_client_unix.http_ctxt diff --git a/src/proto_020_PsParisC/lib_delegate/node_rpc.ml b/src/proto_020_PsParisC/lib_delegate/node_rpc.ml index ce55e8f4d2827fe0206e33487f29a6acd6a77418..4984d13d1f89273d89f391424a55fc5f972e05e9 100644 --- a/src/proto_020_PsParisC/lib_delegate/node_rpc.ml +++ b/src/proto_020_PsParisC/lib_delegate/node_rpc.ml @@ -380,3 +380,11 @@ let register_dal_profiles dal_node_rpc_ctxt delegates = () () profiles + +let get_dal_health dal_node_rpc_ctxt = + Tezos_rpc.Context.make_call + Tezos_dal_node_services.Services.health + dal_node_rpc_ctxt + () + () + () diff --git a/src/proto_020_PsParisC/lib_delegate/node_rpc.mli b/src/proto_020_PsParisC/lib_delegate/node_rpc.mli index ec8cb6d32012f4a7f24da42576e15afe7d2fb849..2998ac346c30a4149e0581bd1390fafc45d1e570 100644 --- a/src/proto_020_PsParisC/lib_delegate/node_rpc.mli +++ b/src/proto_020_PsParisC/lib_delegate/node_rpc.mli @@ -103,3 +103,8 @@ val register_dal_profiles : Tezos_rpc.Context.generic -> Baking_state.consensus_key list -> unit tzresult Lwt.t + +(** [get_dal_health ctxt] calls the DAL node RPC 'GET /health' *) +val get_dal_health : + Tezos_rpc.Context.generic -> + Tezos_dal_node_services.Types.Health.t tzresult Lwt.t diff --git a/src/proto_021_PsQuebec/lib_delegate/baking_actions.ml b/src/proto_021_PsQuebec/lib_delegate/baking_actions.ml index 1abeb7c18eab5666881a8436fd6c670487d1b029..1afc50ae93e2ec6e2dcf7e5386c8301d51b86c4d 100644 --- a/src/proto_021_PsQuebec/lib_delegate/baking_actions.ml +++ b/src/proto_021_PsQuebec/lib_delegate/baking_actions.ml @@ -405,7 +405,7 @@ let only_if_dal_feature_enabled = incr no_dal_node_warning_counter ; let* () = if !no_dal_node_warning_counter mod 10 = 1 then - Events.(emit no_dal_node ()) + Events.(emit no_dal_node_running ()) else return_unit in return default_value diff --git a/src/proto_021_PsQuebec/lib_delegate/baking_commands.ml b/src/proto_021_PsQuebec/lib_delegate/baking_commands.ml index 0ec14e14cdfafca0a51698b5e458e0d4b277a6a1..93484e993087f62f463edd79fc15b0791ce97d24 100644 --- a/src/proto_021_PsQuebec/lib_delegate/baking_commands.ml +++ b/src/proto_021_PsQuebec/lib_delegate/baking_commands.ml @@ -358,6 +358,14 @@ let endpoint_arg = ~doc:"endpoint of the DAL node, e.g. 'http://localhost:8933'" (Tezos_clic.parameter (fun _ s -> return @@ Uri.of_string s)) +let without_dal_arg = + Tezos_clic.switch + ~long:"without-dal" + ~doc: + "If without-dal flag is set, the daemon will not try to connect to a DAL \ + node." + () + let block_count_arg = Tezos_clic.default_arg ~long:"count" @@ -664,10 +672,36 @@ let lookup_default_vote_file_path (cctxt : Protocol_client_context.full) = let base_dir_file = Filename.Infix.(cctxt#get_base_dir // default_filename) in when_s file_exists base_dir_file @@ fun () -> return_none +(* This function checks that a DAL node endpoint was given, + and that the specified DAL node is "healthy", + (the DAL's nodes 'health' RPC is used for that). *) +let check_dal_node without_dal dal_node_rpc_ctxt = + let open Lwt_result_syntax in + let result_emit f x = + let*! () = Events.emit f x in + return_unit + in + match (dal_node_rpc_ctxt, without_dal) with + | None, true -> + (* The user is aware that no DAL node is running, since they explicitly + used the [--without-dal] option. However, we do not want to reduce the + exposition of bakers to warnings about DAL, so we keep it. *) + result_emit Events.no_dal_node_running () + | None, false -> result_emit Events.no_dal_deprecation () + | Some _, true -> tzfail Incompatible_dal_options + | Some ctxt, false -> ( + let*! health = Node_rpc.get_dal_health ctxt in + match health with + | Ok health -> ( + match health.status with + | Tezos_dal_node_services.Types.Health.Up -> return_unit + | _ -> result_emit Events.unhealthy_dal_node (ctxt#base, health)) + | Error _ -> result_emit Events.unreachable_dal_node ctxt#base) + type baking_mode = Local of {local_data_dir_path : string} | Remote let baker_args = - Tezos_clic.args15 + Tezos_clic.args16 pidfile_arg node_version_check_bypass_arg node_version_allowed_arg @@ -681,6 +715,7 @@ let baker_args = per_block_vote_file_arg operations_arg endpoint_arg + without_dal_arg state_recorder_switch_arg pre_emptive_forge_time_arg @@ -698,6 +733,7 @@ let run_baker per_block_vote_file, extra_operations, dal_node_endpoint, + without_dal, state_recorder, pre_emptive_forge_time ) baking_mode sources cctxt = let open Lwt_result_syntax in @@ -721,6 +757,10 @@ let run_baker ~default_adaptive_issuance_vote:adaptive_issuance_vote ~per_block_vote_file in + let dal_node_rpc_ctxt = + Option.map Baking_scheduling.create_dal_node_rpc_ctxt dal_node_endpoint + in + let* () = check_dal_node without_dal dal_node_rpc_ctxt in let* delegates = get_delegates cctxt sources in let context_path = match baking_mode with diff --git a/src/proto_021_PsQuebec/lib_delegate/baking_errors.ml b/src/proto_021_PsQuebec/lib_delegate/baking_errors.ml index 2fa448def8faa76bd5346eea62c02ae1cb8ba740..6f3eb86c26aeae4afd7799a71f8b2423fc5ba333 100644 --- a/src/proto_021_PsQuebec/lib_delegate/baking_errors.ml +++ b/src/proto_021_PsQuebec/lib_delegate/baking_errors.ml @@ -332,3 +332,36 @@ let () = | _ -> None) (fun (chain, block_hash, length) -> Unexpected_empty_block_list {chain; block_hash; length}) + +(* DAL node related errors *) + +type error += No_dal_node_endpoint | Incompatible_dal_options + +let () = + register_error_kind + `Permanent + ~id:"Client_commands.no_dal_node_endpoint" + ~title:"Missing_dal_node_argument" + ~description:"Explicit DAL node configuration is required." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Please connect a running DAL node using '--dal-node '. If \ + you do not want to run a DAL node, you have to opt-out using \ + '--without-dal'.") + Data_encoding.unit + (function No_dal_node_endpoint -> Some () | _ -> None) + (fun () -> No_dal_node_endpoint) ; + register_error_kind + `Permanent + ~id:"Client_commands.incompatible_dal_options" + ~title:"Incompatible_dal_options" + ~description:"'--dal-node' and '--without-dal' are incompatible." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "'--dal-node ' and '--without-dal' are incompatible. Please \ + do not pass '--without-dal' option.") + Data_encoding.unit + (function Incompatible_dal_options -> Some () | _ -> None) + (fun () -> Incompatible_dal_options) diff --git a/src/proto_021_PsQuebec/lib_delegate/baking_events.ml b/src/proto_021_PsQuebec/lib_delegate/baking_events.ml index 42c97ef65371df46cceccf57a7590c1cc4a2ccb8..f541bcb207123fd5a906a2a27280f5d79c4663d5 100644 --- a/src/proto_021_PsQuebec/lib_delegate/baking_events.ml +++ b/src/proto_021_PsQuebec/lib_delegate/baking_events.ml @@ -71,6 +71,55 @@ module Commands = struct ( "baker_commit", Data_encoding.option Tezos_version.Octez_node_version.commit_info_encoding ) + + let no_dal_node_running = + declare_0 + ~section + ~name:"no_dal_node_running" + ~level:Warning + ~msg: + "No DAL node endpoint has been provided.\n\ + It will soon be required to launch a DAL node before running the \ + baker. For instructions on running a DAL node, please visit \ + https://docs.tezos.com/tutorials/join-dal-baker." + () + + let unhealthy_dal_node = + declare_2 + ~section + ~name:"unhealthy_dal_node" + ~level:Error + ~msg: + "The DAL node running on {endpoint} is not healthy. DAL attestations \ + cannot be sent.\n\ + Its health is {health}.\n\ + Please check your DAL node and possibly restart it." + ~pp1:Uri.pp + ("endpoint", Tezos_rpc.Encoding.uri_encoding) + ~pp2:Tezos_dal_node_services.Types.Health.pp + ("health", Tezos_dal_node_services.Types.Health.encoding) + + let unreachable_dal_node = + declare_1 + ~section + ~name:"unreachable_dal_node" + ~level:Error + ~msg: + "The DAL node cannot be reached on endpoint: {endpoint}.\n\ + Please check your DAL node and possibly restart it." + ~pp1:Uri.pp + ("endpoint", Tezos_rpc.Encoding.uri_encoding) + + let no_dal_deprecation = + declare_0 + ~section + ~name:"no_dal_deprecation" + ~level:Warning + ~msg: + "DEPRECATED: Please use a DAL node to launch a baker. If you purposely \ + do not run a DAL node, the option --without-dal will soon be \ + mandatory." + () end module State_transitions = struct @@ -1045,15 +1094,7 @@ module Actions = struct Protocol.Alpha_context.Per_block_votes.adaptive_issuance_vote_encoding ) - let no_dal_node = - declare_0 - ~section - ~name:"no_dal_node" - ~level:Notice - ~msg: - "DAL feature enabled, but no DAL node specified: cannot fetch \ - attestations" - () + let no_dal_node_running = Commands.no_dal_node_running end module VDF = struct diff --git a/src/proto_021_PsQuebec/lib_delegate/baking_scheduling.mli b/src/proto_021_PsQuebec/lib_delegate/baking_scheduling.mli index 948cc995a19a1fa93ba99267839b736f58256b7d..ba9f10403d66af6f419522786dabb71c18fa4d02 100644 --- a/src/proto_021_PsQuebec/lib_delegate/baking_scheduling.mli +++ b/src/proto_021_PsQuebec/lib_delegate/baking_scheduling.mli @@ -109,3 +109,6 @@ val run : Baking_configuration.t -> consensus_key list -> unit tzresult Lwt.t + +val create_dal_node_rpc_ctxt : + Uri.t -> Tezos_rpc_http_client_unix.RPC_client_unix.http_ctxt diff --git a/src/proto_021_PsQuebec/lib_delegate/node_rpc.ml b/src/proto_021_PsQuebec/lib_delegate/node_rpc.ml index 554ed1769e76fdbf24c443612aea57807eebbdeb..c7bfb54005a99df89ab9ec9b7608ffcb86638d0b 100644 --- a/src/proto_021_PsQuebec/lib_delegate/node_rpc.ml +++ b/src/proto_021_PsQuebec/lib_delegate/node_rpc.ml @@ -381,3 +381,11 @@ let register_dal_profiles dal_node_rpc_ctxt delegates = () () profiles + +let get_dal_health dal_node_rpc_ctxt = + Tezos_rpc.Context.make_call + Tezos_dal_node_services.Services.health + dal_node_rpc_ctxt + () + () + () diff --git a/src/proto_021_PsQuebec/lib_delegate/node_rpc.mli b/src/proto_021_PsQuebec/lib_delegate/node_rpc.mli index ec8cb6d32012f4a7f24da42576e15afe7d2fb849..2998ac346c30a4149e0581bd1390fafc45d1e570 100644 --- a/src/proto_021_PsQuebec/lib_delegate/node_rpc.mli +++ b/src/proto_021_PsQuebec/lib_delegate/node_rpc.mli @@ -103,3 +103,8 @@ val register_dal_profiles : Tezos_rpc.Context.generic -> Baking_state.consensus_key list -> unit tzresult Lwt.t + +(** [get_dal_health ctxt] calls the DAL node RPC 'GET /health' *) +val get_dal_health : + Tezos_rpc.Context.generic -> + Tezos_dal_node_services.Types.Health.t tzresult Lwt.t