From 111a53674593e1468345a094d6fbe5e1a45de13f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 13 Nov 2023 19:36:46 +0100 Subject: [PATCH 001/101] initial commit --- .../runtime/common/src/paras_registrar/mod.rs | 59 ++++++++++++++++++- polkadot/runtime/parachains/src/paras/mod.rs | 2 +- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2d33cf28993d..223855bb5b01 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -43,6 +43,8 @@ use sp_runtime::{ traits::{CheckedSub, Saturating}, RuntimeDebug, }; +use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; +use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { @@ -142,6 +144,8 @@ pub mod pallet { #[pallet::constant] type DataDepositPerByte: Get>; + type SovereignAccountOf: ConvertLocation; + /// Weight Information for the Extrinsics in the Pallet type WeightInfo: WeightInfo; } @@ -411,9 +415,23 @@ pub mod pallet { para: ParaId, new_code: ValidationCode, ) -> DispatchResult { - Self::ensure_root_para_or_owner(origin, para)?; - runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; - Ok(()) + Self::ensure_root_para_or_owner(origin.clone(), para)?; + + let fee_payer = if let Ok(caller) = ensure_signed(origin.clone()) { + let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; + ensure!(!para_info.is_locked(), Error::::ParaLocked); + ensure!(para_info.manager == caller, Error::::NotOwner); + + Some(caller) + } else if let Ok(_) = ensure_root(origin) { + None + } else { + let location: MultiLocation = (Parent, Parachain(para.into())).into(); + let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); + Some(sovereign_account) + }; + + Self::do_schedule_code_upgrade(para, new_code, fee_payer) } /// Set the parachain's current head. @@ -642,6 +660,41 @@ impl Pallet { Ok(()) } + fn do_schedule_code_upgrade( + para: ParaId, + new_code: ValidationCode, + fee_payer: Option, + ) -> DispatchResult { + if let Some(payer) = fee_payer { + ensure!(paras::Pallet::::para_head(para).is_some(), Error::::NotRegistered); + let head = paras::Pallet::::para_head(para) + .expect("Ensured that the head is existant above; qed"); + + let per_byte_fee = T::DataDepositPerByte::get(); + let new_deposit = T::ParaDeposit::get() + .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) + .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); + + let current_deposit = Paras::::get(para) + .expect("Para info must be stored if head data for this parachain exists; qed") + .deposit; + + if current_deposit > new_deposit { + // In case the existing deposit exceeds the required amount (due to changes in the + // pallet's configuration), any excess deposit will be removed. + let rebate = current_deposit.saturating_sub(new_deposit); + ::Currency::unreserve(&payer, rebate); + } else { + // An additional deposit is required to cover for the new validation code which has + // a greater size compared to the old one. + let excess = new_deposit.saturating_sub(current_deposit); + ::Currency::reserve(&payer, excess)?; + } + } + + runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) + } + /// Verifies the onboarding data is valid for a para. /// /// Returns `ParaGenesisArgs` and the deposit needed for the data. diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index cd73d23bdadb..982c173db1cf 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -750,7 +750,7 @@ pub mod pallet { pub(super) type FutureCodeHash = StorageMap<_, Twox64Concat, ParaId, ValidationCodeHash>; - /// This is used by the relay-chain to communicate to a parachain a go-ahead with in the upgrade + /// This is used by the relay-chain to communicate to a parachain a go-ahead within the upgrade /// procedure. /// /// This value is absent when there are no upgrades scheduled or during the time the relay chain From 36ff9078ae92c6b532a0e5c8aadefafb2b7759e0 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 14 Nov 2023 12:19:13 +0100 Subject: [PATCH 002/101] required fee for upgrading --- .../runtime/common/src/paras_registrar/mod.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 223855bb5b01..358cc0600335 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -23,7 +23,7 @@ use frame_support::{ dispatch::DispatchResult, ensure, pallet_prelude::Weight, - traits::{Currency, Get, ReservableCurrency}, + traits::{Currency, ExistenceRequirement, Get, ReservableCurrency}, }; use frame_system::{self, ensure_root, ensure_signed}; use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; @@ -144,6 +144,12 @@ pub mod pallet { #[pallet::constant] type DataDepositPerByte: Get>; + #[pallet::constant] + type UpgradeFee: Get>; + + #[pallet::constant] + type FeeReceiver: Get; + type SovereignAccountOf: ConvertLocation; /// Weight Information for the Extrinsics in the Pallet @@ -684,12 +690,19 @@ impl Pallet { // pallet's configuration), any excess deposit will be removed. let rebate = current_deposit.saturating_sub(new_deposit); ::Currency::unreserve(&payer, rebate); - } else { + } else if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&payer, excess)?; } + + ::Currency::transfer( + &payer, + &T::FeeReceiver::get(), + T::UpgradeFee::get(), + ExistenceRequirement::KeepAlive, + )?; } runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) From 628b3f03576c2aa83fb8048de7e0dd44671e2d4e Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 14 Nov 2023 16:30:21 +0100 Subject: [PATCH 003/101] add basic docs --- .../runtime/common/src/paras_registrar/mod.rs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 358cc0600335..fe508d58c369 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -144,12 +144,22 @@ pub mod pallet { #[pallet::constant] type DataDepositPerByte: Get>; + /// The fee required to perform a validation code upgrade for a parachain. + /// + /// This is used to discourage spamming parachain upgrades. #[pallet::constant] type UpgradeFee: Get>; + /// Since the fees paid are not reserved, but are actually removed from the initializer of + /// the upgrade this specifies the location to which the tokens will be moved. + /// + /// NOTE: In most cases this will either go to treasury or simply get burned. #[pallet::constant] type FeeReceiver: Get; + /// Type used to get the sovereign account of a parachain. + /// + /// This is used to enable reserving a deposit from parachains. type SovereignAccountOf: ConvertLocation; /// Weight Information for the Extrinsics in the Pallet @@ -414,6 +424,9 @@ pub mod pallet { /// /// Can be called by Root, the parachain, or the parachain manager if the parachain is /// unlocked. + /// + /// In case the call is made by the parachain manager or the parachain itself the upgrade + /// fee and potential storage costs will be reserved. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))] pub fn schedule_code_upgrade( @@ -429,7 +442,8 @@ pub mod pallet { ensure!(para_info.manager == caller, Error::::NotOwner); Some(caller) - } else if let Ok(_) = ensure_root(origin) { + } else if ensure_root(origin).is_ok() { + // Root doesn't pay. None } else { let location: MultiLocation = (Parent, Parachain(para.into())).into(); @@ -666,6 +680,9 @@ impl Pallet { Ok(()) } + /// Schedules a code upgrade for a parachain. + /// + /// In case the `fee_payer` is defined fees will be charged and extra deposit will be requried. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, @@ -693,6 +710,8 @@ impl Pallet { } else if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. + + // NOTE: what if the upgrade fails? This should be removed or moved to another place. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&payer, excess)?; } From 0439ef9263761f430cc30c9727e9f6e54c1dfbe3 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 16 Nov 2023 20:58:53 +0100 Subject: [PATCH 004/101] start making tests work --- .../runtime/common/src/paras_registrar/mod.rs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index fe508d58c369..24cb003f06ca 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -36,7 +36,7 @@ use sp_std::{prelude::*, result}; use crate::traits::{OnSwap, Registrar}; pub use pallet::*; -use parity_scale_codec::{Decode, Encode}; +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use runtime_parachains::paras::{OnNewHead, ParaKind}; use scale_info::TypeInfo; use sp_runtime::{ @@ -711,7 +711,8 @@ impl Pallet { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. - // NOTE: what if the upgrade fails? This should be removed or moved to another place. + // NOTE: what if the upgrade fails? This should be removed or moved to another + // place. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&payer, excess)?; } @@ -802,10 +803,15 @@ mod tests { BuildStorage, Perbill, }; use sp_std::collections::btree_map::BTreeMap; + use xcm::opaque::lts::NetworkId; + use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; + #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)] + struct AccountId([u8; 32]); + frame_support::construct_runtime!( pub enum Test { @@ -843,7 +849,7 @@ mod tests { type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; @@ -908,8 +914,16 @@ mod tests { pub const ParaDeposit: Balance = 10; pub const DataDepositPerByte: Balance = 1; pub const MaxRetries: u32 = 3; + pub const UpgradeFee: Balance = 2; + pub const RelayNetwork: NetworkId = NetworkId::Kusama; } + pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, + ); + impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -917,6 +931,9 @@ mod tests { type OnSwap = MockSwap; type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; + type UpgradeFee = UpgradeFee; + type FeeReceiver = (); + type SovereignAccountOf = LocationToAccountId; type WeightInfo = TestWeightInfo; } From f12aaf2c325855bb2aae36f0f269afc1d47a4936 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 17 Nov 2023 12:10:44 +0100 Subject: [PATCH 005/101] tests passing --- .../runtime/common/src/integration_tests.rs | 12 +++++++ .../runtime/common/src/paras_registrar/mod.rs | 33 ++++--------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index d5a32775fd49..fe5faef517f8 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -49,6 +49,8 @@ use sp_runtime::{ AccountId32, BuildStorage, }; use sp_std::sync::Arc; +use xcm::opaque::lts::NetworkId; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -211,8 +213,16 @@ impl paras::Config for Test { parameter_types! { pub const ParaDeposit: Balance = 500; pub const DataDepositPerByte: Balance = 1; + pub const UpgradeFee: Balance = 2; + pub const RelayNetwork: NetworkId = NetworkId::Kusama; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId32>, +); + impl paras_registrar::Config for Test { type RuntimeEvent = RuntimeEvent; type OnSwap = (Crowdloan, Slots); @@ -220,6 +230,8 @@ impl paras_registrar::Config for Test { type DataDepositPerByte = DataDepositPerByte; type Currency = Balances; type RuntimeOrigin = RuntimeOrigin; + type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type WeightInfo = crate::paras_registrar::TestWeightInfo; } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 24cb003f06ca..984caff7bdf5 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -23,7 +23,7 @@ use frame_support::{ dispatch::DispatchResult, ensure, pallet_prelude::Weight, - traits::{Currency, ExistenceRequirement, Get, ReservableCurrency}, + traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; @@ -36,7 +36,7 @@ use sp_std::{prelude::*, result}; use crate::traits::{OnSwap, Registrar}; pub use pallet::*; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use parity_scale_codec::{Decode, Encode}; use runtime_parachains::paras::{OnNewHead, ParaKind}; use scale_info::TypeInfo; use sp_runtime::{ @@ -150,15 +150,7 @@ pub mod pallet { #[pallet::constant] type UpgradeFee: Get>; - /// Since the fees paid are not reserved, but are actually removed from the initializer of - /// the upgrade this specifies the location to which the tokens will be moved. - /// - /// NOTE: In most cases this will either go to treasury or simply get burned. - #[pallet::constant] - type FeeReceiver: Get; - - /// Type used to get the sovereign account of a parachain. - /// + /// Type used to get the sovereign account of a parachain. /// /// This is used to enable reserving a deposit from parachains. type SovereignAccountOf: ConvertLocation; @@ -717,10 +709,10 @@ impl Pallet { ::Currency::reserve(&payer, excess)?; } - ::Currency::transfer( + ::Currency::withdraw( &payer, - &T::FeeReceiver::get(), T::UpgradeFee::get(), + WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; } @@ -804,14 +796,10 @@ mod tests { }; use sp_std::collections::btree_map::BTreeMap; use xcm::opaque::lts::NetworkId; - use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; - #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, TypeInfo)] - struct AccountId([u8; 32]); - frame_support::construct_runtime!( pub enum Test { @@ -849,7 +837,7 @@ mod tests { type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = AccountId; + type AccountId = u64; type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; @@ -918,12 +906,6 @@ mod tests { pub const RelayNetwork: NetworkId = NetworkId::Kusama; } - pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, - ); - impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -932,8 +914,7 @@ mod tests { type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type UpgradeFee = UpgradeFee; - type FeeReceiver = (); - type SovereignAccountOf = LocationToAccountId; + type SovereignAccountOf = (); type WeightInfo = TestWeightInfo; } From adeb298fdd50e4ee57a830fa22b3bc65948afc99 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 17 Nov 2023 19:25:19 +0100 Subject: [PATCH 006/101] first test --- .../runtime/common/src/integration_tests.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index fe5faef517f8..d1be2113efce 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -374,6 +374,12 @@ fn contains_event(event: RuntimeEvent) -> bool { System::events().iter().any(|x| x.event == event) } +/// Helper function for getting a custom size validation code. +fn validation_code(size: usize) -> ValidationCode { + let validation_code = vec![0u8; size]; + validation_code.into() +} + // Runs an end to end test of the auction, crowdloan, slots, and onboarding process over varying // lease period offsets. #[test] @@ -563,6 +569,64 @@ fn basic_end_to_end_works() { } } +#[test] +fn para_upgrade_initiated_by_manager_works() { + new_test_ext().execute_with(|| { + assert!(System::block_number().is_one()); /* So events are emitted */ + let para_1 = LOWEST_PUBLIC_ID; + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + let start_block = System::block_number(); + + // User 1 will own parachains + let free_balance = 1_000_000_000; + Balances::make_free_balance_be(&account_id(1), 1_000_000_000); + // Register an on demand parachain + let code_size = 1024 * 1024; + let genesis_head = Registrar::worst_head_data(); + let validation_code = validation_code(code_size); + + assert_ok!(Registrar::reserve(signed(1))); + assert_ok!(Registrar::register( + signed(1), + ParaId::from(para_1), + genesis_head.clone(), + validation_code.clone(), + )); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + + // The para should be onboarding. + assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Onboarding)); + // After two sessions the parachain will be succesfully registered as an on-demand. + run_to_session(START_SESSION_INDEX + 2); + assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread)); + // The deposit should be appropriately taken. + let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // Schedule a para upgrade to set the validation code to a new one which is twice the size. + // The only reason why this is callable by the para manager is because the parachain isn't + // locked. + let validation_code = Registrar::worst_validation_code(); + assert_ok!(Registrar::schedule_code_upgrade( + signed(1), + ParaId::from(para_1), + validation_code.clone(), + )); + // The reserved deposit should cover for the size difference of the new validation code. + let total_bytes_stored = 2 * code_size as u32 + genesis_head.0.len() as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // An additional upgrade fee should also be deducted from the caller's balance. + assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); + }); +} + #[test] fn basic_errors_fail() { new_test_ext().execute_with(|| { From 58b1219e3bac986310a1405aaebbf1b4df704041 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 17 Nov 2023 20:00:53 +0100 Subject: [PATCH 007/101] refund solution - incomplete --- .../runtime/common/src/integration_tests.rs | 1 - .../runtime/common/src/paras_registrar/mod.rs | 37 ++++++++++++++++--- polkadot/runtime/parachains/src/paras/mod.rs | 2 +- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index d1be2113efce..0d7a0bc663ad 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -576,7 +576,6 @@ fn para_upgrade_initiated_by_manager_works() { let para_1 = LOWEST_PUBLIC_ID; const START_SESSION_INDEX: SessionIndex = 1; run_to_session(START_SESSION_INDEX); - let start_block = System::block_number(); // User 1 will own parachains let free_balance = 1_000_000_000; diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 984caff7bdf5..288f717c72ab 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -64,6 +64,15 @@ impl ParaInfo { } } +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] +struct RefundInfo { + /// Potential refund that is awaiting the code upgrade to successfully execute. + pending: Balance, + /// Refund for reducing the validation code size. Refund becomes 'active' once the upgrade + /// executes successfully. + active: Balance, +} + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -217,6 +226,12 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; + /// All the pending and active refunds of a parachain. + /// + /// A parachain is eligable for a refund whenever it reduces its associated validation code. + #[pallet::storage] + pub type Refunds = StorageMap<_, Twox64Concat, ParaId, RefundInfo>, ValueQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -680,6 +695,10 @@ impl Pallet { new_code: ValidationCode, fee_payer: Option, ) -> DispatchResult { + // Before doing anything we ensure that a code upgrade is allowed at the moment for the specific + // parachain. + ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); + if let Some(payer) = fee_payer { ensure!(paras::Pallet::::para_head(para).is_some(), Error::::NotRegistered); let head = paras::Pallet::::para_head(para) @@ -695,16 +714,24 @@ impl Pallet { .deposit; if current_deposit > new_deposit { - // In case the existing deposit exceeds the required amount (due to changes in the - // pallet's configuration), any excess deposit will be removed. + // In case the existing deposit exceeds the required amount due to validation code reduction, + // the excess deposit will be returned to the caller. + + // The reason why the deposit is not instantly refunded is because schedule a code upgrade + // doesn't guarante the success of an upgrade. + // + // If we returned the depoist here a possible attack scenario would be to register the + // validation code of a parachain and then schedule a code upgrade to set the code to + // an empty blob. In such case the pre-checking process would fail so the old code would + // remain on-chain even though there is no deposit to cover it. let rebate = current_deposit.saturating_sub(new_deposit); - ::Currency::unreserve(&payer, rebate); + let mut refund = Refunds::::get(para); + refund.pending = rebate.saturating_add(refund.pending); + Refunds::::insert(para, refund); } else if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. - // NOTE: what if the upgrade fails? This should be removed or moved to another - // place. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&payer, excess)?; } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 982c173db1cf..c5d0c42b7415 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -2130,7 +2130,7 @@ impl Pallet { /// 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 { + pub fn can_upgrade_validation_code(id: ParaId) -> bool { FutureCodeHash::::get(&id).is_none() && UpgradeRestrictionSignal::::get(&id).is_none() } From cdf22f6994793802564ed42e1a9a900e38093c58 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 18 Nov 2023 19:23:07 +0100 Subject: [PATCH 008/101] progress --- .../runtime/common/src/assigned_slots/mod.rs | 1 + .../runtime/common/src/integration_tests.rs | 1 + .../runtime/common/src/paras_registrar/mod.rs | 93 ++++++++++++------- polkadot/runtime/parachains/src/paras/mod.rs | 24 ++++- 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index cb2e5083b0ac..ce4e9112fa98 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -740,6 +740,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 0d7a0bc663ad..a000ac5dce55 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -207,6 +207,7 @@ impl paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 288f717c72ab..a3ad739f1a9d 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -26,10 +26,10 @@ use frame_support::{ traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; -use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; +use primitives::{HeadData, Id as ParaId, ValidationCode, ValidationCodeHash, LOWEST_PUBLIC_ID}; use runtime_parachains::{ configuration, ensure_parachain, - paras::{self, ParaGenesisArgs, SetGoAhead}, + paras::{self, OnCodeUpgrade, ParaGenesisArgs, SetGoAhead}, Origin, ParaLifecycle, }; use sp_std::{prelude::*, result}; @@ -64,15 +64,6 @@ impl ParaInfo { } } -#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -struct RefundInfo { - /// Potential refund that is awaiting the code upgrade to successfully execute. - pending: Balance, - /// Refund for reducing the validation code size. Refund becomes 'active' once the upgrade - /// executes successfully. - active: Balance, -} - type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -226,12 +217,6 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// All the pending and active refunds of a parachain. - /// - /// A parachain is eligable for a refund whenever it reduces its associated validation code. - #[pallet::storage] - pub type Refunds = StorageMap<_, Twox64Concat, ParaId, RefundInfo>, ValueQuery>; - #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -695,8 +680,8 @@ impl Pallet { new_code: ValidationCode, fee_payer: Option, ) -> DispatchResult { - // Before doing anything we ensure that a code upgrade is allowed at the moment for the specific - // parachain. + // Before doing anything we ensure that a code upgrade is allowed at the moment for the + // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); if let Some(payer) = fee_payer { @@ -713,22 +698,7 @@ impl Pallet { .expect("Para info must be stored if head data for this parachain exists; qed") .deposit; - if current_deposit > new_deposit { - // In case the existing deposit exceeds the required amount due to validation code reduction, - // the excess deposit will be returned to the caller. - - // The reason why the deposit is not instantly refunded is because schedule a code upgrade - // doesn't guarante the success of an upgrade. - // - // If we returned the depoist here a possible attack scenario would be to register the - // validation code of a parachain and then schedule a code upgrade to set the code to - // an empty blob. In such case the pre-checking process would fail so the old code would - // remain on-chain even though there is no deposit to cover it. - let rebate = current_deposit.saturating_sub(new_deposit); - let mut refund = Refunds::::get(para); - refund.pending = rebate.saturating_add(refund.pending); - Refunds::::insert(para, refund); - } else if current_deposit < new_deposit { + if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. @@ -736,6 +706,17 @@ impl Pallet { ::Currency::reserve(&payer, excess)?; } + // In case the existing deposit exceeds the required amount due to validation code + // reduction, the excess deposit will be returned to the caller. + + // The reason why the deposit is not instantly refunded is because schedule a code + // upgrade doesn't guarante the success of an upgrade. + // + // If we returned the depoist here a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such case the pre-checking process would fail so the + // old code would remain on-chain even though there is no deposit to cover it. + ::Currency::withdraw( &payer, T::UpgradeFee::get(), @@ -797,6 +778,47 @@ impl OnNewHead for Pallet { } } +impl OnCodeUpgrade for Pallet { + fn on_code_upgrade( + id: ParaId, + old_code_hash: ValidationCodeHash, + new_code_hash: ValidationCodeHash, + ) -> Weight { + let maybe_old_code = paras::Pallet::::code_by_hash(old_code_hash); + let maybe_new_code = paras::Pallet::::code_by_hash(new_code_hash); + + if maybe_old_code.is_none() || maybe_new_code.is_none() { + return T::DbWeight::get().reads(2) + } + + let old_code = maybe_old_code.expect("Ensured above that the old code was found; qed"); + let new_code = maybe_new_code.expect("Ensured above that the new code was found; qed"); + + let head = paras::Pallet::::para_head(id).expect( + "Cannot have a code upgrade for a parachain that doesn't have its head registered; qed", + ); + + let per_byte_fee = T::DataDepositPerByte::get(); + let new_deposit = T::ParaDeposit::get() + .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) + .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); + + let current_deposit = Paras::::get(id) + .expect("Para info must be stored if head data for this parachain exists; qed") + .deposit; + + if current_deposit > new_deposit { + // Repay the para manager or the parachain itself. + + let rebate = current_deposit.saturating_sub(new_deposit); + //::Currency::unreserve(&who, rebate); + } + + // TODO: Correct weight + Weight::zero() + } +} + #[cfg(test)] mod tests { use super::*; @@ -918,6 +940,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index c5d0c42b7415..4b5b298c8eb6 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -506,6 +506,22 @@ impl OnNewHead for Tuple { } } +pub trait OnCodeUpgrade { + /// A function to execute some custom logic once the pre-chekcing is successfully completed. + /// + /// This is currently used by the registrar pallet to perform refunds upon validation code + /// size reduction. + fn on_code_upgrade(id: ParaId, prior_code_hash: ValidationCodeHash, new_code_hash: ValidationCodeHash) -> Weight; +} + +/// An empty implementation of the trait where there is no logic executed upon a successful +/// code upgrade. +impl OnCodeUpgrade for () { + fn on_code_upgrade(_id: ParaId, _prior_code_hash: ValidationCodeHash, _new_code_hash: ValidationCodeHash) -> Weight { + Weight::zero() + } +} + pub trait WeightInfo { fn force_set_current_code(c: u32) -> Weight; fn force_set_current_head(s: u32) -> Weight; @@ -603,6 +619,9 @@ pub mod pallet { /// Runtime hook for when a parachain head is updated. type OnNewHead: OnNewHead; + /// Type that executes some custom logic upon a successful code upgrade. + type OnCodeUpgrade: OnCodeUpgrade; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -2034,7 +2053,10 @@ impl Pallet { let now = >::block_number(); let weight = if let Some(prior_code_hash) = maybe_prior_code_hash { - Self::note_past_code(id, expected_at, now, prior_code_hash) + let mut weight = Self::note_past_code(id, expected_at, now, prior_code_hash); + weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, prior_code_hash, new_code_hash)); + + weight } else { log::error!(target: LOG_TARGET, "Missing prior code hash for para {:?}", &id); Weight::zero() From ab087f6f0be565cb599e49bbf62d9303b333bbe1 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 19 Nov 2023 10:05:15 +0100 Subject: [PATCH 009/101] incomplete implementation --- .../runtime/common/src/paras_registrar/mod.rs | 97 ++++++++++++------- polkadot/runtime/parachains/src/paras/mod.rs | 6 +- 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index a3ad739f1a9d..ffa7154bc24e 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -217,6 +217,13 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; + /// Stores information about the `AccountId` that will receive a refund for reducing the + /// validation code size. + /// + /// This will either be the parachain manager or the sovereign account of the parachain. + #[pallet::storage] + pub type Refunds = StorageMap<_, Twox64Concat, ParaId, T::AccountId>; + #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -674,38 +681,51 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// In case the `fee_payer` is defined fees will be charged and extra deposit will be requried. + /// If `maybe_caller` isn't specified, the caller won't be charged any fees or have its deposit + /// reserved This is intended to be used when the caller is the root origin. + /// + /// If the size of the validation is reduced and the upgrade is successful the caller will be + /// eligable for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - fee_payer: Option, + maybe_caller: Option, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); + ensure!(paras::Pallet::::para_head(para).is_some(), Error::::NotRegistered); - if let Some(payer) = fee_payer { - ensure!(paras::Pallet::::para_head(para).is_some(), Error::::NotRegistered); - let head = paras::Pallet::::para_head(para) - .expect("Ensured that the head is existant above; qed"); + let head = paras::Pallet::::para_head(para) + .expect("Ensured that the head is existant above; qed"); - let per_byte_fee = T::DataDepositPerByte::get(); - let new_deposit = T::ParaDeposit::get() - .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); + let per_byte_fee = T::DataDepositPerByte::get(); + let new_deposit = T::ParaDeposit::get() + .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) + .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - let current_deposit = Paras::::get(para) - .expect("Para info must be stored if head data for this parachain exists; qed") - .deposit; + let current_deposit = Paras::::get(para) + .expect("Para info must be stored if head data for this parachain exists; qed") + .deposit; + if let Some(caller) = maybe_caller.clone() { if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. let excess = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&payer, excess)?; + ::Currency::reserve(&caller, excess)?; } + ::Currency::withdraw( + &caller, + T::UpgradeFee::get(), + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + )?; + } + + if current_deposit > new_deposit { // In case the existing deposit exceeds the required amount due to validation code // reduction, the excess deposit will be returned to the caller. @@ -717,12 +737,20 @@ impl Pallet { // code to an empty blob. In such case the pre-checking process would fail so the // old code would remain on-chain even though there is no deposit to cover it. - ::Currency::withdraw( - &payer, - T::UpgradeFee::get(), - WithdrawReasons::FEE, - ExistenceRequirement::KeepAlive, - )?; + // We store information regarding who is receiving the refund in case the upgrade + // is completed successfully(either the parachain sovereign account or the manager). + // + // If the caller is not specified the refund will be returned to the para manager. + let who = maybe_caller.unwrap_or( + // TODO: would be probably better to refund to the para instead. + Paras::::get(para) + .ok_or(Error::::NotRegistered) + .expect( + "Parachain which is is scheduling a code upgrade must be registered; qed", + ) + .manager, + ); + Refunds::::insert(para, who); } runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) @@ -779,19 +807,13 @@ impl OnNewHead for Pallet { } impl OnCodeUpgrade for Pallet { - fn on_code_upgrade( - id: ParaId, - old_code_hash: ValidationCodeHash, - new_code_hash: ValidationCodeHash, - ) -> Weight { - let maybe_old_code = paras::Pallet::::code_by_hash(old_code_hash); + fn on_code_upgrade(id: ParaId, new_code_hash: ValidationCodeHash) -> Weight { let maybe_new_code = paras::Pallet::::code_by_hash(new_code_hash); - if maybe_old_code.is_none() || maybe_new_code.is_none() { - return T::DbWeight::get().reads(2) + if maybe_new_code.is_none() { + return T::DbWeight::get().reads(1) } - let old_code = maybe_old_code.expect("Ensured above that the old code was found; qed"); let new_code = maybe_new_code.expect("Ensured above that the new code was found; qed"); let head = paras::Pallet::::para_head(id).expect( @@ -809,13 +831,20 @@ impl OnCodeUpgrade for Pallet { if current_deposit > new_deposit { // Repay the para manager or the parachain itself. + if let Some(who) = Refunds::::get(id) { + let rebate = current_deposit.saturating_sub(new_deposit); + ::Currency::unreserve(&who, rebate); - let rebate = current_deposit.saturating_sub(new_deposit); - //::Currency::unreserve(&who, rebate); - } + // TODO: Correct weight + return Weight::zero() + }; - // TODO: Correct weight - Weight::zero() + // TODO: Correct weight + Weight::zero() + } else { + // TODO: Correct weight + Weight::zero() + } } } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 4b5b298c8eb6..f93366efd991 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -511,13 +511,13 @@ pub trait OnCodeUpgrade { /// /// This is currently used by the registrar pallet to perform refunds upon validation code /// size reduction. - fn on_code_upgrade(id: ParaId, prior_code_hash: ValidationCodeHash, new_code_hash: ValidationCodeHash) -> Weight; + fn on_code_upgrade(id: ParaId, new_code_hash: ValidationCodeHash) -> Weight; } /// An empty implementation of the trait where there is no logic executed upon a successful /// code upgrade. impl OnCodeUpgrade for () { - fn on_code_upgrade(_id: ParaId, _prior_code_hash: ValidationCodeHash, _new_code_hash: ValidationCodeHash) -> Weight { + fn on_code_upgrade(_id: ParaId, _new_code_hash: ValidationCodeHash) -> Weight { Weight::zero() } } @@ -2054,7 +2054,7 @@ impl Pallet { let weight = if let Some(prior_code_hash) = maybe_prior_code_hash { let mut weight = Self::note_past_code(id, expected_at, now, prior_code_hash); - weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, prior_code_hash, new_code_hash)); + weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, new_code_hash)); weight } else { From cef519616c30242d73e6060f8457482c7e2c1c52 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 19 Nov 2023 10:26:27 +0100 Subject: [PATCH 010/101] small fixes --- polkadot/runtime/common/src/paras_registrar/mod.rs | 10 +++------- polkadot/runtime/parachains/src/mock.rs | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index ffa7154bc24e..8c0ef74521d8 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -831,20 +831,16 @@ impl OnCodeUpgrade for Pallet { if current_deposit > new_deposit { // Repay the para manager or the parachain itself. - if let Some(who) = Refunds::::get(id) { + if let Some(who) = Refunds::::take(id) { let rebate = current_deposit.saturating_sub(new_deposit); ::Currency::unreserve(&who, rebate); // TODO: Correct weight return Weight::zero() }; - - // TODO: Correct weight - Weight::zero() - } else { - // TODO: Correct weight - Weight::zero() } + + T::DbWeight::get().reads(2) } } diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 9df54bf29d3e..f94b7f79a20d 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -215,6 +215,7 @@ impl crate::paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = TestNextSessionRotation; + type OnCodeUpgrade = (); type OnNewHead = (); } From d7eba240b558873654eb73465fe204e308afdee6 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 19 Nov 2023 12:22:25 +0100 Subject: [PATCH 011/101] new logic almost complete --- polkadot/runtime/common/src/paras_registrar/mod.rs | 9 ++++----- polkadot/runtime/rococo/src/lib.rs | 10 ++++++++++ polkadot/runtime/test-runtime/src/lib.rs | 1 + polkadot/runtime/westend/src/lib.rs | 11 +++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 8c0ef74521d8..b03587978617 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -685,7 +685,7 @@ impl Pallet { /// reserved This is intended to be used when the caller is the root origin. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be - /// eligable for receiving back a portion of their deposit that is no longer required. + /// eligible for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, @@ -742,7 +742,6 @@ impl Pallet { // // If the caller is not specified the refund will be returned to the para manager. let who = maybe_caller.unwrap_or( - // TODO: would be probably better to refund to the para instead. Paras::::get(para) .ok_or(Error::::NotRegistered) .expect( @@ -835,9 +834,9 @@ impl OnCodeUpgrade for Pallet { let rebate = current_deposit.saturating_sub(new_deposit); ::Currency::unreserve(&who, rebate); - // TODO: Correct weight - return Weight::zero() - }; + // TODO: Should probably benchmark instead of hardcoding the weight. + return T::DbWeight::get().reads_writes(3, 1) + } } T::DbWeight::get().reads(2) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 697d22c311ae..a0414597d21d 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -892,6 +892,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type OnCodeUpgrade = Registrar; type OnNewHead = Registrar; } @@ -1010,8 +1011,15 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, +); + parameter_types! { pub const ParaDeposit: Balance = 40 * UNITS; + pub const UpgradeFee: Balance = 2 * UNITS; } impl paras_registrar::Config for Runtime { @@ -1020,6 +1028,8 @@ impl paras_registrar::Config for Runtime { type Currency = Balances; type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; + type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 596e65eca068..5c6738f7b754 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -544,6 +544,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index fe9ed22f4375..4b853d8203d5 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1139,6 +1139,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type OnCodeUpgrade = Registrar; type OnNewHead = (); } @@ -1259,8 +1260,16 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<300>; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, +); + + parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; + pub const UpgradeFee: Balance = 50 * CENTS; pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); } @@ -1271,6 +1280,8 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; + type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } From 3758a15510e477087cb7f9f3520db2960e596c0f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 19 Nov 2023 13:34:27 +0100 Subject: [PATCH 012/101] cleanup --- .../runtime/common/src/integration_tests.rs | 2 -- .../runtime/common/src/paras_registrar/mod.rs | 35 ++++++++----------- polkadot/runtime/parachains/src/paras/mod.rs | 5 +-- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 3 +- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index a000ac5dce55..449eeeeedd88 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -608,8 +608,6 @@ fn para_upgrade_initiated_by_manager_works() { ); // Schedule a para upgrade to set the validation code to a new one which is twice the size. - // The only reason why this is callable by the para manager is because the parachain isn't - // locked. let validation_code = Registrar::worst_validation_code(); assert_ok!(Registrar::schedule_code_upgrade( signed(1), diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index b03587978617..06a5306dbecf 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -150,8 +150,9 @@ pub mod pallet { #[pallet::constant] type UpgradeFee: Get>; - /// Type used to get the sovereign account of a parachain. /// - /// This is used to enable reserving a deposit from parachains. + /// Type used to get the sovereign account of a parachain. + /// + /// This is used to enable reserving or refunding deposit from parachains. type SovereignAccountOf: ConvertLocation; /// Weight Information for the Extrinsics in the Pallet @@ -425,7 +426,10 @@ pub mod pallet { /// unlocked. /// /// In case the call is made by the parachain manager or the parachain itself the upgrade - /// fee and potential storage costs will be reserved. + /// fee will be charged. + /// + /// The caller will receive a refund or be required to reserve an additional deposit, + /// depending on the size of the new validation code. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))] pub fn schedule_code_upgrade( @@ -681,8 +685,8 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `maybe_caller` isn't specified, the caller won't be charged any fees or have its deposit - /// reserved This is intended to be used when the caller is the root origin. + /// If `maybe_caller` isn't specified, the caller won't be charged any fees or have its deposit + /// reserved. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. @@ -694,19 +698,17 @@ impl Pallet { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); - ensure!(paras::Pallet::::para_head(para).is_some(), Error::::NotRegistered); - let head = paras::Pallet::::para_head(para) - .expect("Ensured that the head is existant above; qed"); + let head = + paras::Pallet::::para_head(para).map_or(Err(Error::::NotRegistered), Ok)?; let per_byte_fee = T::DataDepositPerByte::get(); let new_deposit = T::ParaDeposit::get() .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - let current_deposit = Paras::::get(para) - .expect("Para info must be stored if head data for this parachain exists; qed") - .deposit; + let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let current_deposit = info.deposit; if let Some(caller) = maybe_caller.clone() { if current_deposit < new_deposit { @@ -741,14 +743,7 @@ impl Pallet { // is completed successfully(either the parachain sovereign account or the manager). // // If the caller is not specified the refund will be returned to the para manager. - let who = maybe_caller.unwrap_or( - Paras::::get(para) - .ok_or(Error::::NotRegistered) - .expect( - "Parachain which is is scheduling a code upgrade must be registered; qed", - ) - .manager, - ); + let who = maybe_caller.unwrap_or(info.manager); Refunds::::insert(para, who); } @@ -814,7 +809,6 @@ impl OnCodeUpgrade for Pallet { } let new_code = maybe_new_code.expect("Ensured above that the new code was found; qed"); - let head = paras::Pallet::::para_head(id).expect( "Cannot have a code upgrade for a parachain that doesn't have its head registered; qed", ); @@ -834,7 +828,6 @@ impl OnCodeUpgrade for Pallet { let rebate = current_deposit.saturating_sub(new_deposit); ::Currency::unreserve(&who, rebate); - // TODO: Should probably benchmark instead of hardcoding the weight. return T::DbWeight::get().reads_writes(3, 1) } } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index f93366efd991..7723ed647500 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -619,7 +619,7 @@ pub mod pallet { /// Runtime hook for when a parachain head is updated. type OnNewHead: OnNewHead; - /// Type that executes some custom logic upon a successful code upgrade. + /// Type that executes some custom logic upon a successful code upgrade. type OnCodeUpgrade: OnCodeUpgrade; /// Weight information for extrinsics in this pallet. @@ -2054,7 +2054,8 @@ impl Pallet { let weight = if let Some(prior_code_hash) = maybe_prior_code_hash { let mut weight = Self::note_past_code(id, expected_at, now, prior_code_hash); - weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, new_code_hash)); + weight = + weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, new_code_hash)); weight } else { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index a0414597d21d..d6b7fe5b61ef 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -99,7 +99,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::PayOverXcm; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 4b853d8203d5..749ad868490e 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -99,7 +99,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::PayOverXcm; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1266,7 +1266,6 @@ pub type LocationToAccountId = ( Account32Hash<(), AccountId>, ); - parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; pub const UpgradeFee: Balance = 50 * CENTS; From 30a051a848f9f6a5e94acc4369c07f2598143187 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 20 Nov 2023 17:06:14 +0100 Subject: [PATCH 013/101] complete first test & grammar --- .../runtime/common/src/integration_tests.rs | 71 +++++++++++++++---- .../runtime/common/src/paras_registrar/mod.rs | 13 ++-- polkadot/runtime/parachains/src/paras/mod.rs | 9 ++- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 449eeeeedd88..36afc2a394fc 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -207,7 +207,7 @@ impl paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; - type OnCodeUpgrade = (); + type OnCodeUpgrade = Registrar; type OnNewHead = (); } @@ -358,7 +358,9 @@ fn run_to_block(n: u32) { AllPalletsWithSystem::on_finalize(block_number); System::set_block_number(block_number + 1); maybe_new_session(block_number + 1); + Paras::initializer_initialize(block_number + 1); AllPalletsWithSystem::on_initialize(block_number + 1); + Paras::initializer_finalize(block_number + 1); } } @@ -574,7 +576,7 @@ fn basic_end_to_end_works() { fn para_upgrade_initiated_by_manager_works() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ - let para_1 = LOWEST_PUBLIC_ID; + let para_id = LOWEST_PUBLIC_ID; const START_SESSION_INDEX: SessionIndex = 1; run_to_session(START_SESSION_INDEX); @@ -582,24 +584,24 @@ fn para_upgrade_initiated_by_manager_works() { let free_balance = 1_000_000_000; Balances::make_free_balance_be(&account_id(1), 1_000_000_000); // Register an on demand parachain - let code_size = 1024 * 1024; + let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); - let validation_code = validation_code(code_size); + let code_1 = validation_code(code_size); assert_ok!(Registrar::reserve(signed(1))); assert_ok!(Registrar::register( signed(1), - ParaId::from(para_1), + ParaId::from(para_id), genesis_head.clone(), - validation_code.clone(), + code_1.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX); // The para should be onboarding. - assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Onboarding)); + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); - assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread)); + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; assert_eq!( @@ -608,20 +610,63 @@ fn para_upgrade_initiated_by_manager_works() { ); // Schedule a para upgrade to set the validation code to a new one which is twice the size. - let validation_code = Registrar::worst_validation_code(); + code_size *= 2; + let code_2 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( signed(1), - ParaId::from(para_1), - validation_code.clone(), + ParaId::from(para_id), + code_2.clone(), )); + conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 2); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 4); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + // The reserved deposit should cover for the size difference of the new validation code. - let total_bytes_stored = 2 * code_size as u32 + genesis_head.0.len() as u32; + let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); + + // After successfully upgrading the validation code to twice the size of the previous one, + // we will now proceed to upgrade the validation code to a smaller size. It is expected that + // the parachain manager will receive a refund upon the successful completion of the + // upgrade. + + code_size /= 2; + let code_3 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + signed(1), + ParaId::from(para_id), + code_3.clone(), + )); + // Since the para manager iniciated the upgrade he should be the one that will receive the + // refund in case the upgrade is successful. + assert_eq!(crate::paras_registrar::Refunds::::get(para_id), Some(account_id(1))); + conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 4); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 6); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); + assert_eq!(Paras::current_code(¶_id), Some(code_3.clone())); + + // The reserved deposit should cover only the size of the new validation code. + let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // An additional upgrade fee should also be deducted from the caller's balance. + assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (2 * UpgradeFee::get())); + // The para manager shouldn't be marked as a 'refund receiver' anymore. + assert!(crate::paras_registrar::Refunds::::get(para_id).is_none()); }); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 06a5306dbecf..1bc567bb44a0 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -731,16 +731,17 @@ impl Pallet { // In case the existing deposit exceeds the required amount due to validation code // reduction, the excess deposit will be returned to the caller. - // The reason why the deposit is not instantly refunded is because schedule a code - // upgrade doesn't guarante the success of an upgrade. + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of an upgrade. // - // If we returned the depoist here a possible attack scenario would be to register + // If we returned the deposit here, a possible attack scenario would be to register // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such case the pre-checking process would fail so the + // code to an empty blob. In such a case, the pre-checking process would fail, so the // old code would remain on-chain even though there is no deposit to cover it. - // We store information regarding who is receiving the refund in case the upgrade - // is completed successfully(either the parachain sovereign account or the manager). + // We store information about the recipient of the refund in the event that the upgrade + // is successfully completed, which could be either the parachain sovereign account or + // the manager. // // If the caller is not specified the refund will be returned to the para manager. let who = maybe_caller.unwrap_or(info.manager); diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 7723ed647500..d0af66305444 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -1240,13 +1240,15 @@ impl Pallet { } /// Called by the initializer to initialize the paras pallet. - pub(crate) fn initializer_initialize(now: BlockNumberFor) -> Weight { + // TODO: don't affect visibility + pub fn initializer_initialize(now: BlockNumberFor) -> Weight { let weight = Self::prune_old_code(now); weight + Self::process_scheduled_upgrade_changes(now) } /// Called by the initializer to finalize the paras pallet. - pub(crate) fn initializer_finalize(now: BlockNumberFor) { + // TODO: don't affect visibility + pub fn initializer_finalize(now: BlockNumberFor) { Self::process_scheduled_upgrade_cooldowns(now); } @@ -1262,7 +1264,8 @@ impl Pallet { } /// The validation code of live para. - pub(crate) fn current_code(para_id: &ParaId) -> Option { + // TODO: don't affect visibility + pub fn current_code(para_id: &ParaId) -> Option { Self::current_code_hash(para_id).and_then(|code_hash| { let code = CodeByHash::::get(&code_hash); if code.is_none() { From bf1b5e35940f7145fbb091e49d2c1af6aee8b728 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 20 Nov 2023 19:02:22 +0100 Subject: [PATCH 014/101] new test case --- .../runtime/common/src/integration_tests.rs | 80 ++++++++++++------- polkadot/runtime/common/src/mock.rs | 3 +- .../runtime/common/src/paras_registrar/mod.rs | 14 ++-- 3 files changed, 61 insertions(+), 36 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 36afc2a394fc..04458e1b9346 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -410,7 +410,7 @@ fn basic_end_to_end_works() { genesis_head.clone(), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); assert_ok!(Registrar::reserve(signed(2))); assert_ok!(Registrar::register( signed(2), @@ -580,22 +580,22 @@ fn para_upgrade_initiated_by_manager_works() { const START_SESSION_INDEX: SessionIndex = 1; run_to_session(START_SESSION_INDEX); - // User 1 will own parachains + // User 1 will own a parachain let free_balance = 1_000_000_000; Balances::make_free_balance_be(&account_id(1), 1_000_000_000); // Register an on demand parachain let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); - let code_1 = validation_code(code_size); + let code_0 = validation_code(code_size); assert_ok!(Registrar::reserve(signed(1))); assert_ok!(Registrar::register( signed(1), ParaId::from(para_id), genesis_head.clone(), - code_1.clone(), + code_0.clone(), )); - conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&code_0, VALIDATORS, START_SESSION_INDEX, true); // The para should be onboarding. assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); @@ -609,21 +609,21 @@ fn para_upgrade_initiated_by_manager_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // Schedule a para upgrade to set the validation code to a new one which is twice the size. + // CASE 1: Schedule a para upgrade to set the validation code to a new one which is twice the size. code_size *= 2; - let code_2 = validation_code(code_size); + let code_1 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( signed(1), ParaId::from(para_id), - code_2.clone(), + code_1.clone(), )); - conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 2); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 4); // Force a new head to enact the code upgrade. assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); - assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); // The reserved deposit should cover for the size difference of the new validation code. let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; @@ -634,28 +634,28 @@ fn para_upgrade_initiated_by_manager_works() { // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); - // After successfully upgrading the validation code to twice the size of the previous one, - // we will now proceed to upgrade the validation code to a smaller size. It is expected that - // the parachain manager will receive a refund upon the successful completion of the + // CASE 2: After successfully upgrading the validation code to twice the size of the previous + // one, we will now proceed to upgrade the validation code to a smaller size. It is expected + // that the parachain manager will receive a refund upon the successful completion of the // upgrade. code_size /= 2; - let code_3 = validation_code(code_size); + let code_2 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( signed(1), ParaId::from(para_id), - code_3.clone(), + code_2.clone(), )); // Since the para manager iniciated the upgrade he should be the one that will receive the // refund in case the upgrade is successful. assert_eq!(crate::paras_registrar::Refunds::::get(para_id), Some(account_id(1))); - conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 4); + conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 6); // Force a new head to enact the code upgrade. assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); - assert_eq!(Paras::current_code(¶_id), Some(code_3.clone())); + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); // The reserved deposit should cover only the size of the new validation code. let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; @@ -667,6 +667,30 @@ fn para_upgrade_initiated_by_manager_works() { assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (2 * UpgradeFee::get())); // The para manager shouldn't be marked as a 'refund receiver' anymore. assert!(crate::paras_registrar::Refunds::::get(para_id).is_none()); + + // CASE 3: Para manager won't get refunded if the code upgrade fails + let code_3 = validation_code(42); + assert_ok!(Registrar::schedule_code_upgrade( + signed(1), + ParaId::from(para_id), + code_3.clone(), + )); + assert_eq!(crate::paras_registrar::Refunds::::get(para_id), Some(account_id(1))); + // Reject the new validation code. + conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 6, false); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 8); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); + // The upgrade failed and the para manager shouldn't get a refund. + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // Even though the upgrade failed the upgrade fee is deducted from the caller's balance. + assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (3 * UpgradeFee::get())); }); } @@ -745,7 +769,7 @@ fn competing_slots() { )); } // The code undergoing the prechecking is the same for all paras. - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Start a new auction in the future let duration = 149u32; @@ -847,7 +871,7 @@ fn competing_bids() { )); } // The code undergoing the prechecking is the same for all paras. - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Finish registration of paras. run_to_session(START_SESSION_INDEX + 2); @@ -954,7 +978,7 @@ fn basic_swap_works() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); let validation_code = test_validation_code(20); assert_ok!(Registrar::reserve(signed(2))); @@ -964,7 +988,7 @@ fn basic_swap_works() { test_genesis_head(20), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Paras should be onboarding assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Onboarding)); @@ -1116,7 +1140,7 @@ fn parachain_swap_works() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); let validation_code = test_validation_code(20); assert_ok!(Registrar::reserve(signed(2))); @@ -1126,7 +1150,7 @@ fn parachain_swap_works() { test_genesis_head(20), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Paras should be onboarding assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Onboarding)); @@ -1294,7 +1318,7 @@ fn crowdloan_ending_period_bid() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); let validation_code = test_validation_code(20); assert_ok!(Registrar::reserve(signed(2))); @@ -1304,7 +1328,7 @@ fn crowdloan_ending_period_bid() { test_genesis_head(20), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Paras should be onboarding assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Onboarding)); @@ -1426,7 +1450,7 @@ fn auction_bid_requires_registered_para() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Still can't bid until it is fully onboarded assert_noop!( @@ -1492,7 +1516,7 @@ fn gap_bids_work() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Onboarded on Session 2 run_to_session(START_SESSION_INDEX + 2); @@ -1662,7 +1686,7 @@ fn cant_bid_on_existing_lease_periods() { test_genesis_head(10), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Start a new auction in the future let starting_block = System::block_number(); diff --git a/polkadot/runtime/common/src/mock.rs b/polkadot/runtime/common/src/mock.rs index c9e3a8c39f12..0462bd334f18 100644 --- a/polkadot/runtime/common/src/mock.rs +++ b/polkadot/runtime/common/src/mock.rs @@ -247,12 +247,13 @@ pub fn conclude_pvf_checking( validation_code: &ValidationCode, validators: &[Sr25519Keyring], session_index: SessionIndex, + accept: bool, ) { let num_required = primitives::supermajority_threshold(validators.len()); validators.iter().enumerate().take(num_required).for_each(|(idx, key)| { let validator_index = idx as u32; let statement = PvfCheckStatement { - accept: true, + accept, subject: validation_code.hash(), session_index, validator_index: validator_index.into(), diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 1bc567bb44a0..abcf40242c5f 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1112,7 +1112,7 @@ mod tests { test_genesis_head(32), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); run_to_session(START_SESSION_INDEX + 2); // It is now a parathread (on-demand parachain). @@ -1156,7 +1156,7 @@ mod tests { test_genesis_head(32), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); run_to_session(START_SESSION_INDEX + 2); assert!(Parachains::is_parathread(para_id)); @@ -1266,7 +1266,7 @@ mod tests { test_genesis_head(32), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); run_to_session(START_SESSION_INDEX + 2); assert!(Parachains::is_parathread(para_id)); @@ -1294,7 +1294,7 @@ mod tests { test_genesis_head(32), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); run_to_session(START_SESSION_INDEX + 2); assert!(Parachains::is_parathread(para_id)); @@ -1335,7 +1335,7 @@ mod tests { test_genesis_head(max_head_size() as usize), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); run_to_session(START_SESSION_INDEX + 2); @@ -1400,7 +1400,7 @@ mod tests { test_genesis_head(max_head_size() as usize), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX + 6); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX + 6, true); run_to_session(START_SESSION_INDEX + 8); @@ -1508,7 +1508,7 @@ mod tests { test_genesis_head(32), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); // Cannot swap assert_ok!(Registrar::swap(RuntimeOrigin::root(), para_1, para_2)); From eed4c4d629b12ae7251ed4ceb4126cb0e1cc28ef Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 20 Nov 2023 21:10:42 +0100 Subject: [PATCH 015/101] better approach --- .../runtime/common/src/integration_tests.rs | 17 ++--- .../runtime/common/src/paras_registrar/mod.rs | 75 +++++++++++-------- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 04458e1b9346..1b5218463376 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -609,7 +609,8 @@ fn para_upgrade_initiated_by_manager_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Schedule a para upgrade to set the validation code to a new one which is twice the size. + // CASE 1: Schedule a para upgrade to set the validation code to a new one which is twice + // the size. code_size *= 2; let code_1 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( @@ -634,10 +635,10 @@ fn para_upgrade_initiated_by_manager_works() { // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); - // CASE 2: After successfully upgrading the validation code to twice the size of the previous - // one, we will now proceed to upgrade the validation code to a smaller size. It is expected - // that the parachain manager will receive a refund upon the successful completion of the - // upgrade. + // CASE 2: After successfully upgrading the validation code to twice the size of the + // previous one, we will now proceed to upgrade the validation code to a smaller size. It is + // expected that the parachain manager will receive a refund upon the successful completion + // of the upgrade. code_size /= 2; let code_2 = validation_code(code_size); @@ -646,9 +647,6 @@ fn para_upgrade_initiated_by_manager_works() { ParaId::from(para_id), code_2.clone(), )); - // Since the para manager iniciated the upgrade he should be the one that will receive the - // refund in case the upgrade is successful. - assert_eq!(crate::paras_registrar::Refunds::::get(para_id), Some(account_id(1))); conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); // After two more sessions the parachain can be upgraded. @@ -665,8 +663,6 @@ fn para_upgrade_initiated_by_manager_works() { ); // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (2 * UpgradeFee::get())); - // The para manager shouldn't be marked as a 'refund receiver' anymore. - assert!(crate::paras_registrar::Refunds::::get(para_id).is_none()); // CASE 3: Para manager won't get refunded if the code upgrade fails let code_3 = validation_code(42); @@ -675,7 +671,6 @@ fn para_upgrade_initiated_by_manager_works() { ParaId::from(para_id), code_3.clone(), )); - assert_eq!(crate::paras_registrar::Refunds::::get(para_id), Some(account_id(1))); // Reject the new validation code. conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 6, false); diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index abcf40242c5f..09004db45eed 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -50,7 +50,7 @@ use xcm_executor::traits::ConvertLocation; pub struct ParaInfo { /// The account that has placed a deposit for registering this para. pub(crate) manager: Account, - /// The amount reserved by the `manager` account for the registration. + /// The total amount reserved for this para. deposit: Balance, /// Whether the para registration should be locked from being controlled by the manager. /// None means the lock had not been explicitly set, and should be treated as false. @@ -218,12 +218,12 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores information about the `AccountId` that will receive a refund for reducing the - /// validation code size. + /// Stores information about the `AccountId` that made a deposit for the purpose of registering a + /// parachain. /// /// This will either be the parachain manager or the sovereign account of the parachain. #[pallet::storage] - pub type Refunds = StorageMap<_, Twox64Concat, ParaId, T::AccountId>; + pub type Depositor = StorageMap<_, Twox64Concat, ParaId, T::AccountId>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -657,6 +657,7 @@ impl Pallet { let info = ParaInfo { manager: who.clone(), deposit, locked: None }; Paras::::insert(id, info); + Depositor::::insert(id, who.clone()); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -707,16 +708,34 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let current_depositor = Depositor::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; if let Some(caller) = maybe_caller.clone() { - if current_deposit < new_deposit { + let same_depositor = caller == current_depositor; + let deposit_increased = current_deposit < new_deposit; + + if same_depositor && deposit_increased { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&caller, excess)?; + }else if !same_depositor { + // If the caller is not the one that had their funds reserved for this parachain we + // will unreserve all the funds from the original depositor and reserve the required + // amount from the new depositor. + // + // The primary reason for this is to more easily track the account that has its deposit + // reserved for this parachain. Reserving the deposit across both the para manager and + // the parachain itself could unnecessarily complicate refunds. + + ::Currency::reserve(&caller, new_deposit)?; + + // TODO: don't do instant refunds for the same reason as described bellow. + ::Currency::unreserve(¤t_depositor, current_deposit); + Depositor::::insert(para, caller.clone()); } ::Currency::withdraw( @@ -727,26 +746,20 @@ impl Pallet { )?; } - if current_deposit > new_deposit { - // In case the existing deposit exceeds the required amount due to validation code - // reduction, the excess deposit will be returned to the caller. - - // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of an upgrade. - // - // If we returned the deposit here, a possible attack scenario would be to register - // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such a case, the pre-checking process would fail, so the - // old code would remain on-chain even though there is no deposit to cover it. - - // We store information about the recipient of the refund in the event that the upgrade - // is successfully completed, which could be either the parachain sovereign account or - // the manager. - // - // If the caller is not specified the refund will be returned to the para manager. - let who = maybe_caller.unwrap_or(info.manager); - Refunds::::insert(para, who); - } + // In case the existing deposit exceeds the required amount due to validation code + // reduction, the excess deposit will be returned to the caller. + // + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of an upgrade. + // + // If we returned the deposit here, a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such a case, the pre-checking process would fail, so the + // old code would remain on-chain even though there is no deposit to cover it. + + // Update the deposit to the new appropriate amount. + info.deposit = new_deposit; + Paras::::insert(para, info); runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) } @@ -824,13 +837,11 @@ impl OnCodeUpgrade for Pallet { .deposit; if current_deposit > new_deposit { - // Repay the para manager or the parachain itself. - if let Some(who) = Refunds::::take(id) { - let rebate = current_deposit.saturating_sub(new_deposit); - ::Currency::unreserve(&who, rebate); + let rebate = current_deposit.saturating_sub(new_deposit); + let depositor = Depositor::::get(id).expect("The depositor must be known for each parachain; qed"); + ::Currency::unreserve(&depositor, rebate); - return T::DbWeight::get().reads_writes(3, 1) - } + return T::DbWeight::get().reads_writes(3, 1) } T::DbWeight::get().reads(2) From f852348eb6d2280536f6f3c206b1c39f6e6625a5 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 08:30:32 +0100 Subject: [PATCH 016/101] refactor --- .../runtime/common/src/paras_registrar/mod.rs | 102 +++++++++++------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 09004db45eed..1191a5ff5b69 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -64,6 +64,17 @@ impl ParaInfo { } } +#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] +pub struct RefundInfo { + /// The `AccountId` that has its deposit reserved for this parachain. + /// + /// This will either be the parachain manager or the sovereign account of the parachain. + depositor: AccountId, + /// In case there is a pending refund, this stores information about the account that will + /// receive the refund upon a successful code upgrade. + pending_refund: Option, +} + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -218,12 +229,10 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores information about the `AccountId` that made a deposit for the purpose of registering a - /// parachain. - /// - /// This will either be the parachain manager or the sovereign account of the parachain. + /// Stores all the necessary information for initiating deposit refunds whenever the validation + /// code is reduced for any parachain. #[pallet::storage] - pub type Depositor = StorageMap<_, Twox64Concat, ParaId, T::AccountId>; + pub type Refunds = StorageMap<_, Twox64Concat, ParaId, RefundInfo>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -655,9 +664,10 @@ impl Pallet { ::Currency::unreserve(&who, rebate); }; let info = ParaInfo { manager: who.clone(), deposit, locked: None }; + let refund_info = RefundInfo { depositor: who.clone(), pending_refund: None }; Paras::::insert(id, info); - Depositor::::insert(id, who.clone()); + Refunds::::insert(id, refund_info); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -709,33 +719,47 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let current_depositor = Depositor::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let refund_info = Refunds::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let current_deposit = info.deposit; + let current_depositor = refund_info.depositor; if let Some(caller) = maybe_caller.clone() { - let same_depositor = caller == current_depositor; - let deposit_increased = current_deposit < new_deposit; + if caller != current_depositor { + // If the caller is not the one that had their funds reserved for this parachain we + // will unreserve all the funds from the original depositor and reserve the required + // amount from the new depositor. + // + // The primary reason for this is to more easily track the account that has its + // deposit reserved for this parachain. Reserving the deposit across both the para + // manager and the parachain itself could unnecessarily complicate refunds. - if same_depositor && deposit_increased { + ::Currency::reserve(&caller, new_deposit)?; + } + + if current_deposit < new_deposit { // An additional deposit is required to cover for the new validation code which has // a greater size compared to the old one. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&caller, excess)?; - }else if !same_depositor { - // If the caller is not the one that had their funds reserved for this parachain we - // will unreserve all the funds from the original depositor and reserve the required - // amount from the new depositor. + } else { + // In case the existing deposit exceeds the required amount due to validation code + // reduction, the excess deposit will be returned to the caller. // - // The primary reason for this is to more easily track the account that has its deposit - // reserved for this parachain. Reserving the deposit across both the para manager and - // the parachain itself could unnecessarily complicate refunds. - - ::Currency::reserve(&caller, new_deposit)?; - - // TODO: don't do instant refunds for the same reason as described bellow. - ::Currency::unreserve(¤t_depositor, current_deposit); - Depositor::::insert(para, caller.clone()); + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of an upgrade. + // + // If we returned the deposit here, a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such a case, the pre-checking process would fail, so + // the old code would remain on-chain even though there is no deposit to cover it. + + let refund_info = RefundInfo { + depositor: caller.clone(), + pending_refund: Some(current_depositor), + }; + Refunds::::insert(para, refund_info); } ::Currency::withdraw( @@ -746,17 +770,6 @@ impl Pallet { )?; } - // In case the existing deposit exceeds the required amount due to validation code - // reduction, the excess deposit will be returned to the caller. - // - // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of an upgrade. - // - // If we returned the deposit here, a possible attack scenario would be to register - // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such a case, the pre-checking process would fail, so the - // old code would remain on-chain even though there is no deposit to cover it. - // Update the deposit to the new appropriate amount. info.deposit = new_deposit; Paras::::insert(para, info); @@ -837,11 +850,17 @@ impl OnCodeUpgrade for Pallet { .deposit; if current_deposit > new_deposit { - let rebate = current_deposit.saturating_sub(new_deposit); - let depositor = Depositor::::get(id).expect("The depositor must be known for each parachain; qed"); - ::Currency::unreserve(&depositor, rebate); + let mut refund_info = + Refunds::::get(id).expect("The depositor must be known for each parachain; qed"); - return T::DbWeight::get().reads_writes(3, 1) + if let Some(who) = refund_info.pending_refund { + let rebate = current_deposit.saturating_sub(new_deposit); + ::Currency::unreserve(&who, rebate); + refund_info.pending_refund = None; + Refunds::::insert(id, refund_info); + + return T::DbWeight::get().reads_writes(3, 1) + } } T::DbWeight::get().reads(2) @@ -1411,7 +1430,12 @@ mod tests { test_genesis_head(max_head_size() as usize), validation_code.clone(), )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX + 6, true); + conclude_pvf_checking::( + &validation_code, + VALIDATORS, + START_SESSION_INDEX + 6, + true, + ); run_to_session(START_SESSION_INDEX + 8); From 04d22e0ef86b8f2517e3c601a6b95c0934c48b2d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 10:14:14 +0100 Subject: [PATCH 017/101] cleanup & small fixes --- .../runtime/common/src/integration_tests.rs | 19 ++++-- .../runtime/common/src/paras_registrar/mod.rs | 63 +++++++------------ polkadot/runtime/parachains/src/paras/mod.rs | 7 +-- 3 files changed, 40 insertions(+), 49 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 1b5218463376..736ff2679db3 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -586,6 +586,7 @@ fn para_upgrade_initiated_by_manager_works() { // Register an on demand parachain let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); + let head_size = genesis_head.0.len(); let code_0 = validation_code(code_size); assert_ok!(Registrar::reserve(signed(1))); @@ -603,7 +604,7 @@ fn para_upgrade_initiated_by_manager_works() { run_to_session(START_SESSION_INDEX + 2); assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); // The deposit should be appropriately taken. - let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; + let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) @@ -623,11 +624,15 @@ fn para_upgrade_initiated_by_manager_works() { // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 4); // Force a new head to enact the code upgrade. - assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); // The reserved deposit should cover for the size difference of the new validation code. - let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; + let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) @@ -652,11 +657,15 @@ fn para_upgrade_initiated_by_manager_works() { // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 6); // Force a new head to enact the code upgrade. - assert_ok!(Paras::force_note_new_head(RuntimeOrigin::root(), para_id, Default::default())); + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); // The reserved deposit should cover only the size of the new validation code. - let total_bytes_stored = code_size as u32 + genesis_head.0.len() as u32; + let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 1191a5ff5b69..2331f55d9773 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -26,7 +26,7 @@ use frame_support::{ traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; -use primitives::{HeadData, Id as ParaId, ValidationCode, ValidationCodeHash, LOWEST_PUBLIC_ID}; +use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; use runtime_parachains::{ configuration, ensure_parachain, paras::{self, OnCodeUpgrade, ParaGenesisArgs, SetGoAhead}, @@ -65,14 +65,14 @@ impl ParaInfo { } #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -pub struct RefundInfo { +pub struct RefundInfo { /// The `AccountId` that has its deposit reserved for this parachain. /// /// This will either be the parachain manager or the sovereign account of the parachain. depositor: AccountId, - /// In case there is a pending refund, this stores information about the account that will - /// receive the refund upon a successful code upgrade. - pending_refund: Option, + /// In case there is a pending refund, this stores information about the account and the + /// amount that will be refunded upon a successful code upgrade. + pending_refund: Option<(AccountId, Balance)>, } type BalanceOf = @@ -232,7 +232,8 @@ pub mod pallet { /// Stores all the necessary information for initiating deposit refunds whenever the validation /// code is reduced for any parachain. #[pallet::storage] - pub type Refunds = StorageMap<_, Twox64Concat, ParaId, RefundInfo>; + pub type Refunds = + StorageMap<_, Twox64Concat, ParaId, RefundInfo>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -743,7 +744,7 @@ impl Pallet { let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&caller, excess)?; - } else { + } else if current_deposit > new_deposit { // In case the existing deposit exceeds the required amount due to validation code // reduction, the excess deposit will be returned to the caller. // @@ -755,9 +756,14 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. + println!("{:?}", current_deposit); + println!("{:?}", new_deposit); let refund_info = RefundInfo { depositor: caller.clone(), - pending_refund: Some(current_depositor), + pending_refund: Some(( + current_depositor, + current_deposit.saturating_sub(new_deposit), + )), }; Refunds::::insert(para, refund_info); } @@ -828,42 +834,19 @@ impl OnNewHead for Pallet { } impl OnCodeUpgrade for Pallet { - fn on_code_upgrade(id: ParaId, new_code_hash: ValidationCodeHash) -> Weight { - let maybe_new_code = paras::Pallet::::code_by_hash(new_code_hash); - - if maybe_new_code.is_none() { - return T::DbWeight::get().reads(1) - } - - let new_code = maybe_new_code.expect("Ensured above that the new code was found; qed"); - let head = paras::Pallet::::para_head(id).expect( - "Cannot have a code upgrade for a parachain that doesn't have its head registered; qed", - ); - - let per_byte_fee = T::DataDepositPerByte::get(); - let new_deposit = T::ParaDeposit::get() - .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - - let current_deposit = Paras::::get(id) - .expect("Para info must be stored if head data for this parachain exists; qed") - .deposit; + fn on_code_upgrade(id: ParaId) -> Weight { + let mut refund_info = + Refunds::::get(id).expect("The depositor must be known for each parachain; qed"); - if current_deposit > new_deposit { - let mut refund_info = - Refunds::::get(id).expect("The depositor must be known for each parachain; qed"); - - if let Some(who) = refund_info.pending_refund { - let rebate = current_deposit.saturating_sub(new_deposit); - ::Currency::unreserve(&who, rebate); - refund_info.pending_refund = None; - Refunds::::insert(id, refund_info); + if let Some((who, rebate)) = refund_info.pending_refund { + ::Currency::unreserve(&who, rebate); + refund_info.pending_refund = None; + Refunds::::insert(id, refund_info); - return T::DbWeight::get().reads_writes(3, 1) - } + return T::DbWeight::get().reads_writes(2, 1) } - T::DbWeight::get().reads(2) + T::DbWeight::get().reads(1) } } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index d0af66305444..099926eaa1d2 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -511,13 +511,13 @@ pub trait OnCodeUpgrade { /// /// This is currently used by the registrar pallet to perform refunds upon validation code /// size reduction. - fn on_code_upgrade(id: ParaId, new_code_hash: ValidationCodeHash) -> Weight; + fn on_code_upgrade(id: ParaId) -> Weight; } /// An empty implementation of the trait where there is no logic executed upon a successful /// code upgrade. impl OnCodeUpgrade for () { - fn on_code_upgrade(_id: ParaId, _new_code_hash: ValidationCodeHash) -> Weight { + fn on_code_upgrade(_id: ParaId) -> Weight { Weight::zero() } } @@ -2057,8 +2057,7 @@ impl Pallet { let weight = if let Some(prior_code_hash) = maybe_prior_code_hash { let mut weight = Self::note_past_code(id, expected_at, now, prior_code_hash); - weight = - weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id, new_code_hash)); + weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id)); weight } else { From bdba7cb1110ee352d8596899ed7ef4a48dc96e93 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 11:52:49 +0100 Subject: [PATCH 018/101] new test & fixes --- .../runtime/common/src/integration_tests.rs | 98 ++++++++++++++++++- .../runtime/common/src/paras_registrar/mod.rs | 53 +++++----- 2 files changed, 128 insertions(+), 23 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 736ff2679db3..91cecb172c89 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -582,7 +582,7 @@ fn para_upgrade_initiated_by_manager_works() { // User 1 will own a parachain let free_balance = 1_000_000_000; - Balances::make_free_balance_be(&account_id(1), 1_000_000_000); + Balances::make_free_balance_be(&account_id(1), free_balance); // Register an on demand parachain let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); @@ -698,6 +698,102 @@ fn para_upgrade_initiated_by_manager_works() { }); } +#[test] +fn root_upgrading_parachain_works() { + new_test_ext().execute_with(|| { + assert!(System::block_number().is_one()); /* So events are emitted */ + let para_id = LOWEST_PUBLIC_ID; + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + // User 1 will own a parachain + Balances::make_free_balance_be(&account_id(1), 1_000_000_000); + // Register an on demand parachain + let code_size = 1024 * 1024; + let genesis_head = Registrar::worst_head_data(); + let head_size = genesis_head.0.len(); + let code_0 = validation_code(code_size); + + assert_ok!(Registrar::reserve(signed(1))); + assert_ok!(Registrar::register( + signed(1), + ParaId::from(para_id), + genesis_head.clone(), + code_0.clone(), + )); + conclude_pvf_checking::(&code_0, VALIDATORS, START_SESSION_INDEX, true); + + // The para should be onboarding. + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); + // After two sessions the parachain will be succesfully registered as an on-demand. + run_to_session(START_SESSION_INDEX + 2); + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + // The deposit should be appropriately taken. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // CASE 1: Root schedules a para upgrade to set the validation code to a new one which is + // twice the size. + + let code_1 = validation_code(code_size * 2); + assert_ok!(Registrar::schedule_code_upgrade( + RuntimeOrigin::root(), + ParaId::from(para_id), + code_1.clone(), + )); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 4); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); + + // The reserved deposit should remain the same since the upgrade was performed by root. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // CASE 2: Root schedules a para upgrade to set the validation code to a new one which has + // half the size of the initially registered code. The para manager should get a refund for + // the deposit they are no longer required to hold. + + let code_2 = validation_code(code_size / 2); + assert_ok!(Registrar::schedule_code_upgrade( + RuntimeOrigin::root(), + ParaId::from(para_id), + code_2.clone(), + )); + conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 6); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + + // The reserved deposit should remain the same since the upgrade was performed by root. + let total_bytes_stored = (code_size / 2) as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + }); +} + #[test] fn basic_errors_fail() { new_test_ext().execute_with(|| { diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2331f55d9773..d4175a576fa8 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -65,7 +65,7 @@ impl ParaInfo { } #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -pub struct RefundInfo { +pub struct DepositInfo { /// The `AccountId` that has its deposit reserved for this parachain. /// /// This will either be the parachain manager or the sovereign account of the parachain. @@ -229,11 +229,11 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores all the necessary information for initiating deposit refunds whenever the validation - /// code is reduced for any parachain. + /// "Stores all the necessary information about the account currently holding the deposit for + /// the parachain, as well as whether the current or prior depositor has any pending refunds. #[pallet::storage] - pub type Refunds = - StorageMap<_, Twox64Concat, ParaId, RefundInfo>>; + pub type Deposits = + StorageMap<_, Twox64Concat, ParaId, DepositInfo>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -665,10 +665,10 @@ impl Pallet { ::Currency::unreserve(&who, rebate); }; let info = ParaInfo { manager: who.clone(), deposit, locked: None }; - let refund_info = RefundInfo { depositor: who.clone(), pending_refund: None }; + let deposit_info = DepositInfo { depositor: who.clone(), pending_refund: None }; Paras::::insert(id, info); - Refunds::::insert(id, refund_info); + Deposits::::insert(id, deposit_info); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -720,10 +720,10 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let refund_info = Refunds::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let deposit_info = Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; - let current_depositor = refund_info.depositor; + let current_depositor = deposit_info.depositor; if let Some(caller) = maybe_caller.clone() { if caller != current_depositor { @@ -756,16 +756,14 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - println!("{:?}", current_deposit); - println!("{:?}", new_deposit); - let refund_info = RefundInfo { + let deposit_info = DepositInfo { depositor: caller.clone(), pending_refund: Some(( current_depositor, current_deposit.saturating_sub(new_deposit), )), }; - Refunds::::insert(para, refund_info); + Deposits::::insert(para, deposit_info); } ::Currency::withdraw( @@ -774,11 +772,22 @@ impl Pallet { WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; - } - // Update the deposit to the new appropriate amount. - info.deposit = new_deposit; - Paras::::insert(para, info); + // Update the deposit to the new appropriate amount. + info.deposit = new_deposit; + Paras::::insert(para, info); + } else if current_deposit > new_deposit { + // The depositor should receive a refund if the current deposit exceeds the new required + // deposit, even if they did not initiate the upgrade + let deposit_info = DepositInfo { + depositor: current_depositor.clone(), + pending_refund: Some(( + current_depositor, + current_deposit.saturating_sub(new_deposit), + )), + }; + Deposits::::insert(para, deposit_info); + } runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) } @@ -835,13 +844,13 @@ impl OnNewHead for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let mut refund_info = - Refunds::::get(id).expect("The depositor must be known for each parachain; qed"); + let mut deposit_info = + Deposits::::get(id).expect("The depositor must be known for each parachain; qed"); - if let Some((who, rebate)) = refund_info.pending_refund { + if let Some((who, rebate)) = deposit_info.pending_refund { ::Currency::unreserve(&who, rebate); - refund_info.pending_refund = None; - Refunds::::insert(id, refund_info); + deposit_info.pending_refund = None; + Deposits::::insert(id, deposit_info); return T::DbWeight::get().reads_writes(2, 1) } From 51f2d16529d5730c16c36df0ed1027a2b926b772 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 13:32:37 +0100 Subject: [PATCH 019/101] fix & new tests --- .../runtime/common/src/integration_tests.rs | 152 +++++++++++++++++- .../runtime/common/src/paras_registrar/mod.rs | 21 ++- polkadot/xcm/procedural/tests/ui.rs | 2 +- substrate/client/chain-spec/src/chain_spec.rs | 2 +- 4 files changed, 166 insertions(+), 11 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 91cecb172c89..983ac3ce48fb 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -49,8 +49,9 @@ use sp_runtime::{ AccountId32, BuildStorage, }; use sp_std::sync::Arc; -use xcm::opaque::lts::NetworkId; +use xcm::opaque::lts::{Junction::Parachain, MultiLocation, NetworkId, Parent}; use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; +use xcm_executor::traits::ConvertLocation; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -794,6 +795,155 @@ fn root_upgrading_parachain_works() { }); } +#[test] +fn para_upgrading_itself_works() { + new_test_ext().execute_with(|| { + assert!(System::block_number().is_one()); /* So events are emitted */ + let para_id = LOWEST_PUBLIC_ID; + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + // User 1 will own a parachain + let free_balance = 1_000_000_000; + Balances::make_free_balance_be(&account_id(1), free_balance); + // Register an on demand parachain + let mut code_size = 1024 * 1024; + let genesis_head = Registrar::worst_head_data(); + let head_size = genesis_head.0.len(); + let code_0 = validation_code(code_size); + + assert_ok!(Registrar::reserve(signed(1))); + assert_ok!(Registrar::register( + signed(1), + ParaId::from(para_id), + genesis_head.clone(), + code_0.clone(), + )); + conclude_pvf_checking::(&code_0, VALIDATORS, START_SESSION_INDEX, true); + + // The para should be onboarding. + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); + // After two sessions the parachain will be succesfully registered as an on-demand. + run_to_session(START_SESSION_INDEX + 2); + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + // The deposit should be appropriately taken. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // CASE 1: The parachain itself triggers a code upgrade, doubling the size of the validation + // code. Before the upgrade, the depositor is the parachain manager, and we don't allow + // multiple accounts to reserve deposits for a single parachain. This means the parachain + // must reserve the entire deposit, not just the difference. + // + // However, upon successful upgrading, the parachain manager should receive a full refund. + + let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); + let sovereign_account = + ::SovereignAccountOf::convert_location(&location) + .unwrap(); + let para_origin: runtime_parachains::Origin = 2000u32.into(); + + Balances::make_free_balance_be(&sovereign_account, free_balance); + + code_size *= 2; + let code_1 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + para_origin.clone().into(), + ParaId::from(para_id), + code_1.clone(), + )); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 4); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); + + // The parachain should have reserved deposit that covers the new code. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&sovereign_account), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // An additional upgrade fee should also be deducted from the caller's balance. + assert_eq!(Balances::total_balance(&sovereign_account), free_balance - UpgradeFee::get()); + + // Since all the deposit is now held by the parachain, the manager should have received a + // refund. + assert_eq!(Balances::reserved_balance(&account_id(1)), 0); + + // CASE 2: Schedule a para upgrade to set the validation code to a new one which is twice + // the size of the current one. + code_size *= 2; + let code_2 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + para_origin.clone().into(), + ParaId::from(para_id), + code_2.clone(), + )); + conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 6); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + // The deposit should be appropriately updated. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&sovereign_account), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + assert_eq!( + Balances::total_balance(&sovereign_account), + free_balance - 2 * UpgradeFee::get() + ); + + // CASE 3: Schedule a para upgrade to set the validation code half the size of the current + // one. + code_size /= 2; + let code_3 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + para_origin.into(), + ParaId::from(para_id), + code_3.clone(), + )); + conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 6, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 8); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_3.clone())); + // The deposit should be appropriately updated. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&sovereign_account), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + assert_eq!( + Balances::total_balance(&sovereign_account), + free_balance - 3 * UpgradeFee::get() + ); + }); +} + #[test] fn basic_errors_fail() { new_test_ext().execute_with(|| { diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index d4175a576fa8..58c8f4a7b332 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -727,8 +727,8 @@ impl Pallet { if let Some(caller) = maybe_caller.clone() { if caller != current_depositor { - // If the caller is not the one that had their funds reserved for this parachain we - // will unreserve all the funds from the original depositor and reserve the required + // If the caller is not the one who had their funds reserved for this parachain, we + // will unreserve all funds from the original depositor and reserve the required // amount from the new depositor. // // The primary reason for this is to more easily track the account that has its @@ -736,17 +736,22 @@ impl Pallet { // manager and the parachain itself could unnecessarily complicate refunds. ::Currency::reserve(&caller, new_deposit)?; - } + ::Currency::unreserve(¤t_depositor, current_deposit); - if current_deposit < new_deposit { - // An additional deposit is required to cover for the new validation code which has - // a greater size compared to the old one. + // Update the depositor to the caller. + let deposit_info = DepositInfo { depositor: caller.clone(), pending_refund: None }; + Deposits::::insert(para, deposit_info); + } else if current_deposit < new_deposit { + // The caller is the current depositor and an additional deposit is required to + // cover for the new validation code which has a greater size compared to the old + // one. let excess = new_deposit.saturating_sub(current_deposit); ::Currency::reserve(&caller, excess)?; } else if current_deposit > new_deposit { - // In case the existing deposit exceeds the required amount due to validation code - // reduction, the excess deposit will be returned to the caller. + // The caller is the current depositor and in the that case the existing deposit + // exceeds the required amount due to validation code reduction, the excess deposit + // will be returned to the caller. // // The reason why the deposit is not instantly refunded is that scheduling a code // upgrade doesn't guarantee the success of an upgrade. diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs index a6ec35d0862a..fef7c95c6142 100644 --- a/polkadot/xcm/procedural/tests/ui.rs +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -21,7 +21,7 @@ fn ui() { // Only run the ui tests when `RUN_UI_TESTS` is set. if std::env::var("RUN_UI_TESTS").is_err() { - return; + return } // As trybuild is using `cargo check`, we don't need the real WASM binaries. diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index 8d97d9410229..b0f44641f551 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -785,7 +785,7 @@ fn json_eval_value_at_key( fun: &dyn Fn(&json::Value) -> bool, ) -> bool { let Some(key) = path.pop_front() else { - return false; + return false }; if path.is_empty() { From 06b02d5c693c229f686aacbbde1ef3398f70027f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 13:55:12 +0100 Subject: [PATCH 020/101] fix? --- polkadot/runtime/common/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 0882e555aafe..9616bf33e812 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -52,7 +52,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } +xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } From 86041469d0c3afd9ccb2438bbdb6a564ad74f77c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 14:13:06 +0100 Subject: [PATCH 021/101] should compile --- polkadot/runtime/rococo/src/lib.rs | 13 +++++++------ polkadot/runtime/westend/src/lib.rs | 15 ++++++++------- substrate/client/chain-spec/src/chain_spec.rs | 4 +--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index d6b7fe5b61ef..eca686f22fc2 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -96,7 +96,7 @@ use sp_staking::SessionIndex; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, + latest::{InteriorMultiLocation, Junction, Junction::PalletInstance, NetworkId}, VersionedMultiLocation, }; use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; @@ -1011,17 +1011,18 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; } +parameter_types! { + pub const ParaDeposit: Balance = 40 * UNITS; + pub const UpgradeFee: Balance = 2 * UNITS; + pub const RelayNetwork: NetworkId = NetworkId::Rococo; +} + pub type LocationToAccountId = ( ChildParachainConvertsVia, AccountId32Aliases, Account32Hash<(), AccountId>, ); -parameter_types! { - pub const ParaDeposit: Balance = 40 * UNITS; - pub const UpgradeFee: Balance = 2 * UNITS; -} - impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 749ad868490e..f17734da00c5 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -96,7 +96,7 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, + latest::{InteriorMultiLocation, Junction, Junction::PalletInstance, NetworkId}, VersionedMultiLocation, }; use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; @@ -1260,18 +1260,19 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<300>; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, -); - parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; pub const UpgradeFee: Balance = 50 * CENTS; pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); + pub const RelayNetwork: NetworkId = NetworkId::Westend; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, +); + impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index b0f44641f551..fe8fcfda216e 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -784,9 +784,7 @@ fn json_eval_value_at_key( path: &mut VecDeque<&str>, fun: &dyn Fn(&json::Value) -> bool, ) -> bool { - let Some(key) = path.pop_front() else { - return false - }; + let Some(key) = path.pop_front() else { return false }; if path.is_empty() { doc.as_object().map_or(false, |o| o.get(key).map_or(false, |v| fun(v))) From 8cbf899a1043bba3a5507942094765bb31ea3175 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 21 Nov 2023 15:08:39 +0100 Subject: [PATCH 022/101] nitpicks --- polkadot/runtime/common/src/integration_tests.rs | 7 +++---- polkadot/runtime/common/src/paras_registrar/mod.rs | 6 +++--- polkadot/runtime/parachains/src/paras/mod.rs | 3 --- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 983ac3ce48fb..69017ad6d91c 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -786,7 +786,6 @@ fn root_upgrading_parachain_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); - // The reserved deposit should remain the same since the upgrade was performed by root. let total_bytes_stored = (code_size / 2) as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), @@ -838,13 +837,13 @@ fn para_upgrading_itself_works() { // multiple accounts to reserve deposits for a single parachain. This means the parachain // must reserve the entire deposit, not just the difference. // - // However, upon successful upgrading, the parachain manager should receive a full refund. + // However, upon successfully upgrading, the parachain manager should receive a full refund. let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); let sovereign_account = ::SovereignAccountOf::convert_location(&location) .unwrap(); - let para_origin: runtime_parachains::Origin = 2000u32.into(); + let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); Balances::make_free_balance_be(&sovereign_account, free_balance); @@ -867,7 +866,7 @@ fn para_upgrading_itself_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // The parachain should have reserved deposit that covers the new code. + // The parachain should have a deposit reserved that covers the new code. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&sovereign_account), diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 58c8f4a7b332..b10b526f235c 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -229,7 +229,7 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// "Stores all the necessary information about the account currently holding the deposit for + /// Stores all the necessary information about the account currently holding the deposit for /// the parachain, as well as whether the current or prior depositor has any pending refunds. #[pallet::storage] pub type Deposits = @@ -783,7 +783,7 @@ impl Pallet { Paras::::insert(para, info); } else if current_deposit > new_deposit { // The depositor should receive a refund if the current deposit exceeds the new required - // deposit, even if they did not initiate the upgrade + // deposit, even if they did not initiate the upgrade. let deposit_info = DepositInfo { depositor: current_depositor.clone(), pending_refund: Some(( @@ -857,7 +857,7 @@ impl OnCodeUpgrade for Pallet { deposit_info.pending_refund = None; Deposits::::insert(id, deposit_info); - return T::DbWeight::get().reads_writes(2, 1) + return T::DbWeight::get().reads_writes(2, 2) } T::DbWeight::get().reads(1) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 099926eaa1d2..5acc8dbdc046 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -1240,14 +1240,12 @@ impl Pallet { } /// Called by the initializer to initialize the paras pallet. - // TODO: don't affect visibility pub fn initializer_initialize(now: BlockNumberFor) -> Weight { let weight = Self::prune_old_code(now); weight + Self::process_scheduled_upgrade_changes(now) } /// Called by the initializer to finalize the paras pallet. - // TODO: don't affect visibility pub fn initializer_finalize(now: BlockNumberFor) { Self::process_scheduled_upgrade_cooldowns(now); } @@ -1264,7 +1262,6 @@ impl Pallet { } /// The validation code of live para. - // TODO: don't affect visibility pub fn current_code(para_id: &ParaId) -> Option { Self::current_code_hash(para_id).and_then(|code_hash| { let code = CodeByHash::::get(&code_hash); From 74d680f25e153b434177cd56a4011ce0bfb95529 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 2 Dec 2023 14:08:48 +0100 Subject: [PATCH 023/101] make this a non-breaking change --- .../runtime/common/src/paras_registrar/mod.rs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index b10b526f235c..66673efbb772 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -235,6 +235,13 @@ pub mod pallet { pub type Deposits = StorageMap<_, Twox64Concat, ParaId, DepositInfo>>; + /// Stores all the `ParaId`s of parachains that purchased a slot using the legacy auctions + /// model. + /// + /// Each of these parachains is eligible to perform a single code upgrade without upgrade fees. + #[pallet::storage] + pub type LegacyParachains = StorageMap<_, Twox64Concat, ParaId, ()>; + #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -449,15 +456,23 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - let fee_payer = if let Ok(caller) = ensure_signed(origin.clone()) { + let fee_payer = if ensure_root(origin.clone()).is_ok() { + // Root doesn't pay. This, also means that system parachains do not pay for upgrade + // fees. + None + } else if LegacyParachains::::get(para).is_some() { + // Each legacy para is permitted to perform one upgrade for free. + // + // This is introduced to avoid causing a breaking change to the system once para + // upgrade fees are required. + LegacyParachains::::remove(para); + None + } else if let Ok(caller) = ensure_signed(origin) { let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; ensure!(!para_info.is_locked(), Error::::ParaLocked); ensure!(para_info.manager == caller, Error::::NotOwner); Some(caller) - } else if ensure_root(origin).is_ok() { - // Root doesn't pay. - None } else { let location: MultiLocation = (Parent, Parachain(para.into())).into(); let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); From 10d681a12e8071172fa1e0f20c90441502f73d8f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 2 Dec 2023 14:37:03 +0100 Subject: [PATCH 024/101] add migration --- .../common/src/paras_registrar/migration.rs | 136 ++++++++++++------ .../runtime/common/src/paras_registrar/mod.rs | 6 +- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 4 files changed, 99 insertions(+), 47 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index f977674a1e4e..0ffa0805849f 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -17,53 +17,105 @@ use super::*; use frame_support::traits::{Contains, OnRuntimeUpgrade}; -#[derive(Encode, Decode)] -pub struct ParaInfoV1 { - manager: Account, - deposit: Balance, - locked: bool, -} +pub mod v1 { + use super::*; -pub struct VersionUncheckedMigrateToV1( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, -); -impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV1 -{ - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|key, v1| { - count.saturating_inc(); - Some(ParaInfo { - manager: v1.manager, - deposit: v1.deposit, - locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, - }) - }); - - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); - T::DbWeight::get().reads_writes(count, count) + #[derive(Encode, Decode)] + pub struct ParaInfoV1 { + manager: Account, + deposit: Balance, + locked: bool, } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) - } + pub struct VersionUncheckedMigrateToV1( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, + ); + impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV1 + { + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|key, v1| { + count.saturating_inc(); + Some(ParaInfo { + manager: v1.manager, + deposit: v1.deposit, + locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + }) + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + T::DbWeight::get().reads_writes(count, count) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; - ensure!(old_count == new_count, "Paras count should not change"); - Ok(()) + ensure!(old_count == new_count, "Paras count should not change"); + Ok(()) + } } + + pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + super::Pallet, + ::DbWeight, + >; } -pub type MigrateToV1 = frame_support::migrations::VersionedMigration< - 0, - 1, - VersionUncheckedMigrateToV1, - super::Pallet, - ::DbWeight, ->; +pub mod v2 { + use super::*; + + pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData); + + impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { + fn on_runtime_upgrade() -> Weight { + let mut writes = 0u64; + let lowest_public_id: u32 = LOWEST_PUBLIC_ID.into(); + let next_free_para_id: u32 = NextFreeParaId::::get().into(); + + (lowest_public_id..next_free_para_id).for_each(|para_id| { + LegacyParas::::insert(ParaId::new(para_id), ()); + writes.saturating_inc(); + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", writes); + T::DbWeight::get().reads_writes(1, writes) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Default::default()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let expected_legacy_paras_count = + NextFreeParaId::::get().saturating_sub(LOWEST_PUBLIC_ID); + let legacy_paras_count = LegacyParas::::iter_values().count() as u32; + + ensure!( + expected_legacy_paras_count == legacy_paras_count, + "`LegacyParas` must contain all the non-system parachains" + ); + Ok(()) + } + } + + pub type MigrateToV2 = frame_support::migrations::VersionedMigration< + 1, + 2, + VersionUncheckedMigrateToV2, + super::Pallet, + ::DbWeight, + >; +} diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 66673efbb772..784ea7736ae9 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -240,7 +240,7 @@ pub mod pallet { /// /// Each of these parachains is eligible to perform a single code upgrade without upgrade fees. #[pallet::storage] - pub type LegacyParachains = StorageMap<_, Twox64Concat, ParaId, ()>; + pub type LegacyParas = StorageMap<_, Twox64Concat, ParaId, ()>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -460,12 +460,12 @@ pub mod pallet { // Root doesn't pay. This, also means that system parachains do not pay for upgrade // fees. None - } else if LegacyParachains::::get(para).is_some() { + } else if LegacyParas::::get(para).is_some() { // Each legacy para is permitted to perform one upgrade for free. // // This is introduced to avoid causing a breaking change to the system once para // upgrade fees are required. - LegacyParachains::::remove(para); + LegacyParas::::remove(para); None } else if let Ok(caller) = ensure_signed(origin) { let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index eca686f22fc2..87cf701d5e2f 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1490,7 +1490,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::v1::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index f17734da00c5..8126f1b74584 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1565,7 +1565,7 @@ pub mod migrations { parachains_configuration::migration::v8::MigrateToV8, UpgradeSessionKeys, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::v1::MigrateToV1, pallet_nomination_pools::migration::versioned_migrations::V5toV6, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_nomination_pools::migration::versioned_migrations::V6ToV7, From daa0dcc9763ce9aa8540d3850bc8af4ea06bd9e4 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 2 Dec 2023 16:30:10 +0100 Subject: [PATCH 025/101] add test --- .../runtime/common/src/integration_tests.rs | 117 ++++++++++++++++++ .../common/src/paras_registrar/migration.rs | 6 +- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index dbfcc9b63b6c..33ccf2f418de 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -971,6 +971,123 @@ fn para_upgrading_itself_works() { }); } +#[test] +fn legacy_para_upgardes_require_no_fee() { + new_test_ext().execute_with(|| { + assert!(System::block_number().is_one()); /* So events are emitted */ + let para_id = LOWEST_PUBLIC_ID; + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + // User 1 will own a parachain + let free_balance = 1_000_000_000; + Balances::make_free_balance_be(&account_id(1), free_balance); + // Register an on demand parachain + let mut code_size = 1024 * 1024; + let genesis_head = Registrar::worst_head_data(); + let head_size = genesis_head.0.len(); + let code_0 = validation_code(code_size); + + assert_ok!(Registrar::reserve(signed(1))); + assert_ok!(Registrar::register( + signed(1), + ParaId::from(para_id), + genesis_head.clone(), + code_0.clone(), + )); + conclude_pvf_checking::(&code_0, VALIDATORS, START_SESSION_INDEX, true); + + // The para should be onboarding. + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); + // After two sessions the parachain will be succesfully registered as an on-demand. + run_to_session(START_SESSION_INDEX + 2); + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + // The deposit should be appropriately taken. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // We 'simulate' the migration. In this case there is only one legacy parachain. + paras_registrar::LegacyParas::::insert(ParaId::from(para_id), ()); + + // The parachain should be able to schedule a code upgrade without any additional deposit or + // fees necessary. + + let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); + let sovereign_account = + ::SovereignAccountOf::convert_location(&location) + .unwrap(); + let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); + + Balances::make_free_balance_be(&sovereign_account, free_balance); + + code_size *= 2; + let code_1 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + para_origin.clone().into(), + ParaId::from(para_id), + code_1.clone(), + )); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 4); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); + + // Should remain unchanged: + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + assert_eq!(Balances::total_balance(&account_id(1)), free_balance); + assert_eq!(Balances::total_balance(&sovereign_account), free_balance); + + // However, legacy chains are only allowed to perform a free upgrade once. + // Doing a new upgrade will require fees: + + assert!(paras_registrar::LegacyParas::::get(ParaId::from(para_id)).is_none()); + + code_size *= 2; + let code_2 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + para_origin.clone().into(), + ParaId::from(para_id), + code_2.clone(), + )); + conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 6); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + + // The parachain should have a deposit reserved that covers the new code. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&sovereign_account), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // An additional upgrade fee should also be deducted from the caller's balance. + assert_eq!(Balances::total_balance(&sovereign_account), free_balance - UpgradeFee::get()); + // Since all the deposit is now held by the parachain, the manager should have received a + // refund. + assert_eq!(Balances::reserved_balance(&account_id(1)), 0); + }); +} + #[test] fn basic_errors_fail() { new_test_ext().execute_with(|| { diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 0ffa0805849f..65f4950dd65e 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -84,7 +84,7 @@ pub mod v2 { let next_free_para_id: u32 = NextFreeParaId::::get().into(); (lowest_public_id..next_free_para_id).for_each(|para_id| { - LegacyParas::::insert(ParaId::new(para_id), ()); + LegacyParas::::insert(ParaId::from(para_id), ()); writes.saturating_inc(); }); @@ -99,8 +99,8 @@ pub mod v2 { #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let expected_legacy_paras_count = - NextFreeParaId::::get().saturating_sub(LOWEST_PUBLIC_ID); + let expected_legacy_paras_count: u32 = + NextFreeParaId::::get().saturating_sub(LOWEST_PUBLIC_ID).into(); let legacy_paras_count = LegacyParas::::iter_values().count() as u32; ensure!( From dd54e5fbf76f9008a8dbc348507fd3164b5511b8 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 2 Dec 2023 16:52:37 +0100 Subject: [PATCH 026/101] fix try-runtime --- polkadot/runtime/common/src/paras_registrar/migration.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 65f4950dd65e..e1b5d9d7e8ee 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -99,10 +99,11 @@ pub mod v2 { #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let expected_legacy_paras_count: u32 = - NextFreeParaId::::get().saturating_sub(LOWEST_PUBLIC_ID).into(); - let legacy_paras_count = LegacyParas::::iter_values().count() as u32; + let lowest_public_id: u32 = LOWEST_PUBLIC_ID.into(); + let next_free_para_id: u32 = NextFreeParaId::::get().into(); + let expected_legacy_paras_count = next_free_para_id.saturating_sub(lowest_public_id); + let legacy_paras_count = LegacyParas::::iter_values().count() as u32; ensure!( expected_legacy_paras_count == legacy_paras_count, "`LegacyParas` must contain all the non-system parachains" From 6c90742f200e491c15d5acf05dd3e8a0684951a1 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 5 Dec 2023 16:32:03 +0100 Subject: [PATCH 027/101] implement the better solution --- .../runtime/common/src/integration_tests.rs | 61 ++------ .../common/src/paras_registrar/migration.rs | 137 ++++++------------ .../runtime/common/src/paras_registrar/mod.rs | 28 ++-- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 5 files changed, 70 insertions(+), 160 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 33ccf2f418de..e851db50cbf1 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -631,7 +631,7 @@ fn para_upgrade_initiated_by_manager_works() { assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); - assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + assert!(Registrar::is_parathread(para_id)); // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( @@ -756,7 +756,7 @@ fn root_upgrading_parachain_works() { assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); - assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + assert!(Registrar::is_parathread(para_id)); // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( @@ -852,7 +852,7 @@ fn para_upgrading_itself_works() { assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); - assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + assert!(Registrar::is_parathread(para_id)); // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( @@ -972,7 +972,7 @@ fn para_upgrading_itself_works() { } #[test] -fn legacy_para_upgardes_require_no_fee() { +fn lease_holding_parachains_have_no_upgrade_costs() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -1001,7 +1001,8 @@ fn legacy_para_upgardes_require_no_fee() { assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); - assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Parathread)); + assert!(Registrar::is_parathread(para_id)); + // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( @@ -1009,11 +1010,13 @@ fn legacy_para_upgardes_require_no_fee() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // We 'simulate' the migration. In this case there is only one legacy parachain. - paras_registrar::LegacyParas::::insert(ParaId::from(para_id), ()); + // Make the para a lease holding parachain. + assert_ok!(Registrar::make_parachain(para_id)); + run_to_session(START_SESSION_INDEX + 4); + assert!(Registrar::is_parachain(para_id)); - // The parachain should be able to schedule a code upgrade without any additional deposit or - // fees necessary. + // The lease holding parachain should be able to schedule a code upgrade without any additional + // deposit or fees necessary. let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); let sovereign_account = @@ -1030,10 +1033,10 @@ fn legacy_para_upgardes_require_no_fee() { ParaId::from(para_id), code_1.clone(), )); - conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 4, true); // After two more sessions the parachain can be upgraded. - run_to_session(START_SESSION_INDEX + 4); + run_to_session(START_SESSION_INDEX + 6); // Force a new head to enact the code upgrade. assert_ok!(Paras::force_note_new_head( RuntimeOrigin::root(), @@ -1049,42 +1052,6 @@ fn legacy_para_upgardes_require_no_fee() { ); assert_eq!(Balances::total_balance(&account_id(1)), free_balance); assert_eq!(Balances::total_balance(&sovereign_account), free_balance); - - // However, legacy chains are only allowed to perform a free upgrade once. - // Doing a new upgrade will require fees: - - assert!(paras_registrar::LegacyParas::::get(ParaId::from(para_id)).is_none()); - - code_size *= 2; - let code_2 = validation_code(code_size); - assert_ok!(Registrar::schedule_code_upgrade( - para_origin.clone().into(), - ParaId::from(para_id), - code_2.clone(), - )); - conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); - - // After two more sessions the parachain can be upgraded. - run_to_session(START_SESSION_INDEX + 6); - // Force a new head to enact the code upgrade. - assert_ok!(Paras::force_note_new_head( - RuntimeOrigin::root(), - para_id, - genesis_head.clone() - )); - assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); - - // The parachain should have a deposit reserved that covers the new code. - let total_bytes_stored = code_size as u32 + head_size as u32; - assert_eq!( - Balances::reserved_balance(&sovereign_account), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) - ); - // An additional upgrade fee should also be deducted from the caller's balance. - assert_eq!(Balances::total_balance(&sovereign_account), free_balance - UpgradeFee::get()); - // Since all the deposit is now held by the parachain, the manager should have received a - // refund. - assert_eq!(Balances::reserved_balance(&account_id(1)), 0); }); } diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index e1b5d9d7e8ee..f977674a1e4e 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -17,106 +17,53 @@ use super::*; use frame_support::traits::{Contains, OnRuntimeUpgrade}; -pub mod v1 { - use super::*; +#[derive(Encode, Decode)] +pub struct ParaInfoV1 { + manager: Account, + deposit: Balance, + locked: bool, +} - #[derive(Encode, Decode)] - pub struct ParaInfoV1 { - manager: Account, - deposit: Balance, - locked: bool, +pub struct VersionUncheckedMigrateToV1( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, +); +impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV1 +{ + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|key, v1| { + count.saturating_inc(); + Some(ParaInfo { + manager: v1.manager, + deposit: v1.deposit, + locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + }) + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + T::DbWeight::get().reads_writes(count, count) } - pub struct VersionUncheckedMigrateToV1( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, - ); - impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV1 - { - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|key, v1| { - count.saturating_inc(); - Some(ParaInfo { - manager: v1.manager, - deposit: v1.deposit, - locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, - }) - }); - - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); - T::DbWeight::get().reads_writes(count, count) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; - - ensure!(old_count == new_count, "Paras count should not change"); - Ok(()) - } + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) } - pub type MigrateToV1 = frame_support::migrations::VersionedMigration< - 0, - 1, - VersionUncheckedMigrateToV1, - super::Pallet, - ::DbWeight, - >; -} - -pub mod v2 { - use super::*; - - pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData); + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; - impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { - fn on_runtime_upgrade() -> Weight { - let mut writes = 0u64; - let lowest_public_id: u32 = LOWEST_PUBLIC_ID.into(); - let next_free_para_id: u32 = NextFreeParaId::::get().into(); - - (lowest_public_id..next_free_para_id).for_each(|para_id| { - LegacyParas::::insert(ParaId::from(para_id), ()); - writes.saturating_inc(); - }); - - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", writes); - T::DbWeight::get().reads_writes(1, writes) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok(Default::default()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let lowest_public_id: u32 = LOWEST_PUBLIC_ID.into(); - let next_free_para_id: u32 = NextFreeParaId::::get().into(); - - let expected_legacy_paras_count = next_free_para_id.saturating_sub(lowest_public_id); - let legacy_paras_count = LegacyParas::::iter_values().count() as u32; - ensure!( - expected_legacy_paras_count == legacy_paras_count, - "`LegacyParas` must contain all the non-system parachains" - ); - Ok(()) - } + ensure!(old_count == new_count, "Paras count should not change"); + Ok(()) } - - pub type MigrateToV2 = frame_support::migrations::VersionedMigration< - 1, - 2, - VersionUncheckedMigrateToV2, - super::Pallet, - ::DbWeight, - >; } + +pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + super::Pallet, + ::DbWeight, +>; diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index e0a19c89fb75..82b79b544e37 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -235,13 +235,6 @@ pub mod pallet { pub type Deposits = StorageMap<_, Twox64Concat, ParaId, DepositInfo>>; - /// Stores all the `ParaId`s of parachains that purchased a slot using the legacy auctions - /// model. - /// - /// Each of these parachains is eligible to perform a single code upgrade without upgrade fees. - #[pallet::storage] - pub type LegacyParas = StorageMap<_, Twox64Concat, ParaId, ()>; - #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -456,16 +449,19 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - let fee_payer = if ensure_root(origin.clone()).is_ok() { - // Root doesn't pay. This, also means that system parachains do not pay for upgrade - // fees. - None - } else if LegacyParas::::get(para).is_some() { - // Each legacy para is permitted to perform one upgrade for free. + let fee_payer = if ensure_root(origin.clone()).is_ok() || + Self::parachains().contains(¶) + { + // There are two cases where we do not require any upgrade costs from the initiator + // of the upgrade: + // + // 1. Root doesn't pay. This also means that system parachains do not pay for + // upgrade fees. // - // This is introduced to avoid causing a breaking change to the system once para - // upgrade fees are required. - LegacyParas::::remove(para); + // 2. All lease-holding parachains are permitted to do upgrades for free. This is + // introduced to avoid + // causing a breaking change to the system once para upgrade fees are required. + None } else if let Ok(caller) = ensure_signed(origin) { let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index e356fc336a1f..de0d8e955a96 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1624,7 +1624,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::v1::MigrateToV1, + paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index f0d59eb6a3f7..3d41149a4ebf 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1656,7 +1656,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::v1::MigrateToV1, + paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From e82525d47d24ee6488e06babda26b2aa59a5474b Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 5 Dec 2023 20:52:55 +0100 Subject: [PATCH 028/101] safe fund handling --- .../runtime/common/src/integration_tests.rs | 4 +- .../runtime/common/src/paras_registrar/mod.rs | 86 +++++++++++++------ 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index e851db50cbf1..fd50db1fb09d 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -1015,8 +1015,8 @@ fn lease_holding_parachains_have_no_upgrade_costs() { run_to_session(START_SESSION_INDEX + 4); assert!(Registrar::is_parachain(para_id)); - // The lease holding parachain should be able to schedule a code upgrade without any additional - // deposit or fees necessary. + // The lease holding parachain should be able to schedule a code upgrade without any + // additional deposit or fees necessary. let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); let sovereign_account = diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 82b79b544e37..f0016437f133 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -41,7 +41,7 @@ use runtime_parachains::paras::{OnNewHead, ParaKind}; use scale_info::TypeInfo; use sp_runtime::{ traits::{CheckedSub, Saturating}, - RuntimeDebug, + DispatchError, RuntimeDebug, TokenError, }; use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; use xcm_executor::traits::ConvertLocation; @@ -731,13 +731,14 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let deposit_info = Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let current_deposit_info = + Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; - let current_depositor = deposit_info.depositor; + let current_depositor = current_deposit_info.depositor.clone(); if let Some(caller) = maybe_caller.clone() { - if caller != current_depositor { + let new_deposit_info = if caller != current_depositor { // If the caller is not the one who had their funds reserved for this parachain, we // will unreserve all funds from the original depositor and reserve the required // amount from the new depositor. @@ -746,41 +747,39 @@ impl Pallet { // deposit reserved for this parachain. Reserving the deposit across both the para // manager and the parachain itself could unnecessarily complicate refunds. - ::Currency::reserve(&caller, new_deposit)?; - ::Currency::unreserve(¤t_depositor, current_deposit); - // Update the depositor to the caller. - let deposit_info = DepositInfo { depositor: caller.clone(), pending_refund: None }; - Deposits::::insert(para, deposit_info); - } else if current_deposit < new_deposit { - // The caller is the current depositor and an additional deposit is required to - // cover for the new validation code which has a greater size compared to the old - // one. - - let excess = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&caller, excess)?; + DepositInfo { depositor: caller.clone(), pending_refund: None } } else if current_deposit > new_deposit { - // The caller is the current depositor and in the that case the existing deposit - // exceeds the required amount due to validation code reduction, the excess deposit - // will be returned to the caller. + // The caller is the current depositor and the existing deposit exceeds the required + // amount. + // + // The excess deposit will be refunded to the caller upon the success of the code + // upgrade. // // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of an upgrade. + // upgrade doesn't guarantee the success of it. // // If we returned the deposit here, a possible attack scenario would be to register // the validation code of a parachain and then schedule a code upgrade to set the // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - let deposit_info = DepositInfo { + DepositInfo { depositor: caller.clone(), pending_refund: Some(( - current_depositor, + current_depositor.clone(), current_deposit.saturating_sub(new_deposit), )), - }; - Deposits::::insert(para, deposit_info); - } + } + } else { + // The caller is the current depositor and there is no funds to be refunded hence + // the deposit infor remains the same. + current_deposit_info.clone() + }; + + // Before dealing with any currency operations, we ensure beforehand that all of them + // will be successful. + // TODO: Check before charging the fee. ::Currency::withdraw( &caller, @@ -789,9 +788,37 @@ impl Pallet { ExistenceRequirement::KeepAlive, )?; + ensure!( + ::Currency::can_reserve(&new_deposit_info.depositor, new_deposit), + DispatchError::Token(TokenError::FundsUnavailable) + ); + + let additional_deposit = if caller != current_depositor { + // Since the depositor changed the additional deposit needs to account for storing + // the new entire validation code. + new_deposit + } else { + // The depositor did not change, so we only have to reserve the difference to + // account for the increase in the validation code. + new_deposit.saturating_sub(current_deposit) + }; + + ::Currency::reserve(&caller, additional_deposit)?; + + if caller != current_depositor { + // After reserving the required funds from the new depositor we can now safely + // refund the old depositor. + ::Currency::unreserve(¤t_depositor, current_deposit); + } + // Update the deposit to the new appropriate amount. info.deposit = new_deposit; Paras::::insert(para, info); + + // We update the deposit info if it changed. + if current_deposit_info != new_deposit_info { + Deposits::::insert(para, new_deposit_info); + } } else if current_deposit > new_deposit { // The depositor should receive a refund if the current deposit exceeds the new required // deposit, even if they did not initiate the upgrade. @@ -860,8 +887,13 @@ impl OnNewHead for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let mut deposit_info = - Deposits::::get(id).expect("The depositor must be known for each parachain; qed"); + let maybe_deposit_info = Deposits::::get(id); + if maybe_deposit_info.is_none() { + return T::DbWeight::get().reads(1) + } + + let mut deposit_info = maybe_deposit_info + .expect("Ensured above that the deposit info is stored for the parachain; qed"); if let Some((who, rebate)) = deposit_info.pending_refund { ::Currency::unreserve(&who, rebate); From cfd30929309244df460a77739f76fd3eef56173c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 07:34:18 +0100 Subject: [PATCH 029/101] improve docs --- .../runtime/common/src/paras_registrar/mod.rs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f0016437f133..f6cacaac05bc 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -435,11 +435,11 @@ pub mod pallet { /// Can be called by Root, the parachain, or the parachain manager if the parachain is /// unlocked. /// - /// In case the call is made by the parachain manager or the parachain itself the upgrade - /// fee will be charged. + /// In case the call is made by the parachain manager or the parachain itself, there will be + /// associated upgrade costs. /// - /// The caller will receive a refund or be required to reserve an additional deposit, - /// depending on the size of the new validation code. + /// Depending on the size of the new validation code, the caller's reserved deposit might be + /// adjusted to account for the size difference. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))] pub fn schedule_code_upgrade( @@ -449,18 +449,18 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - let fee_payer = if ensure_root(origin.clone()).is_ok() || + let upgrade_cost_payer = if ensure_root(origin.clone()).is_ok() || Self::parachains().contains(¶) { - // There are two cases where we do not require any upgrade costs from the initiator + // There are two cases where we do not charge any upgrade costs from the initiator // of the upgrade: // // 1. Root doesn't pay. This also means that system parachains do not pay for // upgrade fees. // // 2. All lease-holding parachains are permitted to do upgrades for free. This is - // introduced to avoid - // causing a breaking change to the system once para upgrade fees are required. + // introduced to avoid causing a breaking change to the system once para upgrade + // fees are required. None } else if let Ok(caller) = ensure_signed(origin) { @@ -475,7 +475,7 @@ pub mod pallet { Some(sovereign_account) }; - Self::do_schedule_code_upgrade(para, new_code, fee_payer) + Self::do_schedule_code_upgrade(para, new_code, upgrade_cost_payer) } /// Set the parachain's current head. @@ -708,15 +708,15 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `maybe_caller` isn't specified, the caller won't be charged any fees or have its deposit - /// reserved. + /// If `upgrade_cost_payer` isn't specified, there won't be any extra deposit or fees charged + /// for scheduling the code upgrade. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - maybe_caller: Option, + upgrade_cost_payer: Option, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. @@ -737,9 +737,9 @@ impl Pallet { let current_deposit = info.deposit; let current_depositor = current_deposit_info.depositor.clone(); - if let Some(caller) = maybe_caller.clone() { - let new_deposit_info = if caller != current_depositor { - // If the caller is not the one who had their funds reserved for this parachain, we + if let Some(payer) = upgrade_cost_payer.clone() { + let new_deposit_info = if payer != current_depositor { + // If the payer is not the one who had their funds reserved for this parachain, we // will unreserve all funds from the original depositor and reserve the required // amount from the new depositor. // @@ -748,9 +748,9 @@ impl Pallet { // manager and the parachain itself could unnecessarily complicate refunds. // Update the depositor to the caller. - DepositInfo { depositor: caller.clone(), pending_refund: None } + DepositInfo { depositor: payer.clone(), pending_refund: None } } else if current_deposit > new_deposit { - // The caller is the current depositor and the existing deposit exceeds the required + // The payer is the current depositor and the existing deposit exceeds the required // amount. // // The excess deposit will be refunded to the caller upon the success of the code @@ -765,7 +765,7 @@ impl Pallet { // the old code would remain on-chain even though there is no deposit to cover it. DepositInfo { - depositor: caller.clone(), + depositor: payer.clone(), pending_refund: Some(( current_depositor.clone(), current_deposit.saturating_sub(new_deposit), @@ -782,7 +782,7 @@ impl Pallet { // TODO: Check before charging the fee. ::Currency::withdraw( - &caller, + &payer, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, @@ -793,9 +793,9 @@ impl Pallet { DispatchError::Token(TokenError::FundsUnavailable) ); - let additional_deposit = if caller != current_depositor { - // Since the depositor changed the additional deposit needs to account for storing - // the new entire validation code. + let additional_deposit = if payer != current_depositor { + // Since the account covering the costs of storing the validation code has changed, + // the entire required deposit will now be reserved from the new account. new_deposit } else { // The depositor did not change, so we only have to reserve the difference to @@ -803,11 +803,11 @@ impl Pallet { new_deposit.saturating_sub(current_deposit) }; - ::Currency::reserve(&caller, additional_deposit)?; + ::Currency::reserve(&payer, additional_deposit)?; - if caller != current_depositor { + if payer != current_depositor { // After reserving the required funds from the new depositor we can now safely - // refund the old depositor. + // refund the old account. ::Currency::unreserve(¤t_depositor, current_deposit); } From 14b23df9b582440ae3e8c3c532783d95dca8ca7d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 08:50:59 +0100 Subject: [PATCH 030/101] refactoring | incomplete --- .../runtime/common/src/paras_registrar/mod.rs | 188 +++++++++--------- 1 file changed, 90 insertions(+), 98 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f6cacaac05bc..49fb024ea29a 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -43,7 +43,6 @@ use sp_runtime::{ traits::{CheckedSub, Saturating}, DispatchError, RuntimeDebug, TokenError, }; -use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] @@ -68,7 +67,8 @@ impl ParaInfo { pub struct DepositInfo { /// The `AccountId` that has its deposit reserved for this parachain. /// - /// This will either be the parachain manager or the sovereign account of the parachain. + /// when the parachain is registered, this will be set to the parachain manager. However, at + /// a later point, this can be updated by calling the `set_upgrade_cost_payer` extrinsic. depositor: AccountId, /// In case there is a pending refund, this stores information about the account and the /// amount that will be refunded upon a successful code upgrade. @@ -187,6 +187,8 @@ pub mod pallet { AlreadyRegistered, /// The caller is not the owner of this Id. NotOwner, + /// The caller is not the depositor of this parachain. + NotDepositor, /// Invalid para code size. CodeTooLarge, /// Invalid para head data size. @@ -211,6 +213,8 @@ pub mod pallet { /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras /// are correct for the swap to work. CannotSwap, + /// Tried to set the upgrade cost payer to an invalid account. + InvalidUpgradeCostPayer, } /// Pending swap operations. @@ -230,7 +234,7 @@ pub mod pallet { pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; /// Stores all the necessary information about the account currently holding the deposit for - /// the parachain, as well as whether the current or prior depositor has any pending refunds. + /// the parachain, as well as whether the current depositor has any pending refunds. #[pallet::storage] pub type Deposits = StorageMap<_, Twox64Concat, ParaId, DepositInfo>>; @@ -449,33 +453,19 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - let upgrade_cost_payer = if ensure_root(origin.clone()).is_ok() || - Self::parachains().contains(¶) - { - // There are two cases where we do not charge any upgrade costs from the initiator - // of the upgrade: - // - // 1. Root doesn't pay. This also means that system parachains do not pay for - // upgrade fees. - // - // 2. All lease-holding parachains are permitted to do upgrades for free. This is - // introduced to avoid causing a breaking change to the system once para upgrade - // fees are required. - - None - } else if let Ok(caller) = ensure_signed(origin) { - let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; - ensure!(!para_info.is_locked(), Error::::ParaLocked); - ensure!(para_info.manager == caller, Error::::NotOwner); - - Some(caller) - } else { - let location: MultiLocation = (Parent, Parachain(para.into())).into(); - let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); - Some(sovereign_account) - }; - - Self::do_schedule_code_upgrade(para, new_code, upgrade_cost_payer) + // There are two cases where we do not charge any upgrade costs from the initiator + // of the upgrade: + // + // 1. Root doesn't pay. This also means that system parachains do not pay for upgrade + // fees. + // + // 2. All lease-holding parachains are permitted to do upgrades for free. This is + // introduced to avoid causing a breaking change to the system once para upgrade fees + // are required. + let free_upgrade = + ensure_root(origin.clone()).is_ok() || Self::parachains().contains(¶); + + Self::do_schedule_code_upgrade(para, new_code, free_upgrade) } /// Set the parachain's current head. @@ -493,6 +483,43 @@ pub mod pallet { runtime_parachains::set_current_head::(para, new_head); Ok(()) } + + // TODO: add docs & use proper weight + // + /// Can be called by Root, the parachain, or the parachain manager if the parachain is + /// unlocked. + #[pallet::call_index(9)] + #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + pub fn set_upgrade_cost_payer( + origin: OriginFor, + para: ParaId, + cost_payer: T::AccountId, + ) -> DispatchResult { + Self::ensure_root_para_or_owner(origin, para)?; + + let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut deposit_info = + Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + + ensure!(cost_payer != deposit_info.depositor, Error::::InvalidUpgradeCostPayer); + + // When updating the account responsible for all code upgrade costs, we unreserve all + // funds associated with the registered parachain from the original depositor and + // reserve the required amount from the new depositor. + + ::Currency::reserve(&cost_payer, info.deposit)?; + + if cost_payer != deposit_info.depositor { + // After reserving the required funds from the new depositor we can now safely + // refund the old account. + ::Currency::unreserve(&deposit_info.depositor, info.deposit); + } + + deposit_info.depositor = cost_payer; + Deposits::::insert(para, deposit_info); + + Ok(()) + } } } @@ -708,7 +735,7 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `upgrade_cost_payer` isn't specified, there won't be any extra deposit or fees charged + /// If `free_upgrade` is set to true, there won't be any extra deposit or fees charged /// for scheduling the code upgrade. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be @@ -716,7 +743,7 @@ impl Pallet { fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - upgrade_cost_payer: Option, + free_upgrade: bool, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. @@ -731,101 +758,66 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let current_deposit_info = + let mut deposit_info = Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; - let current_depositor = current_deposit_info.depositor.clone(); - - if let Some(payer) = upgrade_cost_payer.clone() { - let new_deposit_info = if payer != current_depositor { - // If the payer is not the one who had their funds reserved for this parachain, we - // will unreserve all funds from the original depositor and reserve the required - // amount from the new depositor. - // - // The primary reason for this is to more easily track the account that has its - // deposit reserved for this parachain. Reserving the deposit across both the para - // manager and the parachain itself could unnecessarily complicate refunds. - - // Update the depositor to the caller. - DepositInfo { depositor: payer.clone(), pending_refund: None } - } else if current_deposit > new_deposit { - // The payer is the current depositor and the existing deposit exceeds the required - // amount. - // - // The excess deposit will be refunded to the caller upon the success of the code - // upgrade. - // - // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of it. - // - // If we returned the deposit here, a possible attack scenario would be to register - // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such a case, the pre-checking process would fail, so - // the old code would remain on-chain even though there is no deposit to cover it. - - DepositInfo { - depositor: payer.clone(), - pending_refund: Some(( - current_depositor.clone(), - current_deposit.saturating_sub(new_deposit), - )), - } - } else { - // The caller is the current depositor and there is no funds to be refunded hence - // the deposit infor remains the same. - current_deposit_info.clone() - }; + if !free_upgrade { // Before dealing with any currency operations, we ensure beforehand that all of them // will be successful. // TODO: Check before charging the fee. ::Currency::withdraw( - &payer, + &deposit_info.depositor, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; + // We need to reserve the difference to account for the increase in the validation code + // size. + + let additional_deposit = new_deposit.saturating_sub(current_deposit); ensure!( - ::Currency::can_reserve(&new_deposit_info.depositor, new_deposit), + ::Currency::can_reserve(&deposit_info.depositor, additional_deposit), DispatchError::Token(TokenError::FundsUnavailable) ); - let additional_deposit = if payer != current_depositor { - // Since the account covering the costs of storing the validation code has changed, - // the entire required deposit will now be reserved from the new account. - new_deposit - } else { - // The depositor did not change, so we only have to reserve the difference to - // account for the increase in the validation code. - new_deposit.saturating_sub(current_deposit) - }; + ::Currency::reserve(&deposit_info.depositor, additional_deposit)?; - ::Currency::reserve(&payer, additional_deposit)?; + if current_deposit > new_deposit { + // The payer is the current depositor and the existing deposit exceeds the required + // amount. + // + // The excess deposit will be refunded to the caller upon the success of the code + // upgrade. + // + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of it. + // + // If we returned the deposit here, a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such a case, the pre-checking process would fail, so + // the old code would remain on-chain even though there is no deposit to cover it. - if payer != current_depositor { - // After reserving the required funds from the new depositor we can now safely - // refund the old account. - ::Currency::unreserve(¤t_depositor, current_deposit); + deposit_info.pending_refund = Some(( + deposit_info.depositor.clone(), + current_deposit.saturating_sub(new_deposit), + )); + Deposits::::insert(para, deposit_info); } // Update the deposit to the new appropriate amount. info.deposit = new_deposit; Paras::::insert(para, info); - - // We update the deposit info if it changed. - if current_deposit_info != new_deposit_info { - Deposits::::insert(para, new_deposit_info); - } } else if current_deposit > new_deposit { // The depositor should receive a refund if the current deposit exceeds the new required // deposit, even if they did not initiate the upgrade. - let deposit_info = DepositInfo { - depositor: current_depositor.clone(), + deposit_info = DepositInfo { + depositor: deposit_info.depositor.clone(), pending_refund: Some(( - current_depositor, + deposit_info.depositor, current_deposit.saturating_sub(new_deposit), )), }; From 3153d6bba33d376d6fded855766457f29acd466a Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 09:00:27 +0100 Subject: [PATCH 031/101] cleanup --- .../runtime/common/src/paras_registrar/mod.rs | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 49fb024ea29a..21d469b35f71 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -213,8 +213,6 @@ pub mod pallet { /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras /// are correct for the swap to work. CannotSwap, - /// Tried to set the upgrade cost payer to an invalid account. - InvalidUpgradeCostPayer, } /// Pending swap operations. @@ -501,8 +499,6 @@ pub mod pallet { let mut deposit_info = Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - ensure!(cost_payer != deposit_info.depositor, Error::::InvalidUpgradeCostPayer); - // When updating the account responsible for all code upgrade costs, we unreserve all // funds associated with the registered parachain from the original depositor and // reserve the required amount from the new depositor. @@ -786,41 +782,30 @@ impl Pallet { ::Currency::reserve(&deposit_info.depositor, additional_deposit)?; - if current_deposit > new_deposit { - // The payer is the current depositor and the existing deposit exceeds the required - // amount. - // - // The excess deposit will be refunded to the caller upon the success of the code - // upgrade. - // - // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of it. - // - // If we returned the deposit here, a possible attack scenario would be to register - // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such a case, the pre-checking process would fail, so - // the old code would remain on-chain even though there is no deposit to cover it. - - deposit_info.pending_refund = Some(( - deposit_info.depositor.clone(), - current_deposit.saturating_sub(new_deposit), - )); - Deposits::::insert(para, deposit_info); - } - // Update the deposit to the new appropriate amount. info.deposit = new_deposit; Paras::::insert(para, info); - } else if current_deposit > new_deposit { + } + + if current_deposit > new_deposit { // The depositor should receive a refund if the current deposit exceeds the new required // deposit, even if they did not initiate the upgrade. - deposit_info = DepositInfo { - depositor: deposit_info.depositor.clone(), - pending_refund: Some(( - deposit_info.depositor, - current_deposit.saturating_sub(new_deposit), - )), - }; + // + // The excess deposit will be refunded to the caller upon the success of the code + // upgrade. + // + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of it. + // + // If we returned the deposit here, a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such a case, the pre-checking process would fail, so + // the old code would remain on-chain even though there is no deposit to cover it. + + deposit_info.pending_refund = Some(( + deposit_info.depositor.clone(), + current_deposit.saturating_sub(new_deposit), + )); Deposits::::insert(para, deposit_info); } From 36f83902f53ea44ec5b3f5728c61b0f522c26ddd Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 09:47:36 +0100 Subject: [PATCH 032/101] cleanup & new test --- polkadot/runtime/common/Cargo.toml | 2 +- .../runtime/common/src/integration_tests.rs | 179 ++++++------------ .../runtime/common/src/paras_registrar/mod.rs | 15 +- polkadot/runtime/rococo/src/lib.rs | 10 +- polkadot/runtime/westend/src/lib.rs | 12 +- 5 files changed, 63 insertions(+), 155 deletions(-) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index c6f88792d617..7e8461c73efa 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -53,7 +53,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index fd50db1fb09d..e61ccd040070 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -32,6 +32,7 @@ use frame_support::{ }; use frame_support_test::TestRandomness; use frame_system::EnsureRoot; +use pallet_balances::Error as BalancesError; use pallet_identity::{self, legacy::IdentityInfo}; use parity_scale_codec::Encode; use primitives::{ @@ -50,9 +51,6 @@ use sp_runtime::{ AccountId32, BuildStorage, }; use sp_std::sync::Arc; -use xcm::opaque::lts::{Junction::Parachain, MultiLocation, NetworkId, Parent}; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; -use xcm_executor::traits::ConvertLocation; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -222,15 +220,8 @@ parameter_types! { pub const ParaDeposit: Balance = 500; pub const DataDepositPerByte: Balance = 1; pub const UpgradeFee: Balance = 2; - pub const RelayNetwork: NetworkId = NetworkId::Kusama; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId32>, -); - impl paras_registrar::Config for Test { type RuntimeEvent = RuntimeEvent; type OnSwap = (Crowdloan, Slots); @@ -239,7 +230,6 @@ impl paras_registrar::Config for Test { type Currency = Balances; type RuntimeOrigin = RuntimeOrigin; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; type WeightInfo = crate::paras_registrar::TestWeightInfo; } @@ -823,7 +813,7 @@ fn root_upgrading_parachain_works() { } #[test] -fn para_upgrading_itself_works() { +fn lease_holding_parachains_have_no_upgrade_costs() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -853,6 +843,7 @@ fn para_upgrading_itself_works() { // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); assert!(Registrar::is_parathread(para_id)); + // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( @@ -860,63 +851,22 @@ fn para_upgrading_itself_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: The parachain itself triggers a code upgrade, doubling the size of the validation - // code. Before the upgrade, the depositor is the parachain manager, and we don't allow - // multiple accounts to reserve deposits for a single parachain. This means the parachain - // must reserve the entire deposit, not just the difference. - // - // However, upon successfully upgrading, the parachain manager should receive a full refund. - - let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); - let sovereign_account = - ::SovereignAccountOf::convert_location(&location) - .unwrap(); - let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); + // Make the para a lease holding parachain. + assert_ok!(Registrar::make_parachain(para_id)); + run_to_session(START_SESSION_INDEX + 4); + assert!(Registrar::is_parachain(para_id)); - Balances::make_free_balance_be(&sovereign_account, free_balance); + // The lease holding parachain should be able to schedule a code upgrade without any + // additional deposit or fees necessary. code_size *= 2; let code_1 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( - para_origin.clone().into(), + signed(1), ParaId::from(para_id), code_1.clone(), )); - conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); - - // After two more sessions the parachain can be upgraded. - run_to_session(START_SESSION_INDEX + 4); - // Force a new head to enact the code upgrade. - assert_ok!(Paras::force_note_new_head( - RuntimeOrigin::root(), - para_id, - genesis_head.clone() - )); - assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - - // The parachain should have a deposit reserved that covers the new code. - let total_bytes_stored = code_size as u32 + head_size as u32; - assert_eq!( - Balances::reserved_balance(&sovereign_account), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) - ); - // An additional upgrade fee should also be deducted from the caller's balance. - assert_eq!(Balances::total_balance(&sovereign_account), free_balance - UpgradeFee::get()); - - // Since all the deposit is now held by the parachain, the manager should have received a - // refund. - assert_eq!(Balances::reserved_balance(&account_id(1)), 0); - - // CASE 2: Schedule a para upgrade to set the validation code to a new one which is twice - // the size of the current one. - code_size *= 2; - let code_2 = validation_code(code_size); - assert_ok!(Registrar::schedule_code_upgrade( - para_origin.clone().into(), - ParaId::from(para_id), - code_2.clone(), - )); - conclude_pvf_checking::(&code_2, VALIDATORS, START_SESSION_INDEX + 4, true); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 4, true); // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 6); @@ -926,53 +876,19 @@ fn para_upgrading_itself_works() { para_id, genesis_head.clone() )); - assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); - // The deposit should be appropriately updated. - let total_bytes_stored = code_size as u32 + head_size as u32; - assert_eq!( - Balances::reserved_balance(&sovereign_account), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) - ); - assert_eq!( - Balances::total_balance(&sovereign_account), - free_balance - 2 * UpgradeFee::get() - ); - - // CASE 3: Schedule a para upgrade to set the validation code half the size of the current - // one. - code_size /= 2; - let code_3 = validation_code(code_size); - assert_ok!(Registrar::schedule_code_upgrade( - para_origin.into(), - ParaId::from(para_id), - code_3.clone(), - )); - conclude_pvf_checking::(&code_3, VALIDATORS, START_SESSION_INDEX + 6, true); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // After two more sessions the parachain can be upgraded. - run_to_session(START_SESSION_INDEX + 8); - // Force a new head to enact the code upgrade. - assert_ok!(Paras::force_note_new_head( - RuntimeOrigin::root(), - para_id, - genesis_head.clone() - )); - assert_eq!(Paras::current_code(¶_id), Some(code_3.clone())); - // The deposit should be appropriately updated. - let total_bytes_stored = code_size as u32 + head_size as u32; + // Should remain unchanged: assert_eq!( - Balances::reserved_balance(&sovereign_account), + Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - assert_eq!( - Balances::total_balance(&sovereign_account), - free_balance - 3 * UpgradeFee::get() - ); + assert_eq!(Balances::total_balance(&account_id(1)), free_balance); }); } #[test] -fn lease_holding_parachains_have_no_upgrade_costs() { +fn changing_upgrade_cost_payer_works() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -981,7 +897,8 @@ fn lease_holding_parachains_have_no_upgrade_costs() { // User 1 will own a parachain let free_balance = 1_000_000_000; - Balances::make_free_balance_be(&account_id(1), free_balance); + let initial_payer = account_id(1); + Balances::make_free_balance_be(&initial_payer, free_balance); // Register an on demand parachain let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); @@ -1002,41 +919,54 @@ fn lease_holding_parachains_have_no_upgrade_costs() { // After two sessions the parachain will be succesfully registered as an on-demand. run_to_session(START_SESSION_INDEX + 2); assert!(Registrar::is_parathread(para_id)); - // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( - Balances::reserved_balance(&account_id(1)), + Balances::reserved_balance(&initial_payer), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // Make the para a lease holding parachain. - assert_ok!(Registrar::make_parachain(para_id)); - run_to_session(START_SESSION_INDEX + 4); - assert!(Registrar::is_parachain(para_id)); + // CASE 1: Attempting to set the code upgrade payer to an account with an insufficient + // balance to cover the required deposit should result in a failure. - // The lease holding parachain should be able to schedule a code upgrade without any - // additional deposit or fees necessary. + // The new payer with an empty balance. + let new_payer = account_id(2); + + assert_noop!( + Registrar::set_upgrade_cost_payer(signed(1), ParaId::from(para_id), new_payer.clone(),), + BalancesError::::InsufficientBalance + ); + + // CASE 2: The happy path.. The new cost payer account is sufficiently funded, so the + // reserve will be successful, and the old account will be refunded. + Balances::make_free_balance_be(&account_id(2), free_balance); + + assert_ok!(Registrar::set_upgrade_cost_payer( + signed(1), + ParaId::from(para_id), + new_payer.clone(), + ),); - let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); - let sovereign_account = - ::SovereignAccountOf::convert_location(&location) - .unwrap(); - let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); + assert_eq!( + Balances::reserved_balance(&new_payer), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + assert_eq!(Balances::free_balance(&initial_payer), free_balance); - Balances::make_free_balance_be(&sovereign_account, free_balance); + // Now, if we attempt to schedule a code upgrade, all the costs will be covered by the new + // payer account. code_size *= 2; let code_1 = validation_code(code_size); assert_ok!(Registrar::schedule_code_upgrade( - para_origin.clone().into(), + signed(1), ParaId::from(para_id), code_1.clone(), )); - conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 4, true); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); // After two more sessions the parachain can be upgraded. - run_to_session(START_SESSION_INDEX + 6); + run_to_session(START_SESSION_INDEX + 4); // Force a new head to enact the code upgrade. assert_ok!(Paras::force_note_new_head( RuntimeOrigin::root(), @@ -1045,13 +975,18 @@ fn lease_holding_parachains_have_no_upgrade_costs() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // Should remain unchanged: + // The new code upgrade payer will have its deposit appropriately adjusted given the + // increase in the new code. + let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( - Balances::reserved_balance(&account_id(1)), + Balances::reserved_balance(&new_payer), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - assert_eq!(Balances::total_balance(&account_id(1)), free_balance); - assert_eq!(Balances::total_balance(&sovereign_account), free_balance); + // The new payer will also be charged with the upgrade fees. + assert_eq!(Balances::total_balance(&new_payer), free_balance - UpgradeFee::get()); + + // The account of the initial payer should however remain unchanged. + assert_eq!(Balances::free_balance(&initial_payer), free_balance); }); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 21d469b35f71..6aa4aaccd31b 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -43,7 +43,6 @@ use sp_runtime::{ traits::{CheckedSub, Saturating}, DispatchError, RuntimeDebug, TokenError, }; -use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { @@ -161,11 +160,6 @@ pub mod pallet { #[pallet::constant] type UpgradeFee: Get>; - /// Type used to get the sovereign account of a parachain. - /// - /// This is used to enable reserving or refunding deposit from parachains. - type SovereignAccountOf: ConvertLocation; - /// Weight Information for the Extrinsics in the Pallet type WeightInfo: WeightInfo; } @@ -802,10 +796,8 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - deposit_info.pending_refund = Some(( - deposit_info.depositor.clone(), - current_deposit.saturating_sub(new_deposit), - )); + deposit_info.pending_refund = + Some((deposit_info.depositor.clone(), current_deposit.saturating_sub(new_deposit))); Deposits::::insert(para, deposit_info); } @@ -909,7 +901,6 @@ mod tests { BuildStorage, Perbill, }; use sp_std::collections::btree_map::BTreeMap; - use xcm::opaque::lts::NetworkId; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -1019,7 +1010,6 @@ mod tests { pub const DataDepositPerByte: Balance = 1; pub const MaxRetries: u32 = 3; pub const UpgradeFee: Balance = 2; - pub const RelayNetwork: NetworkId = NetworkId::Kusama; } impl Config for Test { @@ -1030,7 +1020,6 @@ mod tests { type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = (); type WeightInfo = TestWeightInfo; } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index de0d8e955a96..920a4a253451 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -100,7 +100,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance, NetworkId}, VersionedMultiLocation, }; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; +use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1064,15 +1064,8 @@ impl parachains_slashing::Config for Runtime { parameter_types! { pub const ParaDeposit: Balance = 40 * UNITS; pub const UpgradeFee: Balance = 2 * UNITS; - pub const RelayNetwork: NetworkId = NetworkId::Rococo; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, -); - impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -1080,7 +1073,6 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 3d41149a4ebf..7b2dc0ed37b4 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -96,10 +96,10 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*}; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorMultiLocation, Junction, Junction::PalletInstance, NetworkId}, + latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; +use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1279,15 +1279,8 @@ parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; pub const UpgradeFee: Balance = 50 * CENTS; pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); - pub const RelayNetwork: NetworkId = NetworkId::Westend; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, -); - impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -1296,7 +1289,6 @@ impl paras_registrar::Config for Runtime { type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } From f006db2bb2ee6b4f8569cb11b1cb96a6670e43ca Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 10:38:53 +0100 Subject: [PATCH 033/101] renaming --- .../runtime/common/src/integration_tests.rs | 6 +- .../runtime/common/src/paras_registrar/mod.rs | 100 ++++++++---------- 2 files changed, 49 insertions(+), 57 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index e61ccd040070..8490f61b7d5d 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -888,7 +888,7 @@ fn lease_holding_parachains_have_no_upgrade_costs() { } #[test] -fn changing_upgrade_cost_payer_works() { +fn changing_parachain_stash_works() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -933,7 +933,7 @@ fn changing_upgrade_cost_payer_works() { let new_payer = account_id(2); assert_noop!( - Registrar::set_upgrade_cost_payer(signed(1), ParaId::from(para_id), new_payer.clone(),), + Registrar::set_parachain_stash(signed(1), ParaId::from(para_id), new_payer.clone(),), BalancesError::::InsufficientBalance ); @@ -941,7 +941,7 @@ fn changing_upgrade_cost_payer_works() { // reserve will be successful, and the old account will be refunded. Balances::make_free_balance_be(&account_id(2), free_balance); - assert_ok!(Registrar::set_upgrade_cost_payer( + assert_ok!(Registrar::set_parachain_stash( signed(1), ParaId::from(para_id), new_payer.clone(), diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 6aa4aaccd31b..b9fe9684b545 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -41,7 +41,7 @@ use runtime_parachains::paras::{OnNewHead, ParaKind}; use scale_info::TypeInfo; use sp_runtime::{ traits::{CheckedSub, Saturating}, - DispatchError, RuntimeDebug, TokenError, + RuntimeDebug, }; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] @@ -63,12 +63,16 @@ impl ParaInfo { } #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -pub struct DepositInfo { - /// The `AccountId` that has its deposit reserved for this parachain. +pub struct StashInfo { + /// The stash account of a parachain. This account is responsible for holding the required + /// deposit for this registered parachain. /// - /// when the parachain is registered, this will be set to the parachain manager. However, at - /// a later point, this can be updated by calling the `set_upgrade_cost_payer` extrinsic. - depositor: AccountId, + /// This account will be responsible for covering all associated costs related to performing + /// parachain validation code upgrades. + /// + /// When a parachain is newly registered, this will be set to the parachain manager. However, + /// at a later point, this can be updated by calling the `set_parachain_stash` extrinsic. + stash: AccountId, /// In case there is a pending refund, this stores information about the account and the /// amount that will be refunded upon a successful code upgrade. pending_refund: Option<(AccountId, Balance)>, @@ -181,8 +185,8 @@ pub mod pallet { AlreadyRegistered, /// The caller is not the owner of this Id. NotOwner, - /// The caller is not the depositor of this parachain. - NotDepositor, + /// The caller is not the stash account of this parachain. + NotStash, /// Invalid para code size. CodeTooLarge, /// Invalid para head data size. @@ -213,7 +217,7 @@ pub mod pallet { #[pallet::storage] pub(super) type PendingSwap = StorageMap<_, Twox64Concat, ParaId, ParaId>; - /// Amount held on deposit for each para and the original depositor. + /// Amount held on deposit for each para and the original stash. /// /// The given account ID is responsible for registering the code and initial head data, but may /// only do so if it isn't yet registered. (After that, it's up to governance to do so.) @@ -225,11 +229,11 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores all the necessary information about the account currently holding the deposit for - /// the parachain, as well as whether the current depositor has any pending refunds. + /// Stores all the necessary information about the current stash account of the parachain, as + /// well as whether there are any pending refunds. #[pallet::storage] - pub type Deposits = - StorageMap<_, Twox64Concat, ParaId, DepositInfo>>; + pub type ParaStashes = + StorageMap<_, Twox64Concat, ParaId, StashInfo>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -482,31 +486,31 @@ pub mod pallet { /// unlocked. #[pallet::call_index(9)] #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] - pub fn set_upgrade_cost_payer( + pub fn set_parachain_stash( origin: OriginFor, para: ParaId, - cost_payer: T::AccountId, + new_stash: T::AccountId, ) -> DispatchResult { Self::ensure_root_para_or_owner(origin, para)?; let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut deposit_info = - Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut stash_info = + ParaStashes::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; // When updating the account responsible for all code upgrade costs, we unreserve all - // funds associated with the registered parachain from the original depositor and - // reserve the required amount from the new depositor. + // funds associated with the registered parachain from the original stash and reserve + // the required amount from the new one. - ::Currency::reserve(&cost_payer, info.deposit)?; + ::Currency::reserve(&new_stash, info.deposit)?; - if cost_payer != deposit_info.depositor { - // After reserving the required funds from the new depositor we can now safely + if new_stash != stash_info.stash { + // After reserving the required funds from the new stash we can now safely // refund the old account. - ::Currency::unreserve(&deposit_info.depositor, info.deposit); + ::Currency::unreserve(&stash_info.stash, info.deposit); } - deposit_info.depositor = cost_payer; - Deposits::::insert(para, deposit_info); + stash_info.stash = new_stash; + ParaStashes::::insert(para, stash_info); Ok(()) } @@ -693,10 +697,10 @@ impl Pallet { ::Currency::unreserve(&who, rebate); }; let info = ParaInfo { manager: who.clone(), deposit, locked: None }; - let deposit_info = DepositInfo { depositor: who.clone(), pending_refund: None }; + let stash_info = StashInfo { stash: who.clone(), pending_refund: None }; Paras::::insert(id, info); - Deposits::::insert(id, deposit_info); + ParaStashes::::insert(id, stash_info); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -748,33 +752,21 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut deposit_info = - Deposits::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut stash_info = + ParaStashes::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; if !free_upgrade { - // Before dealing with any currency operations, we ensure beforehand that all of them - // will be successful. - // TODO: Check before charging the fee. - ::Currency::withdraw( - &deposit_info.depositor, + &stash_info.stash, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; - // We need to reserve the difference to account for the increase in the validation code - // size. - let additional_deposit = new_deposit.saturating_sub(current_deposit); - ensure!( - ::Currency::can_reserve(&deposit_info.depositor, additional_deposit), - DispatchError::Token(TokenError::FundsUnavailable) - ); - - ::Currency::reserve(&deposit_info.depositor, additional_deposit)?; + ::Currency::reserve(&stash_info.stash, additional_deposit)?; // Update the deposit to the new appropriate amount. info.deposit = new_deposit; @@ -782,8 +774,8 @@ impl Pallet { } if current_deposit > new_deposit { - // The depositor should receive a refund if the current deposit exceeds the new required - // deposit, even if they did not initiate the upgrade. + // The stash account should receive a refund if the current deposit exceeds the new + // required deposit, even if they did not initiate the upgrade. // // The excess deposit will be refunded to the caller upon the success of the code // upgrade. @@ -796,9 +788,9 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - deposit_info.pending_refund = - Some((deposit_info.depositor.clone(), current_deposit.saturating_sub(new_deposit))); - Deposits::::insert(para, deposit_info); + stash_info.pending_refund = + Some((stash_info.stash.clone(), current_deposit.saturating_sub(new_deposit))); + ParaStashes::::insert(para, stash_info); } runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) @@ -856,18 +848,18 @@ impl OnNewHead for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let maybe_deposit_info = Deposits::::get(id); - if maybe_deposit_info.is_none() { + let maybe_stash_info = ParaStashes::::get(id); + if maybe_stash_info.is_none() { return T::DbWeight::get().reads(1) } - let mut deposit_info = maybe_deposit_info + let mut stash_info = maybe_stash_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some((who, rebate)) = deposit_info.pending_refund { + if let Some((who, rebate)) = stash_info.pending_refund { ::Currency::unreserve(&who, rebate); - deposit_info.pending_refund = None; - Deposits::::insert(id, deposit_info); + stash_info.pending_refund = None; + ParaStashes::::insert(id, stash_info); return T::DbWeight::get().reads_writes(2, 2) } From 35f65eec10415f6b648ab5c566bcf531da28efef Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 11:00:59 +0100 Subject: [PATCH 034/101] add benchmark & docs --- .../runtime/common/src/paras_registrar/mod.rs | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index b9fe9684b545..e1c0409b815b 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -89,6 +89,7 @@ pub trait WeightInfo { fn swap() -> Weight; fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; + fn set_parachain_stash() -> Weight; } pub struct TestWeightInfo; @@ -114,6 +115,9 @@ impl WeightInfo for TestWeightInfo { fn set_current_head(_b: u32) -> Weight { Weight::zero() } + fn set_parachain_stash() -> Weight { + Weight::zero() + } } #[frame_support::pallet] @@ -480,12 +484,19 @@ pub mod pallet { Ok(()) } - // TODO: add docs & use proper weight - // + /// Changes the stash account of a parachain. + /// + /// The newly set account will be responsible for covering all associated costs related to + /// performing parachain validation code upgrades. + /// + /// ## Deposits/Fees + /// For this call to be successful, the `new_stash` account must have a sufficient balance + /// to cover the current deposit required for this parachain. + /// /// Can be called by Root, the parachain, or the parachain manager if the parachain is /// unlocked. #[pallet::call_index(9)] - #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] + #[pallet::weight(::WeightInfo::set_parachain_stash())] pub fn set_parachain_stash( origin: OriginFor, para: ParaId, @@ -503,11 +514,9 @@ pub mod pallet { ::Currency::reserve(&new_stash, info.deposit)?; - if new_stash != stash_info.stash { - // After reserving the required funds from the new stash we can now safely - // refund the old account. - ::Currency::unreserve(&stash_info.stash, info.deposit); - } + // After reserving the required funds from the new stash we can now safely + // refund the old account. + ::Currency::unreserve(&stash_info.stash, info.deposit); stash_info.stash = new_stash; ParaStashes::::insert(para, stash_info); @@ -1785,6 +1794,22 @@ mod benchmarking { let para_id = ParaId::from(1000); }: _(RawOrigin::Root, para_id, new_head) + set_parachain_stash { + let para = register_para::(LOWEST_PUBLIC_ID.into()); + // Actually finish registration process + next_scheduled_session::(); + + let stash: T::AccountId = account("stash", 0, 0); + T::Currency::make_free_balance_be(&stash, BalanceOf::::max_value()); + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller.clone()), para, stash.clone()) + verify { + assert_eq!(ParaStashes::::get(para), Some(StashInfo { + stash, + pending_refund: None + })); + } + impl_benchmark_test_suite!( Registrar, crate::integration_tests::new_test_ext(), From 064464bfe392d2fc597eeabda8eca1c43480b5f4 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 12:02:11 +0100 Subject: [PATCH 035/101] update schedule_code_upgrade benchmark --- .../runtime/common/src/paras_registrar/mod.rs | 26 ++++++++++++------- polkadot/runtime/rococo/src/lib.rs | 2 +- .../weights/runtime_common_paras_registrar.rs | 14 ++++++++++ .../weights/runtime_common_paras_registrar.rs | 14 ++++++++++ 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index e1c0409b815b..2fcf6e9a1370 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1666,10 +1666,9 @@ mod benchmarking { assert_eq!(event, &system_event); } - fn register_para(id: u32) -> ParaId { + fn register_para(id: u32, validation_code: ValidationCode) -> ParaId { let para = ParaId::from(id); let genesis_head = Registrar::::worst_head_data(); - let validation_code = Registrar::::worst_validation_code(); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); assert_ok!(Registrar::::reserve(RawOrigin::Signed(caller.clone()).into())); @@ -1747,7 +1746,7 @@ mod benchmarking { } deregister { - let para = register_para::(LOWEST_PUBLIC_ID.into()); + let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); next_scheduled_session::(); let caller: T::AccountId = whitelisted_caller(); }: _(RawOrigin::Signed(caller), para) @@ -1757,8 +1756,8 @@ mod benchmarking { swap { // On demand parachain - let parathread = register_para::(LOWEST_PUBLIC_ID.into()); - let parachain = register_para::((LOWEST_PUBLIC_ID + 1).into()); + let parathread = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); + let parachain = register_para::((LOWEST_PUBLIC_ID + 1).into(), Registrar::::worst_validation_code()); let parachain_origin = para_origin(parachain.into()); @@ -1783,10 +1782,17 @@ mod benchmarking { } schedule_code_upgrade { - let b in 1 .. MAX_CODE_SIZE; - let new_code = ValidationCode(vec![0; b as usize]); - let para_id = ParaId::from(1000); - }: _(RawOrigin::Root, para_id, new_code) + // The 'worst case' scenario in terms of benchmarking is when the upgrade isn't free + // and there is a refund. + + let b in 2 .. MAX_CODE_SIZE; + let initial_code = ValidationCode(vec![0; b as usize]); + let new_code = ValidationCode(vec![0; b as usize - 1]); + + let para = register_para::(LOWEST_PUBLIC_ID.into(), initial_code); + // Actually finish registration process + next_scheduled_session::(); + }: _(RawOrigin::Root, para, new_code) set_current_head { let b in 1 .. MAX_HEAD_DATA_SIZE; @@ -1795,7 +1801,7 @@ mod benchmarking { }: _(RawOrigin::Root, para_id, new_head) set_parachain_stash { - let para = register_para::(LOWEST_PUBLIC_ID.into()); + let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); // Actually finish registration process next_scheduled_session::(); diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 920a4a253451..9079b154b617 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -97,7 +97,7 @@ use sp_staking::SessionIndex; use sp_version::NativeVersion; use sp_version::RuntimeVersion; use xcm::{ - latest::{InteriorMultiLocation, Junction, Junction::PalletInstance, NetworkId}, + latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; use xcm_builder::PayOverXcm; diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 0a56562a1a95..395e3e48f47b 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -218,4 +218,18 @@ impl runtime_common::paras_registrar::WeightInfo for We .saturating_add(Weight::from_parts(855, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn set_parachain_stash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 50290c0fe59f..3943b362b38a 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -215,4 +215,18 @@ impl runtime_common::paras_registrar::WeightInfo for We .saturating_add(Weight::from_parts(1_029, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn set_parachain_stash() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } } From d7f33ad3f9ddb0c10a542a74a3b919ea4867df4b Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 12:04:26 +0100 Subject: [PATCH 036/101] fmt --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2fcf6e9a1370..9ee2c5f1370c 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1783,7 +1783,7 @@ mod benchmarking { schedule_code_upgrade { // The 'worst case' scenario in terms of benchmarking is when the upgrade isn't free - // and there is a refund. + // and there is a refund. let b in 2 .. MAX_CODE_SIZE; let initial_code = ValidationCode(vec![0; b as usize]); From 26e6779bc64f3eab4768a991ed67c58cf92bc135 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 16:23:33 +0100 Subject: [PATCH 037/101] fixes --- polkadot/runtime/common/Cargo.toml | 2 +- .../runtime/common/src/integration_tests.rs | 60 ++++--- .../runtime/common/src/paras_registrar/mod.rs | 168 ++++++++++++------ polkadot/runtime/rococo/src/lib.rs | 9 +- polkadot/runtime/westend/src/lib.rs | 9 +- 5 files changed, 165 insertions(+), 83 deletions(-) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 7e8461c73efa..c6f88792d617 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -53,7 +53,7 @@ runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parac slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { package = "staging-xcm", path = "../../xcm", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false, optional = true } +xcm-executor = { package = "staging-xcm-executor", path = "../../xcm/xcm-executor", default-features = false } pallet-xcm-benchmarks = { path = "../../xcm/pallet-xcm-benchmarks", default-features = false, optional = true } xcm-builder = { package = "staging-xcm-builder", path = "../../xcm/xcm-builder", default-features = false } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 8490f61b7d5d..f35afd526252 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -51,6 +51,9 @@ use sp_runtime::{ AccountId32, BuildStorage, }; use sp_std::sync::Arc; +use xcm::opaque::lts::{Junction::Parachain, MultiLocation, NetworkId, Parent}; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; +use xcm_executor::traits::ConvertLocation; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -220,8 +223,15 @@ parameter_types! { pub const ParaDeposit: Balance = 500; pub const DataDepositPerByte: Balance = 1; pub const UpgradeFee: Balance = 2; + pub const RelayNetwork: NetworkId = NetworkId::Kusama; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId32>, +); + impl paras_registrar::Config for Test { type RuntimeEvent = RuntimeEvent; type OnSwap = (Crowdloan, Slots); @@ -230,6 +240,7 @@ impl paras_registrar::Config for Test { type Currency = Balances; type RuntimeOrigin = RuntimeOrigin; type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type WeightInfo = crate::paras_registrar::TestWeightInfo; } @@ -888,7 +899,7 @@ fn lease_holding_parachains_have_no_upgrade_costs() { } #[test] -fn changing_parachain_stash_works() { +fn changing_parachain_billing_account_works() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -897,8 +908,7 @@ fn changing_parachain_stash_works() { // User 1 will own a parachain let free_balance = 1_000_000_000; - let initial_payer = account_id(1); - Balances::make_free_balance_be(&initial_payer, free_balance); + Balances::make_free_balance_be(&account_id(1), free_balance); // Register an on demand parachain let mut code_size = 1024 * 1024; let genesis_head = Registrar::worst_head_data(); @@ -922,39 +932,41 @@ fn changing_parachain_stash_works() { // The deposit should be appropriately taken. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( - Balances::reserved_balance(&initial_payer), + Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Attempting to set the code upgrade payer to an account with an insufficient - // balance to cover the required deposit should result in a failure. + // CASE 1: Attempting to set the billing account for the parachain to the parachain sovereign + // account with an insufficient balance to cover the required deposit should result in a failure. - // The new payer with an empty balance. - let new_payer = account_id(2); + let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); + let sovereign_account = + ::SovereignAccountOf::convert_location(&location) + .unwrap(); + let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); assert_noop!( - Registrar::set_parachain_stash(signed(1), ParaId::from(para_id), new_payer.clone(),), + Registrar::set_parachain_billing_account_to_self(para_origin.clone().into(), ParaId::from(para_id)), BalancesError::::InsufficientBalance ); - // CASE 2: The happy path.. The new cost payer account is sufficiently funded, so the + // CASE 2: The happy path.. The new billing account is sufficiently funded, so the // reserve will be successful, and the old account will be refunded. - Balances::make_free_balance_be(&account_id(2), free_balance); + Balances::make_free_balance_be(&sovereign_account, free_balance); - assert_ok!(Registrar::set_parachain_stash( - signed(1), + assert_ok!(Registrar::set_parachain_billing_account_to_self( + para_origin.into(), ParaId::from(para_id), - new_payer.clone(), - ),); + )); assert_eq!( - Balances::reserved_balance(&new_payer), + Balances::reserved_balance(&sovereign_account), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - assert_eq!(Balances::free_balance(&initial_payer), free_balance); + assert_eq!(Balances::free_balance(&account_id(1)), free_balance); // Now, if we attempt to schedule a code upgrade, all the costs will be covered by the new - // payer account. + // billing account. code_size *= 2; let code_1 = validation_code(code_size); @@ -975,18 +987,18 @@ fn changing_parachain_stash_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // The new code upgrade payer will have its deposit appropriately adjusted given the + // The new code upgrade billing account will have its deposit appropriately adjusted given the // increase in the new code. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( - Balances::reserved_balance(&new_payer), + Balances::reserved_balance(&sovereign_account), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // The new payer will also be charged with the upgrade fees. - assert_eq!(Balances::total_balance(&new_payer), free_balance - UpgradeFee::get()); + // The new billing account will also be charged with the upgrade fees. + assert_eq!(Balances::total_balance(&sovereign_account), free_balance - UpgradeFee::get()); - // The account of the initial payer should however remain unchanged. - assert_eq!(Balances::free_balance(&initial_payer), free_balance); + // The account of the initial billing account should however remain unchanged. + assert_eq!(Balances::free_balance(&account_id(1)), free_balance); }); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 9ee2c5f1370c..4755220c5709 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -43,6 +43,8 @@ use sp_runtime::{ traits::{CheckedSub, Saturating}, RuntimeDebug, }; +use xcm_executor::traits::ConvertLocation; +use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { @@ -63,16 +65,16 @@ impl ParaInfo { } #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -pub struct StashInfo { - /// The stash account of a parachain. This account is responsible for holding the required +pub struct BillingInfo { + /// The billing account of a parachain. This account is responsible for holding the required /// deposit for this registered parachain. /// /// This account will be responsible for covering all associated costs related to performing /// parachain validation code upgrades. /// /// When a parachain is newly registered, this will be set to the parachain manager. However, - /// at a later point, this can be updated by calling the `set_parachain_stash` extrinsic. - stash: AccountId, + /// at a later point, this can be updated by calling the `set_parachain_billing_account_to_self` extrinsic. + billing_account: AccountId, /// In case there is a pending refund, this stores information about the account and the /// amount that will be refunded upon a successful code upgrade. pending_refund: Option<(AccountId, Balance)>, @@ -89,7 +91,7 @@ pub trait WeightInfo { fn swap() -> Weight; fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; - fn set_parachain_stash() -> Weight; + fn set_parachain_billing_account_to_self() -> Weight; } pub struct TestWeightInfo; @@ -115,7 +117,7 @@ impl WeightInfo for TestWeightInfo { fn set_current_head(_b: u32) -> Weight { Weight::zero() } - fn set_parachain_stash() -> Weight { + fn set_parachain_billing_account_to_self() -> Weight { Weight::zero() } } @@ -168,6 +170,11 @@ pub mod pallet { #[pallet::constant] type UpgradeFee: Get>; + /// Type used to get the sovereign account of a parachain. + /// + /// This is used to enable reserving or refunding deposit from parachains. + type SovereignAccountOf: ConvertLocation; + /// Weight Information for the Extrinsics in the Pallet type WeightInfo: WeightInfo; } @@ -189,8 +196,6 @@ pub mod pallet { AlreadyRegistered, /// The caller is not the owner of this Id. NotOwner, - /// The caller is not the stash account of this parachain. - NotStash, /// Invalid para code size. CodeTooLarge, /// Invalid para head data size. @@ -221,7 +226,7 @@ pub mod pallet { #[pallet::storage] pub(super) type PendingSwap = StorageMap<_, Twox64Concat, ParaId, ParaId>; - /// Amount held on deposit for each para and the original stash. + /// Amount held on deposit for each para and the original billing account. /// /// The given account ID is responsible for registering the code and initial head data, but may /// only do so if it isn't yet registered. (After that, it's up to governance to do so.) @@ -233,11 +238,11 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores all the necessary information about the current stash account of the parachain, as + /// Stores all the necessary information about the current billing account of the parachain, as /// well as whether there are any pending refunds. #[pallet::storage] - pub type ParaStashes = - StorageMap<_, Twox64Concat, ParaId, StashInfo>>; + pub type ParaBillings = + StorageMap<_, Twox64Concat, ParaId, BillingInfo>>; #[pallet::genesis_config] pub struct GenesisConfig { @@ -484,42 +489,60 @@ pub mod pallet { Ok(()) } - /// Changes the stash account of a parachain. - /// - /// The newly set account will be responsible for covering all associated costs related to - /// performing parachain validation code upgrades. + /// Changes the billing account of a parachain to the caller. /// /// ## Deposits/Fees - /// For this call to be successful, the `new_stash` account must have a sufficient balance + /// For this call to be successful, the caller account must have a sufficient balance /// to cover the current deposit required for this parachain. /// /// Can be called by Root, the parachain, or the parachain manager if the parachain is /// unlocked. #[pallet::call_index(9)] - #[pallet::weight(::WeightInfo::set_parachain_stash())] - pub fn set_parachain_stash( + #[pallet::weight(::WeightInfo::set_parachain_billing_account_to_self())] + pub fn set_parachain_billing_account_to_self( origin: OriginFor, para: ParaId, - new_stash: T::AccountId, ) -> DispatchResult { - Self::ensure_root_para_or_owner(origin, para)?; + Self::ensure_root_para_or_owner(origin.clone(), para)?; - let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut stash_info = - ParaStashes::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let new_billing_account = if let Ok(caller) = ensure_signed(origin.clone()) { + let para_info = Paras::::get(para).ok_or(Error::::NotRegistered)?; + ensure!(!para_info.is_locked(), Error::::ParaLocked); + ensure!(para_info.manager == caller, Error::::NotOwner); - // When updating the account responsible for all code upgrade costs, we unreserve all - // funds associated with the registered parachain from the original stash and reserve - // the required amount from the new one. + caller + } else if ensure_root(origin).is_ok() { + // Root should use `force_set_parachain_billing_account` since it doesn't make sense + // for the root to set itself as the billing account. + return Ok(()) + }else { + let location: MultiLocation = (Parent, Parachain(para.into())).into(); + let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); + sovereign_account + }; - ::Currency::reserve(&new_stash, info.deposit)?; + Self::set_parachain_billing_account(para, new_billing_account)?; - // After reserving the required funds from the new stash we can now safely - // refund the old account. - ::Currency::unreserve(&stash_info.stash, info.deposit); + Ok(()) + } - stash_info.stash = new_stash; - ParaStashes::::insert(para, stash_info); + /// Sets the billing account of a parachain to the caller. + /// + /// ## Deposits/Fees + /// For this call to be successful, the `new_billing_account` account must have a sufficient balance + /// to cover the current deposit required for this parachain. + /// + /// Can only be called by Root. + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::set_parachain_billing_account_to_self())] + pub fn force_set_parachain_billing_account( + origin: OriginFor, + para: ParaId, + new_billing_account: T::AccountId + ) -> DispatchResult { + ensure_root(origin)?; + + Self::set_parachain_billing_account(para, new_billing_account)?; Ok(()) } @@ -706,10 +729,10 @@ impl Pallet { ::Currency::unreserve(&who, rebate); }; let info = ParaInfo { manager: who.clone(), deposit, locked: None }; - let stash_info = StashInfo { stash: who.clone(), pending_refund: None }; + let billing_info = BillingInfo { billing_account: who.clone(), pending_refund: None }; Paras::::insert(id, info); - ParaStashes::::insert(id, stash_info); + ParaBillings::::insert(id, billing_info); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -761,21 +784,21 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut stash_info = - ParaStashes::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut billing_info = + ParaBillings::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; let current_deposit = info.deposit; if !free_upgrade { ::Currency::withdraw( - &stash_info.stash, + &billing_info.billing_account, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&stash_info.stash, additional_deposit)?; + ::Currency::reserve(&billing_info.billing_account, additional_deposit)?; // Update the deposit to the new appropriate amount. info.deposit = new_deposit; @@ -783,7 +806,7 @@ impl Pallet { } if current_deposit > new_deposit { - // The stash account should receive a refund if the current deposit exceeds the new + // The billing account should receive a refund if the current deposit exceeds the new // required deposit, even if they did not initiate the upgrade. // // The excess deposit will be refunded to the caller upon the success of the code @@ -797,9 +820,9 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - stash_info.pending_refund = - Some((stash_info.stash.clone(), current_deposit.saturating_sub(new_deposit))); - ParaStashes::::insert(para, stash_info); + billing_info.pending_refund = + Some((billing_info.billing_account.clone(), current_deposit.saturating_sub(new_deposit))); + ParaBillings::::insert(para, billing_info); } runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) @@ -838,6 +861,34 @@ impl Pallet { debug_assert!(res2.is_ok()); T::OnSwap::on_swap(to_upgrade, to_downgrade); } + + /// Sets the billing account of a parachain. + /// + /// The newly set account will be responsible for covering all associated costs related to + /// performing parachain validation code upgrades. + fn set_parachain_billing_account( + para: ParaId, + new_billing_account: T::AccountId, + ) -> DispatchResult { + let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut billing_info = + ParaBillings::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + + // When updating the account responsible for all code upgrade costs, we unreserve all + // funds associated with the registered parachain from the original billing account and + // reserves the required amount from the new one. + + ::Currency::reserve(&new_billing_account, info.deposit)?; + + // After reserving the required funds from the new billing account we can now safely + // refund the old account. + ::Currency::unreserve(&billing_info.billing_account, info.deposit); + + billing_info.billing_account = new_billing_account; + ParaBillings::::insert(para, billing_info); + + Ok(()) + } } impl OnNewHead for Pallet { @@ -857,18 +908,18 @@ impl OnNewHead for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let maybe_stash_info = ParaStashes::::get(id); - if maybe_stash_info.is_none() { + let maybe_billing_info = ParaBillings::::get(id); + if maybe_billing_info.is_none() { return T::DbWeight::get().reads(1) } - let mut stash_info = maybe_stash_info + let mut billing_info = maybe_billing_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some((who, rebate)) = stash_info.pending_refund { + if let Some((who, rebate)) = billing_info.pending_refund { ::Currency::unreserve(&who, rebate); - stash_info.pending_refund = None; - ParaStashes::::insert(id, stash_info); + billing_info.pending_refund = None; + ParaBillings::::insert(id, billing_info); return T::DbWeight::get().reads_writes(2, 2) } @@ -1021,6 +1072,7 @@ mod tests { type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type UpgradeFee = UpgradeFee; + type SovereignAccountOf = (); type WeightInfo = TestWeightInfo; } @@ -1800,18 +1852,22 @@ mod benchmarking { let para_id = ParaId::from(1000); }: _(RawOrigin::Root, para_id, new_head) - set_parachain_stash { + set_parachain_billing_account_to_self { let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); // Actually finish registration process next_scheduled_session::(); - let stash: T::AccountId = account("stash", 0, 0); - T::Currency::make_free_balance_be(&stash, BalanceOf::::max_value()); - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller.clone()), para, stash.clone()) + let location: MultiLocation = (Parent, Parachain(LOWEST_PUBLIC_ID.into())).into(); + let sovereign_account = + ::SovereignAccountOf::convert_location(&location) + .unwrap(); + T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); + + let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); + }: _(para_origin, para) verify { - assert_eq!(ParaStashes::::get(para), Some(StashInfo { - stash, + assert_eq!(ParaBillings::::get(para), Some(BillingInfo { + billing_account: sovereign_account, pending_refund: None })); } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 9079b154b617..69e3a7198765 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -100,7 +100,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::PayOverXcm; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1061,6 +1061,12 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, +); + parameter_types! { pub const ParaDeposit: Balance = 40 * UNITS; pub const UpgradeFee: Balance = 2 * UNITS; @@ -1073,6 +1079,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 7b2dc0ed37b4..6b03a358ec7f 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -99,7 +99,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::PayOverXcm; +use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -1281,6 +1281,12 @@ parameter_types! { pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); } +pub type LocationToAccountId = ( + ChildParachainConvertsVia, + AccountId32Aliases, + Account32Hash<(), AccountId>, +); + impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -1289,6 +1295,7 @@ impl paras_registrar::Config for Runtime { type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationToAccountId; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } From 8a0789af2fc268e120134291a9e1826d5119a729 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 16:34:43 +0100 Subject: [PATCH 038/101] force_set_parachain_billing_account_works --- .../runtime/common/src/integration_tests.rs | 119 +++++++++++++++++- .../runtime/common/src/paras_registrar/mod.rs | 23 ++-- 2 files changed, 126 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index f35afd526252..73cba2d53b51 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -899,7 +899,7 @@ fn lease_holding_parachains_have_no_upgrade_costs() { } #[test] -fn changing_parachain_billing_account_works() { +fn setting_parachain_billing_account_to_self_works() { new_test_ext().execute_with(|| { assert!(System::block_number().is_one()); /* So events are emitted */ let para_id = LOWEST_PUBLIC_ID; @@ -936,8 +936,9 @@ fn changing_parachain_billing_account_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Attempting to set the billing account for the parachain to the parachain sovereign - // account with an insufficient balance to cover the required deposit should result in a failure. + // CASE 1: Attempting to set the billing account for the parachain to the parachain + // sovereign account with an insufficient balance to cover the required deposit should + // result in a failure. let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); let sovereign_account = @@ -946,7 +947,10 @@ fn changing_parachain_billing_account_works() { let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); assert_noop!( - Registrar::set_parachain_billing_account_to_self(para_origin.clone().into(), ParaId::from(para_id)), + Registrar::set_parachain_billing_account_to_self( + para_origin.clone().into(), + ParaId::from(para_id) + ), BalancesError::::InsufficientBalance ); @@ -987,8 +991,8 @@ fn changing_parachain_billing_account_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // The new code upgrade billing account will have its deposit appropriately adjusted given the - // increase in the new code. + // The new code upgrade billing account will have its deposit appropriately adjusted given + // the increase in the new code. let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&sovereign_account), @@ -1002,6 +1006,109 @@ fn changing_parachain_billing_account_works() { }); } +#[test] +fn force_set_parachain_billing_account_works() { + new_test_ext().execute_with(|| { + assert!(System::block_number().is_one()); /* So events are emitted */ + let para_id = LOWEST_PUBLIC_ID; + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + // User 1 will own a parachain + let free_balance = 1_000_000_000; + Balances::make_free_balance_be(&account_id(1), free_balance); + // Register an on demand parachain + let mut code_size = 1024 * 1024; + let genesis_head = Registrar::worst_head_data(); + let head_size = genesis_head.0.len(); + let code_0 = validation_code(code_size); + + assert_ok!(Registrar::reserve(signed(1))); + assert_ok!(Registrar::register( + signed(1), + ParaId::from(para_id), + genesis_head.clone(), + code_0.clone(), + )); + conclude_pvf_checking::(&code_0, VALIDATORS, START_SESSION_INDEX, true); + + // The para should be onboarding. + assert_eq!(Paras::lifecycle(ParaId::from(para_id)), Some(ParaLifecycle::Onboarding)); + // After two sessions the parachain will be succesfully registered as an on-demand. + run_to_session(START_SESSION_INDEX + 2); + assert!(Registrar::is_parathread(para_id)); + // The deposit should be appropriately taken. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + + // CASE 1: Attempting to set the billing account with an insufficient balance to cover + // the required deposit should result in a failure. + + assert_noop!( + Registrar::force_set_parachain_billing_account( + RuntimeOrigin::root(), + ParaId::from(para_id), + account_id(2), + ), + BalancesError::::InsufficientBalance + ); + + // CASE 2: The happy path.. The new billing account is sufficiently funded, so the + // reserve will be successful, and the old account will be refunded. + Balances::make_free_balance_be(&&account_id(2), free_balance); + + assert_ok!(Registrar::force_set_parachain_billing_account( + RuntimeOrigin::root(), + ParaId::from(para_id), + account_id(2) + )); + + assert_eq!( + Balances::reserved_balance(&account_id(2)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + assert_eq!(Balances::free_balance(&account_id(1)), free_balance); + + // Now, if we attempt to schedule a code upgrade, all the costs will be covered by the new + // billing account. + + code_size *= 2; + let code_1 = validation_code(code_size); + assert_ok!(Registrar::schedule_code_upgrade( + signed(1), + ParaId::from(para_id), + code_1.clone(), + )); + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + + // After two more sessions the parachain can be upgraded. + run_to_session(START_SESSION_INDEX + 4); + // Force a new head to enact the code upgrade. + assert_ok!(Paras::force_note_new_head( + RuntimeOrigin::root(), + para_id, + genesis_head.clone() + )); + assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); + + // The new code upgrade billing account will have its deposit appropriately adjusted given + // the increase in the new code. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + Balances::reserved_balance(&account_id(2)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // The new billing account will also be charged with the upgrade fees. + assert_eq!(Balances::total_balance(&account_id(2)), free_balance - UpgradeFee::get()); + + // The account of the initial billing account should however remain unchanged. + assert_eq!(Balances::free_balance(&account_id(1)), free_balance); + }); +} + #[test] fn basic_errors_fail() { new_test_ext().execute_with(|| { diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 4755220c5709..4a8ffaf04697 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -43,8 +43,8 @@ use sp_runtime::{ traits::{CheckedSub, Saturating}, RuntimeDebug, }; -use xcm_executor::traits::ConvertLocation; use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; +use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { @@ -73,7 +73,8 @@ pub struct BillingInfo { /// parachain validation code upgrades. /// /// When a parachain is newly registered, this will be set to the parachain manager. However, - /// at a later point, this can be updated by calling the `set_parachain_billing_account_to_self` extrinsic. + /// at a later point, this can be updated by calling the + /// `set_parachain_billing_account_to_self` extrinsic. billing_account: AccountId, /// In case there is a pending refund, this stores information about the account and the /// amount that will be refunded upon a successful code upgrade. @@ -512,10 +513,10 @@ pub mod pallet { caller } else if ensure_root(origin).is_ok() { - // Root should use `force_set_parachain_billing_account` since it doesn't make sense + // Root should use `force_set_parachain_billing_account` since it doesn't make sense // for the root to set itself as the billing account. return Ok(()) - }else { + } else { let location: MultiLocation = (Parent, Parachain(para.into())).into(); let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); sovereign_account @@ -529,8 +530,8 @@ pub mod pallet { /// Sets the billing account of a parachain to the caller. /// /// ## Deposits/Fees - /// For this call to be successful, the `new_billing_account` account must have a sufficient balance - /// to cover the current deposit required for this parachain. + /// For this call to be successful, the `new_billing_account` account must have a sufficient + /// balance to cover the current deposit required for this parachain. /// /// Can only be called by Root. #[pallet::call_index(10)] @@ -538,7 +539,7 @@ pub mod pallet { pub fn force_set_parachain_billing_account( origin: OriginFor, para: ParaId, - new_billing_account: T::AccountId + new_billing_account: T::AccountId, ) -> DispatchResult { ensure_root(origin)?; @@ -820,8 +821,10 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - billing_info.pending_refund = - Some((billing_info.billing_account.clone(), current_deposit.saturating_sub(new_deposit))); + billing_info.pending_refund = Some(( + billing_info.billing_account.clone(), + current_deposit.saturating_sub(new_deposit), + )); ParaBillings::::insert(para, billing_info); } @@ -875,7 +878,7 @@ impl Pallet { ParaBillings::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; // When updating the account responsible for all code upgrade costs, we unreserve all - // funds associated with the registered parachain from the original billing account and + // funds associated with the registered parachain from the original billing account and // reserves the required amount from the new one. ::Currency::reserve(&new_billing_account, info.deposit)?; From 04339d9937198420fc85f384dfcc15124607950b Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:27:58 +0100 Subject: [PATCH 039/101] Update polkadot/runtime/parachains/src/paras/mod.rs Co-authored-by: Francisco Aguirre --- polkadot/runtime/parachains/src/paras/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 9b30ed2cdb88..c61e8fa89807 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -507,7 +507,7 @@ impl OnNewHead for Tuple { } pub trait OnCodeUpgrade { - /// A function to execute some custom logic once the pre-chekcing is successfully completed. + /// A function to execute some custom logic once the pre-checking is successfully completed. /// /// This is currently used by the registrar pallet to perform refunds upon validation code /// size reduction. From c380dfa63cfb45859938f63de62f0ea138a689d1 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 19:52:46 +0100 Subject: [PATCH 040/101] resolve comments --- .../runtime/common/src/integration_tests.rs | 8 ++----- .../runtime/common/src/paras_registrar/mod.rs | 24 ++++++++++++++++++- polkadot/runtime/rococo/src/lib.rs | 14 ++++------- .../weights/runtime_common_paras_registrar.rs | 16 ++++++++++++- polkadot/runtime/westend/src/lib.rs | 14 ++++------- .../weights/runtime_common_paras_registrar.rs | 16 ++++++++++++- 6 files changed, 65 insertions(+), 27 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 73cba2d53b51..f33e7a187b35 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -52,7 +52,7 @@ use sp_runtime::{ }; use sp_std::sync::Arc; use xcm::opaque::lts::{Junction::Parachain, MultiLocation, NetworkId, Parent}; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia}; +use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription}; use xcm_executor::traits::ConvertLocation; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -226,11 +226,7 @@ parameter_types! { pub const RelayNetwork: NetworkId = NetworkId::Kusama; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId32>, -); +pub type LocationToAccountId = HashedDescription>; impl paras_registrar::Config for Test { type RuntimeEvent = RuntimeEvent; diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 4a8ffaf04697..30ed686b6935 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -93,6 +93,7 @@ pub trait WeightInfo { fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; fn set_parachain_billing_account_to_self() -> Weight; + fn force_set_parachain_billing_account() -> Weight; } pub struct TestWeightInfo; @@ -121,6 +122,9 @@ impl WeightInfo for TestWeightInfo { fn set_parachain_billing_account_to_self() -> Weight { Weight::zero() } + fn force_set_parachain_billing_account() -> Weight { + Weight::zero() + } } #[frame_support::pallet] @@ -535,7 +539,7 @@ pub mod pallet { /// /// Can only be called by Root. #[pallet::call_index(10)] - #[pallet::weight(::WeightInfo::set_parachain_billing_account_to_self())] + #[pallet::weight(::WeightInfo::force_set_parachain_billing_account())] pub fn force_set_parachain_billing_account( origin: OriginFor, para: ParaId, @@ -1875,6 +1879,24 @@ mod benchmarking { })); } + force_set_parachain_billing_account { + let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); + // Actually finish registration process + next_scheduled_session::(); + + let location: MultiLocation = (Parent, Parachain(LOWEST_PUBLIC_ID.into())).into(); + let sovereign_account = + ::SovereignAccountOf::convert_location(&location) + .unwrap(); + T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); + }: _(RawOrigin::Root, para, sovereign_account.clone()) + verify { + assert_eq!(ParaBillings::::get(para), Some(BillingInfo { + billing_account: sovereign_account, + pending_refund: None + })); + } + impl_benchmark_test_suite!( Registrar, crate::integration_tests::new_test_ext(), diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 69e3a7198765..2bed779d2293 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -100,7 +100,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; +use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -114,6 +114,8 @@ mod weights; // XCM configurations. pub mod xcm_config; +use xcm_config::LocationConverter; + // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -1061,12 +1063,6 @@ impl parachains_slashing::Config for Runtime { type BenchmarkingConfig = parachains_slashing::BenchConfig<200>; } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, -); - parameter_types! { pub const ParaDeposit: Balance = 40 * UNITS; pub const UpgradeFee: Balance = 2 * UNITS; @@ -1079,7 +1075,7 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; + type SovereignAccountOf = LocationConverter; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } @@ -2241,7 +2237,7 @@ sp_api::impl_runtime_apis! { use sp_storage::TrackedStorageKey; use xcm::latest::prelude::*; use xcm_config::{ - AssetHub, LocalCheckAccount, LocationConverter, TokenLocation, XcmConfig, + AssetHub, LocalCheckAccount, TokenLocation, XcmConfig, }; parameter_types! { diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 395e3e48f47b..7b1e809515c5 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -221,7 +221,21 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. - fn set_parachain_stash() -> Weight { + fn set_parachain_billing_account_to_self() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn force_set_parachain_billing_account() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 6b03a358ec7f..ca3a9c587919 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -99,7 +99,7 @@ use xcm::{ latest::{InteriorMultiLocation, Junction, Junction::PalletInstance}, VersionedMultiLocation, }; -use xcm_builder::{Account32Hash, AccountId32Aliases, ChildParachainConvertsVia, PayOverXcm}; +use xcm_builder::PayOverXcm; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -119,6 +119,8 @@ mod bag_thresholds; mod weights; pub mod xcm_config; +use xcm_config::LocationConverter; + // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -1281,12 +1283,6 @@ parameter_types! { pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); } -pub type LocationToAccountId = ( - ChildParachainConvertsVia, - AccountId32Aliases, - Account32Hash<(), AccountId>, -); - impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; @@ -1295,7 +1291,7 @@ impl paras_registrar::Config for Runtime { type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; + type SovereignAccountOf = LocationConverter; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } @@ -2334,7 +2330,7 @@ sp_api::impl_runtime_apis! { impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; - type AccountIdConverter = xcm_config::LocationConverter; + type AccountIdConverter = LocationConverter; type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< xcm_config::XcmConfig, ExistentialDepositMultiAsset, diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 3943b362b38a..368262c22e39 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -218,7 +218,21 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. - fn set_parachain_stash() -> Weight { + fn set_parachain_billing_account_to_self() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn force_set_parachain_billing_account() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` From 7d0709a721d9d93cb48f19c6dba908f437324103 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 6 Dec 2023 20:03:32 +0100 Subject: [PATCH 041/101] fix benchmark --- .../runtime/common/src/integration_tests.rs | 2 +- .../runtime/common/src/paras_registrar/mod.rs | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index f33e7a187b35..11d2ab8435bc 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -940,7 +940,7 @@ fn setting_parachain_billing_account_to_self_works() { let sovereign_account = ::SovereignAccountOf::convert_location(&location) .unwrap(); - let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); + let para_origin: runtime_parachains::Origin = u32::from(para_id).into(); assert_noop!( Registrar::set_parachain_billing_account_to_self( diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 30ed686b6935..7e4bafdd5eae 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1725,9 +1725,10 @@ mod benchmarking { assert_eq!(event, &system_event); } - fn register_para(id: u32, validation_code: ValidationCode) -> ParaId { + fn register_para(id: u32) -> ParaId { let para = ParaId::from(id); let genesis_head = Registrar::::worst_head_data(); + let validation_code = Registrar::::worst_validation_code(); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); assert_ok!(Registrar::::reserve(RawOrigin::Signed(caller.clone()).into())); @@ -1805,7 +1806,7 @@ mod benchmarking { } deregister { - let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); + let para = register_para::(LOWEST_PUBLIC_ID.into()); next_scheduled_session::(); let caller: T::AccountId = whitelisted_caller(); }: _(RawOrigin::Signed(caller), para) @@ -1815,8 +1816,8 @@ mod benchmarking { swap { // On demand parachain - let parathread = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); - let parachain = register_para::((LOWEST_PUBLIC_ID + 1).into(), Registrar::::worst_validation_code()); + let parathread = register_para::(LOWEST_PUBLIC_ID.into()); + let parachain = register_para::((LOWEST_PUBLIC_ID + 1).into()); let parachain_origin = para_origin(parachain.into()); @@ -1844,11 +1845,11 @@ mod benchmarking { // The 'worst case' scenario in terms of benchmarking is when the upgrade isn't free // and there is a refund. - let b in 2 .. MAX_CODE_SIZE; - let initial_code = ValidationCode(vec![0; b as usize]); - let new_code = ValidationCode(vec![0; b as usize - 1]); + let para = register_para::(LOWEST_PUBLIC_ID.into()); + + let b in 1 .. MAX_CODE_SIZE; + let new_code = ValidationCode(vec![0; b as usize]); - let para = register_para::(LOWEST_PUBLIC_ID.into(), initial_code); // Actually finish registration process next_scheduled_session::(); }: _(RawOrigin::Root, para, new_code) @@ -1860,7 +1861,7 @@ mod benchmarking { }: _(RawOrigin::Root, para_id, new_head) set_parachain_billing_account_to_self { - let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); + let para = register_para::(LOWEST_PUBLIC_ID.into()); // Actually finish registration process next_scheduled_session::(); @@ -1880,7 +1881,7 @@ mod benchmarking { } force_set_parachain_billing_account { - let para = register_para::(LOWEST_PUBLIC_ID.into(), Registrar::::worst_validation_code()); + let para = register_para::(LOWEST_PUBLIC_ID.into()); // Actually finish registration process next_scheduled_session::(); From b6562a83d0bffe39ec2997426d657e52ae058179 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 08:33:41 +0100 Subject: [PATCH 042/101] merge BillingInfo into ParaInfo --- .../runtime/common/src/integration_tests.rs | 16 +-- .../common/src/paras_registrar/migration.rs | 4 +- .../runtime/common/src/paras_registrar/mod.rs | 108 ++++++++---------- 3 files changed, 58 insertions(+), 70 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 11d2ab8435bc..5710f6b7f4e5 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -932,9 +932,9 @@ fn setting_parachain_billing_account_to_self_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Attempting to set the billing account for the parachain to the parachain - // sovereign account with an insufficient balance to cover the required deposit should - // result in a failure. + // CASE 1: Parachain origin attempting to assign the billing account to it itself will + // fail if the balance of the sovereign account is insufficient to cover the required + // deposit amount let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); let sovereign_account = @@ -1040,8 +1040,8 @@ fn force_set_parachain_billing_account_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Attempting to set the billing account with an insufficient balance to cover - // the required deposit should result in a failure. + // CASE 1: Attempting to set the billing account to an account with an insufficient balance + // to cover the required deposit should result in a failure. assert_noop!( Registrar::force_set_parachain_billing_account( @@ -1052,9 +1052,9 @@ fn force_set_parachain_billing_account_works() { BalancesError::::InsufficientBalance ); - // CASE 2: The happy path.. The new billing account is sufficiently funded, so the - // reserve will be successful, and the old account will be refunded. - Balances::make_free_balance_be(&&account_id(2), free_balance); + // CASE 2: The new billing account is sufficiently funded, so the reserve will be + // successful, and the old account will be refunded. + Balances::make_free_balance_be(&account_id(2), free_balance); assert_ok!(Registrar::force_set_parachain_billing_account( RuntimeOrigin::root(), diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index f977674a1e4e..37686633ac7d 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -35,9 +35,11 @@ impl> OnRuntimeUpgrade Paras::::translate::>, _>(|key, v1| { count.saturating_inc(); Some(ParaInfo { - manager: v1.manager, + manager: v1.manager.clone(), deposit: v1.deposit, locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + billing_account: v1.manager, + pending_refund: None, }) }); diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 7e4bafdd5eae..2d5b24d3b7bd 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -49,23 +49,15 @@ use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { /// The account that has placed a deposit for registering this para. + /// + /// The given account ID is responsible for registering the code and initial head data, but may + /// only do so if it isn't yet registered. (After that, it's up to governance to do so.) pub(crate) manager: Account, /// The total amount reserved for this para. deposit: Balance, /// Whether the para registration should be locked from being controlled by the manager. /// None means the lock had not been explicitly set, and should be treated as false. locked: Option, -} - -impl ParaInfo { - /// Returns if the para is locked. - pub fn is_locked(&self) -> bool { - self.locked.unwrap_or(false) - } -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] -pub struct BillingInfo { /// The billing account of a parachain. This account is responsible for holding the required /// deposit for this registered parachain. /// @@ -75,10 +67,17 @@ pub struct BillingInfo { /// When a parachain is newly registered, this will be set to the parachain manager. However, /// at a later point, this can be updated by calling the /// `set_parachain_billing_account_to_self` extrinsic. - billing_account: AccountId, - /// In case there is a pending refund, this stores information about the account and the - /// amount that will be refunded upon a successful code upgrade. - pending_refund: Option<(AccountId, Balance)>, + billing_account: Account, + /// In case there is a pending refund for the current billing account, this stores information + /// about the amount that will be refunded upon a successful code upgrade. + pending_refund: Option, +} + +impl ParaInfo { + /// Returns if the para is locked. + pub fn is_locked(&self) -> bool { + self.locked.unwrap_or(false) + } } type BalanceOf = @@ -231,10 +230,8 @@ pub mod pallet { #[pallet::storage] pub(super) type PendingSwap = StorageMap<_, Twox64Concat, ParaId, ParaId>; - /// Amount held on deposit for each para and the original billing account. - /// - /// The given account ID is responsible for registering the code and initial head data, but may - /// only do so if it isn't yet registered. (After that, it's up to governance to do so.) + /// Stores all the registration, code upgrade permission, and billing-related information for + /// the parachain #[pallet::storage] pub type Paras = StorageMap<_, Twox64Concat, ParaId, ParaInfo>>; @@ -243,12 +240,6 @@ pub mod pallet { #[pallet::storage] pub type NextFreeParaId = StorageValue<_, ParaId, ValueQuery>; - /// Stores all the necessary information about the current billing account of the parachain, as - /// well as whether there are any pending refunds. - #[pallet::storage] - pub type ParaBillings = - StorageMap<_, Twox64Concat, ParaId, BillingInfo>>; - #[pallet::genesis_config] pub struct GenesisConfig { #[serde(skip)] @@ -698,7 +689,13 @@ impl Pallet { let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get); ::Currency::reserve(&who, deposit)?; - let info = ParaInfo { manager: who.clone(), deposit, locked: None }; + let info = ParaInfo { + manager: who.clone(), + deposit, + locked: None, + billing_account: who.clone(), + pending_refund: None, + }; Paras::::insert(id, info); Self::deposit_event(Event::::Reserved { para_id: id, who }); @@ -733,11 +730,15 @@ impl Pallet { } else if let Some(rebate) = deposited.checked_sub(&deposit) { ::Currency::unreserve(&who, rebate); }; - let info = ParaInfo { manager: who.clone(), deposit, locked: None }; - let billing_info = BillingInfo { billing_account: who.clone(), pending_refund: None }; + let info = ParaInfo { + manager: who.clone(), + deposit, + locked: None, + billing_account: who.clone(), + pending_refund: None, + }; Paras::::insert(id, info); - ParaBillings::::insert(id, billing_info); // We check above that para has no lifecycle, so this should not fail. let res = runtime_parachains::schedule_para_initialize::(id, genesis); debug_assert!(res.is_ok()); @@ -789,25 +790,21 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut billing_info = - ParaBillings::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let current_deposit = info.deposit; if !free_upgrade { ::Currency::withdraw( - &billing_info.billing_account, + &info.billing_account, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&billing_info.billing_account, additional_deposit)?; + ::Currency::reserve(&info.billing_account, additional_deposit)?; // Update the deposit to the new appropriate amount. info.deposit = new_deposit; - Paras::::insert(para, info); } if current_deposit > new_deposit { @@ -825,13 +822,10 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - billing_info.pending_refund = Some(( - billing_info.billing_account.clone(), - current_deposit.saturating_sub(new_deposit), - )); - ParaBillings::::insert(para, billing_info); + info.pending_refund = Some(current_deposit.saturating_sub(new_deposit)); } + Paras::::insert(para, info); runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) } @@ -877,9 +871,7 @@ impl Pallet { para: ParaId, new_billing_account: T::AccountId, ) -> DispatchResult { - let info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - let mut billing_info = - ParaBillings::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; // When updating the account responsible for all code upgrade costs, we unreserve all // funds associated with the registered parachain from the original billing account and @@ -889,10 +881,10 @@ impl Pallet { // After reserving the required funds from the new billing account we can now safely // refund the old account. - ::Currency::unreserve(&billing_info.billing_account, info.deposit); + ::Currency::unreserve(&info.billing_account, info.deposit); - billing_info.billing_account = new_billing_account; - ParaBillings::::insert(para, billing_info); + info.billing_account = new_billing_account; + Paras::::insert(para, info); Ok(()) } @@ -915,18 +907,18 @@ impl OnNewHead for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let maybe_billing_info = ParaBillings::::get(id); - if maybe_billing_info.is_none() { + let maybe_info = Paras::::get(id); + if maybe_info.is_none() { return T::DbWeight::get().reads(1) } - let mut billing_info = maybe_billing_info + let mut info = maybe_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some((who, rebate)) = billing_info.pending_refund { - ::Currency::unreserve(&who, rebate); - billing_info.pending_refund = None; - ParaBillings::::insert(id, billing_info); + if let Some(rebate) = info.pending_refund { + ::Currency::unreserve(&info.billing_account, rebate); + info.pending_refund = None; + Paras::::insert(id, info); return T::DbWeight::get().reads_writes(2, 2) } @@ -1874,10 +1866,7 @@ mod benchmarking { let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); }: _(para_origin, para) verify { - assert_eq!(ParaBillings::::get(para), Some(BillingInfo { - billing_account: sovereign_account, - pending_refund: None - })); + assert_eq!(ParaInfo::::get(para).billing_account, sovereign_account); } force_set_parachain_billing_account { @@ -1892,10 +1881,7 @@ mod benchmarking { T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); }: _(RawOrigin::Root, para, sovereign_account.clone()) verify { - assert_eq!(ParaBillings::::get(para), Some(BillingInfo { - billing_account: sovereign_account, - pending_refund: None - })); + assert_eq!(ParaInfo::::get(para).billing_account, sovereign_account); } impl_benchmark_test_suite!( From 316b1e3c00275b91d5773ffa6e760c8b8e938350 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 08:55:07 +0100 Subject: [PATCH 043/101] add migration --- .../common/src/paras_registrar/migration.rs | 157 +++++++++++++----- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 116 insertions(+), 45 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 37686633ac7d..5eb06144f5a6 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -17,55 +17,126 @@ use super::*; use frame_support::traits::{Contains, OnRuntimeUpgrade}; -#[derive(Encode, Decode)] -pub struct ParaInfoV1 { - manager: Account, - deposit: Balance, - locked: bool, -} +pub mod v1 { + use super::*; -pub struct VersionUncheckedMigrateToV1( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, -); -impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV1 -{ - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|key, v1| { - count.saturating_inc(); - Some(ParaInfo { - manager: v1.manager.clone(), - deposit: v1.deposit, - locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, - billing_account: v1.manager, - pending_refund: None, - }) - }); + #[derive(Encode, Decode)] + pub struct ParaInfoV1 { + manager: Account, + deposit: Balance, + locked: bool, + } + + pub struct VersionUncheckedMigrateToV1( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, + ); + impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV1 + { + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|key, v1| { + count.saturating_inc(); + Some(ParaInfo { + manager: v1.manager.clone(), + deposit: v1.deposit, + locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + billing_account: v1.manager, + pending_refund: None, + }) + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + T::DbWeight::get().reads_writes(count, count) + } - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); - T::DbWeight::get().reads_writes(count, count) + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; + + ensure!(old_count == new_count, "Paras count should not change"); + Ok(()) + } } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) + pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + super::Pallet, + ::DbWeight, + >; +} + +pub mod v2 { + use super::*; + + #[derive(Encode, Decode)] + pub struct ParaInfoV2 { + manager: Account, + deposit: Balance, + locked: Option, } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; + pub struct VersionUncheckedMigrateToV2( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, + ); + impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV2 + { + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|_key, v2| { + count.saturating_inc(); + Some(ParaInfo { + manager: v2.manager.clone(), + deposit: v2.deposit, + locked: v2.locked, + billing_account: v2.manager, + pending_refund: None, + }) + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", count); + T::DbWeight::get().reads_writes(count, count) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; + + ensure!(old_count == new_count, "Paras count should not change"); - ensure!(old_count == new_count, "Paras count should not change"); - Ok(()) + Paras::::iter_keys().try_for_each(|para_id| -> Result<(), _> { + let info = Paras::::get(para_id).unwrap(); + ensure!( + info.billing_account == info.manager, + "The billing account must be set to the para manager" + ); + ensure!(info.pending_refund.is_none(), "There should be no pending refund"); + + Ok(()) + }) + } } -} -pub type MigrateToV1 = frame_support::migrations::VersionedMigration< - 0, - 1, - VersionUncheckedMigrateToV1, - super::Pallet, - ::DbWeight, ->; + pub type MigrateToV2 = frame_support::migrations::VersionedMigration< + 1, + 2, + VersionUncheckedMigrateToV2, + super::Pallet, + ::DbWeight, + >; +} diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 2bed779d2293..19cd92feca71 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1619,7 +1619,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::v1::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index ca3a9c587919..8b985f607c8c 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1651,7 +1651,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::v1::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From 97974f82d02989bd3774d3e10c320446cf8376e7 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 09:02:24 +0100 Subject: [PATCH 044/101] Revert "add migration" This reverts commit 316b1e3c00275b91d5773ffa6e760c8b8e938350. --- .../common/src/paras_registrar/migration.rs | 157 +++++------------- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 45 insertions(+), 116 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 5eb06144f5a6..37686633ac7d 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -17,126 +17,55 @@ use super::*; use frame_support::traits::{Contains, OnRuntimeUpgrade}; -pub mod v1 { - use super::*; - - #[derive(Encode, Decode)] - pub struct ParaInfoV1 { - manager: Account, - deposit: Balance, - locked: bool, - } - - pub struct VersionUncheckedMigrateToV1( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, - ); - impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV1 - { - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|key, v1| { - count.saturating_inc(); - Some(ParaInfo { - manager: v1.manager.clone(), - deposit: v1.deposit, - locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, - billing_account: v1.manager, - pending_refund: None, - }) - }); - - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); - T::DbWeight::get().reads_writes(count, count) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; - - ensure!(old_count == new_count, "Paras count should not change"); - Ok(()) - } - } - - pub type MigrateToV1 = frame_support::migrations::VersionedMigration< - 0, - 1, - VersionUncheckedMigrateToV1, - super::Pallet, - ::DbWeight, - >; +#[derive(Encode, Decode)] +pub struct ParaInfoV1 { + manager: Account, + deposit: Balance, + locked: bool, } -pub mod v2 { - use super::*; +pub struct VersionUncheckedMigrateToV1( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, +); +impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV1 +{ + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|key, v1| { + count.saturating_inc(); + Some(ParaInfo { + manager: v1.manager.clone(), + deposit: v1.deposit, + locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + billing_account: v1.manager, + pending_refund: None, + }) + }); - #[derive(Encode, Decode)] - pub struct ParaInfoV2 { - manager: Account, - deposit: Balance, - locked: Option, + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + T::DbWeight::get().reads_writes(count, count) } - pub struct VersionUncheckedMigrateToV2( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, - ); - impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV2 - { - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|_key, v2| { - count.saturating_inc(); - Some(ParaInfo { - manager: v2.manager.clone(), - deposit: v2.deposit, - locked: v2.locked, - billing_account: v2.manager, - pending_refund: None, - }) - }); - - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", count); - T::DbWeight::get().reads_writes(count, count) - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; - - ensure!(old_count == new_count, "Paras count should not change"); + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } - Paras::::iter_keys().try_for_each(|para_id| -> Result<(), _> { - let info = Paras::::get(para_id).unwrap(); - ensure!( - info.billing_account == info.manager, - "The billing account must be set to the para manager" - ); - ensure!(info.pending_refund.is_none(), "There should be no pending refund"); + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; - Ok(()) - }) - } + ensure!(old_count == new_count, "Paras count should not change"); + Ok(()) } - - pub type MigrateToV2 = frame_support::migrations::VersionedMigration< - 1, - 2, - VersionUncheckedMigrateToV2, - super::Pallet, - ::DbWeight, - >; } + +pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + super::Pallet, + ::DbWeight, +>; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 19cd92feca71..2bed779d2293 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1619,7 +1619,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::v1::MigrateToV1, + paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 8b985f607c8c..ca3a9c587919 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1651,7 +1651,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::v1::MigrateToV1, + paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From a1781429bfe4da750bcc1dced05b1da1f49776a6 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 09:22:39 +0100 Subject: [PATCH 045/101] require billing_account to be explicitly set --- .../common/src/paras_registrar/migration.rs | 2 +- .../runtime/common/src/paras_registrar/mod.rs | 47 +++++++++++++------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 37686633ac7d..5c94133c0c63 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -38,7 +38,7 @@ impl> OnRuntimeUpgrade manager: v1.manager.clone(), deposit: v1.deposit, locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, - billing_account: v1.manager, + billing_account: None, pending_refund: None, }) }); diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2d5b24d3b7bd..178e7ecaacc6 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -67,7 +67,12 @@ pub struct ParaInfo { /// When a parachain is newly registered, this will be set to the parachain manager. However, /// at a later point, this can be updated by calling the /// `set_parachain_billing_account_to_self` extrinsic. - billing_account: Account, + /// + /// None means that the billing account hasn't been explicitly set. In such a case, if the + /// parachain intends to schedule a code upgrade and is not a lease-holding parachain, either + /// the parachain manager or the parachain itself has to explicitly set itself as the billing + /// account by calling the `set_parachain_billing_account_to_self` extrinsic. + billing_account: Option, /// In case there is a pending refund for the current billing account, this stores information /// about the amount that will be refunded upon a successful code upgrade. pending_refund: Option, @@ -224,6 +229,9 @@ pub mod pallet { /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras /// are correct for the swap to work. CannotSwap, + /// The parachain billing account has to be explicitly set before being able to schedule a + /// code upgrade. + BillingAccountNotSet, } /// Pending swap operations. @@ -693,7 +701,7 @@ impl Pallet { manager: who.clone(), deposit, locked: None, - billing_account: who.clone(), + billing_account: Some(who.clone()), pending_refund: None, }; @@ -734,7 +742,7 @@ impl Pallet { manager: who.clone(), deposit, locked: None, - billing_account: who.clone(), + billing_account: Some(who.clone()), pending_refund: None, }; @@ -790,18 +798,24 @@ impl Pallet { .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + + let billing_account = info.billing_account.clone(); + ensure!(billing_account.clone().is_some(), Error::::BillingAccountNotSet); + let billing_account = + billing_account.expect("Ensured above that the billing account is set to some; qed"); + let current_deposit = info.deposit; if !free_upgrade { ::Currency::withdraw( - &info.billing_account, + &billing_account, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, )?; let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&info.billing_account, additional_deposit)?; + ::Currency::reserve(&billing_account, additional_deposit)?; // Update the deposit to the new appropriate amount. info.deposit = new_deposit; @@ -880,10 +894,11 @@ impl Pallet { ::Currency::reserve(&new_billing_account, info.deposit)?; // After reserving the required funds from the new billing account we can now safely - // refund the old account. - ::Currency::unreserve(&info.billing_account, info.deposit); + // refund the current deposit holder. + let current_deposit_holder = info.billing_account.clone().unwrap_or(info.manager.clone()); + ::Currency::unreserve(¤t_deposit_holder, info.deposit); - info.billing_account = new_billing_account; + info.billing_account = Some(new_billing_account); Paras::::insert(para, info); Ok(()) @@ -915,12 +930,14 @@ impl OnCodeUpgrade for Pallet { let mut info = maybe_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some(rebate) = info.pending_refund { - ::Currency::unreserve(&info.billing_account, rebate); - info.pending_refund = None; - Paras::::insert(id, info); + if let Some(rebate) = info.pending_refund.clone() { + if let Some(billing_account) = info.billing_account.clone() { + ::Currency::unreserve(&billing_account, rebate); + info.pending_refund = None; + Paras::::insert(id, info); - return T::DbWeight::get().reads_writes(2, 2) + return T::DbWeight::get().reads_writes(2, 2) + } } T::DbWeight::get().reads(1) @@ -1866,7 +1883,7 @@ mod benchmarking { let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); }: _(para_origin, para) verify { - assert_eq!(ParaInfo::::get(para).billing_account, sovereign_account); + assert_eq!(ParaInfo::::get(para).billing_account, Some(sovereign_account)); } force_set_parachain_billing_account { @@ -1881,7 +1898,7 @@ mod benchmarking { T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); }: _(RawOrigin::Root, para, sovereign_account.clone()) verify { - assert_eq!(ParaInfo::::get(para).billing_account, sovereign_account); + assert_eq!(ParaInfo::::get(para).billing_account, Some(sovereign_account)); } impl_benchmark_test_suite!( From 44a6cae270d602991658cd551c5bff20be69e706 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 15:19:53 +0100 Subject: [PATCH 046/101] add test --- .../runtime/common/src/paras_registrar/mod.rs | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 178e7ecaacc6..ddc565b52f8c 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -930,7 +930,7 @@ impl OnCodeUpgrade for Pallet { let mut info = maybe_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some(rebate) = info.pending_refund.clone() { + if let Some(rebate) = info.pending_refund { if let Some(billing_account) = info.billing_account.clone() { ::Currency::unreserve(&billing_account, rebate); info.pending_refund = None; @@ -1712,6 +1712,60 @@ mod tests { assert!(Parachains::is_parathread(para_2)); }); } + + #[test] + fn billing_account_has_to_be_explicitly_set() { + new_test_ext().execute_with(|| { + const START_SESSION_INDEX: SessionIndex = 1; + run_to_session(START_SESSION_INDEX); + + let para_id = LOWEST_PUBLIC_ID; + assert!(!Parachains::is_parathread(para_id)); + + let validation_code = test_validation_code(32); + assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1))); + assert_ok!(Registrar::register( + RuntimeOrigin::signed(1), + para_id, + test_genesis_head(32), + validation_code.clone(), + )); + conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); + + run_to_session(START_SESSION_INDEX + 2); + assert!(Parachains::is_parathread(para_id)); + + assert_ok!(Registrar::make_parachain(para_id)); + run_to_session(START_SESSION_INDEX + 4); + assert!(Parachains::is_parachain(para_id)); + + // Legacy lease holding parachains won't have their billing account set. So if a + // parachain no longer has a parachain slot, the billing account must be explicitly + // set before initiating code upgrades. + + // Downgrade the lease holding parachain to a parathread. + assert_ok!(Registrar::make_parathread(para_id)); + run_to_session(START_SESSION_INDEX + 6); + assert!(Registrar::is_parathread(para_id)); + + // To simulate this we will have to manually set the billing account to `None`. + Paras::::mutate_exists(para_id, |maybe_info| { + if let Some(info) = maybe_info { + info.billing_account = None + } + }); + + let validation_code = test_validation_code(42); + assert_noop!( + Registrar::schedule_code_upgrade( + RuntimeOrigin::signed(1), + ParaId::from(para_id), + validation_code + ), + paras_registrar::Error::::BillingAccountNotSet + ); + }); + } } #[cfg(feature = "runtime-benchmarks")] From e88b3f46841dcec1fefb3f06323d39911c4a6e1c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 16:18:15 +0100 Subject: [PATCH 047/101] add events --- .../runtime/common/src/integration_tests.rs | 58 ++++++++++++++----- .../runtime/common/src/paras_registrar/mod.rs | 21 ++++++- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 5710f6b7f4e5..605a47fb3826 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -645,8 +645,25 @@ fn para_upgrade_initiated_by_manager_works() { ParaId::from(para_id), code_1.clone(), )); - conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); + // The reserved deposit should cover for the size difference of the new validation code. + let total_bytes_stored = code_size as u32 + head_size as u32; + assert_eq!( + last_event(), + paras_registrar::Event::::CodeUpgradeScheduled { + para_id, + new_deposit: ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + } + .into(), + ); + assert_eq!( + Balances::reserved_balance(&account_id(1)), + ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ); + // An additional upgrade fee should also be deducted from the caller's balance. + assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); + + conclude_pvf_checking::(&code_1, VALIDATORS, START_SESSION_INDEX + 2, true); // After two more sessions the parachain can be upgraded. run_to_session(START_SESSION_INDEX + 4); // Force a new head to enact the code upgrade. @@ -657,15 +674,6 @@ fn para_upgrade_initiated_by_manager_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // The reserved deposit should cover for the size difference of the new validation code. - let total_bytes_stored = code_size as u32 + head_size as u32; - assert_eq!( - Balances::reserved_balance(&account_id(1)), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) - ); - // An additional upgrade fee should also be deducted from the caller's balance. - assert_eq!(Balances::total_balance(&account_id(1)), free_balance - UpgradeFee::get()); - // CASE 2: After successfully upgrading the validation code to twice the size of the // previous one, we will now proceed to upgrade the validation code to a smaller size. It is // expected that the parachain manager will receive a refund upon the successful completion @@ -690,11 +698,22 @@ fn para_upgrade_initiated_by_manager_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); + let updated_total_bytes_stored = code_size as u32 + head_size as u32; + let expected_rebate = + (total_bytes_stored - updated_total_bytes_stored) * DataDepositPerByte::get(); + + assert!(contains_event( + paras_registrar::Event::::Refunded { + para_id, + who: account_id(1), + amount: expected_rebate + } + .into() + ),); // The reserved deposit should cover only the size of the new validation code. - let total_bytes_stored = code_size as u32 + head_size as u32; assert_eq!( Balances::reserved_balance(&account_id(1)), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ParaDeposit::get() + (updated_total_bytes_stored * DataDepositPerByte::get()) ); // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (2 * UpgradeFee::get())); @@ -717,7 +736,7 @@ fn para_upgrade_initiated_by_manager_works() { assert_eq!(Paras::current_code(¶_id), Some(code_2.clone())); assert_eq!( Balances::reserved_balance(&account_id(1)), - ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) + ParaDeposit::get() + (updated_total_bytes_stored * DataDepositPerByte::get()) ); // Even though the upgrade failed the upgrade fee is deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (3 * UpgradeFee::get())); @@ -958,6 +977,14 @@ fn setting_parachain_billing_account_to_self_works() { para_origin.into(), ParaId::from(para_id), )); + assert_eq!( + last_event(), + paras_registrar::Event::::BillingAccountSet { + para_id, + who: sovereign_account.clone() + } + .into(), + ); assert_eq!( Balances::reserved_balance(&sovereign_account), @@ -1061,6 +1088,11 @@ fn force_set_parachain_billing_account_works() { ParaId::from(para_id), account_id(2) )); + assert_eq!( + last_event(), + paras_registrar::Event::::BillingAccountSet { para_id, who: account_id(2) } + .into(), + ); assert_eq!( Balances::reserved_balance(&account_id(2)), diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index ddc565b52f8c..f4ec773a43e3 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -195,6 +195,9 @@ pub mod pallet { Deregistered { para_id: ParaId }, Reserved { para_id: ParaId, who: T::AccountId }, Swapped { para_id: ParaId, other_id: ParaId }, + BillingAccountSet { para_id: ParaId, who: T::AccountId }, + Refunded { para_id: ParaId, who: T::AccountId, amount: BalanceOf }, + CodeUpgradeScheduled { para_id: ParaId, new_deposit: BalanceOf }, } #[pallet::error] @@ -840,7 +843,10 @@ impl Pallet { } Paras::::insert(para, info); - runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No) + runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; + + Self::deposit_event(Event::::CodeUpgradeScheduled { para_id: para, new_deposit }); + Ok(()) } /// Verifies the onboarding data is valid for a para. @@ -898,9 +904,13 @@ impl Pallet { let current_deposit_holder = info.billing_account.clone().unwrap_or(info.manager.clone()); ::Currency::unreserve(¤t_deposit_holder, info.deposit); - info.billing_account = Some(new_billing_account); - Paras::::insert(para, info); + info.billing_account = Some(new_billing_account.clone()); + Paras::::insert(para, info.clone()); + Self::deposit_event(Event::::BillingAccountSet { + para_id: para, + who: new_billing_account, + }); Ok(()) } } @@ -936,6 +946,11 @@ impl OnCodeUpgrade for Pallet { info.pending_refund = None; Paras::::insert(id, info); + Self::deposit_event(Event::::Refunded { + para_id: id, + who: billing_account, + amount: rebate, + }); return T::DbWeight::get().reads_writes(2, 2) } } From 1ac7da7aef06b22589c8ab713332fcc74b77e3f6 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 7 Dec 2023 16:46:09 +0100 Subject: [PATCH 048/101] add docs --- .../runtime/common/src/paras_registrar/mod.rs | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f4ec773a43e3..d2ee1aa59ef7 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -448,14 +448,20 @@ pub mod pallet { /// Schedule a parachain upgrade. /// - /// Can be called by Root, the parachain, or the parachain manager if the parachain is - /// unlocked. + /// ## Arguments + /// - `origin`: Can be called by Root, the parachain, or the parachain manager if the + /// parachain is unlocked. + /// - `para`: The parachain's ID for which the code upgrade is scheduled. + /// - `new_code`: The new validation code of the parachain. /// + /// ## Deposits/Fees /// In case the call is made by the parachain manager or the parachain itself, there will be - /// associated upgrade costs. + /// associated upgrade costs. Depending on the size of the new validation code, the caller's + /// reserved deposit might be adjusted to account for the size difference. /// - /// Depending on the size of the new validation code, the caller's reserved deposit might be - /// adjusted to account for the size difference. + /// ## Events + /// The `CodeUpgradeScheduled` event is emitted in case of success, containing information + /// about the new total deposit reserved for the parachain. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))] pub fn schedule_code_upgrade( @@ -498,12 +504,17 @@ pub mod pallet { /// Changes the billing account of a parachain to the caller. /// + /// ## Arguments + /// - `origin`: Can be called by Root, the parachain, or the parachain manager if the + /// parachain is unlocked. + /// - `para`: The parachain's ID for which the billing account is set. + /// /// ## Deposits/Fees /// For this call to be successful, the caller account must have a sufficient balance /// to cover the current deposit required for this parachain. /// - /// Can be called by Root, the parachain, or the parachain manager if the parachain is - /// unlocked. + /// ## Events + /// The `BillingAccountSet` event is emitted in case of success. #[pallet::call_index(9)] #[pallet::weight(::WeightInfo::set_parachain_billing_account_to_self())] pub fn set_parachain_billing_account_to_self( @@ -533,23 +544,30 @@ pub mod pallet { Ok(()) } - /// Sets the billing account of a parachain to the caller. + /// Sets the billing account of a parachain to a specific account. + /// + /// ## Arguments + /// - `origin`: Must be Root. + /// - `para`: The parachain's ID for which the billing account is set. + /// - `billing_account`: The account that will be responsible for covering all parachain + /// related costs. /// /// ## Deposits/Fees - /// For this call to be successful, the `new_billing_account` account must have a sufficient + /// For this call to be successful, the `billing_account` account must have a sufficient /// balance to cover the current deposit required for this parachain. /// - /// Can only be called by Root. + /// ## Events + /// The `BillingAccountSet` event is emitted in case of success. #[pallet::call_index(10)] #[pallet::weight(::WeightInfo::force_set_parachain_billing_account())] pub fn force_set_parachain_billing_account( origin: OriginFor, para: ParaId, - new_billing_account: T::AccountId, + billing_account: T::AccountId, ) -> DispatchResult { ensure_root(origin)?; - Self::set_parachain_billing_account(para, new_billing_account)?; + Self::set_parachain_billing_account(para, billing_account)?; Ok(()) } From 189c5daab6d249b2fa60ccd8f6e9bd06119e6bcf Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 8 Dec 2023 20:59:26 +0100 Subject: [PATCH 049/101] add migration --- .../common/src/paras_registrar/migration.rs | 30 ++++++++++++------- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 5c94133c0c63..79dd820b4975 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -21,29 +21,29 @@ use frame_support::traits::{Contains, OnRuntimeUpgrade}; pub struct ParaInfoV1 { manager: Account, deposit: Balance, - locked: bool, + locked: Option, } -pub struct VersionUncheckedMigrateToV1( +pub struct VersionUncheckedMigrateToV2( sp_std::marker::PhantomData<(T, UnlockParaIds)>, ); impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV1 + for VersionUncheckedMigrateToV2 { fn on_runtime_upgrade() -> Weight { let mut count = 0u64; - Paras::::translate::>, _>(|key, v1| { + Paras::::translate::>, _>(|_key, v1| { count.saturating_inc(); Some(ParaInfo { manager: v1.manager.clone(), deposit: v1.deposit, - locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + locked: v1.locked, billing_account: None, pending_refund: None, }) }); - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", count); T::DbWeight::get().reads_writes(count, count) } @@ -58,14 +58,24 @@ impl> OnRuntimeUpgrade let new_count = Paras::::iter_values().count() as u32; ensure!(old_count == new_count, "Paras count should not change"); - Ok(()) + + Paras::::iter_keys().try_for_each(|para_id| -> Result<(), _> { + let info = Paras::::get(para_id).unwrap(); + ensure!( + info.billing_account.is_none(), + "The billing account must be set to the para manager" + ); + ensure!(info.pending_refund.is_none(), "There should be no pending refund"); + + Ok(()) + }) } } -pub type MigrateToV1 = frame_support::migrations::VersionedMigration< - 0, +pub type MigrateToV2 = frame_support::migrations::VersionedMigration< 1, - VersionUncheckedMigrateToV1, + 2, + VersionUncheckedMigrateToV2, super::Pallet, ::DbWeight, >; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index fb68f9116a12..1a04ef4d79be 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1610,7 +1610,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 5a149c040db4..fa26c19d21df 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1643,7 +1643,7 @@ pub mod migrations { parachains_scheduler::migration::v1::MigrateToV1, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From eb2af3d129cc732a82456bb21f8e7fa1137fb82d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 8 Dec 2023 21:07:36 +0100 Subject: [PATCH 050/101] better wording --- .../common/src/paras_registrar/migration.rs | 4 ++-- .../runtime/common/src/paras_registrar/mod.rs | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 79dd820b4975..62c4137f6d08 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -39,7 +39,7 @@ impl> OnRuntimeUpgrade deposit: v1.deposit, locked: v1.locked, billing_account: None, - pending_refund: None, + pending_deposit_refund: None, }) }); @@ -65,7 +65,7 @@ impl> OnRuntimeUpgrade info.billing_account.is_none(), "The billing account must be set to the para manager" ); - ensure!(info.pending_refund.is_none(), "There should be no pending refund"); + ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); Ok(()) }) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index d2ee1aa59ef7..8ccd4eec11fb 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -68,14 +68,13 @@ pub struct ParaInfo { /// at a later point, this can be updated by calling the /// `set_parachain_billing_account_to_self` extrinsic. /// - /// None means that the billing account hasn't been explicitly set. In such a case, if the - /// parachain intends to schedule a code upgrade and is not a lease-holding parachain, either - /// the parachain manager or the parachain itself has to explicitly set itself as the billing - /// account by calling the `set_parachain_billing_account_to_self` extrinsic. + /// None indicates that the billing account hasn't been set, which is the case for all legacy + /// parachains. Attempting to schedule a parachain upgrade will fail if the billing account is + /// set to None, except in the case where the parachain is not a lease-holding parachain. billing_account: Option, /// In case there is a pending refund for the current billing account, this stores information /// about the amount that will be refunded upon a successful code upgrade. - pending_refund: Option, + pending_deposit_refund: Option, } impl ParaInfo { @@ -723,7 +722,7 @@ impl Pallet { deposit, locked: None, billing_account: Some(who.clone()), - pending_refund: None, + pending_deposit_refund: None, }; Paras::::insert(id, info); @@ -764,7 +763,7 @@ impl Pallet { deposit, locked: None, billing_account: Some(who.clone()), - pending_refund: None, + pending_deposit_refund: None, }; Paras::::insert(id, info); @@ -857,7 +856,7 @@ impl Pallet { // code to an empty blob. In such a case, the pre-checking process would fail, so // the old code would remain on-chain even though there is no deposit to cover it. - info.pending_refund = Some(current_deposit.saturating_sub(new_deposit)); + info.pending_deposit_refund = Some(current_deposit.saturating_sub(new_deposit)); } Paras::::insert(para, info); @@ -958,10 +957,10 @@ impl OnCodeUpgrade for Pallet { let mut info = maybe_info .expect("Ensured above that the deposit info is stored for the parachain; qed"); - if let Some(rebate) = info.pending_refund { + if let Some(rebate) = info.pending_deposit_refund { if let Some(billing_account) = info.billing_account.clone() { ::Currency::unreserve(&billing_account, rebate); - info.pending_refund = None; + info.pending_deposit_refund = None; Paras::::insert(id, info); Self::deposit_event(Event::::Refunded { From 700cd1e21d059f89e5f7835193f8ea8050461c1d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 8 Dec 2023 21:27:42 +0100 Subject: [PATCH 051/101] add PreCodeUpgradeChecker type --- .../runtime/common/src/assigned_slots/mod.rs | 1 + .../runtime/common/src/integration_tests.rs | 1 + .../runtime/common/src/paras_registrar/mod.rs | 1 + polkadot/runtime/parachains/src/mock.rs | 1 + polkadot/runtime/parachains/src/paras/mod.rs | 21 +++++++++++++++++++ polkadot/runtime/rococo/src/lib.rs | 1 + polkadot/runtime/test-runtime/src/lib.rs | 1 + polkadot/runtime/westend/src/lib.rs | 1 + 8 files changed, 28 insertions(+) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index 76d5ab3afdf1..3cd3fc389818 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -742,6 +742,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type PreCodeUpgradeChecker = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 605a47fb3826..b019500e108e 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -215,6 +215,7 @@ impl paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type PreCodeUpgradeChecker = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 8ccd4eec11fb..84a007caa27b 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1097,6 +1097,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; + type PreCodeUpgradeChecker = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index df757c229ed9..eb8f117dc9fa 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -216,6 +216,7 @@ impl crate::paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = TestNextSessionRotation; + type PreCodeUpgradeChecker = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index c61e8fa89807..6d0c49ecc689 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -522,6 +522,23 @@ impl OnCodeUpgrade for () { } } +pub trait PreCodeUpgradeChecker { + /// A function that performs custom logic to determine whether a code upgrade is allowed to be + /// performed. + /// + /// This is currently utilized by the registrar pallet to ensure that the necessary validation + /// code upgrade costs are covered. + fn can_upgrade(id: ParaId, new_code: ValidationCode) -> DispatchResultWithPostInfo; +} + +/// An empty implementation of the trait where there are no checks performed before scheduling a +/// code upgrade. +impl PreCodeUpgradeChecker for () { + fn can_upgrade(_id: ParaId, _new_code: ValidationCode) -> DispatchResultWithPostInfo { + Ok(().into()) + } +} + pub trait WeightInfo { fn force_set_current_code(c: u32) -> Weight; fn force_set_current_head(s: u32) -> Weight; @@ -619,6 +636,10 @@ pub mod pallet { /// Runtime hook for when a parachain head is updated. type OnNewHead: OnNewHead; + /// A type that performs custom logic to determine whether a code upgrade is allowed to be + /// performed. + type PreCodeUpgradeChecker: PreCodeUpgradeChecker; + /// Type that executes some custom logic upon a successful code upgrade. type OnCodeUpgrade: OnCodeUpgrade; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 1a04ef4d79be..6007c16ae697 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -935,6 +935,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type PreCodeUpgradeChecker = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = Registrar; } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index b4642a86118e..53897cd4fc57 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -534,6 +534,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type PreCodeUpgradeChecker = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index fa26c19d21df..1681e6271e2b 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1148,6 +1148,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type PreCodeUpgradeChecker = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = (); } From 282588e9ef9222f257dcf780c648d0600e0b2d27 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 8 Dec 2023 22:08:10 +0100 Subject: [PATCH 052/101] implement PreCodeUpgrade | WIP --- .../runtime/common/src/assigned_slots/mod.rs | 2 +- .../runtime/common/src/integration_tests.rs | 2 +- .../runtime/common/src/paras_registrar/mod.rs | 156 ++++++++++-------- polkadot/runtime/parachains/src/mock.rs | 2 +- polkadot/runtime/parachains/src/paras/mod.rs | 23 ++- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/test-runtime/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 8 files changed, 108 insertions(+), 83 deletions(-) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index 3cd3fc389818..d2e86c450c9d 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -742,7 +742,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; - type PreCodeUpgradeChecker = (); + type PreCodeUpgrade = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index b019500e108e..23076d948aad 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -215,7 +215,7 @@ impl paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; - type PreCodeUpgradeChecker = Registrar; + type PreCodeUpgrade = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = (); } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 84a007caa27b..cc468eb9aec6 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -22,14 +22,14 @@ pub mod migration; use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::Weight, + pallet_prelude::{DispatchResultWithPostInfo, Weight}, traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; use runtime_parachains::{ configuration, ensure_parachain, - paras::{self, OnCodeUpgrade, ParaGenesisArgs, SetGoAhead}, + paras::{self, OnCodeUpgrade, ParaGenesisArgs, PreCodeUpgrade, SetGoAhead}, Origin, ParaLifecycle, }; use sp_std::{prelude::*, result}; @@ -470,19 +470,10 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - // There are two cases where we do not charge any upgrade costs from the initiator - // of the upgrade: - // - // 1. Root doesn't pay. This also means that system parachains do not pay for upgrade - // fees. - // - // 2. All lease-holding parachains are permitted to do upgrades for free. This is - // introduced to avoid causing a breaking change to the system once para upgrade fees - // are required. - let free_upgrade = - ensure_root(origin.clone()).is_ok() || Self::parachains().contains(¶); + // Root doesn't pay, hence all the pre checking logic is skipped. + let skip_checks = ensure_root(origin.clone()).is_ok(); - Self::do_schedule_code_upgrade(para, new_code, free_upgrade) + Self::do_schedule_code_upgrade(para, new_code, skip_checks) } /// Set the parachain's current head. @@ -795,74 +786,25 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `free_upgrade` is set to true, there won't be any extra deposit or fees charged - /// for scheduling the code upgrade. + /// If `skip_pre_upgrade` is set to true all the pre code upgrade logic will be skipped. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - free_upgrade: bool, + skip_checks: bool, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); - let head = - paras::Pallet::::para_head(para).map_or(Err(Error::::NotRegistered), Ok)?; - - let per_byte_fee = T::DataDepositPerByte::get(); - let new_deposit = T::ParaDeposit::get() - .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - - let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - - let billing_account = info.billing_account.clone(); - ensure!(billing_account.clone().is_some(), Error::::BillingAccountNotSet); - let billing_account = - billing_account.expect("Ensured above that the billing account is set to some; qed"); - - let current_deposit = info.deposit; - - if !free_upgrade { - ::Currency::withdraw( - &billing_account, - T::UpgradeFee::get(), - WithdrawReasons::FEE, - ExistenceRequirement::KeepAlive, - )?; - - let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&billing_account, additional_deposit)?; - - // Update the deposit to the new appropriate amount. - info.deposit = new_deposit; - } - - if current_deposit > new_deposit { - // The billing account should receive a refund if the current deposit exceeds the new - // required deposit, even if they did not initiate the upgrade. - // - // The excess deposit will be refunded to the caller upon the success of the code - // upgrade. - // - // The reason why the deposit is not instantly refunded is that scheduling a code - // upgrade doesn't guarantee the success of it. - // - // If we returned the deposit here, a possible attack scenario would be to register - // the validation code of a parachain and then schedule a code upgrade to set the - // code to an empty blob. In such a case, the pre-checking process would fail, so - // the old code would remain on-chain even though there is no deposit to cover it. - - info.pending_deposit_refund = Some(current_deposit.saturating_sub(new_deposit)); - } - - Paras::::insert(para, info); + T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_checks) + .map_err(|e| e.error)?; runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; - Self::deposit_event(Event::::CodeUpgradeScheduled { para_id: para, new_deposit }); + // TODO: + //Self::deposit_event(Event::::CodeUpgradeScheduled { para_id: para, new_deposit }); Ok(()) } @@ -947,6 +889,80 @@ impl OnNewHead for Pallet { } } +impl PreCodeUpgrade for Pallet { + fn pre_code_upgrade( + para: ParaId, + new_code: ValidationCode, + skip_checks: bool, + ) -> DispatchResultWithPostInfo { + let head = + paras::Pallet::::para_head(para).map_or(Err(Error::::NotRegistered), Ok)?; + + let per_byte_fee = T::DataDepositPerByte::get(); + let new_deposit = T::ParaDeposit::get() + .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) + .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); + + let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + + let billing_account = info.billing_account.clone(); + ensure!(billing_account.clone().is_some(), Error::::BillingAccountNotSet); + let billing_account = + billing_account.expect("Ensured above that the billing account is set to some; qed"); + + let current_deposit = info.deposit; + + // There are three cases where we do not charge any upgrade costs from the initiator + // of the upgrade: + // + // 1. `skip_checks` is set to true. + // + // 2. System parachains do not pay for upgrade fees. + // + // 2. All lease-holding parachains are permitted to do upgrades for free. This is introduced + // to avoid causing a breaking change to the system once para upgrade fees are required. + let free_upgrade = + skip_checks || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); + + if !free_upgrade { + ::Currency::withdraw( + &billing_account, + T::UpgradeFee::get(), + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + )?; + + let additional_deposit = new_deposit.saturating_sub(current_deposit); + ::Currency::reserve(&billing_account, additional_deposit)?; + + // Update the deposit to the new appropriate amount. + info.deposit = new_deposit; + } + + if current_deposit > new_deposit { + // The billing account should receive a refund if the current deposit exceeds the new + // required deposit, even if they did not initiate the upgrade. + // + // The excess deposit will be refunded to the caller upon the success of the code + // upgrade. + // + // The reason why the deposit is not instantly refunded is that scheduling a code + // upgrade doesn't guarantee the success of it. + // + // If we returned the deposit here, a possible attack scenario would be to register + // the validation code of a parachain and then schedule a code upgrade to set the + // code to an empty blob. In such a case, the pre-checking process would fail, so + // the old code would remain on-chain even though there is no deposit to cover it. + + info.pending_deposit_refund = Some(current_deposit.saturating_sub(new_deposit)); + } + + Paras::::insert(para, info); + + Ok(().into()) // FIXME: actual weight + } +} + impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { let maybe_info = Paras::::get(id); @@ -1097,7 +1113,7 @@ mod tests { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; - type PreCodeUpgradeChecker = (); + type PreCodeUpgrade = Registrar; type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index eb8f117dc9fa..7d5287a32112 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -216,7 +216,7 @@ impl crate::paras::Config for Test { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = TestNextSessionRotation; - type PreCodeUpgradeChecker = (); + type PreCodeUpgrade = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 6d0c49ecc689..7b51c887803f 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -522,19 +522,28 @@ impl OnCodeUpgrade for () { } } -pub trait PreCodeUpgradeChecker { - /// A function that performs custom logic to determine whether a code upgrade is allowed to be - /// performed. +pub trait PreCodeUpgrade { + /// A function that performs custom logic to before performing a code upgrade. /// /// This is currently utilized by the registrar pallet to ensure that the necessary validation /// code upgrade costs are covered. - fn can_upgrade(id: ParaId, new_code: ValidationCode) -> DispatchResultWithPostInfo; + /// + /// TODO: docs + fn pre_code_upgrade( + id: ParaId, + new_code: ValidationCode, + skip_checks: bool, + ) -> DispatchResultWithPostInfo; } /// An empty implementation of the trait where there are no checks performed before scheduling a /// code upgrade. -impl PreCodeUpgradeChecker for () { - fn can_upgrade(_id: ParaId, _new_code: ValidationCode) -> DispatchResultWithPostInfo { +impl PreCodeUpgrade for () { + fn pre_code_upgrade( + _id: ParaId, + _new_code: ValidationCode, + _skip_checks: bool, + ) -> DispatchResultWithPostInfo { Ok(().into()) } } @@ -638,7 +647,7 @@ pub mod pallet { /// A type that performs custom logic to determine whether a code upgrade is allowed to be /// performed. - type PreCodeUpgradeChecker: PreCodeUpgradeChecker; + type PreCodeUpgrade: PreCodeUpgrade; /// Type that executes some custom logic upon a successful code upgrade. type OnCodeUpgrade: OnCodeUpgrade; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 6007c16ae697..ed3438a3afa8 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -935,7 +935,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; - type PreCodeUpgradeChecker = Registrar; + type PreCodeUpgrade = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = Registrar; } diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 53897cd4fc57..e3662c6b5d61 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -534,7 +534,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; - type PreCodeUpgradeChecker = (); + type PreCodeUpgrade = (); type OnCodeUpgrade = (); type OnNewHead = (); } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 1681e6271e2b..593c92013ada 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1148,7 +1148,7 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; - type PreCodeUpgradeChecker = Registrar; + type PreCodeUpgrade = Registrar; type OnCodeUpgrade = Registrar; type OnNewHead = (); } From 1da6ac842a2caa0676e2421a7edeb3c8e7cad50d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 9 Dec 2023 15:19:22 +0100 Subject: [PATCH 053/101] execute pre_code_upgrade in inclusion --- .../runtime/common/src/integration_tests.rs | 8 -------- .../runtime/common/src/paras_registrar/mod.rs | 18 +++++++++--------- .../runtime/parachains/src/inclusion/mod.rs | 17 +++++++++++++++-- polkadot/runtime/parachains/src/paras/mod.rs | 3 ++- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 23076d948aad..20e81fce7bc1 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -649,14 +649,6 @@ fn para_upgrade_initiated_by_manager_works() { // The reserved deposit should cover for the size difference of the new validation code. let total_bytes_stored = code_size as u32 + head_size as u32; - assert_eq!( - last_event(), - paras_registrar::Event::::CodeUpgradeScheduled { - para_id, - new_deposit: ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) - } - .into(), - ); assert_eq!( Balances::reserved_balance(&account_id(1)), ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index cc468eb9aec6..eca02e5a9fce 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -196,7 +196,6 @@ pub mod pallet { Swapped { para_id: ParaId, other_id: ParaId }, BillingAccountSet { para_id: ParaId, who: T::AccountId }, Refunded { para_id: ParaId, who: T::AccountId, amount: BalanceOf }, - CodeUpgradeScheduled { para_id: ParaId, new_deposit: BalanceOf }, } #[pallet::error] @@ -457,10 +456,6 @@ pub mod pallet { /// In case the call is made by the parachain manager or the parachain itself, there will be /// associated upgrade costs. Depending on the size of the new validation code, the caller's /// reserved deposit might be adjusted to account for the size difference. - /// - /// ## Events - /// The `CodeUpgradeScheduled` event is emitted in case of success, containing information - /// about the new total deposit reserved for the parachain. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::schedule_code_upgrade(new_code.0.len() as u32))] pub fn schedule_code_upgrade( @@ -470,7 +465,8 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - // Root doesn't pay, hence all the pre checking logic is skipped. + // Upgrades initiated by the root origin do not have any upgrade cost-related + // requirements. For this reason we can skip all the pre code upgrade checks. let skip_checks = ensure_root(origin.clone()).is_ok(); Self::do_schedule_code_upgrade(para, new_code, skip_checks) @@ -803,8 +799,6 @@ impl Pallet { .map_err(|e| e.error)?; runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; - // TODO: - //Self::deposit_event(Event::::CodeUpgradeScheduled { para_id: para, new_deposit }); Ok(()) } @@ -890,6 +884,12 @@ impl OnNewHead for Pallet { } impl PreCodeUpgrade for Pallet { + /// Ensures that all upgrade-related costs are covered for the specific parachain. + /// + /// Upon success, it updates the deposit-related state for the parachain. + /// + /// `skip_checks` signals that the upgrade can be scheduled for free. This should occur when the + /// upgrade is scheduled by the Root. fn pre_code_upgrade( para: ParaId, new_code: ValidationCode, @@ -919,7 +919,7 @@ impl PreCodeUpgrade for Pallet { // // 2. System parachains do not pay for upgrade fees. // - // 2. All lease-holding parachains are permitted to do upgrades for free. This is introduced + // 3. All lease-holding parachains are permitted to do upgrades for free. This is introduced // to avoid causing a breaking change to the system once para upgrade fees are required. let free_upgrade = skip_checks || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 90af9cde00a8..2d1c5fa7957f 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -22,7 +22,7 @@ use crate::{ configuration::{self, HostConfiguration}, disputes, dmp, hrmp, - paras::{self, SetGoAhead}, + paras::{self, PreCodeUpgrade, SetGoAhead}, scheduler::{self, AvailabilityTimeoutStatus}, shared::{self, AllowedRelayParentsTracker}, }; @@ -882,7 +882,20 @@ impl Pallet { // Block number of candidate's inclusion. let now = >::block_number(); - weight.saturating_add(>::schedule_code_upgrade( + match ::PreCodeUpgrade::pre_code_upgrade( + receipt.descriptor.para_id, + new_code.clone(), + false, + ) { + Ok(info) => weight.saturating_accrue(info.actual_weight.unwrap_or_default()), + Err(err) => { + // The execution of the pre code upgrade logic failed, so we cannot proceed with + // scheduling the upgrade. + return weight.saturating_add(err.post_info.actual_weight.unwrap_or_default()) + }, + }; + + weight.saturating_accrue(>::schedule_code_upgrade( receipt.descriptor.para_id, new_code, now, diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 7b51c887803f..771db6c072c6 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -528,7 +528,8 @@ pub trait PreCodeUpgrade { /// This is currently utilized by the registrar pallet to ensure that the necessary validation /// code upgrade costs are covered. /// - /// TODO: docs + /// `skip_checks` signals that the pre code upgrade checks performed by this function can be + /// skipped. fn pre_code_upgrade( id: ParaId, new_code: ValidationCode, From 713ad0b5a85cb559183829ff11f5577b02f693b9 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sat, 9 Dec 2023 15:42:53 +0100 Subject: [PATCH 054/101] use proper return type | WIP --- .../runtime/common/src/paras_registrar/mod.rs | 46 +++++++++++++------ .../runtime/parachains/src/inclusion/mod.rs | 6 +-- polkadot/runtime/parachains/src/paras/mod.rs | 9 ++-- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index eca02e5a9fce..d02c351c7626 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -93,6 +93,7 @@ pub trait WeightInfo { fn force_register() -> Weight; fn deregister() -> Weight; fn swap() -> Weight; + fn pre_code_upgrade() -> Weight; fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; fn set_parachain_billing_account_to_self() -> Weight; @@ -116,6 +117,9 @@ impl WeightInfo for TestWeightInfo { fn swap() -> Weight { Weight::zero() } + fn pre_code_upgrade() -> Weight { + Weight::zero() + } fn schedule_code_upgrade(_b: u32) -> Weight { Weight::zero() } @@ -795,8 +799,10 @@ impl Pallet { // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); - T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_checks) - .map_err(|e| e.error)?; + ensure!( + T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_checks).is_ok(), + Error::::CannotUpgrade + ); runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; Ok(()) @@ -894,22 +900,22 @@ impl PreCodeUpgrade for Pallet { para: ParaId, new_code: ValidationCode, skip_checks: bool, - ) -> DispatchResultWithPostInfo { - let head = - paras::Pallet::::para_head(para).map_or(Err(Error::::NotRegistered), Ok)?; + ) -> Result { + let Some(head) = paras::Pallet::::para_head(para) else { + return Err(T::DbWeight::get().reads(1)) + }; + + let Some(mut info) = Paras::::get(para) else { return Err(T::DbWeight::get().reads(2)) }; + + let Some(billing_account) = info.billing_account.clone() else { + return Err(T::DbWeight::get().reads(3)) + }; let per_byte_fee = T::DataDepositPerByte::get(); let new_deposit = T::ParaDeposit::get() .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; - - let billing_account = info.billing_account.clone(); - ensure!(billing_account.clone().is_some(), Error::::BillingAccountNotSet); - let billing_account = - billing_account.expect("Ensured above that the billing account is set to some; qed"); - let current_deposit = info.deposit; // There are three cases where we do not charge any upgrade costs from the initiator @@ -930,10 +936,10 @@ impl PreCodeUpgrade for Pallet { T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, - )?; + )/*?*/; let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&billing_account, additional_deposit)?; + ::Currency::reserve(&billing_account, additional_deposit)/*?*/; // Update the deposit to the new appropriate amount. info.deposit = new_deposit; @@ -959,7 +965,7 @@ impl PreCodeUpgrade for Pallet { Paras::::insert(para, info); - Ok(().into()) // FIXME: actual weight + Ok(::WeightInfo::pre_code_upgrade()) } } @@ -1966,6 +1972,16 @@ mod benchmarking { next_scheduled_session::(); }: _(RawOrigin::Root, para, new_code) + /* + pre_code_upgrade { + let para = register_para::(LOWEST_PUBLIC_ID.into()); + let new_code = ValidationCode(vec![0; b as usize]); + + // Actually finish registration process + next_scheduled_session::(); + }: T::pre_code_upgrade(para, new_code, false) + */ + set_current_head { let b in 1 .. MAX_HEAD_DATA_SIZE; let new_head = HeadData(vec![0; b as usize]); diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 2d1c5fa7957f..e732c492fd4a 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -887,11 +887,11 @@ impl Pallet { new_code.clone(), false, ) { - Ok(info) => weight.saturating_accrue(info.actual_weight.unwrap_or_default()), - Err(err) => { + Ok(consumed_weight) => weight.saturating_accrue(consumed_weight), + Err(consumed_weight) => { // The execution of the pre code upgrade logic failed, so we cannot proceed with // scheduling the upgrade. - return weight.saturating_add(err.post_info.actual_weight.unwrap_or_default()) + return weight.saturating_add(consumed_weight) }, }; diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 771db6c072c6..ec252ee3a388 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -530,11 +530,14 @@ pub trait PreCodeUpgrade { /// /// `skip_checks` signals that the pre code upgrade checks performed by this function can be /// skipped. + /// + /// As a result, it indicates either the success or failure of executing the pre-code upgrade + /// logic. In both cases, it returns the consumed weight. fn pre_code_upgrade( id: ParaId, new_code: ValidationCode, skip_checks: bool, - ) -> DispatchResultWithPostInfo; + ) -> Result; } /// An empty implementation of the trait where there are no checks performed before scheduling a @@ -544,8 +547,8 @@ impl PreCodeUpgrade for () { _id: ParaId, _new_code: ValidationCode, _skip_checks: bool, - ) -> DispatchResultWithPostInfo { - Ok(().into()) + ) -> Result { + Ok(Weight::zero()) } } From 4082b7a1577f287d227f64020dafeb04fdc1b1c0 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 10 Dec 2023 11:09:54 +0100 Subject: [PATCH 055/101] fixes --- .../runtime/common/src/paras_registrar/mod.rs | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index d02c351c7626..2e92c10ffcbd 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -22,7 +22,7 @@ pub mod migration; use frame_support::{ dispatch::DispatchResult, ensure, - pallet_prelude::{DispatchResultWithPostInfo, Weight}, + pallet_prelude::Weight, traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; @@ -84,6 +84,17 @@ impl ParaInfo { } } +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +pub enum CodeUpgradeScheduleError { + /// The parachain billing account has to be explicitly set before being able to schedule a + /// code upgrade. + BillingAccountNotSet, + /// Failed to pay the upgrade fee for scheduling the code upgrade. + FailedToPayUpgradeFee, + /// Failed to reserve the appropriate deposit for the new validation code. + FailedToReserveDeposit, +} + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -200,6 +211,10 @@ pub mod pallet { Swapped { para_id: ParaId, other_id: ParaId }, BillingAccountSet { para_id: ParaId, who: T::AccountId }, Refunded { para_id: ParaId, who: T::AccountId, amount: BalanceOf }, + // NOTE: This event won't be emitted if the upgrade is scheduled using the extrinsic, + // since failed dispatchables can't emit events. This is useful for upgrades that are + // triggered from the parachain inclusion pallet, which is almost always the case anyway. + CodeUpgradeScheduleFailed(CodeUpgradeScheduleError), } #[pallet::error] @@ -234,9 +249,6 @@ pub mod pallet { /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras /// are correct for the swap to work. CannotSwap, - /// The parachain billing account has to be explicitly set before being able to schedule a - /// code upgrade. - BillingAccountNotSet, } /// Pending swap operations. @@ -908,6 +920,9 @@ impl PreCodeUpgrade for Pallet { let Some(mut info) = Paras::::get(para) else { return Err(T::DbWeight::get().reads(2)) }; let Some(billing_account) = info.billing_account.clone() else { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::BillingAccountNotSet, + )); return Err(T::DbWeight::get().reads(3)) }; @@ -931,15 +946,29 @@ impl PreCodeUpgrade for Pallet { skip_checks || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); if !free_upgrade { - ::Currency::withdraw( + if let Err(_) = ::Currency::withdraw( &billing_account, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, - )/*?*/; + ) { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToPayUpgradeFee, + )); + // This is an overestimate of the used weight, but it's better to be safe than + // sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } let additional_deposit = new_deposit.saturating_sub(current_deposit); - ::Currency::reserve(&billing_account, additional_deposit)/*?*/; + if let Err(_) = ::Currency::reserve(&billing_account, additional_deposit) { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToReserveDeposit, + )); + // This is an overestimate of the used weight, but it's better to be safe than + // sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } // Update the deposit to the new appropriate amount. info.deposit = new_deposit; @@ -1817,7 +1846,7 @@ mod tests { ParaId::from(para_id), validation_code ), - paras_registrar::Error::::BillingAccountNotSet + paras_registrar::Error::::CannotUpgrade ); }); } @@ -1972,15 +2001,15 @@ mod benchmarking { next_scheduled_session::(); }: _(RawOrigin::Root, para, new_code) - /* pre_code_upgrade { let para = register_para::(LOWEST_PUBLIC_ID.into()); - let new_code = ValidationCode(vec![0; b as usize]); + let new_code = ValidationCode(vec![0]); // Actually finish registration process next_scheduled_session::(); - }: T::pre_code_upgrade(para, new_code, false) - */ + }: { + let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_code, false); + } set_current_head { let b in 1 .. MAX_HEAD_DATA_SIZE; @@ -2002,7 +2031,7 @@ mod benchmarking { let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); }: _(para_origin, para) verify { - assert_eq!(ParaInfo::::get(para).billing_account, Some(sovereign_account)); + assert_eq!(Paras::::get(para).unwrap().billing_account, Some(sovereign_account)); } force_set_parachain_billing_account { @@ -2017,7 +2046,7 @@ mod benchmarking { T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); }: _(RawOrigin::Root, para, sovereign_account.clone()) verify { - assert_eq!(ParaInfo::::get(para).billing_account, Some(sovereign_account)); + assert_eq!(Paras::::get(para).unwrap().billing_account, Some(sovereign_account)); } impl_benchmark_test_suite!( From 66a8e629105c42d26a18207832df10712de40932 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 10 Dec 2023 11:19:46 +0100 Subject: [PATCH 056/101] better docs & add missing weight --- .../runtime/common/src/paras_registrar/mod.rs | 23 ++++++++----------- polkadot/runtime/parachains/src/paras/mod.rs | 6 ++--- .../weights/runtime_common_paras_registrar.rs | 14 +++++++++++ .../weights/runtime_common_paras_registrar.rs | 14 +++++++++++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2e92c10ffcbd..72f53f4622fc 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -905,9 +905,16 @@ impl PreCodeUpgrade for Pallet { /// Ensures that all upgrade-related costs are covered for the specific parachain. /// /// Upon success, it updates the deposit-related state for the parachain. - /// - /// `skip_checks` signals that the upgrade can be scheduled for free. This should occur when the - /// upgrade is scheduled by the Root. + // There are three cases where we do not charge any upgrade costs from the initiator + // of the upgrade: + // + // 1. `skip_checks` is explicitly set to true. This This should occur when the upgrade is + // scheduled by the Root. + // + // 2. System parachains do not pay for upgrade fees. + // + // 3. All lease-holding parachains are permitted to do upgrades for free. This is introduced to + // avoid causing a breaking change to the system once para upgrade fees are required. fn pre_code_upgrade( para: ParaId, new_code: ValidationCode, @@ -933,15 +940,6 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; - // There are three cases where we do not charge any upgrade costs from the initiator - // of the upgrade: - // - // 1. `skip_checks` is set to true. - // - // 2. System parachains do not pay for upgrade fees. - // - // 3. All lease-holding parachains are permitted to do upgrades for free. This is introduced - // to avoid causing a breaking change to the system once para upgrade fees are required. let free_upgrade = skip_checks || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); @@ -993,7 +991,6 @@ impl PreCodeUpgrade for Pallet { } Paras::::insert(para, info); - Ok(::WeightInfo::pre_code_upgrade()) } } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index ec252ee3a388..f2ee2518893e 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -523,7 +523,7 @@ impl OnCodeUpgrade for () { } pub trait PreCodeUpgrade { - /// A function that performs custom logic to before performing a code upgrade. + /// A function that performs custom logic to before sceduling a code upgrade. /// /// This is currently utilized by the registrar pallet to ensure that the necessary validation /// code upgrade costs are covered. @@ -531,8 +531,8 @@ pub trait PreCodeUpgrade { /// `skip_checks` signals that the pre code upgrade checks performed by this function can be /// skipped. /// - /// As a result, it indicates either the success or failure of executing the pre-code upgrade - /// logic. In both cases, it returns the consumed weight. + /// As a result, it indicates either the success or failure of executing the pre code upgrade + /// scheduling logic. In both cases, it returns the consumed weight. fn pre_code_upgrade( id: ParaId, new_code: ValidationCode, diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 7b1e809515c5..1e8cc5e3edf7 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -221,6 +221,20 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. + fn pre_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. fn set_parachain_billing_account_to_self() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 368262c22e39..1b7054e7c57e 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -218,6 +218,20 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. + fn pre_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. fn set_parachain_billing_account_to_self() -> Weight { // Proof Size summary in bytes: // Measured: `0` From 865063d9a869a2ef561942a952cd7d8f31a5b95c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 10 Dec 2023 12:43:49 +0100 Subject: [PATCH 057/101] more clarity --- .../runtime/common/src/paras_registrar/mod.rs | 17 +++++++++-------- polkadot/runtime/parachains/src/paras/mod.rs | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 72f53f4622fc..734c5cebb502 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -483,9 +483,9 @@ pub mod pallet { // Upgrades initiated by the root origin do not have any upgrade cost-related // requirements. For this reason we can skip all the pre code upgrade checks. - let skip_checks = ensure_root(origin.clone()).is_ok(); + let skip_requirements = ensure_root(origin.clone()).is_ok(); - Self::do_schedule_code_upgrade(para, new_code, skip_checks) + Self::do_schedule_code_upgrade(para, new_code, skip_requirements) } /// Set the parachain's current head. @@ -798,21 +798,22 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `skip_pre_upgrade` is set to true all the pre code upgrade logic will be skipped. + /// If `skip_requirements` is set to true all the pre code upgrade cost related requierments + /// will be ignored. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - skip_checks: bool, + skip_requirements: bool, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); ensure!( - T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_checks).is_ok(), + T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_requirements).is_ok(), Error::::CannotUpgrade ); runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; @@ -908,7 +909,7 @@ impl PreCodeUpgrade for Pallet { // There are three cases where we do not charge any upgrade costs from the initiator // of the upgrade: // - // 1. `skip_checks` is explicitly set to true. This This should occur when the upgrade is + // 1. `skip_requirements` is explicitly set to true. This This should occur when the upgrade is // scheduled by the Root. // // 2. System parachains do not pay for upgrade fees. @@ -918,7 +919,7 @@ impl PreCodeUpgrade for Pallet { fn pre_code_upgrade( para: ParaId, new_code: ValidationCode, - skip_checks: bool, + skip_requirements: bool, ) -> Result { let Some(head) = paras::Pallet::::para_head(para) else { return Err(T::DbWeight::get().reads(1)) @@ -941,7 +942,7 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; let free_upgrade = - skip_checks || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); + skip_requirements || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); if !free_upgrade { if let Err(_) = ::Currency::withdraw( diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index f2ee2518893e..554a7468c9c1 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -528,15 +528,15 @@ pub trait PreCodeUpgrade { /// This is currently utilized by the registrar pallet to ensure that the necessary validation /// code upgrade costs are covered. /// - /// `skip_checks` signals that the pre code upgrade checks performed by this function can be - /// skipped. + /// `skip_requirements` signals that the pre code upgrade requirements by this function can be + /// ignored. /// /// As a result, it indicates either the success or failure of executing the pre code upgrade /// scheduling logic. In both cases, it returns the consumed weight. fn pre_code_upgrade( id: ParaId, new_code: ValidationCode, - skip_checks: bool, + skip_requirements: bool, ) -> Result; } @@ -546,7 +546,7 @@ impl PreCodeUpgrade for () { fn pre_code_upgrade( _id: ParaId, _new_code: ValidationCode, - _skip_checks: bool, + _skip_requirements: bool, ) -> Result { Ok(Weight::zero()) } From f1dd96199b999c4a119995e8bd1e185359090fe4 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 10 Dec 2023 12:50:15 +0100 Subject: [PATCH 058/101] doc fixes --- .../runtime/common/src/paras_registrar/mod.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 734c5cebb502..e0cfe23a1540 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -798,7 +798,7 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `skip_requirements` is set to true all the pre code upgrade cost related requierments + /// If `skip_requirements` is set to true all the code upgrade cost related requierments /// will be ignored. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be @@ -906,16 +906,16 @@ impl PreCodeUpgrade for Pallet { /// Ensures that all upgrade-related costs are covered for the specific parachain. /// /// Upon success, it updates the deposit-related state for the parachain. - // There are three cases where we do not charge any upgrade costs from the initiator - // of the upgrade: - // - // 1. `skip_requirements` is explicitly set to true. This This should occur when the upgrade is - // scheduled by the Root. - // - // 2. System parachains do not pay for upgrade fees. - // - // 3. All lease-holding parachains are permitted to do upgrades for free. This is introduced to - // avoid causing a breaking change to the system once para upgrade fees are required. + /// There are three cases where we do not charge any upgrade costs from the initiator + /// of the upgrade: + /// + /// 1. `skip_requirements` is explicitly set to true. This This should occur when the upgrade is + /// scheduled by the Root. + /// + /// 2. System parachains do not pay for upgrade fees. + /// + /// 3. All lease-holding parachains are permitted to do upgrades for free. This is introduced to + /// avoid causing a breaking change to the system once para upgrade fees are required. fn pre_code_upgrade( para: ParaId, new_code: ValidationCode, From c0f31fb1818a8400b1d511ea11163fbcd67223bb Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:00:08 +0100 Subject: [PATCH 059/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index e0cfe23a1540..8908ef268cba 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -70,7 +70,7 @@ pub struct ParaInfo { /// /// None indicates that the billing account hasn't been set, which is the case for all legacy /// parachains. Attempting to schedule a parachain upgrade will fail if the billing account is - /// set to None, except in the case where the parachain is not a lease-holding parachain. + /// set to None, except in the case where the parachain is a lease-holding parachain. billing_account: Option, /// In case there is a pending refund for the current billing account, this stores information /// about the amount that will be refunded upon a successful code upgrade. From f35bf62023abbba2fcdaec80a6dcff1ca43b78e1 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Sun, 10 Dec 2023 16:02:05 +0100 Subject: [PATCH 060/101] Update polkadot/runtime/common/src/paras_registrar/migration.rs --- polkadot/runtime/common/src/paras_registrar/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 62c4137f6d08..bddb58e6a308 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -63,7 +63,7 @@ impl> OnRuntimeUpgrade let info = Paras::::get(para_id).unwrap(); ensure!( info.billing_account.is_none(), - "The billing account must be set to the para manager" + "The billing account must be set to the None" ); ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); From 2082dbf7271a41d0c2e7deeaeaf897e00e867509 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 10 Dec 2023 16:22:44 +0100 Subject: [PATCH 061/101] don't short circuit --- .../common/src/paras_registrar/migration.rs | 5 +--- .../runtime/parachains/src/inclusion/mod.rs | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index bddb58e6a308..ad3477aed05a 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -61,10 +61,7 @@ impl> OnRuntimeUpgrade Paras::::iter_keys().try_for_each(|para_id| -> Result<(), _> { let info = Paras::::get(para_id).unwrap(); - ensure!( - info.billing_account.is_none(), - "The billing account must be set to the None" - ); + ensure!(info.billing_account.is_none(), "The billing account must be set to the None"); ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); Ok(()) diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index e732c492fd4a..a3c77722e33a 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -887,21 +887,26 @@ impl Pallet { new_code.clone(), false, ) { - Ok(consumed_weight) => weight.saturating_accrue(consumed_weight), + Ok(consumed_weight) => { + weight.saturating_accrue(consumed_weight); + + weight.saturating_accrue(>::schedule_code_upgrade( + receipt.descriptor.para_id, + new_code, + now, + &config, + SetGoAhead::Yes, + )); + }, Err(consumed_weight) => { - // The execution of the pre code upgrade logic failed, so we cannot proceed with - // scheduling the upgrade. - return weight.saturating_add(consumed_weight) + log::debug!( + target: LOG_TARGET, + "Failed to schedule a code upgrade for paraID: {}", + receipt.descriptor.para_id, + ); + weight.saturating_accrue(consumed_weight); }, }; - - weight.saturating_accrue(>::schedule_code_upgrade( - receipt.descriptor.para_id, - new_code, - now, - &config, - SetGoAhead::Yes, - )); } // enact the messaging facet of the candidate. From d6082ab848adb96bb4e694d6b0bcc770b9396943 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 11 Dec 2023 08:30:01 +0100 Subject: [PATCH 062/101] set billing account to none upon registration --- .../runtime/common/src/paras_registrar/mod.rs | 33 ++++++++----------- .../runtime/parachains/src/inclusion/mod.rs | 4 +-- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 8908ef268cba..8b8b807fc17a 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -48,7 +48,7 @@ use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { - /// The account that has placed a deposit for registering this para. + /// The account that has initially placed a deposit for registering this para. /// /// The given account ID is responsible for registering the code and initial head data, but may /// only do so if it isn't yet registered. (After that, it's up to governance to do so.) @@ -58,22 +58,17 @@ pub struct ParaInfo { /// Whether the para registration should be locked from being controlled by the manager. /// None means the lock had not been explicitly set, and should be treated as false. locked: Option, - /// The billing account of a parachain. This account is responsible for holding the required - /// deposit for this registered parachain. + /// The billing account for a parachain. This account is responsible for holding the required + /// deposit and covering all associated costs related to scheduling parachain validation code + /// upgrades. /// - /// This account will be responsible for covering all associated costs related to performing - /// parachain validation code upgrades. + /// This account must be explicitly set using the `set_parachain_billing_account_to_self` + /// extrinsic /// - /// When a parachain is newly registered, this will be set to the parachain manager. However, - /// at a later point, this can be updated by calling the - /// `set_parachain_billing_account_to_self` extrinsic. - /// - /// None indicates that the billing account hasn't been set, which is the case for all legacy - /// parachains. Attempting to schedule a parachain upgrade will fail if the billing account is - /// set to None, except in the case where the parachain is a lease-holding parachain. + /// None indicates that the billing account hasn't been set, and attempting to schedule a + /// parachain upgrade will result in failure. billing_account: Option, - /// In case there is a pending refund for the current billing account, this stores information - /// about the amount that will be refunded upon a successful code upgrade. + /// The deposit that will be refunded upon a successful code upgrade. pending_deposit_refund: Option, } @@ -724,7 +719,7 @@ impl Pallet { manager: who.clone(), deposit, locked: None, - billing_account: Some(who.clone()), + billing_account: None, pending_deposit_refund: None, }; @@ -765,7 +760,7 @@ impl Pallet { manager: who.clone(), deposit, locked: None, - billing_account: Some(who.clone()), + billing_account: None, pending_deposit_refund: None, }; @@ -903,9 +898,9 @@ impl OnNewHead for Pallet { } impl PreCodeUpgrade for Pallet { - /// Ensures that all upgrade-related costs are covered for the specific parachain. + /// Ensures that all upgrade-related costs are covered for the specific parachain. Upon success, + /// it updates the deposit-related state for the parachain. /// - /// Upon success, it updates the deposit-related state for the parachain. /// There are three cases where we do not charge any upgrade costs from the initiator /// of the upgrade: /// @@ -1017,7 +1012,7 @@ impl OnCodeUpgrade for Pallet { who: billing_account, amount: rebate, }); - return T::DbWeight::get().reads_writes(2, 2) + return T::DbWeight::get().reads_writes(2, 2) // FIXME: This is inaccurate weight } } diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index a3c77722e33a..ca5306e4dccc 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -901,8 +901,8 @@ impl Pallet { Err(consumed_weight) => { log::debug!( target: LOG_TARGET, - "Failed to schedule a code upgrade for paraID: {}", - receipt.descriptor.para_id, + "Failed to schedule a code upgrade for parachain {}", + u32::from(receipt.descriptor.para_id), ); weight.saturating_accrue(consumed_weight); }, From 2f7832118979799afe64f9e9985c7a0dc3da861c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 11 Dec 2023 08:53:11 +0100 Subject: [PATCH 063/101] update tests to explicitly set the billing account & small fix in pre-code upgrade logic --- .../runtime/common/src/integration_tests.rs | 25 ++++++- .../runtime/common/src/paras_registrar/mod.rs | 72 ++++--------------- 2 files changed, 34 insertions(+), 63 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 20e81fce7bc1..62e7cb0c6fc9 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -637,7 +637,20 @@ fn para_upgrade_initiated_by_manager_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); - // CASE 1: Schedule a para upgrade to set the validation code to a new one which is twice + // CASE 1: Attempting to schedule a parachain upgrade without setting the billing account + // beforehand will result in failure. + assert_noop!( + Registrar::schedule_code_upgrade(signed(1), ParaId::from(para_id), code_0,), + paras_registrar::Error::::CannotUpgrade + ); + + // Set the billing account to be able to schedule code upgrades. + assert_ok!(Registrar::set_parachain_billing_account_to_self( + signed(1), + ParaId::from(para_id), + )); + + // CASE 2: Schedule a para upgrade to set the validation code to a new one which is twice // the size. code_size *= 2; let code_1 = validation_code(code_size); @@ -667,7 +680,7 @@ fn para_upgrade_initiated_by_manager_works() { )); assert_eq!(Paras::current_code(¶_id), Some(code_1.clone())); - // CASE 2: After successfully upgrading the validation code to twice the size of the + // CASE 3: After successfully upgrading the validation code to twice the size of the // previous one, we will now proceed to upgrade the validation code to a smaller size. It is // expected that the parachain manager will receive a refund upon the successful completion // of the upgrade. @@ -711,7 +724,7 @@ fn para_upgrade_initiated_by_manager_works() { // An additional upgrade fee should also be deducted from the caller's balance. assert_eq!(Balances::total_balance(&account_id(1)), free_balance - (2 * UpgradeFee::get())); - // CASE 3: Para manager won't get refunded if the code upgrade fails + // CASE 4: Para manager won't get refunded if the code upgrade fails let code_3 = validation_code(42); assert_ok!(Registrar::schedule_code_upgrade( signed(1), @@ -773,6 +786,12 @@ fn root_upgrading_parachain_works() { ParaDeposit::get() + (total_bytes_stored * DataDepositPerByte::get()) ); + // Set the billing account to be able to schedule code upgrades. + assert_ok!(Registrar::set_parachain_billing_account_to_self( + signed(1), + ParaId::from(para_id), + )); + // CASE 1: Root schedules a para upgrade to set the validation code to a new one which is // twice the size. diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 8b8b807fc17a..f0d4409549ff 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -921,13 +921,15 @@ impl PreCodeUpgrade for Pallet { }; let Some(mut info) = Paras::::get(para) else { return Err(T::DbWeight::get().reads(2)) }; + let lease_holding = Self::is_parachain(para); - let Some(billing_account) = info.billing_account.clone() else { + // The billing account doesn't need to be set for lease holding parachains. + if info.billing_account.is_none() && !lease_holding { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( CodeUpgradeScheduleError::BillingAccountNotSet, )); return Err(T::DbWeight::get().reads(3)) - }; + } let per_byte_fee = T::DataDepositPerByte::get(); let new_deposit = T::ParaDeposit::get() @@ -936,10 +938,14 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; - let free_upgrade = - skip_requirements || para < LOWEST_PUBLIC_ID || Self::parachains().contains(¶); + let free_upgrade = skip_requirements || para < LOWEST_PUBLIC_ID || lease_holding; + + if !free_upgrade && info.billing_account.clone().is_some() { + let billing_account = info + .billing_account + .clone() + .expect("Ensured above that the billing account is defined; qed"); - if !free_upgrade { if let Err(_) = ::Currency::withdraw( &billing_account, T::UpgradeFee::get(), @@ -1012,7 +1018,7 @@ impl OnCodeUpgrade for Pallet { who: billing_account, amount: rebate, }); - return T::DbWeight::get().reads_writes(2, 2) // FIXME: This is inaccurate weight + return T::DbWeight::get().reads_writes(2, 2) // FIXME: This is inaccurate weight } } @@ -1789,60 +1795,6 @@ mod tests { assert!(Parachains::is_parathread(para_2)); }); } - - #[test] - fn billing_account_has_to_be_explicitly_set() { - new_test_ext().execute_with(|| { - const START_SESSION_INDEX: SessionIndex = 1; - run_to_session(START_SESSION_INDEX); - - let para_id = LOWEST_PUBLIC_ID; - assert!(!Parachains::is_parathread(para_id)); - - let validation_code = test_validation_code(32); - assert_ok!(Registrar::reserve(RuntimeOrigin::signed(1))); - assert_ok!(Registrar::register( - RuntimeOrigin::signed(1), - para_id, - test_genesis_head(32), - validation_code.clone(), - )); - conclude_pvf_checking::(&validation_code, VALIDATORS, START_SESSION_INDEX, true); - - run_to_session(START_SESSION_INDEX + 2); - assert!(Parachains::is_parathread(para_id)); - - assert_ok!(Registrar::make_parachain(para_id)); - run_to_session(START_SESSION_INDEX + 4); - assert!(Parachains::is_parachain(para_id)); - - // Legacy lease holding parachains won't have their billing account set. So if a - // parachain no longer has a parachain slot, the billing account must be explicitly - // set before initiating code upgrades. - - // Downgrade the lease holding parachain to a parathread. - assert_ok!(Registrar::make_parathread(para_id)); - run_to_session(START_SESSION_INDEX + 6); - assert!(Registrar::is_parathread(para_id)); - - // To simulate this we will have to manually set the billing account to `None`. - Paras::::mutate_exists(para_id, |maybe_info| { - if let Some(info) = maybe_info { - info.billing_account = None - } - }); - - let validation_code = test_validation_code(42); - assert_noop!( - Registrar::schedule_code_upgrade( - RuntimeOrigin::signed(1), - ParaId::from(para_id), - validation_code - ), - paras_registrar::Error::::CannotUpgrade - ); - }); - } } #[cfg(feature = "runtime-benchmarks")] From a04914b6efe0395da209c0037b961f765e9c26c0 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 11 Dec 2023 09:15:53 +0100 Subject: [PATCH 064/101] benchmarks --- .../runtime/common/src/paras_registrar/mod.rs | 30 ++++++++++++++++--- .../weights/runtime_common_paras_registrar.rs | 14 +++++++++ .../weights/runtime_common_paras_registrar.rs | 14 +++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f0d4409549ff..f7f077c073d1 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -100,6 +100,7 @@ pub trait WeightInfo { fn deregister() -> Weight; fn swap() -> Weight; fn pre_code_upgrade() -> Weight; + fn on_code_upgrade() -> Weight; fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; fn set_parachain_billing_account_to_self() -> Weight; @@ -126,6 +127,9 @@ impl WeightInfo for TestWeightInfo { fn pre_code_upgrade() -> Weight { Weight::zero() } + fn on_code_upgrade() -> Weight { + Weight::zero() + } fn schedule_code_upgrade(_b: u32) -> Weight { Weight::zero() } @@ -1018,7 +1022,7 @@ impl OnCodeUpgrade for Pallet { who: billing_account, amount: rebate, }); - return T::DbWeight::get().reads_writes(2, 2) // FIXME: This is inaccurate weight + return ::WeightInfo::on_code_upgrade() } } @@ -1944,16 +1948,34 @@ mod benchmarking { // Actually finish registration process next_scheduled_session::(); - }: _(RawOrigin::Root, para, new_code) + // Set the billing account + let caller: T::AccountId = whitelisted_caller(); + assert_ok!(Registrar::::force_set_parachain_billing_account(RawOrigin::Root.into(), para, caller.clone())); + }: _(RawOrigin::Signed(caller), para, new_code) pre_code_upgrade { let para = register_para::(LOWEST_PUBLIC_ID.into()); - let new_code = ValidationCode(vec![0]); + let new_small_code = ValidationCode(vec![0]); // Actually finish registration process next_scheduled_session::(); }: { - let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_code, false); + let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_small_code, false); + } + + on_code_upgrade { + let para = register_para::(LOWEST_PUBLIC_ID.into()); + let new_small_code = ValidationCode(vec![0]); + + // Actually finish registration process + next_scheduled_session::(); + // Set the billing account + assert_ok!(Registrar::::force_set_parachain_billing_account(RawOrigin::Root.into(), para, whitelisted_caller())); + + assert_ok!(Registrar::::schedule_code_upgrade(RawOrigin::Root.into(), para, new_small_code)); + assert!(Paras::::get(para).unwrap().pending_deposit_refund.is_some()); + }: { + let _ = T::OnCodeUpgrade::on_code_upgrade(para); } set_current_head { diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 1e8cc5e3edf7..53764d7c1e67 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -235,6 +235,20 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. + fn on_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. fn set_parachain_billing_account_to_self() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 1b7054e7c57e..9ee249f587c5 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -232,6 +232,20 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. + fn on_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. fn set_parachain_billing_account_to_self() -> Weight { // Proof Size summary in bytes: // Measured: `0` From bca6464254755098ec71eefc48d546926cbcbc63 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 11 Dec 2023 13:43:36 +0100 Subject: [PATCH 065/101] improve readability --- .../runtime/common/src/paras_registrar/mod.rs | 97 ++++++++++--------- .../runtime/parachains/src/inclusion/mod.rs | 4 +- polkadot/runtime/parachains/src/paras/mod.rs | 13 ++- 3 files changed, 63 insertions(+), 51 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f7f077c073d1..3e860f466e16 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -37,7 +37,7 @@ use sp_std::{prelude::*, result}; use crate::traits::{OnSwap, Registrar}; pub use pallet::*; use parity_scale_codec::{Decode, Encode}; -use runtime_parachains::paras::{OnNewHead, ParaKind}; +use runtime_parachains::paras::{OnNewHead, ParaKind, UpgradeRequirements}; use scale_info::TypeInfo; use sp_runtime::{ traits::{CheckedSub, Saturating}, @@ -480,11 +480,15 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - // Upgrades initiated by the root origin do not have any upgrade cost-related - // requirements. For this reason we can skip all the pre code upgrade checks. - let skip_requirements = ensure_root(origin.clone()).is_ok(); + let requierments = if ensure_root(origin.clone()).is_ok() { + // Upgrades initiated by the root origin do not have any upgrade cost-related + // requirements. For this reason we can skip all the pre code upgrade checks. + UpgradeRequirements::SkipRequirements + } else { + UpgradeRequirements::EnforceRequirements + }; - Self::do_schedule_code_upgrade(para, new_code, skip_requirements) + Self::do_schedule_code_upgrade(para, new_code, requierments) } /// Set the parachain's current head. @@ -797,22 +801,22 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// - /// If `skip_requirements` is set to true all the code upgrade cost related requierments - /// will be ignored. + /// If `requirements` is set to `UpgradeRequirements::SkipRequirements` all the code upgrade + /// cost related requierments will be ignored. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. fn do_schedule_code_upgrade( para: ParaId, new_code: ValidationCode, - skip_requirements: bool, + requirements: UpgradeRequirements, ) -> DispatchResult { // Before doing anything we ensure that a code upgrade is allowed at the moment for the // specific parachain. ensure!(paras::Pallet::::can_upgrade_validation_code(para), Error::::CannotUpgrade); ensure!( - T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), skip_requirements).is_ok(), + T::PreCodeUpgrade::pre_code_upgrade(para, new_code.clone(), requirements).is_ok(), Error::::CannotUpgrade ); runtime_parachains::schedule_code_upgrade::(para, new_code, SetGoAhead::No)?; @@ -908,8 +912,7 @@ impl PreCodeUpgrade for Pallet { /// There are three cases where we do not charge any upgrade costs from the initiator /// of the upgrade: /// - /// 1. `skip_requirements` is explicitly set to true. This This should occur when the upgrade is - /// scheduled by the Root. + /// 1. `requirements` is explicitly set to `UpgradeRequirements::SkipRequirements`. /// /// 2. System parachains do not pay for upgrade fees. /// @@ -918,7 +921,7 @@ impl PreCodeUpgrade for Pallet { fn pre_code_upgrade( para: ParaId, new_code: ValidationCode, - skip_requirements: bool, + requirements: UpgradeRequirements, ) -> Result { let Some(head) = paras::Pallet::::para_head(para) else { return Err(T::DbWeight::get().reads(1)) @@ -942,40 +945,44 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; - let free_upgrade = skip_requirements || para < LOWEST_PUBLIC_ID || lease_holding; - - if !free_upgrade && info.billing_account.clone().is_some() { - let billing_account = info - .billing_account - .clone() - .expect("Ensured above that the billing account is defined; qed"); - - if let Err(_) = ::Currency::withdraw( - &billing_account, - T::UpgradeFee::get(), - WithdrawReasons::FEE, - ExistenceRequirement::KeepAlive, - ) { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToPayUpgradeFee, - )); - // This is an overestimate of the used weight, but it's better to be safe than - // sorry. - return Err(::WeightInfo::pre_code_upgrade()) - } + let free_upgrade = requirements == UpgradeRequirements::SkipRequirements || + para < LOWEST_PUBLIC_ID || + lease_holding; + + match (free_upgrade, info.billing_account.clone()) { + (false, Some(billing_account)) => { + if let Err(_) = ::Currency::withdraw( + &billing_account, + T::UpgradeFee::get(), + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + ) { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToPayUpgradeFee, + )); + // This is an overestimate of the used weight, but it's better to be safe than + // sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } - let additional_deposit = new_deposit.saturating_sub(current_deposit); - if let Err(_) = ::Currency::reserve(&billing_account, additional_deposit) { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToReserveDeposit, - )); - // This is an overestimate of the used weight, but it's better to be safe than - // sorry. - return Err(::WeightInfo::pre_code_upgrade()) - } + let additional_deposit = new_deposit.saturating_sub(current_deposit); + if let Err(_) = + ::Currency::reserve(&billing_account, additional_deposit) + { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToReserveDeposit, + )); + // This is an overestimate of the used weight, but it's better to be safe than + // sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } - // Update the deposit to the new appropriate amount. - info.deposit = new_deposit; + // Update the deposit to the new appropriate amount. + info.deposit = new_deposit; + }, + _ => { + // No upgrade costs are required. + }, } if current_deposit > new_deposit { @@ -1960,7 +1967,7 @@ mod benchmarking { // Actually finish registration process next_scheduled_session::(); }: { - let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_small_code, false); + let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_small_code, UpgradeRequirements::EnforceRequirements); } on_code_upgrade { diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index ca5306e4dccc..93ae9fa2faef 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -22,7 +22,7 @@ use crate::{ configuration::{self, HostConfiguration}, disputes, dmp, hrmp, - paras::{self, PreCodeUpgrade, SetGoAhead}, + paras::{self, PreCodeUpgrade, SetGoAhead, UpgradeRequirements}, scheduler::{self, AvailabilityTimeoutStatus}, shared::{self, AllowedRelayParentsTracker}, }; @@ -885,7 +885,7 @@ impl Pallet { match ::PreCodeUpgrade::pre_code_upgrade( receipt.descriptor.para_id, new_code.clone(), - false, + UpgradeRequirements::EnforceRequirements, ) { Ok(consumed_weight) => { weight.saturating_accrue(consumed_weight); diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 554a7468c9c1..9c75cf6be544 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -415,6 +415,12 @@ enum PvfCheckOutcome { Rejected, } +#[derive(Debug, Copy, Clone, PartialEq, TypeInfo, Decode, Encode)] +pub enum UpgradeRequirements { + SkipRequirements, + EnforceRequirements, +} + /// This struct describes the current state of an in-progress PVF pre-checking vote. #[derive(Encode, Decode, TypeInfo)] pub(crate) struct PvfCheckActiveVoteState { @@ -528,15 +534,14 @@ pub trait PreCodeUpgrade { /// This is currently utilized by the registrar pallet to ensure that the necessary validation /// code upgrade costs are covered. /// - /// `skip_requirements` signals that the pre code upgrade requirements by this function can be - /// ignored. + /// `requirements` signals whether to enforce the pre code upgrade requirements. /// /// As a result, it indicates either the success or failure of executing the pre code upgrade /// scheduling logic. In both cases, it returns the consumed weight. fn pre_code_upgrade( id: ParaId, new_code: ValidationCode, - skip_requirements: bool, + requirements: UpgradeRequirements, ) -> Result; } @@ -546,7 +551,7 @@ impl PreCodeUpgrade for () { fn pre_code_upgrade( _id: ParaId, _new_code: ValidationCode, - _skip_requirements: bool, + _requirements: UpgradeRequirements, ) -> Result { Ok(Weight::zero()) } From 3373b18f329ede0bfd5cf049bf12990790518e3b Mon Sep 17 00:00:00 2001 From: Szegoo Date: Mon, 11 Dec 2023 15:24:43 +0100 Subject: [PATCH 066/101] use IsSystem --- Cargo.lock | 1 + polkadot/runtime/common/Cargo.toml | 1 + polkadot/runtime/common/src/paras_registrar/mod.rs | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 2e0de32bc6e0..15861c54d7ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12917,6 +12917,7 @@ dependencies = [ "pallet-vesting", "pallet-xcm-benchmarks", "parity-scale-codec", + "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-primitives-test-helpers", "polkadot-runtime-parachains", diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index c6f88792d617..b00a58a3dc56 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -42,6 +42,7 @@ pallet-transaction-payment = { path = "../../../substrate/frame/transaction-paym pallet-treasury = { path = "../../../substrate/frame/treasury", default-features = false } pallet-asset-rate = { path = "../../../substrate/frame/asset-rate", default-features = false, optional = true } pallet-election-provider-multi-phase = { path = "../../../substrate/frame/election-provider-multi-phase", default-features = false } +polkadot-parachain-primitives = { path = "../../parachain", default-features = false } frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support", default-features = false } frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 3e860f466e16..6c255ff4d507 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -26,6 +26,7 @@ use frame_support::{ traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, }; use frame_system::{self, ensure_root, ensure_signed}; +use polkadot_parachain_primitives::primitives::IsSystem; use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; use runtime_parachains::{ configuration, ensure_parachain, @@ -946,7 +947,7 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; let free_upgrade = requirements == UpgradeRequirements::SkipRequirements || - para < LOWEST_PUBLIC_ID || + para.is_system() || lease_holding; match (free_upgrade, info.billing_account.clone()) { From d6f861adc38f7818636aac77565808b03961e36a Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 14 Dec 2023 07:26:28 +0100 Subject: [PATCH 067/101] improve readability --- .../common/src/paras_registrar/migration.rs | 2 +- .../runtime/common/src/paras_registrar/mod.rs | 84 +++++++++---------- 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index ad3477aed05a..784ad937caa3 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -35,7 +35,7 @@ impl> OnRuntimeUpgrade Paras::::translate::>, _>(|_key, v1| { count.saturating_inc(); Some(ParaInfo { - manager: v1.manager.clone(), + manager: v1.manager, deposit: v1.deposit, locked: v1.locked, billing_account: None, diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 6c255ff4d507..a977ae1aff9f 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -481,7 +481,7 @@ pub mod pallet { ) -> DispatchResult { Self::ensure_root_para_or_owner(origin.clone(), para)?; - let requierments = if ensure_root(origin.clone()).is_ok() { + let requirements = if ensure_root(origin.clone()).is_ok() { // Upgrades initiated by the root origin do not have any upgrade cost-related // requirements. For this reason we can skip all the pre code upgrade checks. UpgradeRequirements::SkipRequirements @@ -489,7 +489,7 @@ pub mod pallet { UpgradeRequirements::EnforceRequirements }; - Self::do_schedule_code_upgrade(para, new_code, requierments) + Self::do_schedule_code_upgrade(para, new_code, requirements) } /// Set the parachain's current head. @@ -803,7 +803,7 @@ impl Pallet { /// Schedules a code upgrade for a parachain. /// /// If `requirements` is set to `UpgradeRequirements::SkipRequirements` all the code upgrade - /// cost related requierments will be ignored. + /// cost related requirements will be ignored. /// /// If the size of the validation is reduced and the upgrade is successful the caller will be /// eligible for receiving back a portion of their deposit that is no longer required. @@ -929,15 +929,6 @@ impl PreCodeUpgrade for Pallet { }; let Some(mut info) = Paras::::get(para) else { return Err(T::DbWeight::get().reads(2)) }; - let lease_holding = Self::is_parachain(para); - - // The billing account doesn't need to be set for lease holding parachains. - if info.billing_account.is_none() && !lease_holding { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::BillingAccountNotSet, - )); - return Err(T::DbWeight::get().reads(3)) - } let per_byte_fee = T::DataDepositPerByte::get(); let new_deposit = T::ParaDeposit::get() @@ -946,44 +937,49 @@ impl PreCodeUpgrade for Pallet { let current_deposit = info.deposit; + let lease_holding = Self::is_parachain(para); let free_upgrade = requirements == UpgradeRequirements::SkipRequirements || para.is_system() || lease_holding; - match (free_upgrade, info.billing_account.clone()) { - (false, Some(billing_account)) => { - if let Err(_) = ::Currency::withdraw( - &billing_account, - T::UpgradeFee::get(), - WithdrawReasons::FEE, - ExistenceRequirement::KeepAlive, - ) { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToPayUpgradeFee, - )); - // This is an overestimate of the used weight, but it's better to be safe than - // sorry. - return Err(::WeightInfo::pre_code_upgrade()) - } + if !free_upgrade { + if info.billing_account.is_none() { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::BillingAccountNotSet, + )); + // An overestimate of the used weight, but it's better to be safe than sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } - let additional_deposit = new_deposit.saturating_sub(current_deposit); - if let Err(_) = - ::Currency::reserve(&billing_account, additional_deposit) - { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToReserveDeposit, - )); - // This is an overestimate of the used weight, but it's better to be safe than - // sorry. - return Err(::WeightInfo::pre_code_upgrade()) - } + let billing_account = info + .billing_account + .clone() + .expect("Ensured above that the billing account is set; qed"); + + if let Err(_) = ::Currency::withdraw( + &billing_account, + T::UpgradeFee::get(), + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + ) { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToPayUpgradeFee, + )); + // An overestimate of the used weight, but it's better to be safe than sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } - // Update the deposit to the new appropriate amount. - info.deposit = new_deposit; - }, - _ => { - // No upgrade costs are required. - }, + let additional_deposit = new_deposit.saturating_sub(current_deposit); + if let Err(_) = ::Currency::reserve(&billing_account, additional_deposit) { + Self::deposit_event(Event::::CodeUpgradeScheduleFailed( + CodeUpgradeScheduleError::FailedToReserveDeposit, + )); + // An overestimate of the used weight, but it's better to be safe than sorry. + return Err(::WeightInfo::pre_code_upgrade()) + } + + // Update the deposit to the new appropriate amount. + info.deposit = new_deposit; } if current_deposit > new_deposit { From 34ee5ce343af3bef09c94342ad32113eda41185d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 14 Dec 2023 07:56:13 +0100 Subject: [PATCH 068/101] less nested --- .../runtime/common/src/integration_tests.rs | 4 +- .../runtime/parachains/src/inclusion/mod.rs | 66 +++++++++++-------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 62e7cb0c6fc9..bab267758c45 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -821,8 +821,8 @@ fn root_upgrading_parachain_works() { ); // CASE 2: Root schedules a para upgrade to set the validation code to a new one which has - // half the size of the initially registered code. The para manager should get a refund for - // the deposit they are no longer required to hold. + // half the size of the initially registered code. The billing acount should get a refund + // for the deposit they are no longer required to hold. let code_2 = validation_code(code_size / 2); assert_ok!(Registrar::schedule_code_upgrade( diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index 93ae9fa2faef..af187d5224a2 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -879,34 +879,11 @@ impl Pallet { // initial weight is config read. let mut weight = T::DbWeight::get().reads_writes(1, 0); if let Some(new_code) = commitments.new_validation_code { - // Block number of candidate's inclusion. - let now = >::block_number(); - - match ::PreCodeUpgrade::pre_code_upgrade( + weight.saturating_accrue(Self::try_schedule_code_upgrade( + &config, receipt.descriptor.para_id, - new_code.clone(), - UpgradeRequirements::EnforceRequirements, - ) { - Ok(consumed_weight) => { - weight.saturating_accrue(consumed_weight); - - weight.saturating_accrue(>::schedule_code_upgrade( - receipt.descriptor.para_id, - new_code, - now, - &config, - SetGoAhead::Yes, - )); - }, - Err(consumed_weight) => { - log::debug!( - target: LOG_TARGET, - "Failed to schedule a code upgrade for parachain {}", - u32::from(receipt.descriptor.para_id), - ); - weight.saturating_accrue(consumed_weight); - }, - }; + new_code, + )); } // enact the messaging facet of the candidate. @@ -1142,6 +1119,41 @@ impl Pallet { ) -> Option>> { >::get(¶) } + + /// Attempts to schedule a code upgrade. + /// + /// Returns the consumed weight. + fn try_schedule_code_upgrade( + config: &HostConfiguration>, + para_id: ParaId, + new_code: primitives::ValidationCode, + ) -> Weight { + // Block number of candidate's inclusion. + let now = >::block_number(); + + match ::PreCodeUpgrade::pre_code_upgrade( + para_id, + new_code.clone(), + UpgradeRequirements::EnforceRequirements, + ) { + Ok(consumed_weight) => + consumed_weight.saturating_add(>::schedule_code_upgrade( + para_id, + new_code, + now, + &config, + SetGoAhead::Yes, + )), + Err(consumed_weight) => { + log::debug!( + target: LOG_TARGET, + "Failed to schedule a code upgrade for parachain {}", + u32::from(para_id), + ); + consumed_weight + }, + } + } } const fn availability_threshold(n_validators: usize) -> usize { From 090bccbc4a5d942bb5194085c4878ab4621719f9 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Thu, 14 Dec 2023 17:36:00 +0100 Subject: [PATCH 069/101] small fix --- polkadot/runtime/common/src/paras_registrar/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index a977ae1aff9f..7e34c3348192 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -943,18 +943,13 @@ impl PreCodeUpgrade for Pallet { lease_holding; if !free_upgrade { - if info.billing_account.is_none() { + let Some(billing_account) = info.billing_account.clone() else { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( CodeUpgradeScheduleError::BillingAccountNotSet, )); // An overestimate of the used weight, but it's better to be safe than sorry. return Err(::WeightInfo::pre_code_upgrade()) - } - - let billing_account = info - .billing_account - .clone() - .expect("Ensured above that the billing account is set; qed"); + }; if let Err(_) = ::Currency::withdraw( &billing_account, From 61d9b267c34c8e8538706a6f5f218f724955c83a Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 22 Dec 2023 06:43:54 +0100 Subject: [PATCH 070/101] required_para_deposit helper function --- .../runtime/common/src/paras_registrar/mod.rs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 7e34c3348192..f08446f7e70e 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -841,10 +841,7 @@ impl Pallet { Error::::HeadDataTooLarge ); - let per_byte_fee = T::DataDepositPerByte::get(); - let deposit = T::ParaDeposit::get() - .saturating_add(per_byte_fee.saturating_mul((genesis_head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((validation_code.0.len() as u32).into())); + let deposit = Self::required_para_deposit(genesis_head.0.len(), validation_code.0.len()); Ok((ParaGenesisArgs { genesis_head, validation_code, para_kind }, deposit)) } @@ -889,6 +886,15 @@ impl Pallet { }); Ok(()) } + + /// Determines the required deposit amount for the parachain, given the specified genesis head + /// and validation code size. + fn required_para_deposit(head_size: usize, validation_code_size: usize) -> BalanceOf { + let per_byte_fee = T::DataDepositPerByte::get(); + T::ParaDeposit::get() + .saturating_add(per_byte_fee.saturating_mul((head_size as u32).into())) + .saturating_add(per_byte_fee.saturating_mul((validation_code_size as u32).into())) + } } impl OnNewHead for Pallet { @@ -930,11 +936,7 @@ impl PreCodeUpgrade for Pallet { let Some(mut info) = Paras::::get(para) else { return Err(T::DbWeight::get().reads(2)) }; - let per_byte_fee = T::DataDepositPerByte::get(); - let new_deposit = T::ParaDeposit::get() - .saturating_add(per_byte_fee.saturating_mul((head.0.len() as u32).into())) - .saturating_add(per_byte_fee.saturating_mul((new_code.0.len() as u32).into())); - + let new_deposit = Self::required_para_deposit(head.0.len(), new_code.0.len()); let current_deposit = info.deposit; let lease_holding = Self::is_parachain(para); From 88c9ef3368a63088e947c44e96ad7fad7c7e946f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 22 Dec 2023 07:04:31 +0100 Subject: [PATCH 071/101] add pr doc --- .../runtime/common/src/paras_registrar/mod.rs | 2 +- prdoc/pr_2372.prdoc | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_2372.prdoc diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index f08446f7e70e..83074038a3d8 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -887,7 +887,7 @@ impl Pallet { Ok(()) } - /// Determines the required deposit amount for the parachain, given the specified genesis head + /// Returns the required deposit amount for the parachain, given the specified genesis head /// and validation code size. fn required_para_deposit(head_size: usize, validation_code_size: usize) -> BalanceOf { let per_byte_fee = T::DataDepositPerByte::get(); diff --git a/prdoc/pr_2372.prdoc b/prdoc/pr_2372.prdoc new file mode 100644 index 000000000000..23860ddd88a9 --- /dev/null +++ b/prdoc/pr_2372.prdoc @@ -0,0 +1,23 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: On-demand & Coretime parachain upgrade fees + +doc: + - audience: Runtime User + description: | + All the parachains utilizing on-demand or the coretime model will be charged for performing + validation code upgrades. Before scheduling a code upgrade these parachains will have to + explicitly specify the account that will be charged for performing the code upgrade. + Existing lease holding and system chains are not affected by this change and are allowed + to continue performing code upgrades without associated upgrade costs. +migrations: + db: [] + runtime: + - reference: polkadot-runtime-parachains + description: | + ParaInfo has been updated to include two new fields: billing_account and pending_deposit_refund. + Both fields are initialized to None during the runtime migration. + +crates: + - name: polkadot-runtime-parachains \ No newline at end of file From 61192221900af5a3d0f62e7c80d924f0b960c176 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 22 Dec 2023 07:06:10 +0100 Subject: [PATCH 072/101] missing line --- prdoc/pr_2372.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_2372.prdoc b/prdoc/pr_2372.prdoc index 23860ddd88a9..619dd338501e 100644 --- a/prdoc/pr_2372.prdoc +++ b/prdoc/pr_2372.prdoc @@ -20,4 +20,4 @@ migrations: Both fields are initialized to None during the runtime migration. crates: - - name: polkadot-runtime-parachains \ No newline at end of file + - name: polkadot-runtime-parachains From 37b6617e5812109d6bf09e833d806d1646193f81 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 22 Dec 2023 07:36:32 +0100 Subject: [PATCH 073/101] fmt --- polkadot/runtime/parachains/src/paras/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 5883a7c2e4f2..9de7e482de45 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -554,7 +554,7 @@ impl PreCodeUpgrade for () { _requirements: UpgradeRequirements, ) -> Result { Ok(Weight::zero()) - } + } } /// Assign coretime to some parachain. From 40f303a679ce5b4b61e5682c2cb5216cbdcb3a23 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 07:55:15 +0100 Subject: [PATCH 074/101] remove unnecessary generic --- .../runtime/common/src/paras_registrar/migration.rs | 12 ++++-------- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 784ad937caa3..2b4b79e666a8 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -24,12 +24,8 @@ pub struct ParaInfoV1 { locked: Option, } -pub struct VersionUncheckedMigrateToV2( - sp_std::marker::PhantomData<(T, UnlockParaIds)>, -); -impl> OnRuntimeUpgrade - for VersionUncheckedMigrateToV2 -{ +pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData<(T)>); +impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { fn on_runtime_upgrade() -> Weight { let mut count = 0u64; Paras::::translate::>, _>(|_key, v1| { @@ -69,10 +65,10 @@ impl> OnRuntimeUpgrade } } -pub type MigrateToV2 = frame_support::migrations::VersionedMigration< +pub type MigrateToV2 = frame_support::migrations::VersionedMigration< 1, 2, - VersionUncheckedMigrateToV2, + VersionUncheckedMigrateToV2, super::Pallet, ::DbWeight, >; diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index fb54cdd3dc29..5bb1ca415a88 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1659,7 +1659,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV2, + paras_registrar::migration::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index ae281ec65cd1..9346938c3e7c 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1654,7 +1654,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV2, + paras_registrar::migration::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From 7015621a42cd1d2ce2d44d04c818f0762a2caa92 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 08:02:27 +0100 Subject: [PATCH 075/101] fix --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index a0ba019c5148..d94e664a97e8 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -152,7 +152,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] #[pallet::without_storage_info] From 0da3ee47ce0a74bcb666716cc715fbe4b471e8a3 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 08:05:04 +0100 Subject: [PATCH 076/101] make clippy happy --- polkadot/runtime/common/src/paras_registrar/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 2b4b79e666a8..fdb1454de227 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -24,7 +24,7 @@ pub struct ParaInfoV1 { locked: Option, } -pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData<(T)>); +pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData); impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { fn on_runtime_upgrade() -> Weight { let mut count = 0u64; From ba9f44891e8e2126fd01eb569ecfaa4148241389 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 08:14:23 +0100 Subject: [PATCH 077/101] final fixes --- polkadot/runtime/common/Cargo.toml | 1 + polkadot/runtime/common/src/paras_registrar/migration.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 7714255dad9a..7174533c4afb 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -139,6 +139,7 @@ runtime-benchmarks = [ "pallet-treasury/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", "primitives/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index fdb1454de227..b9ecb6140575 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -15,7 +15,7 @@ // along with Polkadot. If not, see . use super::*; -use frame_support::traits::{Contains, OnRuntimeUpgrade}; +use frame_support::traits::OnRuntimeUpgrade; #[derive(Encode, Decode)] pub struct ParaInfoV1 { From 3319410b36e551297eb1d8d263ef2e65012bda70 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 08:17:40 +0100 Subject: [PATCH 078/101] .. --- polkadot/runtime/common/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index 7174533c4afb..8b55ca39d1f3 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -102,6 +102,7 @@ std = [ "pallet-vesting/std", "pallet-xcm-benchmarks/std", "parity-scale-codec/std", + "polkadot-parachain-primitives/std", "primitives/std", "runtime-parachains/std", "rustc-hex/std", From 55d71292e1623f31a9bde8c3030d9d5a2747c256 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 08:40:30 +0100 Subject: [PATCH 079/101] fix benchmark --- .../runtime/common/src/paras_registrar/mod.rs | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index d94e664a97e8..965231fca0dc 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1991,16 +1991,10 @@ mod benchmarking { // Actually finish registration process next_scheduled_session::(); - let location: MultiLocation = (Parent, Parachain(LOWEST_PUBLIC_ID.into())).into(); - let sovereign_account = - ::SovereignAccountOf::convert_location(&location) - .unwrap(); - T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); - - let para_origin: runtime_parachains::Origin = u32::from(LOWEST_PUBLIC_ID).into(); - }: _(para_origin, para) + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller.clone()), para) verify { - assert_eq!(Paras::::get(para).unwrap().billing_account, Some(sovereign_account)); + assert_eq!(Paras::::get(para).unwrap().billing_account, Some(caller)); } force_set_parachain_billing_account { @@ -2008,14 +2002,10 @@ mod benchmarking { // Actually finish registration process next_scheduled_session::(); - let location: MultiLocation = (Parent, Parachain(LOWEST_PUBLIC_ID.into())).into(); - let sovereign_account = - ::SovereignAccountOf::convert_location(&location) - .unwrap(); - T::Currency::make_free_balance_be(&sovereign_account, BalanceOf::::max_value()); - }: _(RawOrigin::Root, para, sovereign_account.clone()) + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Root, para, caller.clone()) verify { - assert_eq!(Paras::::get(para).unwrap().billing_account, Some(sovereign_account)); + assert_eq!(Paras::::get(para).unwrap().billing_account, Some(caller)); } impl_benchmark_test_suite!( From 490f8b97144722a90d6641008908abaecd7c6f8c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 09:55:50 +0100 Subject: [PATCH 080/101] fix zombienet tests --- .../zombienet/tests/0004-configure-billing.js | 42 +++++++++++++++++++ .../smoke/0002-configure-billing.js | 42 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 cumulus/zombienet/tests/0004-configure-billing.js create mode 100644 polkadot/zombienet_tests/smoke/0002-configure-billing.js diff --git a/cumulus/zombienet/tests/0004-configure-billing.js b/cumulus/zombienet/tests/0004-configure-billing.js new file mode 100644 index 000000000000..f3c671d33861 --- /dev/null +++ b/cumulus/zombienet/tests/0004-configure-billing.js @@ -0,0 +1,42 @@ +const assert = require("assert"); + +async function run(nodeName, networkInfo, _jsArgs) { + const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + await zombie.util.cryptoWaitReady(); + + // account to submit tx + const keyring = new zombie.Keyring({ type: "sr25519" }); + const charlie = keyring.addFromUri("//Charlie"); + + const paraId = 2000; + const setBillingAccountCall = + api.tx.registrar.setParachainBillingAccountToSelf(paraId); + const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); + + await new Promise(async (resolve, reject) => { + const unsub = await sudoCall.signAndSend(charlie, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + return resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + unsub(); + return reject(); + } + }); + }); + + return 0; +} + +module.exports = { run }; diff --git a/polkadot/zombienet_tests/smoke/0002-configure-billing.js b/polkadot/zombienet_tests/smoke/0002-configure-billing.js new file mode 100644 index 000000000000..5cb38277898c --- /dev/null +++ b/polkadot/zombienet_tests/smoke/0002-configure-billing.js @@ -0,0 +1,42 @@ +const assert = require("assert"); + +async function run(nodeName, networkInfo, _jsArgs) { + const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + await zombie.util.cryptoWaitReady(); + + // account to submit tx + const keyring = new zombie.Keyring({ type: "sr25519" }); + const alice = keyring.addFromUri("//Alice"); + + const paraId = 100; + const setBillingAccountCall = + api.tx.registrar.setParachainBillingAccountToSelf(paraId); + const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); + + await new Promise(async (resolve, reject) => { + const unsub = await sudoCall.signAndSend(alice, (result) => { + console.log(`Current status is ${result.status}`); + if (result.status.isInBlock) { + console.log( + `Transaction included at blockHash ${result.status.asInBlock}` + ); + } else if (result.status.isFinalized) { + console.log( + `Transaction finalized at blockHash ${result.status.asFinalized}` + ); + unsub(); + return resolve(); + } else if (result.isError) { + console.log(`Transaction Error`); + unsub(); + return reject(); + } + }); + }); + + return 0; +} + +module.exports = { run }; From da036600285244b69e053095771670ceede70fb7 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 10:10:33 +0100 Subject: [PATCH 081/101] .. fix --- cumulus/zombienet/tests/0004-configure-billing.js | 2 +- polkadot/zombienet_tests/smoke/0002-configure-billing.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cumulus/zombienet/tests/0004-configure-billing.js b/cumulus/zombienet/tests/0004-configure-billing.js index f3c671d33861..3548525a743c 100644 --- a/cumulus/zombienet/tests/0004-configure-billing.js +++ b/cumulus/zombienet/tests/0004-configure-billing.js @@ -12,7 +12,7 @@ async function run(nodeName, networkInfo, _jsArgs) { const paraId = 2000; const setBillingAccountCall = - api.tx.registrar.setParachainBillingAccountToSelf(paraId); + api.tx.registrar.forceSetParachainBillingAccount(paraId, charlie.address); const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); await new Promise(async (resolve, reject) => { diff --git a/polkadot/zombienet_tests/smoke/0002-configure-billing.js b/polkadot/zombienet_tests/smoke/0002-configure-billing.js index 5cb38277898c..8cb21339c9d7 100644 --- a/polkadot/zombienet_tests/smoke/0002-configure-billing.js +++ b/polkadot/zombienet_tests/smoke/0002-configure-billing.js @@ -12,7 +12,7 @@ async function run(nodeName, networkInfo, _jsArgs) { const paraId = 100; const setBillingAccountCall = - api.tx.registrar.setParachainBillingAccountToSelf(paraId); + api.tx.registrar.forceSetParachainBillingAccount(paraId, alice.address); const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); await new Promise(async (resolve, reject) => { From 12cfb4438644d902532d952749a0ae0a6adcd30d Mon Sep 17 00:00:00 2001 From: Szegoo Date: Sun, 24 Dec 2023 12:31:18 +0100 Subject: [PATCH 082/101] fix --- cumulus/zombienet/tests/0004-configure-billing.js | 8 ++++---- cumulus/zombienet/tests/0004-runtime_upgrade.zndsl | 4 ++++ polkadot/zombienet_tests/smoke/0002-configure-billing.js | 2 +- .../smoke/0002-parachains-upgrade-smoke-test.zndsl | 4 ++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cumulus/zombienet/tests/0004-configure-billing.js b/cumulus/zombienet/tests/0004-configure-billing.js index 3548525a743c..e02b09bddc2d 100644 --- a/cumulus/zombienet/tests/0004-configure-billing.js +++ b/cumulus/zombienet/tests/0004-configure-billing.js @@ -6,17 +6,17 @@ async function run(nodeName, networkInfo, _jsArgs) { await zombie.util.cryptoWaitReady(); - // account to submit tx + // The billing account: const keyring = new zombie.Keyring({ type: "sr25519" }); - const charlie = keyring.addFromUri("//Charlie"); + const alice = keyring.addFromUri("//Alice"); const paraId = 2000; const setBillingAccountCall = - api.tx.registrar.forceSetParachainBillingAccount(paraId, charlie.address); + api.tx.registrar.forceSetParachainBillingAccount(paraId, alice.address); const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); await new Promise(async (resolve, reject) => { - const unsub = await sudoCall.signAndSend(charlie, (result) => { + const unsub = await sudoCall.signAndSend(alice, (result) => { console.log(`Current status is ${result.status}`); if (result.status.isInBlock) { console.log( diff --git a/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl b/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl index cdafc48fce9e..7e4c824b29d4 100644 --- a/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl +++ b/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl @@ -4,6 +4,10 @@ Creds: config alice: parachain 2000 is registered within 225 seconds charlie: reports block height is at least 5 within 250 seconds + +# set billing account +alice: js-script ./0004-configure-billing.js with "" return is 0 within 200 secs + charlie: parachain 2000 perform upgrade with /tmp/wasm_binary_spec_version_incremented.rs.compact.compressed.wasm within 200 seconds dave: reports block height is at least 20 within 250 seconds dave: js-script ./runtime_upgrade.js within 200 seconds diff --git a/polkadot/zombienet_tests/smoke/0002-configure-billing.js b/polkadot/zombienet_tests/smoke/0002-configure-billing.js index 8cb21339c9d7..533cb4d3c2a3 100644 --- a/polkadot/zombienet_tests/smoke/0002-configure-billing.js +++ b/polkadot/zombienet_tests/smoke/0002-configure-billing.js @@ -6,7 +6,7 @@ async function run(nodeName, networkInfo, _jsArgs) { await zombie.util.cryptoWaitReady(); - // account to submit tx + // The billing account: const keyring = new zombie.Keyring({ type: "sr25519" }); const alice = keyring.addFromUri("//Alice"); diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl index bcea5aa1646e..d23bc874d138 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl @@ -4,5 +4,9 @@ Creds: config alice: parachain 100 is registered within 225 seconds alice: parachain 100 block height is at least 10 within 460 seconds + +# set billing account +alice: js-script ./0002-configure-billing.js with "" return is 0 within 200 secs + alice: parachain 100 perform dummy upgrade within 200 seconds alice: parachain 100 block height is at least 14 within 200 seconds From eb79964466548c86fc057402441a5da7b46f80b4 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 27 Dec 2023 13:43:41 +0100 Subject: [PATCH 083/101] use versi --- cumulus/zombienet/tests/0004-runtime_upgrade.toml | 2 +- .../smoke/0002-parachains-upgrade-smoke-test.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cumulus/zombienet/tests/0004-runtime_upgrade.toml b/cumulus/zombienet/tests/0004-runtime_upgrade.toml index fbf0dfc69163..a371ca8e0292 100644 --- a/cumulus/zombienet/tests/0004-runtime_upgrade.toml +++ b/cumulus/zombienet/tests/0004-runtime_upgrade.toml @@ -3,7 +3,7 @@ default_image = "{{RELAY_IMAGE}}" default_command = "polkadot" default_args = [ "-lparachain=debug" ] -chain = "rococo-local" +chain = "versi-local" [[relaychain.nodes]] name = "alice" diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml index d72e3ebdb335..f752af5b1f2f 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml @@ -4,8 +4,8 @@ bootnode = true [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" -chain = "rococo-local" -command = "polkadot" +chain = "versi-local" +command = "/Users/sergejsakac/polkadot-sdk/target/testnet/polkadot" [[relaychain.nodes]] name = "alice" @@ -31,7 +31,7 @@ cumulus_based = true [parachains.collator] name = "collator01" image = "{{CUMULUS_IMAGE}}" - command = "polkadot-parachain" + command = "/Users/sergejsakac/polkadot-sdk/target/testnet/polkadot-parachain" [[parachains.collator.env]] name = "RUST_LOG" From c79c4b07210b95c28d9146f8df94e3ad86d21dd3 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 27 Dec 2023 14:00:20 +0100 Subject: [PATCH 084/101] remove upgrade fees from westend --- .../zombienet/tests/0004-configure-billing.js | 42 -------------- .../zombienet/tests/0004-runtime_upgrade.toml | 2 +- .../tests/0004-runtime_upgrade.zndsl | 3 - polkadot/runtime/westend/src/lib.rs | 11 +--- .../weights/runtime_common_paras_registrar.rs | 56 ------------------- .../smoke/0002-configure-billing.js | 42 -------------- .../0002-parachains-upgrade-smoke-test.toml | 2 +- .../0002-parachains-upgrade-smoke-test.zndsl | 3 - 8 files changed, 4 insertions(+), 157 deletions(-) delete mode 100644 cumulus/zombienet/tests/0004-configure-billing.js delete mode 100644 polkadot/zombienet_tests/smoke/0002-configure-billing.js diff --git a/cumulus/zombienet/tests/0004-configure-billing.js b/cumulus/zombienet/tests/0004-configure-billing.js deleted file mode 100644 index e02b09bddc2d..000000000000 --- a/cumulus/zombienet/tests/0004-configure-billing.js +++ /dev/null @@ -1,42 +0,0 @@ -const assert = require("assert"); - -async function run(nodeName, networkInfo, _jsArgs) { - const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - await zombie.util.cryptoWaitReady(); - - // The billing account: - const keyring = new zombie.Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - const paraId = 2000; - const setBillingAccountCall = - api.tx.registrar.forceSetParachainBillingAccount(paraId, alice.address); - const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); - - await new Promise(async (resolve, reject) => { - const unsub = await sudoCall.signAndSend(alice, (result) => { - console.log(`Current status is ${result.status}`); - if (result.status.isInBlock) { - console.log( - `Transaction included at blockHash ${result.status.asInBlock}` - ); - } else if (result.status.isFinalized) { - console.log( - `Transaction finalized at blockHash ${result.status.asFinalized}` - ); - unsub(); - return resolve(); - } else if (result.isError) { - console.log(`Transaction Error`); - unsub(); - return reject(); - } - }); - }); - - return 0; -} - -module.exports = { run }; diff --git a/cumulus/zombienet/tests/0004-runtime_upgrade.toml b/cumulus/zombienet/tests/0004-runtime_upgrade.toml index a371ca8e0292..ca7d557c125c 100644 --- a/cumulus/zombienet/tests/0004-runtime_upgrade.toml +++ b/cumulus/zombienet/tests/0004-runtime_upgrade.toml @@ -3,7 +3,7 @@ default_image = "{{RELAY_IMAGE}}" default_command = "polkadot" default_args = [ "-lparachain=debug" ] -chain = "versi-local" +chain = "westend-local" [[relaychain.nodes]] name = "alice" diff --git a/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl b/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl index 7e4c824b29d4..cf5b637f3f10 100644 --- a/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl +++ b/cumulus/zombienet/tests/0004-runtime_upgrade.zndsl @@ -5,9 +5,6 @@ Creds: config alice: parachain 2000 is registered within 225 seconds charlie: reports block height is at least 5 within 250 seconds -# set billing account -alice: js-script ./0004-configure-billing.js with "" return is 0 within 200 secs - charlie: parachain 2000 perform upgrade with /tmp/wasm_binary_spec_version_incremented.rs.compact.compressed.wasm within 200 seconds dave: reports block height is at least 20 within 250 seconds dave: js-script ./runtime_upgrade.js within 200 seconds diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 9346938c3e7c..fb54bec509b3 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -121,8 +121,6 @@ mod bag_thresholds; mod weights; pub mod xcm_config; -use xcm_config::LocationConverter; - // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -1150,8 +1148,6 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; - type PreCodeUpgrade = Registrar; - type OnCodeUpgrade = Registrar; type OnNewHead = (); type AssignCoretime = (); } @@ -1282,7 +1278,6 @@ impl parachains_slashing::Config for Runtime { parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; - pub const UpgradeFee: Balance = 50 * CENTS; pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); } @@ -1293,8 +1288,6 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; - type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationConverter; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } @@ -1654,7 +1647,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV2, + paras_registrar::migration::MigrateToV1, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, @@ -2353,7 +2346,7 @@ sp_api::impl_runtime_apis! { impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; - type AccountIdConverter = LocationConverter; + type AccountIdConverter = xcm_config::LocationConverter; type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< xcm_config::XcmConfig, ExistentialDepositMultiAsset, diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 9ee249f587c5..50290c0fe59f 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -215,60 +215,4 @@ impl runtime_common::paras_registrar::WeightInfo for We .saturating_add(Weight::from_parts(1_029, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 1048576]`. - fn pre_code_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_804_000 picoseconds. - Weight::from_parts(8_956_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_029, 0)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 1048576]`. - fn on_code_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_804_000 picoseconds. - Weight::from_parts(8_956_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_029, 0)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 1048576]`. - fn set_parachain_billing_account_to_self() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_804_000 picoseconds. - Weight::from_parts(8_956_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_029, 0)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: Paras Heads (r:0 w:1) - /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) - /// The range of component `b` is `[1, 1048576]`. - fn force_set_parachain_billing_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 8_804_000 picoseconds. - Weight::from_parts(8_956_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_029, 0)) - .saturating_add(T::DbWeight::get().writes(1)) - } } diff --git a/polkadot/zombienet_tests/smoke/0002-configure-billing.js b/polkadot/zombienet_tests/smoke/0002-configure-billing.js deleted file mode 100644 index 533cb4d3c2a3..000000000000 --- a/polkadot/zombienet_tests/smoke/0002-configure-billing.js +++ /dev/null @@ -1,42 +0,0 @@ -const assert = require("assert"); - -async function run(nodeName, networkInfo, _jsArgs) { - const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - await zombie.util.cryptoWaitReady(); - - // The billing account: - const keyring = new zombie.Keyring({ type: "sr25519" }); - const alice = keyring.addFromUri("//Alice"); - - const paraId = 100; - const setBillingAccountCall = - api.tx.registrar.forceSetParachainBillingAccount(paraId, alice.address); - const sudoCall = api.tx.sudo.sudo(setBillingAccountCall); - - await new Promise(async (resolve, reject) => { - const unsub = await sudoCall.signAndSend(alice, (result) => { - console.log(`Current status is ${result.status}`); - if (result.status.isInBlock) { - console.log( - `Transaction included at blockHash ${result.status.asInBlock}` - ); - } else if (result.status.isFinalized) { - console.log( - `Transaction finalized at blockHash ${result.status.asFinalized}` - ); - unsub(); - return resolve(); - } else if (result.isError) { - console.log(`Transaction Error`); - unsub(); - return reject(); - } - }); - }); - - return 0; -} - -module.exports = { run }; diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml index f752af5b1f2f..fa25970c1df4 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml @@ -4,7 +4,7 @@ bootnode = true [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" -chain = "versi-local" +chain = "westend-local" command = "/Users/sergejsakac/polkadot-sdk/target/testnet/polkadot" [[relaychain.nodes]] diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl index d23bc874d138..ec1a3e9baeec 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl @@ -5,8 +5,5 @@ Creds: config alice: parachain 100 is registered within 225 seconds alice: parachain 100 block height is at least 10 within 460 seconds -# set billing account -alice: js-script ./0002-configure-billing.js with "" return is 0 within 200 secs - alice: parachain 100 perform dummy upgrade within 200 seconds alice: parachain 100 block height is at least 14 within 200 seconds From 99a95ec21697c4d8c7ca310e02db3dbd187da30f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 27 Dec 2023 14:13:35 +0100 Subject: [PATCH 085/101] fix --- polkadot/runtime/westend/src/lib.rs | 11 +++- .../weights/runtime_common_paras_registrar.rs | 57 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index fb54bec509b3..5f6900a9bdce 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -121,6 +121,8 @@ mod bag_thresholds; mod weights; pub mod xcm_config; +use xcm_config::LocationConverter; + // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -1148,6 +1150,8 @@ impl parachains_paras::Config for Runtime { type UnsignedPriority = ParasUnsignedPriority; type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; + type PreCodeUpgrade = (); + type OnCodeUpgrade = (); type OnNewHead = (); type AssignCoretime = (); } @@ -1278,6 +1282,7 @@ impl parachains_slashing::Config for Runtime { parameter_types! { pub const ParaDeposit: Balance = 2000 * CENTS; + pub const UpgradeFee: Balance = 0; pub const RegistrarDataDepositPerByte: Balance = deposit(0, 1); } @@ -1288,6 +1293,8 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; + type UpgradeFee = UpgradeFee; + type SovereignAccountOf = LocationConverter; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } @@ -1647,7 +1654,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV1, + paras_registrar::migration::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, @@ -2346,7 +2353,7 @@ sp_api::impl_runtime_apis! { impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; - type AccountIdConverter = xcm_config::LocationConverter; + type AccountIdConverter = LocationConverter; type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< xcm_config::XcmConfig, ExistentialDepositMultiAsset, diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 50290c0fe59f..93d6d81c4797 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -215,4 +215,61 @@ impl runtime_common::paras_registrar::WeightInfo for We .saturating_add(Weight::from_parts(1_029, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().writes(1)) } + + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn pre_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn on_code_upgrade() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn set_parachain_billing_account_to_self() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: Paras Heads (r:0 w:1) + /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) + /// The range of component `b` is `[1, 1048576]`. + fn force_set_parachain_billing_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_804_000 picoseconds. + Weight::from_parts(8_956_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_029, 0)) + .saturating_add(T::DbWeight::get().writes(1)) + } } From ea0961f04a2083e8636523ec56b09fafb1c1ebcf Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 27 Dec 2023 15:38:12 +0100 Subject: [PATCH 086/101] =?UTF-8?q?=F0=9F=99=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../smoke/0002-parachains-upgrade-smoke-test.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml index fa25970c1df4..5c44c9217724 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.toml @@ -5,7 +5,7 @@ bootnode = true [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" chain = "westend-local" -command = "/Users/sergejsakac/polkadot-sdk/target/testnet/polkadot" +command = "polkadot" [[relaychain.nodes]] name = "alice" @@ -31,7 +31,7 @@ cumulus_based = true [parachains.collator] name = "collator01" image = "{{CUMULUS_IMAGE}}" - command = "/Users/sergejsakac/polkadot-sdk/target/testnet/polkadot-parachain" + command = "polkadot-parachain" [[parachains.collator.env]] name = "RUST_LOG" From 49b01422e3d1d8d7c6d7d32d0a0dbc3b82832ed2 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Wed, 27 Dec 2023 16:53:32 +0100 Subject: [PATCH 087/101] fix benchmark --- polkadot/runtime/common/src/paras_registrar/mod.rs | 1 - .../smoke/0002-parachains-upgrade-smoke-test.zndsl | 1 - 2 files changed, 2 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 965231fca0dc..a5747d79358c 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1975,7 +1975,6 @@ mod benchmarking { assert_ok!(Registrar::::force_set_parachain_billing_account(RawOrigin::Root.into(), para, whitelisted_caller())); assert_ok!(Registrar::::schedule_code_upgrade(RawOrigin::Root.into(), para, new_small_code)); - assert!(Paras::::get(para).unwrap().pending_deposit_refund.is_some()); }: { let _ = T::OnCodeUpgrade::on_code_upgrade(para); } diff --git a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl index ec1a3e9baeec..bcea5aa1646e 100644 --- a/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl +++ b/polkadot/zombienet_tests/smoke/0002-parachains-upgrade-smoke-test.zndsl @@ -4,6 +4,5 @@ Creds: config alice: parachain 100 is registered within 225 seconds alice: parachain 100 block height is at least 10 within 460 seconds - alice: parachain 100 perform dummy upgrade within 200 seconds alice: parachain 100 block height is at least 14 within 200 seconds From 527e7b3827dcfc23ffc09f8dbec0ad5bfe1ce182 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:09:59 +0100 Subject: [PATCH 088/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index a5747d79358c..5c68cc9bf8a3 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -1004,13 +1004,9 @@ impl PreCodeUpgrade for Pallet { impl OnCodeUpgrade for Pallet { fn on_code_upgrade(id: ParaId) -> Weight { - let maybe_info = Paras::::get(id); - if maybe_info.is_none() { + let Some(mut info) = Paras::::get(id) else { return T::DbWeight::get().reads(1) - } - - let mut info = maybe_info - .expect("Ensured above that the deposit info is stored for the parachain; qed"); + }; if let Some(rebate) = info.pending_deposit_refund { if let Some(billing_account) = info.billing_account.clone() { From 5ad659b81f3cc4a0f171594c1dce97ded56270ee Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:10:37 +0100 Subject: [PATCH 089/101] Update polkadot/runtime/common/src/paras_registrar/migration.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/migration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index b9ecb6140575..5e359ac62bf1 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -55,7 +55,7 @@ impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { ensure!(old_count == new_count, "Paras count should not change"); - Paras::::iter_keys().try_for_each(|para_id| -> Result<(), _> { + Paras::::iter().try_for_each(|(para_id, info)| -> Result<(), _> { let info = Paras::::get(para_id).unwrap(); ensure!(info.billing_account.is_none(), "The billing account must be set to the None"); ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); From 4e842e67839bce4be950bcca398b8b0106697774 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:10:51 +0100 Subject: [PATCH 090/101] Update polkadot/runtime/common/src/paras_registrar/migration.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/migration.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 5e359ac62bf1..9e110af2c16d 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -56,7 +56,6 @@ impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { ensure!(old_count == new_count, "Paras count should not change"); Paras::::iter().try_for_each(|(para_id, info)| -> Result<(), _> { - let info = Paras::::get(para_id).unwrap(); ensure!(info.billing_account.is_none(), "The billing account must be set to the None"); ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); From c16d4a9af2791386960c002d64e429542cb1a83c Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:11:35 +0100 Subject: [PATCH 091/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 5c68cc9bf8a3..2392a0513632 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -864,7 +864,7 @@ impl Pallet { para: ParaId, new_billing_account: T::AccountId, ) -> DispatchResult { - let mut info = Paras::::get(para).map_or(Err(Error::::NotRegistered), Ok)?; + let mut info = Paras::::get(para).ok_or_else(|| Error::::NotRegistered)?; // When updating the account responsible for all code upgrade costs, we unreserve all // funds associated with the registered parachain from the original billing account and From d382ee916682d6954e3f4db307ef91976da19774 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:12:26 +0100 Subject: [PATCH 092/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 2392a0513632..cda9c791f68b 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -878,7 +878,7 @@ impl Pallet { ::Currency::unreserve(¤t_deposit_holder, info.deposit); info.billing_account = Some(new_billing_account.clone()); - Paras::::insert(para, info.clone()); + Paras::::insert(para, info); Self::deposit_event(Event::::BillingAccountSet { para_id: para, From 19ef13149de6c074d0a3378e5d6fe8a75fbfcc6c Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:12:57 +0100 Subject: [PATCH 093/101] Update polkadot/runtime/parachains/src/paras/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/parachains/src/paras/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 9de7e482de45..c36c36e4d9da 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -512,12 +512,10 @@ impl OnNewHead for Tuple { } } -pub trait OnCodeUpgrade { +/// A callback for when a parachain successfully upgraded its code. +pub trait OnCodeUpgraded { /// A function to execute some custom logic once the pre-checking is successfully completed. - /// - /// This is currently used by the registrar pallet to perform refunds upon validation code - /// size reduction. - fn on_code_upgrade(id: ParaId) -> Weight; + fn on_code_upgraded(id: ParaId) -> Weight; } /// An empty implementation of the trait where there is no logic executed upon a successful From 47a4d81629e528c5ca43d7db23ab3aa4702f5d31 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:13:25 +0100 Subject: [PATCH 094/101] Update polkadot/runtime/parachains/src/paras/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/parachains/src/paras/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index c36c36e4d9da..305492677c28 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -529,9 +529,6 @@ impl OnCodeUpgrade for () { pub trait PreCodeUpgrade { /// A function that performs custom logic to before sceduling a code upgrade. /// - /// This is currently utilized by the registrar pallet to ensure that the necessary validation - /// code upgrade costs are covered. - /// /// `requirements` signals whether to enforce the pre code upgrade requirements. /// /// As a result, it indicates either the success or failure of executing the pre code upgrade From 91f8877cd39817e397190b9c32498279c8cb698b Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:13:53 +0100 Subject: [PATCH 095/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index cda9c791f68b..8caf32512a7f 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -953,12 +953,12 @@ impl PreCodeUpgrade for Pallet { return Err(::WeightInfo::pre_code_upgrade()) }; - if let Err(_) = ::Currency::withdraw( + if ::Currency::withdraw( &billing_account, T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, - ) { + ).is_err() { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( CodeUpgradeScheduleError::FailedToPayUpgradeFee, )); From f03377de3373f574c5910a65f27b28219e4e9bb0 Mon Sep 17 00:00:00 2001 From: Sergej Sakac <73715684+Szegoo@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:14:34 +0100 Subject: [PATCH 096/101] Update polkadot/runtime/common/src/paras_registrar/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- polkadot/runtime/common/src/paras_registrar/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 8caf32512a7f..9fe4a956c4e8 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -967,7 +967,7 @@ impl PreCodeUpgrade for Pallet { } let additional_deposit = new_deposit.saturating_sub(current_deposit); - if let Err(_) = ::Currency::reserve(&billing_account, additional_deposit) { + if ::Currency::reserve(&billing_account, additional_deposit).is_err() { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( CodeUpgradeScheduleError::FailedToReserveDeposit, )); From 15d9a2702fa12bc1681e311499c4379fb77d7324 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 19 Jan 2024 08:39:35 +0100 Subject: [PATCH 097/101] renaming & remove SovereignAccountOf --- .../runtime/common/src/assigned_slots/mod.rs | 2 +- .../runtime/common/src/integration_tests.rs | 16 ++------ .../runtime/common/src/paras_registrar/mod.rs | 40 +++++++------------ polkadot/runtime/parachains/src/mock.rs | 2 +- polkadot/runtime/parachains/src/paras/mod.rs | 8 ++-- polkadot/runtime/rococo/src/lib.rs | 3 +- .../weights/runtime_common_paras_registrar.rs | 2 +- polkadot/runtime/test-runtime/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 3 +- .../weights/runtime_common_paras_registrar.rs | 2 +- 10 files changed, 30 insertions(+), 50 deletions(-) diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index d0d743e7bc35..f00ef5963299 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -743,7 +743,7 @@ mod tests { type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; type PreCodeUpgrade = (); - type OnCodeUpgrade = (); + type OnCodeUpgraded = (); type OnNewHead = (); type AssignCoretime = (); } diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 5692165f442c..3231041237d6 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -46,14 +46,12 @@ use sp_io::TestExternalities; use sp_keyring::Sr25519Keyring; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup, One}, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, One}, transaction_validity::TransactionPriority, AccountId32, BuildStorage, }; use sp_std::sync::Arc; -use xcm::opaque::lts::{Junction::Parachain, MultiLocation, NetworkId, Parent}; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription}; -use xcm_executor::traits::ConvertLocation; +use xcm::opaque::lts::NetworkId; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlockU32; @@ -216,7 +214,7 @@ impl paras::Config for Test { type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; type PreCodeUpgrade = Registrar; - type OnCodeUpgrade = Registrar; + type OnCodeUpgraded = Registrar; type OnNewHead = (); type AssignCoretime = (); } @@ -228,8 +226,6 @@ parameter_types! { pub const RelayNetwork: NetworkId = NetworkId::Kusama; } -pub type LocationToAccountId = HashedDescription>; - impl paras_registrar::Config for Test { type RuntimeEvent = RuntimeEvent; type OnSwap = (Crowdloan, Slots); @@ -238,7 +234,6 @@ impl paras_registrar::Config for Test { type Currency = Balances; type RuntimeOrigin = RuntimeOrigin; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationToAccountId; type WeightInfo = crate::paras_registrar::TestWeightInfo; } @@ -968,10 +963,7 @@ fn setting_parachain_billing_account_to_self_works() { // fail if the balance of the sovereign account is insufficient to cover the required // deposit amount - let location: MultiLocation = (Parent, Parachain(para_id.into())).into(); - let sovereign_account = - ::SovereignAccountOf::convert_location(&location) - .unwrap(); + let sovereign_account = para_id.into_account_truncating(); let para_origin: runtime_parachains::Origin = u32::from(para_id).into(); assert_noop!( diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 9fe4a956c4e8..15cf086c2ca4 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -30,7 +30,7 @@ use polkadot_parachain_primitives::primitives::IsSystem; use primitives::{HeadData, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID}; use runtime_parachains::{ configuration, ensure_parachain, - paras::{self, OnCodeUpgrade, ParaGenesisArgs, PreCodeUpgrade, SetGoAhead}, + paras::{self, OnCodeUpgraded, ParaGenesisArgs, PreCodeUpgrade, SetGoAhead}, Origin, ParaLifecycle, }; use sp_std::{prelude::*, result}; @@ -41,11 +41,9 @@ use parity_scale_codec::{Decode, Encode}; use runtime_parachains::paras::{OnNewHead, ParaKind, UpgradeRequirements}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{CheckedSub, Saturating}, + traits::{AccountIdConversion, CheckedSub, Saturating}, RuntimeDebug, }; -use xcm::opaque::lts::{Junction::Parachain, MultiLocation, Parent}; -use xcm_executor::traits::ConvertLocation; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, TypeInfo)] pub struct ParaInfo { @@ -101,7 +99,7 @@ pub trait WeightInfo { fn deregister() -> Weight; fn swap() -> Weight; fn pre_code_upgrade() -> Weight; - fn on_code_upgrade() -> Weight; + fn on_code_upgraded() -> Weight; fn schedule_code_upgrade(b: u32) -> Weight; fn set_current_head(b: u32) -> Weight; fn set_parachain_billing_account_to_self() -> Weight; @@ -128,7 +126,7 @@ impl WeightInfo for TestWeightInfo { fn pre_code_upgrade() -> Weight { Weight::zero() } - fn on_code_upgrade() -> Weight { + fn on_code_upgraded() -> Weight { Weight::zero() } fn schedule_code_upgrade(_b: u32) -> Weight { @@ -193,11 +191,6 @@ pub mod pallet { #[pallet::constant] type UpgradeFee: Get>; - /// Type used to get the sovereign account of a parachain. - /// - /// This is used to enable reserving or refunding deposit from parachains. - type SovereignAccountOf: ConvertLocation; - /// Weight Information for the Extrinsics in the Pallet type WeightInfo: WeightInfo; } @@ -540,9 +533,7 @@ pub mod pallet { // for the root to set itself as the billing account. return Ok(()) } else { - let location: MultiLocation = (Parent, Parachain(para.into())).into(); - let sovereign_account = T::SovereignAccountOf::convert_location(&location).unwrap(); - sovereign_account + para.into_account_truncating() }; Self::set_parachain_billing_account(para, new_billing_account)?; @@ -958,7 +949,9 @@ impl PreCodeUpgrade for Pallet { T::UpgradeFee::get(), WithdrawReasons::FEE, ExistenceRequirement::KeepAlive, - ).is_err() { + ) + .is_err() + { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( CodeUpgradeScheduleError::FailedToPayUpgradeFee, )); @@ -1002,11 +995,9 @@ impl PreCodeUpgrade for Pallet { } } -impl OnCodeUpgrade for Pallet { - fn on_code_upgrade(id: ParaId) -> Weight { - let Some(mut info) = Paras::::get(id) else { - return T::DbWeight::get().reads(1) - }; +impl OnCodeUpgraded for Pallet { + fn on_code_upgraded(id: ParaId) -> Weight { + let Some(mut info) = Paras::::get(id) else { return T::DbWeight::get().reads(1) }; if let Some(rebate) = info.pending_deposit_refund { if let Some(billing_account) = info.billing_account.clone() { @@ -1019,7 +1010,7 @@ impl OnCodeUpgrade for Pallet { who: billing_account, amount: rebate, }); - return ::WeightInfo::on_code_upgrade() + return ::WeightInfo::on_code_upgraded() } } @@ -1149,7 +1140,7 @@ mod tests { type QueueFootprinter = (); type NextSessionRotation = crate::mock::TestNextSessionRotation; type PreCodeUpgrade = Registrar; - type OnCodeUpgrade = (); + type OnCodeUpgraded = (); type OnNewHead = (); type AssignCoretime = (); } @@ -1173,7 +1164,6 @@ mod tests { type ParaDeposit = ParaDeposit; type DataDepositPerByte = DataDepositPerByte; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = (); type WeightInfo = TestWeightInfo; } @@ -1961,7 +1951,7 @@ mod benchmarking { let _ = T::PreCodeUpgrade::pre_code_upgrade(para, new_small_code, UpgradeRequirements::EnforceRequirements); } - on_code_upgrade { + on_code_upgraded { let para = register_para::(LOWEST_PUBLIC_ID.into()); let new_small_code = ValidationCode(vec![0]); @@ -1972,7 +1962,7 @@ mod benchmarking { assert_ok!(Registrar::::schedule_code_upgrade(RawOrigin::Root.into(), para, new_small_code)); }: { - let _ = T::OnCodeUpgrade::on_code_upgrade(para); + let _ = T::OnCodeUpgraded::on_code_upgraded(para); } set_current_head { diff --git a/polkadot/runtime/parachains/src/mock.rs b/polkadot/runtime/parachains/src/mock.rs index 6b2eafb2deaf..262860d2727e 100644 --- a/polkadot/runtime/parachains/src/mock.rs +++ b/polkadot/runtime/parachains/src/mock.rs @@ -227,7 +227,7 @@ impl crate::paras::Config for Test { type QueueFootprinter = ParaInclusion; type NextSessionRotation = TestNextSessionRotation; type PreCodeUpgrade = (); - type OnCodeUpgrade = (); + type OnCodeUpgraded = (); type OnNewHead = (); type AssignCoretime = (); } diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 305492677c28..9c9a65134f23 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -520,8 +520,8 @@ pub trait OnCodeUpgraded { /// An empty implementation of the trait where there is no logic executed upon a successful /// code upgrade. -impl OnCodeUpgrade for () { - fn on_code_upgrade(_id: ParaId) -> Weight { +impl OnCodeUpgraded for () { + fn on_code_upgraded(_id: ParaId) -> Weight { Weight::zero() } } @@ -669,7 +669,7 @@ pub mod pallet { type PreCodeUpgrade: PreCodeUpgrade; /// Type that executes some custom logic upon a successful code upgrade. - type OnCodeUpgrade: OnCodeUpgrade; + type OnCodeUpgraded: OnCodeUpgraded; /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -2112,7 +2112,7 @@ impl Pallet { let weight = if let Some(prior_code_hash) = maybe_prior_code_hash { let mut weight = Self::note_past_code(id, expected_at, now, prior_code_hash); - weight = weight.saturating_add(T::OnCodeUpgrade::on_code_upgrade(id)); + weight = weight.saturating_add(T::OnCodeUpgraded::on_code_upgraded(id)); weight } else { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 1de9e063542f..e28a8c691bc8 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -940,7 +940,7 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type PreCodeUpgrade = Registrar; - type OnCodeUpgrade = Registrar; + type OnCodeUpgraded = Registrar; type OnNewHead = Registrar; type AssignCoretime = CoretimeAssignmentProvider; } @@ -1085,7 +1085,6 @@ impl paras_registrar::Config for Runtime { type OnSwap = (Crowdloan, Slots); type ParaDeposit = ParaDeposit; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationConverter; type DataDepositPerByte = DataDepositPerByte; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } diff --git a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs index 53764d7c1e67..cbb3cb351e72 100644 --- a/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/rococo/src/weights/runtime_common_paras_registrar.rs @@ -235,7 +235,7 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. - fn on_code_upgrade() -> Weight { + fn on_code_upgraded() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 9ebfa9938313..e8eb21c46b1d 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -538,7 +538,7 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type PreCodeUpgrade = (); - type OnCodeUpgrade = (); + type OnCodeUpgraded = (); type OnNewHead = (); type AssignCoretime = (); } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 5f6900a9bdce..e0db78b37077 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1151,7 +1151,7 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type PreCodeUpgrade = (); - type OnCodeUpgrade = (); + type OnCodeUpgraded = (); type OnNewHead = (); type AssignCoretime = (); } @@ -1294,7 +1294,6 @@ impl paras_registrar::Config for Runtime { type ParaDeposit = ParaDeposit; type DataDepositPerByte = RegistrarDataDepositPerByte; type UpgradeFee = UpgradeFee; - type SovereignAccountOf = LocationConverter; type WeightInfo = weights::runtime_common_paras_registrar::WeightInfo; } diff --git a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs index 93d6d81c4797..2b669156cd81 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -233,7 +233,7 @@ impl runtime_common::paras_registrar::WeightInfo for We /// Storage: Paras Heads (r:0 w:1) /// Proof Skipped: Paras Heads (max_values: None, max_size: None, mode: Measured) /// The range of component `b` is `[1, 1048576]`. - fn on_code_upgrade() -> Weight { + fn on_code_upgraded() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` From 6197aa1ab0825a9f5afc2529a5ab1230b404e2e2 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 19 Jan 2024 09:26:07 +0100 Subject: [PATCH 098/101] bring back old migration --- .../common/src/paras_registrar/migration.rs | 147 +++++++++++++----- polkadot/runtime/rococo/src/lib.rs | 2 +- polkadot/runtime/westend/src/lib.rs | 2 +- 3 files changed, 112 insertions(+), 39 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/migration.rs b/polkadot/runtime/common/src/paras_registrar/migration.rs index 9e110af2c16d..efc7825b18f7 100644 --- a/polkadot/runtime/common/src/paras_registrar/migration.rs +++ b/polkadot/runtime/common/src/paras_registrar/migration.rs @@ -15,7 +15,10 @@ // along with Polkadot. If not, see . use super::*; -use frame_support::traits::OnRuntimeUpgrade; +use frame_support::{ + traits::{Contains, OnRuntimeUpgrade}, + Twox64Concat, +}; #[derive(Encode, Decode)] pub struct ParaInfoV1 { @@ -24,50 +27,120 @@ pub struct ParaInfoV1 { locked: Option, } -pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData); -impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { - fn on_runtime_upgrade() -> Weight { - let mut count = 0u64; - Paras::::translate::>, _>(|_key, v1| { - count.saturating_inc(); - Some(ParaInfo { - manager: v1.manager, - deposit: v1.deposit, - locked: v1.locked, - billing_account: None, - pending_deposit_refund: None, - }) - }); +pub mod v1 { + use super::*; - log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", count); - T::DbWeight::get().reads_writes(count, count) - } + #[frame_support::storage_alias] + pub type Paras = StorageMap< + Pallet, + Twox64Concat, + ParaId, + ParaInfoV1<::AccountId, BalanceOf>, + >; - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok((Paras::::iter_keys().count() as u32).encode()) + #[derive(Encode, Decode)] + pub struct ParaInfoOld { + manager: Account, + deposit: Balance, + locked: bool, } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - let old_count = u32::decode(&mut &state[..]).expect("Known good"); - let new_count = Paras::::iter_values().count() as u32; + pub struct VersionUncheckedMigrateToV1( + sp_std::marker::PhantomData<(T, UnlockParaIds)>, + ); + impl> OnRuntimeUpgrade + for VersionUncheckedMigrateToV1 + { + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|key, v1| { + count.saturating_inc(); + Some(ParaInfoV1 { + manager: v1.manager, + deposit: v1.deposit, + locked: if UnlockParaIds::contains(&key) { None } else { Some(v1.locked) }, + }) + }); - ensure!(old_count == new_count, "Paras count should not change"); + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 1", count); + T::DbWeight::get().reads_writes(count, count) + } - Paras::::iter().try_for_each(|(para_id, info)| -> Result<(), _> { - ensure!(info.billing_account.is_none(), "The billing account must be set to the None"); - ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; + + ensure!(old_count == new_count, "Paras count should not change"); Ok(()) - }) + } } + + pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + VersionUncheckedMigrateToV1, + super::Pallet, + ::DbWeight, + >; } -pub type MigrateToV2 = frame_support::migrations::VersionedMigration< - 1, - 2, - VersionUncheckedMigrateToV2, - super::Pallet, - ::DbWeight, ->; +pub mod v2 { + use super::*; + + pub struct VersionUncheckedMigrateToV2(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for VersionUncheckedMigrateToV2 { + fn on_runtime_upgrade() -> Weight { + let mut count = 0u64; + Paras::::translate::>, _>(|_key, v1| { + count.saturating_inc(); + Some(ParaInfo { + manager: v1.manager, + deposit: v1.deposit, + locked: v1.locked, + billing_account: None, + pending_deposit_refund: None, + }) + }); + + log::info!(target: "runtime::registrar", "Upgraded {} storages to version 2", count); + T::DbWeight::get().reads_writes(count, count) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((Paras::::iter_keys().count() as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = Paras::::iter_values().count() as u32; + + ensure!(old_count == new_count, "Paras count should not change"); + + Paras::::iter().try_for_each(|(_para_id, info)| -> Result<(), _> { + ensure!( + info.billing_account.is_none(), + "The billing account must be set to the None" + ); + ensure!(info.pending_deposit_refund.is_none(), "There should be no pending refund"); + + Ok(()) + }) + } + } + + pub type MigrateToV2 = frame_support::migrations::VersionedMigration< + 1, + 2, + VersionUncheckedMigrateToV2, + super::Pallet, + ::DbWeight, + >; +} diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index a2bbe2ac8ea9..3260704ba0ac 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -1641,7 +1641,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV2, + paras_registrar::migration::v2::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_referenda::migration::v1::MigrateV0ToV1, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index deaf606d697f..616cee668e41 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -1651,7 +1651,7 @@ pub mod migrations { parachains_scheduler::migration::MigrateV1ToV2, parachains_configuration::migration::v8::MigrateToV8, parachains_configuration::migration::v9::MigrateToV9, - paras_registrar::migration::MigrateToV2, + paras_registrar::migration::v2::MigrateToV2, pallet_referenda::migration::v1::MigrateV0ToV1, pallet_grandpa::migrations::MigrateV4ToV5, parachains_configuration::migration::v10::MigrateToV10, From 2e2523204fc7dda533e73a130ad46cfcf2d14658 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 19 Jan 2024 09:47:48 +0100 Subject: [PATCH 099/101] signal_code_upgrade_failure --- polkadot/runtime/parachains/src/inclusion/mod.rs | 1 + polkadot/runtime/parachains/src/paras/mod.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index af187d5224a2..8f1b73866ee0 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -1145,6 +1145,7 @@ impl Pallet { SetGoAhead::Yes, )), Err(consumed_weight) => { + >::signal_code_upgrade_failure(para_id); log::debug!( target: LOG_TARGET, "Failed to schedule a code upgrade for parachain {}", diff --git a/polkadot/runtime/parachains/src/paras/mod.rs b/polkadot/runtime/parachains/src/paras/mod.rs index 9c9a65134f23..6ab29847e355 100644 --- a/polkadot/runtime/parachains/src/paras/mod.rs +++ b/polkadot/runtime/parachains/src/paras/mod.rs @@ -2137,6 +2137,11 @@ impl Pallet { weight.saturating_add(T::OnNewHead::on_new_head(id, &new_head)) } + /// Signal to the parachain that scheduling the code upgrade failed. + pub(crate) fn signal_code_upgrade_failure(id: ParaId) { + UpgradeGoAheadSignal::::insert(&id, UpgradeGoAhead::Abort); + } + /// 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 { From cc3e0dfa1e5fdc4b18f9fc0f0825e50996e55f6c Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 19 Jan 2024 09:55:32 +0100 Subject: [PATCH 100/101] fix warning --- polkadot/runtime/rococo/src/lib.rs | 4 +--- polkadot/runtime/westend/src/lib.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 3260704ba0ac..2e8713d7c5df 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -114,8 +114,6 @@ mod weights; // XCM configurations. pub mod xcm_config; -use xcm_config::LocationConverter; - // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -2326,7 +2324,7 @@ sp_api::impl_runtime_apis! { } impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = XcmConfig; - type AccountIdConverter = LocationConverter; + type AccountIdConverter = xcm_config::LocationConverter; type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< XcmConfig, ExistentialDepositAsset, diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 616cee668e41..e4f6e0dee91f 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -121,8 +121,6 @@ mod bag_thresholds; mod weights; pub mod xcm_config; -use xcm_config::LocationConverter; - // Implemented types. mod impls; use impls::ToParachainIdentityReaper; @@ -2352,7 +2350,7 @@ sp_api::impl_runtime_apis! { impl pallet_xcm_benchmarks::Config for Runtime { type XcmConfig = xcm_config::XcmConfig; - type AccountIdConverter = LocationConverter; + type AccountIdConverter = xcm_config::LocationConverter; type DeliveryHelper = runtime_common::xcm_sender::ToParachainDeliveryHelper< xcm_config::XcmConfig, ExistentialDepositAsset, From 78be2ae9a41ad6bdbc757b9b443fd4695d0232e9 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Fri, 19 Jan 2024 10:23:22 +0100 Subject: [PATCH 101/101] make charging costs transactional --- .../runtime/common/src/paras_registrar/mod.rs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/polkadot/runtime/common/src/paras_registrar/mod.rs b/polkadot/runtime/common/src/paras_registrar/mod.rs index 241b8e202f40..6716cb79bdb4 100644 --- a/polkadot/runtime/common/src/paras_registrar/mod.rs +++ b/polkadot/runtime/common/src/paras_registrar/mod.rs @@ -24,6 +24,7 @@ use frame_support::{ ensure, pallet_prelude::Weight, traits::{Currency, ExistenceRequirement, Get, ReservableCurrency, WithdrawReasons}, + transactional, }; use frame_system::{self, ensure_root, ensure_signed}; use polkadot_parachain_primitives::primitives::IsSystem; @@ -83,10 +84,8 @@ pub enum CodeUpgradeScheduleError { /// The parachain billing account has to be explicitly set before being able to schedule a /// code upgrade. BillingAccountNotSet, - /// Failed to pay the upgrade fee for scheduling the code upgrade. - FailedToPayUpgradeFee, - /// Failed to reserve the appropriate deposit for the new validation code. - FailedToReserveDeposit, + /// Failed to pay the associated costs of scheduling the code upgrade. + FailedToPayUpgradeCosts, } type BalanceOf = @@ -878,6 +877,23 @@ impl Pallet { Ok(()) } + #[transactional] + fn charge_upgrade_costs( + billing_account: T::AccountId, + additional_deposit: BalanceOf, + ) -> DispatchResult { + ::Currency::withdraw( + &billing_account, + T::UpgradeFee::get(), + WithdrawReasons::FEE, + ExistenceRequirement::KeepAlive, + )?; + + ::Currency::reserve(&billing_account, additional_deposit)?; + + Ok(()) + } + /// Returns the required deposit amount for the parachain, given the specified genesis head /// and validation code size. fn required_para_deposit(head_size: usize, validation_code_size: usize) -> BalanceOf { @@ -944,29 +960,14 @@ impl PreCodeUpgrade for Pallet { return Err(::WeightInfo::pre_code_upgrade()) }; - if ::Currency::withdraw( - &billing_account, - T::UpgradeFee::get(), - WithdrawReasons::FEE, - ExistenceRequirement::KeepAlive, - ) - .is_err() - { - Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToPayUpgradeFee, - )); - // An overestimate of the used weight, but it's better to be safe than sorry. - return Err(::WeightInfo::pre_code_upgrade()) - } - let additional_deposit = new_deposit.saturating_sub(current_deposit); - if ::Currency::reserve(&billing_account, additional_deposit).is_err() { + if Self::charge_upgrade_costs(billing_account, additional_deposit).is_err() { Self::deposit_event(Event::::CodeUpgradeScheduleFailed( - CodeUpgradeScheduleError::FailedToReserveDeposit, + CodeUpgradeScheduleError::FailedToPayUpgradeCosts, )); // An overestimate of the used weight, but it's better to be safe than sorry. return Err(::WeightInfo::pre_code_upgrade()) - } + }; // Update the deposit to the new appropriate amount. info.deposit = new_deposit;