mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 04:41:03 +00:00
pvf-precheck: Strip PastCodeMeta (#4408)
This PR is a part of https://github.com/paritytech/polkadot/issues/3211. This PR prepares ground for the following runtime changes required for PVF pre-checking. Specifically, we do several changes here: 1. We remove `validation_code_at` and `validation_code_hash_at`. Those functions are not used. They were added in the early days with intent to use it later but turned out that we do not need them. 2. We replace `validation_code_hash_at` with just `current_code_hash` for the case of inclusion and candidate checking. 3. We also replace `last_code_upgrade` with a direct query into `FutureCodeHash` and `UpgradeRestrictionSignal`. Those in conjunction should replace the logic that was used for allowing/disallowing upgrades. This requires special attention of the reviewers. 4. Then we remove the machinery required to support those queries. Specifically the code related to `UseCodeAt`. We do not need it since we do not answer the historical queries. However, we still leave all the data on-chain. At some point we may clean it up, but that would be needed to be done with a dedicated migration which can be done as follow-up. 5. Some now irrelevant tests were removed and/or adapted.
This commit is contained in:
@@ -36,10 +36,7 @@ use primitives::v1::{
|
||||
ValidatorIndex, ValidityAttestation,
|
||||
};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_runtime::{
|
||||
traits::{One, Saturating},
|
||||
DispatchError,
|
||||
};
|
||||
use sp_runtime::{traits::One, DispatchError};
|
||||
use sp_std::{collections::btree_set::BTreeSet, prelude::*};
|
||||
|
||||
pub use pallet::*;
|
||||
@@ -953,7 +950,6 @@ impl<T: Config> CandidateCheckContext<T> {
|
||||
backed_candidate: &BackedCandidate<<T as frame_system::Config>::Hash>,
|
||||
) -> Result<(), Error<T>> {
|
||||
let para_id = backed_candidate.descriptor().para_id;
|
||||
let now = self.now;
|
||||
|
||||
// we require that the candidate is in the context of the parent block.
|
||||
ensure!(
|
||||
@@ -965,7 +961,7 @@ impl<T: Config> CandidateCheckContext<T> {
|
||||
Error::<T>::NotCollatorSigned,
|
||||
);
|
||||
|
||||
let validation_code_hash = <paras::Pallet<T>>::validation_code_hash_at(para_id, now, None)
|
||||
let validation_code_hash = <paras::Pallet<T>>::current_code_hash(para_id)
|
||||
// A candidate for a parachain without current validation code is not scheduled.
|
||||
.ok_or_else(|| Error::<T>::UnscheduledCandidate)?;
|
||||
ensure!(
|
||||
@@ -1019,13 +1015,10 @@ impl<T: Config> CandidateCheckContext<T> {
|
||||
|
||||
// if any, the code upgrade attempt is allowed.
|
||||
if let Some(new_validation_code) = new_validation_code {
|
||||
let valid_upgrade_attempt = <paras::Pallet<T>>::last_code_upgrade(para_id, true)
|
||||
.map_or(true, |last| {
|
||||
last <= self.relay_parent_number &&
|
||||
self.relay_parent_number.saturating_sub(last) >=
|
||||
self.config.validation_upgrade_frequency
|
||||
});
|
||||
ensure!(valid_upgrade_attempt, AcceptanceCheckErr::PrematureCodeUpgrade);
|
||||
ensure!(
|
||||
<paras::Pallet<T>>::can_upgrade_validation_code(para_id),
|
||||
AcceptanceCheckErr::PrematureCodeUpgrade,
|
||||
);
|
||||
ensure!(
|
||||
new_validation_code.0.len() <= self.config.max_code_size as _,
|
||||
AcceptanceCheckErr::NewCodeTooLarge,
|
||||
|
||||
@@ -1281,8 +1281,6 @@ fn candidate_checks() {
|
||||
let expected_at = 10 + cfg.validation_upgrade_delay;
|
||||
assert_eq!(expected_at, 10);
|
||||
Paras::schedule_code_upgrade(chain_a, vec![1, 2, 3, 4].into(), expected_at, &cfg);
|
||||
|
||||
assert_eq!(Paras::last_code_upgrade(chain_a, true), Some(expected_at));
|
||||
}
|
||||
|
||||
assert_noop!(
|
||||
|
||||
@@ -76,16 +76,6 @@ pub struct ParaPastCodeMeta<N> {
|
||||
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
|
||||
@@ -164,71 +154,14 @@ impl<N: Ord + Copy + PartialEq> ParaPastCodeMeta<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.
|
||||
let was_pruned =
|
||||
replaced_after_pos == 0 && self.last_pruned.map_or(false, |t| t >= para_at);
|
||||
|
||||
if was_pruned {
|
||||
None
|
||||
} else {
|
||||
Some(UseCodeAt::ReplacedAt(self.upgrade_times[replaced_after_pos].expected_at))
|
||||
}
|
||||
} else {
|
||||
// No code replacements after this context.
|
||||
// This means either that the current code is valid, or `para_at` is so old that
|
||||
// we don't know the code necessary anymore. Compare against `last_pruned` to determine.
|
||||
self.last_pruned.as_ref().map_or(
|
||||
Some(UseCodeAt::Current), // nothing pruned, use current
|
||||
|earliest_activation| {
|
||||
if ¶_at < earliest_activation {
|
||||
None
|
||||
} else {
|
||||
Some(UseCodeAt::Current)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
/// Returns `true` if the upgrade logs list is empty.
|
||||
fn is_empty(&self) -> bool {
|
||||
self.upgrade_times.is_empty()
|
||||
}
|
||||
|
||||
// The block at which the most recently tracked code change occurred, from the perspective
|
||||
// of the para.
|
||||
#[cfg(test)]
|
||||
fn most_recent_change(&self) -> Option<N> {
|
||||
self.upgrade_times.last().map(|x| x.expected_at.clone())
|
||||
}
|
||||
@@ -802,7 +735,7 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
meta.most_recent_change().is_none() && Self::para_head(¶_id).is_none()
|
||||
meta.is_empty() && Self::para_head(¶_id).is_none()
|
||||
});
|
||||
|
||||
// This parachain has been removed and now the vestigial code
|
||||
@@ -1048,43 +981,6 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches the validation code hash for the validation code to be used when validating a block
|
||||
/// in the context of the given relay-chain height. A second block number parameter may be used
|
||||
/// to tell the lookup to proceed as if an intermediate parablock has been with the given
|
||||
/// relay-chain height as its context. This may return the hash for the past, current, or
|
||||
/// (with certain choices of `assume_intermediate`) future code.
|
||||
///
|
||||
/// `assume_intermediate`, if provided, must be before `at`. This will return `None` if the validation
|
||||
/// code has been pruned.
|
||||
///
|
||||
/// To get associated code see [`Self::validation_code_at`].
|
||||
pub(crate) fn validation_code_hash_at(
|
||||
id: ParaId,
|
||||
at: T::BlockNumber,
|
||||
assume_intermediate: Option<T::BlockNumber>,
|
||||
) -> Option<ValidationCodeHash> {
|
||||
if assume_intermediate.as_ref().map_or(false, |i| &at <= i) {
|
||||
return None
|
||||
}
|
||||
|
||||
let planned_upgrade = <Self as Store>::FutureCodeUpgrades::get(&id);
|
||||
let upgrade_applied_intermediate = match assume_intermediate {
|
||||
Some(a) => planned_upgrade.as_ref().map_or(false, |u| u <= &a),
|
||||
None => false,
|
||||
};
|
||||
|
||||
if upgrade_applied_intermediate {
|
||||
FutureCodeHash::<T>::get(&id)
|
||||
} else {
|
||||
match Self::past_code_meta(&id).code_at(at) {
|
||||
None => None,
|
||||
Some(UseCodeAt::Current) => CurrentCodeHash::<T>::get(&id),
|
||||
Some(UseCodeAt::ReplacedAt(replaced)) =>
|
||||
<Self as Store>::PastCodeHash::get(&(id, replaced)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current lifecycle state of the para.
|
||||
pub fn lifecycle(id: ParaId) -> Option<ParaLifecycle> {
|
||||
ParaLifecycles::<T>::get(&id)
|
||||
@@ -1123,16 +1019,10 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The block number of the last scheduled upgrade of the requested para. Includes future upgrades
|
||||
/// if the flag is set. This is the `expected_at` number, not the `activated_at` number.
|
||||
pub(crate) fn last_code_upgrade(id: ParaId, include_future: bool) -> Option<T::BlockNumber> {
|
||||
if include_future {
|
||||
if let Some(at) = Self::future_code_upgrade_at(id) {
|
||||
return Some(at)
|
||||
}
|
||||
}
|
||||
|
||||
Self::past_code_meta(&id).most_recent_change()
|
||||
/// If a candidate from the specified parachain were submitted at the current block, this
|
||||
/// function returns if that candidate passes the acceptance criteria.
|
||||
pub(crate) fn can_upgrade_validation_code(id: ParaId) -> bool {
|
||||
FutureCodeHash::<T>::get(&id).is_none() && UpgradeRestrictionSignal::<T>::get(&id).is_none()
|
||||
}
|
||||
|
||||
/// Return the session index that should be used for any future scheduled changes.
|
||||
@@ -1237,52 +1127,6 @@ mod tests {
|
||||
assert!(!<Paras as Store>::CodeByHash::contains_key(validation_code.hash()));
|
||||
}
|
||||
|
||||
fn fetch_validation_code_at(
|
||||
para_id: ParaId,
|
||||
at: BlockNumber,
|
||||
assume_intermediate: Option<BlockNumber>,
|
||||
) -> Option<ValidationCode> {
|
||||
Paras::validation_code_hash_at(para_id, at, assume_intermediate)
|
||||
.and_then(Paras::code_by_hash)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn para_past_code_meta_gives_right_code() {
|
||||
let mut past_code = ParaPastCodeMeta::default();
|
||||
assert_eq!(past_code.code_at(0u32), Some(UseCodeAt::Current));
|
||||
|
||||
past_code.note_replacement(10, 12);
|
||||
assert_eq!(past_code.code_at(0), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(12), Some(UseCodeAt::Current));
|
||||
|
||||
past_code.note_replacement(20, 25);
|
||||
assert_eq!(past_code.code_at(1), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(12), Some(UseCodeAt::ReplacedAt(20)));
|
||||
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
|
||||
assert_eq!(past_code.code_at(25), Some(UseCodeAt::Current));
|
||||
|
||||
past_code.note_replacement(30, 30);
|
||||
assert_eq!(past_code.code_at(1), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(10), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(11), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(12), Some(UseCodeAt::ReplacedAt(20)));
|
||||
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
|
||||
assert_eq!(past_code.code_at(25), Some(UseCodeAt::ReplacedAt(30)));
|
||||
assert_eq!(past_code.code_at(30), Some(UseCodeAt::Current));
|
||||
|
||||
past_code.last_pruned = Some(5);
|
||||
assert_eq!(past_code.code_at(1), None);
|
||||
assert_eq!(past_code.code_at(5), None);
|
||||
assert_eq!(past_code.code_at(6), Some(UseCodeAt::ReplacedAt(10)));
|
||||
assert_eq!(past_code.code_at(24), Some(UseCodeAt::ReplacedAt(20)));
|
||||
assert_eq!(past_code.code_at(25), Some(UseCodeAt::ReplacedAt(30)));
|
||||
assert_eq!(past_code.code_at(30), Some(UseCodeAt::Current));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn para_past_code_pruning_works_correctly() {
|
||||
let mut past_code = ParaPastCodeMeta::default();
|
||||
@@ -1705,27 +1549,6 @@ mod tests {
|
||||
|
||||
assert_eq!(Paras::past_code_meta(¶_id).most_recent_change(), Some(expected_at));
|
||||
|
||||
// Some hypothetical block which would have triggered the code change
|
||||
// should still use the old code.
|
||||
assert_eq!(
|
||||
Paras::past_code_meta(¶_id).code_at(expected_at),
|
||||
Some(UseCodeAt::ReplacedAt(expected_at)),
|
||||
);
|
||||
|
||||
// Some hypothetical block at the context which actually triggered the
|
||||
// code change should still use the old code.
|
||||
assert_eq!(
|
||||
Paras::past_code_meta(¶_id).code_at(expected_at + 4),
|
||||
Some(UseCodeAt::ReplacedAt(expected_at)),
|
||||
);
|
||||
|
||||
// Some hypothetical block at the context after the code was upgraded
|
||||
// should use the new code.
|
||||
assert_eq!(
|
||||
Paras::past_code_meta(¶_id).code_at(expected_at + 4 + 1),
|
||||
Some(UseCodeAt::Current),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
<Paras as Store>::PastCodeHash::get(&(para_id, expected_at)),
|
||||
Some(original_code.hash()),
|
||||
@@ -1976,64 +1799,6 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_hash_at_with_intermediate() {
|
||||
let code_retention_period = 10;
|
||||
let validation_upgrade_delay = 10;
|
||||
|
||||
let paras = vec![(
|
||||
0u32.into(),
|
||||
ParaGenesisArgs {
|
||||
parachain: true,
|
||||
genesis_head: Default::default(),
|
||||
validation_code: vec![1, 2, 3].into(),
|
||||
},
|
||||
)];
|
||||
|
||||
let genesis_config = MockGenesisConfig {
|
||||
paras: GenesisConfig { paras, ..Default::default() },
|
||||
configuration: crate::configuration::GenesisConfig {
|
||||
config: HostConfiguration {
|
||||
code_retention_period,
|
||||
validation_upgrade_delay,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
new_test_ext(genesis_config).execute_with(|| {
|
||||
let para_id = ParaId::from(0);
|
||||
let old_code: ValidationCode = vec![1, 2, 3].into();
|
||||
let new_code: ValidationCode = vec![4, 5, 6].into();
|
||||
|
||||
// expected_at = 10 = 0 + validation_upgrade_delay = 0 + 10
|
||||
Paras::schedule_code_upgrade(para_id, new_code.clone(), 0, &Configuration::config());
|
||||
assert_eq!(<Paras as Store>::FutureCodeUpgrades::get(¶_id), Some(10));
|
||||
|
||||
// no intermediate, falls back on current/past.
|
||||
assert_eq!(fetch_validation_code_at(para_id, 1, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 100, None), Some(old_code.clone()));
|
||||
|
||||
// intermediate before upgrade meant to be applied, falls back on current.
|
||||
assert_eq!(fetch_validation_code_at(para_id, 9, Some(8)), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, Some(9)), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 11, Some(9)), Some(old_code.clone()));
|
||||
|
||||
// intermediate at or after upgrade applied
|
||||
assert_eq!(fetch_validation_code_at(para_id, 11, Some(10)), Some(new_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 100, Some(11)), Some(new_code.clone()));
|
||||
|
||||
run_to_block(code_retention_period + 5, None);
|
||||
|
||||
// at <= intermediate not allowed
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, Some(10)), None);
|
||||
assert_eq!(fetch_validation_code_at(para_id, 9, Some(10)), None);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_hash_at_returns_up_to_end_of_code_retention_period() {
|
||||
let code_retention_period = 10;
|
||||
@@ -2071,18 +1836,12 @@ mod tests {
|
||||
Paras::note_new_head(para_id, Default::default(), 7);
|
||||
|
||||
assert_eq!(Paras::past_code_meta(¶_id).upgrade_times, vec![upgrade_at(2, 10)]);
|
||||
assert_eq!(Paras::current_code(¶_id), Some(new_code.clone()));
|
||||
|
||||
assert_eq!(fetch_validation_code_at(para_id, 2, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 3, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 9, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, None), Some(new_code.clone()));
|
||||
|
||||
// Make sure that the old code is available **before** the code retion period passes.
|
||||
run_to_block(10 + code_retention_period, None);
|
||||
|
||||
assert_eq!(fetch_validation_code_at(para_id, 2, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 3, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 9, None), Some(old_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, None), Some(new_code.clone()));
|
||||
assert_eq!(Paras::code_by_hash(&old_code.hash()), Some(old_code.clone()));
|
||||
assert_eq!(Paras::code_by_hash(&new_code.hash()), Some(new_code.clone()));
|
||||
|
||||
run_to_block(10 + code_retention_period + 1, None);
|
||||
|
||||
@@ -2093,10 +1852,8 @@ mod tests {
|
||||
ParaPastCodeMeta { upgrade_times: Vec::new(), last_pruned: Some(10) },
|
||||
);
|
||||
|
||||
assert_eq!(fetch_validation_code_at(para_id, 2, None), None); // pruned :(
|
||||
assert_eq!(fetch_validation_code_at(para_id, 9, None), None);
|
||||
assert_eq!(fetch_validation_code_at(para_id, 10, None), Some(new_code.clone()));
|
||||
assert_eq!(fetch_validation_code_at(para_id, 11, None), Some(new_code.clone()));
|
||||
assert_eq!(Paras::code_by_hash(&old_code.hash()), None); // pruned :(
|
||||
assert_eq!(Paras::code_by_hash(&new_code.hash()), Some(new_code.clone()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user