use crate::{
configuration, dmp, hrmp, inclusion, initializer, paras, paras_inherent, scheduler,
session_info, shared,
};
use primitives::v2::{
AuthorityDiscoveryId, CandidateEvent, CommittedCandidateReceipt, CoreIndex, CoreOccupied,
CoreState, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage,
InboundHrmpMessage, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData,
PvfCheckStatement, ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo,
ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature,
};
use sp_runtime::traits::One;
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
pub fn validators<T: initializer::Config>() -> Vec<ValidatorId> {
<shared::Pallet<T>>::active_validator_keys()
}
pub fn validator_groups<T: initializer::Config>(
) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo<T::BlockNumber>) {
let now = <frame_system::Pallet<T>>::block_number() + One::one();
let groups = <scheduler::Pallet<T>>::validator_groups();
let rotation_info = <scheduler::Pallet<T>>::group_rotation_info(now);
(groups, rotation_info)
}
pub fn availability_cores<T: initializer::Config>() -> Vec<CoreState<T::Hash, T::BlockNumber>> {
let cores = <scheduler::Pallet<T>>::availability_cores();
let parachains = <paras::Pallet<T>>::parachains();
let config = <configuration::Pallet<T>>::config();
let now = <frame_system::Pallet<T>>::block_number() + One::one();
<scheduler::Pallet<T>>::clear();
<scheduler::Pallet<T>>::schedule(Vec::new(), now);
let rotation_info = <scheduler::Pallet<T>>::group_rotation_info(now);
let time_out_at = |backed_in_number, availability_period| {
let time_out_at = backed_in_number + availability_period;
let current_window = rotation_info.last_rotation_at() + availability_period;
let next_rotation = rotation_info.next_rotation_at();
if time_out_at < current_window {
time_out_at
} else if time_out_at <= next_rotation {
next_rotation
} else {
time_out_at
}
};
let group_responsible_for =
|backed_in_number, core_index| match <scheduler::Pallet<T>>::group_assigned_to_core(
core_index,
backed_in_number,
) {
Some(g) => g,
None => {
log::warn!(
target: "runtime::polkadot-api::v2",
"Could not determine the group responsible for core extracted \
from list of cores for some prior block in same session",
);
GroupIndex(0)
},
};
let mut core_states: Vec<_> = cores
.into_iter()
.enumerate()
.map(|(i, core)| match core {
Some(occupied) => CoreState::Occupied(match occupied {
CoreOccupied::Parachain => {
let para_id = parachains[i];
let pending_availability =
<inclusion::Pallet<T>>::pending_availability(para_id)
.expect("Occupied core always has pending availability; qed");
let backed_in_number = *pending_availability.backed_in_number();
OccupiedCore {
next_up_on_available: <scheduler::Pallet<T>>::next_up_on_available(
CoreIndex(i as u32),
),
occupied_since: backed_in_number,
time_out_at: time_out_at(
backed_in_number,
config.chain_availability_period,
),
next_up_on_time_out: <scheduler::Pallet<T>>::next_up_on_time_out(
CoreIndex(i as u32),
),
availability: pending_availability.availability_votes().clone(),
group_responsible: group_responsible_for(
backed_in_number,
pending_availability.core_occupied(),
),
candidate_hash: pending_availability.candidate_hash(),
candidate_descriptor: pending_availability.candidate_descriptor().clone(),
}
},
CoreOccupied::Parathread(p) => {
let para_id = p.claim.0;
let pending_availability =
<inclusion::Pallet<T>>::pending_availability(para_id)
.expect("Occupied core always has pending availability; qed");
let backed_in_number = *pending_availability.backed_in_number();
OccupiedCore {
next_up_on_available: <scheduler::Pallet<T>>::next_up_on_available(
CoreIndex(i as u32),
),
occupied_since: backed_in_number,
time_out_at: time_out_at(
backed_in_number,
config.thread_availability_period,
),
next_up_on_time_out: <scheduler::Pallet<T>>::next_up_on_time_out(
CoreIndex(i as u32),
),
availability: pending_availability.availability_votes().clone(),
group_responsible: group_responsible_for(
backed_in_number,
pending_availability.core_occupied(),
),
candidate_hash: pending_availability.candidate_hash(),
candidate_descriptor: pending_availability.candidate_descriptor().clone(),
}
},
}),
None => CoreState::Free,
})
.collect();
for scheduled in <scheduler::Pallet<T>>::scheduled() {
core_states[scheduled.core.0 as usize] = CoreState::Scheduled(ScheduledCore {
para_id: scheduled.para_id,
collator: scheduled.required_collator().map(|c| c.clone()),
});
}
core_states
}
fn current_relay_parent<T: frame_system::Config>(
) -> (<T as frame_system::Config>::BlockNumber, <T as frame_system::Config>::Hash) {
use parity_scale_codec::Decode as _;
let state_version = <frame_system::Pallet<T>>::runtime_version().state_version();
let relay_parent_number = <frame_system::Pallet<T>>::block_number();
let relay_parent_storage_root = T::Hash::decode(&mut &sp_io::storage::root(state_version)[..])
.expect("storage root must decode to the Hash type; qed");
(relay_parent_number, relay_parent_storage_root)
}
fn with_assumption<Config, T, F>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
build: F,
) -> Option<T>
where
Config: inclusion::Config,
F: FnOnce() -> Option<T>,
{
match assumption {
OccupiedCoreAssumption::Included => {
<inclusion::Pallet<Config>>::force_enact(para_id);
build()
},
OccupiedCoreAssumption::TimedOut => build(),
OccupiedCoreAssumption::Free => {
if <inclusion::Pallet<Config>>::pending_availability(para_id).is_some() {
None
} else {
build()
}
},
}
}
pub fn persisted_validation_data<T: initializer::Config>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<PersistedValidationData<T::Hash, T::BlockNumber>> {
let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
with_assumption::<T, _, _>(para_id, assumption, || {
crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
relay_parent_storage_root,
)
})
}
pub fn assumed_validation_data<T: initializer::Config>(
para_id: ParaId,
expected_persisted_validation_data_hash: Hash,
) -> Option<(PersistedValidationData<T::Hash, T::BlockNumber>, ValidationCodeHash)> {
let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::<T>();
let make_validation_data = || {
crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
relay_parent_storage_root,
)
.filter(|validation_data| validation_data.hash() == expected_persisted_validation_data_hash)
};
let persisted_validation_data = make_validation_data().or_else(|| {
<inclusion::Pallet<T>>::pending_availability(para_id).and_then(|_| {
<inclusion::Pallet<T>>::force_enact(para_id);
make_validation_data()
})
});
persisted_validation_data.zip(<paras::Pallet<T>>::current_code_hash(¶_id))
}
pub fn check_validation_outputs<T: initializer::Config>(
para_id: ParaId,
outputs: primitives::v2::CandidateCommitments,
) -> bool {
<inclusion::Pallet<T>>::check_validation_outputs_for_runtime_api(para_id, outputs)
}
pub fn session_index_for_child<T: initializer::Config>() -> SessionIndex {
<shared::Pallet<T>>::session_index()
}
pub fn relevant_authority_ids<T: initializer::Config + pallet_authority_discovery::Config>(
) -> Vec<AuthorityDiscoveryId> {
let current_session_index = session_index_for_child::<T>();
let earliest_stored_session = <session_info::Pallet<T>>::earliest_stored_session();
let mut authority_ids = <pallet_authority_discovery::Pallet<T>>::current_authorities().to_vec();
authority_ids.extend(<pallet_authority_discovery::Pallet<T>>::next_authorities().to_vec());
for session_index in earliest_stored_session..current_session_index {
let info = <session_info::Pallet<T>>::session_info(session_index);
if let Some(mut info) = info {
authority_ids.append(&mut info.discovery_keys);
}
}
authority_ids.sort();
authority_ids.dedup();
authority_ids
}
pub fn validation_code<T: initializer::Config>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<ValidationCode> {
with_assumption::<T, _, _>(para_id, assumption, || <paras::Pallet<T>>::current_code(¶_id))
}
pub fn candidate_pending_availability<T: initializer::Config>(
para_id: ParaId,
) -> Option<CommittedCandidateReceipt<T::Hash>> {
<inclusion::Pallet<T>>::candidate_pending_availability(para_id)
}
pub fn candidate_events<T, F>(extract_event: F) -> Vec<CandidateEvent<T::Hash>>
where
T: initializer::Config,
F: Fn(<T as frame_system::Config>::RuntimeEvent) -> Option<inclusion::Event<T>>,
{
use inclusion::Event as RawEvent;
<frame_system::Pallet<T>>::read_events_no_consensus()
.into_iter()
.filter_map(|record| extract_event(record.event))
.map(|event| match event {
RawEvent::<T>::CandidateBacked(c, h, core, group) =>
CandidateEvent::CandidateBacked(c, h, core, group),
RawEvent::<T>::CandidateIncluded(c, h, core, group) =>
CandidateEvent::CandidateIncluded(c, h, core, group),
RawEvent::<T>::CandidateTimedOut(c, h, core) =>
CandidateEvent::CandidateTimedOut(c, h, core),
RawEvent::<T>::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
})
.collect()
}
pub fn session_info<T: session_info::Config>(index: SessionIndex) -> Option<SessionInfo> {
<session_info::Pallet<T>>::session_info(index)
}
pub fn dmq_contents<T: dmp::Config>(
recipient: ParaId,
) -> Vec<InboundDownwardMessage<T::BlockNumber>> {
<dmp::Pallet<T>>::dmq_contents(recipient)
}
pub fn inbound_hrmp_channels_contents<T: hrmp::Config>(
recipient: ParaId,
) -> BTreeMap<ParaId, Vec<InboundHrmpMessage<T::BlockNumber>>> {
<hrmp::Pallet<T>>::inbound_hrmp_channels_contents(recipient)
}
pub fn validation_code_by_hash<T: paras::Config>(
hash: ValidationCodeHash,
) -> Option<ValidationCode> {
<paras::Pallet<T>>::code_by_hash(hash)
}
pub fn on_chain_votes<T: paras_inherent::Config>() -> Option<ScrapedOnChainVotes<T::Hash>> {
<paras_inherent::Pallet<T>>::on_chain_votes()
}
pub fn submit_pvf_check_statement<T: paras::Config>(
stmt: PvfCheckStatement,
signature: ValidatorSignature,
) {
<paras::Pallet<T>>::submit_pvf_check_statement(stmt, signature)
}
pub fn pvfs_require_precheck<T: paras::Config>() -> Vec<ValidationCodeHash> {
<paras::Pallet<T>>::pvfs_require_precheck()
}
pub fn validation_code_hash<T>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<ValidationCodeHash>
where
T: inclusion::Config,
{
with_assumption::<T, _, _>(para_id, assumption, || {
<paras::Pallet<T>>::current_code_hash(¶_id)
})
}