diff --git a/Cargo.lock b/Cargo.lock index 3051b32eda..232c102310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1554,7 +1554,7 @@ dependencies = [ [[package]] name = "joystream-node" -version = "2.1.5" +version = "2.1.6" dependencies = [ "ctrlc", "derive_more 0.14.1", @@ -1599,7 +1599,7 @@ dependencies = [ [[package]] name = "joystream-node-runtime" -version = "6.11.0" +version = "6.12.0" dependencies = [ "parity-scale-codec", "safe-mix", @@ -4796,6 +4796,7 @@ dependencies = [ "substrate-common-module", "substrate-membership-module", "substrate-primitives", + "substrate-token-mint-module", ] [[package]] diff --git a/node/Cargo.toml b/node/Cargo.toml index 134bad64a6..bb6930245e 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.5' +version = '2.1.6' default-run = "joystream-node" [[bin]] diff --git a/runtime-modules/governance/Cargo.toml b/runtime-modules/governance/Cargo.toml index 61cce493b9..d3314bd0db 100644 --- a/runtime-modules/governance/Cargo.toml +++ b/runtime-modules/governance/Cargo.toml @@ -17,6 +17,7 @@ std = [ 'rstd/std', 'common/std', 'membership/std', + 'minting/std', ] [dependencies.sr-primitives] @@ -86,4 +87,9 @@ rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' default_features = false git = 'https://github.com/paritytech/substrate.git' package = 'srml-balances' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' \ No newline at end of file +rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' + +[dependencies.minting] +default_features = false +package = 'substrate-token-mint-module' +path = '../token-minting' \ No newline at end of file diff --git a/runtime-modules/governance/src/council.rs b/runtime-modules/governance/src/council.rs index 792977f073..21b84c60ed 100644 --- a/runtime-modules/governance/src/council.rs +++ b/runtime-modules/governance/src/council.rs @@ -21,7 +21,7 @@ impl CouncilTermEnded for (X,) { } } -pub trait Trait: system::Trait + GovernanceCurrency { +pub trait Trait: system::Trait + minting::Trait + GovernanceCurrency { type Event: From> + Into<::Event>; type CouncilTermEnded: CouncilTermEnded; @@ -29,8 +29,14 @@ pub trait Trait: system::Trait + GovernanceCurrency { decl_storage! { trait Store for Module as Council { - ActiveCouncil get(active_council) config(): Seats>; - TermEndsAt get(term_ends_at) config() : T::BlockNumber = T::BlockNumber::from(1); + pub ActiveCouncil get(active_council) config(): Seats>; + + pub TermEndsAt get(term_ends_at) config() : T::BlockNumber = T::BlockNumber::from(1); + + /// The mint that funds council member rewards and spending proposals budget. It is an Option + /// because it was introduced in a runtime upgrade. It will be automatically created when + /// a successful call to set_council_mint_capacity() is made. + pub CouncilMint get(council_mint) : Option<::MintId>; } } @@ -60,6 +66,15 @@ impl Module { pub fn is_councilor(sender: &T::AccountId) -> bool { Self::active_council().iter().any(|c| c.member == *sender) } + + /// Initializes a new mint, discarding previous mint if it existed. + pub fn create_new_council_mint( + capacity: minting::BalanceOf, + ) -> Result { + let mint_id = >::add_mint(capacity, None)?; + CouncilMint::::put(mint_id); + Ok(mint_id) + } } decl_module! { @@ -118,6 +133,29 @@ decl_module! { ensure!(ends_at > >::block_number(), "must set future block number"); >::put(ends_at); } + + /// Sets the capacity of the the council mint, if it doesn't exist, attempts to + /// create a new one. + fn set_council_mint_capacity(origin, capacity: minting::BalanceOf) { + ensure_root(origin)?; + + if let Some(mint_id) = Self::council_mint() { + minting::Module::::set_mint_capacity(mint_id, capacity)?; + } else { + Self::create_new_council_mint(capacity)?; + } + } + + /// Attempts to mint and transfer amount to destination account + fn spend_from_council_mint(origin, amount: minting::BalanceOf, destination: T::AccountId) { + ensure_root(origin)?; + + if let Some(mint_id) = Self::council_mint() { + minting::Module::::transfer_tokens(mint_id, amount, &destination)?; + } else { + return Err("CouncilHashNoMint") + } + } } } diff --git a/runtime-modules/governance/src/election.rs b/runtime-modules/governance/src/election.rs index bcb4bf4185..9df5a4c9b1 100644 --- a/runtime-modules/governance/src/election.rs +++ b/runtime-modules/governance/src/election.rs @@ -174,7 +174,7 @@ impl Module { } fn can_participate(sender: &T::AccountId) -> bool { - !T::Currency::free_balance(sender).is_zero() + !::Currency::free_balance(sender).is_zero() && >::is_member_account(sender) } @@ -374,7 +374,10 @@ impl Module { for stakeholder in Self::existing_stake_holders().iter() { let stake = Self::transferable_stakes(stakeholder); if !stake.seat.is_zero() || !stake.backing.is_zero() { - T::Currency::unreserve(stakeholder, stake.seat + stake.backing); + ::Currency::unreserve( + stakeholder, + stake.seat + stake.backing, + ); } } } @@ -399,7 +402,7 @@ impl Module { // return new stake to account's free balance if !stake.new.is_zero() { - T::Currency::unreserve(applicant, stake.new); + ::Currency::unreserve(applicant, stake.new); } // return unused transferable stake @@ -453,7 +456,7 @@ impl Module { // return new stake to account's free balance let SealedVote { voter, stake, .. } = sealed_vote; if !stake.new.is_zero() { - T::Currency::unreserve(voter, stake.new); + ::Currency::unreserve(voter, stake.new); } // return unused transferable stake @@ -644,12 +647,12 @@ impl Module { let new_stake = Self::new_stake_reusing_transferable(&mut transferable_stake.seat, stake); ensure!( - T::Currency::can_reserve(&applicant, new_stake.new), + ::Currency::can_reserve(&applicant, new_stake.new), "not enough free balance to reserve" ); ensure!( - T::Currency::reserve(&applicant, new_stake.new).is_ok(), + ::Currency::reserve(&applicant, new_stake.new).is_ok(), "failed to reserve applicant stake!" ); @@ -680,12 +683,12 @@ impl Module { Self::new_stake_reusing_transferable(&mut transferable_stake.backing, stake); ensure!( - T::Currency::can_reserve(&voter, vote_stake.new), + ::Currency::can_reserve(&voter, vote_stake.new), "not enough free balance to reserve" ); ensure!( - T::Currency::reserve(&voter, vote_stake.new).is_ok(), + ::Currency::reserve(&voter, vote_stake.new).is_ok(), "failed to reserve voting stake!" ); diff --git a/runtime-modules/governance/src/mock.rs b/runtime-modules/governance/src/mock.rs index 5e6dc33dbe..8d13c511fe 100644 --- a/runtime-modules/governance/src/mock.rs +++ b/runtime-modules/governance/src/mock.rs @@ -70,7 +70,10 @@ impl membership::members::Trait for Test { type ActorId = u32; type InitialMembersBalance = InitialMembersBalance; } - +impl minting::Trait for Test { + type Currency = Balances; + type MintId = u64; +} parameter_types! { pub const ExistentialDeposit: u32 = 0; pub const TransferFee: u32 = 0; diff --git a/runtime-modules/governance/src/proposals.rs b/runtime-modules/governance/src/proposals.rs index e681e51d6c..64a177a6fd 100644 --- a/runtime-modules/governance/src/proposals.rs +++ b/runtime-modules/governance/src/proposals.rs @@ -244,7 +244,7 @@ decl_module! { ensure!(wasm_code.len() as u32 <= Self::wasm_code_max_len(), MSG_TOO_LONG_WASM_CODE); // Lock proposer's stake: - T::Currency::reserve(&proposer, stake) + ::Currency::reserve(&proposer, stake) .map_err(|_| MSG_STAKE_IS_GREATER_THAN_BALANCE)?; let proposal_id = Self::proposal_count() + 1; @@ -312,11 +312,11 @@ decl_module! { // Spend some minimum fee on proposer's balance for canceling a proposal let fee = Self::cancellation_fee(); - let _ = T::Currency::slash_reserved(&proposer, fee); + let _ = ::Currency::slash_reserved(&proposer, fee); // Return unspent part of remaining staked deposit (after taking some fee) let left_stake = proposal.stake - fee; - let _ = T::Currency::unreserve(&proposer, left_stake); + let _ = ::Currency::unreserve(&proposer, left_stake); Self::_update_proposal_status(proposal_id, Cancelled)?; Self::deposit_event(RawEvent::ProposalCanceled(proposer, proposal_id)); @@ -336,7 +336,7 @@ decl_module! { let proposal = Self::proposals(proposal_id); ensure!(proposal.status == Active, MSG_PROPOSAL_FINALIZED); - let _ = T::Currency::unreserve(&proposal.proposer, proposal.stake); + let _ = ::Currency::unreserve(&proposal.proposer, proposal.stake); Self::_update_proposal_status(proposal_id, Cancelled)?; @@ -357,7 +357,7 @@ impl Module { } fn can_participate(sender: &T::AccountId) -> bool { - !T::Currency::free_balance(sender).is_zero() + !::Currency::free_balance(sender).is_zero() && >::is_member_account(sender) } @@ -513,7 +513,8 @@ impl Module { let proposal = Self::proposals(proposal_id); // Slash proposer's stake: - let _ = T::Currency::slash_reserved(&proposal.proposer, proposal.stake); + let _ = + ::Currency::slash_reserved(&proposal.proposer, proposal.stake); Ok(()) } @@ -525,11 +526,11 @@ impl Module { // Spend some minimum fee on proposer's balance to prevent spamming attacks: let fee = Self::rejection_fee(); - let _ = T::Currency::slash_reserved(&proposer, fee); + let _ = ::Currency::slash_reserved(&proposer, fee); // Return unspent part of remaining staked deposit (after taking some fee): let left_stake = proposal.stake - fee; - let _ = T::Currency::unreserve(&proposer, left_stake); + let _ = ::Currency::unreserve(&proposer, left_stake); Ok(()) } @@ -540,7 +541,7 @@ impl Module { let wasm_code = Self::wasm_code_by_hash(proposal.wasm_hash); // Return staked deposit to proposer: - let _ = T::Currency::unreserve(&proposal.proposer, proposal.stake); + let _ = ::Currency::unreserve(&proposal.proposer, proposal.stake); // Update wasm code of node's runtime: >::set_code(system::RawOrigin::Root.into(), wasm_code)?; @@ -649,6 +650,11 @@ mod tests { type InitialMembersBalance = InitialMembersBalance; } + impl minting::Trait for Test { + type Currency = balances::Module; + type MintId = u64; + } + impl Trait for Test { type Event = (); } diff --git a/runtime-modules/token-minting/src/lib.rs b/runtime-modules/token-minting/src/lib.rs index df9c6fef91..20388f2fda 100755 --- a/runtime-modules/token-minting/src/lib.rs +++ b/runtime-modules/token-minting/src/lib.rs @@ -82,6 +82,15 @@ impl From for &'static str { } } +impl From for &'static str { + fn from(err: TransferError) -> &'static str { + match err { + TransferError::MintNotFound => "MintNotFound", + TransferError::NotEnoughCapacity => "NotEnoughCapacity", + } + } +} + #[derive(Encode, Decode, Copy, Clone, Debug, Eq, PartialEq)] pub enum Adjustment { // First adjustment will be after AdjustOnInterval.block_interval diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index eeba50dd15..c0eee0a3cf 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.11.0' +version = '6.12.0' [features] default = ['std'] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index bf1581d52f..13a2701388 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: 11, + spec_version: 12, impl_version: 0, apis: RUNTIME_API_VERSIONS, }; diff --git a/runtime/src/migration.rs b/runtime/src/migration.rs index fc4bd421a9..e0b144429a 100644 --- a/runtime/src/migration.rs +++ b/runtime/src/migration.rs @@ -1,28 +1,24 @@ use crate::VERSION; -use sr_primitives::print; +use sr_primitives::{print, traits::Zero}; use srml_support::{decl_event, decl_module, decl_storage}; use sudo; use system; -// When preparing a new major runtime release version bump this value to match it and update -// the initialization code in runtime_initialization(). Because of the way substrate runs runtime code -// the runtime doesn't need to maintain any logic for old migrations. All knowledge about state of the chain and runtime -// prior to the new runtime taking over is implicit in the migration code implementation. If assumptions are incorrect -// behaviour is undefined. -const MIGRATION_FOR_SPEC_VERSION: u32 = 0; - impl Module { - fn runtime_initialization() { - if VERSION.spec_version != MIGRATION_FOR_SPEC_VERSION { - return; - } - - print("running runtime initializers"); + fn runtime_upgraded() { + print("running runtime initializers..."); // ... - // add initialization of other modules introduced in this runtime + // add initialization of modules introduced in new runtime release. This + // would be any new storage values that need an initial value which would not + // have been initialized with config() or build() mechanism. // ... + // Create the Council mint. If it fails, we can't do anything about it here. + let _ = governance::council::Module::::create_new_council_mint( + minting::BalanceOf::::zero(), + ); + Self::deposit_event(RawEvent::Migrated( >::block_number(), VERSION.spec_version, @@ -36,6 +32,7 @@ pub trait Trait: + storage::data_object_storage_registry::Trait + forum::Trait + sudo::Trait + + governance::council::Trait { type Event: From> + Into<::Event>; } @@ -64,7 +61,7 @@ decl_module! { SpecVersion::put(VERSION.spec_version); // run migrations and store initializers - Self::runtime_initialization(); + Self::runtime_upgraded(); } } }