-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add Paras authorize_code_hash + apply_authorized_code feature
#7592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
90fa441
693c277
1a9dba4
8dc2779
e14f331
a3d1f3c
9bb38f3
9eea06f
caff716
4d746d2
bde0c00
6062032
3243e63
1aeb6b3
77463f4
a1225cb
a699970
ad37325
d0c6e45
65ec72b
eaf9a78
f10b0a8
841f4e4
560c6bb
83abc82
1eb117e
8b1a483
795d59a
5bde2fd
ad907ab
7c386ec
033f908
f6981d0
ab3a823
d5800fc
15552cc
6e0cccb
3cdb61e
3157c46
9bd8c32
4ef9b78
b4fdadc
1a94e0d
9bb5f67
d6eb3c2
e37f698
1a8e1fd
2f26d71
6b46716
cb1dde3
1e52cf9
1cb919e
407a430
90a8d44
f500e19
f34663f
ad5a78e
a8c4624
f9a2de9
9fbb144
2409b1c
70e7acf
a7be028
d770e0a
a4ccb10
56bd15c
651d7b4
a067b68
8b81724
a3e9f5f
f07b826
6075ad7
822a1ec
8036264
abdde6c
b110076
0cb5c59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -117,7 +117,10 @@ use alloc::{collections::btree_set::BTreeSet, vec::Vec}; | |
| use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; | ||
| use codec::{Decode, Encode}; | ||
| use core::{cmp, mem}; | ||
| use frame_support::{pallet_prelude::*, traits::EstimateNextSessionRotation, DefaultNoBound}; | ||
| use frame_support::{ | ||
| dispatch::PostDispatchInfo, pallet_prelude::*, traits::EstimateNextSessionRotation, | ||
| DefaultNoBound, | ||
| }; | ||
| use frame_system::pallet_prelude::*; | ||
| use polkadot_primitives::{ | ||
| ConsensusLog, HeadData, Id as ParaId, PvfCheckStatement, SessionIndex, UpgradeGoAhead, | ||
|
|
@@ -551,6 +554,8 @@ pub trait WeightInfo { | |
| fn include_pvf_check_statement_finalize_onboarding_accept() -> Weight; | ||
| fn include_pvf_check_statement_finalize_onboarding_reject() -> Weight; | ||
| fn include_pvf_check_statement() -> Weight; | ||
| fn authorize_force_set_current_code_hash() -> Weight; | ||
| fn apply_authorized_force_set_current_code(c: u32) -> Weight; | ||
| } | ||
|
|
||
| pub struct TestWeightInfo; | ||
|
|
@@ -596,6 +601,12 @@ impl WeightInfo for TestWeightInfo { | |
| // This special value is to distinguish from the finalizing variants above in tests. | ||
| Weight::MAX - Weight::from_parts(1, 1) | ||
| } | ||
| fn authorize_force_set_current_code_hash() -> Weight { | ||
| Weight::MAX | ||
| } | ||
| fn apply_authorized_force_set_current_code(_c: u32) -> Weight { | ||
| Weight::MAX | ||
| } | ||
| } | ||
|
|
||
| #[frame_support::pallet] | ||
|
|
@@ -649,6 +660,8 @@ pub mod pallet { | |
| pub enum Event { | ||
| /// Current code has been updated for a Para. `para_id` | ||
| CurrentCodeUpdated(ParaId), | ||
| /// New code hash has been authorized for a Para. | ||
| CodeAuthorized { code_hash: ValidationCodeHash, para_id: ParaId }, | ||
| /// Current head has been updated for a Para. `para_id` | ||
| CurrentHeadUpdated(ParaId), | ||
| /// A code upgrade has been scheduled for a Para. `para_id` | ||
|
|
@@ -791,6 +804,11 @@ pub mod pallet { | |
| #[pallet::storage] | ||
| pub type FutureCodeHash<T: Config> = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>; | ||
|
|
||
| /// The code hash of a para which is authorized. | ||
| #[pallet::storage] | ||
| pub(super) type AuthorizedCodeHash<T: Config> = | ||
| StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>; | ||
|
|
||
| /// This is used by the relay-chain to communicate to a parachain a go-ahead with in the upgrade | ||
| /// procedure. | ||
| /// | ||
|
|
@@ -895,10 +913,7 @@ pub mod pallet { | |
| new_code: ValidationCode, | ||
| ) -> DispatchResult { | ||
| ensure_root(origin)?; | ||
| let new_code_hash = new_code.hash(); | ||
| Self::increase_code_ref(&new_code_hash, &new_code); | ||
| Self::set_current_code(para, new_code_hash, frame_system::Pallet::<T>::block_number()); | ||
| Self::deposit_event(Event::CurrentCodeUpdated(para)); | ||
| Self::do_force_set_current_code_update(para, new_code); | ||
| Ok(()) | ||
| } | ||
|
|
||
|
|
@@ -1149,6 +1164,78 @@ pub mod pallet { | |
| MostRecentContext::<T>::insert(¶, context); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Sets the storage for the authorized current code hash of the parachain. | ||
| /// | ||
| /// This can be useful when we want to trigger `Paras::force_set_current_code(para, code)` | ||
| /// from a different chain than the one where the `Paras` pallet is deployed. | ||
| /// | ||
| /// The main reason is to avoid transferring the entire `new_code` wasm blob between chains. | ||
| /// Instead, we authorize `new_code_hash` with `root`, which can later be applied by | ||
| /// `Paras::apply_authorized_force_set_current_code(para, new_code)` by anyone. | ||
| #[pallet::call_index(9)] | ||
| #[pallet::weight(<T as Config>::WeightInfo::authorize_force_set_current_code_hash())] | ||
| pub fn authorize_force_set_current_code_hash( | ||
| origin: OriginFor<T>, | ||
| para: ParaId, | ||
| new_code_hash: ValidationCodeHash, | ||
| ) -> DispatchResult { | ||
| ensure_root(origin)?; | ||
|
|
||
| // if one is already authorized, means it has not been applied yet, so we just replace | ||
| // it. | ||
| if let Some(already_authorized) = AuthorizedCodeHash::<T>::take(para) { | ||
| log::warn!( | ||
| target: LOG_TARGET, | ||
| "Already authorized code hash: {:?} found for para {:?}, just removing it!", | ||
| already_authorized, para | ||
| ); | ||
| } | ||
|
|
||
| // TODO: FAIL-CI - more validations? | ||
| // do we need to check against `FutureCodeHash`, `CodeHashRef`, | ||
| // `PastCodeHash`,... code hashes? | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // insert authorized code hash. | ||
| AuthorizedCodeHash::<T>::insert(¶, new_code_hash); | ||
| Self::deposit_event(Event::CodeAuthorized { para_id: para, code_hash: new_code_hash }); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Applies the already authorized current code for the parachain, | ||
| /// triggering the same functionality as `force_set_current_code`. | ||
| #[pallet::call_index(10)] | ||
| #[pallet::weight(<T as Config>::WeightInfo::apply_authorized_force_set_current_code(new_code.0.len() as u32))] | ||
| pub fn apply_authorized_force_set_current_code( | ||
bkontur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| _origin: OriginFor<T>, | ||
| para: ParaId, | ||
| new_code: ValidationCode, | ||
| ) -> DispatchResultWithPostInfo { | ||
| // no need to ensure, anybody can do this | ||
|
|
||
| // check `new_code` is authorized | ||
| let Some(authorized_code_hash) = AuthorizedCodeHash::<T>::take(¶) else { | ||
| log::error!(target: LOG_TARGET, "No authorized code hash found for para {:?}!", para); | ||
| return Err(Error::<T>::CannotUpgradeCode.into()) | ||
bkontur marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
| let new_code_hash = new_code.hash(); | ||
| ensure!(new_code_hash == authorized_code_hash, Error::<T>::CannotUpgradeCode); | ||
|
|
||
| // TODO: FAIL-CI - more validations? | ||
|
|
||
| // set current code | ||
| Self::do_force_set_current_code_update(para, new_code); | ||
|
|
||
| // if ok, then allows "for free" | ||
| let post = PostDispatchInfo { | ||
| // consume the rest of the block to prevent further transactions | ||
| actual_weight: Some(T::BlockWeights::get().max_block), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to do this? I suppose the code blob can be large, but ref-time wise this doesn't seem like a large transaction at all.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question! Honestly, I'm not entirely sure. I just followed this pattern: |
||
| // no fee for valid upgrade | ||
| pays_fee: Pays::No, | ||
| }; | ||
| Ok(post) | ||
| } | ||
| } | ||
|
|
||
| #[pallet::validate_unsigned] | ||
|
|
@@ -2159,6 +2246,14 @@ impl<T: Config> Pallet<T> { | |
| weight + T::DbWeight::get().writes(1) | ||
| } | ||
|
|
||
| /// Force set the current code for the given parachain. | ||
| fn do_force_set_current_code_update(para: ParaId, new_code: ValidationCode) { | ||
| let new_code_hash = new_code.hash(); | ||
| Self::increase_code_ref(&new_code_hash, &new_code); | ||
| Self::set_current_code(para, new_code_hash, frame_system::Pallet::<T>::block_number()); | ||
| Self::deposit_event(Event::CurrentCodeUpdated(para)); | ||
| } | ||
|
|
||
| /// Returns the list of PVFs (aka validation code) that require casting a vote by a validator in | ||
| /// the active validator set. | ||
| pub(crate) fn pvfs_require_precheck() -> Vec<ValidationCodeHash> { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.