From a05663e7844554fb536c4baec8789bbf15ec449f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 21 Oct 2025 15:35:31 +0200 Subject: [PATCH 1/9] Cumulus: skip building a block on relay parents in old session --- .../consensus/aura/src/collators/mod.rs | 50 +++++++++++++++++++ .../slot_based/block_builder_task.rs | 27 ++++++++++ prdoc/pr_9990.prdoc | 12 +++++ 3 files changed, 89 insertions(+) create mode 100644 prdoc/pr_9990.prdoc diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 0310f2944dc58..9ee9cd025c3a4 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -169,6 +169,56 @@ async fn claim_queue_at( } } +/// Check if we should skip building a block because the relay parent would put us +/// in an older session. +/// +/// We fetch the best block and the child session indices for both the best block and +/// the relay parent. If the relay parent's child session index is less than the best +/// block's child session index, we skip building. +async fn should_skip_building_block_due_to_relay_parent_in_old_session( + relay_client: &Client, + relay_best_hash: RelayHash, + relay_parent: RelayHash, +) -> bool +where + Client: RelayChainInterface, +{ + let Some(relay_parent_child_session_index) = + relay_client.session_index_for_child(relay_parent).await.ok() + else { + // Failed to fetch session index for child of relay parent, do not skip building + return false; + }; + + let Some(best_child_session_index) = + relay_client.session_index_for_child(relay_best_hash).await.ok() + else { + // Failed to fetch session index for child of best block, do not skip building + return false; + }; + + let should_skip = if relay_parent_child_session_index < best_child_session_index { + // Relay parent would put us in an older session → skip. + true + } else { + // Equal (current session) or greater (unexpected) → don't skip. + false + }; + + if should_skip { + tracing::trace!( + target: crate::LOG_TARGET, + ?relay_best_hash, + ?best_child_session_index, + ?relay_parent, + ?relay_parent_child_session_index, + "Relay parent would build into an older session; skipping." + ); + } + + should_skip +} + // Checks if we own the slot at the given block and whether there // is space in the unincluded segment. async fn can_build_upon( diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index e6360a3c8408e..d4f7f0e7dc2ad 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -323,6 +323,33 @@ where }, }; + // Checking that we _can_ build is not enough, we also should check if + // we _should_ build, determined by whether there is any point in doing so. + let should_skip = + crate::collators::should_skip_building_block_due_to_relay_parent_in_old_session( + &relay_client, + relay_best_hash, + relay_parent, + ) + .await; + + // Respect the relay-chain rule not to cross session boundaries. + if should_skip { + tracing::debug!( + target: crate::LOG_TARGET, + unincluded_segment_len = parent.depth, + relay_parent = %relay_parent, + relay_parent_num = %relay_parent_header.number(), + relay_parent_offset, + included_hash = %included_header_hash, + included_num = %included_header.number(), + parent = %parent_hash, + slot = ?para_slot.slot, + "Skipping building block on relay parent in old session", + ); + continue + } + tracing::debug!( target: crate::LOG_TARGET, unincluded_segment_len = parent.depth, diff --git a/prdoc/pr_9990.prdoc b/prdoc/pr_9990.prdoc new file mode 100644 index 0000000000000..139921dae1733 --- /dev/null +++ b/prdoc/pr_9990.prdoc @@ -0,0 +1,12 @@ +title: '`cumulus`: Skip building on blocks on relay parents in old session' +doc: +- audience: Node Dev + description: |- + Collators building on older relay parents must skip building blocks when the chosen RP session is different than best block session. + + The relay chain will not accept such candidate. We can see this happening on the Kusama Canary parachain at each session boundary. + + Slot-based Aura has been modified in Cumulus to skip building blocks on relay parents in old session. +crates: +- name: cumulus-client-consensus-aura + bump: patch From df3445e14e46d5e4d46311bd1673415f625c8110 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 21 Oct 2025 16:04:55 +0200 Subject: [PATCH 2/9] Revert "Cumulus: skip building a block on relay parents in old session" This reverts commit a05663e7844554fb536c4baec8789bbf15ec449f. --- .../consensus/aura/src/collators/mod.rs | 50 ------------------- .../slot_based/block_builder_task.rs | 27 ---------- prdoc/pr_9990.prdoc | 12 ----- 3 files changed, 89 deletions(-) delete mode 100644 prdoc/pr_9990.prdoc diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 9ee9cd025c3a4..0310f2944dc58 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -169,56 +169,6 @@ async fn claim_queue_at( } } -/// Check if we should skip building a block because the relay parent would put us -/// in an older session. -/// -/// We fetch the best block and the child session indices for both the best block and -/// the relay parent. If the relay parent's child session index is less than the best -/// block's child session index, we skip building. -async fn should_skip_building_block_due_to_relay_parent_in_old_session( - relay_client: &Client, - relay_best_hash: RelayHash, - relay_parent: RelayHash, -) -> bool -where - Client: RelayChainInterface, -{ - let Some(relay_parent_child_session_index) = - relay_client.session_index_for_child(relay_parent).await.ok() - else { - // Failed to fetch session index for child of relay parent, do not skip building - return false; - }; - - let Some(best_child_session_index) = - relay_client.session_index_for_child(relay_best_hash).await.ok() - else { - // Failed to fetch session index for child of best block, do not skip building - return false; - }; - - let should_skip = if relay_parent_child_session_index < best_child_session_index { - // Relay parent would put us in an older session → skip. - true - } else { - // Equal (current session) or greater (unexpected) → don't skip. - false - }; - - if should_skip { - tracing::trace!( - target: crate::LOG_TARGET, - ?relay_best_hash, - ?best_child_session_index, - ?relay_parent, - ?relay_parent_child_session_index, - "Relay parent would build into an older session; skipping." - ); - } - - should_skip -} - // Checks if we own the slot at the given block and whether there // is space in the unincluded segment. async fn can_build_upon( diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index d4f7f0e7dc2ad..e6360a3c8408e 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -323,33 +323,6 @@ where }, }; - // Checking that we _can_ build is not enough, we also should check if - // we _should_ build, determined by whether there is any point in doing so. - let should_skip = - crate::collators::should_skip_building_block_due_to_relay_parent_in_old_session( - &relay_client, - relay_best_hash, - relay_parent, - ) - .await; - - // Respect the relay-chain rule not to cross session boundaries. - if should_skip { - tracing::debug!( - target: crate::LOG_TARGET, - unincluded_segment_len = parent.depth, - relay_parent = %relay_parent, - relay_parent_num = %relay_parent_header.number(), - relay_parent_offset, - included_hash = %included_header_hash, - included_num = %included_header.number(), - parent = %parent_hash, - slot = ?para_slot.slot, - "Skipping building block on relay parent in old session", - ); - continue - } - tracing::debug!( target: crate::LOG_TARGET, unincluded_segment_len = parent.depth, diff --git a/prdoc/pr_9990.prdoc b/prdoc/pr_9990.prdoc deleted file mode 100644 index 139921dae1733..0000000000000 --- a/prdoc/pr_9990.prdoc +++ /dev/null @@ -1,12 +0,0 @@ -title: '`cumulus`: Skip building on blocks on relay parents in old session' -doc: -- audience: Node Dev - description: |- - Collators building on older relay parents must skip building blocks when the chosen RP session is different than best block session. - - The relay chain will not accept such candidate. We can see this happening on the Kusama Canary parachain at each session boundary. - - Slot-based Aura has been modified in Cumulus to skip building blocks on relay parents in old session. -crates: -- name: cumulus-client-consensus-aura - bump: patch From 9fb3757b11e7d8bf23e57a94a3a1639d1ad8d0bc Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 23 Oct 2025 11:01:13 +0200 Subject: [PATCH 3/9] Cumulus: skip building a block on relay parents in old session by detecting epoch changes in header. --- .../slot_based/block_builder_task.rs | 17 +++- .../aura/src/collators/slot_based/tests.rs | 83 ++++++++++++++++++- cumulus/client/parachain-inherent/src/lib.rs | 10 +-- substrate/client/consensus/babe/src/lib.rs | 5 ++ 4 files changed, 102 insertions(+), 13 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index e6360a3c8408e..1bddb8ac35efa 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -201,7 +201,7 @@ where continue; }; - let Ok(rp_data) = offset_relay_parent_find_descendants( + let Ok(Some(rp_data)) = offset_relay_parent_find_descendants( &mut relay_chain_data_cache, relay_best_hash, relay_parent_offset, @@ -467,7 +467,7 @@ pub(crate) async fn offset_relay_parent_find_descendants( relay_chain_data_cache: &mut RelayChainDataCache, relay_best_block: RelayHash, relay_parent_offset: u32, -) -> Result +) -> Result, ()> where RelayClient: RelayChainInterface + Clone + 'static, { @@ -481,7 +481,12 @@ where }; if relay_parent_offset == 0 { - return Ok(RelayParentData::new(relay_header)); + return Ok(Some(RelayParentData::new(relay_header))); + } + + if sc_consensus_babe::contains_epoch_change::(&relay_header) { + tracing::debug!(target: LOG_TARGET, ?relay_best_block, "Relay header contains epoch change."); + return Ok(None); } let mut required_ancestors: VecDeque = Default::default(); @@ -492,6 +497,10 @@ where .await? .relay_parent_header .clone(); + if sc_consensus_babe::contains_epoch_change::(&next_header) { + tracing::debug!(target: LOG_TARGET, ?relay_best_block, ancestor = %next_header.hash(), "Next header contains epoch change."); + return Ok(None); + } required_ancestors.push_front(next_header.clone()); relay_header = next_header; } @@ -510,7 +519,7 @@ where "Relay parent descendants." ); - Ok(RelayParentData::new_with_descendants(relay_parent, required_ancestors.into())) + Ok(Some(RelayParentData::new_with_descendants(relay_parent, required_ancestors.into()))) } /// Return value of [`determine_core`]. diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs index a26ac2c581e92..dfb19b211b802 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs @@ -23,11 +23,16 @@ use async_trait::async_trait; use cumulus_primitives_core::{ClaimQueueOffset, CoreInfo, CoreSelector, CumulusDigestItem}; use cumulus_relay_chain_interface::*; use futures::Stream; +use codec::Encode; use polkadot_node_subsystem_util::runtime::ClaimQueueSnapshot; use polkadot_primitives::{ CandidateEvent, CommittedCandidateReceiptV2, CoreIndex, Hash as RelayHash, Header as RelayHeader, Id as ParaId, }; +use sc_consensus_babe::{ + AuthorityId, ConsensusLog as BabeConsensusLog, NextEpochDescriptor, BABE_ENGINE_ID, +}; +use sp_core::sr25519; use sp_runtime::{generic::BlockId, testing::Header as TestHeader, traits::Header}; use sp_version::RuntimeVersion; use std::{ @@ -45,7 +50,7 @@ async fn offset_test_zero_offset() { let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 0).await; assert!(result.is_ok()); - let data = result.unwrap(); + let data = result.unwrap().unwrap(); assert_eq!(data.descendants_len(), 0); assert_eq!(data.relay_parent().hash(), best_hash); assert!(data.into_inherent_descendant_list().is_empty()); @@ -61,7 +66,7 @@ async fn offset_test_two_offset() { let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 2).await; assert!(result.is_ok()); - let data = result.unwrap(); + let data = result.unwrap().unwrap(); assert_eq!(data.descendants_len(), 2); assert_eq!(*data.relay_parent().number(), 98); let descendant_list = data.into_inherent_descendant_list(); @@ -80,7 +85,7 @@ async fn offset_test_five_offset() { let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 5).await; assert!(result.is_ok()); - let data = result.unwrap(); + let data = result.unwrap().unwrap(); assert_eq!(data.descendants_len(), 5); assert_eq!(*data.relay_parent().number(), 95); let descendant_list = data.into_inherent_descendant_list(); @@ -104,6 +109,39 @@ async fn offset_test_too_long() { assert!(result.is_err()); } +#[tokio::test] +async fn offset_returns_none_when_best_header_contains_epoch_change() { + let (headers, best_hash) = build_headers_with_epoch_flags(&[false, false, true]); + let client = TestRelayClient::new(headers); + let mut cache = RelayChainDataCache::new(client, 1.into()); + + let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 1).await; + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); +} + +#[tokio::test] +async fn offset_returns_none_when_first_ancestor_contains_epoch_change() { + let (headers, best_hash) = build_headers_with_epoch_flags(&[false, true, false]); + let client = TestRelayClient::new(headers); + let mut cache = RelayChainDataCache::new(client, 1.into()); + + let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 2).await; + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); +} + +#[tokio::test] +async fn offset_returns_none_when_second_ancestor_contains_epoch_change() { + let (headers, best_hash) = build_headers_with_epoch_flags(&[true, false, false]); + let client = TestRelayClient::new(headers); + let mut cache = RelayChainDataCache::new(client, 1.into()); + + let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 3).await; + assert!(result.is_ok()); + assert!(result.unwrap().is_none()); +} + #[tokio::test] async fn determine_core_new_relay_parent() { let (headers, _best_hash) = create_header_chain(); @@ -593,6 +631,45 @@ impl RelayChainInterface for TestRelayClient { } } +/// Build a consecutive set of relay headers whose digest entries optionally carry a BABE +/// epoch-change marker, returning the underlying map and the hash of the last header. +fn build_headers_with_epoch_flags(flags: &[bool]) -> (HashMap, RelayHash) { + let mut headers = HashMap::new(); + let mut parent_hash = RelayHash::default(); + let mut last_hash = RelayHash::default(); + + for (index, has_epoch_change) in flags.iter().enumerate() { + let digest = + if *has_epoch_change { babe_epoch_change_digest() } else { Default::default() }; + + let header = RelayHeader { + parent_hash, + number: (index as u32 + 1), + state_root: Default::default(), + extrinsics_root: Default::default(), + digest, + }; + + let hash = header.hash(); + headers.insert(hash, header); + parent_hash = hash; + last_hash = hash; + } + + (headers, last_hash) +} + +/// Create a digest containing a single BABE `NextEpochData` item for use in tests. +fn babe_epoch_change_digest() -> sp_runtime::generic::Digest { + let mut digest = sp_runtime::generic::Digest::default(); + let authority_id = AuthorityId::from(sr25519::Public::from_raw([1u8; 32])); + let next_epoch = + NextEpochDescriptor { authorities: vec![(authority_id, 1u64)], randomness: [0u8; 32] }; + let log = BabeConsensusLog::NextEpochData(next_epoch); + digest.push(sp_runtime::generic::DigestItem::Consensus(BABE_ENGINE_ID, log.encode())); + digest +} + fn create_header_chain() -> (HashMap, RelayHash) { let mut headers = HashMap::new(); let mut current_parent = None; diff --git a/cumulus/client/parachain-inherent/src/lib.rs b/cumulus/client/parachain-inherent/src/lib.rs index 47040d4782b5b..863383c1e1caf 100644 --- a/cumulus/client/parachain-inherent/src/lib.rs +++ b/cumulus/client/parachain-inherent/src/lib.rs @@ -171,12 +171,10 @@ impl ParachainInherentDataProvider { ) -> Option { // Only include next epoch authorities when the descendants include an epoch digest. // Skip the first entry because this is the relay parent itself. - let include_next_authorities = relay_parent_descendants.iter().skip(1).any(|header| { - sc_consensus_babe::find_next_epoch_digest::(header) - .ok() - .flatten() - .is_some() - }); + let include_next_authorities = relay_parent_descendants + .iter() + .skip(1) + .any(|header| sc_consensus_babe::contains_epoch_change::(header)); let relay_chain_state = collect_relay_storage_proof( relay_chain_interface, para_id, diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index a7cad251379bf..a21aa92871b8c 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -913,6 +913,11 @@ pub fn find_pre_digest(header: &B::Header) -> Result(header: &B::Header) -> bool { + find_next_epoch_digest::(header).ok().flatten().is_some() +} + /// Extract the BABE epoch change digest from the given header, if it exists. pub fn find_next_epoch_digest( header: &B::Header, From ca7032e8492e8d15eeb23e5c3619ebd83756f3bb Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 23 Oct 2025 15:50:10 +0200 Subject: [PATCH 4/9] Update lib.rs Co-authored-by: Sebastian Kunert --- cumulus/client/parachain-inherent/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/client/parachain-inherent/src/lib.rs b/cumulus/client/parachain-inherent/src/lib.rs index 863383c1e1caf..f851954fd2cd2 100644 --- a/cumulus/client/parachain-inherent/src/lib.rs +++ b/cumulus/client/parachain-inherent/src/lib.rs @@ -174,7 +174,7 @@ impl ParachainInherentDataProvider { let include_next_authorities = relay_parent_descendants .iter() .skip(1) - .any(|header| sc_consensus_babe::contains_epoch_change::(header)); + .any(sc_consensus_babe::contains_epoch_change::); let relay_chain_state = collect_relay_storage_proof( relay_chain_interface, para_id, From ea6acf519fa280be5db10f5be28d776e5d9c05bf Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 24 Oct 2025 10:11:24 +0200 Subject: [PATCH 5/9] collapse 3 unit tests into one using rstest --- .../aura/src/collators/slot_based/tests.rs | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs index dfb19b211b802..afa2e841388a7 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs @@ -20,15 +20,16 @@ use super::{ relay_chain_data_cache::{RelayChainData, RelayChainDataCache}, }; use async_trait::async_trait; +use codec::Encode; use cumulus_primitives_core::{ClaimQueueOffset, CoreInfo, CoreSelector, CumulusDigestItem}; use cumulus_relay_chain_interface::*; use futures::Stream; -use codec::Encode; use polkadot_node_subsystem_util::runtime::ClaimQueueSnapshot; use polkadot_primitives::{ CandidateEvent, CommittedCandidateReceiptV2, CoreIndex, Hash as RelayHash, Header as RelayHeader, Id as ParaId, }; +use rstest::rstest; use sc_consensus_babe::{ AuthorityId, ConsensusLog as BabeConsensusLog, NextEpochDescriptor, BABE_ENGINE_ID, }; @@ -109,31 +110,25 @@ async fn offset_test_too_long() { assert!(result.is_err()); } -#[tokio::test] -async fn offset_returns_none_when_best_header_contains_epoch_change() { - let (headers, best_hash) = build_headers_with_epoch_flags(&[false, false, true]); - let client = TestRelayClient::new(headers); - let mut cache = RelayChainDataCache::new(client, 1.into()); - - let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 1).await; - assert!(result.is_ok()); - assert!(result.unwrap().is_none()); -} - -#[tokio::test] -async fn offset_returns_none_when_first_ancestor_contains_epoch_change() { - let (headers, best_hash) = build_headers_with_epoch_flags(&[false, true, false]); - let client = TestRelayClient::new(headers); - let mut cache = RelayChainDataCache::new(client, 1.into()); - - let result = offset_relay_parent_find_descendants(&mut cache, best_hash, 2).await; - assert!(result.is_ok()); - assert!(result.unwrap().is_none()); +#[derive(PartialEq)] +enum HasEpochChange { + Yes, + No, } +#[rstest] +#[case::in_best( + &[HasEpochChange::No, HasEpochChange::No, HasEpochChange::Yes], +)] +#[case::in_first_ancestor( + &[HasEpochChange::No, HasEpochChange::Yes, HasEpochChange::No], +)] +#[case::in_second_ancestor( + &[HasEpochChange::Yes, HasEpochChange::No, HasEpochChange::No], +)] #[tokio::test] -async fn offset_returns_none_when_second_ancestor_contains_epoch_change() { - let (headers, best_hash) = build_headers_with_epoch_flags(&[true, false, false]); +async fn offset_returns_none_when_epoch_change_encountered(#[case] flags: &[HasEpochChange]) { + let (headers, best_hash) = build_headers_with_epoch_flags(flags); let client = TestRelayClient::new(headers); let mut cache = RelayChainDataCache::new(client, 1.into()); @@ -633,14 +628,14 @@ impl RelayChainInterface for TestRelayClient { /// Build a consecutive set of relay headers whose digest entries optionally carry a BABE /// epoch-change marker, returning the underlying map and the hash of the last header. -fn build_headers_with_epoch_flags(flags: &[bool]) -> (HashMap, RelayHash) { +fn build_headers_with_epoch_flags(flags: &[HasEpochChange]) -> (HashMap, RelayHash) { let mut headers = HashMap::new(); let mut parent_hash = RelayHash::default(); let mut last_hash = RelayHash::default(); for (index, has_epoch_change) in flags.iter().enumerate() { let digest = - if *has_epoch_change { babe_epoch_change_digest() } else { Default::default() }; + if *has_epoch_change == HasEpochChange::Yes { babe_epoch_change_digest() } else { Default::default() }; let header = RelayHeader { parent_hash, From e2ef8b82fdf49ead88260d934ab79d6545afaa7d Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 24 Oct 2025 10:30:43 +0200 Subject: [PATCH 6/9] Better logging --- .../aura/src/collators/slot_based/block_builder_task.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index 1bddb8ac35efa..86ef1a235454d 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -485,7 +485,7 @@ where } if sc_consensus_babe::contains_epoch_change::(&relay_header) { - tracing::debug!(target: LOG_TARGET, ?relay_best_block, "Relay header contains epoch change."); + tracing::debug!(target: LOG_TARGET, ?relay_best_block, relay_best_block_number = relay_header.number(), "Relay parent is in previous session."); return Ok(None); } @@ -498,7 +498,7 @@ where .relay_parent_header .clone(); if sc_consensus_babe::contains_epoch_change::(&next_header) { - tracing::debug!(target: LOG_TARGET, ?relay_best_block, ancestor = %next_header.hash(), "Next header contains epoch change."); + tracing::debug!(target: LOG_TARGET, ?relay_best_block, ancestor = %next_header.hash(), ancestor_block_number = next_header.number(), "Ancestor of best block is in previous session."); return Ok(None); } required_ancestors.push_front(next_header.clone()); From adc6a07cf6614dd8d7cf1651176441819a1f82c3 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 24 Oct 2025 11:16:36 +0200 Subject: [PATCH 7/9] fmt --- .../consensus/aura/src/collators/slot_based/tests.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs index afa2e841388a7..e0ba35e558afe 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/tests.rs @@ -628,14 +628,19 @@ impl RelayChainInterface for TestRelayClient { /// Build a consecutive set of relay headers whose digest entries optionally carry a BABE /// epoch-change marker, returning the underlying map and the hash of the last header. -fn build_headers_with_epoch_flags(flags: &[HasEpochChange]) -> (HashMap, RelayHash) { +fn build_headers_with_epoch_flags( + flags: &[HasEpochChange], +) -> (HashMap, RelayHash) { let mut headers = HashMap::new(); let mut parent_hash = RelayHash::default(); let mut last_hash = RelayHash::default(); for (index, has_epoch_change) in flags.iter().enumerate() { - let digest = - if *has_epoch_change == HasEpochChange::Yes { babe_epoch_change_digest() } else { Default::default() }; + let digest = if *has_epoch_change == HasEpochChange::Yes { + babe_epoch_change_digest() + } else { + Default::default() + }; let header = RelayHeader { parent_hash, From 6c86c3e3d7252bbe703d12dd475adb9b1ffa1212 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 24 Oct 2025 11:19:03 +0200 Subject: [PATCH 8/9] Add PRDoc --- prdoc/pr_9990.prdoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 prdoc/pr_9990.prdoc diff --git a/prdoc/pr_9990.prdoc b/prdoc/pr_9990.prdoc new file mode 100644 index 0000000000000..57029660c66a9 --- /dev/null +++ b/prdoc/pr_9990.prdoc @@ -0,0 +1,16 @@ +title: '`cumulus`: Skip building on blocks on relay parents in old session' +doc: +- audience: Node Dev + description: |- + Collators building on older relay parents must skip building blocks when the chosen RP session is different than best block session. + + The relay chain will not accept such candidate. We can see this happening on the Kusama Canary parachain at each session boundary. + + Slot-based Aura has been modified in Cumulus to skip building blocks on relay parents in old session. +crates: +- name: cumulus-client-consensus-aura + bump: patch +- name: cumulus-client-parachain-inherent + bump: patch +- name: sc-consensus-babe + bump: patch \ No newline at end of file From 6acef9bd0d7b49c76df08ca898d8d262758e766d Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 24 Oct 2025 11:39:27 +0200 Subject: [PATCH 9/9] Change bump to minor to make CI happy --- prdoc/pr_9990.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_9990.prdoc b/prdoc/pr_9990.prdoc index 57029660c66a9..fa7f237466b66 100644 --- a/prdoc/pr_9990.prdoc +++ b/prdoc/pr_9990.prdoc @@ -13,4 +13,4 @@ crates: - name: cumulus-client-parachain-inherent bump: patch - name: sc-consensus-babe - bump: patch \ No newline at end of file + bump: minor \ No newline at end of file