From b55344819f79714396d87dc4a2f04dcbdb77ca58 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 11 May 2021 22:28:37 +0200 Subject: [PATCH] Extrinsic for reserving a parachain ID (#3008) * Extrinsic for reserving a parachain ID * Fixes * Fixes * Docs * cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=runtime_common::paras_registrar --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/runtime_common_paras_registrar.rs * Update runtime/common/src/paras_registrar.rs Co-authored-by: Robert Habermeier * cargo run --release --features=runtime-benchmarks -- benchmark --chain=kusama-dev --steps=50 --repeat=20 --pallet=runtime_common::paras_registrar --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/kusama/src/weights/runtime_common_paras_registrar.rs Co-authored-by: Parity Bot Co-authored-by: Shawn Tabrizi Co-authored-by: Robert Habermeier --- .../runtime/common/src/integration_tests.rs | 15 +- .../runtime/common/src/paras_registrar.rs | 164 ++++++++++++------ .../weights/runtime_common_paras_registrar.rs | 15 +- .../weights/runtime_common_paras_registrar.rs | 15 +- 4 files changed, 142 insertions(+), 67 deletions(-) diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs index 85bcd0fba6..8c320d64b1 100644 --- a/polkadot/runtime/common/src/integration_tests.rs +++ b/polkadot/runtime/common/src/integration_tests.rs @@ -301,12 +301,14 @@ fn basic_end_to_end_works() { // First register 2 parathreads let genesis_head = Registrar::worst_head_data(); let validation_code = Registrar::worst_validation_code(); + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), ParaId::from(para_1), genesis_head.clone(), validation_code.clone(), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_ok!(Registrar::register( Origin::signed(2), ParaId::from(2001), @@ -445,18 +447,20 @@ fn basic_errors_fail() { let genesis_head = Registrar::worst_head_data(); let validation_code = Registrar::worst_validation_code(); + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), para_id, genesis_head.clone(), validation_code.clone(), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_noop!(Registrar::register( Origin::signed(2), para_id, genesis_head, validation_code, - ), paras_registrar::Error::::InvalidParaId); + ), paras_registrar::Error::::NotOwner); // Start an auction let duration = 99u32; @@ -489,6 +493,7 @@ fn competing_slots() { Balances::make_free_balance_be(&n, 1_000); let genesis_head = Registrar::worst_head_data(); let validation_code = Registrar::worst_validation_code(); + assert_ok!(Registrar::reserve(Origin::signed(n))); assert_ok!(Registrar::register( Origin::signed(n), para_id + n - 1, @@ -577,6 +582,7 @@ fn competing_bids() { Balances::make_free_balance_be(&n, 1_000); let genesis_head = Registrar::worst_head_data(); let validation_code = Registrar::worst_validation_code(); + assert_ok!(Registrar::reserve(Origin::signed(n))); assert_ok!(Registrar::register( Origin::signed(n), ParaId::from(start_para + n), @@ -652,12 +658,14 @@ fn basic_swap_works() { Balances::make_free_balance_be(&1, 1_000); Balances::make_free_balance_be(&2, 1_000); // First register 2 parathreads with different data + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), ParaId::from(2000), test_genesis_head(10), test_validation_code(10), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_ok!(Registrar::register( Origin::signed(2), ParaId::from(2001), @@ -783,12 +791,14 @@ fn crowdloan_ending_period_bid() { Balances::make_free_balance_be(&1, 1_000); Balances::make_free_balance_be(&2, 1_000); // First register 2 parathreads + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), ParaId::from(2000), test_genesis_head(10), test_validation_code(10), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_ok!(Registrar::register( Origin::signed(2), ParaId::from(2001), @@ -889,6 +899,7 @@ fn auction_bid_requires_registered_para() { ), AuctionsError::::ParaNotRegistered); // Now we register the para + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), ParaId::from(2000), @@ -935,12 +946,14 @@ fn gap_bids_work() { Balances::make_free_balance_be(&2, 1_000); // Now register 2 paras + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), ParaId::from(2000), test_genesis_head(10), test_validation_code(10), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_ok!(Registrar::register( Origin::signed(2), ParaId::from(2001), diff --git a/polkadot/runtime/common/src/paras_registrar.rs b/polkadot/runtime/common/src/paras_registrar.rs index b096b87bb3..1121994cd5 100644 --- a/polkadot/runtime/common/src/paras_registrar.rs +++ b/polkadot/runtime/common/src/paras_registrar.rs @@ -39,7 +39,7 @@ use runtime_parachains::{ use crate::traits::{Registrar, OnSwap}; use parity_scale_codec::{Encode, Decode}; -use sp_runtime::{RuntimeDebug, traits::Saturating}; +use sp_runtime::{RuntimeDebug, traits::{Saturating, CheckedSub}}; #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug)] pub struct ParaInfo { @@ -55,6 +55,7 @@ type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; pub trait WeightInfo { + fn reserve() -> Weight; fn register() -> Weight; fn force_register() -> Weight; fn deregister() -> Weight; @@ -63,6 +64,7 @@ pub trait WeightInfo { pub struct TestWeightInfo; impl WeightInfo for TestWeightInfo { + fn reserve() -> Weight { 0 } fn register() -> Weight { 0 } fn force_register() -> Weight { 0 } fn deregister() -> Weight { 0 } @@ -126,6 +128,7 @@ decl_event! { { Registered(ParaId, AccountId), Deregistered(ParaId), + Reserved(ParaId, AccountId), } } @@ -155,6 +158,8 @@ decl_error! { ParaLocked, /// The id you are trying to register is reserved for system parachains. InvalidParaId, + /// The ID given for registration has not been reserved. + NotReserved, } } @@ -169,22 +174,20 @@ decl_module! { fn deposit_event() = default; - /// Register a Para Id on the relay chain. + /// Register head data and validation code for a reserved Para Id. /// - /// This function will queue the new Para `id` to be a parathread. - /// Using the Slots pallet, a parathread can then be upgraded to get a - /// parachain slot. + /// ## Arguments + /// - `origin`: Must be called by a `Signed` origin. + /// - `id`: The para ID. Must be owned/managed by the `origin` signing account. + /// - `genesis_head`: The genesis head data of the parachain/thread. + /// - `validation_code`: The initial validation code of the parachain/thread. /// - /// The `id` *MUST* equal the value of `NextFreeParaId`. + /// ## Deposits/Fees + /// The origin signed account must reserve a corresponding deposit for the registration. Anything already + /// reserved previously for this para ID is accounted for. /// - /// DEPRECATED: This function should generally not be used and is provided for backwards compatibility - /// only. Use `register_next` instead. - /// - /// This function must be called by a signed origin. - /// - /// The origin must pay a deposit for the registration information, - /// including the genesis information and validation code. ParaId - /// must be greater than or equal to 1000. + /// ## Events + /// The `Registered` event is emitted in case of success. #[weight = T::WeightInfo::register()] pub fn register( origin, @@ -193,11 +196,7 @@ decl_module! { validation_code: ValidationCode, ) -> DispatchResult { let who = ensure_signed(origin)?; - let valid_id = NextFreeParaId::get().max(LOWEST_PUBLIC_ID); - ensure!(id == valid_id, Error::::InvalidParaId); - Self::do_register(who, None, id, genesis_head, validation_code)?; - - NextFreeParaId::set(id + 1); + Self::do_register(who, None, id, genesis_head, validation_code, true)?; Ok(()) } @@ -217,7 +216,7 @@ decl_module! { validation_code: ValidationCode, ) -> DispatchResult { ensure_root(origin)?; - Self::do_register(who, Some(deposit), id, genesis_head, validation_code) + Self::do_register(who, Some(deposit), id, genesis_head, validation_code, false) } /// Deregister a Para Id, freeing all data and returning any deposit. @@ -284,26 +283,25 @@ decl_module! { Self::remove_lock(para); } - /// Register a Para Id on the relay chain. + /// Reserve a Para Id on the relay chain. /// - /// This function will queue the new Para Id to be a parathread. - /// Using the Slots pallet, a parathread can then be upgraded to get a - /// parachain slot. + /// This function will reserve a new Para Id to be owned/managed by the origin account. + /// The origin account is able to register head data and validation code using `register` to create + /// a parathread. Using the Slots pallet, a parathread can then be upgraded to get a parachain slot. /// - /// This function must be called by a signed origin. + /// ## Arguments + /// - `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new para ID. /// - /// The origin must pay a deposit for the registration information, - /// including the genesis information and validation code. ParaId - /// must be greater than or equal to 1000. - #[weight = T::WeightInfo::register()] - pub fn register_next( - origin, - genesis_head: HeadData, - validation_code: ValidationCode, - ) -> DispatchResult { + /// ## Deposits/Fees + /// The origin must reserve a deposit of `ParaDeposit` for the registration. + /// + /// ## Events + /// The `Reserved` event is emitted in case of success, which provides the ID reserved for use. + #[weight = T::WeightInfo::reserve()] + pub fn reserve(origin) -> DispatchResult { let who = ensure_signed(origin)?; let id = NextFreeParaId::get().max(LOWEST_PUBLIC_ID); - Self::do_register(who, None, id, genesis_head, validation_code)?; + Self::do_reserve(who, None, id)?; NextFreeParaId::set(id + 1); Ok(()) } @@ -353,7 +351,7 @@ impl Registrar for Module { genesis_head: HeadData, validation_code: ValidationCode, ) -> DispatchResult { - Self::do_register(manager, None, id, genesis_head, validation_code) + Self::do_register(manager, None, id, genesis_head, validation_code, false) } // Deregister a Para ID, free any data, and return any deposits. @@ -427,6 +425,27 @@ impl Module { }) } + fn do_reserve( + who: T::AccountId, + deposit_override: Option>, + id: ParaId, + ) -> DispatchResult { + ensure!(!Paras::::contains_key(id), Error::::AlreadyRegistered); + ensure!(paras::Module::::lifecycle(id).is_none(), Error::::AlreadyRegistered); + + let deposit = deposit_override.unwrap_or_else(T::ParaDeposit::get); + ::Currency::reserve(&who, deposit)?; + let info = ParaInfo { + manager: who.clone(), + deposit, + locked: false, + }; + + Paras::::insert(id, info); + Self::deposit_event(RawEvent::Reserved(id, who)); + Ok(()) + } + /// Attempt to register a new Para Id under management of `who` in the /// system with the given information. fn do_register( @@ -435,20 +454,32 @@ impl Module { id: ParaId, genesis_head: HeadData, validation_code: ValidationCode, + ensure_reserved: bool, ) -> DispatchResult { - ensure!(!Paras::::contains_key(id), Error::::AlreadyRegistered); + let deposited = if let Some(para_data) = Paras::::get(id) { + ensure!(para_data.manager == who, Error::::NotOwner); + ensure!(!para_data.locked, Error::::ParaLocked); + para_data.deposit + } else { + ensure!(!ensure_reserved, Error::::NotReserved); + Default::default() + }; ensure!(paras::Module::::lifecycle(id).is_none(), Error::::AlreadyRegistered); let (genesis, deposit) = Self::validate_onboarding_data( genesis_head, validation_code, false )?; - let deposit = deposit_override.unwrap_or(deposit); - ::Currency::reserve(&who, deposit)?; + + if let Some(additional) = deposit.checked_sub(&deposited) { + ::Currency::reserve(&who, additional)?; + } else if let Some(rebate) = deposited.checked_sub(&deposit) { + ::Currency::unreserve(&who, rebate); + }; let info = ParaInfo { manager: who.clone(), - deposit: deposit, + deposit, locked: false, }; @@ -690,6 +721,7 @@ mod tests { // first para is not yet registered assert!(!Parachains::is_parathread(para_id)); // We register the Para ID + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), para_id, @@ -729,6 +761,8 @@ mod tests { run_to_block(1); let para_id = LOWEST_PUBLIC_ID; assert!(!Parachains::is_parathread(para_id)); + assert_ok!(Registrar::reserve(Origin::signed(1))); + assert_eq!(Balances::reserved_balance(&1), ::ParaDeposit::get()); assert_ok!(Registrar::register( Origin::signed(1), para_id, @@ -747,16 +781,25 @@ mod tests { #[test] fn register_handles_basic_errors() { new_test_ext().execute_with(|| { - // Can't register system parachain + let para_id = LOWEST_PUBLIC_ID; + assert_noop!(Registrar::register( Origin::signed(1), - 32.into(), + para_id, test_genesis_head(::MaxHeadSize::get() as usize), test_validation_code(::MaxCodeSize::get() as usize), - ), Error::::InvalidParaId); + ), Error::::NotReserved); // Successfully register para - let para_id = LOWEST_PUBLIC_ID; + assert_ok!(Registrar::reserve(Origin::signed(1))); + + assert_noop!(Registrar::register( + Origin::signed(2), + para_id, + test_genesis_head(::MaxHeadSize::get() as usize), + test_validation_code(::MaxCodeSize::get() as usize), + ), Error::::NotOwner); + assert_ok!(Registrar::register( Origin::signed(1), para_id, @@ -774,9 +817,10 @@ mod tests { para_id, test_genesis_head(::MaxHeadSize::get() as usize), test_validation_code(::MaxCodeSize::get() as usize), - ), Error::::InvalidParaId); + ), Error::::NotReserved); // Head Size Check + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_noop!(Registrar::register( Origin::signed(2), para_id + 1, @@ -793,12 +837,7 @@ mod tests { ), Error::::CodeTooLarge); // Needs enough funds for deposit - assert_noop!(Registrar::register( - Origin::signed(1337), - para_id + 1, - test_genesis_head(::MaxHeadSize::get() as usize), - test_validation_code(::MaxCodeSize::get() as usize), - ), BalancesError::::InsufficientBalance); + assert_noop!(Registrar::reserve(Origin::signed(1337)), BalancesError::::InsufficientBalance); }); } @@ -808,16 +847,13 @@ mod tests { run_to_block(1); let para_id = LOWEST_PUBLIC_ID; assert!(!Parachains::is_parathread(para_id)); + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), para_id, test_genesis_head(32), test_validation_code(32), )); - assert_eq!( - Balances::reserved_balance(&1), - ::ParaDeposit::get() + 64 * ::DataDepositPerByte::get() - ); run_to_session(2); assert!(Parachains::is_parathread(para_id)); assert_ok!(Registrar::deregister( @@ -836,6 +872,7 @@ mod tests { run_to_block(1); let para_id = LOWEST_PUBLIC_ID; assert!(!Parachains::is_parathread(para_id)); + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), para_id, @@ -865,12 +902,14 @@ mod tests { // Successfully register first two parachains let para_1 = LOWEST_PUBLIC_ID; let para_2 = LOWEST_PUBLIC_ID + 1; + assert_ok!(Registrar::reserve(Origin::signed(1))); assert_ok!(Registrar::register( Origin::signed(1), para_1, test_genesis_head(::MaxHeadSize::get() as usize), test_validation_code(::MaxCodeSize::get() as usize), )); + assert_ok!(Registrar::reserve(Origin::signed(2))); assert_ok!(Registrar::register( Origin::signed(2), para_2, @@ -922,8 +961,9 @@ mod tests { fn para_lock_works() { new_test_ext().execute_with(|| { run_to_block(1); - let para_id = LOWEST_PUBLIC_ID; + assert_ok!(Registrar::reserve(Origin::signed(1))); + let para_id = LOWEST_PUBLIC_ID; assert_ok!(Registrar::register( Origin::signed(1), para_id, @@ -972,6 +1012,7 @@ mod benchmarking { 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())); assert_ok!(Registrar::::register(RawOrigin::Signed(caller).into(), para, genesis_head, validation_code)); return para; } @@ -991,12 +1032,23 @@ mod benchmarking { benchmarks! { where_clause { where ParaOrigin: Into<::Origin> } + reserve { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + }: _(RawOrigin::Signed(caller.clone())) + verify { + assert_last_event::(RawEvent::Reserved(LOWEST_PUBLIC_ID, caller).into()); + assert!(Paras::::get(LOWEST_PUBLIC_ID).is_some()); + assert_eq!(paras::Module::::lifecycle(LOWEST_PUBLIC_ID), None); + } + register { let para = LOWEST_PUBLIC_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())); }: _(RawOrigin::Signed(caller.clone()), para, genesis_head, validation_code) verify { assert_last_event::(RawEvent::Registered(para, caller).into()); diff --git a/polkadot/runtime/kusama/src/weights/runtime_common_paras_registrar.rs b/polkadot/runtime/kusama/src/weights/runtime_common_paras_registrar.rs index 2d5a4b20f1..31929feed9 100644 --- a/polkadot/runtime/kusama/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/kusama/src/weights/runtime_common_paras_registrar.rs @@ -16,7 +16,7 @@ //! Autogenerated weights for runtime_common::paras_registrar //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-04-10, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-05-11, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("kusama-dev"), DB CACHE: 128 // Executed Command: @@ -43,23 +43,28 @@ use sp_std::marker::PhantomData; /// Weight functions for runtime_common::paras_registrar. pub struct WeightInfo(PhantomData); impl runtime_common::paras_registrar::WeightInfo for WeightInfo { + fn reserve() -> Weight { + (52_007_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } fn register() -> Weight { - (8_427_766_000 as Weight) + (8_249_033_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn force_register() -> Weight { - (8_394_534_000 as Weight) + (8_228_924_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn deregister() -> Weight { - (88_992_000 as Weight) + (88_947_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn swap() -> Weight { - (69_480_000 as Weight) + (69_868_000 as Weight) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } 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 2baa4b5af5..d54b4b4ab3 100644 --- a/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs +++ b/polkadot/runtime/westend/src/weights/runtime_common_paras_registrar.rs @@ -16,7 +16,7 @@ //! Autogenerated weights for runtime_common::paras_registrar //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-04-10, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2021-05-11, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 128 // Executed Command: @@ -43,23 +43,28 @@ use sp_std::marker::PhantomData; /// Weight functions for runtime_common::paras_registrar. pub struct WeightInfo(PhantomData); impl runtime_common::paras_registrar::WeightInfo for WeightInfo { + fn reserve() -> Weight { + (50_552_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } fn register() -> Weight { - (8_391_121_000 as Weight) + (8_254_937_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn force_register() -> Weight { - (8_404_663_000 as Weight) + (8_253_211_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn deregister() -> Weight { - (88_109_000 as Weight) + (88_240_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn swap() -> Weight { - (66_711_000 as Weight) + (67_140_000 as Weight) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) }