diff --git a/CHANGES.rst b/CHANGES.rst index 98d15c9acb2dbb9ee31638148caf388dc5f87638..c15afce04b6e4c738e84302745258f4bd9d08a52 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -69,6 +69,10 @@ Data Availability Layer (DAL) DAL node ~~~~~~~~ +- **Breaking change** The ``/levels//slots//status`` + RPC now answers with ``unpublished`` status for unpublished slots instead + of a 404 empty response. (MR :gl:`!19613`) + - Added RPC ``GET /profiles/{pkh}/monitor/attestable_slots`` to open a monitoring stream that emits a JSON ``slot_id`` each time a slot becomes attestable for the given public key hash (``pkh``). A slot id is emitted when all shards assigned to diff --git a/src/lib_dal_node/RPC_server.ml b/src/lib_dal_node/RPC_server.ml index a864d5349dfc5637ceea04ed5577f68ed95e8014..e4e9e0f8300eeb60397951f828d1e7af42a017c3 100644 --- a/src/lib_dal_node/RPC_server.ml +++ b/src/lib_dal_node/RPC_server.ml @@ -236,25 +236,10 @@ module Slots_handlers = struct let get_slot_commitment ctxt slot_level slot_index () () = call_handler1 (fun () -> - let slot_id : Types.slot_id = {slot_level; slot_index} in let open Lwt_result_syntax in - let published_level = slot_id.Types.Slot_id.slot_level in - let*? proto_parameters = - Node_context.get_proto_parameters ctxt ~level:(`Level published_level) - |> Errors.other_result - in - let attested_level = - Int32.(add published_level (of_int proto_parameters.attestation_lag)) - in - let*? plugin = - Node_context.get_plugin_for_level ctxt ~level:attested_level - |> Errors.other_result - in + let slot_id : Types.slot_id = {slot_level; slot_index} in let* slot_header_opt = - Slot_manager.try_get_slot_header_from_indexed_skip_list - plugin - ctxt - slot_id + Slot_manager.try_get_slot_header_from_indexed_skip_list ctxt slot_id |> Errors.other_lwt_result in match slot_header_opt with diff --git a/src/lib_dal_node/dal_store_sqlite3.ml b/src/lib_dal_node/dal_store_sqlite3.ml index 747791abb16f32ea71601ea87eb79a91da39e19e..b15ff5f84708a9a32b82d4db3a3625bfb9fc9787 100644 --- a/src/lib_dal_node/dal_store_sqlite3.ml +++ b/src/lib_dal_node/dal_store_sqlite3.ml @@ -175,18 +175,18 @@ module Skip_list_cells = struct WHERE hash = $1 |sql} + (* Returns the skip list cell together with its attestation_lag *) let find_by_slot_id_opt : - (level * slot_index, Skip_list_cell.t, [`One | `Zero]) t = + (level * slot_index, Skip_list_cell.t * int, [`One | `Zero]) t = let open Caqti_type.Std in - (t2 published_level dal_slot_index ->? skip_list_cell) + (t2 published_level dal_slot_index ->? t2 skip_list_cell attestation_lag) @@ {sql| - SELECT cell - FROM skip_list_cells - WHERE hash = ( - SELECT skip_list_cell_hash - FROM skip_list_slots - WHERE published_level = $1 AND slot_index = $2 - )|sql} + SELECT c.cell, s.attestation_lag + FROM skip_list_cells AS c + JOIN skip_list_slots AS s + ON s.skip_list_cell_hash = c.hash + WHERE s.published_level = $1 AND s.slot_index = $2 + |sql} let find_by_level : ( level, diff --git a/src/lib_dal_node/dal_store_sqlite3.mli b/src/lib_dal_node/dal_store_sqlite3.mli index 2a6d9693fa5b0374b3c97d96f80379630f6f4f37..c910e19156af8b83f1405a74db6581fb041b252e 100644 --- a/src/lib_dal_node/dal_store_sqlite3.mli +++ b/src/lib_dal_node/dal_store_sqlite3.mli @@ -49,10 +49,13 @@ module Skip_list_cells : sig Skip_list_cell.t option tzresult Lwt.t (** [find_by_slot_id_opt ?conn store ~slot_id] returns the cell associated to - ([slot_id]) in the [store], if any. Uses the [conn] if provided (defaults - to [None]). *) + ([slot_id]) in the [store] alongside the attestation lag, if any. Uses the + [conn] if provided (defaults to [None]). *) val find_by_slot_id_opt : - ?conn:conn -> t -> Types.slot_id -> Skip_list_cell.t option tzresult Lwt.t + ?conn:conn -> + t -> + Types.slot_id -> + (Skip_list_cell.t * int) option tzresult Lwt.t (** [find_by_level ?conn store ~published_level] retrieves the tuples (cell * hash * slot_index) for the given published level, if any. The results are diff --git a/src/lib_dal_node/slot_manager.ml b/src/lib_dal_node/slot_manager.ml index 7e024be922dcc1d84fe1487d331d2716ed64c1f6..2295d133c65074d28104b3492ebabd534d27f1ad 100644 --- a/src/lib_dal_node/slot_manager.ml +++ b/src/lib_dal_node/slot_manager.ml @@ -361,8 +361,7 @@ let try_get_commitment_of_slot_id_from_memory ctxt slot_id = - Return the extracted slot header, if available. Returns [None] if the cell is not found in the store. *) -let try_get_slot_header_from_indexed_skip_list (module Plugin : Dal_plugin.T) - ctxt slot_id = +let try_get_slot_header_from_indexed_skip_list ctxt slot_id = let open Lwt_result_syntax in let* cell_bytes_opt = Store.Skip_list_cells.find_by_slot_id_opt @@ -371,7 +370,11 @@ let try_get_slot_header_from_indexed_skip_list (module Plugin : Dal_plugin.T) in match cell_bytes_opt with | None -> return_none - | Some cell_bytes -> + | Some (cell_bytes, attestation_lag) -> + let*? (module Plugin : Dal_plugin.T) = + let level = Int32.(add slot_id.slot_level (of_int attestation_lag)) in + Node_context.get_plugin_for_level ctxt ~level + in Dal_proto_types.Skip_list_cell.to_proto Plugin.Skip_list.cell_encoding cell_bytes @@ -429,11 +432,11 @@ let _try_get_slot_header_from_L1_skip_list (module Plugin : Dal_plugin.T) ctxt Returns [Some commitment] if found and matches the [slot_id], [None] otherwise. Performs assertions to ensure the returned slot header matches the requested [slot_id]. *) -let try_get_commitment_of_slot_id_from_skip_list dal_plugin ctxt slot_id = +let try_get_commitment_of_slot_id_from_skip_list ctxt slot_id = let open Lwt_result_syntax in let*! published_slot_header_opt = let*! from_sqlite = - try_get_slot_header_from_indexed_skip_list dal_plugin ctxt slot_id + try_get_slot_header_from_indexed_skip_list ctxt slot_id in match from_sqlite with | Ok (Some _header as res) -> return res @@ -471,28 +474,7 @@ let get_commitment_from_slot_id ctxt slot_id = match try_get_commitment_of_slot_id_from_memory ctxt slot_id with | Some res -> return res | None -> ( - let published_level = slot_id.Types.Slot_id.slot_level in - let*? parameters = - Node_context.get_proto_parameters ctxt ~level:(`Level published_level) - in - let attested_level = - Int32.add - published_level - (Int32.of_int parameters.Types.attestation_lag) - in - (* Note: the precise [attested_level] is not important; however, it is - important that we use the right plugin to decode cells in - [try_get_slot_header_from_indexed_skip_list]. Even if the - [attestation_lag] value decreases in protocol P2 wrt P1, we are sure to - get the right plugin, because [attested_level] is computed with the - (bigger) value from P1, so we are sure [attested_level] is in P2 when - needed. *) - let*? dal_plugin = - Node_context.get_plugin_for_level ctxt ~level:attested_level - in - let*! res = - try_get_commitment_of_slot_id_from_skip_list dal_plugin ctxt slot_id - in + let*! res = try_get_commitment_of_slot_id_from_skip_list ctxt slot_id in match res with | Ok (Some res) -> return res | Ok None -> @@ -783,31 +765,17 @@ module Statuses = struct in match slot_cell with | None -> return_none - | Some cell -> ( - let*? proto_parameters = - Node_context.get_proto_parameters - ctxt - ~level:(`Level slot_id.slot_level) - in - let attested_level = - Int32.( - add slot_id.slot_level (of_int proto_parameters.attestation_lag)) - in + | Some (cell, attestation_lag) -> let*? (module Plugin : Dal_plugin.T) = - Node_context.get_plugin_for_level ctxt ~level:attested_level - in - let cell = - Dal_proto_types.Skip_list_cell.to_proto - Plugin.Skip_list.cell_encoding - cell + let level = Int32.(add slot_id.slot_level (of_int attestation_lag)) in + Node_context.get_plugin_for_level ctxt ~level in - match Plugin.Skip_list.proto_attestation_status cell with - | None -> - (* Old protocols that do not expose the information *) - return_none - | Some `Unpublished -> return_none - | Some `Attested -> return_some `Attested - | Some `Unattested -> return_some `Unattested) + Dal_proto_types.Skip_list_cell.to_proto + Plugin.Skip_list.cell_encoding + cell + |> Plugin.Skip_list.proto_attestation_status + |> Option.map (fun s -> (s :> Types.header_status)) + |> return let find_status ctxt (slot_id : Types.slot_id) = let open Lwt_result_syntax in diff --git a/src/lib_dal_node/slot_manager.mli b/src/lib_dal_node/slot_manager.mli index 64a0b032b68dec9135880e680896f9b5501629bd..a6f91df6f41ee5f5beeb710d33039ae2bede3417 100644 --- a/src/lib_dal_node/slot_manager.mli +++ b/src/lib_dal_node/slot_manager.mli @@ -115,7 +115,6 @@ val get_slot_content : Returns [None] if the cell is not found in the store. *) val try_get_slot_header_from_indexed_skip_list : - (module Dal_plugin.T) -> Node_context.t -> Types.slot_id -> Dal_plugin.slot_header option tzresult Lwt.t diff --git a/src/lib_dal_node/store.ml b/src/lib_dal_node/store.ml index 453d8dd19a335a02fa44cbdbfe5d0e842cd0bdbc..9f291013fa61eb66c571e54fbb69c6641ae1c392 100644 --- a/src/lib_dal_node/store.ml +++ b/src/lib_dal_node/store.ml @@ -991,9 +991,11 @@ let add_slot_headers ~number_of_slots ~block_level slot_headers t = in (* This invariant should hold. *) assert (Int32.equal published_level block_level) ; - let index = Types.Slot_id.{slot_level = published_level; slot_index} in + let slot_id = + Types.Slot_id.{slot_level = published_level; slot_index} + in let () = - Statuses_cache.add_status statuses_cache `Waiting_attestation index + Statuses_cache.add_status statuses_cache `Waiting_attestation slot_id in Slot_id_cache.add ~number_of_slots t.finalized_commitments slot_header ; return (SI.add slot_index waiting)) @@ -1002,6 +1004,12 @@ let add_slot_headers ~number_of_slots ~block_level slot_headers t = in List.iter (fun i -> - Dal_metrics.slot_waiting_for_attestation ~set:(SI.mem i waiting) i) + let i_is_waiting_for_attestation = SI.mem i waiting in + Dal_metrics.slot_waiting_for_attestation + ~set:i_is_waiting_for_attestation + i ; + let slot_id = Types.Slot_id.{slot_level = block_level; slot_index = i} in + if not i_is_waiting_for_attestation then + Statuses_cache.add_status statuses_cache `Unpublished slot_id) (0 -- (number_of_slots - 1)) ; return_unit diff --git a/src/lib_dal_node/store.mli b/src/lib_dal_node/store.mli index 69c9c4c81a1d4993faa8d9820ae654b8ea0426fa..3ac6b9c9119a6ae0fe7eb68e9ea066a2cf392ec2 100644 --- a/src/lib_dal_node/store.mli +++ b/src/lib_dal_node/store.mli @@ -247,13 +247,14 @@ module Skip_list_cells : sig Skip_list_hash.t -> Skip_list_cell.t option tzresult Lwt.t - (** [find_by_slot_id_opt ?conn store slot_id] returns the cell associated to - ([slot_id.slot_level], [slot_id.slot_index]) in the [store], if any. *) + (** [find_by_slot_id_opt ?conn store slot_id] returns the cell and the + attestation lag associated to ([slot_id.slot_level], [slot_id.slot_index]) + in the [store], if any. *) val find_by_slot_id_opt : ?conn:Sqlite.conn -> t -> Types.slot_id -> - Dal_proto_types.Skip_list_cell.t option tzresult Lwt.t + (Dal_proto_types.Skip_list_cell.t * int) option tzresult Lwt.t (** See {!Dal_store_sqlite3.Skip_list_cells.find_by_level}. *) val find_by_level : diff --git a/src/lib_dal_node_services/types.ml b/src/lib_dal_node_services/types.ml index 6941c2ae75bc2dba097e10d9204e1ee0fddcdd78..7a7b00e5e41dad13695ab24fdfeb766dfb8e52ab 100644 --- a/src/lib_dal_node_services/types.ml +++ b/src/lib_dal_node_services/types.ml @@ -347,7 +347,8 @@ type slot_set = {slots : bool list; published_level : int32} type attestable_slots = Attestable_slots of slot_set | Not_in_committee -type header_status = [`Waiting_attestation | `Attested | `Unattested] +type header_status = + [`Waiting_attestation | `Attested | `Unattested | `Unpublished] type shard_index = int @@ -448,12 +449,19 @@ let header_status_encoding : header_status Data_encoding.t = (constant "unattested") (function `Unattested -> Some () | _ -> None) (function () -> `Unattested); + case + ~title:"unpublished" + (Tag 3) + (constant "unpublished") + (function `Unpublished -> Some () | _ -> None) + (function () -> `Unpublished); ] let pp_header_status fmt = function | `Waiting_attestation -> Format.fprintf fmt "waiting_attestation" | `Attested -> Format.fprintf fmt "attested" | `Unattested -> Format.fprintf fmt "unattested" + | `Unpublished -> Format.fprintf fmt "unpublished" let slot_header_encoding = let open Data_encoding in diff --git a/src/lib_dal_node_services/types.mli b/src/lib_dal_node_services/types.mli index 2518b735c819906737072b1fb2fc144f9b6f75b8..cf89aec1294f52f6f5c78b09f6d8f5accd8d1a16 100644 --- a/src/lib_dal_node_services/types.mli +++ b/src/lib_dal_node_services/types.mli @@ -242,7 +242,7 @@ type header_status = | `Attested (** The slot header was included in an L1 block and attested. *) | `Unattested (** The slot header was included in an L1 block but not timely attested. *) - ] + | `Unpublished (** The slot header was not included in any L1 block. *) ] (** A DAL node can be in one of two profiles (aka modes): bootstrap or controller. A controller node can have one or more (sub)profiles that