Add relay storage root to persisted validation data (#2161)

* Cont.: Implement the state root obtaining during inclusion

During inclusion now we obtain the storage root by passing it through
the inclusion_inherent.

* Fix tests

* Bump rococo spec version

* Reorder the parent header into the end

of the inclusion inherent.

When the parent header is in the beginning, it shifts the other two
fields, so that a previous version won't be able to decode that. If
we put the parent header in the end, the other two fields will stay
at their positions, thus make it possible to decode with the previous
version.

That allows us to perform upgrade of rococo runtime without needing of
simultanuous upgrade of nodes and runtime, or restart of the network.

* Squash a stray tab
This commit is contained in:
Sergei Shulepov
2021-01-04 17:58:20 +01:00
committed by GitHub
parent 930978ce77
commit a864eaa093
14 changed files with 120 additions and 36 deletions
+18 -2
View File
@@ -25,7 +25,7 @@ use primitives::v1::{
ValidatorId, CandidateCommitments, CandidateDescriptor, ValidatorIndex, Id as ParaId,
AvailabilityBitfield as AvailabilityBitfield, SignedAvailabilityBitfields, SigningContext,
BackedCandidate, CoreIndex, GroupIndex, CommittedCandidateReceipt,
CandidateReceipt, HeadData, CandidateHash,
CandidateReceipt, HeadData, CandidateHash, Hash,
};
use frame_support::{
decl_storage, decl_module, decl_error, decl_event, ensure, debug,
@@ -382,11 +382,13 @@ impl<T: Config> Module<T> {
Ok(freed_cores)
}
/// Process candidates that have been backed. Provide a set of candidates and scheduled cores.
/// Process candidates that have been backed. Provide the relay storage root, a set of candidates
/// and scheduled cores.
///
/// Both should be sorted ascending by core index, and the candidates should be a subset of
/// scheduled cores. If these conditions are not met, the execution of the function fails.
pub(crate) fn process_candidates(
parent_storage_root: Hash,
candidates: Vec<BackedCandidate<T::Hash>>,
scheduled: Vec<CoreAssignment>,
group_validators: impl Fn(GroupIndex) -> Option<Vec<ValidatorIndex>>,
@@ -491,6 +493,7 @@ impl<T: Config> Module<T> {
match crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
parent_storage_root,
) {
Some(l) => l,
None => {
@@ -1129,6 +1132,7 @@ mod tests {
= crate::util::make_persisted_validation_data::<Test>(
para_id,
relay_parent_number,
Default::default(),
)?;
Some(persisted_validation_data.hash())
}
@@ -1634,6 +1638,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_b_assignment.clone()],
&group_validators,
@@ -1692,6 +1697,7 @@ mod tests {
// out-of-order manifests as unscheduled.
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed_b, backed_a],
vec![chain_a_assignment.clone(), chain_b_assignment.clone()],
&group_validators,
@@ -1726,6 +1732,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -1762,6 +1769,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -1798,6 +1806,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![
chain_a_assignment.clone(),
@@ -1841,6 +1850,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![thread_a_assignment.clone()],
&group_validators,
@@ -1888,6 +1898,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -1929,6 +1940,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -1975,6 +1987,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -2010,6 +2023,7 @@ mod tests {
assert_eq!(
Inclusion::process_candidates(
Default::default(),
vec![backed],
vec![chain_a_assignment.clone()],
&group_validators,
@@ -2151,6 +2165,7 @@ mod tests {
));
let occupied_cores = Inclusion::process_candidates(
Default::default(),
vec![backed_a, backed_b, backed_c],
vec![
chain_a_assignment.clone(),
@@ -2283,6 +2298,7 @@ mod tests {
));
let occupied_cores = Inclusion::process_candidates(
Default::default(),
vec![backed_a],
vec![
chain_a_assignment.clone(),
@@ -23,8 +23,9 @@
use sp_std::prelude::*;
use primitives::v1::{
BackedCandidate, SignedAvailabilityBitfields, INCLUSION_INHERENT_IDENTIFIER,
BackedCandidate, SignedAvailabilityBitfields, INCLUSION_INHERENT_IDENTIFIER, Header,
};
use sp_runtime::traits::One;
use frame_support::{
decl_error, decl_module, decl_storage, ensure,
dispatch::DispatchResult,
@@ -57,6 +58,9 @@ decl_error! {
pub enum Error for Module<T: Config> {
/// Inclusion inherent called more than once per block.
TooManyInclusionInherents,
/// The hash of the submitted parent header doesn't correspond to the saved block hash of
/// the parent.
InvalidParentHeader,
}
}
@@ -81,10 +85,19 @@ decl_module! {
origin,
signed_bitfields: SignedAvailabilityBitfields,
backed_candidates: Vec<BackedCandidate<T::Hash>>,
parent_header: Header,
) -> DispatchResult {
ensure_none(origin)?;
ensure!(!<Included>::exists(), Error::<T>::TooManyInclusionInherents);
// Check that the submitted parent header indeed corresponds to the previous block hash.
let now = <frame_system::Module<T>>::block_number();
let parent_hash = <frame_system::Module<T>>::block_hash(now - One::one());
ensure!(
parent_header.hash().as_ref() == parent_hash.as_ref(),
Error::<T>::InvalidParentHeader,
);
// Process new availability bitfields, yielding any availability cores whose
// work has now concluded.
let freed_concluded = <inclusion::Module<T>>::process_bitfields(
@@ -107,7 +120,9 @@ decl_module! {
<scheduler::Module<T>>::schedule(freed);
// Process backed candidates according to scheduled cores.
let parent_storage_root = parent_header.state_root;
let occupied = <inclusion::Module<T>>::process_candidates(
parent_storage_root,
backed_candidates,
<scheduler::Module<T>>::scheduled(),
<scheduler::Module<T>>::group_validators,
@@ -135,14 +150,27 @@ impl<T: Config> ProvideInherent for Module<T> {
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
data.get_data(&Self::INHERENT_IDENTIFIER)
.expect("inclusion inherent data failed to decode")
.map(|(signed_bitfields, backed_candidates): (SignedAvailabilityBitfields, Vec<BackedCandidate<T::Hash>>)| {
// Sanity check: session changes can invalidate an inherent, and we _really_ don't want that to happen.
// See github.com/paritytech/polkadot/issues/1327
if Self::inclusion(frame_system::RawOrigin::None.into(), signed_bitfields.clone(), backed_candidates.clone()).is_ok() {
Call::inclusion(signed_bitfields, backed_candidates)
} else {
Call::inclusion(Vec::new().into(), Vec::new())
.map(
|(signed_bitfields, backed_candidates, parent_header): (
SignedAvailabilityBitfields,
Vec<BackedCandidate<T::Hash>>,
Header,
)| {
// Sanity check: session changes can invalidate an inherent, and we _really_ don't want that to happen.
// See github.com/paritytech/polkadot/issues/1327
if Self::inclusion(
frame_system::RawOrigin::None.into(),
signed_bitfields.clone(),
backed_candidates.clone(),
parent_header.clone(),
)
.is_ok()
{
Call::inclusion(signed_bitfields, backed_candidates, parent_header)
} else {
Call::inclusion(Vec::new().into(), Vec::new(), parent_header)
}
}
})
)
}
}
@@ -24,7 +24,7 @@ use primitives::v1::{
Id as ParaId, OccupiedCoreAssumption, SessionIndex, ValidationCode,
CommittedCandidateReceipt, ScheduledCore, OccupiedCore, CoreOccupied, CoreIndex,
GroupIndex, CandidateEvent, PersistedValidationData, SessionInfo,
InboundDownwardMessage, InboundHrmpMessage,
InboundDownwardMessage, InboundHrmpMessage, Hash,
};
use frame_support::debug;
use crate::{initializer, inclusion, scheduler, configuration, paras, session_info, dmp, hrmp};
@@ -190,18 +190,24 @@ fn with_assumption<Config, T, F>(
pub fn full_validation_data<T: initializer::Config>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
)
-> Option<ValidationData<T::BlockNumber>>
{
) -> Option<ValidationData<T::BlockNumber>> {
use parity_scale_codec::Decode as _;
let relay_parent_number = <frame_system::Module<T>>::block_number();
with_assumption::<T, _, _>(
para_id,
assumption,
|| Some(ValidationData {
persisted: crate::util::make_persisted_validation_data::<T>(para_id, relay_parent_number)?,
transient: crate::util::make_transient_validation_data::<T>(para_id, relay_parent_number)?,
}),
)
let relay_storage_root = Hash::decode(&mut &sp_io::storage::root()[..])
.expect("storage root must decode to the Hash type; qed");
with_assumption::<T, _, _>(para_id, assumption, || {
Some(ValidationData {
persisted: crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
relay_storage_root,
)?,
transient: crate::util::make_transient_validation_data::<T>(
para_id,
relay_parent_number,
)?,
})
})
}
/// Implementation for the `persisted_validation_data` function of the runtime API.
@@ -209,12 +215,17 @@ pub fn persisted_validation_data<T: initializer::Config>(
para_id: ParaId,
assumption: OccupiedCoreAssumption,
) -> Option<PersistedValidationData<T::BlockNumber>> {
use parity_scale_codec::Decode as _;
let relay_parent_number = <frame_system::Module<T>>::block_number();
with_assumption::<T, _, _>(
para_id,
assumption,
|| crate::util::make_persisted_validation_data::<T>(para_id, relay_parent_number),
)
let relay_storage_root = Hash::decode(&mut &sp_io::storage::root()[..])
.expect("storage root must decode to the Hash type; qed");
with_assumption::<T, _, _>(para_id, assumption, || {
crate::util::make_persisted_validation_data::<T>(
para_id,
relay_parent_number,
relay_storage_root,
)
})
}
/// Implementation for the `check_validation_outputs` function of the runtime API.
+5 -2
View File
@@ -18,22 +18,25 @@
//! on all modules.
use sp_runtime::traits::Saturating;
use primitives::v1::{Id as ParaId, PersistedValidationData, TransientValidationData};
use primitives::v1::{Id as ParaId, PersistedValidationData, TransientValidationData, Hash};
use crate::{configuration, paras, dmp, hrmp};
/// Make the persisted validation data for a particular parachain and a specified relay-parent.
/// Make the persisted validation data for a particular parachain, a specified relay-parent and it's
/// storage root.
///
/// This ties together the storage of several modules.
pub fn make_persisted_validation_data<T: paras::Config + hrmp::Config>(
para_id: ParaId,
relay_parent_number: T::BlockNumber,
relay_storage_root: Hash,
) -> Option<PersistedValidationData<T::BlockNumber>> {
let config = <configuration::Module<T>>::config();
Some(PersistedValidationData {
parent_head: <paras::Module<T>>::para_head(&para_id)?,
block_number: relay_parent_number,
relay_storage_root,
hrmp_mqc_heads: <hrmp::Module<T>>::hrmp_mqc_heads(para_id),
dmq_mqc_head: <dmp::Module<T>>::dmq_mqc_head(para_id),
max_pov_size: config.max_pov_size,