diff --git a/Cargo.toml b/Cargo.toml index c2b1bf3c89..2816d6370f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ authors = ['Joystream'] edition = '2018' name = 'joystream-node-runtime' -version = '5.1.0' +version = '5.2.0' [features] default = ['std'] diff --git a/src/currency.rs b/src/currency.rs index 7d2e27be7a..491780cf15 100644 --- a/src/currency.rs +++ b/src/currency.rs @@ -11,30 +11,28 @@ pub trait GovernanceCurrency: system::Trait + Sized { pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - /// A structure that converts the currency type into a lossy u64 /// And back from u128 pub struct CurrencyToVoteHandler; impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u64 { - if x >> 96 == 0 { - x as u64 - } else { - u64::max_value() - } - } + fn convert(x: u128) -> u64 { + if x >> 96 == 0 { + x as u64 + } else { + u64::max_value() + } + } } impl Convert for CurrencyToVoteHandler { - fn convert(x: u128) -> u128 { - // if it practically fits in u64 - if x >> 64 == 0 { - x - } - else { - // 0000_0000_FFFF_FFFF_FFFF_FFFF_0000_0000 - u64::max_value() as u128 - } - } -} \ No newline at end of file + fn convert(x: u128) -> u128 { + // if it practically fits in u64 + if x >> 64 == 0 { + x + } else { + // 0000_0000_FFFF_FFFF_FFFF_FFFF_0000_0000 + u64::max_value() as u128 + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 143fc011a0..4c990c9d73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,10 +18,7 @@ pub mod currency; pub mod governance; use governance::{council, election, proposals}; pub mod storage; -use storage::{ - data_directory, data_object_storage_registry, data_object_type_registry, - downloads, -}; +use storage::{data_directory, data_object_storage_registry, data_object_type_registry, downloads}; mod membership; mod memo; mod traits; @@ -41,8 +38,8 @@ use rstd::prelude::*; // needed for Vec use runtime_primitives::{ create_runtime_str, generic, traits::{ - self as runtime_traits, AuthorityIdFor, BlakeTwo256, Block as BlockT, - DigestFor, NumberFor, StaticLookup, Verify, + self as runtime_traits, AuthorityIdFor, BlakeTwo256, Block as BlockT, DigestFor, NumberFor, + StaticLookup, Verify, }, transaction_validity::TransactionValidity, AnySignature, ApplyResult, @@ -99,11 +96,11 @@ pub mod opaque { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with = "bytes"))] pub Vec); #[cfg(feature = "std")] - impl std::fmt::Debug for UncheckedExtrinsic { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) - } - } + impl std::fmt::Debug for UncheckedExtrinsic { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "{}", primitives::hexdisplay::HexDisplay::from(&self.0)) + } + } impl runtime_traits::Extrinsic for UncheckedExtrinsic { fn is_signed(&self) -> Option { None @@ -128,7 +125,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("joystream-node"), impl_name: create_runtime_str!("joystream-node"), authoring_version: 5, - spec_version: 1, + spec_version: 2, impl_version: 0, apis: RUNTIME_API_VERSIONS, }; diff --git a/src/roles/actors.rs b/src/roles/actors.rs index 7a21ca66e4..1097d4f476 100644 --- a/src/roles/actors.rs +++ b/src/roles/actors.rs @@ -2,7 +2,7 @@ use crate::currency::{BalanceOf, GovernanceCurrency}; use parity_codec_derive::{Decode, Encode}; use rstd::prelude::*; use runtime_primitives::traits::{As, Bounded, MaybeDebug, Zero}; -use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons}; +use srml_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons, WithdrawReason}; use srml_support::{decl_event, decl_module, decl_storage, ensure, StorageMap, StorageValue}; use system::{self, ensure_signed}; @@ -212,19 +212,29 @@ impl Module { role: Role, member_id: MemberId, unbonding_period: T::BlockNumber, + stake: BalanceOf, ) { // simple unstaking ...only applying unbonding period - let value = T::Currency::free_balance(&actor_account); - T::Currency::set_lock( - STAKING_ID, + Self::update_lock( &actor_account, - value, + stake, >::block_number() + unbonding_period, - WithdrawReasons::all(), ); Self::remove_actor_from_service(actor_account, role, member_id); } + + // Locks account and only allows paying for transaction fees. Account cannot + // transfer or reserve funds. + fn update_lock(account: &T::AccountId, stake: BalanceOf, until: T::BlockNumber) { + T::Currency::set_lock( + STAKING_ID, + account, + stake, + until, + WithdrawReasons::all() & !(WithdrawReason::TransactionPayment | WithdrawReason::Fee), + ); + } } impl Roles for Module { @@ -277,25 +287,23 @@ decl_module! { for actor in accounts.into_iter().map(|account| Self::actor_by_account_id(account)) { if let Some(actor) = actor { if now > actor.joined_at + params.reward_period { - // send reward to member account - not the actor account - if let Ok(member_account) = T::Members::lookup_account_by_member_id(actor.member_id) { - let _ = T::Currency::deposit_into_existing(&member_account, params.reward); + // reward can top up balance if it is below minimum stake requirement + // this guarantees overtime that actor always covers the minimum stake and + // has enough balance to pay for tx fees + let balance = T::Currency::free_balance(&actor.account); + if balance < params.min_stake { + let _ = T::Currency::deposit_into_existing(&actor.account, params.reward); + } else { + // otherwise it should go the the member account + if let Ok(member_account) = T::Members::lookup_account_by_member_id(actor.member_id) { + let _ = T::Currency::deposit_into_existing(&member_account, params.reward); + } } } } } } } - - // eject actors not staking the minimum - // iterating over available roles, so if a role has been removed at some point - // and an actor hasn't unstaked .. this will not apply to them.. which doesn't really matter - // because they are no longer incentivised to stay in the role anyway - // TODO: this needs a bit more preparation. The right time to check for each actor is different, as they enter - // role at different times. - // for role in Self::available_roles().iter() { - // } - } pub fn role_entry_request(origin, role: Role, member_id: MemberId) { @@ -349,8 +357,9 @@ decl_module! { >::mutate(role, |accounts| accounts.push(actor_account.clone())); >::mutate(&member_id, |accounts| accounts.push(actor_account.clone())); - let value = T::Currency::free_balance(&actor_account); - T::Currency::set_lock(STAKING_ID, &actor_account, value, T::BlockNumber::max_value(), WithdrawReasons::all()); + + // Lock minimum stake, but allow spending for transaction fees + Self::update_lock(&actor_account, role_parameters.min_stake, T::BlockNumber::max_value()); >::insert(&actor_account, Actor { member_id, account: actor_account.clone(), @@ -376,13 +385,20 @@ decl_module! { let role_parameters = Self::ensure_role_parameters(actor.role)?; - Self::apply_unstake(actor.account.clone(), actor.role, actor.member_id, role_parameters.unbonding_period); + Self::apply_unstake(actor.account.clone(), actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake); Self::deposit_event(RawEvent::Unstaked(actor.account, actor.role)); } pub fn set_role_parameters(role: Role, params: RoleParameters) { + let new_stake = params.min_stake.clone(); >::insert(role, params); + // Update locks for all actors in the role. The lock for each account is already until max_value + // It doesn't affect actors which are unbonding, they should have already been removed from AccountIdsByRole + let accounts = Self::account_ids_by_role(role); + for account in accounts.into_iter() { + Self::update_lock(&account, new_stake, T::BlockNumber::max_value()); + } } pub fn set_available_roles(roles: Vec) { @@ -405,7 +421,7 @@ decl_module! { let member_id = T::Members::lookup_member_id(&actor_account)?; let actor = Self::ensure_actor_is_member(&actor_account, member_id)?; let role_parameters = Self::ensure_role_parameters(actor.role)?; - Self::apply_unstake(actor.account, actor.role, actor.member_id, role_parameters.unbonding_period); + Self::apply_unstake(actor.account, actor.role, actor.member_id, role_parameters.unbonding_period, role_parameters.min_stake); } } } diff --git a/src/storage/data_directory.rs b/src/storage/data_directory.rs index 246cc1b969..b407e11bfc 100644 --- a/src/storage/data_directory.rs +++ b/src/storage/data_directory.rs @@ -5,7 +5,7 @@ use parity_codec::Codec; use parity_codec_derive::{Decode, Encode}; use rstd::prelude::*; use runtime_primitives::traits::{ - As, MaybeDebug, MaybeSerializeDebug, Member, SimpleArithmetic, MaybeDisplay + As, MaybeDebug, MaybeDisplay, MaybeSerializeDebug, Member, SimpleArithmetic, }; use srml_support::{ decl_event, decl_module, decl_storage, dispatch, ensure, Parameter, StorageMap, StorageValue, @@ -17,8 +17,16 @@ pub trait Trait: timestamp::Trait + system::Trait + DOTRTrait + MaybeDebug { type ContentId: Parameter + Member + MaybeSerializeDebug + MaybeDisplay + Copy + Ord + Default; - type SchemaId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy - + As + As + MaybeSerializeDebug + PartialEq; + type SchemaId: Parameter + + Member + + SimpleArithmetic + + Codec + + Default + + Copy + + As + + As + + MaybeSerializeDebug + + PartialEq; type Members: Members; type Roles: Roles; @@ -61,7 +69,6 @@ pub struct DataObject { pub size: u64, pub liaison: T::AccountId, pub liaison_judgement: LiaisonJudgement, - // TODO signing_key: public key supplied by the uploader, // they sigh the content with this key @@ -290,7 +297,6 @@ impl ContentIdExists for Module { } impl Module { - fn current_block_and_time() -> BlockAndTime { BlockAndTime { block: >::block_number(), @@ -361,7 +367,8 @@ mod tests { assert!(res.is_err()); // However, with the liaison as origin it should. - let res = TestDataDirectory::accept_content(Origin::signed(TEST_MOCK_LIAISON), content_id); + let res = + TestDataDirectory::accept_content(Origin::signed(TEST_MOCK_LIAISON), content_id); assert!(res.is_ok()); }); } @@ -389,7 +396,8 @@ mod tests { assert!(res.is_err()); // However, with the liaison as origin it should. - let res = TestDataDirectory::reject_content(Origin::signed(TEST_MOCK_LIAISON), content_id); + let res = + TestDataDirectory::reject_content(Origin::signed(TEST_MOCK_LIAISON), content_id); assert!(res.is_ok()); }); } diff --git a/src/storage/data_object_storage_registry.rs b/src/storage/data_object_storage_registry.rs index 7b6cf5c9fd..84b7715c01 100644 --- a/src/storage/data_object_storage_registry.rs +++ b/src/storage/data_object_storage_registry.rs @@ -14,8 +14,16 @@ pub trait Trait: timestamp::Trait + system::Trait + DDTrait + MaybeDebug { type Event: From> + Into<::Event>; // TODO deprecated - type DataObjectStorageRelationshipId: Parameter + Member + SimpleArithmetic + Codec - + Default + Copy + As + As + MaybeSerializeDebug + PartialEq; + type DataObjectStorageRelationshipId: Parameter + + Member + + SimpleArithmetic + + Codec + + Default + + Copy + + As + + As + + MaybeSerializeDebug + + PartialEq; type Members: Members; type Roles: Roles; @@ -43,7 +51,7 @@ pub struct DataObjectStorageRelationship { decl_storage! { trait Store for Module as DataObjectStorageRegistry { - + // TODO deprecated // Start at this value pub FirstRelationshipId get(first_relationship_id) config(first_relationship_id): T::DataObjectStorageRelationshipId = T::DataObjectStorageRelationshipId::sa(DEFAULT_FIRST_RELATIONSHIP_ID); @@ -84,7 +92,7 @@ decl_event! { // TODO deprecated DataObjectStorageRelationshipAdded(DataObjectStorageRelationshipId, ContentId, AccountId), DataObjectStorageRelationshipReadyUpdated(DataObjectStorageRelationshipId, bool), - + // NEW & COOL StorageProviderAddedContent(AccountId, ContentId), StorageProviderRemovedContent(AccountId, ContentId), @@ -92,7 +100,6 @@ decl_event! { } impl ContentHasStorage for Module { - // TODO deprecated fn has_storage_provider(which: &T::ContentId) -> bool { let dosr_list = Self::relationships_by_content_id(which); diff --git a/src/storage/data_object_type_registry.rs b/src/storage/data_object_type_registry.rs index 8dd6bad1d0..bacceb0374 100644 --- a/src/storage/data_object_type_registry.rs +++ b/src/storage/data_object_type_registry.rs @@ -8,8 +8,16 @@ use srml_support::{decl_event, decl_module, decl_storage, Parameter, StorageMap, pub trait Trait: system::Trait + MaybeDebug { type Event: From> + Into<::Event>; - type DataObjectTypeId: Parameter + Member + SimpleArithmetic + Codec + Default + Copy - + As + As + MaybeSerializeDebug + PartialEq; + type DataObjectTypeId: Parameter + + Member + + SimpleArithmetic + + Codec + + Default + + Copy + + As + + As + + MaybeSerializeDebug + + PartialEq; } static MSG_DO_TYPE_NOT_FOUND: &str = "Data Object Type with the given ID not found."; @@ -199,10 +207,7 @@ mod tests { description: "bar".as_bytes().to_vec(), active: false, }; - let res = TestDataObjectTypeRegistry::update_data_object_type( - dot_id + 1, - updated1, - ); + let res = TestDataObjectTypeRegistry::update_data_object_type(dot_id + 1, updated1); assert!(res.is_err()); // Finally with an existing ID, it should work. @@ -210,8 +215,7 @@ mod tests { description: "bar".as_bytes().to_vec(), active: false, }; - let res = - TestDataObjectTypeRegistry::update_data_object_type(dot_id, updated3); + let res = TestDataObjectTypeRegistry::update_data_object_type(dot_id, updated3); assert!(res.is_ok()); assert_eq!( *System::events().last().unwrap(), @@ -248,7 +252,8 @@ mod tests { ); // Retrieve, and ensure it's not active. - let data = TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); + let data = + TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); assert!(data.is_some()); assert!(!data.unwrap().active); @@ -270,7 +275,8 @@ mod tests { ); // Ensure that the item is actually activated. - let data = TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); + let data = + TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); assert!(data.is_some()); assert!(data.unwrap().active); @@ -279,7 +285,8 @@ mod tests { TEST_FIRST_DATA_OBJECT_TYPE_ID, ); assert!(res.is_ok()); - let data = TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); + let data = + TestDataObjectTypeRegistry::data_object_types(TEST_FIRST_DATA_OBJECT_TYPE_ID); assert!(data.is_some()); assert!(!data.unwrap().active); }); diff --git a/src/storage/mock.rs b/src/storage/mock.rs index 091864230a..3190edf233 100644 --- a/src/storage/mock.rs +++ b/src/storage/mock.rs @@ -1,8 +1,6 @@ #![cfg(test)] -pub use super::{ - data_directory, data_object_storage_registry, data_object_type_registry, -}; +pub use super::{data_directory, data_object_storage_registry, data_object_type_registry}; use crate::currency::GovernanceCurrency; use crate::roles::actors; use crate::traits; diff --git a/wasm/Cargo.lock b/wasm/Cargo.lock index 44a5257451..64acf5ad3c 100644 --- a/wasm/Cargo.lock +++ b/wasm/Cargo.lock @@ -756,7 +756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "joystream-node-runtime" -version = "5.1.0" +version = "5.2.0" dependencies = [ "parity-codec 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -789,9 +789,9 @@ dependencies = [ [[package]] name = "joystream-node-runtime-wasm" -version = "1.0.1" +version = "5.2.0" dependencies = [ - "joystream-node-runtime 5.1.0", + "joystream-node-runtime 5.2.0", ] [[package]] diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 529e875ebd..e2086689da 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -20,4 +20,4 @@ path = '..' authors = ['Joystream'] edition = '2018' name = 'joystream-node-runtime-wasm' -version = '1.0.1' +version = '5.2.0'