From ed4da8ceaac7f4b96ffa843bf4879f7555ac7387 Mon Sep 17 00:00:00 2001 From: Vlad Proshchavaiev <32250097+F3Joule@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:42:14 +0200 Subject: [PATCH] feat!: Refactor ownership pallet to transfer ownership of spaces, posts and domains (#255) * Rename pallet-space-ownership to pallet-ownership * Provide interfaces for changing spaces and domains ownership * Introduce PostsProvider - Refactor other providers traits * Remove redundant code from domains pallet * Implement transfer ownership logic in ownership pallet * Add migration to the ownership pallet * Add migration to the runtime * Update spec_version to 41 * Update transaction_version to 9 * chore: Add tests for ownership pallet (#256) * Add utility function to ownership for benchmarking * Update benchmarks in ownership pallet * Add migration from old to new ownership pallet * Update spec_version to 42 * Add missing license headers --------- Co-authored-by: Oleh Mell Co-authored-by: Alex Siman --- Cargo.lock | 92 ++--- integration-tests/Cargo.toml | 6 +- integration-tests/src/mock.rs | 36 +- .../src/tests/space_ownership.rs | 16 +- .../src/utils/space_ownership_utils.rs | 9 +- pallets/creator-staking/src/lib.rs | 6 +- pallets/creator-staking/src/tests/mock.rs | 8 +- pallets/domains/Cargo.toml | 5 +- pallets/domains/src/lib.rs | 114 +++++- pallets/domains/src/types.rs | 4 + pallets/posts/src/functions.rs | 47 +++ pallets/posts/src/lib.rs | 4 - pallets/posts/tests/Cargo.toml | 7 +- pallets/posts/tests/src/mock.rs | 43 ++- pallets/posts/tests/src/tests_utils.rs | 5 +- pallets/profiles/src/benchmarking.rs | 4 +- pallets/profiles/src/lib.rs | 6 +- pallets/profiles/src/mock.rs | 8 +- pallets/roles/src/benchmarking.rs | 2 +- pallets/space-ownership/Cargo.toml | 20 +- pallets/space-ownership/src/benchmarking.rs | 161 ++++++--- pallets/space-ownership/src/lib.rs | 222 +++++++----- pallets/space-ownership/src/migration.rs | 134 +++++++ pallets/space-ownership/src/weights.rs | 327 +++++++++++++----- pallets/space-ownership/tests/Cargo.toml | 13 +- pallets/space-ownership/tests/src/mock.rs | 81 ++++- pallets/space-ownership/tests/src/tests.rs | 242 +++++++------ .../space-ownership/tests/src/tests_utils.rs | 229 ++++++++---- pallets/spaces/src/lib.rs | 33 +- pallets/spaces/tests/src/mock.rs | 2 +- pallets/support/Cargo.toml | 3 + pallets/support/src/traits.rs | 4 +- pallets/support/src/traits/common.rs | 29 +- runtime/Cargo.toml | 8 +- runtime/src/lib.rs | 34 +- 35 files changed, 1439 insertions(+), 525 deletions(-) create mode 100644 pallets/space-ownership/src/migration.rs diff --git a/Cargo.lock b/Cargo.lock index 7c4006e1..5e3d046c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3557,13 +3557,14 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", + "pallet-domains", + "pallet-ownership", "pallet-permissions", "pallet-posts", "pallet-profiles", "pallet-reactions", "pallet-roles", "pallet-space-follows", - "pallet-space-ownership", "pallet-spaces", "pallet-timestamp", "parity-scale-codec", @@ -6076,6 +6077,51 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-ownership" +version = "0.2.2" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-permissions", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", + "subsocial-support", +] + +[[package]] +name = "pallet-ownership-tests" +version = "0.2.2" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "lazy_static", + "mockall", + "pallet-balances", + "pallet-domains", + "pallet-ownership", + "pallet-permissions", + "pallet-posts", + "pallet-profiles", + "pallet-roles", + "pallet-space-follows", + "pallet-spaces", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "subsocial-support", +] + [[package]] name = "pallet-permissions" version = "0.2.2" @@ -6153,15 +6199,16 @@ dependencies = [ name = "pallet-posts-tests" version = "0.2.2" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-balances", + "pallet-ownership", "pallet-permissions", "pallet-posts", "pallet-profiles", "pallet-roles", "pallet-space-follows", - "pallet-space-ownership", "pallet-spaces", "pallet-timestamp", "parity-scale-codec", @@ -6468,44 +6515,6 @@ dependencies = [ "subsocial-support", ] -[[package]] -name = "pallet-space-ownership" -version = "0.2.2" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-spaces", - "parity-scale-codec", - "scale-info", - "sp-std", - "subsocial-support", -] - -[[package]] -name = "pallet-space-ownership-tests" -version = "0.2.2" -dependencies = [ - "frame-support", - "frame-system", - "impl-trait-for-tuples", - "pallet-balances", - "pallet-permissions", - "pallet-profiles", - "pallet-roles", - "pallet-space-follows", - "pallet-space-ownership", - "pallet-spaces", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "subsocial-support", -] - [[package]] name = "pallet-spaces" version = "0.2.2" @@ -11755,6 +11764,7 @@ dependencies = [ "pallet-free-proxy", "pallet-insecure-randomness-collective-flip", "pallet-multisig", + "pallet-ownership", "pallet-permissions", "pallet-post-follows", "pallet-posts", @@ -11766,7 +11776,6 @@ dependencies = [ "pallet-roles", "pallet-session", "pallet-space-follows", - "pallet-space-ownership", "pallet-spaces", "pallet-sudo", "pallet-timestamp", @@ -11805,6 +11814,7 @@ dependencies = [ name = "subsocial-support" version = "0.2.2" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", "pallet-timestamp", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 13b8c91b..00a371a2 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -22,13 +22,14 @@ std = [ 'pallet-timestamp/std', 'frame-support/std', 'frame-system/std', + 'pallet-domains/std', 'pallet-permissions/std', 'pallet-posts/std', 'pallet-profiles/std', 'pallet-reactions/std', 'pallet-roles/std', 'pallet-space-follows/std', - 'pallet-space-ownership/std', + 'pallet-ownership/std', 'pallet-spaces/std', 'subsocial-support/std', ] @@ -47,13 +48,14 @@ sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkad sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } [dev-dependencies] +pallet-domains = { path = '../pallets/domains', default-features = false } pallet-permissions = { path = '../pallets/permissions', default-features = false } pallet-posts = { path = '../pallets/posts', default-features = false } pallet-profiles = { path = '../pallets/profiles', default-features = false } pallet-reactions = { path = '../pallets/reactions', default-features = false } pallet-roles = { path = '../pallets/roles', default-features = false } pallet-space-follows = { path = '../pallets/space-follows', default-features = false } -pallet-space-ownership = { path = '../pallets/space-ownership', default-features = false } +pallet-ownership = { path = '../pallets/space-ownership', default-features = false } pallet-spaces = { path = '../pallets/spaces', default-features = false } subsocial-support = { path = '../pallets/support', default-features = false } diff --git a/integration-tests/src/mock.rs b/integration-tests/src/mock.rs index 3bbacbb1..d652b35e 100644 --- a/integration-tests/src/mock.rs +++ b/integration-tests/src/mock.rs @@ -48,8 +48,9 @@ frame_support::construct_runtime!( Reactions: pallet_reactions, Roles: pallet_roles, SpaceFollows: pallet_space_follows, - SpaceOwnership: pallet_space_ownership, + SpaceOwnership: pallet_ownership, Spaces: pallet_spaces, + Domains: pallet_domains, } ); @@ -132,7 +133,7 @@ impl pallet_posts::Config for TestRuntime { impl pallet_profiles::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = Spaces; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type WeightInfo = (); } @@ -160,10 +161,15 @@ impl pallet_space_follows::Config for TestRuntime { type WeightInfo = pallet_space_follows::weights::SubstrateWeight; } -impl pallet_space_ownership::Config for TestRuntime { +impl pallet_ownership::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type ProfileManager = Profiles; + type SpacesProvider = Spaces; + type SpacePermissionsProvider = Spaces; type CreatorStakingProvider = (); + type DomainsProvider = Domains; + type PostsProvider = Posts; + type Currency = Balances; type WeightInfo = (); } @@ -177,6 +183,30 @@ impl pallet_spaces::Config for TestRuntime { type WeightInfo = (); } +parameter_types! { + pub const RegistrationPeriodLimit: BlockNumber = 100; + pub const BaseDomainDeposit: u64 = 10; + pub const OuterValueByteDeposit: u64 = 1; + pub const InitialPaymentBeneficiary: AccountId = ACCOUNT1; + pub InitialPricesConfig: pallet_domains::types::PricesConfigVec = vec![(1, 100)]; +} + +impl pallet_domains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MinDomainLength = ConstU32<1>; + type MaxDomainLength = ConstU32<64>; + type MaxDomainsPerAccount = ConstU32<10>; + type DomainsInsertLimit = ConstU32<10>; + type RegistrationPeriodLimit = RegistrationPeriodLimit; + type MaxOuterValueLength = ConstU32<64>; + type BaseDomainDeposit = BaseDomainDeposit; + type OuterValueByteDeposit = OuterValueByteDeposit; + type InitialPaymentBeneficiary = InitialPaymentBeneficiary; + type InitialPricesConfig = InitialPricesConfig; + type WeightInfo = (); +} + pub(crate) type AccountId = u64; pub(crate) type BlockNumber = u64; diff --git a/integration-tests/src/tests/space_ownership.rs b/integration-tests/src/tests/space_ownership.rs index d03f72ec..4553db70 100644 --- a/integration-tests/src/tests/space_ownership.rs +++ b/integration-tests/src/tests/space_ownership.rs @@ -7,7 +7,7 @@ use frame_support::{assert_ok, assert_noop}; use sp_runtime::traits::Zero; -use pallet_space_ownership::Error as SpaceOwnershipError; +use pallet_ownership::{OwnableEntity, Error as SpaceOwnershipError}; use pallet_spaces::Error as SpacesError; use crate::mock::*; @@ -20,7 +20,7 @@ fn transfer_space_ownership_should_work() { assert_ok!(_transfer_default_space_ownership()); // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2 assert_eq!( - SpaceOwnership::pending_space_owner(SPACE1).unwrap(), + SpaceOwnership::pending_ownership_transfer(OwnableEntity::Space(SPACE1)).unwrap(), ACCOUNT2 ); }); @@ -70,7 +70,7 @@ fn accept_pending_ownership_should_work() { assert_eq!(space.owner, ACCOUNT2); // Check that pending storage is cleared: - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); + assert!(SpaceOwnership::pending_ownership_transfer(OwnableEntity::Space(SPACE1)).is_none()); assert!(Balances::reserved_balance(ACCOUNT1).is_zero()); @@ -96,7 +96,7 @@ fn accept_pending_ownership_should_fail_when_no_pending_transfer_for_space() { ExtBuilder::build_with_space().execute_with(|| { assert_noop!( _accept_default_pending_ownership(), - SpaceOwnershipError::::NoPendingTransferOnSpace + SpaceOwnershipError::::NoPendingTransfer ); }); } @@ -108,7 +108,7 @@ fn accept_pending_ownership_should_fail_if_origin_is_already_an_owner() { assert_noop!( _accept_pending_ownership(Some(RuntimeOrigin::signed(ACCOUNT1)), None), - SpaceOwnershipError::::AlreadyASpaceOwner + SpaceOwnershipError::::NotAllowedToAcceptOwnershipTransfer, ); }); } @@ -137,7 +137,7 @@ fn reject_pending_ownership_should_work() { assert_eq!(space.owner, ACCOUNT1); // Check whether storage state is correct - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); + assert!(SpaceOwnership::pending_ownership_transfer(OwnableEntity::Space(SPACE1)).is_none()); }); } @@ -153,7 +153,7 @@ fn reject_pending_ownership_should_work_when_proposal_rejected_by_current_space_ assert_eq!(space.owner, ACCOUNT1); // Check whether storage state is correct - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); + assert!(SpaceOwnership::pending_ownership_transfer(OwnableEntity::Space(SPACE1)).is_none()); }); } @@ -172,7 +172,7 @@ fn reject_pending_ownership_should_fail_when_no_pending_transfer_on_space() { ExtBuilder::build_with_space().execute_with(|| { assert_noop!( _reject_default_pending_ownership(), - SpaceOwnershipError::::NoPendingTransferOnSpace + SpaceOwnershipError::::NoPendingTransfer ); // Rejecting a transfer from ACCOUNT2 }); } diff --git a/integration-tests/src/utils/space_ownership_utils.rs b/integration-tests/src/utils/space_ownership_utils.rs index 7552b9f6..49aa955d 100644 --- a/integration-tests/src/utils/space_ownership_utils.rs +++ b/integration-tests/src/utils/space_ownership_utils.rs @@ -5,6 +5,7 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE use frame_support::pallet_prelude::*; +use pallet_ownership::OwnableEntity; use subsocial_support::SpaceId; @@ -20,9 +21,9 @@ pub(crate) fn _transfer_space_ownership( space_id: Option, transfer_to: Option, ) -> DispatchResult { - SpaceOwnership::transfer_space_ownership( + SpaceOwnership::transfer_ownership( origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT1)), - space_id.unwrap_or(SPACE1), + space_id.map_or(OwnableEntity::Space(SPACE1), |id| OwnableEntity::Space(id)), transfer_to.unwrap_or(ACCOUNT2), ) } @@ -34,7 +35,7 @@ pub(crate) fn _accept_default_pending_ownership() -> DispatchResult { pub(crate) fn _accept_pending_ownership(origin: Option, space_id: Option) -> DispatchResult { SpaceOwnership::accept_pending_ownership( origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT2)), - space_id.unwrap_or(SPACE1), + space_id.map_or(OwnableEntity::Space(SPACE1), |id| OwnableEntity::Space(id)), ) } @@ -49,6 +50,6 @@ pub(crate) fn _reject_default_pending_ownership_by_current_owner() -> DispatchRe pub(crate) fn _reject_pending_ownership(origin: Option, space_id: Option) -> DispatchResult { SpaceOwnership::reject_pending_ownership( origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT2)), - space_id.unwrap_or(SPACE1), + space_id.map_or(OwnableEntity::Space(SPACE1), |id| OwnableEntity::Space(id)), ) } diff --git a/pallets/creator-staking/src/lib.rs b/pallets/creator-staking/src/lib.rs index d22f3abc..5fd772e2 100644 --- a/pallets/creator-staking/src/lib.rs +++ b/pallets/creator-staking/src/lib.rs @@ -30,7 +30,7 @@ pub mod pallet { use sp_runtime::{traits::Zero, Perbill, Saturating}; use pallet_permissions::SpacePermissionsInfoOf; - use subsocial_support::{traits::{SpacesInterface, SpacePermissionsProvider}, SpaceId}; + use subsocial_support::{traits::{SpacesProvider, SpacePermissionsProvider}, SpaceId}; pub use crate::types::*; @@ -54,7 +54,7 @@ pub mod pallet { type Currency: LockableCurrency + ReservableCurrency; - type SpacesInterface: SpacesInterface; + type SpacesProvider: SpacesProvider; type SpacePermissionsProvider: SpacePermissionsProvider< Self::AccountId, @@ -341,7 +341,7 @@ pub mod pallet { Error::::CreatorAlreadyRegistered, ); - let space_owner = T::SpacesInterface::get_space_owner(space_id)?; + let space_owner = T::SpacesProvider::get_space_owner(space_id)?; T::Currency::reserve(&space_owner, T::CreatorRegistrationDeposit::get())?; RegisteredCreators::::insert(space_id, CreatorInfo::new(space_owner.clone())); diff --git a/pallets/creator-staking/src/tests/mock.rs b/pallets/creator-staking/src/tests/mock.rs index bd81af82..546f28a1 100644 --- a/pallets/creator-staking/src/tests/mock.rs +++ b/pallets/creator-staking/src/tests/mock.rs @@ -126,7 +126,7 @@ impl pallet_timestamp::Config for TestRuntime { use pallet_permissions::default_permissions::DefaultSpacePermissions; use pallet_permissions::SpacePermissionsInfoOf; use subsocial_support::{Content, SpaceId}; -use subsocial_support::traits::{SpacePermissionsProvider, SpacesInterface}; +use subsocial_support::traits::{SpacePermissionsProvider, SpacesProvider}; use crate::tests::tests::Rewards; impl pallet_permissions::Config for TestRuntime { @@ -158,8 +158,10 @@ mock! { fn ensure_space_owner(id: SpaceId, account: &AccountId) -> DispatchResult; } - impl SpacesInterface for Spaces { + impl SpacesProvider for Spaces { fn get_space_owner(_space_id: SpaceId) -> Result; + + fn do_update_space_owner(_space_id: SpaceId, _new_owner: AccountId) -> DispatchResult; fn create_space(_owner: &AccountId, _content: Content) -> Result; } @@ -182,7 +184,7 @@ impl pallet_creator_staking::Config for TestRuntime { type PalletId = CreatorStakingPalletId; type BlockPerEra = BlockPerEra; type Currency = Balances; - type SpacesInterface = MockSpaces; + type SpacesProvider = MockSpaces; type SpacePermissionsProvider = MockSpaces; type CreatorRegistrationDeposit = CreatorRegistrationDeposit; type MinimumTotalStake = MinimumTotalStake; diff --git a/pallets/domains/Cargo.toml b/pallets/domains/Cargo.toml index 6cce0c44..b6b0b80d 100644 --- a/pallets/domains/Cargo.toml +++ b/pallets/domains/Cargo.toml @@ -33,7 +33,10 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot- [features] default = ["std"] -runtime-benchmarks = ["frame-benchmarking"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "subsocial-support/runtime-benchmarks", +] std = [ "codec/std", "scale-info/std", diff --git a/pallets/domains/src/lib.rs b/pallets/domains/src/lib.rs index 000e6926..1ded0e0e 100644 --- a/pallets/domains/src/lib.rs +++ b/pallets/domains/src/lib.rs @@ -44,7 +44,7 @@ pub mod pallet { use sp_runtime::traits::{Saturating, StaticLookup, Zero}; use sp_std::{cmp::Ordering, convert::TryInto, vec::Vec}; - use subsocial_support::ensure_content_is_valid; + use subsocial_support::{ensure_content_is_valid, remove_from_bounded_vec, traits::DomainsProvider}; #[pallet::config] pub trait Config: frame_system::Config + pallet_timestamp::Config { @@ -193,9 +193,10 @@ pub mod pallet { TooManyDomainsPerAccount, /// This domain label may contain only a-z, 0-9 and hyphen characters. DomainContainsInvalidChar, - /// This domain name length must be within the limits defined by - /// [`Config::MinDomainLength`] and [`Config::MaxDomainLength`] characters, inclusive. + /// The length of this domain name must be greater than or equal to the [`Config::MinDomainLength`] limit. DomainIsTooShort, + /// The length of this domain name must be less than or equal to the [`Config::MaxDomainLength`] limit. + DomainIsTooLong, /// This domain has expired. DomainHasExpired, /// Domain was not found by the domain name. @@ -468,11 +469,7 @@ pub mod pallet { Error::::DomainAlreadyOwned, ); - let recipient_domains_count = Self::domains_by_owner(&recipient).len(); - ensure!( - recipient_domains_count < T::MaxDomainsPerAccount::get() as usize, - Error::::TooManyDomainsPerAccount, - ); + Self::ensure_domains_limit_not_reached(&recipient)?; let deposit_info: DomainDeposit> = (caller.clone(), T::BaseDomainDeposit::get()).into(); @@ -555,6 +552,15 @@ pub mod pallet { ); Ok(()) } + + /// Throws an error if domain length exceeds the `DomainName` BoundedVec size limit. + fn ensure_valid_domain_length(domain: &[u8]) -> DispatchResult { + ensure!( + domain.len() <= T::MaxDomainLength::get() as usize, + Error::::DomainIsTooLong, + ); + Ok(()) + } /// Throws an error if domain contains invalid character. fn ensure_domain_contains_valid_chars(domain: &[u8]) -> DispatchResult { @@ -637,21 +643,32 @@ pub mod pallet { Ok(domains.len() as u32) } - /// Try to get domain meta by it's custom and top-level domain names. + /// Try to get domain meta by its custom and top-level domain names. pub fn require_domain(domain: DomainName) -> Result, DispatchError> { Ok(Self::registered_domain(domain).ok_or(Error::::DomainNotFound)?) } + /// Try to get domain meta by its custom and top-level domain names. + /// This function pre-validates the passed argument. + pub fn require_domain_by_ref(domain: &[u8]) -> Result, DispatchError> { + // Because of BoundedVec implementation speicifics, we need to + // check that the domain length does not exceed the BoundedVec size + // before converting from &[u8] to DomainName in `lower_domain_then_bound`, + Self::ensure_valid_domain_length(domain)?; + let domain_lc = Self::lower_domain_then_bound(domain); + + Self::require_domain(domain_lc) + } + /// Check that the domain is not expired and [sender] is the current owner. pub fn ensure_allowed_to_update_domain( domain_meta: &DomainMeta, sender: &T::AccountId, ) -> DispatchResult { - let DomainMeta { owner, .. } = domain_meta; - // FIXME: this is hotfix, handle expired domains correctly! // ensure!(&System::::block_number() < expires_at, Error::::DomainHasExpired); - ensure!(sender == owner, Error::::NotDomainOwner); + + ensure!(domain_meta.is_owner(sender), Error::::NotDomainOwner); Ok(()) } @@ -717,5 +734,78 @@ pub mod pallet { &owner, price, WithdrawReasons::TRANSFER, owner_balance.saturating_sub(total_price) ) } + + fn ensure_domains_limit_not_reached(account: &T::AccountId) -> DispatchResult { + let domains_count = Self::domains_by_owner(account).len(); + ensure!( + domains_count < T::MaxDomainsPerAccount::get() as usize, + Error::::TooManyDomainsPerAccount, + ); + Ok(()) + } + } + + impl DomainsProvider for Pallet { + type MaxDomainLength = T::MaxDomainLength; + + fn get_domain_owner(domain: &[u8]) -> Result { + let meta = Self::require_domain_by_ref(domain)?; + Ok(meta.owner) + } + + fn ensure_domain_owner(domain: &[u8], account: &T::AccountId) -> DispatchResult { + let meta = Self::require_domain_by_ref(domain)?; + ensure!(meta.is_owner(account), Error::::NotDomainOwner); + Ok(()) + } + + fn do_update_domain_owner(domain: &[u8], new_owner: &T::AccountId) -> DispatchResult { + let meta = Self::require_domain_by_ref(domain)?; + if meta.is_owner(new_owner) { + return Ok(()); + } + + let domain_lc = Self::lower_domain_then_bound(domain); + + Self::ensure_domains_limit_not_reached(&new_owner)?; + Self::ensure_can_pay_for_domain(&new_owner, Zero::zero(), meta.domain_deposit.deposit)?; + + if !meta.domain_deposit.deposit.is_zero() { + T::Currency::reserve(&new_owner, meta.domain_deposit.deposit)?; + T::Currency::unreserve(&meta.domain_deposit.depositor, meta.domain_deposit.deposit); + } + + DomainsByOwner::::mutate(&meta.owner, |domains| { + remove_from_bounded_vec(domains, domain_lc.clone()) + }); + + DomainsByOwner::::mutate(&new_owner, |domains| { + domains.try_push(domain_lc.clone()).expect("qed; too many domains per account") + }); + + RegisteredDomains::::mutate(&domain_lc, |stored_meta_opt| { + if let Some(stored_meta) = stored_meta_opt { + stored_meta.owner = new_owner.clone(); + } + }); + + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn register_domain(owner: &T::AccountId, domain: &[u8]) -> Result, DispatchError> { + Self::ensure_valid_domain_length(domain)?; + let domain_lc = Self::lower_domain_then_bound(domain); + + Self::do_register_domain( + owner.clone(), + owner.clone(), + domain_lc.clone(), + Content::None, + T::RegistrationPeriodLimit::get(), + )?; + + Ok(domain_lc.into()) + } } } diff --git a/pallets/domains/src/types.rs b/pallets/domains/src/types.rs index 7111a33c..1d778cae 100644 --- a/pallets/domains/src/types.rs +++ b/pallets/domains/src/types.rs @@ -103,6 +103,10 @@ impl DomainMeta { outer_value_deposit: Zero::zero(), } } + + pub fn is_owner(&self, account: &T::AccountId) -> bool { + self.owner == *account + } } impl From<(AccountId, Balance)> for DomainDeposit { diff --git a/pallets/posts/src/functions.rs b/pallets/posts/src/functions.rs index 1670a340..fc5cbca1 100644 --- a/pallets/posts/src/functions.rs +++ b/pallets/posts/src/functions.rs @@ -8,6 +8,7 @@ use frame_support::dispatch::DispatchResult; use sp_runtime::traits::Saturating; use subsocial_support::{remove_from_vec, SpaceId}; +use subsocial_support::traits::PostsProvider; use super::*; @@ -143,6 +144,11 @@ impl Pallet { post: &Post, space: &Space, ) -> DispatchResult { + ensure!( + T::IsAccountBlocked::is_allowed_account(editor.clone(), space.id), + ModerationError::AccountIsBlocked + ); + let is_owner = post.is_owner(editor); let is_comment = post.is_comment(); @@ -453,3 +459,44 @@ impl Pallet { Ok(()) } } + +impl PostsProvider for Pallet { + fn get_post_owner(post_id: PostId) -> Result { + let post = Self::require_post(post_id)?; + Ok(post.owner) + } + + fn ensure_post_owner(post_id: PostId, account: &T::AccountId) -> DispatchResult { + let post = Self::require_post(post_id)?; + ensure!(post.is_owner(account), Error::::NotAPostOwner); + Ok(()) + } + + fn do_update_post_owner(post_id: PostId, new_owner: &T::AccountId) -> DispatchResult { + let post = Self::require_post(post_id)?; + if post.is_owner(new_owner) { + return Ok(()) + } + + PostById::::mutate(post_id, |stored_post_opt| { + if let Some(stored_post) = stored_post_opt { + stored_post.owner = new_owner.clone(); + } + }); + + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_post(owner: &T::AccountId, space_id: SpaceId, content: Content) -> Result { + let new_post_id = Self::next_post_id(); + let new_post: Post = + Post::new(new_post_id, owner.clone(), Some(space_id), PostExtension::RegularPost, content.clone()); + + PostById::insert(new_post_id, new_post); + PostIdsBySpaceId::::mutate(space_id, |ids| ids.push(new_post_id)); + NextPostId::::mutate(|n| n.saturating_inc()); + + Ok(new_post_id) + } +} diff --git a/pallets/posts/src/lib.rs b/pallets/posts/src/lib.rs index c9e23ee6..8d1bb2be 100644 --- a/pallets/posts/src/lib.rs +++ b/pallets/posts/src/lib.rs @@ -275,10 +275,6 @@ pub mod pallet { let space_opt = &post.try_get_space(); if let Some(space) = space_opt { - ensure!( - T::IsAccountBlocked::is_allowed_account(editor.clone(), space.id), - ModerationError::AccountIsBlocked - ); Self::ensure_account_can_update_post(&editor, &post, space)?; } diff --git a/pallets/posts/tests/Cargo.toml b/pallets/posts/tests/Cargo.toml index 9449c692..daeb5a96 100644 --- a/pallets/posts/tests/Cargo.toml +++ b/pallets/posts/tests/Cargo.toml @@ -29,22 +29,25 @@ sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkad sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } [dev-dependencies] +frame-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } sp-core = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } sp-io = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } pallet-roles = { default-features = false, path = '../../roles' } pallet-space-follows = { default-features = false, path = '../../space-follows' } -pallet-space-ownership = { default-features = false, path = '../../space-ownership' } +pallet-ownership = { default-features = false, path = '../../space-ownership' } pallet-profiles = { default-features = false, path = '../../profiles' } pallet-posts = { default-features = false, path = '..' } pallet-spaces = { default-features = false, path = '../../spaces' } [features] default = ['std'] +runtime-benchmarks = ['subsocial-support/runtime-benchmarks'] std = [ 'codec/std', 'scale-info/std', 'pallet-timestamp/std', + 'frame-benchmarking/std', 'frame-support/std', 'frame-system/std', 'sp-runtime/std', @@ -53,7 +56,7 @@ std = [ 'pallet-balances/std', 'pallet-roles/std', 'pallet-space-follows/std', - 'pallet-space-ownership/std', + 'pallet-ownership/std', 'pallet-profiles/std', 'pallet-posts/std', ] diff --git a/pallets/posts/tests/src/mock.rs b/pallets/posts/tests/src/mock.rs index 747142c1..dfb715c3 100644 --- a/pallets/posts/tests/src/mock.rs +++ b/pallets/posts/tests/src/mock.rs @@ -5,12 +5,11 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE use frame_support::{pallet_prelude::ConstU32, parameter_types, traits::Everything}; +use frame_support::dispatch::DispatchResult; use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; +use sp_runtime::{DispatchError, testing::Header, traits::{BlakeTwo256, IdentityLookup}}; use sp_std::convert::{TryFrom, TryInto}; +use subsocial_support::traits::DomainsProvider; use crate::tests_utils::*; @@ -32,7 +31,7 @@ frame_support::construct_runtime!( SpaceFollows: pallet_space_follows, Posts: pallet_posts, Spaces: pallet_spaces, - SpaceOwnership: pallet_space_ownership, + Ownership: pallet_ownership, } ); @@ -131,7 +130,7 @@ impl pallet_roles::Config for Test { impl pallet_profiles::Config for Test { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = Spaces; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type WeightInfo = (); } @@ -150,9 +149,39 @@ impl pallet_space_follows::Config for Test { type WeightInfo = (); } -impl pallet_space_ownership::Config for Test { +parameter_types! { + pub const MaxDomainLength: u32 = 64; +} +pub struct MockEmptyDomainsProvider; +impl DomainsProvider for MockEmptyDomainsProvider { + type MaxDomainLength = MaxDomainLength; + + fn get_domain_owner(_domain: &[u8]) -> Result { + Ok(ACCOUNT1) + } + + fn ensure_domain_owner(_domain: &[u8], _account: &AccountId) -> DispatchResult { + Ok(()) + } + + fn do_update_domain_owner(_domain: &[u8], _new_owner: &AccountId) -> DispatchResult { + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn register_domain(_owner: &AccountId, _domain: &[u8]) -> Result, DispatchError> { + Ok(Vec::new()) + } +} + +impl pallet_ownership::Config for Test { type RuntimeEvent = RuntimeEvent; type ProfileManager = Profiles; + type SpacesProvider = Spaces; + type SpacePermissionsProvider = Spaces; type CreatorStakingProvider = (); + type DomainsProvider = MockEmptyDomainsProvider; + type PostsProvider = Posts; + type Currency = Balances; type WeightInfo = (); } diff --git a/pallets/posts/tests/src/tests_utils.rs b/pallets/posts/tests/src/tests_utils.rs index b71d2a55..326ddc45 100644 --- a/pallets/posts/tests/src/tests_utils.rs +++ b/pallets/posts/tests/src/tests_utils.rs @@ -13,6 +13,7 @@ use std::{ use frame_support::{assert_ok, pallet_prelude::*}; use sp_core::storage::Storage; use sp_io::TestExternalities; +use pallet_ownership::OwnableEntity; use pallet_permissions::{SpacePermission as SP, SpacePermission, SpacePermissions}; use pallet_posts::{Comment, PostExtension, PostUpdate}; @@ -486,9 +487,9 @@ pub(crate) fn _transfer_space_ownership( space_id: Option, transfer_to: Option, ) -> DispatchResult { - SpaceOwnership::transfer_space_ownership( + Ownership::transfer_ownership( origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT1)), - space_id.unwrap_or(SPACE1), + space_id.map_or(OwnableEntity::Space(SPACE1), |id| OwnableEntity::Space(id)), transfer_to.unwrap_or(ACCOUNT2), ) } diff --git a/pallets/profiles/src/benchmarking.rs b/pallets/profiles/src/benchmarking.rs index f94205df..f3b47edf 100644 --- a/pallets/profiles/src/benchmarking.rs +++ b/pallets/profiles/src/benchmarking.rs @@ -10,13 +10,13 @@ use crate::Pallet as Profiles; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::dispatch::DispatchError; use frame_system::RawOrigin; -use subsocial_support::{traits::SpacesInterface, Content, SpaceId}; +use subsocial_support::{traits::SpacesProvider, Content, SpaceId}; fn create_space( owner: &T::AccountId, content: Content, ) -> Result { - let space_id = T::SpacesInterface::create_space(owner, content)?; + let space_id = T::SpacesProvider::create_space(owner, content)?; Ok(space_id) } diff --git a/pallets/profiles/src/lib.rs b/pallets/profiles/src/lib.rs index b7d38e80..664785e3 100644 --- a/pallets/profiles/src/lib.rs +++ b/pallets/profiles/src/lib.rs @@ -29,7 +29,7 @@ pub mod pallet { use pallet_permissions::SpacePermissions; use subsocial_support::{ - traits::{ProfileManager, SpacePermissionsProvider, SpacesInterface}, + traits::{ProfileManager, SpacePermissionsProvider, SpacesProvider}, Content, SpaceId, SpacePermissionsInfo, }; @@ -46,7 +46,7 @@ pub mod pallet { SpacePermissionsInfoOf, >; - type SpacesInterface: SpacesInterface; + type SpacesProvider: SpacesProvider; type WeightInfo: WeightInfo; } @@ -104,7 +104,7 @@ pub mod pallet { pub fn create_space_as_profile(origin: OriginFor, content: Content) -> DispatchResult { let sender = ensure_signed(origin)?; - let space_id = T::SpacesInterface::create_space(&sender, content)?; + let space_id = T::SpacesProvider::create_space(&sender, content)?; Self::do_set_profile(&sender, space_id)?; diff --git a/pallets/profiles/src/mock.rs b/pallets/profiles/src/mock.rs index 0f7f391f..9ff9b664 100644 --- a/pallets/profiles/src/mock.rs +++ b/pallets/profiles/src/mock.rs @@ -19,7 +19,7 @@ use sp_runtime::{ }; use sp_std::sync::{Mutex, MutexGuard}; use subsocial_support::{ - traits::{SpacePermissionsProvider, SpacesInterface}, + traits::{SpacePermissionsProvider, SpacesProvider}, Content, SpaceId, }; @@ -86,8 +86,10 @@ mock! { fn ensure_space_owner(id: SpaceId, account: &AccountId) -> DispatchResult; } - impl SpacesInterface for Spaces { + impl SpacesProvider for Spaces { fn get_space_owner(_space_id: SpaceId) -> Result; + + fn do_update_space_owner(_space_id: SpaceId, _new_owner: AccountId) -> DispatchResult; fn create_space(_owner: &AccountId, _content: Content) -> Result; } @@ -96,7 +98,7 @@ mock! { impl pallet_profiles::Config for Test { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = MockSpaces; - type SpacesInterface = MockSpaces; + type SpacesProvider = MockSpaces; type WeightInfo = (); } diff --git a/pallets/roles/src/benchmarking.rs b/pallets/roles/src/benchmarking.rs index 1ab88d6c..739da384 100644 --- a/pallets/roles/src/benchmarking.rs +++ b/pallets/roles/src/benchmarking.rs @@ -8,7 +8,7 @@ #![cfg(feature = "runtime-benchmarks")] -// FIXME: refactor once SpacesInterface is added. +// FIXME: refactor once SpacesProvider is added. use super::*; use frame_benchmarking::{account, benchmarks}; diff --git a/pallets/space-ownership/Cargo.toml b/pallets/space-ownership/Cargo.toml index ccce28a6..c1c095d3 100644 --- a/pallets/space-ownership/Cargo.toml +++ b/pallets/space-ownership/Cargo.toml @@ -1,26 +1,31 @@ [package] -name = 'pallet-space-ownership' +name = 'pallet-ownership' version = '0.2.2' authors = ['DappForce '] edition = '2021' license = 'GPL-3.0-only' homepage = 'https://subsocial.network' repository = 'https://github.com/dappforce/subsocial-parachain' -description = 'Pallet to manage space ownership: transfer, accept, reject' +description = 'Pallet to manage spaces/posts/domains ownership: transfer, accept, reject' keywords = ['blockchain', 'cryptocurrency', 'social-network', 'news-feed', 'marketplace'] categories = ['cryptography::cryptocurrencies'] [features] default = ['std'] -runtime-benchmarks = ['frame-benchmarking/runtime-benchmarks'] +runtime-benchmarks = [ + 'frame-benchmarking/runtime-benchmarks', + 'subsocial-support/runtime-benchmarks', +] std = [ 'codec/std', 'scale-info/std', 'frame-benchmarking/std', 'frame-support/std', 'frame-system/std', + 'sp-io/std', + 'sp-runtime/std', 'sp-std/std', - 'pallet-spaces/std', + 'pallet-permissions/std', 'subsocial-support/std', ] try-runtime = ['frame-support/try-runtime'] @@ -30,11 +35,16 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = scale-info = { version = "2.2.0", default-features = false, features = ["derive"] } # Local dependencies -pallet-spaces = { default-features = false, path = '../spaces' } +pallet-permissions = { default-features = false, path = '../permissions' } subsocial-support = { default-features = false, path = '../support' } # Substrate dependencies frame-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false, optional = true } frame-support = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } frame-system = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } +sp-io = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } +sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } + +[dev-dependencies] +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } diff --git a/pallets/space-ownership/src/benchmarking.rs b/pallets/space-ownership/src/benchmarking.rs index 6d6223c5..a238fb8b 100644 --- a/pallets/space-ownership/src/benchmarking.rs +++ b/pallets/space-ownership/src/benchmarking.rs @@ -8,74 +8,153 @@ #![cfg(feature = "runtime-benchmarks")] -use super::*; use frame_benchmarking::{account, benchmarks}; -use frame_support::{dispatch::DispatchError, ensure}; +use frame_support::{dispatch::DispatchError, ensure, traits::Currency}; use frame_system::RawOrigin; -use pallet_spaces::types::Space; -use subsocial_support::Content; +use sp_runtime::traits::Bounded; -fn create_dummy_space( - origin: RawOrigin, -) -> Result, DispatchError> { - let space_id = pallet_spaces::NextSpaceId::::get(); +use subsocial_support::{ + traits::{DomainsProvider, PostsProvider, SpacesProvider}, + Content, +}; + +use super::*; + +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + +fn grab_accounts() -> (T::AccountId, T::AccountId) { + let acc1 = account::("Acc1", 1, 0); + let acc2 = account::("Acc2", 2, 0); + T::Currency::make_free_balance_be(&acc1, BalanceOf::::max_value()); + T::Currency::make_free_balance_be(&acc2, BalanceOf::::max_value()); + + (acc1, acc2) +} - pallet_spaces::Pallet::::create_space(origin.clone().into(), Content::None, None)?; +fn create_dummy_space( + owner: &T::AccountId, +) -> Result, DispatchError> { + let space_id = T::SpacesProvider::create_space(owner, Content::None)?; + Ok(OwnableEntity::Space(space_id)) +} - let space = pallet_spaces::SpaceById::::get(space_id) - .ok_or(DispatchError::Other("Space not found"))?; +fn create_dummy_post( + owner: &T::AccountId, +) -> Result, DispatchError> { + let space_id = T::SpacesProvider::create_space(owner, Content::None)?; + let post_id = T::PostsProvider::create_post(owner, space_id, Content::None)?; + Ok(OwnableEntity::Post(post_id)) +} - Ok(space) +fn create_dummy_domain( + owner: &T::AccountId, +) -> Result, DispatchError> { + let domain = T::DomainsProvider::register_domain(owner, "dappforce.sub".as_bytes())?; + let domain_bounded = domain.try_into().unwrap(); + Ok(OwnableEntity::Domain(domain_bounded)) } benchmarks! { transfer_space_ownership { - let acc1 = account::("Acc1", 1, 0); - let acc2 = account::("Acc2", 2, 0); + let (acc1, acc2) = grab_accounts::(); + let space_entity = create_dummy_space::(&acc1)?; + }: transfer_ownership(RawOrigin::Signed(acc1.clone()), space_entity.clone(), acc2.clone()) + verify { + ensure!( + PendingOwnershipTransfers::::get(&space_entity) == Some(acc2), + "Request was not created", + ); + } + + transfer_post_ownership { + let (acc1, acc2) = grab_accounts::(); + let post_entity = create_dummy_post::(&acc1)?; + }: transfer_ownership(RawOrigin::Signed(acc1.clone()), post_entity.clone(), acc2.clone()) + verify { + ensure!( + PendingOwnershipTransfers::::get(&post_entity) == Some(acc2), + "Request was not created", + ); + } - let space = create_dummy_space::(RawOrigin::Signed(acc1.clone()))?; - }: _(RawOrigin::Signed(acc1.clone()), space.id, acc2.clone()) + transfer_domain_ownership { + let (acc1, acc2) = grab_accounts::(); + let domain_entity = create_dummy_domain::(&acc1)?; + }: transfer_ownership(RawOrigin::Signed(acc1), domain_entity.clone(), acc2.clone()) + verify { + ensure!( + PendingOwnershipTransfers::::get(&domain_entity) == Some(acc2), + "Request was not created", + ); + } + + accept_pending_space_ownership_transfer { + let (acc1, acc2) = grab_accounts::(); + let space_entity = create_dummy_space::(&acc1)?; + + Pallet::::transfer_ownership( + RawOrigin::Signed(acc1).into(), + space_entity.clone(), + acc2.clone(), + )?; + }: accept_pending_ownership(RawOrigin::Signed(acc2), space_entity.clone()) verify { - ensure!(PendingSpaceOwner::::get(&space.id) == Some(acc2), "Request is not found"); + ensure!( + PendingOwnershipTransfers::::get(&space_entity).is_none(), + "Request was not cleaned", + ); } - accept_pending_ownership { - let acc1 = account::("Acc1", 1, 0); - let acc2 = account::("Acc2", 2, 0); + accept_pending_post_ownership_transfer { + let (acc1, acc2) = grab_accounts::(); + let post_entity = create_dummy_post::(&acc1)?; - let space = create_dummy_space::(RawOrigin::Signed(acc1.clone()))?; - Pallet::::transfer_space_ownership( - RawOrigin::Signed(acc1.clone()).into(), - space.id, + Pallet::::transfer_ownership( + RawOrigin::Signed(acc1).into(), + post_entity.clone(), acc2.clone(), )?; - }: _(RawOrigin::Signed(acc2.clone()), space.id) + }: accept_pending_ownership(RawOrigin::Signed(acc2), post_entity.clone()) verify { - let space = pallet_spaces::SpaceById::::get(space.id) - .ok_or(DispatchError::Other("Space not found"))?; + ensure!( + PendingOwnershipTransfers::::get(&post_entity).is_none(), + "Request was not cleaned", + ); + } - ensure!(PendingSpaceOwner::::get(&space.id) == None, "Request was not cleaned"); - ensure!(space.owner == acc2, "Space owner is not updated"); + accept_pending_domain_ownership_transfer { + let (acc1, acc2) = grab_accounts::(); + let domain_entity = create_dummy_domain::(&acc1)?; + + Pallet::::transfer_ownership( + RawOrigin::Signed(acc1).into(), + domain_entity.clone(), + acc2.clone(), + )?; + }: accept_pending_ownership(RawOrigin::Signed(acc2), domain_entity.clone()) + verify { + ensure!( + PendingOwnershipTransfers::::get(&domain_entity).is_none(), + "Request was not cleaned", + ); } reject_pending_ownership { - let acc1 = account::("Acc1", 1, 0); - let acc2 = account::("Acc2", 2, 0); + let (acc1, acc2) = grab_accounts::(); + let space_entity = create_dummy_space::(&acc1)?; - let space = create_dummy_space::(RawOrigin::Signed(acc1.clone()))?; - Pallet::::transfer_space_ownership( - RawOrigin::Signed(acc1.clone()).into(), - space.id, + Pallet::::transfer_ownership( + RawOrigin::Signed(acc1).into(), + space_entity.clone(), acc2.clone(), )?; - }: _(RawOrigin::Signed(acc2.clone()), space.id) + }: _(RawOrigin::Signed(acc2), space_entity.clone()) verify { - let space = pallet_spaces::SpaceById::::get(space.id) - .ok_or(DispatchError::Other("Space not found"))?; - - ensure!(PendingSpaceOwner::::get(&space.id) == None, "Request was not cleaned"); - ensure!(space.owner == acc1, "Space owner is updated"); + ensure!( + PendingOwnershipTransfers::::get(&space_entity).is_none(), + "Request was not cleaned", + ); } } diff --git a/pallets/space-ownership/src/lib.rs b/pallets/space-ownership/src/lib.rs index 20c8d6b4..b6769478 100644 --- a/pallets/space-ownership/src/lib.rs +++ b/pallets/space-ownership/src/lib.rs @@ -1,24 +1,24 @@ -#![cfg_attr(not(feature = "std"), no_std)] // Copyright (C) DAPPFORCE PTE. LTD. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0. // // Full notice is available at https://github.com/dappforce/subsocial-parachain/blob/main/COPYRIGHT // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE +//! # Ownership Module +//! +//! This module allows the transfer of ownership of entities such as spaces, posts, and domains. + +#![cfg_attr(not(feature = "std"), no_std)] use frame_system::ensure_signed; use sp_std::prelude::*; -use pallet_spaces::{Pallet as Spaces, SpaceById, SpaceIdsByOwner}; -use subsocial_support::{ - remove_from_bounded_vec, traits::IsAccountBlocked, ModerationError, SpaceId, -}; - pub use pallet::*; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; pub mod weights; +pub mod migration; #[frame_support::pallet] pub mod pallet { @@ -26,33 +26,60 @@ pub mod pallet { use crate::weights::WeightInfo; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - - use subsocial_support::traits::{CreatorStakingProvider, ProfileManager}; + use pallet_permissions::SpacePermissions; + + use subsocial_support::{PostId, SpaceId, SpacePermissionsInfo, traits::{CreatorStakingProvider, DomainsProvider, ProfileManager, SpacesProvider, PostsProvider, SpacePermissionsProvider}}; + + pub(crate) type DomainLengthOf = + <::DomainsProvider as DomainsProvider<::AccountId>>::MaxDomainLength; + + #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)] + #[scale_info(skip_type_params(T))] + pub enum OwnableEntity { + Space(SpaceId), + Post(PostId), + Domain(BoundedVec>), + } + + pub(crate) type SpacePermissionsInfoOf = + SpacePermissionsInfo<::AccountId, SpacePermissions>; #[pallet::config] - pub trait Config: frame_system::Config + pallet_spaces::Config { + pub trait Config: frame_system::Config + pallet_permissions::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type ProfileManager: ProfileManager; + type SpacesProvider: SpacesProvider; + + type SpacePermissionsProvider: SpacePermissionsProvider>; + type CreatorStakingProvider: CreatorStakingProvider; + type DomainsProvider: DomainsProvider; + + type PostsProvider: PostsProvider; + + type Currency: frame_support::traits::Currency; + type WeightInfo: WeightInfo; } + /// The current storage version + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(PhantomData); #[pallet::error] pub enum Error { - /// The current space owner cannot transfer ownership to themself. + /// The current entity owner cannot transfer ownership to themselves. CannotTransferToCurrentOwner, - /// Account is already an owner of a space. - AlreadyASpaceOwner, /// Cannot transfer ownership, because a space is registered as an active creator. ActiveCreatorCannotTransferOwnership, - /// There is no pending ownership transfer for a given space. - NoPendingTransferOnSpace, + /// There is no pending ownership transfer for a given entity. + NoPendingTransfer, /// Account is not allowed to accept ownership transfer. NotAllowedToAcceptOwnershipTransfer, /// Account is not allowed to reject ownership transfer. @@ -60,121 +87,129 @@ pub mod pallet { } #[pallet::storage] - #[pallet::getter(fn pending_space_owner)] - pub type PendingSpaceOwner = StorageMap<_, Twox64Concat, SpaceId, T::AccountId>; + #[pallet::getter(fn pending_ownership_transfer)] + pub type PendingOwnershipTransfers = + StorageMap<_, Twox64Concat, OwnableEntity, T::AccountId>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - SpaceOwnershipTransferCreated { + OwnershipTransferCreated { current_owner: T::AccountId, - space_id: SpaceId, + entity: OwnableEntity, new_owner: T::AccountId, }, - SpaceOwnershipTransferAccepted { + OwnershipTransferAccepted { account: T::AccountId, - space_id: SpaceId, + entity: OwnableEntity, }, - SpaceOwnershipTransferRejected { + OwnershipTransferRejected { account: T::AccountId, - space_id: SpaceId, + entity: OwnableEntity, }, } #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(::WeightInfo::transfer_space_ownership())] - pub fn transfer_space_ownership( + #[pallet::weight( + match entity { + OwnableEntity::Space(_) => T::WeightInfo::transfer_space_ownership(), + OwnableEntity::Post(_) => T::WeightInfo::transfer_post_ownership(), + OwnableEntity::Domain(_) => T::WeightInfo::transfer_domain_ownership(), + } + )] + pub fn transfer_ownership( origin: OriginFor, - space_id: SpaceId, - transfer_to: T::AccountId, + entity: OwnableEntity, + new_owner: T::AccountId, ) -> DispatchResult { - let who = ensure_signed(origin)?; - - let space = Spaces::::require_space(space_id)?; - space.ensure_space_owner(who.clone())?; - - ensure!(who != transfer_to, Error::::CannotTransferToCurrentOwner); - ensure!( - T::IsAccountBlocked::is_allowed_account(transfer_to.clone(), space_id), - ModerationError::AccountIsBlocked - ); - - Self::ensure_not_active_creator(space_id)?; - - PendingSpaceOwner::::insert(space_id, transfer_to.clone()); - - Self::deposit_event(Event::SpaceOwnershipTransferCreated { - current_owner: who, - space_id, - new_owner: transfer_to, + let current_owner = ensure_signed(origin)?; + + ensure!(current_owner != new_owner, Error::::CannotTransferToCurrentOwner); + + match entity.clone() { + OwnableEntity::Space(space_id) => { + T::SpacePermissionsProvider::ensure_space_owner(space_id, ¤t_owner)?; + Self::ensure_not_active_creator(space_id)?; + } + OwnableEntity::Post(post_id) => + T::PostsProvider::ensure_post_owner(post_id, ¤t_owner)?, + OwnableEntity::Domain(domain) => + T::DomainsProvider::ensure_domain_owner(&domain, ¤t_owner)?, + } + + PendingOwnershipTransfers::::insert(&entity, new_owner.clone()); + + Self::deposit_event(Event::OwnershipTransferCreated { + current_owner, + entity, + new_owner, }); Ok(()) } #[pallet::call_index(1)] - #[pallet::weight(::WeightInfo::accept_pending_ownership())] - pub fn accept_pending_ownership(origin: OriginFor, space_id: SpaceId) -> DispatchResult { - let new_owner = ensure_signed(origin)?; - - let mut space = Spaces::require_space(space_id)?; - ensure!(!space.is_owner(&new_owner), Error::::AlreadyASpaceOwner); - - let transfer_to = - Self::pending_space_owner(space_id).ok_or(Error::::NoPendingTransferOnSpace)?; - - ensure!(new_owner == transfer_to, Error::::NotAllowedToAcceptOwnershipTransfer); - - Self::ensure_not_active_creator(space_id)?; - - Spaces::::ensure_space_limit_not_reached(&transfer_to)?; - - // Here we know that the origin is eligible to become a new owner of this space. - PendingSpaceOwner::::remove(space_id); - - let old_owner = space.owner; - space.owner = new_owner.clone(); - SpaceById::::insert(space_id, space); - - T::ProfileManager::unlink_space_from_profile(&old_owner, space_id); - - // Remove space id from the list of spaces by old owner - SpaceIdsByOwner::::mutate(old_owner, |space_ids| { - remove_from_bounded_vec(space_ids, space_id) - }); - - // Add space id to the list of spaces by new owner - SpaceIdsByOwner::::mutate(new_owner.clone(), |ids| { - ids.try_push(space_id).expect("qed; too many spaces per account") - }); - - // TODO add a new owner as a space follower? See - // T::BeforeSpaceCreated::before_space_created(new_owner.clone(), space)?; - - Self::deposit_event(Event::SpaceOwnershipTransferAccepted { - account: new_owner, - space_id, + #[pallet::weight( + match entity { + OwnableEntity::Space(_) => T::WeightInfo::accept_pending_space_ownership_transfer(), + OwnableEntity::Post(_) => T::WeightInfo::accept_pending_post_ownership_transfer(), + OwnableEntity::Domain(_) => T::WeightInfo::accept_pending_domain_ownership_transfer(), + } + )] + pub fn accept_pending_ownership(origin: OriginFor, entity: OwnableEntity) -> DispatchResult { + let ownership_claimant = ensure_signed(origin)?; + + let pending_owner = + Self::pending_ownership_transfer(&entity).ok_or(Error::::NoPendingTransfer)?; + + ensure!(ownership_claimant == pending_owner, Error::::NotAllowedToAcceptOwnershipTransfer); + + match entity.clone() { + OwnableEntity::Space(space_id) => { + let previous_owner = T::SpacesProvider::get_space_owner(space_id)?; + + Self::ensure_not_active_creator(space_id)?; + + T::SpacesProvider::do_update_space_owner(space_id, pending_owner.clone())?; + T::ProfileManager::unlink_space_from_profile(&previous_owner, space_id); + } + OwnableEntity::Post(post_id) => + T::PostsProvider::do_update_post_owner(post_id, &pending_owner)?, + OwnableEntity::Domain(domain) => + T::DomainsProvider::do_update_domain_owner(&domain, &pending_owner)?, + } + + PendingOwnershipTransfers::::remove(&entity); + + Self::deposit_event(Event::OwnershipTransferAccepted { + account: pending_owner, + entity, }); Ok(()) } #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::reject_pending_ownership())] - pub fn reject_pending_ownership(origin: OriginFor, space_id: SpaceId) -> DispatchResult { + pub fn reject_pending_ownership(origin: OriginFor, entity: OwnableEntity) -> DispatchResult { let who = ensure_signed(origin)?; - let space = Spaces::::require_space(space_id)?; - let transfer_to = - Self::pending_space_owner(space_id).ok_or(Error::::NoPendingTransferOnSpace)?; + let pending_owner = + Self::pending_ownership_transfer(&entity).ok_or(Error::::NoPendingTransfer)?; + let current_owner = match entity.clone() { + OwnableEntity::Space(space_id) => T::SpacesProvider::get_space_owner(space_id), + OwnableEntity::Post(post_id) => T::PostsProvider::get_post_owner(post_id), + OwnableEntity::Domain(domain) => T::DomainsProvider::get_domain_owner(&domain), + }?; + ensure!( - who == transfer_to || who == space.owner, + who == pending_owner || who == current_owner, Error::::NotAllowedToRejectOwnershipTransfer ); - PendingSpaceOwner::::remove(space_id); + PendingOwnershipTransfers::::remove(&entity); - Self::deposit_event(Event::SpaceOwnershipTransferRejected { account: who, space_id }); + Self::deposit_event(Event::OwnershipTransferRejected { account: who, entity }); Ok(()) } } @@ -185,6 +220,7 @@ pub mod pallet { !T::CreatorStakingProvider::is_creator_active(creator_id), Error::::ActiveCreatorCannotTransferOwnership, ); + Ok(()) } } diff --git a/pallets/space-ownership/src/migration.rs b/pallets/space-ownership/src/migration.rs new file mode 100644 index 00000000..a96551c3 --- /dev/null +++ b/pallets/space-ownership/src/migration.rs @@ -0,0 +1,134 @@ +// Copyright (C) DAPPFORCE PTE. LTD. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0. +// +// Full notice is available at https://github.com/dappforce/subsocial-parachain/blob/main/COPYRIGHT +// Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE + +use frame_support::{log, traits::OnRuntimeUpgrade}; +#[cfg(feature = "try-runtime")] +use sp_runtime::traits::Zero; +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +use super::*; + +const LOG_TARGET: &'static str = "runtime::ownership"; + +pub mod v1 { + use frame_support::{ + pallet_prelude::*, weights::Weight, + }; + use sp_io::hashing::twox_128; + use sp_io::KillStorageResult; + + use super::*; + + pub struct MigrateToV1(sp_std::marker::PhantomData<(T, P, N)>); + + impl> OnRuntimeUpgrade + for MigrateToV1 + { + fn on_runtime_upgrade() -> Weight { + let current_version = Pallet::::current_storage_version(); + + let old_pallet_name = N::get(); + let old_pallet_prefix = twox_128(old_pallet_name.as_bytes()); + let old_pallet_has_data = sp_io::storage::next_key(&old_pallet_prefix).is_some(); + + let new_pallet_name =

::name(); + + log::info!( + target: LOG_TARGET, + "Running migration to clean-up the old pallet name {}", + old_pallet_name, + ); + + if old_pallet_has_data { + if new_pallet_name == old_pallet_name { + log::warn!( + target: LOG_TARGET, + "new ownership name is equal to the old one, migration won't run" + ); + return T::DbWeight::get().reads(1) + } + + current_version.put::>(); + + match sp_io::storage::clear_prefix(&old_pallet_prefix, None) { + KillStorageResult::SomeRemaining(remaining) => { + log::warn!( + target: LOG_TARGET, + "Some records from the old pallet {} have not been removed: {:?}", + old_pallet_name, + remaining + ); + } + KillStorageResult::AllRemoved(removed) => { + log::info!( + target: LOG_TARGET, + "Removed all the {} records from the old pallet {}", + removed, + old_pallet_name + ); + }, + } + + ::BlockWeights::get().max_block + } else { + log::info!( + target: LOG_TARGET, + "Migration did not execute. v1 upgrade should be removed" + ); + T::DbWeight::get().reads(1) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + let old_pallet_name = N::get().as_bytes(); + let old_pallet_prefix = twox_128(old_pallet_name); + + ensure!( + sp_io::storage::next_key(&old_pallet_prefix).is_some(), + "no data for the old pallet name has been detected; consider removing the migration" + ); + + Ok(Vec::new()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), &'static str> { + let old_pallet_name = N::get(); + let new_pallet_name =

::name(); + + // skip storage prefix checks for the same pallet names + if new_pallet_name == old_pallet_name { + return Ok(()); + } + + // Assert that nothing remains at the old prefix. + let old_pallet_prefix = twox_128(N::get().as_bytes()); + let old_pallet_prefix_iter = frame_support::storage::KeyPrefixIterator::new( + old_pallet_prefix.to_vec(), + old_pallet_prefix.to_vec(), + |_| Ok(()), + ); + ensure!( + old_pallet_prefix_iter.count().is_zero(), + "old pallet data hasn't been removed" + ); + + // Assert nothing redundant is left in the new prefix. + // NOTE: storage_version_key is already in the new prefix. + let new_pallet_prefix = twox_128(new_pallet_name.as_bytes()); + let new_pallet_prefix_iter = frame_support::storage::KeyPrefixIterator::new( + new_pallet_prefix.to_vec(), + new_pallet_prefix.to_vec(), + |_| Ok(()), + ); + assert_eq!(new_pallet_prefix_iter.count(), 1); + + Ok(()) + } + } +} diff --git a/pallets/space-ownership/src/weights.rs b/pallets/space-ownership/src/weights.rs index 05c67e33..b3d1ad04 100644 --- a/pallets/space-ownership/src/weights.rs +++ b/pallets/space-ownership/src/weights.rs @@ -5,37 +5,30 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE -//! Autogenerated weights for pallet_space_ownership +//! Autogenerated weights for pallet_ownership //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-02-15, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `benchmarks-ci`, CPU: `Intel(R) Xeon(R) Platinum 8280 CPU @ 2.70GHz` +//! DATE: 2024-02-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `MacBook-Pro-Vladislav.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: - // ./scripts/../target/release/subsocial-collator - // benchmark - // pallet - // --chain - // dev - // --execution - // wasm - // --wasm-execution - // Compiled - // --pallet - // pallet_space_ownership - // --extrinsic - // * - // --steps - // 50 - // --repeat - // 20 - // --heap-pages - // 4096 - // --output - // pallets/space-ownership/src/weights.rs - // --template - // ./.maintain/weight-template.hbs +// ./scripts/../target/release/subsocial-collator +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet +// pallet_ownership +// --extrinsic +// * +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=pallets/space-ownership/src/weights.rs +// --template=./.maintain/weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -45,70 +38,236 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_space_ownership. +/// Weight functions needed for pallet_ownership. pub trait WeightInfo { fn transfer_space_ownership() -> Weight; - fn accept_pending_ownership() -> Weight; + fn transfer_post_ownership() -> Weight; + fn transfer_domain_ownership() -> Weight; + fn accept_pending_space_ownership_transfer() -> Weight; + fn accept_pending_post_ownership_transfer() -> Weight; + fn accept_pending_domain_ownership_transfer() -> Weight; fn reject_pending_ownership() -> Weight; } -/// Weights for pallet_space_ownership using the Substrate node and recommended hardware. +/// Weights for pallet_ownership using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); - impl WeightInfo for SubstrateWeight { - // Storage: Spaces SpaceById (r:1 w:0) - // Storage: SpaceOwnership PendingSpaceOwner (r:0 w:1) - fn transfer_space_ownership() -> Weight { - // Minimum execution time: 36_442 nanoseconds. - Weight::from_parts(36_970_000, 0) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - // Storage: Spaces SpaceById (r:1 w:1) - // Storage: SpaceOwnership PendingSpaceOwner (r:1 w:1) - // Storage: Spaces SpaceIdsByOwner (r:2 w:2) - // Storage: Profiles ProfileSpaceIdByAccount (r:1 w:0) - fn accept_pending_ownership() -> Weight { - // Minimum execution time: 60_105 nanoseconds. - Weight::from_parts(60_893_000, 0) - .saturating_add(T::DbWeight::get().reads(5)) - .saturating_add(T::DbWeight::get().writes(4)) - } - // Storage: Spaces SpaceById (r:1 w:0) - // Storage: SpaceOwnership PendingSpaceOwner (r:1 w:1) - fn reject_pending_ownership() -> Weight { - // Minimum execution time: 40_600 nanoseconds. - Weight::from_parts(41_309_000, 0) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } +impl WeightInfo for SubstrateWeight { + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: CreatorStaking RegisteredCreators (r:1 w:0) + /// Proof: CreatorStaking RegisteredCreators (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_space_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1447` + // Estimated: `8430` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(21_000_000, 8430) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } + /// Storage: Posts PostById (r:1 w:0) + /// Proof Skipped: Posts PostById (max_values: None, max_size: None, mode: Measured) + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceFollows SpaceFollowedByAccount (r:1 w:0) + /// Proof Skipped: SpaceFollows SpaceFollowedByAccount (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_post_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1700` + // Estimated: `15495` + // Minimum execution time: 27_000_000 picoseconds. + Weight::from_parts(28_000_000, 15495) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Domains RegisteredDomains (r:1 w:0) + /// Proof Skipped: Domains RegisteredDomains (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_domain_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `282` + // Estimated: `3747` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3747) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Spaces SpaceById (r:1 w:1) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: CreatorStaking RegisteredCreators (r:1 w:0) + /// Proof: CreatorStaking RegisteredCreators (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: Spaces SpaceIdsByOwner (r:2 w:2) + /// Proof Skipped: Spaces SpaceIdsByOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: Profiles ProfileSpaceIdByAccount (r:1 w:0) + /// Proof: Profiles ProfileSpaceIdByAccount (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn accept_pending_space_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `1638` + // Estimated: `23290` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(37_000_000, 23290) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Posts PostById (r:1 w:1) + /// Proof Skipped: Posts PostById (max_values: None, max_size: None, mode: Measured) + fn accept_pending_post_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `403` + // Estimated: `7438` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(20_000_000, 7438) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Domains RegisteredDomains (r:1 w:1) + /// Proof Skipped: Domains RegisteredDomains (max_values: None, max_size: None, mode: Measured) + /// Storage: Domains DomainsByOwner (r:2 w:2) + /// Proof Skipped: Domains DomainsByOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn accept_pending_domain_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `688` + // Estimated: `20547` + // Minimum execution time: 56_000_000 picoseconds. + Weight::from_parts(57_000_000, 20547) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + fn reject_pending_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1595` + // Estimated: `8630` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 8630) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} - // For backwards compatibility and tests - impl WeightInfo for () { - // Storage: Spaces SpaceById (r:1 w:0) - // Storage: SpaceOwnership PendingSpaceOwner (r:0 w:1) - fn transfer_space_ownership() -> Weight { - // Minimum execution time: 36_442 nanoseconds. - Weight::from_parts(36_970_000, 0) - .saturating_add(RocksDbWeight::get().reads(1)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - // Storage: Spaces SpaceById (r:1 w:1) - // Storage: SpaceOwnership PendingSpaceOwner (r:1 w:1) - // Storage: Spaces SpaceIdsByOwner (r:2 w:2) - // Storage: Profiles ProfileSpaceIdByAccount (r:1 w:0) - fn accept_pending_ownership() -> Weight { - // Minimum execution time: 60_105 nanoseconds. - Weight::from_parts(60_893_000, 0) - .saturating_add(RocksDbWeight::get().reads(5)) - .saturating_add(RocksDbWeight::get().writes(4)) - } - // Storage: Spaces SpaceById (r:1 w:0) - // Storage: SpaceOwnership PendingSpaceOwner (r:1 w:1) - fn reject_pending_ownership() -> Weight { - // Minimum execution time: 40_600 nanoseconds. - Weight::from_parts(41_309_000, 0) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: CreatorStaking RegisteredCreators (r:1 w:0) + /// Proof: CreatorStaking RegisteredCreators (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_space_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1447` + // Estimated: `8430` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(21_000_000, 8430) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Posts PostById (r:1 w:0) + /// Proof Skipped: Posts PostById (max_values: None, max_size: None, mode: Measured) + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceFollows SpaceFollowedByAccount (r:1 w:0) + /// Proof Skipped: SpaceFollows SpaceFollowedByAccount (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_post_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1700` + // Estimated: `15495` + // Minimum execution time: 27_000_000 picoseconds. + Weight::from_parts(28_000_000, 15495) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Domains RegisteredDomains (r:1 w:0) + /// Proof Skipped: Domains RegisteredDomains (max_values: None, max_size: None, mode: Measured) + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:0 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + fn transfer_domain_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `282` + // Estimated: `3747` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3747) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Spaces SpaceById (r:1 w:1) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + /// Storage: CreatorStaking RegisteredCreators (r:1 w:0) + /// Proof: CreatorStaking RegisteredCreators (max_values: None, max_size: Some(53), added: 2528, mode: MaxEncodedLen) + /// Storage: Spaces SpaceIdsByOwner (r:2 w:2) + /// Proof Skipped: Spaces SpaceIdsByOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: Profiles ProfileSpaceIdByAccount (r:1 w:0) + /// Proof: Profiles ProfileSpaceIdByAccount (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + fn accept_pending_space_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `1638` + // Estimated: `23290` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(37_000_000, 23290) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Posts PostById (r:1 w:1) + /// Proof Skipped: Posts PostById (max_values: None, max_size: None, mode: Measured) + fn accept_pending_post_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `403` + // Estimated: `7438` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(20_000_000, 7438) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Domains RegisteredDomains (r:1 w:1) + /// Proof Skipped: Domains RegisteredDomains (max_values: None, max_size: None, mode: Measured) + /// Storage: Domains DomainsByOwner (r:2 w:2) + /// Proof Skipped: Domains DomainsByOwner (max_values: None, max_size: None, mode: Measured) + /// Storage: System Account (r:2 w:2) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn accept_pending_domain_ownership_transfer() -> Weight { + // Proof Size summary in bytes: + // Measured: `688` + // Estimated: `20547` + // Minimum execution time: 56_000_000 picoseconds. + Weight::from_parts(57_000_000, 20547) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: SpaceOwnership PendingOwnershipTransfers (r:1 w:1) + /// Proof: SpaceOwnership PendingOwnershipTransfers (max_values: None, max_size: Some(105), added: 2580, mode: MaxEncodedLen) + /// Storage: Spaces SpaceById (r:1 w:0) + /// Proof Skipped: Spaces SpaceById (max_values: None, max_size: None, mode: Measured) + fn reject_pending_ownership() -> Weight { + // Proof Size summary in bytes: + // Measured: `1595` + // Estimated: `8630` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 8630) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } +} \ No newline at end of file diff --git a/pallets/space-ownership/tests/Cargo.toml b/pallets/space-ownership/tests/Cargo.toml index ecc7b4b5..ce5fcc2c 100644 --- a/pallets/space-ownership/tests/Cargo.toml +++ b/pallets/space-ownership/tests/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = 'pallet-space-ownership-tests' +name = 'pallet-ownership-tests' version = '0.2.2' authors = ['DappForce '] edition = '2021' @@ -21,7 +21,7 @@ scale-info = { version = "2.2.0", default-features = false, features = ["derive" # Local dependencies subsocial-support = { default-features = false, path = '../../support' } pallet-permissions = { default-features = false, path = '../../permissions' } -pallet-space-ownership = { default-features = false, path = '..' } +pallet-ownership = { default-features = false, path = '..' } # Substrate dependencies pallet-timestamp = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } @@ -31,11 +31,16 @@ sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = 'polkad sp-std = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } [dev-dependencies] +mockall = '0.11.3' +lazy_static = '1.4.0' + sp-core = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } sp-io = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } -pallet-roles = { default-features = false, path = '../../roles' } +pallet-domains = { default-features = false, path = '../../domains' } +pallet-posts = { default-features = false, path = '../../posts' } pallet-profiles = { default-features = false, path = '../../profiles' } +pallet-roles = { default-features = false, path = '../../roles' } pallet-spaces = { default-features = false, path = '../../spaces' } pallet-space-follows = { default-features = false, path = '../../space-follows' } @@ -51,8 +56,8 @@ std = [ 'sp-std/std', 'pallet-permissions/std', 'pallet-balances/std', + 'pallet-profiles/std', 'pallet-roles/std', 'pallet-space-follows/std', - 'pallet-profiles/std', ] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/space-ownership/tests/src/mock.rs b/pallets/space-ownership/tests/src/mock.rs index 7b7c544d..8d051380 100644 --- a/pallets/space-ownership/tests/src/mock.rs +++ b/pallets/space-ownership/tests/src/mock.rs @@ -5,12 +5,19 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE use frame_support::{pallet_prelude::ConstU32, parameter_types, traits::Everything}; +use lazy_static::lazy_static; +use mockall::mock; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -use sp_std::convert::{TryFrom, TryInto}; +use sp_std::{ + convert::{TryFrom, TryInto}, + sync::{Mutex, MutexGuard}, +}; + +use subsocial_support::{traits::CreatorStakingProvider, SpaceId}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -28,8 +35,10 @@ frame_support::construct_runtime!( Roles: pallet_roles, Profiles: pallet_profiles, SpaceFollows: pallet_space_follows, - SpaceOwnership: pallet_space_ownership, + Ownership: pallet_ownership, Spaces: pallet_spaces, + Domains: pallet_domains, + Posts: pallet_posts, } ); @@ -37,6 +46,31 @@ pub(super) type AccountId = u64; pub(super) type Balance = u64; pub(super) type BlockNumber = u64; +pub(crate) const DOMAIN_DEPOSIT: Balance = 10; + +// Mocks + +// CreatorStakingProvider +mock! { + // This will generate MockSpaces + pub CreatorStaking {} + impl CreatorStakingProvider for CreatorStaking { + fn is_creator_active(creator_id: SpaceId) -> bool; + } +} + +lazy_static! { + static ref MTX: Mutex<()> = Mutex::new(()); +} + +// mockall crate requires synchronized access for the mocking of static methods. +pub(super) fn use_static_mock() -> MutexGuard<'static, ()> { + match MTX.lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + } +} + parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; @@ -117,7 +151,7 @@ impl pallet_roles::Config for Test { impl pallet_profiles::Config for Test { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = Spaces; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type WeightInfo = (); } @@ -136,9 +170,46 @@ impl pallet_space_follows::Config for Test { type WeightInfo = (); } -impl pallet_space_ownership::Config for Test { +impl pallet_posts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type MaxCommentDepth = ConstU32<10>; + type IsPostBlocked = (); + type WeightInfo = (); +} + +parameter_types! { + pub const MaxDomainLength: u32 = 64; + pub const BaseDomainDeposit: Balance = DOMAIN_DEPOSIT; + pub const OuterValueByteDeposit: Balance = 5; + pub const RegistrationPeriodLimit: BlockNumber = 5; + pub const InitialPaymentBeneficiary: AccountId = 1; + pub InitialPricesConfig: pallet_domains::types::PricesConfigVec = vec![(1, 100)]; +} + +impl pallet_domains::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type MinDomainLength = ConstU32<1>; + type MaxDomainLength = MaxDomainLength; + type MaxDomainsPerAccount = ConstU32<5>; + type DomainsInsertLimit = ConstU32<5>; + type RegistrationPeriodLimit = RegistrationPeriodLimit; + type MaxOuterValueLength = ConstU32<64>; + type BaseDomainDeposit = BaseDomainDeposit; + type OuterValueByteDeposit = OuterValueByteDeposit; + type InitialPaymentBeneficiary = InitialPaymentBeneficiary; + type InitialPricesConfig = InitialPricesConfig; + type WeightInfo = (); +} + +impl pallet_ownership::Config for Test { type RuntimeEvent = RuntimeEvent; type ProfileManager = Profiles; - type CreatorStakingProvider = (); + type SpacesProvider = Spaces; + type SpacePermissionsProvider = Spaces; + type CreatorStakingProvider = MockCreatorStaking; + type DomainsProvider = Domains; + type PostsProvider = Posts; + type Currency = Balances; type WeightInfo = (); } diff --git a/pallets/space-ownership/tests/src/tests.rs b/pallets/space-ownership/tests/src/tests.rs index 4ae0cece..9209ab70 100644 --- a/pallets/space-ownership/tests/src/tests.rs +++ b/pallets/space-ownership/tests/src/tests.rs @@ -5,169 +5,183 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::Zero; -use pallet_space_ownership::Error as SpaceOwnershipError; -use pallet_spaces::Error as SpacesError; +use pallet_ownership::{OwnableEntity, Error as OwnershipError, Event as OwnershipEvent}; use crate::{mock::*, tests_utils::*}; #[test] -fn transfer_space_ownership_should_work() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2 - - assert_eq!(SpaceOwnership::pending_space_owner(SPACE1).unwrap(), ACCOUNT2); - }); +fn transfer_ownership_works() { + ExtBuilder::build_with_pending_transfers(); } #[test] -fn transfer_space_ownership_should_fail_when_space_not_found() { - ExtBuilder::build().execute_with(|| { - assert_noop!(_transfer_default_space_ownership(), SpacesError::::SpaceNotFound); +fn accept_pending_ownership_works() { + ExtBuilder::build_with_pending_transfers().execute_with(|| { + let _m = use_static_mock(); + let creator_staking_ctx = MockCreatorStaking::is_creator_active_context(); + + // `is_creator_active` should return `false`. + creator_staking_ctx.expect().returning(|_| false).once(); + + // Mock accepting ownership transfer for a space entity + assert_ok!(Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT2), + default_space_entity(), + )); + + // Mock accepting ownership transfer for a post entity + assert_ok!(Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT2), + default_post_entity(), + )); + + // Mock accepting ownership transfer for a domain entity + assert_ok!(Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT2), + default_domain_entity(), + )); + + System::assert_has_event(OwnershipEvent::OwnershipTransferAccepted { + account: ACCOUNT2, + entity: default_space_entity(), + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferAccepted { + account: ACCOUNT2, + entity: default_post_entity(), + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferAccepted { + account: ACCOUNT2, + entity: default_domain_entity(), + }.into()); }); } #[test] -fn transfer_space_ownership_should_fail_when_account_is_not_space_owner() { - ExtBuilder::build_with_space().execute_with(|| { - assert_noop!( - _transfer_space_ownership(Some(RuntimeOrigin::signed(ACCOUNT2)), None, Some(ACCOUNT1)), - SpacesError::::NotASpaceOwner - ); +fn reject_pending_ownership_works() { + ExtBuilder::build_with_pending_transfers().execute_with(|| { + // Rejecting ownership transfer by the current owner + assert_reject_transfers_ok(ACCOUNT1); + + // Re-create the pending transfers + assert_create_transfers_ok(); + + // Rejecting ownership transfer by a target account + assert_reject_transfers_ok(ACCOUNT2); }); } #[test] -fn transfer_space_ownership_should_fail_when_trying_to_transfer_to_current_owner() { - ExtBuilder::build_with_space().execute_with(|| { +fn transfer_ownership_should_not_allow_transfer_to_current_owner() { + ExtBuilder::build_with_all_enitities().execute_with(|| { + // Mock a transfer from account 1 to itself for a space entity assert_noop!( - _transfer_space_ownership(Some(RuntimeOrigin::signed(ACCOUNT1)), None, Some(ACCOUNT1)), - SpaceOwnershipError::::CannotTransferToCurrentOwner + Ownership::transfer_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Space(SPACE1), + ACCOUNT1 + ), + OwnershipError::::CannotTransferToCurrentOwner ); }); } #[test] -fn accept_pending_ownership_should_work() { - ExtBuilder::build_with_space().execute_with(|| { - // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2: - assert_ok!(_transfer_default_space_ownership()); - - // Account 2 accepts the transfer of ownership: - assert_ok!(_accept_default_pending_ownership()); - - // Check that Account 2 is a new space owner: - let space = Spaces::space_by_id(SPACE1).unwrap(); - assert_eq!(space.owner, ACCOUNT2); - - // Check that pending storage is cleared: - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); +fn transfer_ownership_should_not_allow_active_creator_to_transfer_space_ownership() { + ExtBuilder::build_with_all_enitities().execute_with(|| { + let _m = use_static_mock(); + let creator_staking_ctx = MockCreatorStaking::is_creator_active_context(); - assert!(Balances::reserved_balance(ACCOUNT1).is_zero()); + // `is_creator_active` should return `true`. + creator_staking_ctx.expect().returning(|_| true).once(); - // assert_eq!(Balances::reserved_balance(ACCOUNT2), HANDLE_DEPOSIT); - }); -} - -#[test] -fn accept_pending_ownership_should_fail_when_space_not_found() { - ExtBuilder::build_with_pending_ownership_transfer_no_space().execute_with(|| { - assert_noop!(_accept_default_pending_ownership(), SpacesError::::SpaceNotFound); - }); -} - -#[test] -fn accept_pending_ownership_should_fail_when_no_pending_transfer_for_space() { - ExtBuilder::build_with_space().execute_with(|| { + // Mock a transfer from an active creator (creator is active in this test) assert_noop!( - _accept_default_pending_ownership(), - SpaceOwnershipError::::NoPendingTransferOnSpace + Ownership::transfer_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Space(SPACE1), + ACCOUNT2 + ), + OwnershipError::::ActiveCreatorCannotTransferOwnership ); }); } #[test] -fn accept_pending_ownership_should_fail_if_origin_is_already_an_owner() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); - +fn accept_pending_ownership_should_fail_if_no_pending_transfer() { + ExtBuilder::build_with_all_enitities().execute_with(|| { + // Mock accepting ownership transfer for a space entity with no pending transfer assert_noop!( - _accept_pending_ownership(Some(RuntimeOrigin::signed(ACCOUNT1)), None), - SpaceOwnershipError::::AlreadyASpaceOwner + Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT2), + OwnableEntity::Space(SPACE1) + ), + OwnershipError::::NoPendingTransfer ); }); } #[test] -fn accept_pending_ownership_should_fail_if_origin_is_not_equal_to_pending_account() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); +fn accept_pending_ownership_should_not_allow_non_target_account_to_accept() { + ExtBuilder::build_with_pending_transfers().execute_with(|| { + let _m = use_static_mock(); + let creator_staking_ctx = MockCreatorStaking::is_creator_active_context(); + // `is_creator_active` should return `true` once. + creator_staking_ctx.expect().returning(|_| false).once(); + + // Mock accepting ownership transfer for a space entity by a non-owner assert_noop!( - _accept_pending_ownership(Some(RuntimeOrigin::signed(ACCOUNT3)), None), - SpaceOwnershipError::::NotAllowedToAcceptOwnershipTransfer + Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT3), + OwnableEntity::Space(SPACE1) + ), + OwnershipError::::NotAllowedToAcceptOwnershipTransfer ); - }); -} - -#[test] -fn reject_pending_ownership_should_work() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); - // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2 - assert_ok!(_reject_default_pending_ownership()); // Rejecting a transfer from ACCOUNT2 - - // Check whether owner was not changed - let space = Spaces::space_by_id(SPACE1).unwrap(); - assert_eq!(space.owner, ACCOUNT1); - - // Check whether storage state is correct - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); - }); -} - -#[test] -fn reject_pending_ownership_should_work_when_proposal_rejected_by_current_space_owner() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); - // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2 - assert_ok!(_reject_default_pending_ownership_by_current_owner()); // Rejecting a transfer from ACCOUNT2 - - // Check whether owner was not changed - let space = Spaces::space_by_id(SPACE1).unwrap(); - assert_eq!(space.owner, ACCOUNT1); - - // Check whether storage state is correct - assert!(SpaceOwnership::pending_space_owner(SPACE1).is_none()); - }); -} -#[test] -fn reject_pending_ownership_should_fail_when_space_not_found() { - ExtBuilder::build_with_pending_ownership_transfer_no_space().execute_with(|| { - assert_noop!(_reject_default_pending_ownership(), SpacesError::::SpaceNotFound); + assert_ok!( + Ownership::accept_pending_ownership( + RuntimeOrigin::signed(ACCOUNT2), + OwnableEntity::Space(SPACE1) + ) + ); }); } #[test] -fn reject_pending_ownership_should_fail_when_no_pending_transfer_on_space() { - ExtBuilder::build_with_space().execute_with(|| { +fn reject_pending_ownership_should_fail_if_no_pending_transfer() { + ExtBuilder::build_with_all_enitities().execute_with(|| { + // Mock rejecting ownership transfer for a space entity with no pending transfer assert_noop!( - _reject_default_pending_ownership(), - SpaceOwnershipError::::NoPendingTransferOnSpace - ); // Rejecting a transfer from ACCOUNT2 + Ownership::reject_pending_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Space(SPACE1) + ), + OwnershipError::::NoPendingTransfer + ); }); } #[test] -fn reject_pending_ownership_should_fail_when_account_is_not_allowed_to_reject() { - ExtBuilder::build_with_space().execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); // Transfer SpaceId 1 owned by ACCOUNT1 to ACCOUNT2 - +fn reject_pending_ownership_should_not_allow_ineligible_account_to_reject() { + ExtBuilder::build_with_pending_transfers().execute_with(|| { + // Mock rejecting ownership transfer for a space entity by a non-owner assert_noop!( - _reject_pending_ownership(Some(RuntimeOrigin::signed(ACCOUNT3)), None), - SpaceOwnershipError::::NotAllowedToRejectOwnershipTransfer - ); // Rejecting a transfer from ACCOUNT2 + Ownership::reject_pending_ownership( + RuntimeOrigin::signed(ACCOUNT3), + OwnableEntity::Space(SPACE1) + ), + OwnershipError::::NotAllowedToRejectOwnershipTransfer + ); + + // Mock rejecting ownership transfer for a space entity by the owner + assert_ok!( + Ownership::reject_pending_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Space(SPACE1) + ) + ); }); } diff --git a/pallets/space-ownership/tests/src/tests_utils.rs b/pallets/space-ownership/tests/src/tests_utils.rs index 8b705280..0092fd8a 100644 --- a/pallets/space-ownership/tests/src/tests_utils.rs +++ b/pallets/space-ownership/tests/src/tests_utils.rs @@ -7,10 +7,12 @@ use frame_support::{assert_ok, pallet_prelude::*}; use sp_core::storage::Storage; use sp_io::TestExternalities; +use pallet_ownership::{OwnableEntity, Event as OwnershipEvent}; -use pallet_permissions::SpacePermissions; +use pallet_posts::PostExtension; use pallet_spaces::*; -use subsocial_support::{Content, SpaceId}; +use subsocial_support::{Content, PostId, SpaceId}; +use subsocial_support::mock_functions::valid_content_ipfs; use crate::mock::*; @@ -26,7 +28,7 @@ impl ExtBuilder { } let _ = pallet_balances::GenesisConfig:: { - balances: accounts.iter().cloned().map(|k| (k, 100)).collect(), + balances: accounts.iter().cloned().map(|k| (k, 1000)).collect(), } .assimilate_storage(storage); } @@ -38,109 +40,218 @@ impl ExtBuilder { Self::configure_storages(&mut storage); let mut ext = TestExternalities::from(storage); - ext.execute_with(|| System::set_block_number(1)); + ext.execute_with(|| { + let _m = use_static_mock(); + System::set_block_number(1); + }); ext } - fn add_default_space() { + pub fn add_default_space() { assert_ok!(_create_default_space()); } - /// Custom ext configuration with SpaceId 1 and BlockNumber 1 - pub fn build_with_space() -> TestExternalities { + pub fn add_default_post(space_id_opt: Option) { + assert_ok!(_create_default_post(space_id_opt)); + } + + pub fn add_default_domain() { + assert_ok!(_create_default_domain()); + } + + /// Custom ext configuration with SpaceId 1001, PostId 1 and Domain "dappforce.sub" + pub fn build_with_all_enitities() -> TestExternalities { let mut ext = Self::build(); - ext.execute_with(Self::add_default_space); + ext.execute_with(|| { + Self::add_default_space(); + let space_id = NextSpaceId::::get().saturating_sub(1); + Self::add_default_post(Some(space_id)); + Self::add_default_domain(); + }); ext } - /// Custom ext configuration with pending ownership transfer without Space - pub fn build_with_pending_ownership_transfer_no_space() -> TestExternalities { - let mut ext = Self::build_with_space(); + /// Custom ext configuration with SpaceId 1001, PostId 1 and Domain "dappforce.sub" + /// and pending transfers for all entities (current owner: `ACCOUNT1`, new owner: `ACCOUNT2`) + pub fn build_with_pending_transfers() -> TestExternalities { + let mut ext = Self::build_with_all_enitities(); ext.execute_with(|| { - assert_ok!(_transfer_default_space_ownership()); - >::remove(SPACE1); + assert_create_transfers_ok(); }); ext } } -////// Consts +// Constants pub(crate) const ACCOUNT1: AccountId = 1; pub(crate) const ACCOUNT2: AccountId = 2; pub(crate) const ACCOUNT3: AccountId = 3; pub(crate) const SPACE1: SpaceId = 1001; +pub(crate) const POST1: PostId = 1; -///////////// Space Utils +// Other pallets utils -pub(crate) fn space_content_ipfs() -> Content { - Content::IPFS(b"bafyreib3mgbou4xln42qqcgj6qlt3cif35x4ribisxgq7unhpun525l54e".to_vec()) +// Data + +pub(crate) fn default_domain() -> BoundedVec { + "dappforce.sub".as_bytes().to_vec().try_into().unwrap() } -pub(crate) fn _create_default_space() -> DispatchResult { - _create_space(None, None, None) +pub(crate) fn default_domain_entity() -> OwnableEntity { + OwnableEntity::Domain(default_domain()) } -pub(crate) fn _create_space( - origin: Option, - content: Option, - permissions: Option>, -) -> DispatchResult { +pub(crate) const fn default_space_entity() -> OwnableEntity { + OwnableEntity::Space(SPACE1) +} + +pub(crate) const fn default_post_entity() -> OwnableEntity { + OwnableEntity::Post(POST1) +} + +// Extrinsics + +pub(crate) fn _create_default_space() -> DispatchResult { Spaces::create_space( - origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT1)), - content.unwrap_or_else(space_content_ipfs), - permissions.unwrap_or_default(), + RuntimeOrigin::signed(ACCOUNT1), + valid_content_ipfs(), + None, ) } -//////// Space ownership utils - -pub(crate) fn _transfer_default_space_ownership() -> DispatchResult { - _transfer_space_ownership(None, None, None) +pub(crate) fn _create_default_post(space_id_opt: Option) -> DispatchResult { + Posts::create_post( + RuntimeOrigin::signed(ACCOUNT1), + space_id_opt, + PostExtension::RegularPost, + valid_content_ipfs(), + ) } -pub(crate) fn _transfer_space_ownership( - origin: Option, - space_id: Option, - transfer_to: Option, -) -> DispatchResult { - SpaceOwnership::transfer_space_ownership( - origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT1)), - space_id.unwrap_or(SPACE1), - transfer_to.unwrap_or(ACCOUNT2), +pub(crate) fn _create_default_domain() -> DispatchResult { + Domains::register_domain( + RuntimeOrigin::signed(ACCOUNT1), + None, + default_domain(), + Content::None, + RegistrationPeriodLimit::get(), ) } -pub(crate) fn _accept_default_pending_ownership() -> DispatchResult { - _accept_pending_ownership(None, None) +// Space ownership utils + +pub(crate) fn _transfer_ownership( + account: AccountId, + entity: OwnableEntity, + transfer_to: AccountId, +) -> DispatchResult { + Ownership::transfer_ownership( + RuntimeOrigin::signed(account), + entity, + transfer_to, + ) } pub(crate) fn _accept_pending_ownership( - origin: Option, - space_id: Option, + account: AccountId, + entity: OwnableEntity, ) -> DispatchResult { - SpaceOwnership::accept_pending_ownership( - origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT2)), - space_id.unwrap_or(SPACE1), + Ownership::accept_pending_ownership( + RuntimeOrigin::signed(account), + entity, ) } -pub(crate) fn _reject_default_pending_ownership() -> DispatchResult { - _reject_pending_ownership(None, None) +pub(crate) fn _reject_pending_ownership( + account: AccountId, + entity: OwnableEntity, +) -> DispatchResult { + Ownership::reject_pending_ownership( + RuntimeOrigin::signed(account), + entity, + ) } -pub(crate) fn _reject_default_pending_ownership_by_current_owner() -> DispatchResult { - _reject_pending_ownership(Some(RuntimeOrigin::signed(ACCOUNT1)), None) +pub(crate) fn assert_create_transfers_ok() { + let _m = use_static_mock(); + // `is_creator_active` should return `is_active`. + let creator_staking_ctx = MockCreatorStaking::is_creator_active_context(); + creator_staking_ctx.expect().returning(|_| false).once(); + + // Mock a transfer from account 1 to account 2 for a space entity + assert_ok!(Ownership::transfer_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Space(SPACE1), + ACCOUNT2 + )); + + // Mock a transfer from account 1 to account 2 for a post entity + assert_ok!(Ownership::transfer_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Post(POST1), + ACCOUNT2 + )); + + // Mock a transfer from account 1 to account 2 for a domain entity + assert_ok!(Ownership::transfer_ownership( + RuntimeOrigin::signed(ACCOUNT1), + OwnableEntity::Domain(default_domain()), + ACCOUNT2 + )); + + System::assert_has_event(OwnershipEvent::OwnershipTransferCreated { + current_owner: ACCOUNT1, + entity: default_space_entity(), + new_owner: ACCOUNT2, + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferCreated { + current_owner: ACCOUNT1, + entity: default_post_entity(), + new_owner: ACCOUNT2, + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferCreated { + current_owner: ACCOUNT1, + entity: default_domain_entity(), + new_owner: ACCOUNT2, + }.into()); } -pub(crate) fn _reject_pending_ownership( - origin: Option, - space_id: Option, -) -> DispatchResult { - SpaceOwnership::reject_pending_ownership( - origin.unwrap_or_else(|| RuntimeOrigin::signed(ACCOUNT2)), - space_id.unwrap_or(SPACE1), - ) +pub(crate) fn assert_reject_transfers_ok(account: AccountId) { + // Mock rejecting ownership transfer for a space entity + assert_ok!(Ownership::reject_pending_ownership( + RuntimeOrigin::signed(account), + default_space_entity(), + )); + + // Mock rejecting ownership transfer for a post entity + assert_ok!(Ownership::reject_pending_ownership( + RuntimeOrigin::signed(account), + default_post_entity(), + )); + + // Mock rejecting ownership transfer for a domain entity + assert_ok!(Ownership::reject_pending_ownership( + RuntimeOrigin::signed(account), + default_domain_entity(), + )); + + System::assert_has_event(OwnershipEvent::OwnershipTransferRejected { + account, + entity: default_space_entity(), + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferRejected { + account, + entity: default_post_entity(), + }.into()); + + System::assert_has_event(OwnershipEvent::OwnershipTransferRejected { + account, + entity: default_domain_entity(), + }.into()); } diff --git a/pallets/spaces/src/lib.rs b/pallets/spaces/src/lib.rs index a10d4408..793f5e2d 100644 --- a/pallets/spaces/src/lib.rs +++ b/pallets/spaces/src/lib.rs @@ -49,7 +49,7 @@ pub mod pallet { }; use subsocial_support::{ ensure_content_is_valid, remove_from_bounded_vec, - traits::{IsAccountBlocked, IsContentBlocked, SpacePermissionsProvider, SpacesInterface}, + traits::{IsAccountBlocked, IsContentBlocked, SpacePermissionsProvider, SpacesProvider}, ModerationError, SpacePermissionsInfo, WhoAndWhen, WhoAndWhenOf, }; use types::*; @@ -426,11 +426,40 @@ pub mod pallet { } } - impl SpacesInterface for Pallet { + impl SpacesProvider for Pallet { fn get_space_owner(space_id: SpaceId) -> Result { let space = Pallet::::require_space(space_id)?; Ok(space.owner) } + + fn do_update_space_owner(space_id: SpaceId, new_owner: T::AccountId) -> DispatchResult { + let space = Pallet::::require_space(space_id)?; + if space.is_owner(&new_owner) { + return Ok(()); + } + + Self::ensure_space_limit_not_reached(&new_owner)?; + + ensure!( + T::IsAccountBlocked::is_allowed_account(new_owner.clone(), space_id), + ModerationError::AccountIsBlocked + ); + + SpaceIdsByOwner::::mutate(&space.owner, |ids| { + remove_from_bounded_vec(ids, space_id) + }); + + SpaceIdsByOwner::::mutate(&new_owner, |ids| { + ids.try_push(space_id).expect("qed; too many spaces per account") + }); + + SpaceById::::mutate(space_id, |stored_space_opt| { + if let Some(stored_space) = stored_space_opt { + stored_space.owner = new_owner; + } + }); + Ok(()) + } fn create_space(owner: &T::AccountId, content: Content) -> Result { Self::do_create_space(owner, content, None) diff --git a/pallets/spaces/tests/src/mock.rs b/pallets/spaces/tests/src/mock.rs index 9aad34e5..b441b844 100644 --- a/pallets/spaces/tests/src/mock.rs +++ b/pallets/spaces/tests/src/mock.rs @@ -130,7 +130,7 @@ impl pallet_roles::Config for Test { impl pallet_profiles::Config for Test { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = Spaces; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type WeightInfo = (); } diff --git a/pallets/support/Cargo.toml b/pallets/support/Cargo.toml index 04c443d9..bb10b005 100644 --- a/pallets/support/Cargo.toml +++ b/pallets/support/Cargo.toml @@ -12,11 +12,13 @@ categories = ["cryptography::cryptocurrencies"] [features] default = ["std"] +runtime-benchmarks = ['frame-benchmarking/runtime-benchmarks'] std = [ "serde/std", "strum/std", "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "pallet-timestamp/std", @@ -31,6 +33,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = scale-info = { version = "2.2.0", default-features = false, features = ["derive"] } # Substrate dependencies +frame-benchmarking = { git = 'https://github.com/paritytech/substrate', branch = 'polkadot-v0.9.40', default-features = false, optional = true } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.40", default-features = false } diff --git a/pallets/support/src/traits.rs b/pallets/support/src/traits.rs index 28955eb9..99477fde 100644 --- a/pallets/support/src/traits.rs +++ b/pallets/support/src/traits.rs @@ -5,8 +5,8 @@ // Full license is available at https://github.com/dappforce/subsocial-parachain/blob/main/LICENSE pub use common::{ - CreatorStakingProvider, ProfileManager, SpaceFollowsProvider, SpacePermissionsProvider, - SpacesInterface, PostFollowsProvider, + CreatorStakingProvider, DomainsProvider, PostFollowsProvider, PostsProvider, ProfileManager, + SpaceFollowsProvider, SpacePermissionsProvider, SpacesProvider, }; pub use moderation::{IsAccountBlocked, IsContentBlocked, IsPostBlocked, IsSpaceBlocked}; diff --git a/pallets/support/src/traits/common.rs b/pallets/support/src/traits/common.rs index 9db5805a..60ad69b6 100644 --- a/pallets/support/src/traits/common.rs +++ b/pallets/support/src/traits/common.rs @@ -32,8 +32,11 @@ pub trait ProfileManager { fn unlink_space_from_profile(account: &AccountId, space_id: SpaceId); } -pub trait SpacesInterface { +pub trait SpacesProvider { + fn get_space_owner(space_id: SpaceId) -> Result; + + fn do_update_space_owner(space_id: SpaceId, new_owner: AccountId) -> DispatchResult; fn create_space(owner: &AccountId, content: Content) -> Result; } @@ -51,3 +54,27 @@ impl CreatorStakingProvider for () { false } } + +pub trait DomainsProvider { + type MaxDomainLength: frame_support::traits::Get; + + fn get_domain_owner(domain: &[u8]) -> Result; + + fn ensure_domain_owner(domain: &[u8], account: &AccountId) -> DispatchResult; + + fn do_update_domain_owner(domain: &[u8], new_owner: &AccountId) -> DispatchResult; + + #[cfg(feature = "runtime-benchmarks")] + fn register_domain(owner: &AccountId, domain: &[u8]) -> Result, DispatchError>; +} + +pub trait PostsProvider { + fn get_post_owner(post_id: PostId) -> Result; + + fn ensure_post_owner(post_id: PostId, account: &AccountId) -> DispatchResult; + + fn do_update_post_owner(post_id: PostId, new_owner: &AccountId) -> DispatchResult; + + #[cfg(feature = "runtime-benchmarks")] + fn create_post(owner: &AccountId, space_id: SpaceId, content: Content) -> Result; +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 7351f10b..c534f7c5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -36,7 +36,7 @@ pallet-reactions = { path = '../pallets/reactions', default-features = false } pallet-resource-discussions = { path = '../pallets/resource-discussions', default-features = false } pallet-roles = { path = '../pallets/roles', default-features = false } pallet-space-follows = { path = '../pallets/space-follows', default-features = false } -pallet-space-ownership = { path = '../pallets/space-ownership', default-features = false } +pallet-ownership = { path = '../pallets/space-ownership', default-features = false } pallet-spaces = { path = '../pallets/spaces', default-features = false } subsocial-support = { path = '../pallets/support', default-features = false } pallet-free-proxy = { path = "../pallets/free-proxy", default-features = false } @@ -171,7 +171,7 @@ std = [ "pallet-resource-discussions/std", "pallet-roles/std", "pallet-space-follows/std", - "pallet-space-ownership/std", + "pallet-ownership/std", "pallet-spaces/std", "subsocial-support/std", "pallet-free-proxy/std", @@ -208,7 +208,7 @@ runtime-benchmarks = [ "pallet-resource-discussions/runtime-benchmarks", "pallet-roles/runtime-benchmarks", "pallet-space-follows/runtime-benchmarks", - "pallet-space-ownership/runtime-benchmarks", + "pallet-ownership/runtime-benchmarks", "pallet-spaces/runtime-benchmarks", "pallet-post-follows/runtime-benchmarks", "pallet-posts/runtime-benchmarks", @@ -255,7 +255,7 @@ try-runtime = [ "pallet-resource-discussions/try-runtime", "pallet-roles/try-runtime", "pallet-space-follows/try-runtime", - "pallet-space-ownership/try-runtime", + "pallet-ownership/try-runtime", "pallet-spaces/try-runtime", "pallet-evm-addresses/try-runtime", ] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f4fef019..13b8bf3a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -121,9 +121,20 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, - (), + pallet_ownership::migration::v1::MigrateToV1< + Runtime, + Ownership, + OwnershipMigrationV1OldPallet, + >, >; +pub struct OwnershipMigrationV1OldPallet; +impl frame_support::traits::Get<&'static str> for OwnershipMigrationV1OldPallet { + fn get() -> &'static str { + "SpaceOwnership" + } +} + /// Handles converting a weight scalar to a fee value, based on the scale and granularity of the /// node's balance type. /// @@ -178,10 +189,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("subsocial-parachain"), impl_name: create_runtime_str!("subsocial-parachain"), authoring_version: 1, - spec_version: 41, + spec_version: 42, impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 8, + transaction_version: 9, state_version: 0, }; @@ -728,7 +739,7 @@ impl pallet_reactions::Config for Runtime { impl pallet_profiles::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SpacePermissionsProvider = Spaces; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type WeightInfo = pallet_profiles::weights::SubstrateWeight; } @@ -765,11 +776,16 @@ impl pallet_spaces::Config for Runtime { type WeightInfo = pallet_spaces::weights::SubstrateWeight; } -impl pallet_space_ownership::Config for Runtime { +impl pallet_ownership::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ProfileManager = Profiles; + type SpacesProvider = Spaces; + type SpacePermissionsProvider = Spaces; type CreatorStakingProvider = CreatorStaking; - type WeightInfo = pallet_space_ownership::weights::SubstrateWeight; + type DomainsProvider = Domains; + type PostsProvider = Posts; + type Currency = Balances; + type WeightInfo = pallet_ownership::weights::SubstrateWeight; } impl pallet_account_follows::Config for Runtime { @@ -825,7 +841,7 @@ impl pallet_creator_staking::Config for Runtime { type PalletId = CreatorStakingPalletId; type BlockPerEra = BlockPerEra; type Currency = Balances; - type SpacesInterface = Spaces; + type SpacesProvider = Spaces; type SpacePermissionsProvider = Spaces; type CreatorRegistrationDeposit = CreatorRegistrationDeposit; type MinimumTotalStake = MinimumTotalStake; @@ -901,7 +917,7 @@ construct_runtime!( AccountFollows: pallet_account_follows = 72, Profiles: pallet_profiles = 73, SpaceFollows: pallet_space_follows = 74, - SpaceOwnership: pallet_space_ownership = 75, + Ownership: pallet_ownership = 75, Spaces: pallet_spaces = 76, PostFollows: pallet_post_follows = 77, Posts: pallet_posts = 78, @@ -932,7 +948,7 @@ mod benches { [pallet_reactions, Reactions] [pallet_roles, Roles] [pallet_space_follows, SpaceFollows] - [pallet_space_ownership, SpaceOwnership] + [pallet_ownership, Ownership] [pallet_spaces, Spaces] [pallet_post_follows, PostFollows] [pallet_posts, Posts]