diff --git a/src/bin_dal_node/RPC_server.ml b/src/bin_dal_node/RPC_server.ml index 233401b28d7dc893d467bd481ef096ceb7c58403..d17523f2fc548d964b25bf6928156be5be27fb19 100644 --- a/src/bin_dal_node/RPC_server.ml +++ b/src/bin_dal_node/RPC_server.ml @@ -217,7 +217,7 @@ module Slots_handlers = struct end module Profile_handlers = struct - let patch_profiles ctxt () operator_profiles = + let patch_profiles ctxt Services.{save_config} operator_profiles = let open Lwt_result_syntax in let gs_worker = Node_context.get_gs_worker ctxt in call_handler2 ctxt (fun _store {proto_parameters; _} -> @@ -229,7 +229,13 @@ module Profile_handlers = struct operator_profiles with | None -> fail Errors.[Profile_incompatibility] - | Some pctxt -> return @@ Node_context.set_profile_ctxt ctxt pctxt) + | Some pctxt -> + Node_context.set_profile_ctxt ctxt pctxt ; + if save_config then + let config = Node_context.get_config ctxt in + let profiles = Profile_manager.get_profiles pctxt in + Configuration_file.save {config with profiles} + else return_unit) let get_profiles ctxt () () = let open Lwt_result_syntax in diff --git a/src/lib_dal_node_services/services.ml b/src/lib_dal_node_services/services.ml index b5dd3c5a9dcacefdcc572e13a75eb07d2d3f043c..0964275acbf5023bf84362c79b5555f5de6edbc5 100644 --- a/src/lib_dal_node_services/services.ml +++ b/src/lib_dal_node_services/services.ml @@ -192,20 +192,27 @@ let get_published_level_headers : ~output:(Data_encoding.list slot_header_encoding) Tezos_rpc.Path.(open_root / "levels" /: Tezos_rpc.Arg.int32 / "headers") +type patch_profiles_query = {save_config : bool} + let patch_profiles : < meth : [`PATCH] ; input : operator_profiles ; output : unit ; prefix : unit ; params : unit - ; query : unit > + ; query : patch_profiles_query > service = Tezos_rpc.Service.patch_service ~description: "Update the list of profiles tracked by the DAL node. Note that it does \ not take the bootstrap profile as it is incompatible with other \ - profiles." - ~query:Tezos_rpc.Query.empty + profiles. The boolean parameter `save_config` makes the node save the \ + new profile list in its configuration file." + ~query: + Tezos_rpc.Query.( + query (fun save_config -> {save_config}) + |+ flag "save_config" (fun t -> t.save_config) + |> seal) ~input:(Data_encoding.list operator_profile_encoding) ~output:Data_encoding.unit Tezos_rpc.Path.(open_root / "profiles") diff --git a/src/lib_dal_node_services/services.mli b/src/lib_dal_node_services/services.mli index 1518e4c5269221b85d83b97198483c9610e3b75d..1440225483156383a22de7c0e5fce65f22e3f3df 100644 --- a/src/lib_dal_node_services/services.mli +++ b/src/lib_dal_node_services/services.mli @@ -141,16 +141,19 @@ val get_published_level_headers : ; query : Types.header_status option > service -(** Update the list of profiles tracked by the DAL node. - Note that it does not take the bootstrap profile as it - is incompatible with other profiles. *) +type patch_profiles_query = {save_config : bool} + +(** Update the list of profiles tracked by the DAL node. Note that it does not + take the bootstrap profile as it is incompatible with other profiles. The + boolean parameter `save_config` makes the node save the new profile list in its + configuration file. *) val patch_profiles : < meth : [`PATCH] ; input : Types.operator_profiles ; output : unit ; prefix : unit ; params : unit - ; query : unit > + ; query : patch_profiles_query > service (** Return the list of current profiles tracked by the DAL node *) diff --git a/src/proto_018_Proxford/lib_delegate/node_rpc.ml b/src/proto_018_Proxford/lib_delegate/node_rpc.ml index f43a7ef34fbbf4695fc3063e894dfc625d9c6981..d398ec7d0fc858d8ceb32c51de6db159268a04fe 100644 --- a/src/proto_018_Proxford/lib_delegate/node_rpc.ml +++ b/src/proto_018_Proxford/lib_delegate/node_rpc.ml @@ -306,9 +306,10 @@ let register_dal_profiles dal_node_rpc_ctxt delegates = Tezos_dal_node_services.Types.Attester consensus_key.public_key_hash) delegates in + let save_config = true in Tezos_rpc.Context.make_call Tezos_dal_node_services.Services.patch_profiles dal_node_rpc_ctxt () - () + Tezos_dal_node_services.Services.{save_config} profiles diff --git a/src/proto_alpha/lib_delegate/node_rpc.ml b/src/proto_alpha/lib_delegate/node_rpc.ml index fc9608f62ef9ef3ea4ca608c08a0b0177575e518..ab04265dde7e2818a961a04036329c0c7af69b89 100644 --- a/src/proto_alpha/lib_delegate/node_rpc.ml +++ b/src/proto_alpha/lib_delegate/node_rpc.ml @@ -306,9 +306,10 @@ let register_dal_profiles dal_node_rpc_ctxt delegates = Tezos_dal_node_services.Types.Attester consensus_key.public_key_hash) delegates in + let save_config = true in Tezos_rpc.Context.make_call Tezos_dal_node_services.Services.patch_profiles dal_node_rpc_ctxt () - () + Tezos_dal_node_services.Services.{save_config} profiles diff --git a/tezt/lib_tezos/dal_common.ml b/tezt/lib_tezos/dal_common.ml index 9dbe9ee46886f23c023eb372266703845eff5424..bc60c8604324a0a79b8cc5bcf102759af39f1346 100644 --- a/tezt/lib_tezos/dal_common.ml +++ b/tezt/lib_tezos/dal_common.ml @@ -173,6 +173,9 @@ module Dal_RPC = struct | [] -> () | _ -> JSON.error t "Not an empty object" + let mk_query_arg ~to_string field v_opt = + Option.fold ~none:[] ~some:(fun v -> [(field, to_string v)]) v_opt + let post_commitment slot = let slot = JSON.parse @@ -247,17 +250,17 @@ module Dal_RPC = struct Operator operator_profiles | _ -> failwith "invalid case" - let patch_profiles profiles = + let patch_profiles ?save_config profiles = let data : RPC_core.data = Data (`A (List.map json_of_operator_profile profiles)) in - make ~data PATCH ["profiles"] as_empty_object_or_fail + let query_string = + mk_query_arg ~to_string:string_of_bool "save_config" save_config + in + make ~query_string ~data PATCH ["profiles"] as_empty_object_or_fail let get_profiles () = make GET ["profiles"] profiles_of_json - let mk_query_arg ~to_string field v_opt = - Option.fold ~none:[] ~some:(fun v -> [(field, to_string v)]) v_opt - let get_commitment_headers ?slot_level ?slot_index commitment = let query_string = mk_query_arg ~to_string:string_of_int "slot_level" slot_level diff --git a/tezt/lib_tezos/dal_common.mli b/tezt/lib_tezos/dal_common.mli index 165a48b85265ec94df68f9b1d53392113e785f97..b890712920f846fef25e31ea9edf259530567cbe 100644 --- a/tezt/lib_tezos/dal_common.mli +++ b/tezt/lib_tezos/dal_common.mli @@ -168,7 +168,7 @@ module RPC : sig (** Call RPC "PATCH /profiles" to update the list of profiles tracked by the DAL node. *) - val patch_profiles : operator_profiles -> unit RPC_core.t + val patch_profiles : ?save_config:bool -> operator_profiles -> unit RPC_core.t (** Call RPC "GET /profiles" to retrieve the list of profiles tracked by the DAL node. *) diff --git a/tezt/lib_tezos/dal_node.mli b/tezt/lib_tezos/dal_node.mli index 6631ef27906ff4a37b00a86c98ed0fe0cb97eb2c..88d0881c676bc39a32009cc7d390ff886ffe50a8 100644 --- a/tezt/lib_tezos/dal_node.mli +++ b/tezt/lib_tezos/dal_node.mli @@ -106,6 +106,9 @@ val terminate : ?timeout:float -> t -> unit Lwt.t (** Send SIGKILL and wait for the process to terminate. *) val kill : t -> unit Lwt.t +(** Send SIGSTOP to a daemon. Do not wait for the process to terminate. *) +val stop : t -> unit Lwt.t + (** Shows in stdout every events sent by the node *) val log_events : t -> unit diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 6b4bc7742712e342bcada8b0ee09167358d7dfcf..4e8dcf4f8d0d3d79ee5a8c69e7b96f2e5155f624 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -2136,8 +2136,8 @@ let test_dal_node_test_patch_profile _protocol _parameters _cryptobox _node let* response = Dal_RPC.(call_raw dal_node @@ patch_profiles [profile]) in return @@ RPC_core.check_string_response ~code:400 response in - let patch_profile_rpc profile = - Dal_RPC.(call dal_node (patch_profiles [profile])) + let patch_profile_rpc ?save_config profile = + Dal_RPC.(call dal_node (patch_profiles ?save_config [profile])) in let profile1 = Dal_RPC.Attester Constant.bootstrap1.public_key_hash in let profile2 = Dal_RPC.Attester Constant.bootstrap2.public_key_hash in @@ -2155,10 +2155,24 @@ let test_dal_node_test_patch_profile _protocol _parameters _cryptobox _node let* () = check_profiles ~__LOC__ dal_node ~expected:(Operator [profile1; profile2]) in - (* Test that the patched profiles are persisted after restart. *) + (* Test that the patched profiles are persisted after restart using SIGTERM. *) let* () = Dal_node.terminate dal_node in let* () = Dal_node.run dal_node ~wait_ready:true in - check_profiles ~__LOC__ dal_node ~expected:(Operator [profile1; profile2]) + let* () = + check_profiles ~__LOC__ dal_node ~expected:(Operator [profile1; profile2]) + in + (* Test whether the patched profiles persist after a restart, even if we stop + the DAL node abruptly to not let it the opportunity to save its config at + the end of its execution, when the [save_config] flag is used. *) + let profile3 = Dal_RPC.Attester Constant.bootstrap3.public_key_hash in + let* () = patch_profile_rpc ~save_config:true profile3 in + let* () = Dal_node.stop dal_node in + let* () = Dal_node.kill dal_node in + let* () = Dal_node.run dal_node ~wait_ready:true in + check_profiles + ~__LOC__ + dal_node + ~expected:(Operator [profile1; profile2; profile3]) (* Check that result of the DAL node's GET /profiles//attested_levels//assigned_shard_indices