diff --git a/Cargo.lock b/Cargo.lock index 4fd6571eb9..3051b32eda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1554,7 +1554,7 @@ dependencies = [ [[package]] name = "joystream-node" -version = "2.1.4" +version = "2.1.5" dependencies = [ "ctrlc", "derive_more 0.14.1", @@ -1599,7 +1599,7 @@ dependencies = [ [[package]] name = "joystream-node-runtime" -version = "6.10.0" +version = "6.11.0" dependencies = [ "parity-scale-codec", "safe-mix", diff --git a/node/Cargo.toml b/node/Cargo.toml index 62394af5a0..134bad64a6 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -3,7 +3,7 @@ authors = ['Joystream'] build = 'build.rs' edition = '2018' name = 'joystream-node' -version = '2.1.4' +version = '2.1.5' default-run = "joystream-node" [[bin]] diff --git a/runtime-modules/content-working-group/src/lib.rs b/runtime-modules/content-working-group/src/lib.rs index 482a4a49c3..9972ef0b55 100755 --- a/runtime-modules/content-working-group/src/lib.rs +++ b/runtime-modules/content-working-group/src/lib.rs @@ -1910,103 +1910,23 @@ decl_module! { ); } - /* - * Root origin routines for managing lead. - */ - - - /// Introduce a lead when one is not currently set. - pub fn set_lead(origin, member: T::MemberId, role_account: T::AccountId) { - + /// Replace the current lead. First unsets the active lead if there is one. + /// If a value is provided for new_lead it will then set that new lead. + /// It is responsibility of the caller to ensure the new lead can be set + /// to avoid the lead role being vacant at the end of the call. + pub fn replace_lead(origin, new_lead: Option<(T::MemberId, T::AccountId)>) { // Ensure root is origin ensure_root(origin)?; - // Ensure there is no current lead - ensure!( - >::get().is_none(), - MSG_CURRENT_LEAD_ALREADY_SET - ); - - // Ensure that member can actually become lead - let new_lead_id = >::get(); - - let new_lead_role = - role_types::ActorInRole::new(role_types::Role::CuratorLead, new_lead_id); - - let _profile = >::can_register_role_on_member( - &member, - &role_types::ActorInRole::new(role_types::Role::CuratorLead, new_lead_id), - )?; - - // - // == MUTATION SAFE == - // - - // Construct lead - let new_lead = Lead { - role_account: role_account.clone(), - reward_relationship: None, - inducted: >::block_number(), - stage: LeadRoleState::Active, - }; - - // Store lead - >::insert(new_lead_id, new_lead); - - // Update current lead - >::put(new_lead_id); // Some(new_lead_id) - - // Update next lead counter - >::mutate(|id| *id += as One>::one()); - - // Register in role - let registered_role = - >::register_role_on_member(member, &new_lead_role).is_ok(); - - assert!(registered_role); - - // Trigger event - Self::deposit_event(RawEvent::LeadSet(new_lead_id)); - } - - /// Evict the currently unset lead - pub fn unset_lead(origin) { - - // Ensure root is origin - ensure_root(origin)?; - - // Ensure there is a lead set - let (lead_id,lead) = Self::ensure_lead_is_set()?; - - // - // == MUTATION SAFE == - // - - // Unregister from role in membership model - let current_lead_role = role_types::ActorInRole{ - role: role_types::Role::CuratorLead, - actor_id: lead_id - }; - - let unregistered_role = >::unregister_role(current_lead_role).is_ok(); - - assert!(unregistered_role); - - // Update lead stage as exited - let current_block = >::block_number(); - - let new_lead = Lead{ - stage: LeadRoleState::Exited(ExitedLeadRole { initiated_at_block_number: current_block}), - ..lead - }; - - >::insert(lead_id, new_lead); - - // Update current lead - >::take(); // None + // Unset current lead first + if Self::ensure_lead_is_set().is_ok() { + Self::unset_lead()?; + } - // Trigger event - Self::deposit_event(RawEvent::LeadUnset(lead_id)); + // Try to set new lead + if let Some((member_id, role_account)) = new_lead { + Self::set_lead(member_id, role_account)?; + } } /// Add an opening for a curator role. @@ -2122,6 +2042,87 @@ impl versioned_store_permissions::CredentialChecker for Module { } impl Module { + /// Introduce a lead when one is not currently set. + fn set_lead(member: T::MemberId, role_account: T::AccountId) -> dispatch::Result { + // Ensure there is no current lead + ensure!( + >::get().is_none(), + MSG_CURRENT_LEAD_ALREADY_SET + ); + + let new_lead_id = >::get(); + + let new_lead_role = + role_types::ActorInRole::new(role_types::Role::CuratorLead, new_lead_id); + + // + // == MUTATION SAFE == + // + + // Register in role - will fail if member cannot become lead + members::Module::::register_role_on_member(member, &new_lead_role)?; + + // Construct lead + let new_lead = Lead { + role_account: role_account.clone(), + reward_relationship: None, + inducted: >::block_number(), + stage: LeadRoleState::Active, + }; + + // Store lead + >::insert(new_lead_id, new_lead); + + // Update current lead + >::put(new_lead_id); // Some(new_lead_id) + + // Update next lead counter + >::mutate(|id| *id += as One>::one()); + + // Trigger event + Self::deposit_event(RawEvent::LeadSet(new_lead_id)); + + Ok(()) + } + + /// Evict the currently set lead + fn unset_lead() -> dispatch::Result { + // Ensure there is a lead set + let (lead_id, lead) = Self::ensure_lead_is_set()?; + + // + // == MUTATION SAFE == + // + + // Unregister from role in membership model + let current_lead_role = role_types::ActorInRole { + role: role_types::Role::CuratorLead, + actor_id: lead_id, + }; + + >::unregister_role(current_lead_role)?; + + // Update lead stage as exited + let current_block = >::block_number(); + + let new_lead = Lead { + stage: LeadRoleState::Exited(ExitedLeadRole { + initiated_at_block_number: current_block, + }), + ..lead + }; + + >::insert(lead_id, new_lead); + + // Update current lead + >::take(); // None + + // Trigger event + Self::deposit_event(RawEvent::LeadUnset(lead_id)); + + Ok(()) + } + fn ensure_member_has_no_active_application_on_opening( curator_applications: CuratorApplicationIdSet, member_id: T::MemberId, diff --git a/runtime-modules/content-working-group/src/tests.rs b/runtime-modules/content-working-group/src/tests.rs index 8d8f6f8198..d2b83af4ff 100644 --- a/runtime-modules/content-working-group/src/tests.rs +++ b/runtime-modules/content-working-group/src/tests.rs @@ -1160,7 +1160,10 @@ struct SetLeadFixture { impl SetLeadFixture { fn call(&self) -> Result<(), &'static str> { - ContentWorkingGroup::set_lead(self.origin.clone(), self.member_id, self.new_role_account) + ContentWorkingGroup::replace_lead( + self.origin.clone(), + Some((self.member_id, self.new_role_account)), + ) } pub fn call_and_assert_success(&self) { @@ -1221,7 +1224,7 @@ struct UnsetLeadFixture { impl UnsetLeadFixture { fn call(&self) -> Result<(), &'static str> { - ContentWorkingGroup::unset_lead(self.origin.clone()) + ContentWorkingGroup::replace_lead(self.origin.clone(), None) } pub fn call_and_assert_success(&self) { @@ -2121,10 +2124,9 @@ pub fn set_lead( // Set lead assert_eq!( - ContentWorkingGroup::set_lead( + ContentWorkingGroup::replace_lead( mock::Origin::system(system::RawOrigin::Root), - member_id, - new_role_account + Some((member_id, new_role_account)) ) .unwrap(), () diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 74b1a68aa3..eeba50dd15 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -5,7 +5,7 @@ edition = '2018' name = 'joystream-node-runtime' # Follow convention: https://github.com/Joystream/substrate-runtime-joystream/issues/1 # {Authoring}.{Spec}.{Impl} of the RuntimeVersion -version = '6.10.0' +version = '6.11.0' [features] default = ['std'] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5829a0b3b3..bf1581d52f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -115,7 +115,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("joystream-node"), impl_name: create_runtime_str!("joystream-node"), authoring_version: 6, - spec_version: 10, + spec_version: 11, impl_version: 0, apis: RUNTIME_API_VERSIONS, };