Browse Source

Add add_curator_opening() method to the new bureaucracy module

- create new bureaucracy module
- add add_curator_opening() method
Shamil Gadelshin 4 years ago
parent
commit
09a00d73cf

+ 16 - 0
Cargo.lock

@@ -4461,6 +4461,22 @@ dependencies = [
  "sha2",
 ]
 
+[[package]]
+name = "substrate-bureaucracy-module"
+version = "1.0.0"
+dependencies = [
+ "parity-scale-codec",
+ "serde",
+ "sr-io",
+ "sr-primitives",
+ "sr-std",
+ "srml-support",
+ "srml-system",
+ "substrate-hiring-module",
+ "substrate-primitives",
+ "substrate-stake-module",
+]
+
 [[package]]
 name = "substrate-chain-spec"
 version = "2.0.0"

+ 1 - 0
Cargo.toml

@@ -19,6 +19,7 @@ members = [
 	"runtime-modules/token-minting",
 	"runtime-modules/versioned-store",
 	"runtime-modules/versioned-store-permissions",
+	"runtime-modules/bureaucracy",
 	"node",
 	"utils/chain-spec-builder/"
 ]

+ 61 - 0
runtime-modules/bureaucracy/Cargo.toml

@@ -0,0 +1,61 @@
+[package]
+name = 'substrate-bureaucracy-module'
+version = '1.0.0'
+authors = ['Joystream contributors']
+edition = '2018'
+
+[features]
+default = ['std']
+std = [
+	'sr-primitives/std',
+	'srml-support/std',
+	'system/std',
+	'codec/std',
+	'rstd/std',
+	'serde',
+    'hiring/std',
+    'stake/std'
+]
+
+
+[dependencies]
+codec = { package = 'parity-scale-codec', version = '1.0.0', default-features = false, features = ['derive'] }
+primitives = { package = 'substrate-primitives', git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+rstd = { package = 'sr-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+
+
+[dev-dependencies]
+runtime-io = { package = 'sr-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'}
+
+[dependencies.serde]
+features = ['derive']
+optional = true
+version = '1.0.101'
+
+[dependencies.sr-primitives]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'sr-primitives'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.srml-support]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'srml-support'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.system]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'srml-system'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.hiring]
+default_features = false
+package = 'substrate-hiring-module'
+path = '../hiring'
+
+[dependencies.stake]
+default_features = false
+package = 'substrate-stake-module'
+path = '../stake'

+ 40 - 0
runtime-modules/bureaucracy/src/constraints.rs

@@ -0,0 +1,40 @@
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+use codec::{Encode, Decode};
+
+/// Length constraint for input validation
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
+#[derive(Encode, Decode, Default, Clone, PartialEq, Eq)]
+pub struct InputValidationLengthConstraint {
+	/// Minimum length
+	pub min: u16,
+
+	/// Difference between minimum length and max length.
+	/// While having max would have been more direct, this
+	/// way makes max < min unrepresentable semantically,
+	/// which is safer.
+	pub max_min_diff: u16,
+}
+
+impl InputValidationLengthConstraint {
+	/// Helper for computing max
+	pub fn max(&self) -> u16 {
+		self.min + self.max_min_diff
+	}
+
+	pub fn ensure_valid(
+		&self,
+		len: usize,
+		too_short_msg: &'static str,
+		too_long_msg: &'static str,
+	) -> Result<(), &'static str> {
+		let length = len as u16;
+		if length < self.min {
+			Err(too_short_msg)
+		} else if length > self.max() {
+			Err(too_long_msg)
+		} else {
+			Ok(())
+		}
+	}
+}

+ 203 - 0
runtime-modules/bureaucracy/src/lib.rs

@@ -0,0 +1,203 @@
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+
+mod types;
+mod constraints;
+
+use system;
+use srml_support::{decl_module, decl_storage, dispatch};
+use rstd::collections::btree_set::BTreeSet;
+use sr_primitives::traits::{One, }; // Member, SimpleArithmetic, MaybeSerialize, Zero
+use srml_support::traits::Currency;
+
+use types::{OpeningPolicyCommitment, CuratorOpening};
+use constraints::InputValidationLengthConstraint;
+
+
+pub static MSG_CHANNEL_DESCRIPTION_TOO_SHORT: &str = "Channel description too short";
+pub static MSG_CHANNEL_DESCRIPTION_TOO_LONG: &str = "Channel description too long";
+
+/// Type for the identifer for an opening for a curator.
+pub type CuratorOpeningId<T> = <T as hiring::Trait>::OpeningId;
+
+/// Tyoe for the indentifier for an application as a curator.
+pub type CuratorApplicationId<T> = <T as hiring::Trait>::ApplicationId;
+
+/// Balance type of runtime
+pub type BalanceOf<T> =
+<<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
+
+
+pub trait Trait<I: Instance>: system::Trait + hiring::Trait{}
+
+decl_storage! {
+    trait Store for Module<T: Trait<I>, I: Instance> as Bureaucracy {
+        pub TestData get(test_data): u32;
+
+        /// Next identifier valuefor new curator opening.
+        pub NextCuratorOpeningId get(next_curator_opening_id): CuratorOpeningId<T>;
+
+        /// Maps identifeir to curator opening.
+        pub CuratorOpeningById get(curator_opening_by_id): linked_map CuratorOpeningId<T> => CuratorOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>>;
+
+        pub OpeningHumanReadableText get(opening_human_readable_text): InputValidationLengthConstraint;
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait<I>, I: Instance> for enum Call where origin: T::Origin {
+        /// Add an opening for a curator role.
+        pub fn add_curator_opening(origin, activate_at: hiring::ActivateOpeningAt<T::BlockNumber>, commitment: OpeningPolicyCommitment<T::BlockNumber, BalanceOf<T>>, human_readable_text: Vec<u8>)  {
+
+            // Ensure lead is set and is origin signer
+ //           Self::ensure_origin_is_set_lead(origin)?;
+
+            // Ensure human radable text is valid
+            Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
+
+            // Add opening
+            // NB: This call can in principle fail, because the staking policies
+            // may not respect the minimum currency requirement.
+
+            let policy_commitment = commitment.clone();
+
+            // let opening_id = ensure_on_wrapped_error!(
+            //     hiring::Module::<T>::add_opening(
+            //         activate_at,
+            //         commitment.max_review_period_length,
+            //         commitment.application_rationing_policy,
+            //         commitment.application_staking_policy,
+            //         commitment.role_staking_policy,
+            //         human_readable_text,
+            //     ))?;
+
+            let opening_id = hiring::Module::<T>::add_opening(
+                activate_at,
+                commitment.max_review_period_length,
+                commitment.application_rationing_policy,
+                commitment.application_staking_policy,
+                commitment.role_staking_policy,
+                human_readable_text,
+            ).unwrap(); //TODO
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let new_curator_opening_id = NextCuratorOpeningId::<T, I>::get();
+
+            // Create and add curator opening.
+            let new_opening_by_id = CuratorOpening::<CuratorOpeningId<T>, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>> {
+                opening_id : opening_id,
+                curator_applications: BTreeSet::new(),
+                policy_commitment: policy_commitment
+            };
+
+            CuratorOpeningById::<T, I>::insert(new_curator_opening_id, new_opening_by_id);
+
+            // Update NextCuratorOpeningId
+            NextCuratorOpeningId::<T, I>::mutate(|id| *id += <CuratorOpeningId<T> as One>::one());
+
+            // Trigger event
+            //Self::deposit_event(RawEvent::CuratorOpeningAdded(new_curator_opening_id));
+        }
+    }
+}
+
+
+impl<T: Trait<I>, I: Instance> Module<T, I> {
+    pub fn set_test_data(data : u32) {
+        <TestData<I>>::put(data);
+    }
+
+    fn ensure_opening_human_readable_text_is_valid(text: &Vec<u8>) -> dispatch::Result {
+        <OpeningHumanReadableText<I>>::get().ensure_valid(
+            text.len(),
+            MSG_CHANNEL_DESCRIPTION_TOO_SHORT,
+            MSG_CHANNEL_DESCRIPTION_TOO_LONG,
+        )
+    }
+}
+
+
+
+#[cfg(test)]
+mod test {
+
+    use primitives::H256;
+    use sr_primitives::{
+        testing::Header,
+        traits::{BlakeTwo256, IdentityLookup},
+        Perbill,
+    };
+    use srml_support::{impl_outer_origin, parameter_types};
+
+    use crate::{Module, Trait};
+
+    impl_outer_origin! {
+            pub enum Origin for Test {}
+    }
+
+    parameter_types! {
+        pub const BlockHashCount: u64 = 250;
+        pub const MaximumBlockWeight: u32 = 1024;
+        pub const MaximumBlockLength: u32 = 2 * 1024;
+        pub const AvailableBlockRatio: Perbill = Perbill::one();
+        pub const MinimumPeriod: u64 = 5;
+    }
+
+    // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
+    #[derive(Clone, PartialEq, Eq, Debug)]
+    pub struct Test;
+
+    impl system::Trait for Test {
+        type Origin = Origin;
+        type Index = u64;
+        type BlockNumber = u64;
+        type Call = ();
+        type Hash = H256;
+        type Hashing = BlakeTwo256;
+        type AccountId = u64;
+        type Lookup = IdentityLookup<Self::AccountId>;
+        type Header = Header;
+        type Event = ();
+        type BlockHashCount = BlockHashCount;
+        type MaximumBlockWeight = MaximumBlockWeight;
+        type MaximumBlockLength = MaximumBlockLength;
+        type AvailableBlockRatio = AvailableBlockRatio;
+        type Version = ();
+    }
+
+    impl Trait<Instance1> for Test {}
+    impl Trait<Instance2> for Test {}
+
+    use crate::Instance1;
+    use crate::Instance2;
+
+    type Bureaucracy1 = Module<Test, Instance1>;
+    type Bureaucracy2 = Module<Test, Instance2>;
+
+    impl Trait<Instance15> for Test {}
+    use crate::Instance15;
+    type Bureaucracy16 = Module<Test, Instance15>;
+
+	pub fn build_test_externalities() -> runtime_io::TestExternalities {
+		let t = system::GenesisConfig::default()
+			.build_storage::<Test>()
+			.unwrap();
+
+		t.into()
+	}
+
+
+
+	#[test]
+    fn test_instances_storage_separation() {
+        build_test_externalities().execute_with(|| {
+            Bureaucracy1::set_test_data(10);
+
+            assert_eq!(Bureaucracy1::test_data(), 10);
+            assert_eq!(Bureaucracy2::test_data(), 10);
+        });
+	}
+}

+ 92 - 0
runtime-modules/bureaucracy/src/types.rs

@@ -0,0 +1,92 @@
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+use codec::{Encode, Decode};
+use rstd::collections::btree_set::BTreeSet;
+
+/// Terms for slashings applied to a given role
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
+pub struct SlashableTerms {
+	/// Maximum number of slashes.
+	pub max_count: u16,
+
+	/// Maximum percentage points of remaining stake which may be slashed in a single slash.
+	pub max_percent_pts_per_time: u16,
+}
+
+/// Terms for what slashing can be applied in some context
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq)]
+pub enum SlashingTerms {
+	Unslashable,
+	Slashable(SlashableTerms),
+}
+
+/// Must be default constructible because it indirectly is a value in a storage map.
+/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
+impl Default for SlashingTerms {
+	fn default() -> Self {
+		Self::Unslashable
+	}
+}
+
+/// A commitment to the set of policy variables relevant to an opening.
+/// An applicant can observe this commitment and be secure that the terms
+/// of the application process cannot be changed ex-post.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, Default, PartialEq, Eq)]
+pub struct OpeningPolicyCommitment<BlockNumber, Balance> {
+	/// Rationing to be used
+	pub application_rationing_policy: Option<hiring::ApplicationRationingPolicy>,
+
+	/// Maximum length of review period of applications
+	pub max_review_period_length: BlockNumber,
+
+	/// Staking policy for application
+	pub application_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
+
+	/// Staking policy for role itself
+	pub role_staking_policy: Option<hiring::StakingPolicy<Balance, BlockNumber>>,
+
+	// Slashing terms during application
+	// pub application_slashing_terms: SlashingTerms,
+
+	// Slashing terms during role, NOT application itself!
+	pub role_slashing_terms: SlashingTerms,
+
+	/// When filling an opening: Unstaking period for application stake of successful applicants
+	pub fill_opening_successful_applicant_application_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When filling an opening:
+	pub fill_opening_failed_applicant_application_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When filling an opening:
+	pub fill_opening_failed_applicant_role_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When terminating a curator:
+	pub terminate_curator_application_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When terminating a curator:
+	pub terminate_curator_role_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When a curator exists: ..
+	pub exit_curator_role_application_stake_unstaking_period: Option<BlockNumber>,
+
+	/// When a curator exists: ..
+	pub exit_curator_role_stake_unstaking_period: Option<BlockNumber>,
+}
+
+
+/// An opening for a curator role.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
+pub struct CuratorOpening<OpeningId, BlockNumber, Balance, CuratorApplicationId: core::cmp::Ord> {
+	/// Identifer for underlying opening in the hiring module.
+	pub opening_id: OpeningId,
+
+	/// Set of identifiers for all curator applications ever added
+	pub curator_applications: BTreeSet<CuratorApplicationId>,
+
+	/// Commitment to policies in opening.
+	pub policy_commitment: OpeningPolicyCommitment<BlockNumber, Balance>,
+}