diff --git a/Cargo.lock b/Cargo.lock index 7c352b3..0c7013e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1430,6 +1430,30 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "create-department" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-support-test", + "frame-system", + "pallet-balances", + "pallet-schelling-game-shared", + "pallet-shared-storage", + "pallet-sortition-sum-game", + "pallet-support", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=polkadot-v1.9.0)", + "trait-schelling-game-shared", + "trait-shared-storage", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" diff --git a/Cargo.toml b/Cargo.toml index a7bcee2..b407f23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ "custom-pallets/sortition-sum-game", "custom-pallets/spaces", "custom-pallets/support", - "runtime", + "runtime", "custom-pallets/create-department", ] resolver = "2" [profile.release] diff --git a/custom-pallets/create-department/Cargo.toml b/custom-pallets/create-department/Cargo.toml new file mode 100644 index 0000000..c204931 --- /dev/null +++ b/custom-pallets/create-department/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "create-department" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +homepage.workspace = true + + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +frame-benchmarking = { workspace = true , optional = true} +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true} +pallet-timestamp = { workspace = true } +pallet-balances = { workspace = true } +pallet-support = { workspace = true } +pallet-shared-storage = { workspace = true } +trait-shared-storage = { workspace = true } +pallet-schelling-game-shared = { workspace = true } +trait-schelling-game-shared = { workspace = true } +pallet-sortition-sum-game = { workspace = true } + +[dev-dependencies] +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +frame-support-test = { workspace = true } + +[features] +default = ["std"] +std = [ + "parity-scale-codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "pallet-timestamp/std", + "pallet-balances/std", + "pallet-support/std", + "pallet-shared-storage/std", + "pallet-schelling-game-shared/std", + "pallet-sortition-sum-game/std", + "frame-support-test/std", +] +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] + diff --git a/custom-pallets/create-department/src/benchmarking.rs b/custom-pallets/create-department/src/benchmarking.rs new file mode 100644 index 0000000..5a26241 --- /dev/null +++ b/custom-pallets/create-department/src/benchmarking.rs @@ -0,0 +1,35 @@ +//! Benchmarking setup for pallet-template +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::v2::*; +use frame_system::RawOrigin; + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn do_something() { + let value = 100u32.into(); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + do_something(RawOrigin::Signed(caller), value); + + assert_eq!(Something::::get(), Some(value)); + } + + #[benchmark] + fn cause_error() { + Something::::put(100u32); + let caller: T::AccountId = whitelisted_caller(); + #[extrinsic_call] + cause_error(RawOrigin::Signed(caller)); + + assert_eq!(Something::::get(), Some(101u32)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/custom-pallets/create-department/src/lib.rs b/custom-pallets/create-department/src/lib.rs new file mode 100644 index 0000000..7edb45a --- /dev/null +++ b/custom-pallets/create-department/src/lib.rs @@ -0,0 +1,229 @@ +//! # Template Pallet +//! +//! A pallet with minimal functionality to help developers understand the essential components of +//! writing a FRAME pallet. It is typically used in beginner tutorials or in Substrate template +//! nodes as a starting point for creating a new pallet and **not meant to be used in production**. +//! +//! ## Overview +//! +//! This template pallet contains basic examples of: +//! - declaring a storage item that stores a single `u32` value +//! - declaring and using events +//! - declaring and using errors +//! - a dispatchable function that allows a user to set a new value to storage and emits an event +//! upon success +//! - another dispatchable function that causes a custom error to be thrown +//! +//! Each pallet section is annotated with an attribute using the `#[pallet::...]` procedural macro. +//! This macro generates the necessary code for a pallet to be aggregated into a FRAME runtime. +//! +//! Learn more about FRAME macros [here](https://docs.substrate.io/reference/frame-macros/). +//! +//! ### Pallet Sections +//! +//! The pallet sections in this template are: +//! +//! - A **configuration trait** that defines the types and parameters which the pallet depends on +//! (denoted by the `#[pallet::config]` attribute). See: [`Config`]. +//! - A **means to store pallet-specific data** (denoted by the `#[pallet::storage]` attribute). +//! See: [`storage_types`]. +//! - A **declaration of the events** this pallet emits (denoted by the `#[pallet::event]` +//! attribute). See: [`Event`]. +//! - A **declaration of the errors** that this pallet can throw (denoted by the `#[pallet::error]` +//! attribute). See: [`Error`]. +//! - A **set of dispatchable functions** that define the pallet's functionality (denoted by the +//! `#[pallet::call]` attribute). See: [`dispatchables`]. +//! +//! Run `cargo doc --package pallet-template --open` to view this pallet's documentation. + +// We make sure this pallet uses `no_std` for compiling to Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +// FRAME pallets require their own "mock runtimes" to be able to run unit tests. This module +// contains a mock runtime specific for testing this pallet's functionality. +#[cfg(test)] +mod mock; + +// This module contains the unit tests for this pallet. +// Learn about pallet unit testing here: https://docs.substrate.io/test/unit-testing/ +#[cfg(test)] +mod tests; + + + +// Every callable function or "dispatchable" a pallet exposes must have weight values that correctly +// estimate a dispatchable's execution time. The benchmarking module is used to calculate weights +// for each dispatchable and generates this pallet's weight.rs file. Learn more about benchmarking here: https://docs.substrate.io/test/benchmark/ +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; +pub use weights::*; +pub mod types; + +pub use types::{DepartmentDetails,FIRST_DEPARTMENT_ID}; + +type DepartmentId = u64; +use pallet_support::{ + ensure_content_is_valid, new_who_and_when, remove_from_vec, Content, WhoAndWhen, WhoAndWhenOf, +}; + +// All pallet logic is defined in its own module and must be annotated by the `pallet` attribute. +#[frame_support::pallet(dev_mode)] +pub mod pallet { + // Import various useful types required by all FRAME pallets. + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + // The `Pallet` struct serves as a placeholder to implement traits, methods and dispatchables + // (`Call`s) in this pallet. + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + /// The pallet's configuration trait. + /// + /// All our types and constants a pallet depends on must be declared here. + /// These types are defined generically and made concrete when the pallet is declared in the + /// `runtime/src/lib.rs` file of your chain. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_timestamp::Config { + /// The overarching runtime event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// A type representing the weights required by the dispatchables of this pallet. + type WeightInfo: WeightInfo; + } + + /// A storage item for this pallet. + /// + /// In this template, we are declaring a storage item called `Something` that stores a single + /// `u32` value. Learn more about runtime storage here: + #[pallet::storage] + pub type Something = StorageValue<_, u32>; + + #[pallet::type_value] + pub fn DefaultForNextDepartmentId() -> DepartmentId { + FIRST_DEPARTMENT_ID + } + + #[pallet::storage] + #[pallet::getter(fn next_department_id)] + pub type NextDepartmentId = + StorageValue<_, DepartmentId, ValueQuery, DefaultForNextDepartmentId>; + + #[pallet::storage] + #[pallet::getter(fn department_profile)] + pub type DepartmentProfile = + StorageMap<_, Blake2_128Concat, DepartmentId, DepartmentDetails>; // Peer account id => Peer Profile Hash + + + /// Events that functions in this pallet can emit. + /// + /// Events are a simple means of indicating to the outside world (such as dApps, chain explorers + /// or other users) that some notable update in the runtime has occurred. In a FRAME pallet, the + /// documentation for each event field and its parameters is added to a node's metadata so it + /// can be used by external interfaces or tools. + /// + /// The `generate_deposit` macro generates a function on `Pallet` called `deposit_event` which + /// will convert the event type of your pallet into `RuntimeEvent` (declared in the pallet's + /// [`Config`] trait) and deposit it using [`frame_system::Pallet::deposit_event`]. + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// A user has successfully set a new value. + SomethingStored { + /// The new value set. + something: u32, + /// The account who set the new value. + who: T::AccountId, + }, + } + + /// Errors that can be returned by this pallet. + /// + /// Errors tell users that something went wrong so it's important that their naming is + /// informative. Similar to events, error documentation is added to a node's metadata so it's + /// equally important that they have helpful documentation associated with them. + /// + /// This type of runtime error can be up to 4 bytes in size should you want to return additional + /// information. + #[pallet::error] + pub enum Error { + /// The value retrieved was `None` as no value was previously set. + NoneValue, + /// There was an attempt to increment the value in storage over `u32::MAX`. + StorageOverflow, + } + + /// The pallet's dispatchable functions ([`Call`]s). + /// + /// Dispatchable functions allows users to interact with the pallet and invoke state changes. + /// These functions materialize as "extrinsics", which are often compared to transactions. + /// They must always return a `DispatchResult` and be annotated with a weight and call index. + /// + /// The [`call_index`] macro is used to explicitly + /// define an index for calls in the [`Call`] enum. This is useful for pallets that may + /// introduce new dispatchables over time. If the order of a dispatchable changes, its index + /// will also change which will break backwards compatibility. + /// + /// The [`weight`] macro is used to assign a weight to each call. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a single u32 value as a parameter, writes the value + /// to storage and emits an event. + /// + /// It checks that the _origin_ for this call is _Signed_ and returns a dispatch + /// error if it isn't. Learn more about origins here: + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + let who = ensure_signed(origin)?; + + // Update storage. + Something::::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored { something, who }); + + // Return a successful `DispatchResult` + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + /// + /// It checks that the caller is a signed origin and reads the current value from the + /// `Something` storage item. If a current value exists, it is incremented by 1 and then + /// written back to storage. + /// + /// ## Errors + /// + /// The function will return an error under the following conditions: + /// + /// - If no value has been set ([`Error::NoneValue`]) + /// - If incrementing the value in storage causes an arithmetic overflow + /// ([`Error::StorageOverflow`]) + #[pallet::call_index(1)] + #[pallet::weight(0)] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match Something::::get() { + // Return an error if the value has not been set. + None => Err(Error::::NoneValue.into()), + Some(old) => { + // Increment the value read from storage. This will cause an error in the event + // of overflow. + let new = old.checked_add(1).ok_or(Error::::StorageOverflow)?; + // Update the value in storage with the incremented result. + Something::::put(new); + Ok(()) + }, + } + } + } +} diff --git a/custom-pallets/create-department/src/mock.rs b/custom-pallets/create-department/src/mock.rs new file mode 100644 index 0000000..e0c5fdc --- /dev/null +++ b/custom-pallets/create-department/src/mock.rs @@ -0,0 +1,69 @@ +use crate as pallet_template; +use frame_support::{ + derive_impl,parameter_types, + traits::{ConstU16, ConstU64}, +}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + CreateDepartmentModule: pallet_template, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_template::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::::default().build_storage().unwrap().into() +} diff --git a/custom-pallets/create-department/src/tests.rs b/custom-pallets/create-department/src/tests.rs new file mode 100644 index 0000000..c187c85 --- /dev/null +++ b/custom-pallets/create-department/src/tests.rs @@ -0,0 +1,27 @@ +use crate::{mock::*, Error, Event, Something}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + // Dispatch a signed extrinsic. + assert_ok!(CreateDepartmentModule::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(Something::::get(), Some(42)); + // Assert that the correct event was deposited + System::assert_last_event(Event::SomethingStored { something: 42, who: 1 }.into()); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!( + CreateDepartmentModule::cause_error(RuntimeOrigin::signed(1)), + Error::::NoneValue + ); + }); +} diff --git a/custom-pallets/create-department/src/types.rs b/custom-pallets/create-department/src/types.rs new file mode 100644 index 0000000..db03440 --- /dev/null +++ b/custom-pallets/create-department/src/types.rs @@ -0,0 +1,14 @@ +use super::*; +use frame_support::pallet_prelude::*; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; + +pub const FIRST_DEPARTMENT_ID: DepartmentId = 1; + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct DepartmentDetails { + pub created: WhoAndWhenOf, + pub department_id: DepartmentId, + pub content: Content, +} \ No newline at end of file diff --git a/custom-pallets/create-department/src/weights.rs b/custom-pallets/create-department/src/weights.rs new file mode 100644 index 0000000..7c42936 --- /dev/null +++ b/custom-pallets/create-department/src/weights.rs @@ -0,0 +1,90 @@ + +//! Autogenerated weights for pallet_template +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `Alexs-MacBook-Pro-2.local`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ../../target/release/node-template +// benchmark +// pallet +// --chain +// dev +// --pallet +// pallet_template +// --extrinsic +// * +// --steps=50 +// --repeat=20 +// --wasm-execution=compiled +// --output +// pallets/template/src/weights.rs +// --template +// ../../.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet_template. +pub trait WeightInfo { + fn do_something() -> Weight; + fn cause_error() -> Weight; +} + +/// Weights for pallet_template using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: TemplateModule Something (r:0 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn do_something() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 8_000_000 picoseconds. + Weight::from_parts(9_000_000, 0) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: TemplateModule Something (r:1 w:1) + /// Proof: TemplateModule Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn cause_error() -> Weight { + // Proof Size summary in bytes: + // Measured: `32` + // Estimated: `1489` + // Minimum execution time: 6_000_000 picoseconds. + Weight::from_parts(6_000_000, 1489) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/custom-pallets/shared-storage/src/lib.rs b/custom-pallets/shared-storage/src/lib.rs index 9c4c57a..37669e3 100644 --- a/custom-pallets/shared-storage/src/lib.rs +++ b/custom-pallets/shared-storage/src/lib.rs @@ -69,6 +69,8 @@ pub mod pallet { pub type ReputationScoreOfAccount = StorageMap<_, Blake2_128Concat, T::AccountId, ReputationScore>; + + #[pallet::genesis_config] pub struct GenesisConfig { pub approved_citizen_address: Vec, diff --git a/custom-pallets/shared-storage/src/types.rs b/custom-pallets/shared-storage/src/types.rs index b5d5f1b..f22d577 100644 --- a/custom-pallets/shared-storage/src/types.rs +++ b/custom-pallets/shared-storage/src/types.rs @@ -4,6 +4,8 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + + /// Reputation scores that can be used for schelling game. #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] @@ -74,3 +76,6 @@ impl ReputationScore { self.total_score } } + + +