mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-30 16:37:57 +00:00
Implement Runtime APIs (#1411)
* create a README on Runtime APIs * add ParaId type * write up runtime APIs * more preamble * rename * rejig runtime APIs * add occupied_since to `BlockNumber` * skeleton crate for runtime API subsystem * improve group_for_core * improve docs on availability cores runtime API * guide: freed -> free * add primitives for runtime APIs * create a v1 ParachainHost API trait * guide: make validation code return `Option`al. * skeleton runtime API helpers * make parachain-host runtime-generic * skeleton for most runtime API implementation functions * guide: add runtime API helper methods * implement new helpers of the inclusion module * guide: remove retries check, as it is unneeded * implement helpers for scheduler module for Runtime APIs * clean up `validator_groups` implementation * implement next_rotation_at and last_rotation_at * guide: more helpers on GroupRotationInfo * almost finish implementing runtime APIs * add explicit block parameter to runtime API fns * guide: generalize number parameter * guide: add group_responsible to occupied-core * update primitives due to guide changes * finishing touches on runtime API implementation; squash warnings * break out runtime API impl to separate file * add tests for next_up logic * test group rotation info * point to filed TODO * remove unused TODO [now] * indentation * guide: para -> para_id * rename para field to para_id for core meta * remove reference to outdated AvailabilityCores type * add an event in `inclusion` for candidates being included or timing out * guide: candidate events * guide: adjust language * Candidate events type from guide and adjust inclusion event * implement `candidate_events` runtime API * fix runtime test compilation * max -> min * fix typos * guide: add `RuntimeAPIRequest::CandidateEvents`
This commit is contained in:
committed by
GitHub
parent
5624bd8bf4
commit
dddde219a2
@@ -0,0 +1,23 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Runtime API implementations for Parachains.
|
||||
//!
|
||||
//! These are exposed as different modules using different sets of primitives.
|
||||
//! At the moment there is only a v1 module and it is not completely clear how migration
|
||||
//! to a v2 would be done.
|
||||
|
||||
pub mod v1;
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2020 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 v1
|
||||
//! primitives.
|
||||
//!
|
||||
//! Runtimes implementing the v1 runtime API are recommended to forward directly to these
|
||||
//! functions.
|
||||
|
||||
use primitives::v1::{
|
||||
ValidatorId, ValidatorIndex, GroupRotationInfo, CoreState, GlobalValidationSchedule,
|
||||
Id as ParaId, OccupiedCoreAssumption, LocalValidationData, SessionIndex, ValidationCode,
|
||||
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
|
||||
GroupIndex, CandidateEvent,
|
||||
};
|
||||
use sp_runtime::traits::{One, BlakeTwo256, Hash as HashT, Saturating, Zero};
|
||||
use frame_support::debug;
|
||||
use crate::{initializer, inclusion, scheduler, configuration, paras};
|
||||
|
||||
/// Implementation for the `validators` function of the runtime API.
|
||||
pub fn validators<T: initializer::Trait>() -> Vec<ValidatorId> {
|
||||
<inclusion::Module<T>>::validators()
|
||||
}
|
||||
|
||||
/// Implementation for the `validator_groups` function of the runtime API.
|
||||
pub fn validator_groups<T: initializer::Trait>() -> (
|
||||
Vec<Vec<ValidatorIndex>>,
|
||||
GroupRotationInfo<T::BlockNumber>,
|
||||
) {
|
||||
let groups = <scheduler::Module<T>>::validator_groups();
|
||||
let rotation_info = <scheduler::Module<T>>::group_rotation_info();
|
||||
|
||||
(groups, rotation_info)
|
||||
}
|
||||
|
||||
/// Implementation for the `availability_cores` function of the runtime API.
|
||||
pub fn availability_cores<T: initializer::Trait>() -> Vec<CoreState<T::BlockNumber>> {
|
||||
let cores = <scheduler::Module<T>>::availability_cores();
|
||||
let parachains = <paras::Module<T>>::parachains();
|
||||
let config = <configuration::Module<T>>::config();
|
||||
|
||||
let rotation_info = <scheduler::Module<T>>::group_rotation_info();
|
||||
|
||||
let time_out_at = |backed_in_number, availability_period| {
|
||||
let time_out_at = backed_in_number + availability_period;
|
||||
|
||||
if rotation_info.group_rotation_frequency == Zero::zero() {
|
||||
return time_out_at;
|
||||
}
|
||||
|
||||
let current_window = rotation_info.last_rotation_at() + availability_period;
|
||||
let next_rotation = rotation_info.next_rotation_at();
|
||||
|
||||
// If we are within `period` blocks of rotation, timeouts are being checked
|
||||
// actively. We could even time out this block.
|
||||
if time_out_at < current_window {
|
||||
time_out_at
|
||||
} else if time_out_at <= next_rotation {
|
||||
// Otherwise, it will time out at the sooner of the next rotation
|
||||
next_rotation
|
||||
} else {
|
||||
// or the scheduled time-out. This is by definition within `period` blocks
|
||||
// of `next_rotation` and is thus a valid timeout block.
|
||||
time_out_at
|
||||
}
|
||||
};
|
||||
|
||||
let group_responsible_for = |backed_in_number, core_index| {
|
||||
match <scheduler::Module<T>>::group_assigned_to_core(core_index, backed_in_number) {
|
||||
Some(g) => g,
|
||||
None => {
|
||||
debug::warn!("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::Module<T>>
|
||||
::pending_availability(para_id)
|
||||
.expect("Occupied core always has pending availability; qed");
|
||||
|
||||
let backed_in_number = pending_availability.backed_in_number().clone();
|
||||
OccupiedCore {
|
||||
para_id,
|
||||
next_up_on_available: <scheduler::Module<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::Module<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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
CoreOccupied::Parathread(p) => {
|
||||
let para_id = p.claim.0;
|
||||
let pending_availability = <inclusion::Module<T>>
|
||||
::pending_availability(para_id)
|
||||
.expect("Occupied core always has pending availability; qed");
|
||||
|
||||
let backed_in_number = pending_availability.backed_in_number().clone();
|
||||
OccupiedCore {
|
||||
para_id,
|
||||
next_up_on_available: <scheduler::Module<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::Module<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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
None => CoreState::Free,
|
||||
}).collect();
|
||||
|
||||
// This will overwrite only `Free` cores if the scheduler module is working as intended.
|
||||
for scheduled in <scheduler::Module<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
|
||||
}
|
||||
|
||||
/// Implementation for the `global_validation_schedule` function of the runtime API.
|
||||
pub fn global_validation_schedule<T: initializer::Trait>()
|
||||
-> GlobalValidationSchedule<T::BlockNumber>
|
||||
{
|
||||
let config = <configuration::Module<T>>::config();
|
||||
GlobalValidationSchedule {
|
||||
max_code_size: config.max_code_size,
|
||||
max_head_data_size: config.max_head_data_size,
|
||||
block_number: <system::Module<T>>::block_number() - One::one(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for the `local_validation_data` function of the runtime API.
|
||||
pub fn local_validation_data<T: initializer::Trait>(
|
||||
para_id: ParaId,
|
||||
assumption: OccupiedCoreAssumption,
|
||||
) -> Option<LocalValidationData<T::BlockNumber>> {
|
||||
let construct = || {
|
||||
let relay_parent_number = <system::Module<T>>::block_number() - One::one();
|
||||
|
||||
let config = <configuration::Module<T>>::config();
|
||||
let freq = config.validation_upgrade_frequency;
|
||||
let delay = config.validation_upgrade_delay;
|
||||
|
||||
let last_code_upgrade = <paras::Module<T>>::last_code_upgrade(para_id, true)?;
|
||||
let can_upgrade_code = last_code_upgrade <= relay_parent_number
|
||||
&& relay_parent_number.saturating_sub(last_code_upgrade) >= freq;
|
||||
|
||||
let code_upgrade_allowed = if can_upgrade_code {
|
||||
Some(relay_parent_number + delay)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Some(LocalValidationData {
|
||||
parent_head: <paras::Module<T>>::para_head(¶_id)?,
|
||||
balance: 0,
|
||||
validation_code_hash: BlakeTwo256::hash_of(
|
||||
&<paras::Module<T>>::current_code(¶_id)?
|
||||
),
|
||||
code_upgrade_allowed,
|
||||
})
|
||||
};
|
||||
|
||||
match assumption {
|
||||
OccupiedCoreAssumption::Included => {
|
||||
<inclusion::Module<T>>::force_enact(para_id);
|
||||
construct()
|
||||
}
|
||||
OccupiedCoreAssumption::TimedOut => {
|
||||
construct()
|
||||
}
|
||||
OccupiedCoreAssumption::Free => {
|
||||
if <inclusion::Module<T>>::pending_availability(para_id).is_some() {
|
||||
None
|
||||
} else {
|
||||
construct()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for the `session_index_for_child` function of the runtime API.
|
||||
pub fn session_index_for_child<T: initializer::Trait>() -> 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".
|
||||
<inclusion::Module<T>>::session_index()
|
||||
}
|
||||
|
||||
/// Implementation for the `validation_code` function of the runtime API.
|
||||
pub fn validation_code<T: initializer::Trait>(
|
||||
para_id: ParaId,
|
||||
assumption: OccupiedCoreAssumption,
|
||||
) -> Option<ValidationCode> {
|
||||
let fetch = || {
|
||||
<paras::Module<T>>::current_code(¶_id)
|
||||
};
|
||||
|
||||
match assumption {
|
||||
OccupiedCoreAssumption::Included => {
|
||||
<inclusion::Module<T>>::force_enact(para_id);
|
||||
fetch()
|
||||
}
|
||||
OccupiedCoreAssumption::TimedOut => {
|
||||
fetch()
|
||||
}
|
||||
OccupiedCoreAssumption::Free => {
|
||||
if <inclusion::Module<T>>::pending_availability(para_id).is_some() {
|
||||
None
|
||||
} else {
|
||||
fetch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for the `candidate_pending_availability` function of the runtime API.
|
||||
pub fn candidate_pending_availability<T: initializer::Trait>(para_id: ParaId)
|
||||
-> Option<CommittedCandidateReceipt<T::Hash>>
|
||||
{
|
||||
<inclusion::Module<T>>::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<T: initializer::Trait>(
|
||||
extract_event: impl Fn(<T as system::Trait>::Event) -> Option<inclusion::Event<T>>,
|
||||
) -> Vec<CandidateEvent<T::Hash>> {
|
||||
use inclusion::Event as RawEvent;
|
||||
|
||||
<system::Module<T>>::events().into_iter()
|
||||
.filter_map(|record| extract_event(record.event))
|
||||
.map(|event| match event {
|
||||
RawEvent::<T>::CandidateBacked(c, h) => CandidateEvent::CandidateBacked(c, h),
|
||||
RawEvent::<T>::CandidateIncluded(c, h) => CandidateEvent::CandidateIncluded(c, h),
|
||||
RawEvent::<T>::CandidateTimedOut(c, h) => CandidateEvent::CandidateTimedOut(c, h),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
Reference in New Issue
Block a user