From d018ed859f48f2060bb916dd2bd75f2e172f451e Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Mon, 20 Oct 2025 18:07:25 +0200 Subject: [PATCH 1/4] DAL: get_slot_status returns the lag alongside attestation status --- src/lib_dal_node/dal_plugin.ml | 2 +- src/lib_dal_node/dal_plugin.mli | 2 +- src/lib_dal_node/store.ml | 13 +++++++---- src/lib_dal_node_services/types.ml | 23 +++++++++++++------ src/lib_dal_node_services/types.mli | 3 ++- .../lib_dal/dal_plugin_registration.ml | 17 ++++++++------ .../lib_dal/dal_plugin_registration.ml | 17 ++++++++------ .../lib_dal/dal_plugin_registration.ml | 17 ++++++++------ .../lib_dal/dal_plugin_registration.ml | 5 ++-- 9 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index d379adfaa9e1..c124d70f1402 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -132,7 +132,7 @@ module type T = sig val slot_header_of_cell : cell -> slot_header option val proto_attestation_status : - cell -> [`Attested | `Unattested | `Unpublished] option + cell -> [`Attested of attestation_lag | `Unattested | `Unpublished] option end module RPC : sig diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index d09a92f3b7a9..8b0dc0d9799a 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -191,7 +191,7 @@ module type T = sig The function returns [None] for older protocols that do not expose the necessary information to access the status. *) val proto_attestation_status : - cell -> [`Attested | `Unattested | `Unpublished] option + cell -> [`Attested of attestation_lag | `Unattested | `Unpublished] option end module RPC : sig diff --git a/src/lib_dal_node/store.ml b/src/lib_dal_node/store.ml index 9f291013fa61..4003617a8d1f 100644 --- a/src/lib_dal_node/store.ml +++ b/src/lib_dal_node/store.ml @@ -671,14 +671,14 @@ module Statuses_cache = struct let get_slot_status = Slot_map.find_opt - let update_slot_headers_attestation ~published_level ~number_of_slots t - attested = + let update_slot_headers_attestation ~published_level ~number_of_slots + ~attestation_lag t attested = List.iter (fun slot_index -> let index = Types.Slot_id.{slot_level = published_level; slot_index} in if attested slot_index then ( Dal_metrics.slot_attested ~set:true slot_index ; - add_status t `Attested index) + add_status t (`Attested attestation_lag) index) else let old_data_opt = get_slot_status t index in Dal_metrics.slot_attested ~set:false slot_index ; @@ -693,7 +693,12 @@ module Statuses_cache = struct let update_selected_slot_headers_statuses ~block_level ~attestation_lag ~number_of_slots attested t = let published_level = Int32.(sub block_level (of_int attestation_lag)) in - update_slot_headers_attestation ~published_level ~number_of_slots t attested + update_slot_headers_attestation + ~published_level + ~number_of_slots + ~attestation_lag + t + attested end module Commitment_indexed_cache = diff --git a/src/lib_dal_node_services/types.ml b/src/lib_dal_node_services/types.ml index 7a7b00e5e41d..4c545d0a7929 100644 --- a/src/lib_dal_node_services/types.ml +++ b/src/lib_dal_node_services/types.ml @@ -348,7 +348,10 @@ 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 | `Unpublished] + [ `Waiting_attestation + | `Attested of attestation_lag + | `Unattested + | `Unpublished ] type shard_index = int @@ -424,9 +427,8 @@ let attestable_slots_encoding : attestable_slots Data_encoding.t = (function () -> Not_in_committee); ] -(* Note: this encoding is used to store statuses on disk using the - [Key_value_store] module. As such, it's important that this is a - fixed-size encoding. *) +let legacy_attestation_lag = 8 + let header_status_encoding : header_status Data_encoding.t = let open Data_encoding in union @@ -441,8 +443,8 @@ let header_status_encoding : header_status Data_encoding.t = ~title:"attested" (Tag 1) (constant "attested") - (function `Attested -> Some () | _ -> None) - (function () -> `Attested); + (function _ -> None (* Don't encode with this case anymore *)) + (function () -> `Attested legacy_attestation_lag); case ~title:"unattested" (Tag 2) @@ -455,11 +457,18 @@ let header_status_encoding : header_status Data_encoding.t = (constant "unpublished") (function `Unpublished -> Some () | _ -> None) (function () -> `Unpublished); + case + ~title:"attested_with_lag" + (Tag 4) + (obj2 (req "kind" (constant "attested")) (req "attestation_lag" uint8)) + (function + | `Attested attestation_lag -> Some ((), attestation_lag) | _ -> None) + (function (), attestation_lag -> `Attested attestation_lag); ] let pp_header_status fmt = function | `Waiting_attestation -> Format.fprintf fmt "waiting_attestation" - | `Attested -> Format.fprintf fmt "attested" + | `Attested lag -> Format.fprintf fmt "attested(lag:%d)" lag | `Unattested -> Format.fprintf fmt "unattested" | `Unpublished -> Format.fprintf fmt "unpublished" diff --git a/src/lib_dal_node_services/types.mli b/src/lib_dal_node_services/types.mli index cf89aec1294f..4262575836a3 100644 --- a/src/lib_dal_node_services/types.mli +++ b/src/lib_dal_node_services/types.mli @@ -239,7 +239,8 @@ type header_status = [ `Waiting_attestation (** The slot header was included and applied in a finalized L1 block but remains to be attested. *) - | `Attested (** The slot header was included in an L1 block and attested. *) + | `Attested of attestation_lag + (** 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. *) ] diff --git a/src/proto_022_PsRiotum/lib_dal/dal_plugin_registration.ml b/src/proto_022_PsRiotum/lib_dal/dal_plugin_registration.ml index 76fa0002ee46..d8591e76193e 100644 --- a/src/proto_022_PsRiotum/lib_dal/dal_plugin_registration.ml +++ b/src/proto_022_PsRiotum/lib_dal/dal_plugin_registration.ml @@ -351,13 +351,16 @@ module Plugin = struct commitment; } - let proto_attestation_status cell = - Option.some - @@ - match Dal.Slots_history.(content cell) with - | Dal.Slots_history.Unpublished _ -> `Unpublished - | Published {is_proto_attested; _} -> - if is_proto_attested then `Attested else `Unattested + let proto_attestation_status = + let legacy_attestation_lag = 8 in + fun cell -> + Option.some + @@ + match Dal.Slots_history.(content cell) with + | Dal.Slots_history.Unpublished _ -> `Unpublished + | Published {is_proto_attested; _} -> + if is_proto_attested then `Attested legacy_attestation_lag + else `Unattested end module RPC = struct diff --git a/src/proto_023_PtSeouLo/lib_dal/dal_plugin_registration.ml b/src/proto_023_PtSeouLo/lib_dal/dal_plugin_registration.ml index e8c651481ed7..08de5ea63589 100644 --- a/src/proto_023_PtSeouLo/lib_dal/dal_plugin_registration.ml +++ b/src/proto_023_PtSeouLo/lib_dal/dal_plugin_registration.ml @@ -373,13 +373,16 @@ module Plugin = struct commitment; } - let proto_attestation_status cell = - Option.some - @@ - match Dal.Slots_history.(content cell) with - | Dal.Slots_history.Unpublished _ -> `Unpublished - | Published {is_proto_attested; _} -> - if is_proto_attested then `Attested else `Unattested + let proto_attestation_status = + let legacy_attestation_lag = 8 in + fun cell -> + Option.some + @@ + match Dal.Slots_history.(content cell) with + | Dal.Slots_history.Unpublished _ -> `Unpublished + | Published {is_proto_attested; _} -> + if is_proto_attested then `Attested legacy_attestation_lag + else `Unattested end module RPC = struct diff --git a/src/proto_024_PsD5wVTJ/lib_dal/dal_plugin_registration.ml b/src/proto_024_PsD5wVTJ/lib_dal/dal_plugin_registration.ml index 6c18cd0a4fa4..9105382db013 100644 --- a/src/proto_024_PsD5wVTJ/lib_dal/dal_plugin_registration.ml +++ b/src/proto_024_PsD5wVTJ/lib_dal/dal_plugin_registration.ml @@ -373,13 +373,16 @@ module Plugin = struct commitment; } - let proto_attestation_status cell = - Option.some - @@ - match Dal.Slots_history.(content cell) with - | Dal.Slots_history.Unpublished _ -> `Unpublished - | Published {is_proto_attested; _} -> - if is_proto_attested then `Attested else `Unattested + let proto_attestation_status = + let legacy_attestation_lag = 8 in + fun cell -> + Option.some + @@ + match Dal.Slots_history.(content cell) with + | Dal.Slots_history.Unpublished _ -> `Unpublished + | Published {is_proto_attested; _} -> + if is_proto_attested then `Attested legacy_attestation_lag + else `Unattested end module RPC = struct diff --git a/src/proto_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index daee90d9f388..9faa6b7e336d 100644 --- a/src/proto_alpha/lib_dal/dal_plugin_registration.ml +++ b/src/proto_alpha/lib_dal/dal_plugin_registration.ml @@ -427,8 +427,9 @@ module Plugin = struct @@ match Dal.Slots_history.(content cell) with | Dal.Slots_history.Unpublished _ -> `Unpublished - | Published {is_proto_attested; _} -> - if is_proto_attested then `Attested else `Unattested + | Published {is_proto_attested; attestation_lag; _} -> + let lag = Dal.Slots_history.attestation_lag_value attestation_lag in + if is_proto_attested then `Attested lag else `Unattested end module RPC = struct -- GitLab From 861bf36233746ecafa3c629f3715019f30b64c5b Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Tue, 21 Oct 2025 16:38:57 +0200 Subject: [PATCH 2/4] DAL/Tezt: adapt tests to support a lag value in Attested status --- src/bin_testnet_scenarios/dal.ml | 2 +- tezt/lib_tezos/dal_common.ml | 29 ++++++++++++++++++++--------- tezt/lib_tezos/dal_common.mli | 2 +- tezt/tests/dal.ml | 17 +++++++++-------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/bin_testnet_scenarios/dal.ml b/src/bin_testnet_scenarios/dal.ml index 29c099ae03e6..c4394889f212 100644 --- a/src/bin_testnet_scenarios/dal.ml +++ b/src/bin_testnet_scenarios/dal.ml @@ -162,7 +162,7 @@ let check_attestations node dal_node ~lag ~number_of_slots ~published_level = else x in let num_attested, indexes = - match Map.find_opt Dal_RPC.Attested map with + match Map.find_opt (Dal_RPC.Attested lag) map with | None -> (0, []) | Some (c, l) -> (c, l) in diff --git a/tezt/lib_tezos/dal_common.ml b/tezt/lib_tezos/dal_common.ml index 2b4dc41aacba..766468c2255f 100644 --- a/tezt/lib_tezos/dal_common.ml +++ b/tezt/lib_tezos/dal_common.ml @@ -196,7 +196,7 @@ module Dal_RPC = struct type slot_id_status = | Waiting_attestation - | Attested + | Attested of int (* of attestation lag *) | Unattested | Unpublished @@ -209,17 +209,28 @@ module Dal_RPC = struct let pp_slot_id_status fmt = function | Waiting_attestation -> Format.fprintf fmt "Waiting_attestation" - | Attested -> Format.fprintf fmt "Attested" + | Attested lag -> Format.fprintf fmt "Attested(lag:%d)" lag | Unattested -> Format.fprintf fmt "Unattested" | Unpublished -> Format.fprintf fmt "Unpublished" - let slot_id_status_of_json json = - match String.lowercase_ascii @@ JSON.as_string json with - | "waiting_attestation" -> Waiting_attestation - | "attested" -> Attested - | "unattested" -> Unattested - | "unpublished" -> Unattested - | s -> failwith @@ Format.sprintf "Unknown slot_id status %s" s + let slot_id_status_of_json = + let legacy_attestation_lag = 8 in + fun json -> + match String.lowercase_ascii @@ JSON.as_string json with + | "waiting_attestation" -> Waiting_attestation + | "attested" -> Attested legacy_attestation_lag + | "unattested" -> Unattested + | "unpublished" -> Unattested + | exception _exn -> ( + match + JSON. + ( json |-> "kind" |> as_string, + json |-> "attestation_lag" |> as_int ) + with + | "attested", lag -> Attested lag + | (exception _) | _ -> + failwith @@ Format.sprintf "Unknown slot_id status") + | s -> failwith @@ Format.sprintf "Unknown slot_id status %s" s let slot_header_of_json json = let open JSON in diff --git a/tezt/lib_tezos/dal_common.mli b/tezt/lib_tezos/dal_common.mli index e0b0a99ba1fa..57d0f5b77c09 100644 --- a/tezt/lib_tezos/dal_common.mli +++ b/tezt/lib_tezos/dal_common.mli @@ -202,7 +202,7 @@ module RPC : sig (* The status of a slot id (published level + slot index) on L1. *) type slot_id_status = | Waiting_attestation - | Attested + | Attested of int (* of attestation lag *) | Unattested | Unpublished diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index 007e555a5222..60556582bf8a 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -2085,7 +2085,7 @@ let test_dal_node_slots_headers_tracking protocol parameters _cryptobox node let* () = check_get_commitment get_commitment_succeeds attested in (* Slots that were waiting for attestation and now attested. *) let* () = - check_slot_status ~__LOC__ ~expected_status:Dal_RPC.Attested attested + check_slot_status ~__LOC__ ~expected_status:(Dal_RPC.Attested lag) attested in (* Slots not published or not included in blocks. *) let* () = check_get_commitment get_commitment_not_found ko in @@ -3045,7 +3045,8 @@ let test_attester_with_daemon protocol parameters cryptobox node client dal_node should be [unattested]. *) let expected_status = let open Dal_RPC in - if level < first_not_attested_published_level then Attested + if level < first_not_attested_published_level then + Attested parameters.attestation_lag else Unattested in Check.( @@ -3160,7 +3161,7 @@ let test_attester_with_bake_for _protocol parameters cryptobox node client in let expected_status = let open Dal_RPC in - if level <= intermediary_level then Attested else Unattested + if level <= intermediary_level then Attested lag else Unattested in Check.( (expected_status = status) @@ -5747,7 +5748,7 @@ let test_attestation_through_p2p ~batching_time_interval _protocol call attester @@ get_level_slot_status ~slot_level:publication_level ~slot_index:index) in - Check.(status = Dal_RPC.Attested) + Check.(status = Dal_RPC.Attested attestation_lag) Dal.Check.slot_id_status_typ ~error_msg:"Expected status %R (got %L)" ; Log.info "Slot sucessfully attested" ; @@ -7310,7 +7311,7 @@ module Garbage_collection = struct call slot_producer @@ get_level_slot_status ~slot_level:published_level ~slot_index) in - Check.(status = Dal_RPC.Attested) + Check.(status = Dal_RPC.Attested dal_parameters.attestation_lag) ~__LOC__ Dal.Check.slot_id_status_typ ~error_msg: @@ -7577,7 +7578,7 @@ module Garbage_collection = struct call slot_producer @@ get_level_slot_status ~slot_level:published_level ~slot_index) in - Check.(status = Dal_RPC.Attested) + Check.(status = Dal_RPC.Attested dal_parameters.attestation_lag) ~__LOC__ Dal.Check.slot_id_status_typ ~error_msg: @@ -10150,7 +10151,7 @@ let test_producer_attester (protocol : Protocol.t) Log.info "Status is %a" Dal_RPC.pp_slot_id_status status ; log_step "Final check." ; Check.( - (status = Dal_RPC.Attested) + (status = Dal_RPC.Attested lag) Dal.Check.slot_id_status_typ ~error_msg:"Published slot was supposed to be attested.") ; unit @@ -10300,7 +10301,7 @@ let test_attester_did_not_attest (protocol : Protocol.t) Log.info "Status is %a" Dal_RPC.pp_slot_id_status status ; log_step "Final checks." ; Check.( - (status = Dal_RPC.Attested) + (status = Dal_RPC.Attested lag) Dal.Check.slot_id_status_typ ~error_msg:"Published slot was supposed to be attested.") ; (* If the [not_attested_by_bootstrap2_promise] is not fulfilled yet, it means that -- GitLab From b2a45845f4e04d0d06a9c2436114162da9b2af29 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 21 Oct 2025 18:24:38 +0200 Subject: [PATCH 3/4] DAL/Node: do not emit warning for unpublished slots --- src/lib_dal_node/RPC_server.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib_dal_node/RPC_server.ml b/src/lib_dal_node/RPC_server.ml index e4e9e0f8300e..3eea9a16b0aa 100644 --- a/src/lib_dal_node/RPC_server.ml +++ b/src/lib_dal_node/RPC_server.ml @@ -337,6 +337,7 @@ module Profile_handlers = struct match res with | `Waiting_attestation -> Lwt.return_some (`Not_ok (slot_index, num_stored)) + | `Unpublished -> Lwt.return_none | status -> (* Most probably the RPC was called/handled too late. This may mean that that DAL node is lagging. *) -- GitLab From 259c82ffb019caa60ab5f8b9953632f0a9122dec Mon Sep 17 00:00:00 2001 From: "iguerNL@Functori" Date: Tue, 21 Oct 2025 19:01:19 +0200 Subject: [PATCH 4/4] DAL/Node: avoid fragile pattern matching --- src/lib_dal_node/RPC_server.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_dal_node/RPC_server.ml b/src/lib_dal_node/RPC_server.ml index 3eea9a16b0aa..cb6163d0d13f 100644 --- a/src/lib_dal_node/RPC_server.ml +++ b/src/lib_dal_node/RPC_server.ml @@ -338,7 +338,7 @@ module Profile_handlers = struct | `Waiting_attestation -> Lwt.return_some (`Not_ok (slot_index, num_stored)) | `Unpublished -> Lwt.return_none - | status -> + | (`Attested _ | `Unattested) as status -> (* Most probably the RPC was called/handled too late. This may mean that that DAL node is lagging. *) let* () = -- GitLab