use crate::collator::SlotClaim;
use codec::Codec;
use cumulus_client_consensus_common::{
self as consensus_common, load_abridged_host_configuration, ParentSearchParams,
};
use cumulus_primitives_aura::{AuraUnincludedSegmentApi, Slot};
use cumulus_primitives_core::{relay_chain::Hash as ParaHash, BlockT, ClaimQueueOffset};
use cumulus_relay_chain_interface::RelayChainInterface;
use polkadot_node_subsystem_util::runtime::ClaimQueueSnapshot;
use polkadot_primitives::{
AsyncBackingParams, CoreIndex, Hash as RelayHash, Id as ParaId, OccupiedCoreAssumption,
ValidationCodeHash,
};
use sc_consensus_aura::{standalone as aura_internal, AuraApi};
use sp_api::ProvideRuntimeApi;
use sp_core::Pair;
use sp_keystore::KeystorePtr;
use sp_timestamp::Timestamp;
pub mod basic;
pub mod lookahead;
pub mod slot_based;
const PARENT_SEARCH_DEPTH: usize = 10;
async fn check_validation_code_or_log(
local_validation_code_hash: &ValidationCodeHash,
para_id: ParaId,
relay_client: &impl RelayChainInterface,
relay_parent: RelayHash,
) {
let state_validation_code_hash = match relay_client
.validation_code_hash(relay_parent, para_id, OccupiedCoreAssumption::Included)
.await
{
Ok(hash) => hash,
Err(error) => {
tracing::debug!(
target: super::LOG_TARGET,
%error,
?relay_parent,
%para_id,
"Failed to fetch validation code hash",
);
return
},
};
match state_validation_code_hash {
Some(state) =>
if state != *local_validation_code_hash {
tracing::warn!(
target: super::LOG_TARGET,
%para_id,
?relay_parent,
?local_validation_code_hash,
relay_validation_code_hash = ?state,
"Parachain code doesn't match validation code stored in the relay chain state.",
);
},
None => {
tracing::warn!(
target: super::LOG_TARGET,
%para_id,
?relay_parent,
"Could not find validation code for parachain in the relay chain state.",
);
},
}
}
async fn async_backing_params(
relay_parent: RelayHash,
relay_client: &impl RelayChainInterface,
) -> Option<AsyncBackingParams> {
match load_abridged_host_configuration(relay_parent, relay_client).await {
Ok(Some(config)) => Some(config.async_backing_params),
Ok(None) => {
tracing::error!(
target: crate::LOG_TARGET,
"Active config is missing in relay chain storage",
);
None
},
Err(err) => {
tracing::error!(
target: crate::LOG_TARGET,
?err,
?relay_parent,
"Failed to read active config from relay chain client",
);
None
},
}
}
async fn cores_scheduled_for_para(
relay_parent: RelayHash,
para_id: ParaId,
relay_client: &impl RelayChainInterface,
claim_queue_offset: ClaimQueueOffset,
) -> Vec<CoreIndex> {
let claim_queue: ClaimQueueSnapshot = match relay_client.claim_queue(relay_parent).await {
Ok(claim_queue) => claim_queue.into(),
Err(error) => {
tracing::error!(
target: crate::LOG_TARGET,
?error,
?relay_parent,
"Failed to query claim queue runtime API",
);
return Vec::new()
},
};
claim_queue
.iter_claims_at_depth(claim_queue_offset.0 as usize)
.filter_map(|(core_index, core_para_id)| (core_para_id == para_id).then_some(core_index))
.collect()
}
async fn can_build_upon<Block: BlockT, Client, P>(
slot: Slot,
timestamp: Timestamp,
parent_hash: Block::Hash,
included_block: Block::Hash,
client: &Client,
keystore: &KeystorePtr,
) -> Option<SlotClaim<P::Public>>
where
Client: ProvideRuntimeApi<Block>,
Client::Api: AuraApi<Block, P::Public> + AuraUnincludedSegmentApi<Block>,
P: Pair,
P::Public: Codec,
P::Signature: Codec,
{
let runtime_api = client.runtime_api();
let authorities = runtime_api.authorities(parent_hash).ok()?;
let author_pub = aura_internal::claim_slot::<P>(slot, &authorities, keystore).await?;
if parent_hash != included_block &&
!runtime_api.can_build_upon(parent_hash, included_block, slot).ok()?
{
return None
}
Some(SlotClaim::unchecked::<P>(author_pub, slot, timestamp))
}
async fn find_parent<Block>(
relay_parent: ParaHash,
para_id: ParaId,
para_backend: &impl sc_client_api::Backend<Block>,
relay_client: &impl RelayChainInterface,
) -> Option<(<Block as BlockT>::Hash, consensus_common::PotentialParent<Block>)>
where
Block: BlockT,
{
let parent_search_params = ParentSearchParams {
relay_parent,
para_id,
ancestry_lookback: crate::collators::async_backing_params(relay_parent, relay_client)
.await
.map_or(0, |params| params.allowed_ancestry_len as usize),
max_depth: PARENT_SEARCH_DEPTH,
ignore_alternative_branches: true,
};
let potential_parents = cumulus_client_consensus_common::find_potential_parents::<Block>(
parent_search_params,
para_backend,
relay_client,
)
.await;
let potential_parents = match potential_parents {
Err(e) => {
tracing::error!(
target: crate::LOG_TARGET,
?relay_parent,
err = ?e,
"Could not fetch potential parents to build upon"
);
return None
},
Ok(x) => x,
};
let included_block = potential_parents.iter().find(|x| x.depth == 0)?.hash;
potential_parents
.into_iter()
.max_by_key(|a| a.depth)
.map(|parent| (included_block, parent))
}