From 89a6c93fb8986dec11b676a82e2852392509d702 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Thu, 20 Nov 2025 10:28:48 +0100 Subject: [PATCH 1/4] DAL/Node: do not emit attestation warnings for published level 1 --- 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 54337a2dc066..5d4e28fee114 100644 --- a/src/lib_dal_node/RPC_server.ml +++ b/src/lib_dal_node/RPC_server.ml @@ -492,7 +492,7 @@ module Profile_handlers = struct attested_level (of_int last_known_parameters.Types.attestation_lag)) in - if published_level < 1l then + if published_level <= 1l then let slots = Stdlib.List.init last_known_parameters.number_of_slots (fun _ -> false) -- GitLab From 69f2fb7dc880ee3b5cefa233fb01329d8df151e1 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Thu, 20 Nov 2025 10:49:15 +0100 Subject: [PATCH 2/4] DAL/Node: backfill status info at startup --- src/lib_dal_node/daemon.ml | 48 ++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/lib_dal_node/daemon.ml b/src/lib_dal_node/daemon.ml index 40795aae62c6..ef19256ffc08 100644 --- a/src/lib_dal_node/daemon.ml +++ b/src/lib_dal_node/daemon.ml @@ -435,6 +435,30 @@ let update_and_register_profiles ctxt = let*! () = Node_context.set_profile_ctxt ctxt profile_ctxt in return_unit +(* This back-fills the store with slot headers at levels from [from_level] to + [from_level - attestation_lag]. *) +let backfill_slot_statuses cctxt store (module Plugin : Dal_plugin.T) + proto_parameters ~from_level = + let open Lwt_result_syntax in + let number_of_slots = proto_parameters.Types.number_of_slots in + List.iter_es + (fun i -> + let block_level = Int32.(sub from_level (of_int i)) in + if block_level > 1l then + let* slot_headers = + Plugin.get_published_slot_headers ~block_level cctxt + in + let*! () = + Slot_manager.store_slot_headers + ~number_of_slots + ~block_level + slot_headers + store + in + return_unit + else return_unit) + (Stdlib.List.init proto_parameters.attestation_lag Fun.id) + let run ?(disable_shard_validation = false) ~ignore_pkhs ~data_dir ~config_file ~configuration_override () = let open Lwt_result_syntax in @@ -517,7 +541,7 @@ let run ?(disable_shard_validation = false) ~ignore_pkhs ~data_dir ~config_file (* Get the current L1 head and its DAL plugin and parameters. *) let* header, proto_plugins = L1_helpers.wait_for_block_with_plugin cctxt in let head_level = header.Block_header.shell.level in - let*? _plugin, proto_parameters = + let*? (module Plugin), proto_parameters = Proto_plugins.get_plugin_and_parameters_for_level proto_plugins ~level:head_level @@ -773,13 +797,13 @@ let run ?(disable_shard_validation = false) ~ignore_pkhs ~data_dir ~config_file ~head_level proto_parameters in + (* We reload the last processed level because [clean_up_store] has likely + modified it. *) + let* last_notified_level = + let last_processed_level_store = Store.last_processed_level store in + Store.Last_processed_level.load last_processed_level_store + in let* crawler = - (* We reload the last processed level because [clean_up_store] has likely - modified it. *) - let* last_notified_level = - let last_processed_level_store = Store.last_processed_level store in - Store.Last_processed_level.load last_processed_level_store - in let open Constants in let*! crawler = Crawler.start @@ -792,6 +816,16 @@ let run ?(disable_shard_validation = false) ~ignore_pkhs ~data_dir ~config_file in return crawler in + let* () = + let from_level = Int32.pred head_level in + (backfill_slot_statuses + cctxt + store + (module Plugin) + proto_parameters + ~from_level + [@profiler.record_s {verbosity = Notice} "backfill_slot_statuses"]) + in (* Activate the p2p instance. *) let shards_store = Store.shards store in let gs = -- GitLab From 0b62e42c4d3d15cfbea0c55a8b918c04050113a1 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Thu, 20 Nov 2025 10:50:27 +0100 Subject: [PATCH 3/4] DAL/Tests: use check_slot_status consistently in tests --- tezt/tests/dal.ml | 131 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index cec6835e6087..bb90d16a4f46 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -1949,23 +1949,27 @@ let check_stored_level_headers ~__LOC__ dal_node ~pub_level ~number_of_slots ~error_msg:"Unexpected slot headers length (got = %L, expected = %R)" ; unit -let check_slot_status ~__LOC__ ?expected_status dal_node ~slot_level slots_info +let check_slot_status ~__LOC__ ?expected_status dal_node ~slot_level ~slot_index = + match expected_status with + | None -> unit + | Some expected_status -> + let* status = + Dal_RPC.(call dal_node @@ get_level_slot_status ~slot_level ~slot_index) + in + let prefix = + sf "Unexpected slot status at level %d, index %d " slot_level slot_index + in + Check.(status = expected_status) + ~__LOC__ + Dal.Check.slot_id_status_typ + ~error_msg:(prefix ^ "(got = %L, expected = %R)") ; + unit + +let check_slots_statuses ~__LOC__ ?expected_status dal_node ~slot_level + slots_info = let test (slot_index, _commitment) = - match expected_status with - | None -> unit - | Some expected_status -> - let* status = - Dal_RPC.( - call dal_node @@ get_level_slot_status ~slot_level ~slot_index) - in - Check.(status = expected_status) - ~__LOC__ - Dal.Check.slot_id_status_typ - ~error_msg: - "The value of the fetched status should match the expected one \ - (current = %L, expected = %R)" ; - unit + check_slot_status ~__LOC__ ?expected_status dal_node ~slot_level ~slot_index in Lwt_list.iter_s test slots_info @@ -2011,7 +2015,7 @@ let test_dal_node_slots_headers_tracking protocol parameters _cryptobox node Log.info "Just after injecting slots and before baking, there are no headers" ; (* because headers are stored based on information from finalized blocks *) let* () = - check_slot_status + check_slots_statuses dal_node ~slot_level:level ~__LOC__ @@ -2030,7 +2034,7 @@ let test_dal_node_slots_headers_tracking protocol parameters _cryptobox node "After baking one block, there is still no header, because the block is \ not final" ; let* () = - check_slot_status + check_slots_statuses dal_node ~slot_level:level ~__LOC__ @@ -2051,8 +2055,8 @@ let test_dal_node_slots_headers_tracking protocol parameters _cryptobox node period is not over, so they are not yet present in the skip-list. *) check_stored_level_headers ~__LOC__ ~pub_level ~number_of_headers:0 in - let check_slot_status ?expected_status l = - check_slot_status ?expected_status dal_node ~slot_level:pub_level l + let check_slots_statuses ?expected_status l = + check_slots_statuses ?expected_status dal_node ~slot_level:pub_level l in let check_get_commitment = check_get_commitment dal_node ~slot_level:pub_level @@ -2061,10 +2065,13 @@ let test_dal_node_slots_headers_tracking protocol parameters _cryptobox node (* Slots waiting for attestation. *) let* () = check_get_commitment get_commitment_not_found ok in let* () = - check_slot_status ~__LOC__ ~expected_status:Dal_RPC.Waiting_attestation ok + check_slots_statuses + ~__LOC__ + ~expected_status:Dal_RPC.Waiting_attestation + ok in (* slot_2_a is not selected. *) - let* () = check_slot_status ~__LOC__ [slot2_a] in + let* () = check_slots_statuses ~__LOC__ [slot2_a] in (* Slots not published or not included in blocks. *) let* () = check_get_commitment get_commitment_not_found ko in @@ -2098,19 +2105,22 @@ 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 lag) attested + check_slots_statuses + ~__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 (* Slots that were waiting for attestation and now unattested. *) - let* () = check_slot_status ~__LOC__ unattested in + let* () = check_slots_statuses ~__LOC__ unattested in (* slot2_a is still not selected. *) - let* () = check_slot_status ~__LOC__ [slot2_a] in + let* () = check_slots_statuses ~__LOC__ [slot2_a] in (* slot3 never finished in an L1 block, so the DAL node did not store a status for it. *) - let* () = check_slot_status ~__LOC__ [slot3] in + let* () = check_slots_statuses ~__LOC__ [slot3] in (* slot4 is never injected in any of the nodes. So, it's not known by the Dal node. *) - let* () = check_slot_status ~__LOC__ [slot4] in + let* () = check_slots_statuses ~__LOC__ [slot4] in (* The number of stored slots has not changed. *) let* () = check_stored_level_headers @@ -3052,13 +3062,6 @@ let test_attester_with_daemon protocol parameters cryptobox node client dal_node let rec check_attestations level = if level >= max_level then return () else - let* status = - Dal_RPC.( - call dal_node - @@ get_level_slot_status - ~slot_level:level - ~slot_index:(slot_idx level)) - in (* Before [first_not_attested_published_level], it should be [attested], and above (and including) [first_not_attested_published_level], it should be [unattested]. *) @@ -3068,10 +3071,14 @@ let test_attester_with_daemon protocol parameters cryptobox node client dal_node Attested parameters.attestation_lag else Unattested in - Check.( - (expected_status = status) - Dal.Check.slot_id_status_typ - ~error_msg:"Expected status %L (got %R)") ; + let* () = + check_slot_status + ~__LOC__ + dal_node + ~expected_status + ~slot_level:level + ~slot_index:(slot_idx level) + in check_attestations (level + 1) in check_attestations first_level @@ -3171,21 +3178,18 @@ let test_attester_with_bake_for _protocol parameters cryptobox node client let rec check_attestations level = if level > last_checked_level then return () else - let* status = - Dal_RPC.( - call dal_node - @@ get_level_slot_status - ~slot_level:level - ~slot_index:(slot_idx level)) - in let expected_status = let open Dal_RPC in if level <= intermediary_level then Attested lag else Unattested in - Check.( - (expected_status = status) - Dal.Check.slot_id_status_typ - ~error_msg:"Expected status %L (got %R)") ; + let* () = + check_slot_status + ~__LOC__ + dal_node + ~expected_status + ~slot_level:level + ~slot_index:(slot_idx level) + in check_attestations (level + 1) in check_attestations first_level @@ -5776,14 +5780,14 @@ let test_attestation_through_p2p ~batching_time_interval _protocol [attester] in - let* status = - Dal_RPC.( - call attester - @@ get_level_slot_status ~slot_level:publication_level ~slot_index:index) + let* () = + check_slot_status + ~__LOC__ + attester + ~expected_status:(Dal_RPC.Attested attestation_lag) + ~slot_level:publication_level + ~slot_index:index in - 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" ; unit @@ -6101,10 +6105,6 @@ module Skip_list_rpcs = struct statuses are inserted at L1 head level + 1. *) if level >= max_level then unit else - let* status = - Dal_RPC.( - call dal_node @@ get_level_slot_status ~slot_level:level ~slot_index) - in let expected_status = if level <= migration_level - lag then Dal_RPC.Attested lag else if level == migration_level + 1 - lag then Dal_RPC.Unattested @@ -6114,13 +6114,14 @@ module Skip_list_rpcs = struct Dal_RPC.Attested lag else Dal_RPC.Unpublished in - Check.( - (status = expected_status) - Dal_common.Check.slot_id_status_typ + let* () = + check_slot_status ~__LOC__ - ~error_msg: - (let msg = sf "Unexpected status at level %d: " level in - msg ^ "got %L, expected %R")) ; + dal_node + ~expected_status + ~slot_level:level + ~slot_index + in call_get_status (level + 1) in Log.info "Check fetching statuses from the skip-list store" ; -- GitLab From bdb76292741b1fdfc45dc756b6371361de16c555 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Thu, 20 Nov 2025 10:50:55 +0100 Subject: [PATCH 4/4] DAL/Tests: add test for backfilling slot statuses at startup --- tezt/tests/dal.ml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index bb90d16a4f46..c4491e70f804 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -12068,6 +12068,39 @@ let test_denunciation_when_all_bakers_attest protocol dal_parameters _cryptobox in unit +let test_statuses_backfill_at_restart _protocol dal_parameters _cryptobox + _l1_node client dal_node = + let index = 0 in + let slot_size = dal_parameters.Dal.Parameters.cryptobox.slot_size in + let* _ = + Helpers.publish_and_store_slot client dal_node Constant.bootstrap1 ~index + @@ Helpers.make_slot ~slot_size "Hello world!" + in + let* lvl_inject = Client.level client in + let slot_level = lvl_inject + 1 in + Log.info "Bake a few blocks" ; + let* () = bake_for ~count:3 client in + let* () = Dal_node.terminate dal_node in + let* () = bake_for ~count:3 client in + let* () = Dal_node.run dal_node in + let* () = + check_slot_status + ~__LOC__ + dal_node + ~expected_status:Waiting_attestation + ~slot_level + ~slot_index:index + in + let* () = + check_slot_status + ~__LOC__ + dal_node + ~expected_status:Unpublished + ~slot_level + ~slot_index:(index + 1) + in + unit + let register ~protocols = (* Tests with Layer1 node only *) scenario_with_layer1_node @@ -12447,6 +12480,12 @@ let register ~protocols = "Trap is denounced when all bakers attest" test_denunciation_when_all_bakers_attest protocols ; + scenario_with_layer1_and_dal_nodes + ~tags:["restart"; "statuses"] + "Status information is backfilled at restart" + ~operator_profiles:[0] + test_statuses_backfill_at_restart + protocols ; (* Tests with all nodes *) scenario_with_all_nodes -- GitLab