// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. //! A module exporting runtime API implementation functions for all runtime APIs using `v5` //! primitives. //! //! Runtimes implementing the v2 runtime API are recommended to forward directly to these //! functions. use crate::{ configuration, disputes, dmp, hrmp, inclusion, initializer, paras, paras_inherent, scheduler::{self, CoreOccupied}, session_info, shared, }; use frame_system::pallet_prelude::*; use primitives::{ async_backing::{ AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, }, slashing, AuthorityDiscoveryId, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_runtime::traits::One; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; /// Implementation for the `validators` function of the runtime API. pub fn validators() -> Vec { >::active_validator_keys() } /// Implementation for the `validator_groups` function of the runtime API. pub fn validator_groups( ) -> (Vec>, GroupRotationInfo>) { let now = >::block_number() + One::one(); let groups = >::validator_groups(); let rotation_info = >::group_rotation_info(now); (groups, rotation_info) } /// Implementation for the `availability_cores` function of the runtime API. pub fn availability_cores() -> Vec>> { let cores = >::availability_cores(); let now = >::block_number() + One::one(); // This explicit update is only strictly required for session boundaries: // // At the end of a session we clear the claim queues: Without this update call, nothing would be // scheduled to the client. >::update_claimqueue(Vec::new(), now); let time_out_for = >::availability_timeout_predicate(); let group_responsible_for = |backed_in_number, core_index| match >::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 scheduled: BTreeMap<_, _> = >::scheduled_paras().collect(); cores .into_iter() .enumerate() .map(|(i, core)| match core { CoreOccupied::Paras(entry) => { let pending_availability = >::pending_availability(entry.para_id()) .expect("Occupied core always has pending availability; qed"); let backed_in_number = *pending_availability.backed_in_number(); CoreState::Occupied(OccupiedCore { next_up_on_available: >::next_up_on_available(CoreIndex( i as u32, )), occupied_since: backed_in_number, time_out_at: time_out_for(backed_in_number).live_until, next_up_on_time_out: >::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::Free => { if let Some(para_id) = scheduled.get(&CoreIndex(i as _)).cloned() { CoreState::Scheduled(primitives::ScheduledCore { para_id, collator: None }) } else { CoreState::Free } }, }) .collect() } /// Returns current block number being processed and the corresponding root hash. fn current_relay_parent( ) -> (BlockNumberFor, ::Hash) { use parity_scale_codec::Decode as _; let state_version = >::runtime_version().state_version(); let relay_parent_number = >::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( para_id: ParaId, assumption: OccupiedCoreAssumption, build: F, ) -> Option where Config: inclusion::Config, F: FnOnce() -> Option, { match assumption { OccupiedCoreAssumption::Included => { >::force_enact(para_id); build() }, OccupiedCoreAssumption::TimedOut => build(), OccupiedCoreAssumption::Free => { if >::pending_availability(para_id).is_some() { None } else { build() } }, } } /// Implementation for the `persisted_validation_data` function of the runtime API. pub fn persisted_validation_data( para_id: ParaId, assumption: OccupiedCoreAssumption, ) -> Option>> { let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::(); with_assumption::(para_id, assumption, || { crate::util::make_persisted_validation_data::( para_id, relay_parent_number, relay_parent_storage_root, ) }) } /// Implementation for the `assumed_validation_data` function of the runtime API. pub fn assumed_validation_data( para_id: ParaId, expected_persisted_validation_data_hash: Hash, ) -> Option<(PersistedValidationData>, ValidationCodeHash)> { let (relay_parent_number, relay_parent_storage_root) = current_relay_parent::(); // This closure obtains the `persisted_validation_data` for the given `para_id` and matches // its hash against an expected one. let make_validation_data = || { crate::util::make_persisted_validation_data::( 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(|| { // Try again with force enacting the core. This check only makes sense if // the core is occupied. >::pending_availability(para_id).and_then(|_| { >::force_enact(para_id); make_validation_data() }) }); // If we were successful, also query current validation code hash. persisted_validation_data.zip(>::current_code_hash(¶_id)) } /// Implementation for the `check_validation_outputs` function of the runtime API. pub fn check_validation_outputs( para_id: ParaId, outputs: primitives::CandidateCommitments, ) -> bool { let relay_parent_number = >::block_number(); >::check_validation_outputs_for_runtime_api( para_id, relay_parent_number, outputs, ) } /// Implementation for the `session_index_for_child` function of the runtime API. pub fn session_index_for_child() -> SessionIndex { // Just returns the session index from `inclusion`. Runtime APIs follow // initialization so the initializer will have applied any pending session change // which is expected at the child of the block whose context the runtime API was invoked // in. // // Incidentally, this is also the rationale for why it is OK to query validators or // occupied cores or etc. and expect the correct response "for child". >::session_index() } /// Implementation for the `AuthorityDiscoveryApi::authorities()` function of the runtime API. /// It is a heavy call, but currently only used for authority discovery, so it is fine. /// Gets next, current and some historical authority ids using `session_info` module. pub fn relevant_authority_ids( ) -> Vec { let current_session_index = session_index_for_child::(); let earliest_stored_session = >::earliest_stored_session(); // Due to `max_validators`, the `SessionInfo` stores only the validators who are actively // selected to participate in parachain consensus. We'd like all authorities for the current // and next sessions to be used in authority-discovery. The two sets likely have large overlap. let mut authority_ids = >::current_authorities().to_vec(); authority_ids.extend(>::next_authorities().to_vec()); // Due to disputes, we'd like to remain connected to authorities of the previous few sessions. // For this, we don't need anyone other than the validators actively participating in consensus. for session_index in earliest_stored_session..current_session_index { let info = >::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 } /// Implementation for the `validation_code` function of the runtime API. pub fn validation_code( para_id: ParaId, assumption: OccupiedCoreAssumption, ) -> Option { with_assumption::(para_id, assumption, || >::current_code(¶_id)) } /// Implementation for the `candidate_pending_availability` function of the runtime API. pub fn candidate_pending_availability( para_id: ParaId, ) -> Option> { >::candidate_pending_availability(para_id) } /// Implementation for the `candidate_events` function of the runtime API. // NOTE: this runs without block initialization, as it accesses events. // this means it can run in a different session than other runtime APIs at the same block. pub fn candidate_events(extract_event: F) -> Vec> where T: initializer::Config, F: Fn(::RuntimeEvent) -> Option>, { use inclusion::Event as RawEvent; >::read_events_no_consensus() .into_iter() .filter_map(|record| extract_event(record.event)) .filter_map(|event| { Some(match event { RawEvent::::CandidateBacked(c, h, core, group) => CandidateEvent::CandidateBacked(c, h, core, group), RawEvent::::CandidateIncluded(c, h, core, group) => CandidateEvent::CandidateIncluded(c, h, core, group), RawEvent::::CandidateTimedOut(c, h, core) => CandidateEvent::CandidateTimedOut(c, h, core), // Not needed for candidate events. RawEvent::::UpwardMessagesReceived { .. } => return None, RawEvent::::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), }) }) .collect() } /// Get the session info for the given session, if stored. pub fn session_info(index: SessionIndex) -> Option { >::session_info(index) } /// Implementation for the `dmq_contents` function of the runtime API. pub fn dmq_contents( recipient: ParaId, ) -> Vec>> { >::dmq_contents(recipient) } /// Implementation for the `inbound_hrmp_channels_contents` function of the runtime API. pub fn inbound_hrmp_channels_contents( recipient: ParaId, ) -> BTreeMap>>> { >::inbound_hrmp_channels_contents(recipient) } /// Implementation for the `validation_code_by_hash` function of the runtime API. pub fn validation_code_by_hash( hash: ValidationCodeHash, ) -> Option { >::code_by_hash(hash) } /// Disputes imported via means of on-chain imports. pub fn on_chain_votes() -> Option> { >::on_chain_votes() } /// Submits an PVF pre-checking vote. pub fn submit_pvf_check_statement( stmt: PvfCheckStatement, signature: ValidatorSignature, ) { >::submit_pvf_check_statement(stmt, signature) } /// Returns the list of all PVF code hashes that require pre-checking. pub fn pvfs_require_precheck() -> Vec { >::pvfs_require_precheck() } /// Returns the validation code hash for the given parachain making the given /// `OccupiedCoreAssumption`. pub fn validation_code_hash( para_id: ParaId, assumption: OccupiedCoreAssumption, ) -> Option where T: inclusion::Config, { with_assumption::(para_id, assumption, || { >::current_code_hash(¶_id) }) } /// Implementation for `get_session_disputes` function from the runtime API pub fn get_session_disputes( ) -> Vec<(SessionIndex, CandidateHash, DisputeState>)> { >::disputes() } /// Get session executor parameter set pub fn session_executor_params( session_index: SessionIndex, ) -> Option { // This is to bootstrap the storage working around the runtime migration issue: // https://github.com/paritytech/substrate/issues/9997 // After the bootstrap is complete (no less than 7 session passed with the runtime) // this code should be replaced with a pure // >::session_executor_params(session_index) call. match >::session_executor_params(session_index) { Some(ep) => Some(ep), None => Some(ExecutorParams::default()), } } /// Implementation of `unapplied_slashes` runtime API pub fn unapplied_slashes( ) -> Vec<(SessionIndex, CandidateHash, slashing::PendingSlashes)> { >::unapplied_slashes() } /// Implementation of `submit_report_dispute_lost` runtime API pub fn submit_unsigned_slashing_report( dispute_proof: slashing::DisputeProof, key_ownership_proof: slashing::OpaqueKeyOwnershipProof, ) -> Option<()> { let key_ownership_proof = key_ownership_proof.decode()?; >::submit_unsigned_slashing_report( dispute_proof, key_ownership_proof, ) } /// Return the min backing votes threshold from the configuration. pub fn minimum_backing_votes() -> u32 { >::config().minimum_backing_votes } /// Implementation for `ParaBackingState` function from the runtime API pub fn backing_state( para_id: ParaId, ) -> Option>> { let config = >::config(); // Async backing is only expected to be enabled with a tracker capacity of 1. // Subsequent configuration update gets applied on new session, which always // clears the buffer. // // Thus, minimum relay parent is ensured to have asynchronous backing enabled. let now = >::block_number(); let min_relay_parent_number = >::allowed_relay_parents() .hypothetical_earliest_block_number(now, config.async_backing_params.allowed_ancestry_len); let required_parent = >::para_head(para_id)?; let validation_code_hash = >::current_code_hash(para_id)?; let upgrade_restriction = >::upgrade_restriction_signal(para_id); let future_validation_code = >::future_code_upgrade_at(para_id).and_then(|block_num| { // Only read the storage if there's a pending upgrade. Some(block_num).zip(>::future_code_hash(para_id)) }); let (ump_msg_count, ump_total_bytes) = >::relay_dispatch_queue_size(para_id); let ump_remaining = config.max_upward_queue_count - ump_msg_count; let ump_remaining_bytes = config.max_upward_queue_size - ump_total_bytes; let dmp_remaining_messages = >::dmq_contents(para_id) .into_iter() .map(|msg| msg.sent_at) .collect(); let valid_watermarks = >::valid_watermarks(para_id); let hrmp_inbound = InboundHrmpLimitations { valid_watermarks }; let hrmp_channels_out = >::outbound_remaining_capacity(para_id) .into_iter() .map(|(para, (messages_remaining, bytes_remaining))| { (para, OutboundHrmpChannelLimitations { messages_remaining, bytes_remaining }) }) .collect(); let constraints = Constraints { min_relay_parent_number, max_pov_size: config.max_pov_size, max_code_size: config.max_code_size, ump_remaining, ump_remaining_bytes, max_ump_num_per_candidate: config.max_upward_message_num_per_candidate, dmp_remaining_messages, hrmp_inbound, hrmp_channels_out, max_hrmp_num_per_candidate: config.hrmp_max_message_num_per_candidate, required_parent, validation_code_hash, upgrade_restriction, future_validation_code, }; let pending_availability = { // Note: the API deals with a `Vec` as it is future-proof for cases // where there may be multiple candidates pending availability at a time. // But at the moment only one candidate can be pending availability per // parachain. crate::inclusion::PendingAvailability::::get(¶_id) .and_then(|pending| { let commitments = crate::inclusion::PendingAvailabilityCommitments::::get(¶_id); commitments.map(move |c| (pending, c)) }) .map(|(pending, commitments)| { CandidatePendingAvailability { candidate_hash: pending.candidate_hash(), descriptor: pending.candidate_descriptor().clone(), commitments, relay_parent_number: pending.relay_parent_number(), max_pov_size: constraints.max_pov_size, // assume always same in session. } }) .into_iter() .collect() }; Some(BackingState { constraints, pending_availability }) } /// Implementation for `AsyncBackingParams` function from the runtime API pub fn async_backing_params() -> AsyncBackingParams { >::config().async_backing_params }