Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 3249186

Browse files
joepetrowskibkchr
andauthored
Add Version Checks on Para Upgrade (#2261)
* add version checks on para ugprade * Apply suggestions from code review Co-authored-by: Bastian Köcher <[email protected]> * remove unneeded imports and errors * fix test error path --------- Co-authored-by: Bastian Köcher <[email protected]>
1 parent 5854708 commit 3249186

File tree

2 files changed

+100
-19
lines changed

2 files changed

+100
-19
lines changed

pallets/parachain-system/src/lib.rs

+64-18
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@
1616

1717
#![cfg_attr(not(feature = "std"), no_std)]
1818

19-
//! cumulus-pallet-parachain-system is a base pallet for cumulus-based parachains.
19+
//! `cumulus-pallet-parachain-system` is a base pallet for Cumulus-based parachains.
2020
//!
21-
//! This pallet handles low-level details of being a parachain. It's responsibilities include:
21+
//! This pallet handles low-level details of being a parachain. Its responsibilities include:
2222
//!
23-
//! - ingestion of the parachain validation data
24-
//! - ingestion of incoming downward and lateral messages and dispatching them
25-
//! - coordinating upgrades with the relay-chain
26-
//! - communication of parachain outputs, such as sent messages, signalling an upgrade, etc.
23+
//! - ingestion of the parachain validation data;
24+
//! - ingestion and dispatch of incoming downward and lateral messages;
25+
//! - coordinating upgrades with the Relay Chain; and
26+
//! - communication of parachain outputs, such as sent messages, signaling an upgrade, etc.
2727
//!
2828
//! Users must ensure that they register this pallet as an inherent provider.
2929
30-
use codec::Encode;
30+
use codec::{Decode, Encode, MaxEncodedLen};
3131
use cumulus_primitives_core::{
3232
relay_chain, AbridgedHostConfiguration, ChannelStatus, CollationInfo, DmpMessageHandler,
3333
GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, MessageSendError,
@@ -45,6 +45,7 @@ use frame_support::{
4545
};
4646
use frame_system::{ensure_none, ensure_root};
4747
use polkadot_parachain::primitives::RelayChainBlockNumber;
48+
use scale_info::TypeInfo;
4849
use sp_runtime::{
4950
traits::{Block as BlockT, BlockNumberProvider, Hash},
5051
transaction_validity::{
@@ -134,6 +135,20 @@ impl CheckAssociatedRelayNumber for AnyRelayNumber {
134135
fn check_associated_relay_number(_: RelayChainBlockNumber, _: RelayChainBlockNumber) {}
135136
}
136137

138+
/// Information needed when a new runtime binary is submitted and needs to be authorized before
139+
/// replacing the current runtime.
140+
#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
141+
#[scale_info(skip_type_params(T))]
142+
struct CodeUpgradeAuthorization<T>
143+
where
144+
T: Config,
145+
{
146+
/// Hash of the new runtime binary.
147+
code_hash: T::Hash,
148+
/// Whether or not to carry out version checks.
149+
check_version: bool,
150+
}
151+
137152
#[frame_support::pallet]
138153
pub mod pallet {
139154
use super::*;
@@ -442,17 +457,40 @@ pub mod pallet {
442457
Ok(())
443458
}
444459

460+
/// Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied
461+
/// later.
462+
///
463+
/// The `check_version` parameter sets a boolean flag for whether or not the runtime's spec
464+
/// version and name should be verified on upgrade. Since the authorization only has a hash,
465+
/// it cannot actually perform the verification.
466+
///
467+
/// This call requires Root origin.
445468
#[pallet::call_index(2)]
446469
#[pallet::weight((1_000_000, DispatchClass::Operational))]
447-
pub fn authorize_upgrade(origin: OriginFor<T>, code_hash: T::Hash) -> DispatchResult {
470+
pub fn authorize_upgrade(
471+
origin: OriginFor<T>,
472+
code_hash: T::Hash,
473+
check_version: bool,
474+
) -> DispatchResult {
448475
ensure_root(origin)?;
449-
450-
AuthorizedUpgrade::<T>::put(&code_hash);
476+
AuthorizedUpgrade::<T>::put(CodeUpgradeAuthorization {
477+
code_hash: code_hash.clone(),
478+
check_version,
479+
});
451480

452481
Self::deposit_event(Event::UpgradeAuthorized { code_hash });
453482
Ok(())
454483
}
455484

485+
/// Provide the preimage (runtime binary) `code` for an upgrade that has been authorized.
486+
///
487+
/// If the authorization required a version check, this call will ensure the spec name
488+
/// remains unchanged and that the spec version has increased.
489+
///
490+
/// Note that this function will not apply the new `code`, but only attempt to schedule the
491+
/// upgrade with the Relay Chain.
492+
///
493+
/// All origins are allowed.
456494
#[pallet::call_index(3)]
457495
#[pallet::weight(1_000_000)]
458496
pub fn enact_authorized_upgrade(
@@ -487,16 +525,16 @@ pub mod pallet {
487525

488526
#[pallet::error]
489527
pub enum Error<T> {
490-
/// Attempt to upgrade validation function while existing upgrade pending
528+
/// Attempt to upgrade validation function while existing upgrade pending.
491529
OverlappingUpgrades,
492-
/// Polkadot currently prohibits this parachain from upgrading its validation function
530+
/// Polkadot currently prohibits this parachain from upgrading its validation function.
493531
ProhibitedByPolkadot,
494532
/// The supplied validation function has compiled into a blob larger than Polkadot is
495-
/// willing to run
533+
/// willing to run.
496534
TooBig,
497-
/// The inherent which supplies the validation data did not run this block
535+
/// The inherent which supplies the validation data did not run this block.
498536
ValidationDataNotAvailable,
499-
/// The inherent which supplies the host configuration did not run this block
537+
/// The inherent which supplies the host configuration did not run this block.
500538
HostConfigurationNotAvailable,
501539
/// No validation function upgrade is currently scheduled.
502540
NotScheduled,
@@ -645,7 +683,7 @@ pub mod pallet {
645683

646684
/// The next authorized upgrade, if there is one.
647685
#[pallet::storage]
648-
pub(super) type AuthorizedUpgrade<T: Config> = StorageValue<_, T::Hash>;
686+
pub(super) type AuthorizedUpgrade<T: Config> = StorageValue<_, CodeUpgradeAuthorization<T>>;
649687

650688
/// A custom head data that should be returned as result of `validate_block`.
651689
///
@@ -712,9 +750,17 @@ pub mod pallet {
712750

713751
impl<T: Config> Pallet<T> {
714752
fn validate_authorized_upgrade(code: &[u8]) -> Result<T::Hash, DispatchError> {
715-
let required_hash = AuthorizedUpgrade::<T>::get().ok_or(Error::<T>::NothingAuthorized)?;
753+
let authorization = AuthorizedUpgrade::<T>::get().ok_or(Error::<T>::NothingAuthorized)?;
754+
755+
// ensure that the actual hash matches the authorized hash
716756
let actual_hash = T::Hashing::hash(&code[..]);
717-
ensure!(actual_hash == required_hash, Error::<T>::Unauthorized);
757+
ensure!(actual_hash == authorization.code_hash, Error::<T>::Unauthorized);
758+
759+
// check versions if required as part of the authorization
760+
if authorization.check_version {
761+
frame_system::Pallet::<T>::can_set_code(code)?;
762+
}
763+
718764
Ok(actual_hash)
719765
}
720766
}

pallets/parachain-system/src/tests.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ use frame_support::{
3232
use frame_system::RawOrigin;
3333
use hex_literal::hex;
3434
use relay_chain::HrmpChannelId;
35-
use sp_core::H256;
35+
use sp_core::{blake2_256, H256};
3636
use sp_runtime::{
3737
testing::Header,
3838
traits::{BlakeTwo256, IdentityLookup},
39+
DispatchErrorWithPostInfo,
3940
};
4041
use sp_version::RuntimeVersion;
4142
use std::cell::RefCell;
@@ -974,3 +975,37 @@ fn test() {
974975
.add(1, || {})
975976
.add(2, || {});
976977
}
978+
979+
#[test]
980+
fn upgrade_version_checks_should_work() {
981+
let test_data = vec![
982+
("test", 0, 1, Err(frame_system::Error::<Test>::SpecVersionNeedsToIncrease)),
983+
("test", 1, 0, Err(frame_system::Error::<Test>::SpecVersionNeedsToIncrease)),
984+
("test", 1, 1, Err(frame_system::Error::<Test>::SpecVersionNeedsToIncrease)),
985+
("test", 1, 2, Err(frame_system::Error::<Test>::SpecVersionNeedsToIncrease)),
986+
("test2", 1, 1, Err(frame_system::Error::<Test>::InvalidSpecName)),
987+
];
988+
989+
for (spec_name, spec_version, impl_version, expected) in test_data.into_iter() {
990+
let version = RuntimeVersion {
991+
spec_name: spec_name.into(),
992+
spec_version,
993+
impl_version,
994+
..Default::default()
995+
};
996+
let read_runtime_version = ReadRuntimeVersion(version.encode());
997+
998+
let mut ext = new_test_ext();
999+
ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(read_runtime_version));
1000+
ext.execute_with(|| {
1001+
let new_code = vec![1, 2, 3, 4];
1002+
let new_code_hash = sp_core::H256(blake2_256(&new_code));
1003+
1004+
let _authorize =
1005+
ParachainSystem::authorize_upgrade(RawOrigin::Root.into(), new_code_hash, true);
1006+
let res = ParachainSystem::enact_authorized_upgrade(RawOrigin::None.into(), new_code);
1007+
1008+
assert_eq!(expected.map_err(DispatchErrorWithPostInfo::from), res);
1009+
});
1010+
}
1011+
}

0 commit comments

Comments
 (0)