Parcourir la source

runtime: Adding new ‘team’ pallet infrastructure

- module, trait, storage, events, errors
- mock & tests
Shamil Gadelshin il y a 4 ans
Parent
commit
8aa0b55862

+ 18 - 0
Cargo.lock

@@ -3721,6 +3721,24 @@ dependencies = [
  "sp-std",
 ]
 
+[[package]]
+name = "pallet-team"
+version = "1.0.0"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-common",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "serde",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
 [[package]]
 name = "pallet-timestamp"
 version = "2.0.0-rc4"

+ 1 - 0
Cargo.toml

@@ -19,6 +19,7 @@ members = [
 	"runtime-modules/versioned-store",
 	"runtime-modules/versioned-store-permissions",
 	"runtime-modules/working-group",
+	"runtime-modules/team",
 	"node",
 	"utils/chain-spec-builder/"
 ]

+ 34 - 0
runtime-modules/team/Cargo.toml

@@ -0,0 +1,34 @@
+[package]
+name = "pallet-team"
+version = "1.0.0"
+authors = ['Joystream contributors']
+edition = '2018'
+
+[dependencies]
+serde = { version = "1.0.101", optional = true, features = ["derive"] }
+codec = { package = 'parity-scale-codec', version = '1.3.1', default-features = false, features = ['derive'] }
+sp-runtime = { package = 'sp-runtime', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+frame-support = { package = 'frame-support', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+system = { package = 'frame-system', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+sp-arithmetic = { package = 'sp-arithmetic', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+sp-std = { package = 'sp-std', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+common = { package = 'pallet-common', default-features = false, path = '../common'}
+
+[dev-dependencies]
+sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+balances = { package = 'pallet-balances', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+pallet-timestamp = { package = 'pallet-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'}
+
+[features]
+default = ['std']
+std = [
+	'serde',
+	'codec/std',
+	'sp-runtime/std',
+	'frame-support/std',
+	'system/std',
+	'sp-arithmetic/std',
+	'sp-std/std',
+	'common/std',
+]

+ 12 - 0
runtime-modules/team/src/errors.rs

@@ -0,0 +1,12 @@
+#![warn(missing_docs)]
+
+use crate::{Instance, Module, Trait};
+use frame_support::decl_error;
+
+decl_error! {
+    /// Discussion module predefined errors
+    pub enum Error for Module<T: Trait<I>, I: Instance>{
+        /// Provided stake balance cannot be zero.
+        StakeBalanceCannotBeZero,
+    }
+}

+ 137 - 0
runtime-modules/team/src/lib.rs

@@ -0,0 +1,137 @@
+// Ensure we're `no_std` when compiling for Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+
+// Do not delete! Cannot be uncommented by default, because of Parity decl_module! issue.
+//#![warn(missing_docs)]
+
+mod errors;
+#[cfg(test)]
+mod tests;
+mod types;
+
+use codec::Codec;
+// use frame_support::dispatch::{DispatchError, DispatchResult};
+//use frame_support::storage::IterableStorageMap;
+//use frame_support::traits::{Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons};
+use frame_support::traits::Get;
+use frame_support::{decl_event, decl_module, decl_storage, Parameter, StorageValue}; // ensure, print,
+use sp_arithmetic::traits::{BaseArithmetic, One};
+use sp_runtime::traits::{MaybeSerialize, Member};
+// use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
+// use sp_std::vec;
+// use sp_std::vec::Vec;
+// use system::{ensure_root, ensure_signed};
+
+//use common::constraints::InputValidationLengthConstraint;
+
+pub use errors::Error;
+pub use types::{JobOpening, JobOpeningType};
+
+/// The _Team_ main _Trait_
+pub trait Trait<I: Instance>: system::Trait {
+    /// OpeningId type
+    type OpeningId: Parameter
+        + Member
+        + BaseArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
+    /// _Administration_ event type.
+    type Event: From<Event<Self, I>> + Into<<Self as system::Trait>::Event>;
+
+    /// Defines max workers number in the team.
+    type MaxWorkerNumberLimit: Get<u32>;
+}
+
+decl_event!(
+    /// _Team_ events
+    pub enum Event<T, I>
+    where
+       <T as Trait<I>>::OpeningId,
+    {
+        /// Emits on adding new job opening.
+        /// Params:
+        /// - Opening id
+        OpeningAdded(OpeningId),
+    }
+);
+
+decl_storage! {
+    trait Store for Module<T: Trait<I>, I: Instance> as Team {
+        /// Next identifier value for new job opening.
+        pub NextOpeningId get(fn next_opening_id): T::OpeningId;
+
+        /// Maps identifier to job opening.
+        pub OpeningById get(fn opening_by_id): map hasher(blake2_128_concat)
+            T::OpeningId => JobOpening<T::BlockNumber>;
+
+        /// Count of active workers.
+        pub ActiveWorkerCount get(fn active_worker_count): u32;
+    }
+}
+
+decl_module! {
+    /// _Working group_ substrate module.
+    pub struct Module<T: Trait<I>, I: Instance> for enum Call where origin: T::Origin {
+        /// Default deposit_event() handler
+        fn deposit_event() = default;
+
+        /// Predefined errors
+        type Error = Error<T, I>;
+
+        /// Exports const -  max simultaneous active worker number.
+        const MaxWorkerNumberLimit: u32 = T::MaxWorkerNumberLimit::get();
+
+        // ****************** Hiring flow **********************
+
+        /// Add a job opening for a worker role.
+        /// Require signed leader origin or the root (to add opening for the leader position).
+        #[weight = 10_000_000] // TODO: adjust weight
+        pub fn add_opening(
+            origin,
+            human_readable_text: Vec<u8>,
+            opening_type: JobOpeningType,
+        ){
+            // Self::ensure_origin_for_opening_type(origin, opening_type)?;
+
+            // Self::ensure_opening_human_readable_text_is_valid(&human_readable_text)?;
+
+            // Self::ensure_opening_policy_commitment_is_valid(&commitment)?;
+
+
+            //
+            // == MUTATION SAFE ==
+            //
+
+            let new_opening_id = NextOpeningId::<T, I>::get();
+
+            // Create and add worker opening.
+            let new_opening_by_id = JobOpening::<T::BlockNumber> {
+//                applications: BTreeSet::new(),
+//                policy_commitment,
+                opening_type,
+                created: Self::current_block(),
+                description_hash: Vec::new(), // TODO
+                is_active: true,
+            };
+
+            OpeningById::<T, I>::insert(new_opening_id, new_opening_by_id);
+
+            // Update NextOpeningId
+            NextOpeningId::<T, I>::mutate(|id| *id += <T::OpeningId as One>::one());
+
+            // Trigger event
+            Self::deposit_event(RawEvent::OpeningAdded(new_opening_id));
+        }
+    }
+}
+
+impl<T: Trait<I>, I: Instance> Module<T, I> {
+    // Wrapper-function over system::block_number()
+    fn current_block() -> T::BlockNumber {
+        <system::Module<T>>::block_number()
+    }
+}

+ 123 - 0
runtime-modules/team/src/tests/mock.rs

@@ -0,0 +1,123 @@
+use frame_support::traits::{OnFinalize, OnInitialize};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use sp_core::H256;
+use sp_runtime::{
+    testing::Header,
+    traits::{BlakeTwo256, IdentityLookup},
+    Perbill,
+};
+use system;
+
+use crate::{Module, Trait};
+
+impl_outer_origin! {
+    pub enum Origin for Test {}
+}
+
+mod working_team {
+    pub use super::TestWorkingTeamInstance;
+    pub use crate::Event;
+}
+
+impl_outer_event! {
+    pub enum TestEvent for Test {
+        balances<T>,
+        working_team TestWorkingTeamInstance <T>,
+        system<T>,
+    }
+}
+
+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;
+    pub const StakePoolId: [u8; 8] = *b"joystake";
+    pub const ExistentialDeposit: u32 = 0;
+}
+
+// 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 BaseCallFilter = ();
+    type Origin = Origin;
+    type Call = ();
+    type Index = u64;
+    type BlockNumber = u64;
+    type Hash = H256;
+    type Hashing = BlakeTwo256;
+    type AccountId = u64;
+    type Lookup = IdentityLookup<Self::AccountId>;
+    type Header = Header;
+    type Event = TestEvent;
+    type BlockHashCount = BlockHashCount;
+    type MaximumBlockWeight = MaximumBlockWeight;
+    type DbWeight = ();
+    type BlockExecutionWeight = ();
+    type ExtrinsicBaseWeight = ();
+    type MaximumExtrinsicWeight = ();
+    type MaximumBlockLength = MaximumBlockLength;
+    type AvailableBlockRatio = AvailableBlockRatio;
+    type Version = ();
+    type ModuleToIndex = ();
+    type AccountData = balances::AccountData<u64>;
+    type OnNewAccount = ();
+    type OnKilledAccount = ();
+}
+
+impl common::currency::GovernanceCurrency for Test {
+    type Currency = Balances;
+}
+
+impl pallet_timestamp::Trait for Test {
+    type Moment = u64;
+    type OnTimestampSet = ();
+    type MinimumPeriod = MinimumPeriod;
+}
+
+impl balances::Trait for Test {
+    type Balance = u64;
+    type DustRemoval = ();
+    type Event = TestEvent;
+    type ExistentialDeposit = ExistentialDeposit;
+    type AccountStore = System;
+}
+
+pub type Balances = balances::Module<Test>;
+pub type System = system::Module<Test>;
+
+parameter_types! {
+    pub const MaxWorkerNumberLimit: u32 = 3;
+}
+
+impl Trait<TestWorkingTeamInstance> for Test {
+    type Event = TestEvent;
+    type MaxWorkerNumberLimit = MaxWorkerNumberLimit;
+    type OpeningId = u64;
+}
+
+pub type TestWorkingTeamInstance = crate::Instance1;
+pub type TestWorkingTeam = Module<Test, TestWorkingTeamInstance>;
+
+pub fn build_test_externalities() -> sp_io::TestExternalities {
+    let t = system::GenesisConfig::default()
+        .build_storage::<Test>()
+        .unwrap();
+
+    t.into()
+}
+
+// Recommendation from Parity on testing on_finalize
+// https://substrate.dev/docs/en/next/development/module/tests
+pub fn run_to_block(n: u64) {
+    while System::block_number() < n {
+        <System as OnFinalize<u64>>::on_finalize(System::block_number());
+        <TestWorkingTeam as OnFinalize<u64>>::on_finalize(System::block_number());
+        System::set_block_number(System::block_number() + 1);
+        <System as OnInitialize<u64>>::on_initialize(System::block_number());
+        <TestWorkingTeam as OnInitialize<u64>>::on_initialize(System::block_number());
+    }
+}

+ 10 - 0
runtime-modules/team/src/tests/mod.rs

@@ -0,0 +1,10 @@
+mod mock;
+
+use mock::{build_test_externalities, Test};
+
+#[test]
+fn mock_test() {
+    build_test_externalities().execute_with(|| {
+        assert!(true);
+    });
+}

+ 47 - 0
runtime-modules/team/src/types.rs

@@ -0,0 +1,47 @@
+#![warn(missing_docs)]
+
+use codec::{Decode, Encode};
+//use sp_std::collections::btree_set::BTreeSet;
+
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+
+/// Job opening for the normal or leader position.
+/// An opening represents the process of hiring one or more new actors into some available role.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, Eq)]
+pub struct JobOpening<BlockNumber> {
+    /// Set of identifiers for all worker applications ever added.
+    //	pub applications: BTreeSet<JobApplicationId>,
+
+    /// Defines opening type: Leader or worker.
+    pub opening_type: JobOpeningType,
+
+    /// Block at which opening was added.
+    pub created: BlockNumber,
+
+    /// Hash of the opening description.
+    pub description_hash: Vec<u8>,
+
+    /// Opening status.
+    pub is_active: bool,
+}
+
+/// Defines type of the opening: regular working group fellow or group leader.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, Copy)]
+pub enum JobOpeningType {
+    /// Group leader.
+    Leader,
+
+    /// Regular worker.
+    Regular,
+}
+
+/// 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 JobOpeningType {
+    fn default() -> Self {
+        Self::Regular
+    }
+}