Newer
Older
// 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/>.
//! The paras pallet is responsible for storing data on parachains and parathreads.
//!
//! It tracks which paras are parachains, what their current head data is in
//! this fork of the relay chain, what their validation code is, and what their past and upcoming
//! validation code is.
//!
//! A para is not considered live until it is registered and activated in this pallet. Activation can
//! only occur at session boundaries.
use crate::{configuration, initializer::SessionChangeNotification, shared};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use parity_scale_codec::{Decode, Encode};
ConsensusLog, HeadData, Id as ParaId, SessionIndex, ValidationCode, ValidationCodeHash,
use sp_runtime::{traits::One, DispatchResult, SaturatedConversion};
use sp_std::{prelude::*, result};
// the two key times necessary to track for every code replacement.
#[derive(Default, Encode, Decode)]
#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
pub struct ReplacementTimes<N> {
/// The relay-chain block number that the code upgrade was expected to be activated.
/// This is when the code change occurs from the para's perspective - after the
/// first parablock included with a relay-parent with number >= this value.
expected_at: N,
/// The relay-chain block number at which the parablock activating the code upgrade was
/// actually included. This means considered included and available, so this is the time at which
/// that parablock enters the acceptance period in this fork of the relay-chain.
activated_at: N,
}
/// Metadata used to track previous parachain validation code that we keep in
/// the state.
#[derive(Default, Encode, Decode)]
#[cfg_attr(test, derive(Debug, Clone, PartialEq))]
pub struct ParaPastCodeMeta<N> {
/// Block numbers where the code was expected to be replaced and where the code
/// was actually replaced, respectively. The first is used to do accurate lookups
/// of historic code in historic contexts, whereas the second is used to do
/// pruning on an accurate timeframe. These can be used as indices
/// into the `PastCodeHash` map along with the `ParaId` to fetch the code itself.
upgrade_times: Vec<ReplacementTimes<N>>,
/// Tracks the highest pruned code-replacement, if any. This is the `activated_at` value,
/// not the `expected_at` value.
last_pruned: Option<N>,
}
#[cfg_attr(test, derive(Debug, PartialEq))]
enum UseCodeAt<N> {
/// Use the current code.
Current,
/// Use the code that was replaced at the given block number.
/// This is an inclusive endpoint - a parablock in the context of a relay-chain block on this fork
/// with number N should use the code that is replaced at N.
ReplacedAt(N),
}
/// The possible states of a para, to take into account delayed lifecycle changes.
///
/// If the para is in a "transition state", it is expected that the parachain is
/// queued in the `ActionsQueue` to transition it into a stable state. Its lifecycle
/// state will be used to determine the state transition to apply to the para.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
pub enum ParaLifecycle {
/// Para is new and is onboarding as a Parathread or Parachain.
Onboarding,
/// Para is a Parathread.
Parathread,
/// Para is a Parachain.
Parachain,
/// Para is a Parathread which is upgrading to a Parachain.
UpgradingParathread,
/// Para is a Parachain which is downgrading to a Parathread.
DowngradingParachain,
/// Parathread is queued to be offboarded.
OffboardingParathread,
/// Parachain is queued to be offboarded.
OffboardingParachain,
}
impl ParaLifecycle {
/// Returns true if parachain is currently onboarding. To learn if the
/// parachain is onboarding as a parachain or parathread, look at the
/// `UpcomingGenesis` storage item.
pub fn is_onboarding(&self) -> bool {
matches!(self, ParaLifecycle::Onboarding)
}
/// Returns true if para is in a stable state, i.e. it is currently
/// a parachain or parathread, and not in any transition state.
pub fn is_stable(&self) -> bool {
matches!(self, ParaLifecycle::Parathread | ParaLifecycle::Parachain)
}
/// Returns true if para is currently treated as a parachain.
/// This also includes transitioning states, so you may want to combine
/// this check with `is_stable` if you specifically want `Paralifecycle::Parachain`.
pub fn is_parachain(&self) -> bool {
ParaLifecycle::Parachain |
ParaLifecycle::DowngradingParachain |
ParaLifecycle::OffboardingParachain
/// Returns true if para is currently treated as a parathread.
/// This also includes transitioning states, so you may want to combine
/// this check with `is_stable` if you specifically want `Paralifecycle::Parathread`.
pub fn is_parathread(&self) -> bool {
ParaLifecycle::Parathread |
ParaLifecycle::UpgradingParathread |
ParaLifecycle::OffboardingParathread
/// Returns true if para is currently offboarding.
pub fn is_offboarding(&self) -> bool {
matches!(self, ParaLifecycle::OffboardingParathread | ParaLifecycle::OffboardingParachain)
/// Returns true if para is in any transitionary state.
pub fn is_transitioning(&self) -> bool {
!Self::is_stable(self)
}
}
impl<N: Ord + Copy + PartialEq> ParaPastCodeMeta<N> {
// note a replacement has occurred at a given block number.
fn note_replacement(&mut self, expected_at: N, activated_at: N) {
self.upgrade_times.push(ReplacementTimes { expected_at, activated_at })
}
// Yields an identifier that should be used for validating a
// parablock in the context of a particular relay-chain block number in this chain.
//
// a return value of `None` means that there is no code we are aware of that
// should be used to validate at the given height.
fn code_at(&self, para_at: N) -> Option<UseCodeAt<N>> {
// Find out
// a) if there is a point where code was replaced in the current chain after the context
// we are finding out code for.
// b) what the index of that point is.
//
// The reason we use `activated_at` instead of `expected_at` is that a gap may occur
// between expectation and actual activation. Any block executed in a context from
// `expected_at..activated_at` is expected to activate the code upgrade and therefore should
// use the previous code.
//
// A block executed in the context of `activated_at` should use the new code.
//
// Cases where `expected_at` and `activated_at` are the same, that is, zero-delay code upgrades
// are also handled by this rule correctly.
let replaced_after_pos = self.upgrade_times.iter().position(|t| {
// example: code replaced at (5, 5)
//
// context #4 should use old code
// context #5 should use new code
//
// example: code replaced at (10, 20)
// context #9 should use the old code
// context #10 should use the old code
// context #19 should use the old code
// context #20 should use the new code
para_at < t.activated_at
});
if let Some(replaced_after_pos) = replaced_after_pos {
// The earliest stored code replacement needs to be special-cased, since we need to check
// against the pruning state to see if this replacement represents the correct code, or
// is simply after a replacement that actually represents the correct code, but has been pruned.
Loading full blame...