From f47b8a2952fe8b7c9c532dd9bb0d31779ce96d48 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 20 Jul 2022 18:04:53 -0500 Subject: [PATCH 001/103] Adds rbac pallet --- Cargo.lock | 17 ++ node/src/chain_spec.rs | 7 +- pallets/substrate-rbac/Cargo.toml | 49 ++++++ pallets/substrate-rbac/README.md | 1 + pallets/substrate-rbac/src/lib.rs | 283 ++++++++++++++++++++++++++++++ runtime/Cargo.toml | 3 +- runtime/src/lib.rs | 7 + 7 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 pallets/substrate-rbac/Cargo.toml create mode 100644 pallets/substrate-rbac/README.md create mode 100644 pallets/substrate-rbac/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 67215a19..8f3e3fee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,6 +2168,7 @@ dependencies = [ "sp-std", "sp-transaction-pool", "sp-version", + "substrate-rbac", "substrate-wasm-builder", ] @@ -7376,6 +7377,22 @@ dependencies = [ "tokio", ] +[[package]] +name = "substrate-rbac" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index ab4ac07b..950c7a9d 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,6 +1,6 @@ use hashed_runtime::{ AccountId, AuraConfig, BalancesConfig, CouncilConfig, GenesisConfig, GrandpaConfig, Signature, - SudoConfig, SystemConfig, NodeAuthorizationConfig, NBVStorageConfig ,WASM_BINARY, + SudoConfig, SystemConfig, NodeAuthorizationConfig, NBVStorageConfig, RBACConfig ,WASM_BINARY, }; use sc_chain_spec::Properties; use sc_service::ChainType; @@ -279,6 +279,9 @@ fn testnet_genesis( transaction_payment: Default::default(), nbv_storage : NBVStorageConfig{ bdk_services_url : BDK_SERVICES_MAINNET_URL.as_bytes().to_vec(), - } + }, + rbac: Some(RBACConfig { + super_admins: vec![get_account_id_from_seed::("Alice")] + }) } } diff --git a/pallets/substrate-rbac/Cargo.toml b/pallets/substrate-rbac/Cargo.toml new file mode 100644 index 00000000..088b963f --- /dev/null +++ b/pallets/substrate-rbac/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "substrate-rbac" +version = "4.0.0-dev" +description = "" +authors = ["Hashed > + IsType<::Event>; + + /// Origin for adding or removing a roles and permissions. + type RbacAdminOrigin: EnsureOrigin; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + // The pallet's storage items. + #[pallet::storage] + #[pallet::getter(fn super_admins)] + pub type SuperAdmins = StorageMap<_, Blake2_128Concat, T::AccountId, ()>; + + #[pallet::storage] + #[pallet::getter(fn permissions)] + pub type Permissions = StorageMap<_, Blake2_128Concat, (T::AccountId, Role), ()>; + + #[pallet::storage] + #[pallet::getter(fn roles)] + pub type Roles = StorageMap<_, Blake2_128Concat, Role, ()>; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub super_admins: Vec, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + Self { + super_admins: Vec::new(), + } + } + } + + // The build of genesis for the pallet. + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for admin in &self.super_admins { + >::insert(admin, ()); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + AccessRevoked(T::AccountId, Vec), + AccessGranted(T::AccountId, Vec), + SuperAdminAdded(T::AccountId), + } + + #[pallet::error] + pub enum Error { + AccessDenied, + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + #[pallet::weight(0)] + pub fn create_role( + origin: OriginFor, + pallet_name: Vec, + permission: Permission, + ) -> DispatchResult { + ensure_signed(origin)?; + + let role = Role { + pallet: pallet_name, + permission, + }; + + Roles::::insert(role, ()); + + Ok(()) + } + + #[pallet::weight(0)] + pub fn assign_role( + origin: OriginFor, + account_id: T::AccountId, + role: Role, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + if Self::verify_manage_access(who, role.pallet.clone()) { + Self::deposit_event(Event::AccessGranted( + account_id.clone(), + role.pallet.clone(), + )); + >::insert((account_id, role), ()); + } else { + return Err(Error::::AccessDenied.into()); + } + + Ok(()) + } + + #[pallet::weight(0)] + pub fn revoke_access( + origin: OriginFor, + account_id: T::AccountId, + role: Role, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + if Self::verify_manage_access(who, role.pallet.clone()) { + Self::deposit_event(Event::AccessRevoked( + account_id.clone(), + role.pallet.clone(), + )); + >::remove((account_id, role)); + } else { + return Err(Error::::AccessDenied.into()); + } + + Ok(()) + } + + /// Add a new Super Admin. + /// Super Admins have access to execute and manage all pallets. + /// + /// Only _root_ can add a Super Admin. + #[pallet::weight(0)] + pub fn add_super_admin(origin: OriginFor, account_id: T::AccountId) -> DispatchResult { + T::RbacAdminOrigin::ensure_origin(origin)?; + >::insert(&account_id, ()); + Self::deposit_event(Event::SuperAdminAdded(account_id)); + Ok(()) + } + } +} + +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] +pub enum Permission { + Execute = 1, + Manage = 2, +} + +impl Default for Permission { + fn default() -> Self { + Permission::Execute + } +} + +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] +pub struct Role { + pallet: Vec, + permission: Permission, +} + +impl Pallet { + pub fn verify_execute_access(account_id: T::AccountId, pallet: Vec) -> bool { + let role = Role { + pallet, + permission: Permission::Execute, + }; + + if >::contains_key(&role) && >::contains_key((account_id, role)) { + return true; + } + + false + } + + fn verify_manage_access(account_id: T::AccountId, pallet: Vec) -> bool { + let role = Role { + pallet, + permission: Permission::Manage, + }; + + if >::contains_key(&role) && >::contains_key((account_id, role)) { + return true; + } + + false + } +} + +/// The following section implements the `SignedExtension` trait +/// for the `Authorize` type. +/// `SignedExtension` is being used here to filter out the not authorized accounts +/// when they try to send extrinsics to the runtime. +/// Inside the `validate` function of the `SignedExtension` trait, +/// we check if the sender (origin) of the extrinsic has the execute permission or not. +/// The validation happens at the transaction queue level, +/// and the extrinsics are filtered out before they hit the pallet logic. + +/// The `Authorize` struct. +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct Authorize(sp_std::marker::PhantomData); + +/// Debug impl for the `Authorize` struct. +impl sp_std::fmt::Debug for Authorize { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "Authorize") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +impl Authorize { + pub fn new() -> Self { + Self(sp_std::marker::PhantomData) + } +} + +impl SignedExtension for Authorize +where + T::Call: Dispatchable + GetCallMetadata, +{ + type AccountId = T::AccountId; + type Call = T::Call; + type AdditionalSigned = (); + type Pre = (); + const IDENTIFIER: &'static str = "Authorize"; + + fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + let md = call.get_call_metadata(); + + if >::contains_key(who.clone()) { + print("Access Granted!"); + Ok(Default::default()) + } else if >::verify_execute_access( + who.clone(), + md.pallet_name.as_bytes().to_vec(), + ) { + print("Access Granted!"); + Ok(Default::default()) + } else { + print("Access Denied!"); + Err(InvalidTransaction::Call.into()) + } + } +} \ No newline at end of file diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index dbb7231d..5496f002 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -65,7 +65,7 @@ pallet-template = { version = "4.0.0-dev", default-features = false, path = "../ pallet-fruniques = { version = "0.1.0-dev", default-features = false, path = "../pallets/fruniques" } pallet-nbv-storage = { version = "4.0.0-dev", default-features = false, path = "../pallets/nbv-storage" } pallet-gated-marketplace = { version = "4.0.0-dev", default-features = false, path = "../pallets/gated-marketplace" } - +substrate-rbac = { version = "4.0.0-dev", default-features = false, path = "../pallets/substrate-rbac" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } @@ -101,6 +101,7 @@ std = [ "pallet-node-authorization/std", "pallet-nbv-storage/std", "pallet-gated-marketplace/std", + "substrate-rbac/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 08006860..b17dd422 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -594,6 +594,11 @@ impl pallet_nbv_storage::Config for Runtime { type MaxProposalsPerVault = MaxProposalsPerVault; } +impl substrate_rbac::Config for Runtime { + type Event = Event; + type RbacAdminOrigin = EnsureRoot; +} + parameter_types! { pub const MaxRecursions: u32 = 10; @@ -630,6 +635,7 @@ where frame_system::CheckNonce::::from(index), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), + substrate_rbac::Authorize, ); let raw_payload = SignedPayload::new(call, extra) @@ -686,6 +692,7 @@ construct_runtime!( GatedMarketplace: pallet_gated_marketplace, Assets: pallet_assets, NBVStorage: pallet_nbv_storage, + RBAC: substrate_rbac::{Pallet, Call, Storage, Event, Config}, } ); From 4331b5e24e8c23016ce7b8debb33330550e8c74d Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 22 Jul 2022 11:27:10 -0500 Subject: [PATCH 002/103] Removed rbac in runtime and adds a RBAC custom pallet form the template --- Cargo.lock | 17 ----- node/src/chain_spec.rs | 5 +- pallets/rbac/Cargo.toml | 40 ++++++++++++ pallets/rbac/README.md | 1 + pallets/rbac/src/benchmarking.rs | 20 ++++++ pallets/rbac/src/lib.rs | 102 ++++++++++++++++++++++++++++++ pallets/rbac/src/mock.rs | 64 +++++++++++++++++++ pallets/rbac/src/tests.rs | 20 ++++++ pallets/substrate-rbac/src/lib.rs | 12 ++++ runtime/Cargo.toml | 2 - runtime/src/lib.rs | 7 -- 11 files changed, 260 insertions(+), 30 deletions(-) create mode 100644 pallets/rbac/Cargo.toml create mode 100644 pallets/rbac/README.md create mode 100644 pallets/rbac/src/benchmarking.rs create mode 100644 pallets/rbac/src/lib.rs create mode 100644 pallets/rbac/src/mock.rs create mode 100644 pallets/rbac/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 8f3e3fee..67215a19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,7 +2168,6 @@ dependencies = [ "sp-std", "sp-transaction-pool", "sp-version", - "substrate-rbac", "substrate-wasm-builder", ] @@ -7377,22 +7376,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "substrate-rbac" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "substrate-wasm-builder" version = "5.0.0-dev" diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 950c7a9d..3fdce072 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,6 +1,6 @@ use hashed_runtime::{ AccountId, AuraConfig, BalancesConfig, CouncilConfig, GenesisConfig, GrandpaConfig, Signature, - SudoConfig, SystemConfig, NodeAuthorizationConfig, NBVStorageConfig, RBACConfig ,WASM_BINARY, + SudoConfig, SystemConfig, NodeAuthorizationConfig, NBVStorageConfig, WASM_BINARY, }; use sc_chain_spec::Properties; use sc_service::ChainType; @@ -280,8 +280,5 @@ fn testnet_genesis( nbv_storage : NBVStorageConfig{ bdk_services_url : BDK_SERVICES_MAINNET_URL.as_bytes().to_vec(), }, - rbac: Some(RBACConfig { - super_admins: vec![get_account_id_from_seed::("Alice")] - }) } } diff --git a/pallets/rbac/Cargo.toml b/pallets/rbac/Cargo.toml new file mode 100644 index 00000000..6ed46601 --- /dev/null +++ b/pallets/rbac/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "pallet-rbac" +version = "4.0.0-dev" +description = "FRAME pallet template for defining custom runtime logic." +authors = ["Substrate DevHub "] +homepage = "https://substrate.io/" +edition = "2021" +license = "Unlicense" +publish = false +repository = "https://github.com/substrate-developer-hub/substrate-node-template/" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23"} +frame-system = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23", optional = true } + +[dev-dependencies] +sp-core = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +sp-io = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", +] + +runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/rbac/README.md b/pallets/rbac/README.md new file mode 100644 index 00000000..8d751a42 --- /dev/null +++ b/pallets/rbac/README.md @@ -0,0 +1 @@ +License: Unlicense \ No newline at end of file diff --git a/pallets/rbac/src/benchmarking.rs b/pallets/rbac/src/benchmarking.rs new file mode 100644 index 00000000..d496a9fc --- /dev/null +++ b/pallets/rbac/src/benchmarking.rs @@ -0,0 +1,20 @@ +//! Benchmarking setup for pallet-template + +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_system::RawOrigin; + +benchmarks! { + do_something { + let s in 0 .. 100; + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), s) + verify { + assert_eq!(Something::::get(), Some(s)); + } + + impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs new file mode 100644 index 00000000..58a5a5b8 --- /dev/null +++ b/pallets/rbac/src/lib.rs @@ -0,0 +1,102 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +/// Edit this file to define custom logic or remove it if it is not needed. +/// Learn more about FRAME and the core library of Substrate FRAME pallets: +/// +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type Event: From> + IsType<::Event>; + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + // The pallet's runtime storage items. + // https://docs.substrate.io/v3/runtime/storage + #[pallet::storage] + #[pallet::getter(fn something)] + // Learn more about declaring storage items: + // https://docs.substrate.io/v3/runtime/storage#declaring-storage-items + pub type Something = StorageValue<_, u32>; + + // Pallets use events to inform users when important changes are made. + // https://docs.substrate.io/v3/runtime/events-and-errors + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event documentation should end with an array that provides descriptive names for event + /// parameters. [something, who] + SomethingStored(u32, T::AccountId), + } + + // Errors inform users that something went wrong. + #[pallet::error] + pub enum Error { + /// Error names should be descriptive. + NoneValue, + /// Errors should have helpful documentation associated with them. + StorageOverflow, + } + + // Dispatchable functions allows users to interact with the pallet and invoke state changes. + // These functions materialize as "extrinsics", which are often compared to transactions. + // Dispatchable functions must be annotated with a weight and must return a DispatchResult. + #[pallet::call] + impl Pallet { + /// An example dispatchable that takes a singles value as a parameter, writes the value to + /// storage and emits an event. This function must be dispatched by a signed extrinsic. + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { + // Check that the extrinsic was signed and get the signer. + // This function will return an error if the extrinsic is not signed. + // https://docs.substrate.io/v3/runtime/origins + let who = ensure_signed(origin)?; + + // Update storage. + >::put(something); + + // Emit an event. + Self::deposit_event(Event::SomethingStored(something, who)); + // Return a successful DispatchResultWithPostInfo + Ok(()) + } + + /// An example dispatchable that may throw a custom error. + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))] + pub fn cause_error(origin: OriginFor) -> DispatchResult { + let _who = ensure_signed(origin)?; + + // Read a value from storage. + match >::get() { + // Return an error if the value has not been set. + None => Err(Error::::NoneValue)?, + Some(old) => { + // Increment the value read from storage; will 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. + >::put(new); + Ok(()) + }, + } + } + } +} \ No newline at end of file diff --git a/pallets/rbac/src/mock.rs b/pallets/rbac/src/mock.rs new file mode 100644 index 00000000..733ac79d --- /dev/null +++ b/pallets/rbac/src/mock.rs @@ -0,0 +1,64 @@ +use crate as pallet_template; +use frame_support::parameter_types; +use frame_system as system; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + TemplateModule: pallet_template::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +impl system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_template::Config for Test { + type Event = Event; +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::default().build_storage::().unwrap().into() +} diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs new file mode 100644 index 00000000..22056586 --- /dev/null +++ b/pallets/rbac/src/tests.rs @@ -0,0 +1,20 @@ +use crate::{mock::*, Error}; +use frame_support::{assert_noop, assert_ok}; + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Dispatch a signed extrinsic. + assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(TemplateModule::something(), Some(42)); + }); +} + +#[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!(TemplateModule::cause_error(Origin::signed(1)), Error::::NoneValue); + }); +} diff --git a/pallets/substrate-rbac/src/lib.rs b/pallets/substrate-rbac/src/lib.rs index f1031ed9..4d836090 100644 --- a/pallets/substrate-rbac/src/lib.rs +++ b/pallets/substrate-rbac/src/lib.rs @@ -280,4 +280,16 @@ where Err(InvalidTransaction::Call.into()) } } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?; + Ok((self.0, who.clone(), imbalance)) + } + } \ No newline at end of file diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5496f002..b5b686a3 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -65,7 +65,6 @@ pallet-template = { version = "4.0.0-dev", default-features = false, path = "../ pallet-fruniques = { version = "0.1.0-dev", default-features = false, path = "../pallets/fruniques" } pallet-nbv-storage = { version = "4.0.0-dev", default-features = false, path = "../pallets/nbv-storage" } pallet-gated-marketplace = { version = "4.0.0-dev", default-features = false, path = "../pallets/gated-marketplace" } -substrate-rbac = { version = "4.0.0-dev", default-features = false, path = "../pallets/substrate-rbac" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } @@ -101,7 +100,6 @@ std = [ "pallet-node-authorization/std", "pallet-nbv-storage/std", "pallet-gated-marketplace/std", - "substrate-rbac/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b17dd422..08006860 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -594,11 +594,6 @@ impl pallet_nbv_storage::Config for Runtime { type MaxProposalsPerVault = MaxProposalsPerVault; } -impl substrate_rbac::Config for Runtime { - type Event = Event; - type RbacAdminOrigin = EnsureRoot; -} - parameter_types! { pub const MaxRecursions: u32 = 10; @@ -635,7 +630,6 @@ where frame_system::CheckNonce::::from(index), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - substrate_rbac::Authorize, ); let raw_payload = SignedPayload::new(call, extra) @@ -692,7 +686,6 @@ construct_runtime!( GatedMarketplace: pallet_gated_marketplace, Assets: pallet_assets, NBVStorage: pallet_nbv_storage, - RBAC: substrate_rbac::{Pallet, Call, Storage, Event, Config}, } ); From 737356e517b743ecacf7608e84774ec80bc7be2b Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 25 Jul 2022 18:03:31 -0500 Subject: [PATCH 003/103] Adds some basic hanlder functions --- Cargo.lock | 16 ++++ pallets/rbac/Cargo.toml | 1 + pallets/rbac/src/functions.rs | 45 ++++++++++++ pallets/rbac/src/lib.rs | 134 +++++++++++++++++++++------------- pallets/rbac/src/types.rs | 8 ++ runtime/Cargo.toml | 3 + runtime/src/lib.rs | 15 ++++ 7 files changed, 172 insertions(+), 50 deletions(-) create mode 100644 pallets/rbac/src/functions.rs create mode 100644 pallets/rbac/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 67215a19..fb2f706a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2146,6 +2146,7 @@ dependencies = [ "pallet-nbv-storage", "pallet-node-authorization", "pallet-randomness-collective-flip", + "pallet-rbac", "pallet-recovery", "pallet-society", "pallet-sudo", @@ -4160,6 +4161,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-rbac" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-recovery" version = "4.0.0-dev" diff --git a/pallets/rbac/Cargo.toml b/pallets/rbac/Cargo.toml index 6ed46601..2403a1db 100644 --- a/pallets/rbac/Cargo.toml +++ b/pallets/rbac/Cargo.toml @@ -13,6 +13,7 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] +log = "0.4" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ "derive", ] } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs new file mode 100644 index 00000000..9cc01d8e --- /dev/null +++ b/pallets/rbac/src/functions.rs @@ -0,0 +1,45 @@ +use super::*; +use frame_support::{pallet_prelude::*, BoundedBTreeSet}; +use frame_system::pallet_prelude::*; +use frame_support::sp_io::hashing::blake2_256; +//use sp_runtime::sp_std::vec::Vec; +use crate::types::*; + +impl Pallet { + + /*---- Basic CRUD of individual storage maps ---*/ + pub fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ + >::try_mutate(pallet_id, |scopes_option|{ + let scopes =scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; + scopes.try_push(scope_id).map_err(|_| Error::::ExceedMaxScopesPerPallet)?; + Ok(()) + }) + } + + pub fn create_role(role: BoundedVec>)-> [u8;32]{ + let role_id = role.using_encoded(blake2_256); + // insert is infalible in this case + >::insert(role_id, role); + role_id + } + + pub fn set_pallet_role(pallet_id: u32, role_id: [u8;32])-> DispatchResult{ + ensure!(>::contains_key(role_id), Error::::RoleNotFound); + >::try_mutate(pallet_id, |roles|{ + ensure!(!roles.contains(&role_id), Error::::DuplicateRole ); + roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerPallet) + })?; + Ok(()) + } + + pub fn create_permission(pallet_id: u32, scope_id:[u8;32] ,role_id: [u8;32], permission: BoundedVec) -> DispatchResult{ + let p = >::get(pallet_id).ok_or(Error::::PalletNotFound)?; + ensure!(p.contains(&scope_id), Error::::ScopeNotFound); + ensure!(>::contains_key(role_id), Error::::RoleNotFound); + >::try_mutate((pallet_id, scope_id,role_id), |ps|{ + ensure!(!ps.contains(&permission), Error::::DuplicatePermission); + ps.try_push(permission).map_err(|_| Error::::ExceedMaxPermissionsPerRole) + })?; + Ok(()) + } +} \ No newline at end of file diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 58a5a5b8..98d61e32 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -14,37 +14,85 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; + +mod functions; +mod types; + #[frame_support::pallet] pub mod pallet { - use frame_support::pallet_prelude::*; + use frame_support::pallet_prelude::{*, ValueQuery}; + use frame_support::traits::{PalletInfoAccess}; +use frame_support::{PalletId, transactional}; use frame_system::pallet_prelude::*; + use crate::types::*; - /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config { - /// Because this pallet emits events, it depends on the runtime's definition of an event. type Event: From> + IsType<::Event>; + + type MaxScopesPerPallet: Get; + + type MaxRolesPerPallet: Get; + + type PermissionMaxLen: Get; + + type MaxPermissionsPerRole: Get; } #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); - // The pallet's runtime storage items. - // https://docs.substrate.io/v3/runtime/storage + /*--- Onchain storage section ---*/ + + #[pallet::storage] + #[pallet::getter(fn scopes)] + pub(super) type Scopes = StorageMap< + _, + Blake2_128Concat, + u32, // pallet_id + BoundedVec<[u8;32], T::MaxScopesPerPallet>, // scopes_id + OptionQuery, + >; + #[pallet::storage] - #[pallet::getter(fn something)] - // Learn more about declaring storage items: - // https://docs.substrate.io/v3/runtime/storage#declaring-storage-items - pub type Something = StorageValue<_, u32>; + #[pallet::getter(fn roles)] + pub(super) type Roles = StorageMap< + _, + Identity, + [u8;32], // role_id + BoundedVec >, // role + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn pallet_roles)] + pub(super) type PalletRoles = StorageMap< + _, + Blake2_128Concat, + u32, // pallet_id + BoundedVec<[u8;32], T::MaxRolesPerPallet >, // role_id + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn permissions)] + pub(super) type Permissions = StorageNMap< + _, + ( + NMapKey, // pallet_id + NMapKey, // scope_id + NMapKey, // role_id + ), + BoundedVec, T::MaxPermissionsPerRole >, // permissions + ValueQuery, + >; + - // Pallets use events to inform users when important changes are made. - // https://docs.substrate.io/v3/runtime/events-and-errors #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Event documentation should end with an array that provides descriptive names for event - /// parameters. [something, who] SomethingStored(u32, T::AccountId), } @@ -53,50 +101,36 @@ pub mod pallet { pub enum Error { /// Error names should be descriptive. NoneValue, - /// Errors should have helpful documentation associated with them. - StorageOverflow, + /// The pallet doesnt have scopes associated + PalletNotFound, + /// The specified scope doesnt exists + ScopeNotFound, + /// The specified role doesnt exists + RoleNotFound, + /// The role is already linked in the pallet + DuplicateRole, + DuplicatePermission, + /// The pallet has too many scopes + ExceedMaxScopesPerPallet, + ExceedMaxRolesPerPallet, + ExceedMaxPermissionsPerRole, } - // Dispatchable functions allows users to interact with the pallet and invoke state changes. - // These functions materialize as "extrinsics", which are often compared to transactions. - // Dispatchable functions must be annotated with a weight and must return a DispatchResult. #[pallet::call] impl Pallet { - /// An example dispatchable that takes a singles value as a parameter, writes the value to - /// storage and emits an event. This function must be dispatched by a signed extrinsic. + + #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn do_something(origin: OriginFor, something: u32) -> DispatchResult { - // Check that the extrinsic was signed and get the signer. - // This function will return an error if the extrinsic is not signed. - // https://docs.substrate.io/v3/runtime/origins - let who = ensure_signed(origin)?; - - // Update storage. - >::put(something); - - // Emit an event. - Self::deposit_event(Event::SomethingStored(something, who)); - // Return a successful DispatchResultWithPostInfo - Ok(()) - } + pub fn get_pallet_id( + origin: OriginFor, + ) -> DispatchResult { + let who = ensure_signed(origin.clone())?; + let a = Self::index(); + log::info!("henlo {:?}", a); + log::warn!("Name: {:?} Module Name: {:?}",Self::name(), Self::module_name()); + Self::deposit_event(Event::SomethingStored(a.try_into().unwrap(), who)); - /// An example dispatchable that may throw a custom error. - #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))] - pub fn cause_error(origin: OriginFor) -> DispatchResult { - let _who = ensure_signed(origin)?; - - // Read a value from storage. - match >::get() { - // Return an error if the value has not been set. - None => Err(Error::::NoneValue)?, - Some(old) => { - // Increment the value read from storage; will 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. - >::put(new); - Ok(()) - }, - } + Ok(()) } } } \ No newline at end of file diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs new file mode 100644 index 00000000..8bb61b3a --- /dev/null +++ b/pallets/rbac/src/types.rs @@ -0,0 +1,8 @@ +use super::*; +use frame_support::pallet_prelude::*; + + +pub struct Permission { + name: BoundedVec>, + value: bool, +} \ No newline at end of file diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b5b686a3..658cd293 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -65,6 +65,8 @@ pallet-template = { version = "4.0.0-dev", default-features = false, path = "../ pallet-fruniques = { version = "0.1.0-dev", default-features = false, path = "../pallets/fruniques" } pallet-nbv-storage = { version = "4.0.0-dev", default-features = false, path = "../pallets/nbv-storage" } pallet-gated-marketplace = { version = "4.0.0-dev", default-features = false, path = "../pallets/gated-marketplace" } +pallet-rbac = { version = "4.0.0-dev", default-features = false, path = "../pallets/rbac" } + [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } @@ -100,6 +102,7 @@ std = [ "pallet-node-authorization/std", "pallet-nbv-storage/std", "pallet-gated-marketplace/std", + "pallet-rbac/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 08006860..ba42e58f 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -595,6 +595,20 @@ impl pallet_nbv_storage::Config for Runtime { } +parameter_types! { + pub const MaxScopesPerPallet: u32 = 1000; + pub const MaxRolesPerPallet: u32 = 20; + pub const PermissionMaxLen: u32 = 35; + pub const MaxPermissionsPerRole: u32 = 10; +} +impl pallet_rbac::Config for Runtime { + type Event = Event; + type MaxScopesPerPallet = MaxScopesPerPallet; + type MaxRolesPerPallet = MaxRolesPerPallet; + type PermissionMaxLen = PermissionMaxLen; + type MaxPermissionsPerRole = MaxPermissionsPerRole; +} + parameter_types! { pub const MaxRecursions: u32 = 10; pub const ResourceSymbolLimit: u32 = 10; @@ -686,6 +700,7 @@ construct_runtime!( GatedMarketplace: pallet_gated_marketplace, Assets: pallet_assets, NBVStorage: pallet_nbv_storage, + RBAC: pallet_rbac, } ); From 2faec3a1fd5deec4f72acf3773773719de53a99e Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 26 Jul 2022 17:34:13 -0500 Subject: [PATCH 004/103] Adds roles, users and handler functions Permissions map needs to be changed, as they dont vary through scopes, but for roles --- pallets/rbac/Cargo.toml | 2 +- pallets/rbac/src/functions.rs | 91 ++++++++++++++++++++++++++++++++--- pallets/rbac/src/lib.rs | 53 ++++++++++++++++++-- pallets/rbac/src/types.rs | 10 ++-- runtime/src/lib.rs | 4 ++ 5 files changed, 142 insertions(+), 18 deletions(-) diff --git a/pallets/rbac/Cargo.toml b/pallets/rbac/Cargo.toml index 2403a1db..bfa1b790 100644 --- a/pallets/rbac/Cargo.toml +++ b/pallets/rbac/Cargo.toml @@ -21,11 +21,11 @@ scale-info = { version = "2.0.1", default-features = false, features = ["derive" frame-support = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23"} frame-system = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23", optional = true } +sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } [dev-dependencies] sp-core = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } sp-io = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } -sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } [features] default = ["std"] diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 9cc01d8e..f95a445d 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -2,12 +2,16 @@ use super::*; use frame_support::{pallet_prelude::*, BoundedBTreeSet}; use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; -//use sp_runtime::sp_std::vec::Vec; +use sp_runtime::sp_std::vec::Vec; + use crate::types::*; impl Pallet { - /*---- Basic CRUD of individual storage maps ---*/ + pub fn insert_initial_config(pallet_id: u32, roles: BoundedVec){ + todo!() + } + /*---- Basic Insertion of individual storage maps ---*/ pub fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ >::try_mutate(pallet_id, |scopes_option|{ let scopes =scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; @@ -23,8 +27,10 @@ impl Pallet { role_id } - pub fn set_pallet_role(pallet_id: u32, role_id: [u8;32])-> DispatchResult{ - ensure!(>::contains_key(role_id), Error::::RoleNotFound); + pub fn set_pallet_role(pallet_id: u32, id_or_role: IdOrRole)-> DispatchResult{ + // get_role checks if that role exists + let role_id = Self::get_role(id_or_role)?; + >::try_mutate(pallet_id, |roles|{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole ); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerPallet) @@ -32,14 +38,83 @@ impl Pallet { Ok(()) } - pub fn create_permission(pallet_id: u32, scope_id:[u8;32] ,role_id: [u8;32], permission: BoundedVec) -> DispatchResult{ - let p = >::get(pallet_id).ok_or(Error::::PalletNotFound)?; - ensure!(p.contains(&scope_id), Error::::ScopeNotFound); - ensure!(>::contains_key(role_id), Error::::RoleNotFound); + pub fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec)->DispatchResult{ + let mut role_ids = Vec::<[u8;32]>::new(); + for id_or_role in roles{ + let id = Self::get_role(id_or_role)?; + ensure!(!>::get(&pallet_id).contains(&id), Error::::DuplicateRole ); + role_ids.push(id) + } + >::try_mutate(pallet_id, |roles|{ + roles.try_append(&mut role_ids) + .map_err(|_| Error::::ExceedMaxRolesPerPallet) + })?; + + Ok(()) + } + + pub fn create_permission(pallet_id: u32, scope_id:[u8;32] ,id_or_role: IdOrRole, permission: BoundedVec) -> DispatchResult{ + let role_id = Self::get_role(id_or_role)?; + Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; >::try_mutate((pallet_id, scope_id,role_id), |ps|{ ensure!(!ps.contains(&permission), Error::::DuplicatePermission); ps.try_push(permission).map_err(|_| Error::::ExceedMaxPermissionsPerRole) })?; Ok(()) } + + pub fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ + Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; + >::try_mutate((&user, pallet_id, scope_id), | roles |{ + ensure!(!roles.contains(&role_id), Error::::DuplicateRole); + roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) + })?; + + >::try_mutate((pallet_id, scope_id, role_id), | users|{ + ensure!(!users.contains(&user), Error::::UserAlreadyHasRole); + users.try_push(user).map_err(|_| Error::::ExceedMaxUsersPerRole) + })?; + Ok(()) + } + + /*---- Helper functions ----*/ + + /// Authorize by rolE, not permissions + pub fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrRole ) -> DispatchResult{ + // get id, whether is given directly or by its string in boundedvec format + let role_id = Self::get_role(role)?; + Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; + // Perform confirmation on both maps + // TODO: test if a role that doesnt exists cause any errors + let users = >::get( (pallet_id, scope_id, role_id) ); + ensure!(users.contains(&user), Error::::NotAuthorized); + let roles = >::get((user, pallet_id, scope_id)); + // Not likelly to happen but just in case: + ensure!(roles.contains(&role_id), Error::::NotAuthorized ); + Ok(()) + } + /// Also checks if pallet is stored + fn scope_exists(pallet_id: &u32, scope_id:&[u8;32]) -> DispatchResult{ + let p = >::get(pallet_id).ok_or(Error::::PalletNotFound)?; + ensure!(p.contains(&scope_id), Error::::ScopeNotFound); + Ok(()) + } + + fn is_role_in_scope(pallet_id: &u32, scope_id:&[u8;32], role_id: &[u8;32])-> DispatchResult{ + Self::scope_exists(pallet_id, scope_id)?; + //ensure!(>::contains_key(role_id), Error::::RoleNotFound); + // The role exists, now check if the role is assigned to that pallet + >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) + .ok_or(Error::::RoleNotLinkedToPallet)?; + Ok(()) + } + + fn get_role(id_or_role: IdOrRole)->Result<[u8;32], DispatchError>{ + let role_id = match id_or_role{ + IdOrRole::Id(id)=>id, + IdOrRole::Role(role_str)=> role_str.using_encoded(blake2_256), + }; + ensure!(>::contains_key(role_id), Error::::RoleNotFound); + Ok(role_id) + } } \ No newline at end of file diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 98d61e32..2d409fa6 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -22,7 +22,7 @@ mod types; pub mod pallet { use frame_support::pallet_prelude::{*, ValueQuery}; use frame_support::traits::{PalletInfoAccess}; -use frame_support::{PalletId, transactional}; + use frame_support::{transactional}; use frame_system::pallet_prelude::*; use crate::types::*; @@ -37,6 +37,10 @@ use frame_support::{PalletId, transactional}; type PermissionMaxLen: Get; type MaxPermissionsPerRole: Get; + + type MaxRolesPerUser: Get; + + type MaxUsersPerRole: Get; } #[pallet::pallet] @@ -88,6 +92,34 @@ use frame_support::{PalletId, transactional}; ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn users)] + pub(super) type Users = StorageNMap< + _, + ( + NMapKey,// user + NMapKey, // pallet_id + NMapKey, // scope_id + ), + BoundedVec<[u8;32], T::MaxRolesPerUser>, // roles (ids) + ValueQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn users_by_scope)] + pub(super) type UsersByScope = StorageNMap< + _, + ( + NMapKey, // pallet_id + NMapKey, // scope_id + NMapKey, // role_id + ), + BoundedVec, // users + ValueQuery, + >; + + + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -101,19 +133,32 @@ use frame_support::{PalletId, transactional}; pub enum Error { /// Error names should be descriptive. NoneValue, - /// The pallet doesnt have scopes associated + /// The pallet doesn't have scopes associated PalletNotFound, - /// The specified scope doesnt exists + /// The specified scope doesn't exists ScopeNotFound, - /// The specified role doesnt exists + /// The specified role doesn't exists RoleNotFound, /// The role is already linked in the pallet DuplicateRole, + /// The permission is already linked to that role in that scope DuplicatePermission, + /// The user has that role asigned in that scope + UserAlreadyHasRole, + /// The role exists but it hasn't been linked to the pallet + RoleNotLinkedToPallet, /// The pallet has too many scopes ExceedMaxScopesPerPallet, + /// The pallet cannot have more roles ExceedMaxRolesPerPallet, + /// The specified role cannot have more permission in this scope ExceedMaxPermissionsPerRole, + /// The user cannot have more roles in this scope + ExceedMaxRolesPerUser, + /// This role cannot have assigned to more users in this scope + ExceedMaxUsersPerRole, + /// The user does not have the specified role + NotAuthorized, } #[pallet::call] diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 8bb61b3a..1efd8c30 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -1,8 +1,8 @@ use super::*; use frame_support::pallet_prelude::*; - -pub struct Permission { - name: BoundedVec>, - value: bool, -} \ No newline at end of file +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo,)] +pub enum IdOrRole{ + Id([u8;32]), + Role(BoundedVec >) +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ba42e58f..6ca56f2e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -600,6 +600,8 @@ parameter_types! { pub const MaxRolesPerPallet: u32 = 20; pub const PermissionMaxLen: u32 = 35; pub const MaxPermissionsPerRole: u32 = 10; + pub const MaxRolesPerUser: u32 = 10; + pub const MaxUsersPerRole: u32 = 10; } impl pallet_rbac::Config for Runtime { type Event = Event; @@ -607,6 +609,8 @@ impl pallet_rbac::Config for Runtime { type MaxRolesPerPallet = MaxRolesPerPallet; type PermissionMaxLen = PermissionMaxLen; type MaxPermissionsPerRole = MaxPermissionsPerRole; + type MaxRolesPerUser = MaxRolesPerUser; + type MaxUsersPerRole = MaxUsersPerRole; } parameter_types! { From 164ce677d508b241dfed8402b7fc94254d276fda Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 27 Jul 2022 13:09:20 -0500 Subject: [PATCH 005/103] Changes permissions storage maps --- pallets/rbac/src/functions.rs | 16 ++++++++-------- pallets/rbac/src/lib.rs | 23 +++++++++++++++++------ pallets/rbac/src/types.rs | 9 +++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index f95a445d..4fd3ad92 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -53,10 +53,10 @@ impl Pallet { Ok(()) } - pub fn create_permission(pallet_id: u32, scope_id:[u8;32] ,id_or_role: IdOrRole, permission: BoundedVec) -> DispatchResult{ + pub fn create_permission(pallet_id: u32 ,id_or_role: IdOrRole, permission: BoundedVec) -> DispatchResult{ let role_id = Self::get_role(id_or_role)?; - Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; - >::try_mutate((pallet_id, scope_id,role_id), |ps|{ + Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; + >::try_mutate(pallet_id,role_id, |ps|{ ensure!(!ps.contains(&permission), Error::::DuplicatePermission); ps.try_push(permission).map_err(|_| Error::::ExceedMaxPermissionsPerRole) })?; @@ -64,7 +64,8 @@ impl Pallet { } pub fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ - Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; + Self::scope_exists(&pallet_id, &scope_id)?; + Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) @@ -83,7 +84,8 @@ impl Pallet { pub fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrRole ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format let role_id = Self::get_role(role)?; - Self::is_role_in_scope(&pallet_id, &scope_id, &role_id)?; + Self::scope_exists(&pallet_id, &scope_id)?; + Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; // Perform confirmation on both maps // TODO: test if a role that doesnt exists cause any errors let users = >::get( (pallet_id, scope_id, role_id) ); @@ -100,9 +102,7 @@ impl Pallet { Ok(()) } - fn is_role_in_scope(pallet_id: &u32, scope_id:&[u8;32], role_id: &[u8;32])-> DispatchResult{ - Self::scope_exists(pallet_id, scope_id)?; - //ensure!(>::contains_key(role_id), Error::::RoleNotFound); + fn is_role_linked_to_pallet(pallet_id: &u32, role_id: &[u8;32])-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) .ok_or(Error::::RoleNotLinkedToPallet)?; diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 2d409fa6..723438be 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -81,17 +81,28 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn permissions)] - pub(super) type Permissions = StorageNMap< + pub(super) type Permissions = StorageDoubleMap< _, - ( - NMapKey, // pallet_id - NMapKey, // scope_id - NMapKey, // role_id - ), + Blake2_128Concat, + u32, // pallet_id + Blake2_128Concat, + [u8;32], // permission_id BoundedVec, T::MaxPermissionsPerRole >, // permissions ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn permissions_by_role)] + pub(super) type PermissionsByRole = StorageDoubleMap< + _, + Blake2_128Concat, + u32, // pallet_id + Blake2_128Concat, + [u8;32], // role_id + BoundedVec<[u8;32], T::MaxPermissionsPerRole >, // permission_ids + ValueQuery, + >; + #[pallet::storage] #[pallet::getter(fn users)] pub(super) type Users = StorageNMap< diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 1efd8c30..bd8d0729 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -6,3 +6,12 @@ pub enum IdOrRole{ Id([u8;32]), Role(BoundedVec >) } + + +pub struct RoleConfiguration{ + pub role_name: BoundedVec>, + pub permissions: BoundedVec< + BoundedVec, + T::MaxPermissionsPerRole + > +} \ No newline at end of file From 44c57b2da9d9bacad758615fc1c13119119e0701 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 27 Jul 2022 18:05:44 -0500 Subject: [PATCH 006/103] trying to import rbac pallet --- Cargo.lock | 1 + pallets/gated-marketplace/Cargo.toml | 3 +- pallets/gated-marketplace/src/functions.rs | 3 + pallets/gated-marketplace/src/lib.rs | 4 +- pallets/rbac/src/functions.rs | 101 +++++++++++++-------- pallets/rbac/src/lib.rs | 12 ++- pallets/rbac/src/types.rs | 17 +++- runtime/src/lib.rs | 1 + 8 files changed, 97 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb2f706a..d2cd51dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4031,6 +4031,7 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-rbac", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index d4e34fcb..4fe4955d 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -23,7 +23,7 @@ frame-support = { default-features = false, version = "4.0.0-dev", git = "https: frame-system = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23", optional = true } sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } - +pallet-rbac = { default-features = false, version = "4.0.0-dev", path="../rbac/"} [dev-dependencies] sp-core = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } @@ -39,6 +39,7 @@ std = [ "frame-support/std", "frame-system/std", "frame-benchmarking/std", + "pallet-rbac/std" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 4afa6ffa..3fa4ff69 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,12 +4,15 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; +use pallet_rbac::types::RoleBasedAccessControl; + impl Pallet { pub fn do_create_marketplace(owner: T::AccountId, admin: T::AccountId ,marketplace: Marketplace)->DispatchResult{ // Gen market id let marketplace_id = marketplace.using_encoded(blake2_256); + T::Rbac::create_scope(Self::index().try_into().unwrap(),marketplace_id.clone())?; // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 232fdc83..a08f24a8 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -20,7 +20,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; - + use pallet_rbac::types::RoleBasedAccessControl; #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; @@ -45,6 +45,8 @@ pub mod pallet { type MaxFiles: Get; #[pallet::constant] type MaxApplicationsPerCustodian: Get; + + type Rbac : RoleBasedAccessControl; } #[pallet::pallet] diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 4fd3ad92..9214d1a9 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -1,35 +1,45 @@ use super::*; -use frame_support::{pallet_prelude::*, BoundedBTreeSet}; +use frame_support::{pallet_prelude::*}; use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; +use frame_support::sp_std::borrow::ToOwned; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -impl Pallet { - - pub fn insert_initial_config(pallet_id: u32, roles: BoundedVec){ - todo!() - } +impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ - pub fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ - >::try_mutate(pallet_id, |scopes_option|{ - let scopes =scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; + fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ + >::try_mutate(pallet_id, |scopes|{ + ensure!(!scopes.contains(&scope_id), Error::::ScopeAlreadyExists); scopes.try_push(scope_id).map_err(|_| Error::::ExceedMaxScopesPerPallet)?; Ok(()) }) } - pub fn create_role(role: BoundedVec>)-> [u8;32]{ + /// Inserts roles and links them to the pallet + fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, T::MaxRolesPerPallet>) -> + Result, DispatchError>{ + // TODO: check for duplicates + //ensure!(Self::has_unique_elements(roles.to_vec()), Error::::DuplicateRole); + + let role_ids: Vec<[u8;32]> = roles.iter().map(|r|{ + Self::create_role(r.to_owned()) + }).collect(); + let bounded_ids = BoundedVec::try_from(role_ids).map_err(|_| Error::::ExceedMaxRolesPerPallet)?; + Self::set_multiple_pallet_roles(pallet_id, bounded_ids.clone())?; + Ok(bounded_ids) + } + + fn create_role(role: BoundedVec>)-> [u8;32]{ let role_id = role.using_encoded(blake2_256); // insert is infalible in this case >::insert(role_id, role); role_id } - pub fn set_pallet_role(pallet_id: u32, id_or_role: IdOrRole)-> DispatchResult{ - // get_role checks if that role exists - let role_id = Self::get_role(id_or_role)?; + fn set_role_to_pallet(pallet_id: u32, role_id: [u8;32] )-> DispatchResult{ + ensure!(>::contains_key(role_id), Error::::RoleNotFound); >::try_mutate(pallet_id, |roles|{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole ); @@ -38,32 +48,32 @@ impl Pallet { Ok(()) } - pub fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec)->DispatchResult{ - let mut role_ids = Vec::<[u8;32]>::new(); - for id_or_role in roles{ - let id = Self::get_role(id_or_role)?; + fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult{ + for id in roles.clone(){ ensure!(!>::get(&pallet_id).contains(&id), Error::::DuplicateRole ); - role_ids.push(id) } - >::try_mutate(pallet_id, |roles|{ - roles.try_append(&mut role_ids) - .map_err(|_| Error::::ExceedMaxRolesPerPallet) - })?; + >::try_mutate(pallet_id, |pallet_roles|{ + pallet_roles.try_extend(roles.into_iter()) + }).map_err(|_| Error::::ExceedMaxRolesPerPallet)?; Ok(()) } - pub fn create_permission(pallet_id: u32 ,id_or_role: IdOrRole, permission: BoundedVec) -> DispatchResult{ - let role_id = Self::get_role(id_or_role)?; - Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; - >::try_mutate(pallet_id,role_id, |ps|{ - ensure!(!ps.contains(&permission), Error::::DuplicatePermission); - ps.try_push(permission).map_err(|_| Error::::ExceedMaxPermissionsPerRole) - })?; + fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]{ + let permission_id = permission.using_encoded(blake2_256); + >::insert(pallet_id, permission_id, permission); + permission_id + } + + fn set_permission_to_role( pallet_id: u32, role: [u8;32], permission: [u8;32] ) -> DispatchResult{ + //check for duplicates + + // try pushing + Ok(()) } - pub fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ + fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ Self::scope_exists(&pallet_id, &scope_id)?; Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; >::try_mutate((&user, pallet_id, scope_id), | roles |{ @@ -80,10 +90,10 @@ impl Pallet { /*---- Helper functions ----*/ - /// Authorize by rolE, not permissions - pub fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrRole ) -> DispatchResult{ + /// Authorize by role, not permissions + pub fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format - let role_id = Self::get_role(role)?; + let role_id = Self::get_role_id(role)?; Self::scope_exists(&pallet_id, &scope_id)?; Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; // Perform confirmation on both maps @@ -91,7 +101,7 @@ impl Pallet { let users = >::get( (pallet_id, scope_id, role_id) ); ensure!(users.contains(&user), Error::::NotAuthorized); let roles = >::get((user, pallet_id, scope_id)); - // Not likelly to happen but just in case: + // Not likely to happen but just in case: ensure!(roles.contains(&role_id), Error::::NotAuthorized ); Ok(()) } @@ -109,12 +119,29 @@ impl Pallet { Ok(()) } - fn get_role(id_or_role: IdOrRole)->Result<[u8;32], DispatchError>{ + fn get_role_id(id_or_role: IdOrString>)->Result<[u8;32], DispatchError>{ let role_id = match id_or_role{ - IdOrRole::Id(id)=>id, - IdOrRole::Role(role_str)=> role_str.using_encoded(blake2_256), + IdOrString::Id(id)=>id, + IdOrString::String(role_str)=> role_str.using_encoded(blake2_256), }; ensure!(>::contains_key(role_id), Error::::RoleNotFound); Ok(role_id) } + + fn get_permission(pallet_id: u32 ,id_or_permission: IdOrString)->Result<[u8;32], DispatchError>{ + let permission_id = match id_or_permission{ + IdOrString::Id(id)=>id, + IdOrString::String(permission_str)=> permission_str.using_encoded(blake2_256), + }; + ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); + Ok(permission_id) + } + + fn has_unique_elements(vec: Vec) -> bool{ + let mut filtered_vec = vec.clone(); + filtered_vec.sort(); + filtered_vec.dedup(); + vec.len() == filtered_vec.len() + } + } \ No newline at end of file diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 723438be..e7b5cc83 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -16,7 +16,7 @@ mod benchmarking; mod functions; -mod types; +pub mod types; #[frame_support::pallet] pub mod pallet { @@ -56,7 +56,7 @@ pub mod pallet { Blake2_128Concat, u32, // pallet_id BoundedVec<[u8;32], T::MaxScopesPerPallet>, // scopes_id - OptionQuery, + ValueQuery, >; #[pallet::storage] @@ -84,10 +84,10 @@ pub mod pallet { pub(super) type Permissions = StorageDoubleMap< _, Blake2_128Concat, - u32, // pallet_id + u32, // pallet_id Blake2_128Concat, [u8;32], // permission_id - BoundedVec, T::MaxPermissionsPerRole >, // permissions + BoundedVec, // permission str ValueQuery, >; @@ -148,8 +148,12 @@ pub mod pallet { PalletNotFound, /// The specified scope doesn't exists ScopeNotFound, + /// The scope is already linked with the pallet + ScopeAlreadyExists, /// The specified role doesn't exists RoleNotFound, + /// The permission doesn't exists in the pallet + PermissionNotFound, /// The role is already linked in the pallet DuplicateRole, /// The permission is already linked to that role in that scope diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index bd8d0729..0d839d13 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -2,9 +2,9 @@ use super::*; use frame_support::pallet_prelude::*; #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo,)] -pub enum IdOrRole{ +pub enum IdOrString >{ Id([u8;32]), - Role(BoundedVec >) + String(BoundedVec) } @@ -14,4 +14,17 @@ pub struct RoleConfiguration{ BoundedVec, T::MaxPermissionsPerRole > +} + +pub trait RoleBasedAccessControl{ + fn create_scope(pallet_id: u32, scope_id: [u8;32]) -> DispatchResult; + fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, T::MaxRolesPerPallet>) -> + Result, DispatchError>; + fn create_role(role: BoundedVec>)-> [u8;32]; + fn set_role_to_pallet(pallet_id: u32, role_id: [u8;32] )-> DispatchResult; + fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult; + fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]; + fn set_permission_to_role( pallet_id: u32, role: [u8;32], permission: [u8;32] ) -> DispatchResult; + fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; + } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 540fa23c..1d808fad 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -568,6 +568,7 @@ impl pallet_gated_marketplace::Config for Runtime { type NameMaxLen= NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; + type Rbac = RBAC; } parameter_types! { From c2e217f19d97628207fa479fdac6f933285491aa Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 28 Jul 2022 11:57:05 -0500 Subject: [PATCH 007/103] Imports rbac to gated marketplaces --- pallets/gated-marketplace/src/functions.rs | 2 +- pallets/gated-marketplace/src/lib.rs | 17 +++++++++++++- pallets/rbac/src/functions.rs | 11 +++++---- pallets/rbac/src/types.rs | 26 +++++++++++++++++----- 4 files changed, 44 insertions(+), 12 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 3fa4ff69..cc5d0f49 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -12,9 +12,9 @@ impl Pallet { pub fn do_create_marketplace(owner: T::AccountId, admin: T::AccountId ,marketplace: Marketplace)->DispatchResult{ // Gen market id let marketplace_id = marketplace.using_encoded(blake2_256); - T::Rbac::create_scope(Self::index().try_into().unwrap(),marketplace_id.clone())?; // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); + T::Rbac::create_scope(Self::index().try_into().unwrap(),marketplace_id.clone())?; //Insert on marketplaces and marketplaces by auth Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index a08f24a8..5e1d82e3 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -21,6 +21,13 @@ pub mod pallet { //use sp_runtime::sp_std::vec::Vec; use crate::types::*; use pallet_rbac::types::RoleBasedAccessControl; + // RBAC pallet aliases + type MaxRolesPerPallet = <::Rbac as RoleBasedAccessControl<::AccountId,>>::MaxRolesPerPallet; + type PermissionMaxLen = <::Rbac as RoleBasedAccessControl<::AccountId,>>::PermissionMaxLen; + // <::Currency as Currency< + //::AccountId, + //>>::NegativeImbalance; + #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; @@ -46,7 +53,7 @@ pub mod pallet { #[pallet::constant] type MaxApplicationsPerCustodian: Get; - type Rbac : RoleBasedAccessControl; + type Rbac : RoleBasedAccessControl; } #[pallet::pallet] @@ -212,6 +219,14 @@ pub mod pallet { #[pallet::call] impl Pallet { + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] + pub fn set_up_permissions(origin: OriginFor) -> DispatchResult { + T::RemoveOrigin::ensure_origin(origin.clone())?; + + Ok(()) + } + /// Create a new marketplace. /// /// Creates a new marketplace with the given label diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 9214d1a9..af386bd0 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -7,7 +7,7 @@ use sp_runtime::sp_std::vec::Vec; use crate::types::*; -impl RoleBasedAccessControl for Pallet{ +impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ >::try_mutate(pallet_id, |scopes|{ @@ -91,7 +91,7 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ /// Authorize by role, not permissions - pub fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ + fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format let role_id = Self::get_role_id(role)?; Self::scope_exists(&pallet_id, &scope_id)?; @@ -107,8 +107,7 @@ impl RoleBasedAccessControl for Pallet{ } /// Also checks if pallet is stored fn scope_exists(pallet_id: &u32, scope_id:&[u8;32]) -> DispatchResult{ - let p = >::get(pallet_id).ok_or(Error::::PalletNotFound)?; - ensure!(p.contains(&scope_id), Error::::ScopeNotFound); + ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); Ok(()) } @@ -144,4 +143,8 @@ impl RoleBasedAccessControl for Pallet{ vec.len() == filtered_vec.len() } + type MaxRolesPerPallet = T::MaxRolesPerPallet; + + type PermissionMaxLen = T::PermissionMaxLen; + } \ No newline at end of file diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 0d839d13..34aac3fc 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::pallet_prelude::*; +use sp_runtime::sp_std::vec::Vec; #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo,)] pub enum IdOrString >{ @@ -16,15 +17,28 @@ pub struct RoleConfiguration{ > } -pub trait RoleBasedAccessControl{ +pub trait RoleBasedAccessControl{ + type MaxRolesPerPallet: Get; + type PermissionMaxLen: Get; + // scopes fn create_scope(pallet_id: u32, scope_id: [u8;32]) -> DispatchResult; - fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, T::MaxRolesPerPallet>) -> - Result, DispatchError>; + // roles + fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, Self::MaxRolesPerPallet>) -> + Result, DispatchError>; fn create_role(role: BoundedVec>)-> [u8;32]; fn set_role_to_pallet(pallet_id: u32, role_id: [u8;32] )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult; - fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]; + fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], Self::MaxRolesPerPallet>)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; + // permissions + fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]; fn set_permission_to_role( pallet_id: u32, role: [u8;32], permission: [u8;32] ) -> DispatchResult; - fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; + // helpers + fn is_user_authorized(user: AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult; + fn scope_exists(pallet_id: &u32, scope_id:&[u8;32]) -> DispatchResult; + fn is_role_linked_to_pallet(pallet_id: &u32, role_id: &[u8;32])-> DispatchResult; + fn get_role_id(id_or_role: IdOrString>)->Result<[u8;32], DispatchError>; + fn get_permission(pallet_id: u32 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; + fn has_unique_elements(vec: Vec) -> bool; + } \ No newline at end of file From 8f2701bd3fc82f2e53e444db573e760589892887 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 28 Jul 2022 14:25:54 -0500 Subject: [PATCH 008/103] import necessary pallets to tightly coupling pallets --- Cargo.lock | 3 ++ pallets/gated-marketplace/Cargo.toml | 3 ++ pallets/gated-marketplace/src/lib.rs | 2 +- pallets/gated-marketplace/src/mock.rs | 67 +++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67215a19..9655147f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4030,6 +4030,9 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-balances", + "pallet-fruniques", + "pallet-uniques", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index d4e34fcb..35f70ef3 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -24,6 +24,9 @@ frame-system = { default-features = false, version = "4.0.0-dev", git = "https:/ frame-benchmarking = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23", optional = true } sp-runtime = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +pallet-uniques = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +pallet-fruniques = {path = "../fruniques", default-features = false, version = "0.1.0-dev"} [dev-dependencies] sp-core = { default-features = false, version = "6.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 232fdc83..8240c53e 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -22,7 +22,7 @@ pub mod pallet { use crate::types::*; #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_fruniques::Config { type Event: From> + IsType<::Event>; type RemoveOrigin: EnsureOrigin; diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 03b022fb..62cb2a7b 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -1,5 +1,5 @@ use crate as pallet_gated_marketplace; -use frame_support::parameter_types; +use frame_support::{parameter_types, traits::AsEnsureOriginWithArg}; use frame_system as system; use sp_core::H256; use sp_runtime::{ @@ -10,6 +10,7 @@ use sp_runtime::{ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; use frame_system::EnsureRoot; +use system::EnsureSigned; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test where @@ -19,6 +20,10 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, GatedMarketplace: pallet_gated_marketplace::{Pallet, Call, Storage, Event}, + Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, + Fruniques: pallet_fruniques::{Pallet, Call, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + } ); @@ -45,7 +50,7 @@ impl system::Config for Test { type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -80,7 +85,63 @@ impl pallet_gated_marketplace::Config for Test { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; } +impl pallet_fruniques::Config for Test { + type Event = Event; +} + + + +parameter_types! { + pub const ClassDeposit: u64 = 2; + pub const InstanceDeposit: u64 = 1; + pub const KeyLimit: u32 = 50; + pub const ValueLimit: u32 = 50; + pub const StringLimit: u32 = 50; + pub const MetadataDepositBase: u64 = 1; + pub const AttributeDepositBase: u64 = 1; + pub const MetadataDepositPerByte: u64 = 1; +} + +impl pallet_uniques::Config for Test { + type Event = Event; + type CollectionId = u32; + type ItemId = u32; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type CollectionDeposit = ClassDeposit; + type ItemDeposit = InstanceDeposit; + type MetadataDepositBase = MetadataDepositBase; + type AttributeDepositBase = MetadataDepositBase; + type DepositPerByte = MetadataDepositPerByte; + type StringLimit = StringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type CreateOrigin = AsEnsureOriginWithArg>; + type Locker = (); + +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() -} +} \ No newline at end of file From 531fab4e943dda0ee6d54f1ee5b8948312f9781a Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 28 Jul 2022 16:48:44 -0500 Subject: [PATCH 009/103] changes pallet id and polishes existing functions --- pallets/gated-marketplace/src/functions.rs | 12 ++++- pallets/gated-marketplace/src/lib.rs | 8 ++- pallets/rbac/src/functions.rs | 60 ++++++++++++---------- pallets/rbac/src/lib.rs | 15 +++--- pallets/rbac/src/types.rs | 22 ++++---- 5 files changed, 71 insertions(+), 46 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index cc5d0f49..34c479eb 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,3 +1,5 @@ +use core::default; + use super::*; use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; @@ -14,7 +16,7 @@ impl Pallet { let marketplace_id = marketplace.using_encoded(blake2_256); // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); - T::Rbac::create_scope(Self::index().try_into().unwrap(),marketplace_id.clone())?; + T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; //Insert on marketplaces and marketplaces by auth Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; @@ -390,4 +392,12 @@ impl Pallet { Ok(()) } + fn get_pallet_id()->u64{ + Self::index().try_into().unwrap() + } + + pub fn str_to_bvec_uncheked>(str: &str)->BoundedVec{ + BoundedVec::::try_from(str.as_bytes().to_vec()).expect("Conversion error") + } + } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 5e1d82e3..3bdee82d 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -16,7 +16,9 @@ mod types; #[frame_support::pallet] pub mod pallet { - use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; + use core::default; + +use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; use frame_system::pallet_prelude::*; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; @@ -223,7 +225,9 @@ pub mod pallet { #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] pub fn set_up_permissions(origin: OriginFor) -> DispatchResult { T::RemoveOrigin::ensure_origin(origin.clone())?; - + //T::Rbac::create_and_set_roles() + //let mut roles = BoundedVec:: >, MaxRolesPerPallet >::default(); + //roles.try_push(Self::str_to_bvec_uncheked("Owner"))?; Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index af386bd0..09b2f8b3 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -6,10 +6,11 @@ use frame_support::sp_std::borrow::ToOwned; use sp_runtime::sp_std::vec::Vec; use crate::types::*; - +// TODO: make vec to manage pallet errors here impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ - fn create_scope(pallet_id: u32, scope_id: [u8;32])-> DispatchResult{ + fn create_scope(pallet_id: u64, scope_id: [u8;32])-> DispatchResult{ + let pallet_id: u64 = pallet_id.try_into().unwrap(); >::try_mutate(pallet_id, |scopes|{ ensure!(!scopes.contains(&scope_id), Error::::ScopeAlreadyExists); scopes.try_push(scope_id).map_err(|_| Error::::ExceedMaxScopesPerPallet)?; @@ -18,11 +19,8 @@ impl RoleBasedAccessControl for Pallet{ } /// Inserts roles and links them to the pallet - fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, T::MaxRolesPerPallet>) -> + fn create_and_set_roles(pallet_id: u64, roles: BoundedVec >, T::MaxRolesPerPallet>) -> Result, DispatchError>{ - // TODO: check for duplicates - //ensure!(Self::has_unique_elements(roles.to_vec()), Error::::DuplicateRole); - let role_ids: Vec<[u8;32]> = roles.iter().map(|r|{ Self::create_role(r.to_owned()) }).collect(); @@ -33,12 +31,13 @@ impl RoleBasedAccessControl for Pallet{ fn create_role(role: BoundedVec>)-> [u8;32]{ let role_id = role.using_encoded(blake2_256); + // no "get_or_insert" method found // insert is infalible in this case - >::insert(role_id, role); + if !>::contains_key(role_id) {>::insert(role_id, role)}; role_id } - fn set_role_to_pallet(pallet_id: u32, role_id: [u8;32] )-> DispatchResult{ + fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); >::try_mutate(pallet_id, |roles|{ @@ -48,9 +47,11 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult{ + fn set_multiple_pallet_roles(pallet_id: u64, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult{ + // checks for duplicates: + let pallet_roles = >::get(&pallet_id); for id in roles.clone(){ - ensure!(!>::get(&pallet_id).contains(&id), Error::::DuplicateRole ); + ensure!(!pallet_roles.contains(&id), Error::::DuplicateRole ); } >::try_mutate(pallet_id, |pallet_roles|{ pallet_roles.try_extend(roles.into_iter()) @@ -59,23 +60,29 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]{ + fn create_permission(pallet_id: u64, permission: BoundedVec) -> [u8;32]{; let permission_id = permission.using_encoded(blake2_256); - >::insert(pallet_id, permission_id, permission); + if !>::contains_key(pallet_id, permission_id){ + >::insert(pallet_id, permission_id, permission); + } permission_id } - fn set_permission_to_role( pallet_id: u32, role: [u8;32], permission: [u8;32] ) -> DispatchResult{ - //check for duplicates - - // try pushing + fn set_permission_to_role( pallet_id: u64, role_id: [u8;32], permission_id: [u8;32] ) -> DispatchResult{ + ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); + Self::is_role_linked_to_pallet(pallet_id, &role_id)?; + >::try_mutate(pallet_id, role_id, | role_permissions|{ + ensure!(role_permissions.contains(&permission_id), Error::::DuplicatePermission); + role_permissions.try_push(permission_id).map_err(|_| Error::::ExceedMaxPermissionsPerRole) + })?; Ok(()) } - fn assign_role_to_user(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ - Self::scope_exists(&pallet_id, &scope_id)?; - Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; + fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ + Self::scope_exists(pallet_id, &scope_id)?; + Self::is_role_linked_to_pallet(pallet_id, &role_id)?; + let pallet_id: u64 = pallet_id.try_into().unwrap(); >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) @@ -91,12 +98,13 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ /// Authorize by role, not permissions - fn is_user_authorized(user: T::AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ + fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format let role_id = Self::get_role_id(role)?; - Self::scope_exists(&pallet_id, &scope_id)?; - Self::is_role_linked_to_pallet(&pallet_id, &role_id)?; + Self::scope_exists(pallet_id, &scope_id)?; + Self::is_role_linked_to_pallet(pallet_id, &role_id)?; // Perform confirmation on both maps + let pallet_id: u64 = pallet_id.try_into().unwrap(); // TODO: test if a role that doesnt exists cause any errors let users = >::get( (pallet_id, scope_id, role_id) ); ensure!(users.contains(&user), Error::::NotAuthorized); @@ -105,13 +113,13 @@ impl RoleBasedAccessControl for Pallet{ ensure!(roles.contains(&role_id), Error::::NotAuthorized ); Ok(()) } - /// Also checks if pallet is stored - fn scope_exists(pallet_id: &u32, scope_id:&[u8;32]) -> DispatchResult{ + /// Also checks if pallet is stored. Need this function to expose the check to other pallets + fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult{ ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); Ok(()) } - fn is_role_linked_to_pallet(pallet_id: &u32, role_id: &[u8;32])-> DispatchResult{ + fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) .ok_or(Error::::RoleNotLinkedToPallet)?; @@ -127,7 +135,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(role_id) } - fn get_permission(pallet_id: u32 ,id_or_permission: IdOrString)->Result<[u8;32], DispatchError>{ + fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString)->Result<[u8;32], DispatchError>{ let permission_id = match id_or_permission{ IdOrString::Id(id)=>id, IdOrString::String(permission_str)=> permission_str.using_encoded(blake2_256), diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index e7b5cc83..f174070e 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -54,7 +54,7 @@ pub mod pallet { pub(super) type Scopes = StorageMap< _, Blake2_128Concat, - u32, // pallet_id + u64, // pallet_id BoundedVec<[u8;32], T::MaxScopesPerPallet>, // scopes_id ValueQuery, >; @@ -74,7 +74,7 @@ pub mod pallet { pub(super) type PalletRoles = StorageMap< _, Blake2_128Concat, - u32, // pallet_id + u64, // pallet_id BoundedVec<[u8;32], T::MaxRolesPerPallet >, // role_id ValueQuery, >; @@ -84,7 +84,7 @@ pub mod pallet { pub(super) type Permissions = StorageDoubleMap< _, Blake2_128Concat, - u32, // pallet_id + u64, // pallet_id Blake2_128Concat, [u8;32], // permission_id BoundedVec, // permission str @@ -96,7 +96,7 @@ pub mod pallet { pub(super) type PermissionsByRole = StorageDoubleMap< _, Blake2_128Concat, - u32, // pallet_id + u64, // pallet_id Blake2_128Concat, [u8;32], // role_id BoundedVec<[u8;32], T::MaxPermissionsPerRole >, // permission_ids @@ -109,7 +109,8 @@ pub mod pallet { _, ( NMapKey,// user - NMapKey, // pallet_id + // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors + NMapKey, // pallet_id NMapKey, // scope_id ), BoundedVec<[u8;32], T::MaxRolesPerUser>, // roles (ids) @@ -121,7 +122,9 @@ pub mod pallet { pub(super) type UsersByScope = StorageNMap< _, ( - NMapKey, // pallet_id + // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors + // on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes. + NMapKey, // pallet_id NMapKey, // scope_id NMapKey, // role_id ), diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 34aac3fc..4a1e1092 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -21,23 +21,23 @@ pub trait RoleBasedAccessControl{ type MaxRolesPerPallet: Get; type PermissionMaxLen: Get; // scopes - fn create_scope(pallet_id: u32, scope_id: [u8;32]) -> DispatchResult; + fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; // roles - fn create_and_set_roles(pallet_id: u32, roles: BoundedVec >, Self::MaxRolesPerPallet>) -> + fn create_and_set_roles(pallet_id: u64, roles: BoundedVec >, Self::MaxRolesPerPallet>) -> Result, DispatchError>; fn create_role(role: BoundedVec>)-> [u8;32]; - fn set_role_to_pallet(pallet_id: u32, role_id: [u8;32] )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_id: u32, roles: BoundedVec<[u8;32], Self::MaxRolesPerPallet>)->DispatchResult; - fn assign_role_to_user(user: AccountId, pallet_id: u32, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; + fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; + fn set_multiple_pallet_roles(pallet_id: u64, roles: BoundedVec<[u8;32], Self::MaxRolesPerPallet>)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; // permissions - fn create_permission(pallet_id: u32, permission: BoundedVec) -> [u8;32]; - fn set_permission_to_role( pallet_id: u32, role: [u8;32], permission: [u8;32] ) -> DispatchResult; + fn create_permission(pallet_id: u64, permission: BoundedVec) -> [u8;32]; + fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; // helpers - fn is_user_authorized(user: AccountId, pallet_id: u32, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult; - fn scope_exists(pallet_id: &u32, scope_id:&[u8;32]) -> DispatchResult; - fn is_role_linked_to_pallet(pallet_id: &u32, role_id: &[u8;32])-> DispatchResult; + fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult; + fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; + fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; fn get_role_id(id_or_role: IdOrString>)->Result<[u8;32], DispatchError>; - fn get_permission(pallet_id: u32 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; + fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; fn has_unique_elements(vec: Vec) -> bool; From dae172bb6f56962c9d3c3be1b359efa92ea17cad Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 28 Jul 2022 20:11:13 -0500 Subject: [PATCH 010/103] Improves error handling --- pallets/rbac/src/functions.rs | 45 +++++++++++++++++++++++++---------- pallets/rbac/src/lib.rs | 2 ++ pallets/rbac/src/types.rs | 9 ++++--- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 09b2f8b3..cd134f9e 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -19,22 +19,26 @@ impl RoleBasedAccessControl for Pallet{ } /// Inserts roles and links them to the pallet - fn create_and_set_roles(pallet_id: u64, roles: BoundedVec >, T::MaxRolesPerPallet>) -> + fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>{ - let role_ids: Vec<[u8;32]> = roles.iter().map(|r|{ - Self::create_role(r.to_owned()) - }).collect(); - let bounded_ids = BoundedVec::try_from(role_ids).map_err(|_| Error::::ExceedMaxRolesPerPallet)?; - Self::set_multiple_pallet_roles(pallet_id, bounded_ids.clone())?; + let mut role_ids= Vec::<[u8;32]>::new(); + for role in roles{ + role_ids.push( Self::create_role(role.to_owned())? ); + } + Self::set_multiple_pallet_roles(pallet_id, role_ids.clone())?; + let bounded_ids = Self::bound(role_ids, Error::::ExceedMaxRolesPerPallet)?; Ok(bounded_ids) } - fn create_role(role: BoundedVec>)-> [u8;32]{ + fn create_role(role: Vec)-> Result<[u8;32], DispatchError>{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found // insert is infalible in this case - if !>::contains_key(role_id) {>::insert(role_id, role)}; - role_id + // TODO: Parametrize role length and declare error + let b_role = Self::bound::<_,ConstU32<100>>(role, Error::::ExceedMaxRolesPerUser)?; + ensure!(role_id == b_role.using_encoded(blake2_256), Error::::NoneValue); + if !>::contains_key(role_id) {>::insert(role_id, b_role)}; + Ok(role_id) } fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult{ @@ -47,7 +51,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn set_multiple_pallet_roles(pallet_id: u64, roles: BoundedVec<[u8;32], T::MaxRolesPerPallet>)->DispatchResult{ + fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult{ // checks for duplicates: let pallet_roles = >::get(&pallet_id); for id in roles.clone(){ @@ -60,12 +64,19 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn create_permission(pallet_id: u64, permission: BoundedVec) -> [u8;32]{; + fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>{ let permission_id = permission.using_encoded(blake2_256); + //let b_permission= BoundedVec::::try_from(permission); + let b_permission = Self::bound:: + <_,T::PermissionMaxLen>(permission, Error::::ExceedPermissionMaxLen)?; + // Testing: a boundedvec id should be equal to a vec id because they have the same data + ensure!(permission_id == b_permission.using_encoded(blake2_256), Error::::NoneValue); + + log::info!("Is permission_id equal: {}",permission_id == b_permission.using_encoded(blake2_256)); if !>::contains_key(pallet_id, permission_id){ - >::insert(pallet_id, permission_id, permission); + >::insert(pallet_id, permission_id, b_permission); } - permission_id + Ok(permission_id) } fn set_permission_to_role( pallet_id: u64, role_id: [u8;32], permission_id: [u8;32] ) -> DispatchResult{ @@ -151,8 +162,16 @@ impl RoleBasedAccessControl for Pallet{ vec.len() == filtered_vec.len() } + type MaxRolesPerPallet = T::MaxRolesPerPallet; type PermissionMaxLen = T::PermissionMaxLen; +} + +impl Pallet{ + fn bound>(vec: Vec, err : Error )->Result, Error>{ + let err = Error::::DuplicatePermission; + BoundedVec::::try_from(vec).map_err(|_| err) + } } \ No newline at end of file diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index f174070e..37e39759 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -175,6 +175,8 @@ pub mod pallet { ExceedMaxRolesPerUser, /// This role cannot have assigned to more users in this scope ExceedMaxUsersPerRole, + /// The permission string is too long + ExceedPermissionMaxLen, /// The user does not have the specified role NotAuthorized, } diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 4a1e1092..f3b376de 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -23,14 +23,14 @@ pub trait RoleBasedAccessControl{ // scopes fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; // roles - fn create_and_set_roles(pallet_id: u64, roles: BoundedVec >, Self::MaxRolesPerPallet>) -> + fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>; - fn create_role(role: BoundedVec>)-> [u8;32]; + fn create_role(role: Vec)-> Result<[u8;32], DispatchError>; fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_id: u64, roles: BoundedVec<[u8;32], Self::MaxRolesPerPallet>)->DispatchResult; + fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; // permissions - fn create_permission(pallet_id: u64, permission: BoundedVec) -> [u8;32]; + fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>; fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; // helpers fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult; @@ -40,5 +40,4 @@ pub trait RoleBasedAccessControl{ fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; fn has_unique_elements(vec: Vec) -> bool; - } \ No newline at end of file From ae6005b23c57a808f9ec19225b8d4b23302a3bd8 Mon Sep 17 00:00:00 2001 From: didiermis Date: Fri, 29 Jul 2022 08:17:17 -0500 Subject: [PATCH 011/103] backup --- pallets/gated-marketplace/src/lib.rs | 16 ++++++++++++++-- pallets/gated-marketplace/src/types.rs | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 8240c53e..eecf98eb 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -120,7 +120,7 @@ pub mod pallet { ApplicationStatus, //K2: application_status BoundedVec, ValueQuery - >; + >; #[pallet::storage] #[pallet::getter(fn custodians)] @@ -208,7 +208,10 @@ pub mod pallet { } #[pallet::call] - impl Pallet { + impl Pallet + where + T: pallet_uniques::Config, + { /// Create a new marketplace. /// @@ -425,6 +428,15 @@ pub mod pallet { Self::do_remove_marketplace(who, marketplace_id) } + + // #[transactional] + // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + // pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: OfferType, item_id: Vec, price:) -> DispatchResult { + // let who = ensure_signed(origin)?; + + // Self::do_enlist_offer(who, marketplace_id, offer_id, offer_type, offer_data) + // } + /// Kill all the stored data. /// diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 4ff5f046..4e969165 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -68,3 +68,22 @@ impl PartialEq for ApplicationField{ self.cid == other.cid && self.display_name == other.display_name } } + + +//offers +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] +pub enum OfferStatus{ + Open, + Freezed, + Closed, +} + +pub struct OfferData{ + pub status: OfferStatus, + pub price: u64, + pub cid: [u8;32], + pub label: BoundedVec >, + pub notes: BoundedVec >, + pub feedback: BoundedVec >, + +} \ No newline at end of file From d207a797d2db3364895801908a12a4662558978b Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 29 Jul 2022 16:48:52 -0500 Subject: [PATCH 012/103] adds multiple permissions setting and creates roles on gated-marketplaces pallet --- pallets/gated-marketplace/src/functions.rs | 2 +- pallets/gated-marketplace/src/lib.rs | 6 +++- pallets/rbac/src/functions.rs | 35 ++++++++++++++++++++-- pallets/rbac/src/lib.rs | 6 +++- pallets/rbac/src/types.rs | 11 +++++-- runtime/src/lib.rs | 4 ++- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 34c479eb..9f43444f 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -392,7 +392,7 @@ impl Pallet { Ok(()) } - fn get_pallet_id()->u64{ + pub fn get_pallet_id()->u64{ Self::index().try_into().unwrap() } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 3bdee82d..83409e9f 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -20,7 +20,7 @@ pub mod pallet { use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; use frame_system::pallet_prelude::*; - //use sp_runtime::sp_std::vec::Vec; + use sp_runtime::sp_std::vec::Vec; use crate::types::*; use pallet_rbac::types::RoleBasedAccessControl; // RBAC pallet aliases @@ -228,6 +228,10 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; //T::Rbac::create_and_set_roles() //let mut roles = BoundedVec:: >, MaxRolesPerPallet >::default(); //roles.try_push(Self::str_to_bvec_uncheked("Owner"))?; + let mut roles = Vec::>::new(); + roles.push("Owner".as_bytes().to_vec()); + roles.push("Admin".as_bytes().to_vec()); + T::Rbac::create_and_set_roles(Self::get_pallet_id(), roles)?; Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index cd134f9e..9a261047 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -35,7 +35,7 @@ impl RoleBasedAccessControl for Pallet{ // no "get_or_insert" method found // insert is infalible in this case // TODO: Parametrize role length and declare error - let b_role = Self::bound::<_,ConstU32<100>>(role, Error::::ExceedMaxRolesPerUser)?; + let b_role = Self::bound::<_,T::RoleMaxLen>(role, Error::::ExceedMaxRolesPerUser)?; ensure!(role_id == b_role.using_encoded(blake2_256), Error::::NoneValue); if !>::contains_key(role_id) {>::insert(role_id, b_role)}; Ok(role_id) @@ -64,6 +64,18 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn create_and_set_permissions(pallet_id: u64, role_id: [u8;32], permissions: Vec>)-> + Result, DispatchError> { + // TODO: Test this functionality + let mut permission_ids = Vec::<[u8;32]>::new(); + for permision in permissions{ + permission_ids.push( Self::create_permission(pallet_id, permision.to_owned())? ); + } + Self::set_multiple_permisions_to_role(pallet_id, role_id, permission_ids.clone())?; + let b_permissions = Self::bound(permission_ids, Error::::ExceedMaxPermissionsPerRole)?; + Ok(b_permissions) + } + fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>{ let permission_id = permission.using_encoded(blake2_256); //let b_permission= BoundedVec::::try_from(permission); @@ -90,6 +102,19 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn set_multiple_permisions_to_role( pallet_id: u64, role_id: [u8;32], permissions: Vec<[u8;32]> )-> DispatchResult{ + // checks for duplicates: + let role_permissions = >::get(&pallet_id, role_id); + for id in permissions.clone(){ + ensure!(!role_permissions.contains(&id), Error::::DuplicateRole ); + } + >::try_mutate(pallet_id, role_id, |role_permissions|{ + role_permissions.try_extend(permissions.into_iter()) + }).map_err(|_| Error::::ExceedMaxPermissionsPerRole)?; + + Ok(()) + } + fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ Self::scope_exists(pallet_id, &scope_id)?; Self::is_role_linked_to_pallet(pallet_id, &role_id)?; @@ -109,7 +134,7 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ /// Authorize by role, not permissions - fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult{ + fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format let role_id = Self::get_role_id(role)?; Self::scope_exists(pallet_id, &scope_id)?; @@ -137,7 +162,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn get_role_id(id_or_role: IdOrString>)->Result<[u8;32], DispatchError>{ + fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>{ let role_id = match id_or_role{ IdOrString::Id(id)=>id, IdOrString::String(role_str)=> role_str.using_encoded(blake2_256), @@ -165,8 +190,12 @@ impl RoleBasedAccessControl for Pallet{ type MaxRolesPerPallet = T::MaxRolesPerPallet; + type MaxPermissionsPerRole = T::MaxPermissionsPerRole; + type PermissionMaxLen = T::PermissionMaxLen; + type RoleMaxLen = T::RoleMaxLen; + } impl Pallet{ diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 37e39759..3faa0187 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -34,6 +34,8 @@ pub mod pallet { type MaxRolesPerPallet: Get; + type RoleMaxLen: Get; + type PermissionMaxLen: Get; type MaxPermissionsPerRole: Get; @@ -65,7 +67,7 @@ pub mod pallet { _, Identity, [u8;32], // role_id - BoundedVec >, // role + BoundedVec, // role OptionQuery, >; @@ -175,6 +177,8 @@ pub mod pallet { ExceedMaxRolesPerUser, /// This role cannot have assigned to more users in this scope ExceedMaxUsersPerRole, + /// The role string is too long + ExceedRoleMaxLen, /// The permission string is too long ExceedPermissionMaxLen, /// The user does not have the specified role diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index f3b376de..5e53ebf9 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -10,7 +10,7 @@ pub enum IdOrString >{ pub struct RoleConfiguration{ - pub role_name: BoundedVec>, + pub role_name: BoundedVec, pub permissions: BoundedVec< BoundedVec, T::MaxPermissionsPerRole @@ -19,6 +19,8 @@ pub struct RoleConfiguration{ pub trait RoleBasedAccessControl{ type MaxRolesPerPallet: Get; + type MaxPermissionsPerRole: Get; + type RoleMaxLen: Get; type PermissionMaxLen: Get; // scopes fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; @@ -30,13 +32,16 @@ pub trait RoleBasedAccessControl{ fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; // permissions + fn create_and_set_permissions(pallet_id: u64, role: [u8;32], permissions: Vec>)-> + Result, DispatchError>; fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>; fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; + fn set_multiple_permisions_to_role( pallet_id: u64, role: [u8;32], permission: Vec<[u8;32]> )-> DispatchResult; // helpers - fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString> ) -> DispatchResult; + fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString ) -> DispatchResult; fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; - fn get_role_id(id_or_role: IdOrString>)->Result<[u8;32], DispatchError>; + fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>; fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; fn has_unique_elements(vec: Vec) -> bool; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1d808fad..fa5be6cb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -601,7 +601,8 @@ impl pallet_nbv_storage::Config for Runtime { parameter_types! { pub const MaxScopesPerPallet: u32 = 1000; pub const MaxRolesPerPallet: u32 = 20; - pub const PermissionMaxLen: u32 = 35; + pub const RoleMaxLen: u32 = 30; + pub const PermissionMaxLen: u32 = 30; pub const MaxPermissionsPerRole: u32 = 10; pub const MaxRolesPerUser: u32 = 10; pub const MaxUsersPerRole: u32 = 10; @@ -610,6 +611,7 @@ impl pallet_rbac::Config for Runtime { type Event = Event; type MaxScopesPerPallet = MaxScopesPerPallet; type MaxRolesPerPallet = MaxRolesPerPallet; + type RoleMaxLen = RoleMaxLen; type PermissionMaxLen = PermissionMaxLen; type MaxPermissionsPerRole = MaxPermissionsPerRole; type MaxRolesPerUser = MaxRolesPerUser; From b6cf73bb1e7075d5a5be8e052cb32b1a8a8764b0 Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 09:29:17 -0500 Subject: [PATCH 013/103] add enums OfferStatus & OfferType --- pallets/gated-marketplace/src/types.rs | 32 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 4e969165..1465b42e 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -78,12 +78,28 @@ pub enum OfferStatus{ Closed, } -pub struct OfferData{ +impl Default for OfferStatus{ + fn default() -> Self { + OfferStatus::Open + } +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] +pub enum OfferType{ + Sale, + Buy, +} + +#[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] +#[scale_info(skip_type_params(T))] +#[codec(mel_bound())] +pub struct OfferData{ + pub offer_id: [u8;32], + pub marketplace_id: [u8;32], + pub creator: T::AccountId, + //pub price: u128, pub status: OfferStatus, - pub price: u64, - pub cid: [u8;32], - pub label: BoundedVec >, - pub notes: BoundedVec >, - pub feedback: BoundedVec >, - -} \ No newline at end of file + //pub creation_date: u128, + //pub expiration_date: u128, + //pub offer_type: OfferType, +} From 08e7479a6794885cafc342a676fba3262ef98f7d Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 09:46:45 -0500 Subject: [PATCH 014/103] add storagemaps for prices & offers --- pallets/gated-marketplace/src/lib.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index eecf98eb..db0f6055 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -135,6 +135,31 @@ pub mod pallet { >; + #[pallet::storage] + #[pallet::getter(fn prices)] + pub(super) type Prices = StorageDoubleMap< + _, + Blake2_128Concat, + T::CollectionId, //collection_id + Blake2_128Concat, + T::ItemId, //item_id + u128, // price + ValueQuery +>; + + #[pallet::storage] + #[pallet::getter(fn offers)] + pub(super) type Offers = StorageDoubleMap< + _, + Blake2_128Concat, + T::CollectionId, //collection_id + Blake2_128Concat, + T::ItemId, //item_id + OfferData, //offer data + OptionQuery + >; + + #[pallet::event] From f77c48dfa18a6cc3c594978ae856e16bd4e3f23b Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 1 Aug 2022 09:50:59 -0500 Subject: [PATCH 015/103] adds permissions to initial setup Within a helper function --- pallets/gated-marketplace/src/functions.rs | 24 ++++++++++++++++++++-- pallets/gated-marketplace/src/lib.rs | 10 ++------- pallets/rbac/src/functions.rs | 2 -- pallets/rbac/src/types.rs | 9 -------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 9f43444f..57020f37 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,4 +1,5 @@ use core::default; +use core::ops::Bound; use super::*; use frame_support::pallet_prelude::*; @@ -6,11 +7,28 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -use pallet_rbac::types::RoleBasedAccessControl; +use pallet_rbac::types::*; impl Pallet { + pub fn do_initial_setup()->DispatchResult{ + let pallet_id = Self::get_pallet_id(); + let mut super_roles = Vec::>::new(); + let mut super_permissions = Vec::>::new(); + super_roles.push("Owner".as_bytes().to_vec()); + super_roles.push("Admin".as_bytes().to_vec()); + super_permissions.push("enroll".as_bytes().to_vec()); + super_permissions.push("add_authority".as_bytes().to_vec()); + super_permissions.push("remove_authority".as_bytes().to_vec()); + super_permissions.push("update_label_marketplace".as_bytes().to_vec()); + let super_role_ids = T::Rbac::create_and_set_roles(pallet_id, super_roles)?; + for super_role in super_role_ids{ + T::Rbac::create_and_set_permissions(pallet_id, super_role, super_permissions.clone())?; + } + Ok(()) + } + pub fn do_create_marketplace(owner: T::AccountId, admin: T::AccountId ,marketplace: Marketplace)->DispatchResult{ // Gen market id let marketplace_id = marketplace.using_encoded(blake2_256); @@ -52,6 +70,8 @@ impl Pallet { pub fn do_enroll(authority: T::AccountId,marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ // ensure the origin is owner or admin + // T::Rbac::is_user_authorized(authority,Self::get_pallet_id(), marketplace_id, + // IdOrString::String(BoundedVec::try_from("henlo".as_bytes().to_vec()).expect("erfge")) )?; Self::can_enroll(authority, marketplace_id)?; let next_status = match approved{ true => ApplicationStatus::Approved, @@ -69,7 +89,7 @@ impl Pallet { }, }; Self::change_applicant_status(applicant, marketplace_id, next_status, feedback)?; - // TODO: if rejected remove application and files? + Self::deposit_event(Event::ApplicationProcessed(account_or_application, marketplace_id, next_status)); Ok(()) } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 83409e9f..f024ac2b 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -223,15 +223,9 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(10))] - pub fn set_up_permissions(origin: OriginFor) -> DispatchResult { + pub fn initial_setup(origin: OriginFor) -> DispatchResult { T::RemoveOrigin::ensure_origin(origin.clone())?; - //T::Rbac::create_and_set_roles() - //let mut roles = BoundedVec:: >, MaxRolesPerPallet >::default(); - //roles.try_push(Self::str_to_bvec_uncheked("Owner"))?; - let mut roles = Vec::>::new(); - roles.push("Owner".as_bytes().to_vec()); - roles.push("Admin".as_bytes().to_vec()); - T::Rbac::create_and_set_roles(Self::get_pallet_id(), roles)?; + Self::do_initial_setup()?; Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 9a261047..52be76da 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -33,8 +33,6 @@ impl RoleBasedAccessControl for Pallet{ fn create_role(role: Vec)-> Result<[u8;32], DispatchError>{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found - // insert is infalible in this case - // TODO: Parametrize role length and declare error let b_role = Self::bound::<_,T::RoleMaxLen>(role, Error::::ExceedMaxRolesPerUser)?; ensure!(role_id == b_role.using_encoded(blake2_256), Error::::NoneValue); if !>::contains_key(role_id) {>::insert(role_id, b_role)}; diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 5e53ebf9..1510fe7e 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -8,15 +8,6 @@ pub enum IdOrString >{ String(BoundedVec) } - -pub struct RoleConfiguration{ - pub role_name: BoundedVec, - pub permissions: BoundedVec< - BoundedVec, - T::MaxPermissionsPerRole - > -} - pub trait RoleBasedAccessControl{ type MaxRolesPerPallet: Get; type MaxPermissionsPerRole: Get; From edff7802eb3866708339f59c2ef005e0d855b971 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 1 Aug 2022 13:30:25 -0500 Subject: [PATCH 016/103] Adds rbac to add_authority tx --- pallets/gated-marketplace/src/functions.rs | 18 +++++++++++++----- pallets/gated-marketplace/src/types.rs | 13 ++++++++++++- pallets/rbac/src/functions.rs | 6 +++--- pallets/rbac/src/types.rs | 2 +- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 57020f37..8ef7dd8f 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -16,8 +16,8 @@ impl Pallet { let pallet_id = Self::get_pallet_id(); let mut super_roles = Vec::>::new(); let mut super_permissions = Vec::>::new(); - super_roles.push("Owner".as_bytes().to_vec()); - super_roles.push("Admin".as_bytes().to_vec()); + super_roles.push(MarketplaceAuthority::Owner.to_vec()); + super_roles.push(MarketplaceAuthority::Admin.to_vec()); super_permissions.push("enroll".as_bytes().to_vec()); super_permissions.push("add_authority".as_bytes().to_vec()); super_permissions.push("remove_authority".as_bytes().to_vec()); @@ -34,12 +34,16 @@ impl Pallet { let marketplace_id = marketplace.using_encoded(blake2_256); // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); - T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; //Insert on marketplaces and marketplaces by auth Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); + T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; + T::Rbac::assign_role_to_user(owner.clone(), Self::get_pallet_id(), + &marketplace_id, MarketplaceAuthority::Owner.to_vec().using_encoded(blake2_256))?; + T::Rbac::assign_role_to_user(admin.clone(), Self::get_pallet_id(), + &marketplace_id, MarketplaceAuthority::Admin.to_vec().using_encoded(blake2_256))?; Self::deposit_event(Event::MarketplaceStored(owner, admin, marketplace_id)); Ok(()) } @@ -107,10 +111,14 @@ impl Pallet { MarketplaceAuthority::Owner => { ensure!(!Self::owner_exist(marketplace_id), Error::::OnlyOneOwnerIsAllowed); Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; + T::Rbac::assign_role_to_user(account.clone(), Self::get_pallet_id(), + &marketplace_id, authority_type.to_vec().using_encoded(blake2_256))?; + }, _ =>{ - - Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; + Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; + T::Rbac::assign_role_to_user(account.clone(), Self::get_pallet_id(), + &marketplace_id, authority_type.to_vec().using_encoded(blake2_256))?; } } diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 4ff5f046..5560c1c1 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; - +use sp_runtime::sp_std::vec::Vec; #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -33,6 +33,17 @@ impl Default for MarketplaceAuthority{ } } +impl MarketplaceAuthority{ + pub fn to_vec(&self) -> Vec{ + match self{ + MarketplaceAuthority::Owner => "Owner".as_bytes().to_vec(), + MarketplaceAuthority::Admin => "Admin".as_bytes().to_vec(), + MarketplaceAuthority::Appraiser => "Appraiser".as_bytes().to_vec(), + MarketplaceAuthority::RedemptionSpecialist => "Redemption_specialist".as_bytes().to_vec(), + } + } +} + #[derive(CloneNoBound,Encode, Decode, Eq, PartialEq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 52be76da..8e82e3ef 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -113,10 +113,10 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult{ - Self::scope_exists(pallet_id, &scope_id)?; + fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult{ + Self::scope_exists(pallet_id, scope_id)?; Self::is_role_linked_to_pallet(pallet_id, &role_id)?; - let pallet_id: u64 = pallet_id.try_into().unwrap(); + >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 1510fe7e..30c665a7 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -21,7 +21,7 @@ pub trait RoleBasedAccessControl{ fn create_role(role: Vec)-> Result<[u8;32], DispatchError>; fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; - fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: [u8;32], role_id: [u8;32]) -> DispatchResult; + fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; // permissions fn create_and_set_permissions(pallet_id: u64, role: [u8;32], permissions: Vec>)-> Result, DispatchError>; From dd173e848c2e34a544b3a5a4d4219583855048a2 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 1 Aug 2022 17:34:08 -0500 Subject: [PATCH 017/103] includes rbac authorizatino mechanism in gated marketplaces --- pallets/gated-marketplace/src/functions.rs | 22 ++++++++++++++-------- pallets/rbac/src/functions.rs | 21 +++++++++++++++------ pallets/rbac/src/types.rs | 6 ++++-- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 8ef7dd8f..163c4bf2 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -72,11 +72,12 @@ impl Pallet { Ok(()) } - pub fn do_enroll(authority: T::AccountId,marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ + pub fn do_enroll(authority: T::AccountId, marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ // ensure the origin is owner or admin // T::Rbac::is_user_authorized(authority,Self::get_pallet_id(), marketplace_id, // IdOrString::String(BoundedVec::try_from("henlo".as_bytes().to_vec()).expect("erfge")) )?; Self::can_enroll(authority, marketplace_id)?; + //T::Rbac::is_user_authorized(authority, Self::get_pallet_id(), &marketplace_id, MarketplaceAuthority::Owner. ); let next_status = match approved{ true => ApplicationStatus::Approved, false => ApplicationStatus::Rejected, @@ -289,13 +290,18 @@ impl Pallet { fn can_enroll( authority: T::AccountId, marketplace_id: [u8;32] ) -> DispatchResult{ // to enroll, the account needs to be an owner or an admin - let roles = >::try_get(authority, marketplace_id) - .map_err(|_| Error::::CannotEnroll)?; - // iter().any could be called too but this maps directly to desired error - roles.iter().find(|&role|{ - role.eq(&MarketplaceAuthority::Owner) || role.eq(&MarketplaceAuthority::Admin) - }).ok_or(Error::::CannotEnroll)?; - Ok(()) + // let roles = >::try_get(authority, marketplace_id) + // .map_err(|_| Error::::CannotEnroll)?; + // // iter().any could be called too but this maps directly to desired error + // roles.iter().find(|&role|{ + // role.eq(&MarketplaceAuthority::Owner) || role.eq(&MarketplaceAuthority::Admin) + // }).ok_or(Error::::CannotEnroll)?; + let auths = [ + MarketplaceAuthority::Owner.to_vec().using_encoded(blake2_256), + MarketplaceAuthority::Admin.to_vec().using_encoded(blake2_256) + ].to_vec(); + //TODO: Test if the auth mechanism works + T::Rbac::has_role(authority.clone(),Self::get_pallet_id(), &marketplace_id,auths) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 8e82e3ef..29f6d604 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -132,13 +132,12 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ /// Authorize by role, not permissions - fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString ) -> DispatchResult{ + fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id : &[u8;32] ) -> DispatchResult{ // get id, whether is given directly or by its string in boundedvec format - let role_id = Self::get_role_id(role)?; - Self::scope_exists(pallet_id, &scope_id)?; - Self::is_role_linked_to_pallet(pallet_id, &role_id)?; + //let role_id = Self::get_role_id(role)?; + Self::scope_exists(pallet_id, scope_id)?; + Self::is_role_linked_to_pallet(pallet_id, role_id)?; // Perform confirmation on both maps - let pallet_id: u64 = pallet_id.try_into().unwrap(); // TODO: test if a role that doesnt exists cause any errors let users = >::get( (pallet_id, scope_id, role_id) ); ensure!(users.contains(&user), Error::::NotAuthorized); @@ -147,6 +146,17 @@ impl RoleBasedAccessControl for Pallet{ ensure!(roles.contains(&role_id), Error::::NotAuthorized ); Ok(()) } + + fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult { + Self::scope_exists(pallet_id, scope_id)?; + + let user_roles = >::get((user, pallet_id, scope_id)); + ensure!( + user_roles.iter().any(|r| role_ids.contains(r) ), + Error::::NotAuthorized + ); + Ok(()) + } /// Also checks if pallet is stored. Need this function to expose the check to other pallets fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult{ ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); @@ -198,7 +208,6 @@ impl RoleBasedAccessControl for Pallet{ impl Pallet{ fn bound>(vec: Vec, err : Error )->Result, Error>{ - let err = Error::::DuplicatePermission; BoundedVec::::try_from(vec).map_err(|_| err) } } \ No newline at end of file diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 30c665a7..bb9d5a5f 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -15,13 +15,14 @@ pub trait RoleBasedAccessControl{ type PermissionMaxLen: Get; // scopes fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; - // roles + // roles creation and setting fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>; fn create_role(role: Vec)-> Result<[u8;32], DispatchError>; fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; + // TODO: role removal // permissions fn create_and_set_permissions(pallet_id: u64, role: [u8;32], permissions: Vec>)-> Result, DispatchError>; @@ -29,7 +30,8 @@ pub trait RoleBasedAccessControl{ fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; fn set_multiple_permisions_to_role( pallet_id: u64, role: [u8;32], permission: Vec<[u8;32]> )-> DispatchResult; // helpers - fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: [u8;32], role: IdOrString ) -> DispatchResult; + fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: &[u8;32] ) -> DispatchResult; + fn has_role(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult; fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>; From 71240e01f66f774cd52dca4cb04295a03b9e829e Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 21:47:21 -0500 Subject: [PATCH 018/103] add pallet-timestamp --- pallets/gated-marketplace/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index 35f70ef3..e1a6a23f 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -26,6 +26,7 @@ sp-runtime = { default-features = false, version = "6.0.0", git = "https://githu pallet-balances = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } pallet-uniques = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } +pallet-timestamp = { default-features = false, version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.23" } pallet-fruniques = {path = "../fruniques", default-features = false, version = "0.1.0-dev"} [dev-dependencies] From 9d2e03baa659645a284affb8978a8b69ced798df Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 21:49:54 -0500 Subject: [PATCH 019/103] use temporal variable types for timestamp --- pallets/gated-marketplace/src/types.rs | 11 ++++++----- runtime/src/lib.rs | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 1465b42e..8ea08fda 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -90,16 +90,17 @@ pub enum OfferType{ Buy, } -#[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] +#[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct OfferData{ pub offer_id: [u8;32], pub marketplace_id: [u8;32], pub creator: T::AccountId, - //pub price: u128, + pub price: u128, pub status: OfferStatus, - //pub creation_date: u128, - //pub expiration_date: u128, - //pub offer_type: OfferType, + pub creation_date: u64, + pub expiration_date: u64, + pub offer_type: OfferType, } + diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0ca7bea2..b6fc4ae8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -568,6 +568,7 @@ impl pallet_gated_marketplace::Config for Runtime { type NameMaxLen= NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; + type TimeProvider = Timestamp; } parameter_types! { From 4888aef73a6e963802fee13b203136d8b8f4f96c Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 21:50:21 -0500 Subject: [PATCH 020/103] add extrinsic enlist_offer, events and errors --- pallets/gated-marketplace/src/lib.rs | 29 +++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index db0f6055..4cbe1c42 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -20,11 +20,12 @@ pub mod pallet { use frame_system::pallet_prelude::*; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; + use frame_support::traits::UnixTime; #[pallet::config] - pub trait Config: frame_system::Config + pallet_fruniques::Config { + pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config{ type Event: From> + IsType<::Event>; - + type TimeProvider: UnixTime; type RemoveOrigin: EnsureOrigin; #[pallet::constant] @@ -144,7 +145,7 @@ pub mod pallet { Blake2_128Concat, T::ItemId, //item_id u128, // price - ValueQuery + OptionQuery >; #[pallet::storage] @@ -179,6 +180,8 @@ pub mod pallet { MarketplaceLabelUpdated([u8;32]), /// The selected marketplace has been removed. [market_id] MarketplaceRemoved([u8;32]), + /// Offer stored. [collection_id, item_id] + OfferStored([u8;32], [u8;32]), } // Errors inform users that something went wrong. @@ -230,6 +233,14 @@ pub mod pallet { ApplicationStatusStillPending, /// The application has already been approved, application status is approved ApplicationHasAlreadyBeenApproved, + /// Collection not found + CollectionNotFound, + /// User who calls the function is not the owner of the collection + NotOwner, + /// Offer already exists + OfferAlreadyExists, + /// Price already exists + PriceAlreadyExists } #[pallet::call] @@ -454,13 +465,13 @@ pub mod pallet { } - // #[transactional] - // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - // pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: OfferType, item_id: Vec, price:) -> DispatchResult { - // let who = ensure_signed(origin)?; + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: u128,) -> DispatchResult { + let who = ensure_signed(origin)?; - // Self::do_enlist_offer(who, marketplace_id, offer_id, offer_type, offer_data) - // } + Self::do_enlist_offer(who, marketplace_id, collection_id, item_id, offer_type, price) + } /// Kill all the stored data. From 988166e2222147d01c50f345f30402161091e972 Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 21:50:47 -0500 Subject: [PATCH 021/103] add helper functions to enlist offers --- pallets/gated-marketplace/src/functions.rs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 4afa6ffa..6677e04b 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,6 +4,8 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; +use frame_support::traits::UnixTime; +//use frame_support::traits::tokens::nonfungibles::Inspect; impl Pallet { @@ -150,6 +152,47 @@ impl Pallet { Ok(()) } + pub fn do_enlist_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: u128,) -> DispatchResult { + //ensure the origin is owner or admin + Self::can_enroll(authority.clone(), marketplace_id)?; + //ensure the collection exists + if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { + ensure!(a == authority, Error::::NotOwner); + } else { + Err(Error::::CollectionNotFound)?; + } + //create offer_id + let offer_iid = (marketplace_id, authority.clone(), collection_id).using_encoded(blake2_256); + + + // create a timestamp + let time: u64 = T::TimeProvider::now().as_secs(); + //create offer strcuture + let offer_data = OfferData:: { + offer_id: offer_iid, + marketplace_id: marketplace_id, + creator: authority.clone(), + price: price, + creation_date: time, + expiration_date: 94635434, + status: OfferStatus::Open, + offer_type: offer_type, + }; + + //insert offer in offer list + //validate offer already exists + ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); + >::insert(collection_id, item_id, offer_data); + + //insert price item + //validate price already exists + ensure!(!>::contains_key(collection_id, item_id), Error::::PriceAlreadyExists); + >::insert(collection_id, item_id, price); + + + Ok(()) + } + From 9a8b754f8a3dce9e138cf543c9804ce42bf62e3c Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 1 Aug 2022 21:50:58 -0500 Subject: [PATCH 022/103] add pallet timestamp --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 9655147f..603fe79d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4032,6 +4032,7 @@ dependencies = [ "log", "pallet-balances", "pallet-fruniques", + "pallet-timestamp", "pallet-uniques", "parity-scale-codec", "scale-info", From 9dee9d23e8637841193a6bb570a80965b761e06a Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 09:05:53 -0500 Subject: [PATCH 023/103] delete UnixTime import, add pallet timestamp --- pallets/gated-marketplace/src/functions.rs | 54 +++++++++++++++------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 6677e04b..0e357c8b 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,8 +4,6 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -use frame_support::traits::UnixTime; -//use frame_support::traits::tokens::nonfungibles::Inspect; impl Pallet { @@ -161,33 +159,45 @@ impl Pallet { } else { Err(Error::::CollectionNotFound)?; } + + + // create a timestamp + //let time: u64 = T::TimeProvider::now().as_secs(); + let timestamp: ::Moment = >::get(); + let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); + // add 7 days to the timestamp + let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); + //create offer_id - let offer_iid = (marketplace_id, authority.clone(), collection_id).using_encoded(blake2_256); + let offer_iid = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); - // create a timestamp - let time: u64 = T::TimeProvider::now().as_secs(); //create offer strcuture - let offer_data = OfferData:: { + let _offer_data = OfferData:: { offer_id: offer_iid, marketplace_id: marketplace_id, creator: authority.clone(), price: price, - creation_date: time, - expiration_date: 94635434, + creation_date: timestamp2, + expiration_date: timestamp3, status: OfferStatus::Open, offer_type: offer_type, }; - - //insert offer in offer list - //validate offer already exists - ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); - >::insert(collection_id, item_id, offer_data); - //insert price item - //validate price already exists - ensure!(!>::contains_key(collection_id, item_id), Error::::PriceAlreadyExists); - >::insert(collection_id, item_id, price); + //insert offer in offer_id + //validate offer already exists + ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); + >::insert(collection_id, item_id, offer_iid); + + //insert offer in offer_data + //validate offer_info already exists + ensure!(!>::contains_key(offer_iid, marketplace_id), Error::::OfferAlreadyExists); + >::insert(offer_iid, marketplace_id, _offer_data.clone()); + + //insert offer in offer_info + //validate offer_info already exists + ensure!(!>::contains_key(offer_iid), Error::::OfferAlreadyExists); + >::insert(offer_iid, _offer_data); Ok(()) @@ -430,4 +440,14 @@ impl Pallet { Ok(()) } + fn convert_moment_to_u64_in_milliseconds(date: T::Moment) -> Result { + let date_as_u64_millis; + if let Some(_date_as_u64) = TryInto::::try_into(date).ok() { + date_as_u64_millis = _date_as_u64; + } else { + return Err(DispatchError::Other("Unable to convert Moment to i64 for date")); + } + return Ok(date_as_u64_millis); + } + } \ No newline at end of file From 0927166d17b9b58ded715c8749b278901a9a4a25 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 09:06:30 -0500 Subject: [PATCH 024/103] delete UnixTime import, add pallet timestamp, modify storage maps --- pallets/gated-marketplace/src/lib.rs | 36 +++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 4cbe1c42..8ca21a8c 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -20,12 +20,11 @@ pub mod pallet { use frame_system::pallet_prelude::*; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; - use frame_support::traits::UnixTime; #[pallet::config] - pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config{ + pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config + pallet_timestamp::Config{ type Event: From> + IsType<::Event>; - type TimeProvider: UnixTime; + //type TimeProvider: UnixTime; type RemoveOrigin: EnsureOrigin; #[pallet::constant] @@ -46,6 +45,8 @@ pub mod pallet { type MaxFiles: Get; #[pallet::constant] type MaxApplicationsPerCustodian: Get; + #[pallet::constant] + type MaxMarketsPerOffer: Get; } #[pallet::pallet] @@ -137,29 +138,42 @@ pub mod pallet { #[pallet::storage] - #[pallet::getter(fn prices)] - pub(super) type Prices = StorageDoubleMap< + #[pallet::getter(fn offers_id)] + pub(super) type OffersId = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, //collection_id Blake2_128Concat, - T::ItemId, //item_id - u128, // price + T::ItemId, // item_id + [u8;32], // offer_id OptionQuery >; #[pallet::storage] - #[pallet::getter(fn offers)] - pub(super) type Offers = StorageDoubleMap< + #[pallet::getter(fn offers_data)] + pub(super) type OffersData = StorageDoubleMap< _, Blake2_128Concat, - T::CollectionId, //collection_id + [u8;32], //offer_id Blake2_128Concat, - T::ItemId, //item_id + [u8;32], //marketplace_id + //BoundedVec<[u8;32], T::MaxMarketsPerOffer>, //marketplace_id OfferData, //offer data OptionQuery >; + #[pallet::storage] + #[pallet::getter(fn offers_info)] + pub(super) type OffersInfo = StorageMap< + _, + Identity, + [u8;32], //offer_id + OfferData, //offer info + OptionQuery + >; + + + From 384f4e6d35ca49244143a81ada14458894074a2c Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 09:07:07 -0500 Subject: [PATCH 025/103] add MaxMarketsPerOfferor constant --- pallets/gated-marketplace/src/mock.rs | 13 ++++++++++--- runtime/src/lib.rs | 4 +++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 62cb2a7b..5fd30566 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -68,7 +68,8 @@ parameter_types! { pub const MaxFeedbackLen: u32 = 256; pub const NameMaxLen: u32 = 100; pub const MaxFiles: u32 = 10; - pub const MaxApplicationsPerCustodian: u32 = 2; + pub const MaxApplicationsPerCustodian: u32 = 2; + pub const MaxMarketsPerOfferor: u32 = 10; } impl pallet_gated_marketplace::Config for Test { @@ -83,14 +84,13 @@ impl pallet_gated_marketplace::Config for Test { type NameMaxLen = NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; + type MaxMarketsPerOffer = MaxMarketsPerOfferor; } impl pallet_fruniques::Config for Test { type Event = Event; } - - parameter_types! { pub const ClassDeposit: u64 = 2; pub const InstanceDeposit: u64 = 1; @@ -144,4 +144,11 @@ impl pallet_balances::Config for Test { // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { frame_system::GenesisConfig::default().build_storage::().unwrap().into() +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = (); + type WeightInfo = (); } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b6fc4ae8..0aa579c9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -552,6 +552,7 @@ parameter_types! { pub const NameMaxLen: u32 = 100; pub const MaxFiles: u32 = 10; pub const MaxApplicationsPerCustodian: u32 = 10; + pub const MaxMarketsPerOffer: u32 = 10; } impl pallet_gated_marketplace::Config for Runtime { type Event = Event; @@ -568,7 +569,8 @@ impl pallet_gated_marketplace::Config for Runtime { type NameMaxLen= NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; - type TimeProvider = Timestamp; + type MaxMarketsPerOffer = MaxMarketsPerOffer; + //type TimeProvider = Timestamp; } parameter_types! { From 738bc9ae7948983c15a4ee307a2c17b7f4cb2142 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 10:45:21 -0500 Subject: [PATCH 026/103] move offer_id outisde struct --- pallets/gated-marketplace/src/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 8ea08fda..7f4e52a5 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -94,7 +94,6 @@ pub enum OfferType{ #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct OfferData{ - pub offer_id: [u8;32], pub marketplace_id: [u8;32], pub creator: T::AccountId, pub price: u128, From 17296f842a27d1f51c7cbe90acf307cbb762acfb Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 10:50:02 -0500 Subject: [PATCH 027/103] delete unused storage map --- pallets/gated-marketplace/src/lib.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 8ca21a8c..48b7a574 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -25,8 +25,7 @@ pub mod pallet { pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config + pallet_timestamp::Config{ type Event: From> + IsType<::Event>; //type TimeProvider: UnixTime; - type RemoveOrigin: EnsureOrigin; - + type RemoveOrigin: EnsureOrigin; #[pallet::constant] type MaxAuthsPerMarket: Get; #[pallet::constant] @@ -147,7 +146,7 @@ pub mod pallet { T::ItemId, // item_id [u8;32], // offer_id OptionQuery ->; + >; #[pallet::storage] #[pallet::getter(fn offers_data)] @@ -162,15 +161,6 @@ pub mod pallet { OptionQuery >; - #[pallet::storage] - #[pallet::getter(fn offers_info)] - pub(super) type OffersInfo = StorageMap< - _, - Identity, - [u8;32], //offer_id - OfferData, //offer info - OptionQuery - >; From aff5719d92439ff673831e117c1b41cfbc5d13dd Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 10:50:16 -0500 Subject: [PATCH 028/103] delete unused storage map --- pallets/gated-marketplace/src/functions.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 0e357c8b..69a5c86e 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -160,7 +160,7 @@ impl Pallet { Err(Error::::CollectionNotFound)?; } - + //TODO: use a helper function to handle timestamping // create a timestamp //let time: u64 = T::TimeProvider::now().as_secs(); let timestamp: ::Moment = >::get(); @@ -172,9 +172,9 @@ impl Pallet { let offer_iid = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); - //create offer strcuture + //create offer structure let _offer_data = OfferData:: { - offer_id: offer_iid, + //offer_id: offer_iid, marketplace_id: marketplace_id, creator: authority.clone(), price: price, @@ -194,11 +194,6 @@ impl Pallet { ensure!(!>::contains_key(offer_iid, marketplace_id), Error::::OfferAlreadyExists); >::insert(offer_iid, marketplace_id, _offer_data.clone()); - //insert offer in offer_info - //validate offer_info already exists - ensure!(!>::contains_key(offer_iid), Error::::OfferAlreadyExists); - >::insert(offer_iid, _offer_data); - Ok(()) } From 83fd2e54f3d8fcd2f11ad9cc890d848f424dfb9b Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 2 Aug 2022 13:57:58 -0500 Subject: [PATCH 029/103] implements and adds remove_role_from_user function to gated_marketplace --- pallets/gated-marketplace/src/functions.rs | 3 +- pallets/rbac/src/functions.rs | 43 ++++++++++++++++++++++ pallets/rbac/src/lib.rs | 10 ++++- pallets/rbac/src/types.rs | 5 ++- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 163c4bf2..b8e89959 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -261,7 +261,8 @@ impl Pallet { accounts.remove(author_index); Ok(()) }).map_err(|_:Error::| Error::::UserNotFound)?; - + T::Rbac::remove_role_from_user(account, Self::get_pallet_id(), + &marketplace_id, author_type.to_vec().using_encoded(blake2_256))?; Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 29f6d604..730fc298 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -18,6 +18,27 @@ impl RoleBasedAccessControl for Pallet{ }) } + fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult{ + // WIP + // remove on scopes + >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ + let scopes = scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; + let s_pos = scopes.iter().position(|&s| s==scope_id).ok_or(Error::::ScopeNotFound)?; + scopes.remove(s_pos); + if scopes.is_empty(){ + scopes_option.clone_from(&None); + } + Ok(()) + })?; + // remove on users by scope + let mut scope_users = >::iter_prefix((pallet_id, scope_id)).map( + |(_role, users)|users).flatten().collect::>(); + // remove duplicate users + scope_users.sort(); scope_users.dedup(); + // remove scope users + + Ok(()) + } /// Inserts roles and links them to the pallet fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>{ @@ -129,6 +150,28 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult{ + >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ + let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; + let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; + user_roles.remove(r_pos); + if user_roles.is_empty(){ + user_roles_option.clone_from(&None) + } + Ok(()) + })?; + >::try_mutate_exists::<_,(),DispatchError,_>((pallet_id, scope_id, role_id), |auth_users_option|{ + let auth_users = auth_users_option.as_mut().ok_or(Error::::RoleHasNoUsers)?; + let u_pos = auth_users.iter().position(|u| *u==user).ok_or(Error::::UserNotFound)?; + auth_users.remove(u_pos); + if auth_users.is_empty(){ + auth_users_option.clone_from(&None); + } + Ok(()) + })?; + Ok(()) + } + /*---- Helper functions ----*/ /// Authorize by role, not permissions diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 3faa0187..496cd63f 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -155,10 +155,12 @@ pub mod pallet { ScopeNotFound, /// The scope is already linked with the pallet ScopeAlreadyExists, - /// The specified role doesn't exists + /// The specified role doesn't exist RoleNotFound, - /// The permission doesn't exists in the pallet + /// The permission doesn't exist in the pallet PermissionNotFound, + /// The specified user hasn't been asigned to this scope + UserNotFound, /// The role is already linked in the pallet DuplicateRole, /// The permission is already linked to that role in that scope @@ -167,6 +169,10 @@ pub mod pallet { UserAlreadyHasRole, /// The role exists but it hasn't been linked to the pallet RoleNotLinkedToPallet, + /// The user doesn't have any roles in this pallet + UserHasNoRoles, + /// The role doesn't have any users assigned to it + RoleHasNoUsers, /// The pallet has too many scopes ExceedMaxScopesPerPallet, /// The pallet cannot have more roles diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index bb9d5a5f..05209ba2 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -15,6 +15,8 @@ pub trait RoleBasedAccessControl{ type PermissionMaxLen: Get; // scopes fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; + // scope removal + fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; // roles creation and setting fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>; @@ -22,7 +24,8 @@ pub trait RoleBasedAccessControl{ fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; - // TODO: role removal + // role removal + fn remove_role_from_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; // permissions fn create_and_set_permissions(pallet_id: u64, role: [u8;32], permissions: Vec>)-> Result, DispatchError>; From 35a6443e9642f446f96834bce037183afc36f6e0 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 16:49:01 -0500 Subject: [PATCH 030/103] add currency trait --- pallets/gated-marketplace/src/functions.rs | 8 +++++--- pallets/gated-marketplace/src/lib.rs | 19 ++++++++++++++++--- pallets/gated-marketplace/src/mock.rs | 1 + pallets/gated-marketplace/src/types.rs | 5 +++-- runtime/src/lib.rs | 7 +++++-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 69a5c86e..5ef742bd 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,6 +4,7 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; +use frame_support::traits::Currency; impl Pallet { @@ -150,9 +151,10 @@ impl Pallet { Ok(()) } - pub fn do_enlist_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: u128,) -> DispatchResult { + pub fn do_enlist_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { //ensure the origin is owner or admin Self::can_enroll(authority.clone(), marketplace_id)?; + //ensure the collection exists if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a == authority, Error::::NotOwner); @@ -171,6 +173,7 @@ impl Pallet { //create offer_id let offer_iid = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); + let _total = T::LocalCurrency::total_balance(&authority); //create offer structure let _offer_data = OfferData:: { @@ -194,13 +197,12 @@ impl Pallet { ensure!(!>::contains_key(offer_iid, marketplace_id), Error::::OfferAlreadyExists); >::insert(offer_iid, marketplace_id, _offer_data.clone()); - + Self::deposit_event(Event::OfferStored(collection_id, item_id)); Ok(()) } - /*---- Helper functions ----*/ pub fn set_up_application( diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 48b7a574..8529663d 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -14,17 +14,24 @@ mod benchmarking; mod functions; mod types; + + #[frame_support::pallet] pub mod pallet { use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; use frame_system::pallet_prelude::*; + use frame_support::traits::Currency; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; + //use frame_support::traits::tokens::Balance; + //use std::fmt::Debug; #[pallet::config] pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config + pallet_timestamp::Config{ type Event: From> + IsType<::Event>; - //type TimeProvider: UnixTime; + type LocalCurrency: Currency; + //type Balance: Balance + MaybeSerializeDeserialize + Debug + MaxEncodedLen; + type RemoveOrigin: EnsureOrigin; #[pallet::constant] type MaxAuthsPerMarket: Get; @@ -185,7 +192,7 @@ pub mod pallet { /// The selected marketplace has been removed. [market_id] MarketplaceRemoved([u8;32]), /// Offer stored. [collection_id, item_id] - OfferStored([u8;32], [u8;32]), + OfferStored(T::CollectionId, T::ItemId), } // Errors inform users that something went wrong. @@ -471,13 +478,19 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: u128,) -> DispatchResult { + pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_enlist_offer(who, marketplace_id, collection_id, item_id, offer_type, price) } + //TODO: Add extrinsic to take an offer. + + + //TODO: Add CRUD operations for the offers + //TODO: Add extrinsic to duplicate offers in other marketplace. + /// Kill all the stored data. /// /// This function is used to kill ALL the stored data. diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 5fd30566..d9a91316 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -85,6 +85,7 @@ impl pallet_gated_marketplace::Config for Test { type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxMarketsPerOffer = MaxMarketsPerOfferor; + type LocalCurrency = Balances; } impl pallet_fruniques::Config for Test { diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 7f4e52a5..b46867a0 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -2,7 +2,9 @@ use super::*; use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; +use frame_support::traits::Currency; +pub type BalanceOf = <::LocalCurrency as Currency<::AccountId>>::Balance; #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -96,10 +98,9 @@ pub enum OfferType{ pub struct OfferData{ pub marketplace_id: [u8;32], pub creator: T::AccountId, - pub price: u128, + pub price: BalanceOf, pub status: OfferStatus, pub creation_date: u64, pub expiration_date: u64, pub offer_type: OfferType, } - diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0aa579c9..c38f01fb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -255,7 +255,7 @@ impl pallet_balances::Config for Runtime { type MaxReserves = (); type ReserveIdentifier = [u8; 8]; /// The type for recording an account's balance. - type Balance = Balance; + type Balance = u128; /// The ubiquitous event type. type Event = Event; type DustRemoval = (); @@ -570,7 +570,10 @@ impl pallet_gated_marketplace::Config for Runtime { type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxMarketsPerOffer = MaxMarketsPerOffer; - //type TimeProvider = Timestamp; + //type Balance = u128; + type LocalCurrency = Balances; + + } parameter_types! { From db12fb1914ec6a682030f6f61d5405015b55aa9d Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 16:54:36 -0500 Subject: [PATCH 031/103] change enum names --- pallets/gated-marketplace/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index b46867a0..98df5d78 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -88,8 +88,8 @@ impl Default for OfferStatus{ #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum OfferType{ - Sale, - Buy, + SellOrder, + BuyOrder, } #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,)] From 6133dbf5121b604caf22cd18c88f5fff94f165d9 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:42:08 -0500 Subject: [PATCH 032/103] added a new enum NotFound in case the storage source is corrupted --- pallets/gated-marketplace/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 98df5d78..cbf87bb4 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -78,6 +78,7 @@ pub enum OfferStatus{ Open, Freezed, Closed, + NotFound, } impl Default for OfferStatus{ From 2c7a3fe3f272566bfdc2496cffe370d8a7c0c42c Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:42:45 -0500 Subject: [PATCH 033/103] add extrinsic take_offer --- pallets/gated-marketplace/src/lib.rs | 46 ++++++++++++++++++---------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 8529663d..f2132961 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -146,26 +146,26 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn offers_id)] pub(super) type OffersId = StorageDoubleMap< - _, - Blake2_128Concat, - T::CollectionId, //collection_id - Blake2_128Concat, - T::ItemId, // item_id - [u8;32], // offer_id - OptionQuery + _, + Blake2_128Concat, + T::CollectionId, //collection_id + Blake2_128Concat, + T::ItemId, // item_id + [u8;32], // offer_id + OptionQuery >; #[pallet::storage] #[pallet::getter(fn offers_data)] pub(super) type OffersData = StorageDoubleMap< - _, - Blake2_128Concat, - [u8;32], //offer_id - Blake2_128Concat, - [u8;32], //marketplace_id - //BoundedVec<[u8;32], T::MaxMarketsPerOffer>, //marketplace_id - OfferData, //offer data - OptionQuery + _, + Blake2_128Concat, + [u8;32], //offer_id + Blake2_128Concat, + [u8;32], //marketplace_id + //BoundedVec<[u8;32], T::MaxMarketsPerOffer>, //marketplace_id + OfferData, //offer data + OptionQuery >; @@ -250,8 +250,12 @@ pub mod pallet { NotOwner, /// Offer already exists OfferAlreadyExists, - /// Price already exists - PriceAlreadyExists + /// Offer not found + OfferNotFound, + /// Offer status is freezed, user cannot take the current offer + OfferIsFreezed, + + } #[pallet::call] @@ -486,6 +490,14 @@ pub mod pallet { //TODO: Add extrinsic to take an offer. + + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn take_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_take_offer(who, offer_id, marketplace_id, collection_id, item_id) + } //TODO: Add CRUD operations for the offers From 6d455db1974cb4047b2dc61935c851c5db040ce5 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:43:03 -0500 Subject: [PATCH 034/103] add helper functions for extrinsic take_offer --- pallets/gated-marketplace/src/functions.rs | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 5ef742bd..23ad8f1e 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -201,6 +201,36 @@ impl Pallet { Ok(()) } + pub fn do_take_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + //ensure the origin is owner or admin + Self::can_enroll(authority.clone(), marketplace_id)?; + //ensure the collection exists + if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { + ensure!(a == authority, Error::::NotOwner); + } else { + Err(Error::::CollectionNotFound)?; + } + //ensure the selected item has an offer id + ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed + //TODO: change the offer status to freezed use unique's function freeze + + //ensure the offer is open + ensure!(>::get(offer_iid, marketplace_id).status == OfferStatus::Open, Error::::OfferNotOpen); + //get offer_data + let offer_data = >::get(offer_iid, marketplace_id); + //ensure the offer is not expired + ensure!(offer_data.expiration_date > Self::convert_u64_to_moment(Self::convert_moment_to_u64_in_milliseconds(>::get()).unwrap_or(0)).unwrap_or(0), Error::::OfferExpired); + //ensure the offer is not already taken + ensure!(offer_data.status == OfferStatus::Open, Error::::OfferAlreadyTaken); + //update offer status + >::mutate(offer_iid, marketplace_id, |offer_data| { + offer_data.status = OfferStatus::Taken; + }); + Self::deposit_event(Event::OfferTaken(collection_id, item_id)); + Ok(()) + } + /*---- Helper functions ----*/ @@ -447,4 +477,17 @@ impl Pallet { return Ok(date_as_u64_millis); } + fn get_offer_status(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferStatus{ + //we already know that the offer exists, so we don't need to check it. + //we have added a NotFound status in case the storage source is corrupted. + if let Some(offer) = >::get(offer_id, marketplace_id) { + return offer.status; + } else { + return OfferStatus::NotFound; + } + } + + + + } \ No newline at end of file From 8b069056fc1f66602d70551e0efeb9fd2a18c934 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 2 Aug 2022 17:47:40 -0500 Subject: [PATCH 035/103] replaces marketplace auth validations with role validations --- pallets/gated-marketplace/src/functions.rs | 59 ++++++++++++---------- pallets/gated-marketplace/src/types.rs | 25 +++++++-- pallets/rbac/src/functions.rs | 15 +++--- pallets/rbac/src/lib.rs | 4 +- pallets/rbac/src/types.rs | 1 + 5 files changed, 64 insertions(+), 40 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index b8e89959..70c5282d 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -38,12 +38,9 @@ impl Pallet { Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); - + T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; - T::Rbac::assign_role_to_user(owner.clone(), Self::get_pallet_id(), - &marketplace_id, MarketplaceAuthority::Owner.to_vec().using_encoded(blake2_256))?; - T::Rbac::assign_role_to_user(admin.clone(), Self::get_pallet_id(), - &marketplace_id, MarketplaceAuthority::Admin.to_vec().using_encoded(blake2_256))?; + Self::deposit_event(Event::MarketplaceStored(owner, admin, marketplace_id)); Ok(()) } @@ -106,20 +103,16 @@ impl Pallet { Self::can_enroll(authority, marketplace_id)?; //ensure the account is not already an authority - ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); - + // handled by T::Rbac::assign_role_to_user + //ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); match authority_type{ MarketplaceAuthority::Owner => { ensure!(!Self::owner_exist(marketplace_id), Error::::OnlyOneOwnerIsAllowed); Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; - T::Rbac::assign_role_to_user(account.clone(), Self::get_pallet_id(), - &marketplace_id, authority_type.to_vec().using_encoded(blake2_256))?; }, _ =>{ Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; - T::Rbac::assign_role_to_user(account.clone(), Self::get_pallet_id(), - &marketplace_id, authority_type.to_vec().using_encoded(blake2_256))?; } } @@ -133,7 +126,8 @@ impl Pallet { Self::can_enroll(authority.clone(), marketplace_id)?; //ensure the account has the selected authority before to try to remove - ensure!(Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AuthorityNotFoundForUser); + // T::Rbac handles the if role doesnt hasnt been asigned to the user + //ensure!(Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AuthorityNotFoundForUser); match authority_type{ MarketplaceAuthority::Owner => { @@ -218,8 +212,12 @@ impl Pallet { }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; >::try_mutate(marketplace_id, role, | accounts|{ - accounts.try_push(authority) + accounts.try_push(authority.clone()) }).map_err(|_| Error::::ExceedMaxMarketsPerAuth)?; + + T::Rbac::assign_role_to_user(authority.clone(), Self::get_pallet_id(), + &marketplace_id, role.get_id())?; + Ok(()) } @@ -227,6 +225,7 @@ impl Pallet { >::try_mutate(marketplace_id, status,|applicants|{ applicants.try_push(applicant) }).map_err(|_| Error::::ExceedMaxApplicants)?; + // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? Ok(()) } @@ -234,6 +233,7 @@ impl Pallet { >::try_mutate(custodian, marketplace_id, | applications |{ applications.try_push(applicant) }).map_err(|_| Error::::ExceedMaxApplicationsPerCustodian)?; + // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? Ok(()) } @@ -242,6 +242,7 @@ impl Pallet { let applicant_index = applicants.iter().position(|a| *a==applicant.clone()) .ok_or(Error::::ApplicantNotFound)?; applicants.remove(applicant_index); + // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? Ok(()) }) } @@ -262,7 +263,7 @@ impl Pallet { Ok(()) }).map_err(|_:Error::| Error::::UserNotFound)?; T::Rbac::remove_role_from_user(account, Self::get_pallet_id(), - &marketplace_id, author_type.to_vec().using_encoded(blake2_256))?; + &marketplace_id, author_type.get_id())?; Ok(()) } @@ -298,8 +299,8 @@ impl Pallet { // role.eq(&MarketplaceAuthority::Owner) || role.eq(&MarketplaceAuthority::Admin) // }).ok_or(Error::::CannotEnroll)?; let auths = [ - MarketplaceAuthority::Owner.to_vec().using_encoded(blake2_256), - MarketplaceAuthority::Admin.to_vec().using_encoded(blake2_256) + MarketplaceAuthority::Owner.get_id(), + MarketplaceAuthority::Admin.get_id() ].to_vec(); //TODO: Test if the auth mechanism works T::Rbac::has_role(authority.clone(),Self::get_pallet_id(), &marketplace_id,auths) @@ -309,12 +310,14 @@ impl Pallet { ///Lets us know if the selected user is an admin. /// It returns true if the user is an admin, false otherwise. fn is_admin(account: T::AccountId, marketplace_id: [u8;32]) -> bool{ - let roles = match >::try_get(account, marketplace_id){ - Ok(roles) => roles, - Err(_) => return false, - }; - - roles.iter().any(|&authority_type| authority_type == MarketplaceAuthority::Admin) + // let roles = match >::try_get(account, marketplace_id){ + // Ok(roles) => roles, + // Err(_) => return false, + // }; + + // roles.iter().any(|&authority_type| authority_type == MarketplaceAuthority::Admin) + T::Rbac::has_role(account, Self::get_pallet_id(), + &marketplace_id, [MarketplaceAuthority::Admin.get_id()].to_vec()).is_ok() } @@ -332,12 +335,14 @@ impl Pallet { /// Let us know if there's an owner for the selected marketplace. /// It returns true if there's an owner, false otherwise fn owner_exist(marketplace_id: [u8;32]) -> bool { - let owners = match >::try_get( marketplace_id, MarketplaceAuthority::Owner){ - Ok(owners) => owners, - Err(_) => return false, - }; + // let owners = match >::try_get( marketplace_id, MarketplaceAuthority::Owner){ + // Ok(owners) => owners, + // Err(_) => return false, + // }; - owners.len() == 1 + //owners.len() == 1 + T::Rbac::get_role_users_len(Self::get_pallet_id(), + &marketplace_id, &MarketplaceAuthority::Owner.get_id()) == 1 } /// Let us update the marketplace's label. diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 5560c1c1..705d70d3 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -3,6 +3,7 @@ use super::*; use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; +use frame_support::sp_io::hashing::blake2_256; #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -25,23 +26,37 @@ pub enum MarketplaceAuthority{ Admin, Appraiser, RedemptionSpecialist, + Applicant, + Participant, } impl Default for MarketplaceAuthority{ fn default() -> Self { - MarketplaceAuthority::Appraiser + MarketplaceAuthority::Applicant } } impl MarketplaceAuthority{ pub fn to_vec(&self) -> Vec{ match self{ - MarketplaceAuthority::Owner => "Owner".as_bytes().to_vec(), - MarketplaceAuthority::Admin => "Admin".as_bytes().to_vec(), - MarketplaceAuthority::Appraiser => "Appraiser".as_bytes().to_vec(), - MarketplaceAuthority::RedemptionSpecialist => "Redemption_specialist".as_bytes().to_vec(), + Self::Owner => "Owner".as_bytes().to_vec(), + Self::Admin => "Admin".as_bytes().to_vec(), + Self::Appraiser => "Appraiser".as_bytes().to_vec(), + Self::RedemptionSpecialist => "Redemption_specialist".as_bytes().to_vec(), + Self::Applicant => "Applicant".as_bytes().to_vec(), + Self::Participant => "Participant".as_bytes().to_vec(), } } + + pub fn get_id(&self) -> [u8;32]{ + self.to_vec().using_encoded(blake2_256) + } + + pub fn enum_to_vec() -> Vec>{ + use crate::types::MarketplaceAuthority::*; + [Owner.to_vec(), Admin.to_vec(), Appraiser.to_vec(), RedemptionSpecialist.to_vec(), + Applicant.to_vec(), Participant.to_vec()].to_vec() + } } #[derive(CloneNoBound,Encode, Decode, Eq, PartialEq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 730fc298..e1a0fbab 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -6,7 +6,7 @@ use frame_support::sp_std::borrow::ToOwned; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -// TODO: make vec to manage pallet errors here + impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ fn create_scope(pallet_id: u64, scope_id: [u8;32])-> DispatchResult{ @@ -85,7 +85,6 @@ impl RoleBasedAccessControl for Pallet{ fn create_and_set_permissions(pallet_id: u64, role_id: [u8;32], permissions: Vec>)-> Result, DispatchError> { - // TODO: Test this functionality let mut permission_ids = Vec::<[u8;32]>::new(); for permision in permissions{ permission_ids.push( Self::create_permission(pallet_id, permision.to_owned())? ); @@ -138,7 +137,7 @@ impl RoleBasedAccessControl for Pallet{ Self::scope_exists(pallet_id, scope_id)?; Self::is_role_linked_to_pallet(pallet_id, &role_id)?; - >::try_mutate((&user, pallet_id, scope_id), | roles |{ + >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::DuplicateRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) })?; @@ -151,7 +150,7 @@ impl RoleBasedAccessControl for Pallet{ } fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult{ - >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ + >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; user_roles.remove(r_pos); @@ -184,7 +183,7 @@ impl RoleBasedAccessControl for Pallet{ // TODO: test if a role that doesnt exists cause any errors let users = >::get( (pallet_id, scope_id, role_id) ); ensure!(users.contains(&user), Error::::NotAuthorized); - let roles = >::get((user, pallet_id, scope_id)); + let roles = >::get((user, pallet_id, scope_id)); // Not likely to happen but just in case: ensure!(roles.contains(&role_id), Error::::NotAuthorized ); Ok(()) @@ -193,7 +192,7 @@ impl RoleBasedAccessControl for Pallet{ fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult { Self::scope_exists(pallet_id, scope_id)?; - let user_roles = >::get((user, pallet_id, scope_id)); + let user_roles = >::get((user, pallet_id, scope_id)); ensure!( user_roles.iter().any(|r| role_ids.contains(r) ), Error::::NotAuthorized @@ -213,6 +212,10 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize{ + >::get((pallet_id, scope_id, role_id)).len() + } + fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>{ let role_id = match id_or_role{ IdOrString::Id(id)=>id, diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 496cd63f..0aa0ec2a 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -106,8 +106,8 @@ pub mod pallet { >; #[pallet::storage] - #[pallet::getter(fn users)] - pub(super) type Users = StorageNMap< + #[pallet::getter(fn roles_by_user)] + pub(super) type RolesByUser = StorageNMap< _, ( NMapKey,// user diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 05209ba2..f7a7725a 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -37,6 +37,7 @@ pub trait RoleBasedAccessControl{ fn has_role(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult; fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; + fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize; fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>; fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; fn has_unique_elements(vec: Vec) -> bool; From 01764cf621a074d3d08390632e6deac16a8bf357 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 2 Aug 2022 17:55:43 -0500 Subject: [PATCH 036/103] Changes marketplace auths enum name --- pallets/gated-marketplace/src/functions.rs | 33 ++++----- pallets/gated-marketplace/src/lib.rs | 12 +-- pallets/gated-marketplace/src/tests.rs | 86 +++++++++++----------- pallets/gated-marketplace/src/types.rs | 10 +-- pallets/rbac/src/functions.rs | 2 +- 5 files changed, 71 insertions(+), 72 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 70c5282d..f7ccae21 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,5 +1,4 @@ use core::default; -use core::ops::Bound; use super::*; use frame_support::pallet_prelude::*; @@ -16,8 +15,8 @@ impl Pallet { let pallet_id = Self::get_pallet_id(); let mut super_roles = Vec::>::new(); let mut super_permissions = Vec::>::new(); - super_roles.push(MarketplaceAuthority::Owner.to_vec()); - super_roles.push(MarketplaceAuthority::Admin.to_vec()); + super_roles.push(MarketplaceRole::Owner.to_vec()); + super_roles.push(MarketplaceRole::Admin.to_vec()); super_permissions.push("enroll".as_bytes().to_vec()); super_permissions.push("add_authority".as_bytes().to_vec()); super_permissions.push("remove_authority".as_bytes().to_vec()); @@ -35,8 +34,8 @@ impl Pallet { // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth - Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; - Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; + Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; + Self::insert_in_auth_market_lists(admin.clone(), MarketplaceRole::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; @@ -97,7 +96,7 @@ impl Pallet { } - pub fn do_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceAuthority, marketplace_id: [u8;32], ) -> DispatchResult { + pub fn do_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32], ) -> DispatchResult { //ensure the origin is owner or admin //TODO: implement copy trait for MarketplaceAuthority & T::AccountId Self::can_enroll(authority, marketplace_id)?; @@ -106,7 +105,7 @@ impl Pallet { // handled by T::Rbac::assign_role_to_user //ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); match authority_type{ - MarketplaceAuthority::Owner => { + MarketplaceRole::Owner => { ensure!(!Self::owner_exist(marketplace_id), Error::::OnlyOneOwnerIsAllowed); Self::insert_in_auth_market_lists(account.clone(), authority_type, marketplace_id)?; @@ -121,7 +120,7 @@ impl Pallet { } - pub fn do_remove_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceAuthority, marketplace_id: [u8;32], ) -> DispatchResult { + pub fn do_remove_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32], ) -> DispatchResult { //ensure the origin is owner or admin Self::can_enroll(authority.clone(), marketplace_id)?; @@ -130,11 +129,11 @@ impl Pallet { //ensure!(Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AuthorityNotFoundForUser); match authority_type{ - MarketplaceAuthority::Owner => { + MarketplaceRole::Owner => { ensure!(Self::owner_exist(marketplace_id), Error::::OwnerNotFound); Err(Error::::CantRemoveOwner)?; }, - MarketplaceAuthority::Admin => { + MarketplaceRole::Admin => { // Admins can not delete themselves ensure!(authority != account, Error::::AdminCannotRemoveItself); @@ -205,7 +204,7 @@ impl Pallet { (custodian, BoundedVec::::try_from(f).unwrap_or_default() ) } - fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceAuthority, marketplace_id: [u8;32])->DispatchResult{ + fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceRole, marketplace_id: [u8;32])->DispatchResult{ >::try_mutate(authority.clone(), marketplace_id, |account_auths|{ account_auths.try_push(role) @@ -248,7 +247,7 @@ impl Pallet { } - fn remove_from_market_lists(account: T::AccountId, author_type: MarketplaceAuthority , marketplace_id : [u8;32])->DispatchResult{ + fn remove_from_market_lists(account: T::AccountId, author_type: MarketplaceRole , marketplace_id : [u8;32])->DispatchResult{ >::try_mutate(account.clone(), marketplace_id, |account_auths|{ let author_index = account_auths.iter().position(|a| *a==author_type) .ok_or(Error::::UserNotFound)?; @@ -299,8 +298,8 @@ impl Pallet { // role.eq(&MarketplaceAuthority::Owner) || role.eq(&MarketplaceAuthority::Admin) // }).ok_or(Error::::CannotEnroll)?; let auths = [ - MarketplaceAuthority::Owner.get_id(), - MarketplaceAuthority::Admin.get_id() + MarketplaceRole::Owner.get_id(), + MarketplaceRole::Admin.get_id() ].to_vec(); //TODO: Test if the auth mechanism works T::Rbac::has_role(authority.clone(),Self::get_pallet_id(), &marketplace_id,auths) @@ -317,13 +316,13 @@ impl Pallet { // roles.iter().any(|&authority_type| authority_type == MarketplaceAuthority::Admin) T::Rbac::has_role(account, Self::get_pallet_id(), - &marketplace_id, [MarketplaceAuthority::Admin.get_id()].to_vec()).is_ok() + &marketplace_id, [MarketplaceRole::Admin.get_id()].to_vec()).is_ok() } /// Let us know if the selected account has the selected authority type. /// It returns true if the account has the authority type, false otherwise - fn does_exist_authority(account: T::AccountId, marketplace_id: [u8;32], authority_type: MarketplaceAuthority) -> bool{ + fn does_exist_authority(account: T::AccountId, marketplace_id: [u8;32], authority_type: MarketplaceRole) -> bool{ let roles = match >::try_get(account, marketplace_id){ Ok(roles) => roles, Err(_) => return false, @@ -342,7 +341,7 @@ impl Pallet { //owners.len() == 1 T::Rbac::get_role_users_len(Self::get_pallet_id(), - &marketplace_id, &MarketplaceAuthority::Owner.get_id()) == 1 + &marketplace_id, &MarketplaceRole::Owner.get_id()) == 1 } /// Let us update the marketplace's label. diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index f024ac2b..259fa0b8 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -82,7 +82,7 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; T::AccountId, // K1: Authority Blake2_128Concat, [u8;32], // K2: marketplace_id - BoundedVec, // scales with MarketplaceAuthority cardinality + BoundedVec, // scales with MarketplaceAuthority cardinality ValueQuery >; @@ -93,7 +93,7 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; Identity, [u8;32], //K1: marketplace_id Blake2_128Concat, - MarketplaceAuthority, //k2: authority + MarketplaceRole, //k2: authority BoundedVec, ValueQuery >; @@ -158,9 +158,9 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; /// An applicant was accepted or rejected on the marketplace. [AccountOrApplication, market_id, status] ApplicationProcessed(AccountOrApplication,[u8;32], ApplicationStatus), /// Add a new authority to the selected marketplace [account, authority] - AuthorityAdded(T::AccountId, MarketplaceAuthority), + AuthorityAdded(T::AccountId, MarketplaceRole), /// Remove the selected authority from the selected marketplace [account, authority] - AuthorityRemoved(T::AccountId, MarketplaceAuthority), + AuthorityRemoved(T::AccountId, MarketplaceRole), /// The label of the selected marketplace has been updated. [market_id] MarketplaceLabelUpdated([u8;32]), /// The selected marketplace has been removed. [market_id] @@ -372,7 +372,7 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; /// authority type, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn add_authority(origin: OriginFor, account: T::AccountId, authority_type: MarketplaceAuthority, marketplace_id: [u8;32]) -> DispatchResult { + pub fn add_authority(origin: OriginFor, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32]) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_authority(who, account, authority_type, marketplace_id) @@ -394,7 +394,7 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; /// If the user doesn't have the selected authority type, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn remove_authority(origin: OriginFor, account: T::AccountId, authority_type: MarketplaceAuthority, marketplace_id: [u8;32]) -> DispatchResult { + pub fn remove_authority(origin: OriginFor, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32]) -> DispatchResult { let who = ensure_signed(origin)?; //TOREVIEW: If we're allowing more than one role per user per marketplace, we should // check what role we want to remove instead of removing the user completely from diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index c80837b3..4ed3e719 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -301,8 +301,8 @@ fn add_authority_appraiser_works() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceAuthority::Appraiser]); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); }); } @@ -311,8 +311,8 @@ fn add_authority_admin_works() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceAuthority::Admin]); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); + assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Admin]); }); } @@ -321,8 +321,8 @@ fn add_authority_redenmption_specialist_works() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceAuthority::RedemptionSpecialist]); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); + assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); }); } @@ -331,7 +331,7 @@ fn add_authority_owner_shouldnt_work() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Owner, m_id), Error::::OnlyOneOwnerIsAllowed); + assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Owner, m_id), Error::::OnlyOneOwnerIsAllowed); }); } @@ -340,8 +340,8 @@ fn add_authority_cant_apply_twice_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id), Error::::AlreadyApplied); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id), Error::::AlreadyApplied); }); } @@ -353,8 +353,8 @@ fn remove_authority_appraiser_works() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); }); } @@ -364,8 +364,8 @@ fn remove_authority_admin_works() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); - assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); + assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); }); } @@ -375,8 +375,8 @@ fn remove_authority_redemption_specialist_work() { new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::RedemptionSpecialist, m_id)); - assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceAuthority::RedemptionSpecialist, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); + assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); }); } @@ -386,7 +386,7 @@ fn remove_authority_owner_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 1 , MarketplaceAuthority::Owner, m_id), Error::::CantRemoveOwner); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 1 , MarketplaceRole::Owner, m_id), Error::::CantRemoveOwner); }); } @@ -397,8 +397,8 @@ fn remove_authority_admin_by_admin_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 3, MarketplaceAuthority::Admin, m_id), Error::::AdminCannotRemoveItself); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 3, MarketplaceRole::Admin, m_id), Error::::AdminCannotRemoveItself); }); } @@ -407,8 +407,8 @@ fn remove_authority_user_tries_to_remove_non_existent_role_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id), Error::::AuthorityNotFoundForUser); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id), Error::::AuthorityNotFoundForUser); }); } @@ -417,9 +417,9 @@ fn remove_authority_user_is_not_admin_or_owner_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceAuthority::Admin, m_id)); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 4,MarketplaceAuthority::Appraiser, m_id), Error::::CannotEnroll); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::Admin, m_id)); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 4,MarketplaceRole::Appraiser, m_id), Error::::CannotEnroll); }); } @@ -428,8 +428,8 @@ fn remove_authority_only_owner_can_remove_admins_works(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); - assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceAuthority::Admin, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); + assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); }); } @@ -453,7 +453,7 @@ fn update_marketplace_user_without_permission_shouldnt_work(){ //user should be an admin or owner assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(3), m_id, create_label("my marketplace2")), Error::::CannotEnroll); }); } @@ -490,7 +490,7 @@ fn remove_marketplace_user_without_permission_shouldnt_work(){ new_test_ext().execute_with(|| { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(3), m_id), Error::::CannotEnroll); }); } @@ -513,13 +513,13 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works(){ let m_id = create_label("my marketplace").using_encoded(blake2_256); assert!(GatedMarketplace::marketplaces(m_id).is_some()); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceAuthority::RedemptionSpecialist, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![MarketplaceAuthority::Owner]); - assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![MarketplaceAuthority::Admin]); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceAuthority::Appraiser]); - assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![MarketplaceAuthority::RedemptionSpecialist]); + assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![MarketplaceRole::Owner]); + assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![MarketplaceRole::Admin]); + assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); + assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); @@ -538,20 +538,20 @@ fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works(){ let m_id = create_label("my marketplace").using_encoded(blake2_256); assert!(GatedMarketplace::marketplaces(m_id).is_some()); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceAuthority::Appraiser, m_id)); - assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceAuthority::RedemptionSpecialist, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Owner) == vec![1]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Admin) == vec![2]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Appraiser) == vec![3]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::RedemptionSpecialist) == vec![4]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![1]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![2]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![3]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![4]); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Owner) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Admin) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::Appraiser) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceAuthority::RedemptionSpecialist) == vec![]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![]); + assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![]); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); } diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 705d70d3..e20a9d97 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -21,7 +21,7 @@ pub enum AccountOrApplication{ } #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] -pub enum MarketplaceAuthority{ +pub enum MarketplaceRole{ Owner, Admin, Appraiser, @@ -30,13 +30,13 @@ pub enum MarketplaceAuthority{ Participant, } -impl Default for MarketplaceAuthority{ +impl Default for MarketplaceRole{ fn default() -> Self { - MarketplaceAuthority::Applicant + MarketplaceRole::Applicant } } -impl MarketplaceAuthority{ +impl MarketplaceRole{ pub fn to_vec(&self) -> Vec{ match self{ Self::Owner => "Owner".as_bytes().to_vec(), @@ -53,7 +53,7 @@ impl MarketplaceAuthority{ } pub fn enum_to_vec() -> Vec>{ - use crate::types::MarketplaceAuthority::*; + use crate::types::MarketplaceRole::*; [Owner.to_vec(), Admin.to_vec(), Appraiser.to_vec(), RedemptionSpecialist.to_vec(), Applicant.to_vec(), Participant.to_vec()].to_vec() } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index e1a0fbab..b7a2815c 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -35,7 +35,7 @@ impl RoleBasedAccessControl for Pallet{ |(_role, users)|users).flatten().collect::>(); // remove duplicate users scope_users.sort(); scope_users.dedup(); - // remove scope users + // TODO: remove scope users Ok(()) } From d0a76b37bdba4748b154a189269a57ff620e088c Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:56:00 -0500 Subject: [PATCH 037/103] add pallet::error OfferIsNotAvailable --- pallets/gated-marketplace/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index f2132961..36f57d85 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -254,7 +254,8 @@ pub mod pallet { OfferNotFound, /// Offer status is freezed, user cannot take the current offer OfferIsFreezed, - + /// Offer is not available at the moment + OfferIsNotAvailable, } From faebeb991663b6ce4407a964e7a27cf3323e691a Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:56:22 -0500 Subject: [PATCH 038/103] add todo comments --- pallets/gated-marketplace/src/functions.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 23ad8f1e..b7c670c9 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -214,9 +214,10 @@ impl Pallet { ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed //TODO: change the offer status to freezed use unique's function freeze + //we need to change the status in all the markets where the offer is stored //ensure the offer is open - ensure!(>::get(offer_iid, marketplace_id).status == OfferStatus::Open, Error::::OfferNotOpen); + ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); //get offer_data let offer_data = >::get(offer_iid, marketplace_id); //ensure the offer is not expired @@ -478,7 +479,7 @@ impl Pallet { } fn get_offer_status(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferStatus{ - //we already know that the offer exists, so we don't need to check it. + //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. if let Some(offer) = >::get(offer_id, marketplace_id) { return offer.status; From 82260356b084c5cbe0d76906095e03b226010316 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:57:02 -0500 Subject: [PATCH 039/103] ensure the marketplace exists --- pallets/gated-marketplace/src/functions.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index b7c670c9..d5239525 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -155,6 +155,9 @@ impl Pallet { //ensure the origin is owner or admin Self::can_enroll(authority.clone(), marketplace_id)?; + //ensure the marketplace exists + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + //ensure the collection exists if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a == authority, Error::::NotOwner); From b9ed22c06ca77cfb7cf6592974a56534f0ff6676 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 17:58:59 -0500 Subject: [PATCH 040/103] add Expired status --- pallets/gated-marketplace/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index cbf87bb4..ed81d313 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -79,6 +79,7 @@ pub enum OfferStatus{ Freezed, Closed, NotFound, + Expired, } impl Default for OfferStatus{ From b867d287c628f8b7ad52bd1894f2126f7c0e0cdc Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 2 Aug 2022 19:47:04 -0500 Subject: [PATCH 041/103] add OriginFor to call uniques extrinsics --- pallets/gated-marketplace/src/functions.rs | 42 +++++++++++++++------- pallets/gated-marketplace/src/lib.rs | 4 +-- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index d5239525..d50c3ee7 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,7 +4,8 @@ use frame_support::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -use frame_support::traits::Currency; +use frame_support::traits::{Currency, Contains}; +use frame_system::pallet_prelude::OriginFor; impl Pallet { @@ -204,7 +205,7 @@ impl Pallet { Ok(()) } - pub fn do_take_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_offer(origin: OriginFor, authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //ensure the origin is owner or admin Self::can_enroll(authority.clone(), marketplace_id)?; //ensure the collection exists @@ -213,25 +214,35 @@ impl Pallet { } else { Err(Error::::CollectionNotFound)?; } + //freeze the item from uniques pallet + //error mismatch types + // user who calls take_offer extrinsic must be the freezer of the item + // in order to call freeze extrinsic from uniques pallet or maybe pass the freezer in the signature + pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; //ensure the selected item has an offer id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed //TODO: change the offer status to freezed use unique's function freeze //we need to change the status in all the markets where the offer is stored + //idk if I should separte this part in another extrinsic before we complete the transfer: + // 1. is the offer open? + // 2. change the status in the offer_data -> status: OfferStatus::Freezed - //ensure the offer is open + //ensure the offer is open and available ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); + //get offer_data - let offer_data = >::get(offer_iid, marketplace_id); - //ensure the offer is not expired - ensure!(offer_data.expiration_date > Self::convert_u64_to_moment(Self::convert_moment_to_u64_in_milliseconds(>::get()).unwrap_or(0)).unwrap_or(0), Error::::OfferExpired); - //ensure the offer is not already taken - ensure!(offer_data.status == OfferStatus::Open, Error::::OfferAlreadyTaken); + //let offer_data = Self::get_offer_data(offer_id, marketplace_id); + //TODO: ensure the offer is not expired //update offer status - >::mutate(offer_iid, marketplace_id, |offer_data| { - offer_data.status = OfferStatus::Taken; - }); - Self::deposit_event(Event::OfferTaken(collection_id, item_id)); + // Self::deposit_event(Event::OfferTaken(collection_id, item_id)); + + //TODO: create a new storage map offerid-boundedvec(marketplace_id) + // to facilitate the search of the marketplaces where the offer is stored + + // user who calls take_offer extrinsic must be the admin of the item + // in order to call thaw extrinsic from uniques pallet or maybe pass the admin in the signature + pallet_uniques::Pallet::::thaw(origin, collection_id, item_id)?; Ok(()) } @@ -491,6 +502,13 @@ impl Pallet { } } + //get offer_data + fn get_offer_data(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferData { + //we already know that the offer exists, so we don't need to check it here. + //we have added a NotFound status in case the storage source is corrupted. + >::get(offer_id, marketplace_id).unwrap() + } + diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 36f57d85..bfc54068 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -495,9 +495,9 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn take_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { - let who = ensure_signed(origin)?; + let who = ensure_signed(origin.clone())?; - Self::do_take_offer(who, offer_id, marketplace_id, collection_id, item_id) + Self::do_take_offer(origin.clone(), who, offer_id, marketplace_id, collection_id, item_id) } From fc60d5d2d1e395fd644279fdcfca64005694d8d2 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 3 Aug 2022 12:04:29 -0500 Subject: [PATCH 042/103] Since we no longer need to freeze the item, I'll remove both calls to freeze & thaw unique extrinsics. --- pallets/gated-marketplace/src/functions.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index d50c3ee7..e2d83713 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -218,8 +218,9 @@ impl Pallet { //error mismatch types // user who calls take_offer extrinsic must be the freezer of the item // in order to call freeze extrinsic from uniques pallet or maybe pass the freezer in the signature - pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; - //ensure the selected item has an offer id + //pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; + + //ensure the selected item has an offer_id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed //TODO: change the offer status to freezed use unique's function freeze @@ -242,7 +243,7 @@ impl Pallet { // user who calls take_offer extrinsic must be the admin of the item // in order to call thaw extrinsic from uniques pallet or maybe pass the admin in the signature - pallet_uniques::Pallet::::thaw(origin, collection_id, item_id)?; + //pallet_uniques::Pallet::::thaw(origin, collection_id, item_id)?; Ok(()) } From 25c9ca66410b91f7aa48f3746ffb4175cef187aa Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 3 Aug 2022 12:32:10 -0500 Subject: [PATCH 043/103] Removes authorities storage adds remove_scope function --- pallets/gated-marketplace/src/functions.rs | 53 +++++++--------------- pallets/gated-marketplace/src/lib.rs | 35 +------------- pallets/rbac/src/functions.rs | 11 +++-- 3 files changed, 26 insertions(+), 73 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index f7ccae21..d4bec717 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -34,11 +34,11 @@ impl Pallet { // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth + T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceRole::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); - T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; Self::deposit_event(Event::MarketplaceStored(owner, admin, marketplace_id)); Ok(()) @@ -206,14 +206,6 @@ impl Pallet { fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceRole, marketplace_id: [u8;32])->DispatchResult{ - >::try_mutate(authority.clone(), marketplace_id, |account_auths|{ - account_auths.try_push(role) - }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; - - >::try_mutate(marketplace_id, role, | accounts|{ - accounts.try_push(authority.clone()) - }).map_err(|_| Error::::ExceedMaxMarketsPerAuth)?; - T::Rbac::assign_role_to_user(authority.clone(), Self::get_pallet_id(), &marketplace_id, role.get_id())?; @@ -248,19 +240,7 @@ impl Pallet { fn remove_from_market_lists(account: T::AccountId, author_type: MarketplaceRole , marketplace_id : [u8;32])->DispatchResult{ - >::try_mutate(account.clone(), marketplace_id, |account_auths|{ - let author_index = account_auths.iter().position(|a| *a==author_type) - .ok_or(Error::::UserNotFound)?; - account_auths.remove(author_index); - Ok(()) - }).map_err(|_:Error::| Error::::UserNotFound)?; - >::try_mutate( marketplace_id, author_type, |accounts|{ - let author_index = accounts.iter().position(|a| *a==account.clone()) - .ok_or(Error::::UserNotFound)?; - accounts.remove(author_index); - Ok(()) - }).map_err(|_:Error::| Error::::UserNotFound)?; T::Rbac::remove_role_from_user(account, Self::get_pallet_id(), &marketplace_id, author_type.get_id())?; Ok(()) @@ -322,14 +302,14 @@ impl Pallet { /// Let us know if the selected account has the selected authority type. /// It returns true if the account has the authority type, false otherwise - fn does_exist_authority(account: T::AccountId, marketplace_id: [u8;32], authority_type: MarketplaceRole) -> bool{ - let roles = match >::try_get(account, marketplace_id){ - Ok(roles) => roles, - Err(_) => return false, - }; + // fn does_exist_authority(account: T::AccountId, marketplace_id: [u8;32], authority_type: MarketplaceRole) -> bool{ + // let roles = match >::try_get(account, marketplace_id){ + // Ok(roles) => roles, + // Err(_) => return false, + // }; - roles.iter().any(|authority| authority == &authority_type) - } + // roles.iter().any(|authority| authority == &authority_type) + // } /// Let us know if there's an owner for the selected marketplace. /// It returns true if there's an owner, false otherwise @@ -363,16 +343,16 @@ impl Pallet { // as well as the applicants/applications. //First we need to get the list of all the authorities for the marketplace. - let _users = >::iter_prefix(marketplace_id) - .map(|(_authority, users)| users).flatten().collect::>(); + // let _users = >::iter_prefix(marketplace_id) + // .map(|(_authority, users)| users).flatten().collect::>(); - //1. remove from MarketplacesByAuthority - _users.iter().for_each(|user|{ - >::remove(user, marketplace_id); - }); + // //1. remove from MarketplacesByAuthority + // _users.iter().for_each(|user|{ + // >::remove(user, marketplace_id); + // }); - //2. remove from authorities by marketplace list - >::remove_prefix(marketplace_id, None); + // //2. remove from authorities by marketplace list + // >::remove_prefix(marketplace_id, None); //3. remove from Applications lists let mut applications = Vec::new(); @@ -403,6 +383,7 @@ impl Pallet { //7. remove from Marketplaces list >::remove(marketplace_id); + T::Rbac::remove_scope(Self::get_pallet_id(), marketplace_id)?; Ok(()) } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 259fa0b8..11c49453 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -23,12 +23,6 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; use sp_runtime::sp_std::vec::Vec; use crate::types::*; use pallet_rbac::types::RoleBasedAccessControl; - // RBAC pallet aliases - type MaxRolesPerPallet = <::Rbac as RoleBasedAccessControl<::AccountId,>>::MaxRolesPerPallet; - type PermissionMaxLen = <::Rbac as RoleBasedAccessControl<::AccountId,>>::PermissionMaxLen; - // <::Currency as Currency< - //::AccountId, - //>>::NegativeImbalance; #[pallet::config] pub trait Config: frame_system::Config { @@ -74,30 +68,6 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; OptionQuery, >; - #[pallet::storage] - #[pallet::getter(fn marketplaces_by_authority)] - pub(super) type MarketplacesByAuthority = StorageDoubleMap< - _, - Blake2_128Concat, - T::AccountId, // K1: Authority - Blake2_128Concat, - [u8;32], // K2: marketplace_id - BoundedVec, // scales with MarketplaceAuthority cardinality - ValueQuery - >; - - #[pallet::storage] - #[pallet::getter(fn authorities_by_marketplace)] - pub(super) type AuthoritiesByMarketplace = StorageDoubleMap< - _, - Identity, - [u8;32], //K1: marketplace_id - Blake2_128Concat, - MarketplaceRole, //k2: authority - BoundedVec, - ValueQuery - >; - #[pallet::storage] #[pallet::getter(fn applications)] pub(super) type Applications = StorageMap< @@ -146,8 +116,6 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; >; - - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -462,12 +430,11 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; ) -> DispatchResult{ T::RemoveOrigin::ensure_origin(origin.clone())?; >::remove_all(None); - >::remove_all(None); - >::remove_all(None); >::remove_all(None); >::remove_all(None); >::remove_all(None); >::remove_all(None); + // TODO: call T::Rbac::remove_pallet_storage Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index b7a2815c..3e40b0c3 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -30,15 +30,20 @@ impl RoleBasedAccessControl for Pallet{ } Ok(()) })?; - // remove on users by scope let mut scope_users = >::iter_prefix((pallet_id, scope_id)).map( |(_role, users)|users).flatten().collect::>(); - // remove duplicate users + // exclude duplicate users scope_users.sort(); scope_users.dedup(); - // TODO: remove scope users + // remove on RolesByUser + scope_users.iter().for_each(|user|{ + >::remove((user, pallet_id, scope_id)); + }); + // remove on users by scope + >::remove_prefix((pallet_id, scope_id), None); Ok(()) } + /// Inserts roles and links them to the pallet fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>{ From e1142575092d1cafe4b579c0885f09cc0ba46bdb Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 3 Aug 2022 15:35:04 -0500 Subject: [PATCH 044/103] add OfferType::NotFound in case the offer storage were corrupted --- pallets/gated-marketplace/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index ed81d313..ccb875be 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -92,6 +92,7 @@ impl Default for OfferStatus{ pub enum OfferType{ SellOrder, BuyOrder, + NotFound, } #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,)] From 3d0585a0be6082129e38a325517ecb5fca973a61 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 3 Aug 2022 15:35:48 -0500 Subject: [PATCH 045/103] delete get_offer_data, is no longer needed --- pallets/gated-marketplace/src/functions.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index e2d83713..521385d2 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -218,10 +218,11 @@ impl Pallet { //error mismatch types // user who calls take_offer extrinsic must be the freezer of the item // in order to call freeze extrinsic from uniques pallet or maybe pass the freezer in the signature - //pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; + pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; //ensure the selected item has an offer_id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed //TODO: change the offer status to freezed use unique's function freeze //we need to change the status in all the markets where the offer is stored @@ -503,12 +504,6 @@ impl Pallet { } } - //get offer_data - fn get_offer_data(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferData { - //we already know that the offer exists, so we don't need to check it here. - //we have added a NotFound status in case the storage source is corrupted. - >::get(offer_id, marketplace_id).unwrap() - } From ec0f3dd7c100ae92f66c78784614cb7b60ef2918 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 10:45:29 -0500 Subject: [PATCH 046/103] add CannotTakeOffer to prevent a user takes its own offers --- pallets/gated-marketplace/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index bfc54068..fcaa34f6 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -256,7 +256,8 @@ pub mod pallet { OfferIsFreezed, /// Offer is not available at the moment OfferIsNotAvailable, - + /// Owner cannnot buy its own offer + CannotTakeOffer, } #[pallet::call] From 59e4e04c202ee7ad718c2ccc146a77e7e6bc1c02 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 10:53:06 -0500 Subject: [PATCH 047/103] add event OfferTransferred --- pallets/gated-marketplace/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index fcaa34f6..26feb704 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -193,6 +193,8 @@ pub mod pallet { MarketplaceRemoved([u8;32]), /// Offer stored. [collection_id, item_id] OfferStored(T::CollectionId, T::ItemId), + /// Offer was transferred to the specified account. [offer_id, account] + OfferTransferred([u8;32], T::AccountId), } // Errors inform users that something went wrong. From 1a9a9e3c87b9470a86107fcb33df8a5935f2cf90 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 10:53:48 -0500 Subject: [PATCH 048/103] Delete unused fucntions & comments. --- pallets/gated-marketplace/src/functions.rs | 54 +++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 521385d2..a3fae3bd 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -206,45 +206,35 @@ impl Pallet { } pub fn do_take_offer(origin: OriginFor, authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { - //ensure the origin is owner or admin - Self::can_enroll(authority.clone(), marketplace_id)?; - //ensure the collection exists - if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { - ensure!(a == authority, Error::::NotOwner); - } else { - Err(Error::::CollectionNotFound)?; - } - //freeze the item from uniques pallet - //error mismatch types - // user who calls take_offer extrinsic must be the freezer of the item - // in order to call freeze extrinsic from uniques pallet or maybe pass the freezer in the signature - pallet_uniques::Pallet::::freeze(origin.clone(), collection_id, item_id)?; + //ensure the collection & owner exists + let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + + //ensure owner is not the same as the buyer + ensure!(owner_item != authority.clone(), Error::::CannotTakeOffer); + //ensure the selected item has an offer_id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); - //TODO: add validation to check if the offer isn't freezed Error::::OfferIsFreezed - //TODO: change the offer status to freezed use unique's function freeze - //we need to change the status in all the markets where the offer is stored - //idk if I should separte this part in another extrinsic before we complete the transfer: - // 1. is the offer open? - // 2. change the status in the offer_data -> status: OfferStatus::Freezed //ensure the offer is open and available ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); - - //get offer_data - //let offer_data = Self::get_offer_data(offer_id, marketplace_id); + + //TODO: Add transfer from currency trait + + //TODO: add transfer from uniques, admin needs to sign the transfer + pallet_uniques::Pallet::::do_transfer(collection_id, item_id, authority.clone(), |_, _|{ + Ok(()) + })?; + //TODO: ensure the offer is not expired - //update offer status - // Self::deposit_event(Event::OfferTaken(collection_id, item_id)); + //TODO: update offer status + //TODO: create a new storage map offerid-boundedvec(marketplace_id) // to facilitate the search of the marketplaces where the offer is stored - // user who calls take_offer extrinsic must be the admin of the item - // in order to call thaw extrinsic from uniques pallet or maybe pass the admin in the signature - //pallet_uniques::Pallet::::thaw(origin, collection_id, item_id)?; + Self::deposit_event(Event::OfferTransferred(offer_id, authority)); Ok(()) } @@ -504,6 +494,16 @@ impl Pallet { } } + fn _get_offer_type(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferType{ + //we already know that the offer exists, so we don't need to check it here. + //we have added a NotFound status in case the storage source is corrupted. + if let Some(offer) = >::get(offer_id, marketplace_id) { + return offer.offer_type; + } else { + return OfferType::NotFound; + } + } + From be8ccca4c6715532abb97cfc4b5a115ac55842ee Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 10:55:20 -0500 Subject: [PATCH 049/103] Remove OriginFor from take_offer extrinsic, we no longer need to sign the tx --- pallets/gated-marketplace/src/functions.rs | 2 +- pallets/gated-marketplace/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index a3fae3bd..fd2479fc 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -205,7 +205,7 @@ impl Pallet { Ok(()) } - pub fn do_take_offer(origin: OriginFor, authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 26feb704..556093af 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -500,7 +500,7 @@ pub mod pallet { pub fn take_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_offer(origin.clone(), who, offer_id, marketplace_id, collection_id, item_id) + Self::do_take_offer(who, offer_id, marketplace_id, collection_id, item_id) } From fdf14345b09161b63e51728b3a20177b54a9760e Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 10:58:12 -0500 Subject: [PATCH 050/103] Remove OriginFor from take_offer extrinsic, we no longer need to sign the tx --- pallets/gated-marketplace/src/functions.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index fd2479fc..e28cdcec 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -5,7 +5,6 @@ use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; use frame_support::traits::{Currency, Contains}; -use frame_system::pallet_prelude::OriginFor; impl Pallet { From bee40d6d4fb456fc8590a9f38a4bbc23ad912c3a Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 4 Aug 2022 12:36:07 -0500 Subject: [PATCH 051/103] adds rbac auth to gated marketplaces requires more testing --- pallets/gated-marketplace/src/functions.rs | 127 +++++++++------------ pallets/gated-marketplace/src/lib.rs | 2 + pallets/gated-marketplace/src/types.rs | 44 ++++++- pallets/rbac/src/functions.rs | 34 +++--- pallets/rbac/src/lib.rs | 2 + pallets/rbac/src/types.rs | 4 +- 6 files changed, 120 insertions(+), 93 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index d4bec717..6007bdf0 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -12,19 +12,18 @@ use pallet_rbac::types::*; impl Pallet { pub fn do_initial_setup()->DispatchResult{ - let pallet_id = Self::get_pallet_id(); + let pallet_id = Self::pallet_id(); let mut super_roles = Vec::>::new(); - let mut super_permissions = Vec::>::new(); super_roles.push(MarketplaceRole::Owner.to_vec()); super_roles.push(MarketplaceRole::Admin.to_vec()); - super_permissions.push("enroll".as_bytes().to_vec()); - super_permissions.push("add_authority".as_bytes().to_vec()); - super_permissions.push("remove_authority".as_bytes().to_vec()); - super_permissions.push("update_label_marketplace".as_bytes().to_vec()); let super_role_ids = T::Rbac::create_and_set_roles(pallet_id, super_roles)?; for super_role in super_role_ids{ - T::Rbac::create_and_set_permissions(pallet_id, super_role, super_permissions.clone())?; + T::Rbac::create_and_set_permissions(pallet_id, super_role, Permission::admin_permissions())?; } + // participant role and permissions + let participant_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Participant.to_vec()].to_vec())?; + T::Rbac::create_and_set_permissions(pallet_id, participant_role_id[0],["buy".as_bytes().to_vec(),"sell".as_bytes().to_vec()].to_vec() )?; + Ok(()) } @@ -34,7 +33,7 @@ impl Pallet { // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth - T::Rbac::create_scope(Self::get_pallet_id(),marketplace_id.clone())?; + T::Rbac::create_scope(Self::pallet_id(),marketplace_id.clone())?; Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceRole::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); @@ -70,10 +69,8 @@ impl Pallet { pub fn do_enroll(authority: T::AccountId, marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ // ensure the origin is owner or admin - // T::Rbac::is_user_authorized(authority,Self::get_pallet_id(), marketplace_id, - // IdOrString::String(BoundedVec::try_from("henlo".as_bytes().to_vec()).expect("erfge")) )?; - Self::can_enroll(authority, marketplace_id)?; - //T::Rbac::is_user_authorized(authority, Self::get_pallet_id(), &marketplace_id, MarketplaceAuthority::Owner. ); + //Self::can_enroll(authority, marketplace_id)?; + Self::is_authorized(authority.clone(), &marketplace_id,Permission::Enroll)?; let next_status = match approved{ true => ApplicationStatus::Approved, false => ApplicationStatus::Rejected, @@ -99,8 +96,8 @@ impl Pallet { pub fn do_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32], ) -> DispatchResult { //ensure the origin is owner or admin //TODO: implement copy trait for MarketplaceAuthority & T::AccountId - Self::can_enroll(authority, marketplace_id)?; - + //Self::can_enroll(authority, marketplace_id)?; + Self::is_authorized(authority.clone(), &marketplace_id,Permission::AddAuth)?; //ensure the account is not already an authority // handled by T::Rbac::assign_role_to_user //ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); @@ -122,8 +119,8 @@ impl Pallet { pub fn do_remove_authority(authority: T::AccountId, account: T::AccountId, authority_type: MarketplaceRole, marketplace_id: [u8;32], ) -> DispatchResult { //ensure the origin is owner or admin - Self::can_enroll(authority.clone(), marketplace_id)?; - + //Self::can_enroll(authority.clone(), marketplace_id)?; + Self::is_authorized(authority.clone(), &marketplace_id,Permission::RemoveAuth)?; //ensure the account has the selected authority before to try to remove // T::Rbac handles the if role doesnt hasnt been asigned to the user //ensure!(Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AuthorityNotFoundForUser); @@ -158,7 +155,8 @@ impl Pallet { //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); //ensure the origin is owner or admin - Self::can_enroll(authority, marketplace_id)?; + //Self::can_enroll(authority, marketplace_id)?; + Self::is_authorized(authority, &marketplace_id, Permission::UpdateLabel)?; //update marketplace Self::update_label(marketplace_id, new_label)?; Self::deposit_event(Event::MarketplaceLabelUpdated(marketplace_id)); @@ -170,7 +168,8 @@ impl Pallet { //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); //ensure the origin is owner or admin - Self::can_enroll(authority, marketplace_id)?; + //Self::can_enroll(authority, marketplace_id)?; + Self::is_authorized(authority, &marketplace_id, Permission::RemoveMarketplace)?; //remove marketplace Self::remove_selected_marketplace(marketplace_id)?; Self::deposit_event(Event::MarketplaceRemoved(marketplace_id)); @@ -206,8 +205,8 @@ impl Pallet { fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceRole, marketplace_id: [u8;32])->DispatchResult{ - T::Rbac::assign_role_to_user(authority.clone(), Self::get_pallet_id(), - &marketplace_id, role.get_id())?; + T::Rbac::assign_role_to_user(authority.clone(), Self::pallet_id(), + &marketplace_id, role.id())?; Ok(()) } @@ -216,7 +215,7 @@ impl Pallet { >::try_mutate(marketplace_id, status,|applicants|{ applicants.try_push(applicant) }).map_err(|_| Error::::ExceedMaxApplicants)?; - // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? + Ok(()) } @@ -224,7 +223,7 @@ impl Pallet { >::try_mutate(custodian, marketplace_id, | applications |{ applications.try_push(applicant) }).map_err(|_| Error::::ExceedMaxApplicationsPerCustodian)?; - // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? + Ok(()) } @@ -233,7 +232,7 @@ impl Pallet { let applicant_index = applicants.iter().position(|a| *a==applicant.clone()) .ok_or(Error::::ApplicantNotFound)?; applicants.remove(applicant_index); - // TODO: implement rbac for applicants ApplicantsByMarketplace is necessary and ? + Ok(()) }) } @@ -241,8 +240,8 @@ impl Pallet { fn remove_from_market_lists(account: T::AccountId, author_type: MarketplaceRole , marketplace_id : [u8;32])->DispatchResult{ - T::Rbac::remove_role_from_user(account, Self::get_pallet_id(), - &marketplace_id, author_type.get_id())?; + T::Rbac::remove_role_from_user(account, Self::pallet_id(), + &marketplace_id, author_type.id())?; Ok(()) } @@ -261,42 +260,38 @@ impl Pallet { } Ok(()) })?; + ensure!(prev_status != next_status, Error::::AlreadyEnrolled); //remove from previous state list Self::remove_from_applicants_lists(applicant.clone(),prev_status, marketplace_id)?; //insert in current state list - Self::insert_in_applicants_lists(applicant, next_status,marketplace_id)?; + Self::insert_in_applicants_lists(applicant.clone(), next_status,marketplace_id)?; + + if prev_status == ApplicationStatus::Approved{ + T::Rbac::remove_role_from_user(applicant.clone(), Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())?; + } + if next_status == ApplicationStatus::Approved{ + T::Rbac::assign_role_to_user(applicant, Self::pallet_id(), &marketplace_id, MarketplaceRole::Participant.id())? + } + Ok(()) } - fn can_enroll( authority: T::AccountId, marketplace_id: [u8;32] ) -> DispatchResult{ - // to enroll, the account needs to be an owner or an admin - // let roles = >::try_get(authority, marketplace_id) - // .map_err(|_| Error::::CannotEnroll)?; - // // iter().any could be called too but this maps directly to desired error - // roles.iter().find(|&role|{ - // role.eq(&MarketplaceAuthority::Owner) || role.eq(&MarketplaceAuthority::Admin) - // }).ok_or(Error::::CannotEnroll)?; - let auths = [ - MarketplaceRole::Owner.get_id(), - MarketplaceRole::Admin.get_id() - ].to_vec(); - //TODO: Test if the auth mechanism works - T::Rbac::has_role(authority.clone(),Self::get_pallet_id(), &marketplace_id,auths) + fn is_authorized( authority: T::AccountId, marketplace_id: &[u8;32], permission: Permission ) -> DispatchResult{ + T::Rbac::is_authorized( + authority, + Self::pallet_id(), + &marketplace_id, + &permission.id(), + ) } ///Lets us know if the selected user is an admin. /// It returns true if the user is an admin, false otherwise. fn is_admin(account: T::AccountId, marketplace_id: [u8;32]) -> bool{ - // let roles = match >::try_get(account, marketplace_id){ - // Ok(roles) => roles, - // Err(_) => return false, - // }; - - // roles.iter().any(|&authority_type| authority_type == MarketplaceAuthority::Admin) - T::Rbac::has_role(account, Self::get_pallet_id(), - &marketplace_id, [MarketplaceRole::Admin.get_id()].to_vec()).is_ok() + T::Rbac::has_role(account, Self::pallet_id(), + &marketplace_id, [MarketplaceRole::Admin.id()].to_vec()).is_ok() } @@ -320,8 +315,8 @@ impl Pallet { // }; //owners.len() == 1 - T::Rbac::get_role_users_len(Self::get_pallet_id(), - &marketplace_id, &MarketplaceRole::Owner.get_id()) == 1 + T::Rbac::get_role_users_len(Self::pallet_id(), + &marketplace_id, &MarketplaceRole::Owner.id()) == 1 } /// Let us update the marketplace's label. @@ -343,20 +338,9 @@ impl Pallet { // as well as the applicants/applications. //First we need to get the list of all the authorities for the marketplace. - // let _users = >::iter_prefix(marketplace_id) - // .map(|(_authority, users)| users).flatten().collect::>(); - - // //1. remove from MarketplacesByAuthority - // _users.iter().for_each(|user|{ - // >::remove(user, marketplace_id); - // }); - - // //2. remove from authorities by marketplace list - // >::remove_prefix(marketplace_id, None); - - //3. remove from Applications lists let mut applications = Vec::new(); - + + // remove from Applications lists for ele in >::iter() { if ele.1 == marketplace_id { applications.push(ele.2); @@ -367,23 +351,24 @@ impl Pallet { >::remove(application); } - //4. remove from ApplicationsByAccount list + // remove from ApplicationsByAccount list >::iter().for_each(|(_k1, _k2, _k3)|{ >::remove(_k1, marketplace_id); }); - //5. remove from ApplicantsByMarketplace list + // remove from ApplicantsByMarketplace list >::remove_prefix(marketplace_id, None); - //6. remove from Custodians list + // remove from Custodians list >::iter().for_each(|(_k1, _k2, _k3)|{ >::remove(_k1, marketplace_id); }); - //7. remove from Marketplaces list + // remove from Marketplaces list >::remove(marketplace_id); - T::Rbac::remove_scope(Self::get_pallet_id(), marketplace_id)?; + T::Rbac::remove_scope(Self::pallet_id(), marketplace_id)?; + Ok(()) } @@ -412,12 +397,8 @@ impl Pallet { Ok(()) } - pub fn get_pallet_id()->u64{ + pub fn pallet_id()->u64{ Self::index().try_into().unwrap() } - pub fn str_to_bvec_uncheked>(str: &str)->BoundedVec{ - BoundedVec::::try_from(str.as_bytes().to_vec()).expect("Conversion error") - } - } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 11c49453..b2e7c302 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -164,6 +164,8 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; MarketplaceNotFound, /// You need to be an owner or an admin of the marketplace CannotEnroll, + /// There was no change regarding the application status + AlreadyEnrolled, /// There cannot be more than one owner per marketplace OnlyOneOwnerIsAllowed, /// Cannot remove the owner of the marketplace diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index e20a9d97..2e6c2db1 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -26,13 +26,12 @@ pub enum MarketplaceRole{ Admin, Appraiser, RedemptionSpecialist, - Applicant, Participant, } impl Default for MarketplaceRole{ fn default() -> Self { - MarketplaceRole::Applicant + MarketplaceRole::Participant } } @@ -43,19 +42,52 @@ impl MarketplaceRole{ Self::Admin => "Admin".as_bytes().to_vec(), Self::Appraiser => "Appraiser".as_bytes().to_vec(), Self::RedemptionSpecialist => "Redemption_specialist".as_bytes().to_vec(), - Self::Applicant => "Applicant".as_bytes().to_vec(), Self::Participant => "Participant".as_bytes().to_vec(), } } - pub fn get_id(&self) -> [u8;32]{ + pub fn id(&self) -> [u8;32]{ self.to_vec().using_encoded(blake2_256) } pub fn enum_to_vec() -> Vec>{ use crate::types::MarketplaceRole::*; - [Owner.to_vec(), Admin.to_vec(), Appraiser.to_vec(), RedemptionSpecialist.to_vec(), - Applicant.to_vec(), Participant.to_vec()].to_vec() + [Owner.to_vec(), Admin.to_vec(), Appraiser.to_vec(), RedemptionSpecialist.to_vec(), Participant.to_vec()].to_vec() + } +} + +/// Extrinsics which require previous authorization to call them +#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] +pub enum Permission{ + Enroll, + AddAuth, + RemoveAuth, + UpdateLabel, + RemoveMarketplace, +} + +impl Permission{ + pub fn to_vec(&self) -> Vec{ + match self{ + Self::Enroll => "Enroll".as_bytes().to_vec(), + Self::AddAuth => "AddAuth".as_bytes().to_vec(), + Self::RemoveAuth => "RemoveAuth".as_bytes().to_vec(), + Self::UpdateLabel => "UpdateLabel".as_bytes().to_vec(), + Self::RemoveMarketplace => "RemoveMarketplace".as_bytes().to_vec(), + } + } + + pub fn id(&self) -> [u8;32]{ + self.to_vec().using_encoded(blake2_256) + } + + pub fn admin_permissions()-> Vec>{ + use crate::types::Permission::*; + [Enroll.to_vec(), + AddAuth.to_vec(), + RemoveAuth.to_vec(), + UpdateLabel.to_vec(), + RemoveMarketplace.to_vec()].to_vec() } } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 3e40b0c3..138e29af 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -101,9 +101,10 @@ impl RoleBasedAccessControl for Pallet{ fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>{ let permission_id = permission.using_encoded(blake2_256); - //let b_permission= BoundedVec::::try_from(permission); + let b_permission = Self::bound:: <_,T::PermissionMaxLen>(permission, Error::::ExceedPermissionMaxLen)?; + // Testing: a boundedvec id should be equal to a vec id because they have the same data ensure!(permission_id == b_permission.using_encoded(blake2_256), Error::::NoneValue); @@ -178,19 +179,15 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ - /// Authorize by role, not permissions - fn is_user_authorized(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id : &[u8;32] ) -> DispatchResult{ - // get id, whether is given directly or by its string in boundedvec format - //let role_id = Self::get_role_id(role)?; + fn is_authorized(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], permission_id: &[u8;32]) -> DispatchResult{ Self::scope_exists(pallet_id, scope_id)?; - Self::is_role_linked_to_pallet(pallet_id, role_id)?; - // Perform confirmation on both maps - // TODO: test if a role that doesnt exists cause any errors - let users = >::get( (pallet_id, scope_id, role_id) ); - ensure!(users.contains(&user), Error::::NotAuthorized); - let roles = >::get((user, pallet_id, scope_id)); - // Not likely to happen but just in case: - ensure!(roles.contains(&role_id), Error::::NotAuthorized ); + Self::permission_exists(pallet_id, permission_id)?; + + // get roles the user has in this scope + let user_roles = >::get((user, pallet_id, scope_id)); + // determine if one of the roles has the requested permission + let has_permission = user_roles.iter().any(|r_id| >::get(pallet_id, r_id).contains(permission_id)); + ensure!(has_permission, Error::::NotAuthorized); Ok(()) } @@ -210,6 +207,11 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn permission_exists(pallet_id: u64, permission_id: &[u8;32])->DispatchResult{ + ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); + Ok(()) + } + fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) @@ -217,6 +219,12 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn is_permission_linked_to_role(pallet_id: u64, role_id: &[u8;32], permission_id: &[u8;32])-> DispatchResult{ + let role_permissions = >::get(pallet_id, role_id); + ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); + Ok(()) + } + fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize{ >::get((pallet_id, scope_id, role_id)).len() } diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 0aa0ec2a..10a7afd5 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -169,6 +169,8 @@ pub mod pallet { UserAlreadyHasRole, /// The role exists but it hasn't been linked to the pallet RoleNotLinkedToPallet, + /// The permission wasn't found in the roles capabilities + PermissionNotLinkedToRole, /// The user doesn't have any roles in this pallet UserHasNoRoles, /// The role doesn't have any users assigned to it diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index f7a7725a..470a3f2c 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -33,10 +33,12 @@ pub trait RoleBasedAccessControl{ fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; fn set_multiple_permisions_to_role( pallet_id: u64, role: [u8;32], permission: Vec<[u8;32]> )-> DispatchResult; // helpers - fn is_user_authorized(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: &[u8;32] ) -> DispatchResult; + fn is_authorized(user: AccountId, pallet_id: u64, scope_id: &[u8;32], permission_id: &[u8;32] ) -> DispatchResult; fn has_role(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult; fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; + fn permission_exists(pallet_id: u64, permission_id: &[u8;32])->DispatchResult; fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; + fn is_permission_linked_to_role(pallet_id: u64, role_id: &[u8;32], permission_id: &[u8;32])-> DispatchResult; fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize; fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>; fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; From 9db36a2b93c897f6f85238f68bedbd3034e2313a Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 13:01:14 -0500 Subject: [PATCH 052/103] create a helper function update_offer_status to change the offer status to closed --- pallets/gated-marketplace/src/functions.rs | 44 +++++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index e28cdcec..6a38254d 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -152,8 +152,7 @@ impl Pallet { } pub fn do_enlist_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { - //ensure the origin is owner or admin - Self::can_enroll(authority.clone(), marketplace_id)?; + //TODO: ensure the user is a Marketparticipant //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -208,26 +207,30 @@ impl Pallet { //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + //TODO: ensure the offer type + //ensure owner is not the same as the buyer ensure!(owner_item != authority.clone(), Error::::CannotTakeOffer); - //ensure the selected item has an offer_id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); - //ensure the offer is open and available ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); //TODO: Add transfer from currency trait - //TODO: add transfer from uniques, admin needs to sign the transfer + //TODO: add transfer from uniques pallet_uniques::Pallet::::do_transfer(collection_id, item_id, authority.clone(), |_, _|{ Ok(()) })?; //TODO: ensure the offer is not expired - //TODO: update offer status + + //TODO: update offer status from all marketplaces + Self::update_offer_status(offer_id)?; + + //TODO: remove the offer from all marketplace //TODO: create a new storage map offerid-boundedvec(marketplace_id) @@ -401,6 +404,7 @@ impl Pallet { /// If returns ok if the deletion was successful, error otherwise. /// Errors only could happen if the storage sources are corrupted. fn remove_selected_marketplace(marketplace_id: [u8;32]) -> DispatchResult { + //TODO: evaluate use iter_key_prefix ->instead iter() //Before to remove the marketplace, we need to remove all its associated authorities // as well as the applicants/applications. @@ -503,6 +507,34 @@ impl Pallet { } } + pub fn update_offer_status(offer_id: [u8;32]) -> DispatchResult{ + + let marketplaces_list = >::iter_prefix(offer_id).map(|(k1, _k2)|{ + k1 + }); + + for ele in marketplaces_list { + >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ + let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; + offer.status = OfferStatus::Closed; + Ok(()) + })?; + } + + //Try this optimization next time. + // for ele in >::iter_prefix(offer_id).map(|(k1, _k2)|{ + // k1 + // }){ + // >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ + // let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; + // offer.status = OfferStatus::Closed; + // Ok(()) + // })?; + // } + + Ok(()) + } + From e91b17d6d7b517c9eb731b668255aefa20579a99 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 4 Aug 2022 16:44:23 -0500 Subject: [PATCH 053/103] adds remove_pallet_storage extrinsic --- pallets/gated-marketplace/src/lib.rs | 2 +- pallets/rbac/src/functions.rs | 23 ++++++++++++++++++++++- pallets/rbac/src/types.rs | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index b2e7c302..2a0d8d83 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -436,7 +436,7 @@ use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; >::remove_all(None); >::remove_all(None); >::remove_all(None); - // TODO: call T::Rbac::remove_pallet_storage + T::Rbac::remove_pallet_storage(Self::pallet_id())?; Ok(()) } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 138e29af..0dfe670e 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -19,7 +19,6 @@ impl RoleBasedAccessControl for Pallet{ } fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult{ - // WIP // remove on scopes >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ let scopes = scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; @@ -44,6 +43,28 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + fn remove_pallet_storage(pallet_id: u64) -> DispatchResult{ + + //remove all scopes + let scopes = >::get(pallet_id); + for scope in scopes{ + Self::remove_scope(pallet_id, scope)?; + } + // remove all roles + let pallet_roles = >::take(pallet_id); + //check if there's other pallet that uses the roles, if not, remove them + let all_pallet_roles = >::iter().map(| p| p.1.to_vec()) + .collect::>>(); + let flatten_all_pallet_roles = all_pallet_roles.iter().flatten().collect::>(); + let filtered_roles = pallet_roles.iter().filter(|pallet_role| !flatten_all_pallet_roles.contains(pallet_role)); + filtered_roles.for_each(|role|{ + >::remove(role); + }); + //remove all permissions + >::remove_prefix(pallet_id, None); + >::remove_prefix(pallet_id, None); + Ok(()) + } /// Inserts roles and links them to the pallet fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>{ diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 470a3f2c..adc9192a 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -17,6 +17,8 @@ pub trait RoleBasedAccessControl{ fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; // scope removal fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; + // removes all from one pallet/application + fn remove_pallet_storage(pallet_id: u64) -> DispatchResult; // roles creation and setting fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>; From ca24bcf0b0917cbeb1f1f92f708a9d7d0a199bd2 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 4 Aug 2022 17:32:44 -0500 Subject: [PATCH 054/103] add helper functions to delete offer from storage --- pallets/gated-marketplace/src/functions.rs | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 6a38254d..e82c5be7 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,5 +1,5 @@ use super::*; -use frame_support::pallet_prelude::*; +use frame_support::{pallet_prelude::*, traits::LockableCurrency}; //use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; @@ -219,7 +219,7 @@ impl Pallet { ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); //TODO: Add transfer from currency trait - + T::LocalCurrency::transfer(&authority, &owner_item, Self::get_offer_price(offer_id, marketplace_id), AllowDeath::No)?; //TODO: add transfer from uniques pallet_uniques::Pallet::::do_transfer(collection_id, item_id, authority.clone(), |_, _|{ Ok(()) @@ -231,6 +231,7 @@ impl Pallet { Self::update_offer_status(offer_id)?; //TODO: remove the offer from all marketplace + Self::delete_offer(collection_id, item_id, offer_id)?; //TODO: create a new storage map offerid-boundedvec(marketplace_id) @@ -531,10 +532,28 @@ impl Pallet { // Ok(()) // })?; // } + Ok(()) + } + + pub fn delete_offer(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult{ + //remove from OffersData list + >::remove_prefix(offer_id, None); + //remove from OffersId list + >::remove(collection_id, item_id); Ok(()) } + pub fn get_offer_price(offer_id: [u8;32], marketplace_id : [u8;32]) -> Result { + //we already know that the offer exists, so we don't need to check it here. + //we have added a NotFound status in case the storage source is corrupted. + if let Some(offer) = >::get(offer_id, marketplace_id) { + return Ok(offer.price); + } else { + return Err(Error::::OfferNotFound)?; + } + } + From c3b25758e510c5ce8c525c718b0d0d4baf712622 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 4 Aug 2022 17:48:14 -0500 Subject: [PATCH 055/103] adds ids aliases --- pallets/rbac/src/functions.rs | 136 ++++++++++++++-------------------- pallets/rbac/src/lib.rs | 20 ++--- pallets/rbac/src/types.rs | 47 ++++++------ 3 files changed, 92 insertions(+), 111 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 0dfe670e..7bd14acd 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -9,7 +9,7 @@ use crate::types::*; impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ - fn create_scope(pallet_id: u64, scope_id: [u8;32])-> DispatchResult{ + fn create_scope(pallet_id: u64, scope_id: ScopeId)-> DispatchResult{ let pallet_id: u64 = pallet_id.try_into().unwrap(); >::try_mutate(pallet_id, |scopes|{ ensure!(!scopes.contains(&scope_id), Error::::ScopeAlreadyExists); @@ -18,7 +18,7 @@ impl RoleBasedAccessControl for Pallet{ }) } - fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult{ + fn remove_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult{ // remove on scopes >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ let scopes = scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; @@ -44,7 +44,6 @@ impl RoleBasedAccessControl for Pallet{ } fn remove_pallet_storage(pallet_id: u64) -> DispatchResult{ - //remove all scopes let scopes = >::get(pallet_id); for scope in scopes{ @@ -65,9 +64,10 @@ impl RoleBasedAccessControl for Pallet{ >::remove_prefix(pallet_id, None); Ok(()) } + /// Inserts roles and links them to the pallet fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> - Result, DispatchError>{ + Result, DispatchError>{ let mut role_ids= Vec::<[u8;32]>::new(); for role in roles{ role_ids.push( Self::create_role(role.to_owned())? ); @@ -77,7 +77,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(bounded_ids) } - fn create_role(role: Vec)-> Result<[u8;32], DispatchError>{ + fn create_role(role: Vec)-> Result{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found let b_role = Self::bound::<_,T::RoleMaxLen>(role, Error::::ExceedMaxRolesPerUser)?; @@ -86,7 +86,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(role_id) } - fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult{ + fn set_role_to_pallet(pallet_id: u64, role_id: RoleId )-> DispatchResult{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); >::try_mutate(pallet_id, |roles|{ @@ -96,7 +96,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult{ + fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult{ // checks for duplicates: let pallet_roles = >::get(&pallet_id); for id in roles.clone(){ @@ -109,8 +109,46 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn create_and_set_permissions(pallet_id: u64, role_id: [u8;32], permissions: Vec>)-> - Result, DispatchError> { + fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + Self::scope_exists(pallet_id, scope_id)?; + Self::is_role_linked_to_pallet(pallet_id, &role_id)?; + + >::try_mutate((&user, pallet_id, scope_id), | roles |{ + ensure!(!roles.contains(&role_id), Error::::DuplicateRole); + roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) + })?; + + >::try_mutate((pallet_id, scope_id, role_id), | users|{ + ensure!(!users.contains(&user), Error::::UserAlreadyHasRole); + users.try_push(user).map_err(|_| Error::::ExceedMaxUsersPerRole) + })?; + Ok(()) + } + + fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ + let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; + let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; + user_roles.remove(r_pos); + if user_roles.is_empty(){ + user_roles_option.clone_from(&None) + } + Ok(()) + })?; + >::try_mutate_exists::<_,(),DispatchError,_>((pallet_id, scope_id, role_id), |auth_users_option|{ + let auth_users = auth_users_option.as_mut().ok_or(Error::::RoleHasNoUsers)?; + let u_pos = auth_users.iter().position(|u| *u==user).ok_or(Error::::UserNotFound)?; + auth_users.remove(u_pos); + if auth_users.is_empty(){ + auth_users_option.clone_from(&None); + } + Ok(()) + })?; + Ok(()) + } + + fn create_and_set_permissions(pallet_id: u64, role_id: RoleId, permissions: Vec>)-> + Result, DispatchError> { let mut permission_ids = Vec::<[u8;32]>::new(); for permision in permissions{ permission_ids.push( Self::create_permission(pallet_id, permision.to_owned())? ); @@ -120,23 +158,19 @@ impl RoleBasedAccessControl for Pallet{ Ok(b_permissions) } - fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>{ + fn create_permission(pallet_id: u64, permission: Vec) -> Result{ let permission_id = permission.using_encoded(blake2_256); let b_permission = Self::bound:: <_,T::PermissionMaxLen>(permission, Error::::ExceedPermissionMaxLen)?; - // Testing: a boundedvec id should be equal to a vec id because they have the same data - ensure!(permission_id == b_permission.using_encoded(blake2_256), Error::::NoneValue); - - log::info!("Is permission_id equal: {}",permission_id == b_permission.using_encoded(blake2_256)); if !>::contains_key(pallet_id, permission_id){ >::insert(pallet_id, permission_id, b_permission); } Ok(permission_id) } - fn set_permission_to_role( pallet_id: u64, role_id: [u8;32], permission_id: [u8;32] ) -> DispatchResult{ + fn set_permission_to_role( pallet_id: u64, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); Self::is_role_linked_to_pallet(pallet_id, &role_id)?; @@ -147,7 +181,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn set_multiple_permisions_to_role( pallet_id: u64, role_id: [u8;32], permissions: Vec<[u8;32]> )-> DispatchResult{ + fn set_multiple_permisions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ @@ -160,47 +194,9 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult{ - Self::scope_exists(pallet_id, scope_id)?; - Self::is_role_linked_to_pallet(pallet_id, &role_id)?; - - >::try_mutate((&user, pallet_id, scope_id), | roles |{ - ensure!(!roles.contains(&role_id), Error::::DuplicateRole); - roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) - })?; - - >::try_mutate((pallet_id, scope_id, role_id), | users|{ - ensure!(!users.contains(&user), Error::::UserAlreadyHasRole); - users.try_push(user).map_err(|_| Error::::ExceedMaxUsersPerRole) - })?; - Ok(()) - } - - fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult{ - >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ - let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; - let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; - user_roles.remove(r_pos); - if user_roles.is_empty(){ - user_roles_option.clone_from(&None) - } - Ok(()) - })?; - >::try_mutate_exists::<_,(),DispatchError,_>((pallet_id, scope_id, role_id), |auth_users_option|{ - let auth_users = auth_users_option.as_mut().ok_or(Error::::RoleHasNoUsers)?; - let u_pos = auth_users.iter().position(|u| *u==user).ok_or(Error::::UserNotFound)?; - auth_users.remove(u_pos); - if auth_users.is_empty(){ - auth_users_option.clone_from(&None); - } - Ok(()) - })?; - Ok(()) - } - /*---- Helper functions ----*/ - fn is_authorized(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], permission_id: &[u8;32]) -> DispatchResult{ + fn is_authorized(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ Self::scope_exists(pallet_id, scope_id)?; Self::permission_exists(pallet_id, permission_id)?; @@ -212,7 +208,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult { + fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { Self::scope_exists(pallet_id, scope_id)?; let user_roles = >::get((user, pallet_id, scope_id)); @@ -223,51 +219,33 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } /// Also checks if pallet is stored. Need this function to expose the check to other pallets - fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult{ + fn scope_exists(pallet_id: u64, scope_id:&ScopeId) -> DispatchResult{ ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); Ok(()) } - fn permission_exists(pallet_id: u64, permission_id: &[u8;32])->DispatchResult{ + fn permission_exists(pallet_id: u64, permission_id: &PermissionId)->DispatchResult{ ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); Ok(()) } - fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult{ + fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) .ok_or(Error::::RoleNotLinkedToPallet)?; Ok(()) } - fn is_permission_linked_to_role(pallet_id: u64, role_id: &[u8;32], permission_id: &[u8;32])-> DispatchResult{ + fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ let role_permissions = >::get(pallet_id, role_id); ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); Ok(()) } - fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize{ + fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize{ >::get((pallet_id, scope_id, role_id)).len() } - fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>{ - let role_id = match id_or_role{ - IdOrString::Id(id)=>id, - IdOrString::String(role_str)=> role_str.using_encoded(blake2_256), - }; - ensure!(>::contains_key(role_id), Error::::RoleNotFound); - Ok(role_id) - } - - fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString)->Result<[u8;32], DispatchError>{ - let permission_id = match id_or_permission{ - IdOrString::Id(id)=>id, - IdOrString::String(permission_str)=> permission_str.using_encoded(blake2_256), - }; - ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); - Ok(permission_id) - } - fn has_unique_elements(vec: Vec) -> bool{ let mut filtered_vec = vec.clone(); filtered_vec.sort(); diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 10a7afd5..ac9445f1 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -57,7 +57,7 @@ pub mod pallet { _, Blake2_128Concat, u64, // pallet_id - BoundedVec<[u8;32], T::MaxScopesPerPallet>, // scopes_id + BoundedVec, // scopes_id ValueQuery, >; @@ -66,7 +66,7 @@ pub mod pallet { pub(super) type Roles = StorageMap< _, Identity, - [u8;32], // role_id + RoleId, // role_id BoundedVec, // role OptionQuery, >; @@ -77,7 +77,7 @@ pub mod pallet { _, Blake2_128Concat, u64, // pallet_id - BoundedVec<[u8;32], T::MaxRolesPerPallet >, // role_id + BoundedVec, // role_id ValueQuery, >; @@ -88,7 +88,7 @@ pub mod pallet { Blake2_128Concat, u64, // pallet_id Blake2_128Concat, - [u8;32], // permission_id + PermissionId, // permission_id BoundedVec, // permission str ValueQuery, >; @@ -100,8 +100,8 @@ pub mod pallet { Blake2_128Concat, u64, // pallet_id Blake2_128Concat, - [u8;32], // role_id - BoundedVec<[u8;32], T::MaxPermissionsPerRole >, // permission_ids + RoleId, // role_id + BoundedVec, // permission_ids ValueQuery, >; @@ -113,9 +113,9 @@ pub mod pallet { NMapKey,// user // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors NMapKey, // pallet_id - NMapKey, // scope_id + NMapKey, // scope_id ), - BoundedVec<[u8;32], T::MaxRolesPerUser>, // roles (ids) + BoundedVec, // roles (ids) ValueQuery, >; @@ -127,8 +127,8 @@ pub mod pallet { // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors // on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes. NMapKey, // pallet_id - NMapKey, // scope_id - NMapKey, // role_id + NMapKey, // scope_id + NMapKey, // role_id ), BoundedVec, // users ValueQuery, diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index adc9192a..d8db058b 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -2,6 +2,11 @@ use super::*; use frame_support::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; + +pub type RoleId = [u8;32]; +pub type ScopeId = [u8;32]; +pub type PermissionId = [u8;32]; + #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo,)] pub enum IdOrString >{ Id([u8;32]), @@ -14,36 +19,34 @@ pub trait RoleBasedAccessControl{ type RoleMaxLen: Get; type PermissionMaxLen: Get; // scopes - fn create_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; + fn create_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult; // scope removal - fn remove_scope(pallet_id: u64, scope_id: [u8;32]) -> DispatchResult; + fn remove_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult; // removes all from one pallet/application fn remove_pallet_storage(pallet_id: u64) -> DispatchResult; // roles creation and setting fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> - Result, DispatchError>; - fn create_role(role: Vec)-> Result<[u8;32], DispatchError>; - fn set_role_to_pallet(pallet_id: u64, role_id: [u8;32] )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec<[u8;32]>)->DispatchResult; - fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; + Result, DispatchError>; + fn create_role(role: Vec)-> Result; + fn set_role_to_pallet(pallet_id: u64, role_id: RoleId )-> DispatchResult; + fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // role removal - fn remove_role_from_user(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_id: [u8;32]) -> DispatchResult; + fn remove_role_from_user(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // permissions - fn create_and_set_permissions(pallet_id: u64, role: [u8;32], permissions: Vec>)-> - Result, DispatchError>; - fn create_permission(pallet_id: u64, permission: Vec) -> Result<[u8;32], DispatchError>; - fn set_permission_to_role( pallet_id: u64, role: [u8;32], permission: [u8;32] ) -> DispatchResult; - fn set_multiple_permisions_to_role( pallet_id: u64, role: [u8;32], permission: Vec<[u8;32]> )-> DispatchResult; + fn create_and_set_permissions(pallet_id: u64, role: RoleId, permissions: Vec>)-> + Result, DispatchError>; + fn create_permission(pallet_id: u64, permissions: Vec) -> Result; + fn set_permission_to_role( pallet_id: u64, role: RoleId, permission: PermissionId ) -> DispatchResult; + fn set_multiple_permisions_to_role( pallet_id: u64, role: RoleId, permission: Vec )-> DispatchResult; // helpers - fn is_authorized(user: AccountId, pallet_id: u64, scope_id: &[u8;32], permission_id: &[u8;32] ) -> DispatchResult; - fn has_role(user: AccountId, pallet_id: u64, scope_id: &[u8;32], role_ids: Vec<[u8;32]>)->DispatchResult; - fn scope_exists(pallet_id: u64, scope_id:&[u8;32]) -> DispatchResult; - fn permission_exists(pallet_id: u64, permission_id: &[u8;32])->DispatchResult; - fn is_role_linked_to_pallet(pallet_id: u64, role_id: &[u8;32])-> DispatchResult; - fn is_permission_linked_to_role(pallet_id: u64, role_id: &[u8;32], permission_id: &[u8;32])-> DispatchResult; - fn get_role_users_len(pallet_id: u64, scope_id:&[u8;32], role_id: &[u8;32]) -> usize; - fn get_role_id(id_or_role: IdOrString)->Result<[u8;32], DispatchError>; - fn get_permission(pallet_id: u64 ,id_or_permission: IdOrString< Self::PermissionMaxLen>)->Result<[u8;32], DispatchError>; + fn is_authorized(user: AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; + fn has_role(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; + fn scope_exists(pallet_id: u64, scope_id:&ScopeId) -> DispatchResult; + fn permission_exists(pallet_id: u64, permission_id: &PermissionId)->DispatchResult; + fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult; + fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; + fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize; fn has_unique_elements(vec: Vec) -> bool; } \ No newline at end of file From b512b9ef822a36c8fb3416a43675b7d97f3a7379 Mon Sep 17 00:00:00 2001 From: didiermis Date: Fri, 5 Aug 2022 13:33:15 -0500 Subject: [PATCH 056/103] Modify storage maps. Update insertions. Update helper functions used to remove data. --- pallets/gated-marketplace/src/functions.rs | 111 +++++++++++++-------- pallets/gated-marketplace/src/lib.rs | 69 +++++++++---- runtime/src/lib.rs | 6 +- 3 files changed, 121 insertions(+), 65 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index e82c5be7..0876ac78 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -5,6 +5,7 @@ use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; use frame_support::traits::{Currency, Contains}; +use frame_support::traits::ExistenceRequirement::KeepAlive; impl Pallet { @@ -17,7 +18,6 @@ impl Pallet { Self::insert_in_auth_market_lists(owner.clone(), MarketplaceAuthority::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceAuthority::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); - Self::deposit_event(Event::MarketplaceStored(owner, admin, marketplace_id)); Ok(()) } @@ -151,7 +151,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { //TODO: ensure the user is a Marketparticipant //ensure the marketplace exists @@ -172,14 +172,14 @@ impl Pallet { // add 7 days to the timestamp let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); - //create offer_id - let offer_iid = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); - + //create an offer_sale_id + let offer_sale_id = (collection_id, item_id).using_encoded(blake2_256); + let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); + let _total = T::LocalCurrency::total_balance(&authority); //create offer structure - let _offer_data = OfferData:: { - //offer_id: offer_iid, + let offer_data = OfferData:: { marketplace_id: marketplace_id, creator: authority.clone(), price: price, @@ -189,21 +189,31 @@ impl Pallet { offer_type: offer_type, }; - //insert offer in offer_id + //insert in TrackSellOffers //validate offer already exists - ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); - >::insert(collection_id, item_id, offer_iid); + ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); + >::insert(collection_id, item_id, offer_sale_id); + + //insert in OffersBySaleId + >::try_mutate(offer_sale_id, |offer| { + offer.try_push((marketplace_id, offer_id)) + }).map_err(|_| Error::::OfferAlreadyExists)?; + + //insert in OfferInfo + // validate offer already exists + ensure!(!>::contains_key(offer_id), Error::::OfferAlreadyExists); + >::insert(offer_id, offer_data); - //insert offer in offer_data - //validate offer_info already exists - ensure!(!>::contains_key(offer_iid, marketplace_id), Error::::OfferAlreadyExists); - >::insert(offer_iid, marketplace_id, _offer_data.clone()); + //Insert in OffersByMarketplace + >::try_mutate(marketplace_id, |offer| { + offer.try_push(offer_id) + }).map_err(|_| Error::::OfferAlreadyExists)?; Self::deposit_event(Event::OfferStored(collection_id, item_id)); Ok(()) } - pub fn do_take_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_sell_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; @@ -212,14 +222,15 @@ impl Pallet { //ensure owner is not the same as the buyer ensure!(owner_item != authority.clone(), Error::::CannotTakeOffer); - //ensure the selected item has an offer_id - ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + //ensure the selected item has an offer_sale_id + ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); //ensure the offer is open and available ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); //TODO: Add transfer from currency trait - T::LocalCurrency::transfer(&authority, &owner_item, Self::get_offer_price(offer_id, marketplace_id), AllowDeath::No)?; + let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; + T::LocalCurrency::transfer(&authority, &owner_item, price_item, KeepAlive)?; //TODO: add transfer from uniques pallet_uniques::Pallet::::do_transfer(collection_id, item_id, authority.clone(), |_, _|{ Ok(()) @@ -228,12 +239,11 @@ impl Pallet { //TODO: ensure the offer is not expired //TODO: update offer status from all marketplaces - Self::update_offer_status(offer_id)?; + Self::update_offer_status(collection_id, item_id)?; //TODO: remove the offer from all marketplace - Self::delete_offer(collection_id, item_id, offer_id)?; + Self::delete_offer(collection_id, item_id)?; - //TODO: create a new storage map offerid-boundedvec(marketplace_id) // to facilitate the search of the marketplaces where the offer is stored @@ -268,7 +278,6 @@ impl Pallet { } fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceAuthority, marketplace_id: [u8;32])->DispatchResult{ - >::try_mutate(authority.clone(), marketplace_id, |account_auths|{ account_auths.try_push(role) }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; @@ -491,7 +500,7 @@ impl Pallet { fn get_offer_status(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferStatus{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id, marketplace_id) { + if let Some(offer) = >::get(offer_id) { return offer.status; } else { return OfferStatus::NotFound; @@ -501,28 +510,37 @@ impl Pallet { fn _get_offer_type(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferType{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id, marketplace_id) { + if let Some(offer) = >::get(offer_id) { return offer.offer_type; } else { return OfferType::NotFound; } } - pub fn update_offer_status(offer_id: [u8;32]) -> DispatchResult{ + pub fn update_offer_status(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ + //1. get the offer_sell_id + let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; - let marketplaces_list = >::iter_prefix(offer_id).map(|(k1, _k2)|{ - k1 - }); - - for ele in marketplaces_list { - >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ - let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; - offer.status = OfferStatus::Closed; - Ok(()) + //2. update the status from all marketplaces to closed + for ele in >::get(offer_sell_id){ + >::try_mutate::<_,_,DispatchError,_>(ele.1, |offer|{ + let offer_map = offer.as_mut().ok_or(Error::::OfferNotFound)?; + offer_map.status = OfferStatus::Closed; + Ok(()) })?; } + // let marketplaces_list = >::iter_prefix(offer_id).map(|(k1, _k2)|{ + // k1 + // }); - //Try this optimization next time. + // for ele in marketplaces_list { + // >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ + // let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; + // offer.status = OfferStatus::Closed; + // Ok(()) + // })?; + // } + // Try this optimization next time. // for ele in >::iter_prefix(offer_id).map(|(k1, _k2)|{ // k1 // }){ @@ -535,19 +553,28 @@ impl Pallet { Ok(()) } - pub fn delete_offer(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult{ - //remove from OffersData list - >::remove_prefix(offer_id, None); - //remove from OffersId list - >::remove(collection_id, item_id); + pub fn delete_offer(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ + let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; + + for ele in >::get(offer_sell_id){ + //delete from OffersByMarketplace -> market_id + // delete from OfferInfo -> offer_id + >::remove(ele.0); + >::remove(ele.1); + } + + //remove from OffersBySellId2 -> offer_sell_id + >::remove(offer_sell_id); + //remove from TrackSellOffers + >::remove(collection_id, item_id); Ok(()) } - pub fn get_offer_price(offer_id: [u8;32], marketplace_id : [u8;32]) -> Result { + pub fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id, marketplace_id) { + if let Some(offer) = >::get(offer_id) { return Ok(offer.price); } else { return Err(Error::::OfferNotFound)?; diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 556093af..73dd8ee4 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -52,7 +52,9 @@ pub mod pallet { #[pallet::constant] type MaxApplicationsPerCustodian: Get; #[pallet::constant] - type MaxMarketsPerOffer: Get; + type MaxMarketsPerItem: Get; + #[pallet::constant] + type MaxOffersPerMarket: Get; } #[pallet::pallet] @@ -144,34 +146,60 @@ pub mod pallet { #[pallet::storage] - #[pallet::getter(fn offers_id)] - pub(super) type OffersId = StorageDoubleMap< + #[pallet::getter(fn offer_sell_id)] + pub(super) type TrackSellOffers = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, //collection_id Blake2_128Concat, T::ItemId, // item_id - [u8;32], // offer_id + [u8;32], // offer_sale_id OptionQuery >; + // #[pallet::storage] + // #[pallet::getter(fn offer_by_sell_id)] + // pub(super) type OffersBySellId = StorageDoubleMap< + // _, + // Blake2_128Concat, + // [u8; 32], // offer_sale_id + // Blake2_128Concat, + // [u8;32], // marketplace_id + // [u8;32], // offer_id + // OptionQuery, + // >; + #[pallet::storage] - #[pallet::getter(fn offers_data)] - pub(super) type OffersData = StorageDoubleMap< + #[pallet::getter(fn offer_by_sell_id2)] + pub(super) type OffersBySellId2 = StorageMap< _, - Blake2_128Concat, - [u8;32], //offer_id - Blake2_128Concat, - [u8;32], //marketplace_id - //BoundedVec<[u8;32], T::MaxMarketsPerOffer>, //marketplace_id - OfferData, //offer data - OptionQuery + Identity, + [u8; 32], // offer_sale_id + BoundedVec<([u8;32], [u8;32]), T::MaxOffersPerMarket>, // matkerplace_id / offer_id + ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn offer_info)] + pub(super) type OfferInfo = StorageMap< + _, + Identity, + [u8; 32], // offer_id + //StorageDoubleMap -> marketplace_id(?) + OfferData, // offer data + OptionQuery, + >; - - - + #[pallet::storage] + #[pallet::getter(fn offers_by_marketplace)] + pub(super) type OffersByMarketplace = StorageMap< + _, + Identity, + [u8; 32], // Marketplace_id + BoundedVec<[u8;32], T::MaxOffersPerMarket>, // offer_id's + //StorageDoubleMap -> offer data(?) + ValueQuery, + >; #[pallet::event] @@ -486,21 +514,20 @@ pub mod pallet { #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_enlist_offer(who, marketplace_id, collection_id, item_id, offer_type, price) + Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, offer_type, price) } //TODO: Add extrinsic to take an offer. - #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn take_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_offer(who, offer_id, marketplace_id, collection_id, item_id) + Self::do_take_sell_offer(who, offer_id, marketplace_id, collection_id, item_id) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c38f01fb..b8ad137a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -552,7 +552,8 @@ parameter_types! { pub const NameMaxLen: u32 = 100; pub const MaxFiles: u32 = 10; pub const MaxApplicationsPerCustodian: u32 = 10; - pub const MaxMarketsPerOffer: u32 = 10; + pub const MaxMarketsPerItem: u32 = 10; + pub const MaxOffersPerMarket: u32 = 100; } impl pallet_gated_marketplace::Config for Runtime { type Event = Event; @@ -569,7 +570,8 @@ impl pallet_gated_marketplace::Config for Runtime { type NameMaxLen= NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; - type MaxMarketsPerOffer = MaxMarketsPerOffer; + type MaxMarketsPerItem = MaxMarketsPerItem; + type MaxOffersPerMarket = MaxOffersPerMarket; //type Balance = u128; type LocalCurrency = Balances; From 83634d8ec2a96313ed1ba72102edb399b51d8436 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 8 Aug 2022 18:43:40 -0500 Subject: [PATCH 057/103] updates rbac documentation and fixes typos --- pallets/rbac/README.md | 48 ++++++++++++++++++++++++++++++++++- pallets/rbac/src/functions.rs | 4 +-- pallets/rbac/src/types.rs | 2 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/pallets/rbac/README.md b/pallets/rbac/README.md index 8d751a42..e0617811 100644 --- a/pallets/rbac/README.md +++ b/pallets/rbac/README.md @@ -1 +1,47 @@ -License: Unlicense \ No newline at end of file +# Role-Based Access Control (RBAC) +Restrict access to custom pallets by coupling this pallet. Create roles and assign permissions. + +- [Role-Based Access Control (RBAC)](#role-based-access-control-rbac) + - [Overview](#overview) + - [Terminology](#terminology) + - [Interface](#interface) + - [Helper functions](#helper-functions) + - [Getters](#getters) + +## Overview +This module allows to +- Define roles grouping them by the runtime pallet index. +- Assign permissions to roles. +- Create scopes, each of them will have an independent list of users. +- Assign roles to users within defined scopes. +- Ask if a user has certain permission, the pallet will search which roles the user has and will determine if its authorized. +- Remove roles from users, scopes and the entire storage assigned to an external pallet. + + +## Terminology +- **Scope**: A group of users with one or more roles, scopes are delimited and categorized by the pallet that created it. +- **Role**: A group of permissions, the RBAC pallet has a global list of roles to avoid data redundancy, however, only the selected roles will be assigned (or created if they don't exist) to the pallet. +- **Permission**: The bottom level filter, permissions are stored and categorized by pallet, and it is highly recommended each restricted extrinsic have its own permission. +- **Pallet index**: a unique number that serves as an identifier, as it is assigned automatically to a pallet when its instantiated in the runtime. + +## Interface + +### Helper functions +This module is intended to use with conjunction of a pallet which loosely couples it, due to that, the pallet doesn't expose any extrinsic. However, the implementation of `RoleBasedAccessControl` has numerous helper functions that allow a flexible roles management. + +- `create_scope` inserts a scope within a external pallet context using its index. +- `remove_scope` deletes all role lists linked to that scope. +- `remove_pallet_storage` deletes all role lists and permissions associated with the pallet. +- `create_and_set_roles` is the recommended first step for setting up the role access for the pallet, as it takes the pallet index and a list of roles to be created (and assigned) in encoded string format. +- `create_role` inserts a role in the global role list and return a generated `role_id`, if its already in the list, it won't perform the id generation and will return the previously stored one instead. It is important to mention that this function won't assign the role to any pallet. +- `set_role_to_pallet` assigns a previously created role to a pallet. +- `set_multiple_pallet_roles` assigns multiple, previously created roles to a pallet. +- `assign_role_to_user` assigns a role to a user in a scope context. The role needs to be previously created and assigned to that pallet. After this function is executed, the specified user will have additional capabilities according to the role. +- `remove_role_from_user` removes a specified role from a user in a scope context. After this function is executed, the user will no longer be able to enforce the removed role and its permissions. +- `create_and_set_permissions` a good second step for enabling role access to the coupled pallet, as it creates and assigns a list of permissions to a role in a pallet context. +- `create_permission` inserts a permission in a pallet context, after this function is executed, the permission is not yet assigned to any role. +- `set_permission_to_role` assigns a previously created permission to a role in a pallet context. +- `set_multiple_permissions_to_role` assigns multiple, previously created permission to a role. + +### Getters +- \ No newline at end of file diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 7bd14acd..08b42498 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -153,7 +153,7 @@ impl RoleBasedAccessControl for Pallet{ for permision in permissions{ permission_ids.push( Self::create_permission(pallet_id, permision.to_owned())? ); } - Self::set_multiple_permisions_to_role(pallet_id, role_id, permission_ids.clone())?; + Self::set_multiple_permissions_to_role(pallet_id, role_id, permission_ids.clone())?; let b_permissions = Self::bound(permission_ids, Error::::ExceedMaxPermissionsPerRole)?; Ok(b_permissions) } @@ -181,7 +181,7 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - fn set_multiple_permisions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ + fn set_multiple_permissions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index d8db058b..023f1b3c 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -38,7 +38,7 @@ pub trait RoleBasedAccessControl{ Result, DispatchError>; fn create_permission(pallet_id: u64, permissions: Vec) -> Result; fn set_permission_to_role( pallet_id: u64, role: RoleId, permission: PermissionId ) -> DispatchResult; - fn set_multiple_permisions_to_role( pallet_id: u64, role: RoleId, permission: Vec )-> DispatchResult; + fn set_multiple_permissions_to_role( pallet_id: u64, role: RoleId, permission: Vec )-> DispatchResult; // helpers fn is_authorized(user: AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; fn has_role(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; From b59c3beafbc77ae4a4ec9e9764bf29fe36c7f03e Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 8 Aug 2022 20:19:57 -0500 Subject: [PATCH 058/103] update flow to delete a particular offer --- pallets/gated-marketplace/src/functions.rs | 100 ++++++++++++++++++--- pallets/gated-marketplace/src/lib.rs | 42 +++++++-- pallets/gated-marketplace/src/types.rs | 2 +- 3 files changed, 127 insertions(+), 17 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 0876ac78..603f090e 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -172,8 +172,8 @@ impl Pallet { // add 7 days to the timestamp let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); - //create an offer_sale_id - let offer_sale_id = (collection_id, item_id).using_encoded(blake2_256); + //create an offer_sell_id + let offer_sell_id = (collection_id, item_id).using_encoded(blake2_256); let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); let _total = T::LocalCurrency::total_balance(&authority); @@ -192,10 +192,10 @@ impl Pallet { //insert in TrackSellOffers //validate offer already exists ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); - >::insert(collection_id, item_id, offer_sale_id); + >::insert(collection_id, item_id, offer_sell_id); //insert in OffersBySaleId - >::try_mutate(offer_sale_id, |offer| { + >::try_mutate(offer_sell_id, |offer| { offer.try_push((marketplace_id, offer_id)) }).map_err(|_| Error::::OfferAlreadyExists)?; @@ -222,7 +222,7 @@ impl Pallet { //ensure owner is not the same as the buyer ensure!(owner_item != authority.clone(), Error::::CannotTakeOffer); - //ensure the selected item has an offer_sale_id + //ensure the selected item has an offer_sell_id ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); //ensure the offer is open and available @@ -241,8 +241,11 @@ impl Pallet { //TODO: update offer status from all marketplaces Self::update_offer_status(collection_id, item_id)?; - //TODO: remove the offer from all marketplace - Self::delete_offer(collection_id, item_id)?; + //TODO: remove the offer from all marketplaces + //we don't need to delete the info from all marketplaces, just + // remove from TrackSellOffers & OffersBySellId2 + + //TODO: create a new storage map offerid-boundedvec(marketplace_id) // to facilitate the search of the marketplaces where the offer is stored @@ -251,8 +254,79 @@ impl Pallet { Ok(()) } + pub fn do_duplicate_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult{ + //ensure new marketplace_id exits + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //ensure that the offer_id exists + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + + //ensure that the offer has assigned an offer_sell_id. Get the offer_sell_id + let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; + + //get the offer data + let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + // if let Some(a) = >::try_get(offer_id).ok(){ + // let offer = a; + // //modify + // } + + //modify the offer data + copy_offer_data.price = modified_price; + + //generate a new offer_id + let new_offer_id = (marketplace_id, authority.clone(), collection_id, copy_offer_data.creation_date, copy_offer_data.expiration_date).using_encoded(blake2_256); + + //add the new offer to the offers by sell id + >::try_mutate(offer_sell_id, |offer| { + offer.try_push((marketplace_id, new_offer_id)) + }).map_err(|_| Error::::OfferAlreadyExists)?; + + //add the new offer to the offers by marketplace + + >::try_mutate(marketplace_id, |offer| { + offer.try_push(new_offer_id) + }).map_err(|_| Error::::OfferAlreadyExists)?; + + // insert the new offer to the offer info + >::insert(new_offer_id, copy_offer_data); + Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); + Ok(()) + } + + pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, ) -> DispatchResult { + //ensure the offer_id exists + let copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + + ensure!(copy_offer_data.creator == authority.clone(), Error::::CannotRemoveOffer); + + //remove the offer from OfferInfo + >::remove(offer_id); + + //remove the offer from OffersByMarketplace + >::try_mutate(marketplace_id, |offers| { + let offers_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + offers.remove(offers_index); + Ok(()) + }).map_err(|_| Error::::OfferNotFound)?; + + //remove the offer from OffersBySellId + //get the offer_sell_id + let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; + >::try_mutate(offer_sell_id, |offers| { + let offers_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + offers.remove(offers_index); + Ok(()) + }).map_err(|_| Error::::OfferNotFound)?; + + + Ok(()) + } + + + /*---- Helper functions ----*/ pub fn set_up_application( @@ -553,13 +627,17 @@ impl Pallet { Ok(()) } - pub fn delete_offer(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ + pub fn delete_offer_data(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; for ele in >::get(offer_sell_id){ - //delete from OffersByMarketplace -> market_id + //delete from OffersByMarketplace -> market_id -> offer_id // delete from OfferInfo -> offer_id - >::remove(ele.0); + let _vector_offers_id = >::try_mutate(ele.0, |offers|{ + let offers_index = offers.iter().position(|x| *x == ele.1).ok_or(Error::::OfferNotFound)?; + offers.remove(offers_index); + Ok(()) + }).map_err(|_:Error::| Error::::OfferNotFound)?; >::remove(ele.1); } @@ -585,4 +663,6 @@ impl Pallet { + + } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 73dd8ee4..8cccfcba 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -153,7 +153,7 @@ pub mod pallet { T::CollectionId, //collection_id Blake2_128Concat, T::ItemId, // item_id - [u8;32], // offer_sale_id + [u8;32], // offer_sell_id OptionQuery >; @@ -162,7 +162,7 @@ pub mod pallet { // pub(super) type OffersBySellId = StorageDoubleMap< // _, // Blake2_128Concat, - // [u8; 32], // offer_sale_id + // [u8; 32], // offer_sell_id // Blake2_128Concat, // [u8;32], // marketplace_id // [u8;32], // offer_id @@ -174,11 +174,13 @@ pub mod pallet { pub(super) type OffersBySellId2 = StorageMap< _, Identity, - [u8; 32], // offer_sale_id + [u8; 32], // offer_sell_id BoundedVec<([u8;32], [u8;32]), T::MaxOffersPerMarket>, // matkerplace_id / offer_id ValueQuery, >; + //Try yo convert this one into a StorageNMap + #[pallet::storage] #[pallet::getter(fn offer_info)] pub(super) type OfferInfo = StorageMap< @@ -201,6 +203,8 @@ pub mod pallet { ValueQuery, >; + + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -223,6 +227,8 @@ pub mod pallet { OfferStored(T::CollectionId, T::ItemId), /// Offer was transferred to the specified account. [offer_id, account] OfferTransferred([u8;32], T::AccountId), + /// Offer was duplicated. [new_offer_id, new_marketplace_id] + OfferDuplicated([u8;32], [u8;32]), } // Errors inform users that something went wrong. @@ -288,6 +294,8 @@ pub mod pallet { OfferIsNotAvailable, /// Owner cannnot buy its own offer CannotTakeOffer, + /// User cannot remove the offer from the marketplace + CannotRemoveOffer, } #[pallet::call] @@ -392,9 +400,6 @@ pub mod pallet { Self::do_apply(who, custodian, marketplace_id, application) } - - - /// Accept or reject an application. /// @@ -530,6 +535,31 @@ pub mod pallet { Self::do_take_sell_offer(who, offer_id, marketplace_id, collection_id, item_id) } + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn duplicate_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult { + let who = ensure_signed(origin.clone())?; + + Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) + } + + //TODO: REMOVE OFFER + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn remove_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + let who = ensure_signed(origin.clone())?; + + Self::do_remove_offer(who, offer_id, marketplace_id, collection_id, item_id) + } + + + // #[transactional] + // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + // pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + // let who = ensure_signed(origin)?; + // Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, offer_type, price) + // } + //TODO: Add CRUD operations for the offers //TODO: Add extrinsic to duplicate offers in other marketplace. diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index ccb875be..a7b4a433 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -79,7 +79,6 @@ pub enum OfferStatus{ Freezed, Closed, NotFound, - Expired, } impl Default for OfferStatus{ @@ -106,4 +105,5 @@ pub struct OfferData{ pub creation_date: u64, pub expiration_date: u64, pub offer_type: OfferType, + //pub buyer: Option, } From ea11bb809535588a811a407bc9acbd753bfb6226 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 9 Aug 2022 12:44:51 -0500 Subject: [PATCH 059/103] Adds rbac cli tutorial in readme Sets up rbac parameters as constants. removes unused dependencies --- pallets/rbac/README.md | 179 +++++++++++++++++++++++++++++++++- pallets/rbac/src/functions.rs | 10 +- pallets/rbac/src/lib.rs | 14 +-- pallets/rbac/src/types.rs | 3 +- 4 files changed, 184 insertions(+), 22 deletions(-) diff --git a/pallets/rbac/README.md b/pallets/rbac/README.md index e0617811..666101b7 100644 --- a/pallets/rbac/README.md +++ b/pallets/rbac/README.md @@ -7,6 +7,18 @@ Restrict access to custom pallets by coupling this pallet. Create roles and assi - [Interface](#interface) - [Helper functions](#helper-functions) - [Getters](#getters) + - [Constants](#constants) + - [Usage](#usage) + - [Loosely coupling RBAC with another pallet](#loosely-coupling-rbac-with-another-pallet) + - [Querying with Polkadot-js CLI](#querying-with-polkadot-js-cli) + - [Get pallet scopes](#get-pallet-scopes) + - [Get role by its id](#get-role-by-its-id) + - [Get all role ids linked to a pallet](#get-all-role-ids-linked-to-a-pallet) + - [Get a permission by pallet and id](#get-a-permission-by-pallet-and-id) + - [Get permissions linked to a role within a pallet](#get-permissions-linked-to-a-role-within-a-pallet) + - [Get which roles the user has in a pallet scope](#get-which-roles-the-user-has-in-a-pallet-scope) + - [Get which users have the role in a pallet scope](#get-which-users-have-the-role-in-a-pallet-scope) + - [Querying with Polkadot-js API (js library)](#querying-with-polkadot-js-api-js-library) ## Overview This module allows to @@ -22,12 +34,12 @@ This module allows to - **Scope**: A group of users with one or more roles, scopes are delimited and categorized by the pallet that created it. - **Role**: A group of permissions, the RBAC pallet has a global list of roles to avoid data redundancy, however, only the selected roles will be assigned (or created if they don't exist) to the pallet. - **Permission**: The bottom level filter, permissions are stored and categorized by pallet, and it is highly recommended each restricted extrinsic have its own permission. -- **Pallet index**: a unique number that serves as an identifier, as it is assigned automatically to a pallet when its instantiated in the runtime. +- **Pallet index**: a unique number that serves as an identifier, as it is assigned automatically to a pallet when its instantiated in the runtime. The term is interchangeable with pallet id. ## Interface ### Helper functions -This module is intended to use with conjunction of a pallet which loosely couples it, due to that, the pallet doesn't expose any extrinsic. However, the implementation of `RoleBasedAccessControl` has numerous helper functions that allow a flexible roles management. +This module is intended to be used in conjunction with a pallet which loosely couples it, due to that, the pallet doesn't expose any extrinsic. However, the implementation of the `RoleBasedAccessControl` trait has numerous helper functions that allow a flexible roles management. - `create_scope` inserts a scope within a external pallet context using its index. - `remove_scope` deletes all role lists linked to that scope. @@ -41,7 +53,166 @@ This module is intended to use with conjunction of a pallet which loosely couple - `create_and_set_permissions` a good second step for enabling role access to the coupled pallet, as it creates and assigns a list of permissions to a role in a pallet context. - `create_permission` inserts a permission in a pallet context, after this function is executed, the permission is not yet assigned to any role. - `set_permission_to_role` assigns a previously created permission to a role in a pallet context. -- `set_multiple_permissions_to_role` assigns multiple, previously created permission to a role. +- `set_multiple_permissions_to_role` assigns multiple, previously created permissions to a role in a pallet context. +- `is_authorized` is the suggested authorization mechanism, as it takes the pallet index, scope and the requested permission to be enforced. This function will search the users permissions and will validate if there's a role that has the permission enabled. + - `has_role` a secondary authorization mechanism that takes the pallet index, scope, and a set of roles that the user tentatively has. This method is specially useful when its unclear which roles the user has and any of the specified roles will suffice the authorization. + - `scope_exists` a validation function used internally by other methods, ensure the requested scope is registered in the specified pallet. + - `permission_exists` is a validation function used internally, as it provides, as it confirms if the permission is stored in the specified pallet. + - `is_role_linked_to_pallet` validates if a role is registered in the pallet. This method doesn't validates if the role has been previously created and assumes it is. + - `is_permission_linked_to_role` ensures the specified permission is linked to the role in a pallet context. This method assumes both the role and permission exists. + - `get_role_users_len` returns the number of users that have the specified role, useful when implementing restrictions on the number of users that can have that role. ### Getters -- \ No newline at end of file +- `scopes` +- `roles` +- `pallet_roles` +- `permissions` (storage double map) +- `permissions_by_role` (storage double map) +- `roles_by_user` (storage N map with 3 keys) +- `users_by_scope` (storage N map with 3 keys) + + +### Constants +- `MaxScopesPerPallet: Get` +- `MaxRolesPerPallet: Get` +- `RoleMaxLen: Get` +- `PermissionMaxLen: Get` +- `MaxPermissionsPerRole: Get` +- `MaxRolesPerUser: Get` +- `MaxUsersPerRole: Get` + +## Usage + +### Loosely coupling RBAC with another pallet +Once the RBAC pallet is imported and configured in the runtime, the first step is to import the `RoleBasedAccessControl` trait from the rbac types into the custom pallet, and declare a type within the pallet configuration: +```rust +use pallet_rbac::types::RoleBasedAccessControl; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + // ... + type Rbac : RoleBasedAccessControl; + } +``` + +Then the RBAC pallet can safely be imported as a parameter within another pallet, for example, `gated_marketplaces`: + +```rust +impl pallet_gated_marketplace::Config for Runtime { + type Event = Event; + // ... + type Rbac = RBAC; +} +``` + +Now all the previously mentioned functions are accessible within the custom pallet: +```rust +let create_scope_result : DispatchResult = T::Rbac::create_scope(pallet_id,marketplace_id); +``` + +### Querying with Polkadot-js CLI +As previously stated, this pallet doesn't expose any extrinsics, but rather expose a collection of helper functions that are accessible by any custom pallet that couples it. Therefore, the following section assumes theres a basic RBAC configuration stored on chain. + +#### Get pallet scopes +```bash +# pallet_id +polkadot-js-api query.rbac.scopes 20 +``` +```bash +# Expected output +{ + "scopes": [ + "0x112a94197eb935a48b13ac5e6d37d316a143dd3dcf725c9d9d27d64dbba62890" + ] +} +``` + +#### Get role by its id +```bash +# role_id +polkadot-js-api query.rbac.roles 0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b +``` +```bash +# Expected output: +{ + "roles": "Owner" +} +``` + +#### Get all role ids linked to a pallet + +```bash +# pallet_id +polkadot-js-api query.rbac.palletRoles 20 +``` +```bash +# Expected output +{ + "palletRoles": [ + "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b", + "0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01", + "0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b" + ] +} +``` + +#### Get a permission by pallet and id +```bash +# pallet_id, permission_id +polkadot-js-api query.rbac.permissions 20 0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674 +``` +```bash +# Expected output +{ + "permissions": "Enroll" +} +``` + +#### Get permissions linked to a role within a pallet +```bash +# pallet_id, permission_id +polkadot-js-api query.rbac.permissionsByRole 20 0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b +``` +```bash +# Expected output +{ + "permissionsByRole": [ + "0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674", + "0x2c40feed7853568ca1cb5f852636359f8cc8dc82108191397cb7b8ad90a1d0a1", + "0x78dcd6644c3f21fd1872659dcb32c58af797c5c06963fb2ea0937b8d24479815", + "0xbe1f77a2f9266a2dbaa4858ec7aa3933da37346e96a7968c99870d15552d51a5", + "0x599314a6cceabfd08491d4847fe78ad0e932340ff1877704376890aa6ddb045c" + ] +} +``` + +#### Get which roles the user has in a pallet scope +```bash +# account_id, pallet_id, scope_id +polkadot-js-api query.rbac.rolesByUser 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY 20 0x112a94197eb935a48b13ac5e6d37d316a143dd3dcf725c9d9d27d64dbba62890 +``` +```bash +# Expected output +{ + "rolesByUser": [ + "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b" + ] +} +``` + +#### Get which users have the role in a pallet scope +```bash +# pallet_id, scope_id, role_id +polkadot-js-api query.rbac.usersByScope 20 0x112a94197eb935a48b13ac5e6d37d316a143dd3dcf725c9d9d27d64dbba62890 0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b +``` +```bash +# Expected output +{ + "usersByScope": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + ] +} +``` + +### Querying with Polkadot-js API (js library) \ No newline at end of file diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 08b42498..35452097 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -1,6 +1,6 @@ use super::*; use frame_support::{pallet_prelude::*}; -use frame_system::pallet_prelude::*; +//use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use frame_support::sp_std::borrow::ToOwned; use sp_runtime::sp_std::vec::Vec; @@ -246,14 +246,6 @@ impl RoleBasedAccessControl for Pallet{ >::get((pallet_id, scope_id, role_id)).len() } - fn has_unique_elements(vec: Vec) -> bool{ - let mut filtered_vec = vec.clone(); - filtered_vec.sort(); - filtered_vec.dedup(); - vec.len() == filtered_vec.len() - } - - type MaxRolesPerPallet = T::MaxRolesPerPallet; type MaxPermissionsPerRole = T::MaxPermissionsPerRole; diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index ac9445f1..de836983 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -29,19 +29,19 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; - + #[pallet::constant] type MaxScopesPerPallet: Get; - + #[pallet::constant] type MaxRolesPerPallet: Get; - + #[pallet::constant] type RoleMaxLen: Get; - + #[pallet::constant] type PermissionMaxLen: Get; - + #[pallet::constant] type MaxPermissionsPerRole: Get; - + #[pallet::constant] type MaxRolesPerUser: Get; - + #[pallet::constant] type MaxUsersPerRole: Get; } diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 023f1b3c..712058b7 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -1,4 +1,4 @@ -use super::*; +//use super::*; use frame_support::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; @@ -47,6 +47,5 @@ pub trait RoleBasedAccessControl{ fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult; fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize; - fn has_unique_elements(vec: Vec) -> bool; } \ No newline at end of file From 93ccf3d672177c18d66dc93afb1dada54246b8d2 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 9 Aug 2022 17:05:18 -0500 Subject: [PATCH 060/103] update workflow to version5 --- pallets/gated-marketplace/src/functions.rs | 320 ++++++++++++--------- pallets/gated-marketplace/src/lib.rs | 66 ++--- pallets/gated-marketplace/src/mock.rs | 7 +- pallets/gated-marketplace/src/types.rs | 4 +- 4 files changed, 220 insertions(+), 177 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 603f090e..25796828 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -164,45 +164,42 @@ impl Pallet { Err(Error::::CollectionNotFound)?; } - //TODO: use a helper function to handle timestamping - // create a timestamp - //let time: u64 = T::TimeProvider::now().as_secs(); - let timestamp: ::Moment = >::get(); - let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); - // add 7 days to the timestamp - let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); + //Add timestamp to offer + let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; //create an offer_sell_id - let offer_sell_id = (collection_id, item_id).using_encoded(blake2_256); - let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp2, timestamp3).using_encoded(blake2_256); - - let _total = T::LocalCurrency::total_balance(&authority); + //TODO: create an offer id generator, used in cases where the offer_id generated is not unique + let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp, timestamp2).using_encoded(blake2_256); //create offer structure let offer_data = OfferData:: { marketplace_id: marketplace_id, + collection_id: collection_id, + item_id: item_id, creator: authority.clone(), price: price, - creation_date: timestamp2, - expiration_date: timestamp3, + creation_date: timestamp, + expiration_date: timestamp2, status: OfferStatus::Open, offer_type: offer_type, + buyer: None, }; - //insert in TrackSellOffers - //validate offer already exists - ensure!(!>::contains_key(collection_id, item_id), Error::::OfferAlreadyExists); - >::insert(collection_id, item_id, offer_sell_id); + //insert in OffersByItem + Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + >::try_mutate(collection_id, item_id, |offers| { + offers.try_push(offer_id) + }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; - //insert in OffersBySaleId - >::try_mutate(offer_sell_id, |offer| { - offer.try_push((marketplace_id, offer_id)) + //insert in OffersByAccount + >::try_mutate(authority.clone(), |offer| { + offer.try_push(offer_id) }).map_err(|_| Error::::OfferAlreadyExists)?; - //insert in OfferInfo + //insert in OffersInfo // validate offer already exists - ensure!(!>::contains_key(offer_id), Error::::OfferAlreadyExists); - >::insert(offer_id, offer_data); + ensure!(!>::contains_key(offer_id), Error::::OfferAlreadyExists); + >::insert(offer_id, offer_data); //Insert in OffersByMarketplace >::try_mutate(marketplace_id, |offer| { @@ -213,44 +210,45 @@ impl Pallet { Ok(()) } - pub fn do_take_sell_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; - - //TODO: ensure the offer type //ensure owner is not the same as the buyer - ensure!(owner_item != authority.clone(), Error::::CannotTakeOffer); + ensure!(owner_item != buyer.clone(), Error::::CannotTakeOffer); - //ensure the selected item has an offer_sell_id - ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + //ensure the selected item has a valid offer_id in OffersInfo + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + + //ensure the offer_id exists in OffersByItem + Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //ensure the offer is open and available ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); - //TODO: Add transfer from currency trait + //Transfer balance to the seller let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; - T::LocalCurrency::transfer(&authority, &owner_item, price_item, KeepAlive)?; - //TODO: add transfer from uniques - pallet_uniques::Pallet::::do_transfer(collection_id, item_id, authority.clone(), |_, _|{ + let total_user_balance = T::LocalCurrency::total_balance(&buyer); + ensure!(total_user_balance >= price_item, Error::::NotEnoughBalance); + //Transfer the balance + T::LocalCurrency::transfer(&buyer, &owner_item, price_item, KeepAlive)?; + + //Use uniques transfer to transfer the item to the buyer + pallet_uniques::Pallet::::do_transfer(collection_id, item_id, buyer.clone(), |_, _|{ Ok(()) })?; //TODO: ensure the offer is not expired - //TODO: update offer status from all marketplaces - Self::update_offer_status(collection_id, item_id)?; + //update offer status from all marketplaces + Self::update_offer_status(buyer.clone(), collection_id, item_id, marketplace_id)?; - //TODO: remove the offer from all marketplaces - //we don't need to delete the info from all marketplaces, just - // remove from TrackSellOffers & OffersBySellId2 - - - - //TODO: create a new storage map offerid-boundedvec(marketplace_id) - // to facilitate the search of the marketplaces where the offer is stored + //remove all SellOrder offer types from OffersByItem so the item it's available to beign sold again + Self::delete_all_sell_orders_for_this_item(collection_id, item_id )?; + + //TODO: add the offer_id from this offer to the buyer's history - Self::deposit_event(Event::OfferTransferred(offer_id, authority)); + Self::deposit_event(Event::OfferTransferred(offer_id, buyer)); Ok(()) } @@ -259,37 +257,42 @@ impl Pallet { ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); //ensure that the offer_id exists - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - //ensure that the offer has assigned an offer_sell_id. Get the offer_sell_id - let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; + //ensure the offer_id exists in OffersByItem + Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //get the offer data - let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - // if let Some(a) = >::try_get(offer_id).ok(){ - // let offer = a; - // //modify - // } + let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; //modify the offer data + //by know we only allow to modify its price by the user + //we modify its marketplace_id because the offer is duplicated to another marketplace copy_offer_data.price = modified_price; + copy_offer_data.marketplace_id = marketplace_id; //generate a new offer_id let new_offer_id = (marketplace_id, authority.clone(), collection_id, copy_offer_data.creation_date, copy_offer_data.expiration_date).using_encoded(blake2_256); - //add the new offer to the offers by sell id - >::try_mutate(offer_sell_id, |offer| { - offer.try_push((marketplace_id, new_offer_id)) - }).map_err(|_| Error::::OfferAlreadyExists)?; - - //add the new offer to the offers by marketplace + //insert in OffersInfo + // validate new offer_id does not exists + ensure!(!>::contains_key(new_offer_id), Error::::OfferAlreadyExists); + >::insert(new_offer_id, copy_offer_data); + //insert in OffersByMarketplace >::try_mutate(marketplace_id, |offer| { offer.try_push(new_offer_id) + }).map_err(|_| Error::::OfferAlreadyExists)?; + + //insert in OffersByAccount + >::try_mutate(authority.clone(), |offer| { + offer.try_push(new_offer_id) }).map_err(|_| Error::::OfferAlreadyExists)?; - - // insert the new offer to the offer info - >::insert(new_offer_id, copy_offer_data); + + //add the new offer_id to OffersByItem + >::try_mutate(collection_id, item_id, |offers| { + offers.try_push(new_offer_id) + }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); @@ -297,27 +300,40 @@ impl Pallet { } pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, ) -> DispatchResult { - //ensure the offer_id exists - let copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + //ensure marketplace_id exits + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //ensure the offer_id exists & get the offer data + let copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + // ensure the owner is the same as the authority ensure!(copy_offer_data.creator == authority.clone(), Error::::CannotRemoveOffer); + //ensure the offer_id exists in OffersByItem + Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + + //remove the offer from OfferInfo - >::remove(offer_id); + >::remove(offer_id); //remove the offer from OffersByMarketplace >::try_mutate(marketplace_id, |offers| { - let offers_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; - offers.remove(offers_index); + let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + offers.remove(offer_index); Ok(()) }).map_err(|_| Error::::OfferNotFound)?; - //remove the offer from OffersBySellId - //get the offer_sell_id - let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; - >::try_mutate(offer_sell_id, |offers| { - let offers_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; - offers.remove(offers_index); + //remove the offer from OffersByAccount + >::try_mutate(authority.clone(), |offers| { + let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + offers.remove(offer_index); + Ok(()) + }).map_err(|_| Error::::OfferNotFound)?; + + //remove the offer from OffersByItem + >::try_mutate(collection_id, item_id, |offers| { + let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + offers.remove(offer_index); Ok(()) }).map_err(|_| Error::::OfferNotFound)?; @@ -566,7 +582,7 @@ impl Pallet { if let Some(_date_as_u64) = TryInto::::try_into(date).ok() { date_as_u64_millis = _date_as_u64; } else { - return Err(DispatchError::Other("Unable to convert Moment to i64 for date")); + return Err(Error::::TimestampError)?; } return Ok(date_as_u64_millis); } @@ -574,93 +590,127 @@ impl Pallet { fn get_offer_status(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferStatus{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id) { + if let Some(offer) = >::get(offer_id) { return offer.status; } else { return OfferStatus::NotFound; } } - fn _get_offer_type(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferType{ + fn update_offer_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ + let offer_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + for offer_id in offer_ids { + >::try_mutate::<_,_,DispatchError,_>(offer_id, |offer|{ + let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; + offer.status = OfferStatus::Closed; + offer.buyer = Some((buyer.clone(), marketplace_id)); + Ok(()) + })?; + + } + Ok(()) + } + + //fn delete_offer_data_from_whole_marketplace(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ + + // Ok(()) + // } + + fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id) { - return offer.offer_type; + if let Some(offer) = >::get(offer_id) { + return Ok(offer.price); } else { - return OfferType::NotFound; + return Err(Error::::OfferNotFound)?; } } - pub fn update_offer_status(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ - //1. get the offer_sell_id - let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; + fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { + let timestamp: ::Moment = >::get(); + let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); + let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); - //2. update the status from all marketplaces to closed - for ele in >::get(offer_sell_id){ - >::try_mutate::<_,_,DispatchError,_>(ele.1, |offer|{ - let offer_map = offer.as_mut().ok_or(Error::::OfferNotFound)?; - offer_map.status = OfferStatus::Closed; - Ok(()) - })?; - } - // let marketplaces_list = >::iter_prefix(offer_id).map(|(k1, _k2)|{ - // k1 - // }); - - // for ele in marketplaces_list { - // >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ - // let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; - // offer.status = OfferStatus::Closed; - // Ok(()) - // })?; - // } - // Try this optimization next time. - // for ele in >::iter_prefix(offer_id).map(|(k1, _k2)|{ - // k1 - // }){ - // >::try_mutate::<_,_,_,DispatchError,_>(offer_id, ele, |offer|{ - // let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; - // offer.status = OfferStatus::Closed; - // Ok(()) - // })?; - // } - Ok(()) + Some((timestamp2, timestamp3)) } - pub fn delete_offer_data(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ - let offer_sell_id = >::get(collection_id, item_id).ok_or(Error::::OfferNotFound)?; - for ele in >::get(offer_sell_id){ - //delete from OffersByMarketplace -> market_id -> offer_id - // delete from OfferInfo -> offer_id - let _vector_offers_id = >::try_mutate(ele.0, |offers|{ - let offers_index = offers.iter().position(|x| *x == ele.1).ok_or(Error::::OfferNotFound)?; - offers.remove(offers_index); - Ok(()) - }).map_err(|_:Error::| Error::::OfferNotFound)?; - >::remove(ele.1); + fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { + let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + + for offer in offers { + let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; + //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. + if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { + return Err(Error::::OfferAlreadyExists)?; + } } - - //remove from OffersBySellId2 -> offer_sell_id - >::remove(offer_sell_id); - //remove from TrackSellOffers - >::remove(collection_id, item_id); Ok(()) } - pub fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { - //we already know that the offer exists, so we don't need to check it here. - //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id) { - return Ok(offer.price); - } else { - return Err(Error::::OfferNotFound)?; - } + fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { + let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + //find the offer_id in the vector of offers_ids + offers.iter().find(|&x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + Ok(()) } + fn delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { + //This could be easier if we had a separate storage source for offer_id where offer_type = SellOrder. + // let offers_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + // let mut offers_ids_to_delete: Vec<[u8;32]> = Vec::new(); + + // for offer_id in offers_ids { + // let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + // //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. + // if offer_info.offer_type == OfferType::SellOrder { + // offers_ids_to_delete.push(offer_id); + // } + // } + // for offer_id in offers_ids_to_delete { + // >::try_mutate(collection_id, item_id, |offers|{ + // let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + // offers.remove(offer_index); + // Ok(()) + // }).map_err(|_:Error::| Error::::OfferNotFound)?; + // } + + //I think an alternative it could be the following: + // delete the offer_ids where offer_type == SellOrder, in the offers_ids vector. + // then insert a new boundedvec with the remaining offer_ids. + + //Im not sure if the cicle should be there or outsider. Because everytime we delete an offer_id, + //the vector is updated (?). + // >::try_mutate(collection_id, item_id, |offers|{ + // for offer_id in offers_ids_to_delete { + // let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + // offers.remove(offer_index); + // } + // Ok(()) + // }).map_err(|_:Error::| Error::::OfferNotFound)?; + + //ensure the item has offers associated with it. + ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + let offers_ids = >::take(collection_id, item_id); + //let mut remaining_offer_ids: Vec<[u8;32]> = Vec::new(); + let mut buy_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket>; + + for offer_id in offers_ids { + let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. + if offer_info.offer_type != OfferType::SellOrder { + buy_offer_ids.try_push(offer_id); + } + } + + //ensure we already took the entry from the storage map, so we can insert it again. + ensure!(!>::contains_key(collection_id, item_id), Error::::OfferNotFound); + >::insert(collection_id, item_id, buy_offer_ids); + Ok(()) + } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 8cccfcba..af8b4946 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -144,52 +144,26 @@ pub mod pallet { ValueQuery >; - #[pallet::storage] - #[pallet::getter(fn offer_sell_id)] - pub(super) type TrackSellOffers = StorageDoubleMap< + #[pallet::getter(fn offers_by_item)] + pub(super) type OffersByItem = StorageDoubleMap< _, Blake2_128Concat, T::CollectionId, //collection_id Blake2_128Concat, - T::ItemId, // item_id - [u8;32], // offer_sell_id - OptionQuery - >; - - // #[pallet::storage] - // #[pallet::getter(fn offer_by_sell_id)] - // pub(super) type OffersBySellId = StorageDoubleMap< - // _, - // Blake2_128Concat, - // [u8; 32], // offer_sell_id - // Blake2_128Concat, - // [u8;32], // marketplace_id - // [u8;32], // offer_id - // OptionQuery, - // >; - - #[pallet::storage] - #[pallet::getter(fn offer_by_sell_id2)] - pub(super) type OffersBySellId2 = StorageMap< - _, - Identity, - [u8; 32], // offer_sell_id - BoundedVec<([u8;32], [u8;32]), T::MaxOffersPerMarket>, // matkerplace_id / offer_id + T::ItemId, //item_id + BoundedVec<[u8;32], T::MaxOffersPerMarket>, // offer_id's ValueQuery, >; - //Try yo convert this one into a StorageNMap - #[pallet::storage] - #[pallet::getter(fn offer_info)] - pub(super) type OfferInfo = StorageMap< + #[pallet::getter(fn offers_by_account)] + pub(super) type OffersByAccount = StorageMap< _, Identity, - [u8; 32], // offer_id - //StorageDoubleMap -> marketplace_id(?) - OfferData, // offer data - OptionQuery, + T::AccountId, // account_id + BoundedVec<[u8;32], T::MaxOffersPerMarket>, // offer_id's + ValueQuery, >; #[pallet::storage] @@ -199,12 +173,21 @@ pub mod pallet { Identity, [u8; 32], // Marketplace_id BoundedVec<[u8;32], T::MaxOffersPerMarket>, // offer_id's - //StorageDoubleMap -> offer data(?) ValueQuery, >; - + #[pallet::storage] + #[pallet::getter(fn offers_info)] + pub(super) type OffersInfo = StorageMap< + _, + Identity, + [u8; 32], // offer_id + //StorageDoubleMap -> marketplace_id(?) + OfferData, // offer data + OptionQuery, + >; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -296,6 +279,10 @@ pub mod pallet { CannotTakeOffer, /// User cannot remove the offer from the marketplace CannotRemoveOffer, + /// Error related to the timestamp + TimestampError, + /// User does not have enough balance to buy the offer + NotEnoughBalance, } #[pallet::call] @@ -526,7 +513,6 @@ pub mod pallet { } - //TODO: Add extrinsic to take an offer. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { @@ -543,10 +529,12 @@ pub mod pallet { Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) } - //TODO: REMOVE OFFER + #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn remove_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + //Currently, we can only remove one offer at a time. + //TODO: Add support for removing multiple offers at a time. let who = ensure_signed(origin.clone())?; Self::do_remove_offer(who, offer_id, marketplace_id, collection_id, item_id) diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index d9a91316..267859a2 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -69,7 +69,9 @@ parameter_types! { pub const NameMaxLen: u32 = 100; pub const MaxFiles: u32 = 10; pub const MaxApplicationsPerCustodian: u32 = 2; - pub const MaxMarketsPerOfferor: u32 = 10; + pub const MaxMarketsPerItem: u32 = 10; + + pub const MaxOffersPerMarket: u32 = 100; } impl pallet_gated_marketplace::Config for Test { @@ -84,7 +86,8 @@ impl pallet_gated_marketplace::Config for Test { type NameMaxLen = NameMaxLen; type MaxFiles = MaxFiles; type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; - type MaxMarketsPerOffer = MaxMarketsPerOfferor; + type MaxOffersPerMarket = MaxOffersPerMarket; + type MaxMarketsPerItem = MaxMarketsPerItem; type LocalCurrency = Balances; } diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index a7b4a433..d9be97f2 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -99,11 +99,13 @@ pub enum OfferType{ #[codec(mel_bound())] pub struct OfferData{ pub marketplace_id: [u8;32], + pub collection_id: T::CollectionId, + pub item_id: T::ItemId, pub creator: T::AccountId, pub price: BalanceOf, pub status: OfferStatus, pub creation_date: u64, pub expiration_date: u64, pub offer_type: OfferType, - //pub buyer: Option, + pub buyer: Option<(T::AccountId, [u8;32])>, } From 3bdb415470979f5a29b617c866e55208873ee28c Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 9 Aug 2022 17:25:43 -0500 Subject: [PATCH 061/103] update workflow to version5 --- pallets/gated-marketplace/src/functions.rs | 14 +++++++------- pallets/gated-marketplace/src/lib.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 25796828..16fa7a15 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -224,7 +224,7 @@ impl Pallet { Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //ensure the offer is open and available - ensure!(Self::get_offer_status(offer_id, marketplace_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); + ensure!(Self::get_offer_status(offer_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); //Transfer balance to the seller let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; @@ -321,21 +321,21 @@ impl Pallet { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) - }).map_err(|_| Error::::OfferNotFound)?; + }).map_err(|_:Error::| Error::::OfferNotFound)?; //remove the offer from OffersByAccount >::try_mutate(authority.clone(), |offers| { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) - }).map_err(|_| Error::::OfferNotFound)?; + }).map_err(|_:Error::| Error::::OfferNotFound)?; //remove the offer from OffersByItem >::try_mutate(collection_id, item_id, |offers| { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) - }).map_err(|_| Error::::OfferNotFound)?; + }).map_err(|_:Error::| Error::::OfferNotFound)?; Ok(()) @@ -587,7 +587,7 @@ impl Pallet { return Ok(date_as_u64_millis); } - fn get_offer_status(offer_id: [u8;32], marketplace_id : [u8;32]) -> OfferStatus{ + fn get_offer_status(offer_id: [u8;32],) -> OfferStatus{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. if let Some(offer) = >::get(offer_id) { @@ -695,13 +695,13 @@ impl Pallet { ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); let offers_ids = >::take(collection_id, item_id); //let mut remaining_offer_ids: Vec<[u8;32]> = Vec::new(); - let mut buy_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket>; + let mut buy_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); for offer_id in offers_ids { let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. if offer_info.offer_type != OfferType::SellOrder { - buy_offer_ids.try_push(offer_id); + buy_offer_ids.try_push(offer_id).map_err(|_| Error::::LimitExceeded)?; } } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index af8b4946..b34cedeb 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -221,6 +221,8 @@ pub mod pallet { NotYetImplemented, /// Error names should be descriptive. NoneValue, + ///Limit bounded vector exceeded + LimitExceeded, /// The account supervises too many marketplaces ExceedMaxMarketsPerAuth, /// The account has too many roles in that marketplace @@ -271,8 +273,6 @@ pub mod pallet { OfferAlreadyExists, /// Offer not found OfferNotFound, - /// Offer status is freezed, user cannot take the current offer - OfferIsFreezed, /// Offer is not available at the moment OfferIsNotAvailable, /// Owner cannnot buy its own offer From c6cceff637c97ea1ab3c0b2f5841c92a32bcdad9 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 9 Aug 2022 17:52:46 -0500 Subject: [PATCH 062/103] adds rbac overview using polkadot api rbac errors cleanup --- pallets/rbac/README.md | 323 +++++++++++++++++++++++++++++++++- pallets/rbac/src/functions.rs | 2 +- pallets/rbac/src/lib.rs | 2 - 3 files changed, 322 insertions(+), 5 deletions(-) diff --git a/pallets/rbac/README.md b/pallets/rbac/README.md index 666101b7..0d273d6e 100644 --- a/pallets/rbac/README.md +++ b/pallets/rbac/README.md @@ -19,6 +19,22 @@ Restrict access to custom pallets by coupling this pallet. Create roles and assi - [Get which roles the user has in a pallet scope](#get-which-roles-the-user-has-in-a-pallet-scope) - [Get which users have the role in a pallet scope](#get-which-users-have-the-role-in-a-pallet-scope) - [Querying with Polkadot-js API (js library)](#querying-with-polkadot-js-api-js-library) + - [Get pallet scopes](#get-pallet-scopes-1) + - [Get all pallet scopes](#get-all-pallet-scopes) + - [Get role by its id](#get-role-by-its-id-1) + - [Get all stored roles](#get-all-stored-roles) + - [Get all role ids linked to a pallet](#get-all-role-ids-linked-to-a-pallet-1) + - [Get all role ids linked to all pallets](#get-all-role-ids-linked-to-all-pallets) + - [Get a permission by pallet and id](#get-a-permission-by-pallet-and-id-1) + - [Get all permissions from a pallet](#get-all-permissions-from-a-pallet) + - [Get permissions linked to a role within a pallet](#get-permissions-linked-to-a-role-within-a-pallet-1) + - [Get all role permissions from a pallet](#get-all-role-permissions-from-a-pallet) + - [Get which roles the user has in a pallet scope](#get-which-roles-the-user-has-in-a-pallet-scope-1) + - [Get which roles the user has in a pallet scope](#get-which-roles-the-user-has-in-a-pallet-scope-2) + - [Get which roles the user has in all scopes from a pallet](#get-which-roles-the-user-has-in-all-scopes-from-a-pallet) + - [Get which users have the role in a pallet scope](#get-which-users-have-the-role-in-a-pallet-scope-1) + - [Get scope users by role](#get-scope-users-by-role) + - [Errors](#errors) ## Overview This module allows to @@ -171,7 +187,7 @@ polkadot-js-api query.rbac.permissions 20 0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc3 #### Get permissions linked to a role within a pallet ```bash -# pallet_id, permission_id +# pallet_id, role_id polkadot-js-api query.rbac.permissionsByRole 20 0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b ``` ```bash @@ -215,4 +231,307 @@ polkadot-js-api query.rbac.usersByScope 20 0x112a94197eb935a48b13ac5e6d37d316a14 } ``` -### Querying with Polkadot-js API (js library) \ No newline at end of file +### Querying with Polkadot-js API (js library) +The javascript version of the API offers more versatile queries. Again, this section assumes the RBAC pallet has been previously pre-populated with data from another custom pallet. + +#### Get pallet scopes +```js +// pallet_id +const scopes = await api.query.rbac.scopes(20); +console.log(scopes.toHuman()); +``` +```bash +# Expected output +[ + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' +] +``` + +#### Get all pallet scopes +```js +const all_scopes = await api.query.rbac.scopes.entries(); +all_scopes.forEach(([key, exposure]) => { + console.log('key pallet_id:', key.args.map((k) => k.toHuman())); + console.log(' scopes:', exposure.toHuman()); +}); +``` +```bash +# Expected output: +key pallet_id: [ '20' ] + scopes: ['0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95'] +``` + +#### Get role by its id +```js +// role_id +const roles = await api.query.rbac.roles("0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b"); +console.log(roles.toHuman()); +``` +```bash +# Expected output +Owner +``` + +#### Get all stored roles +```js +const all_roles = await api.query.rbac.roles.entries(); +all_roles.forEach(([key, exposure]) => { + console.log('role_id:', key.args.map((k) => k.toHuman())); + console.log(' name:', exposure.toHuman()); +}); +``` +```bash +role_id: ['0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b'] + name: Owner +role_id: ['0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b'] + name: Participant +role_id: ['0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01'] + name: Admin +``` + +#### Get all role ids linked to a pallet +```js +// pallet_id +const pallet_roles = await api.query.rbac.palletRoles(20); +console.log(pallet_roles.toHuman()); +``` +```bash +# Expected output +[ + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b', + '0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01', + '0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b' +] +``` + +#### Get all role ids linked to all pallets +```js +const all_pallet_roles = await api.query.rbac.palletRoles.entries(); +all_pallet_roles.forEach(([key, exposure]) => { + console.log('pallet_id:', key.args.map((k) => k.toHuman())); + console.log(' role_ids:', exposure.toHuman()); +}); +``` +```bash +# Expected output +pallet_id: [ '20' ] + role_ids: [ + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b', + '0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01', + '0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b' +] +``` + +#### Get a permission by pallet and id +```js +// pallet_id, permission_id +const permission = await api.query.rbac.permissions(20, "0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674"); +console.log(permission.toHuman()); +``` +```bash +# Expected output +Enroll +``` + +#### Get all permissions from a pallet +```js +// the pallet_id can be omitted to get all permissions from all pallets +const all_pallet_permissions = await api.query.rbac.permissions.entries(20); +all_pallet_permissions.forEach(([key, exposure]) => { + console.log('pallet_id and permission_id:', key.args.map((k) => k.toHuman())); + console.log(' permission:', exposure.toHuman()); +}); +``` +```bash +# Expected output +pallet_id and permission_id: [ + '20', + '0x2c40feed7853568ca1cb5f852636359f8cc8dc82108191397cb7b8ad90a1d0a1' +] + permission: AddAuth +pallet_id and permission_id: [ + '20', + '0xbe1f77a2f9266a2dbaa4858ec7aa3933da37346e96a7968c99870d15552d51a5' +] + permission: UpdateLabel +pallet_id and permission_id: [ + '20', + '0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674' +] + permission: Enroll +``` + +#### Get permissions linked to a role within a pallet +```js +// pallet_id, role_id +const permissionsByRole = await api.query.rbac.permissionsByRole(20, "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b"); +console.log(permissionsByRole.toHuman()); +``` +```bash +# Expected output +[ + '0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674', + '0x2c40feed7853568ca1cb5f852636359f8cc8dc82108191397cb7b8ad90a1d0a1', + '0x78dcd6644c3f21fd1872659dcb32c58af797c5c06963fb2ea0937b8d24479815', + '0xbe1f77a2f9266a2dbaa4858ec7aa3933da37346e96a7968c99870d15552d51a5', + '0x599314a6cceabfd08491d4847fe78ad0e932340ff1877704376890aa6ddb045c' +] +``` + +#### Get all role permissions from a pallet +```js +// the pallet_id can be omitted to get all permissions from all pallets by role +const all_pallet_permissions_by_role = await api.query.rbac.permissionsByRole.entries(20); +all_pallet_permissions_by_role.forEach(([key, exposure]) => { + console.log('pallet_id:', key.args.map((k) => k.toHuman())); + console.log(' permission_ids', exposure.toHuman()); +}); +``` +```bash +# Output should look like this +pallet_id: [ + '20', + '0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b' +] + permission_ids: [ + '0xf010b3ffd94e992da28d394e7c065514710383a75508decaaead76e99d6ec4fc', + '0x70ff830f1d86d3f63ebf39fb1270fcab37abab1668a8fc7a5e18c9b1f0b793c2' +] +pallet_id: [ + '20', + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b' +] + permission_ids: [ + '0xdd2f4fc1f525a38ab2f18b2ef4ff4559ddc344d04aa2ceaec1f5d0c6b4f67674', + '0x2c40feed7853568ca1cb5f852636359f8cc8dc82108191397cb7b8ad90a1d0a1' +] +``` + +#### Get which roles the user has in a pallet scope +```js +// account_id, pallet_id, scope_id +const rolesByUser = await api.query.rbac.rolesByUser(alice.address,20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); +console.log(rolesByUser.toHuman()); +``` +```bash +# Output should look like this +['0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b'] +``` + +#### Get which roles the user has in a pallet scope +```js +// account_id, pallet_id, scope_id +const rolesByUser = await api.query.rbac.rolesByUser(alice.address,20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); +console.log(rolesByUser.toHuman()); +``` +```bash +# Output should look like this +['0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b'] +``` + +#### Get which roles the user has in all scopes from a pallet +```js +// The pallet_id can be omitted to get all roles the user has from all pallets. +// Both account_id and pallet_id can be omitted all roles from all users, categorized by pallet_id +const all_roles_by_user = await api.query.rbac.rolesByUser.entries(alice.address, 20); +all_roles_by_user.forEach(([key, exposure]) => { + console.log('account_id, pallet_id, scope_id:', key.args.map((k) => k.toHuman())); + console.log(' role_ids', exposure.toHuman()); +}); +``` +```bash +# Output should look like this +account_id, pallet_id, scope_id: [ + '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + '20', + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' +] + role_ids [ + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b' +] +``` + + +#### Get which users have the role in a pallet scope +```js +// pallet_id, scope_id, role_id +const usersByScope = await api.query.rbac.usersByScope(20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b"); +console.log(usersByScope.toHuman()); +``` +```bash +# Expected output +[ '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' ] +``` + +#### Get scope users by role +```js +// The scope_id could be omitted to get all users by role of all pallet scopes. +// The pallet_id and scope_id could be omitted to get a global list of users categorized by pallet, scope, and role. +const scope_users_by_role = await api.query.rbac.usersByScope.entries(20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); +scope_users_by_role.forEach(([key, exposure]) => { + console.log('pallet_id, scope_id, role_id:', key.args.map((k) => k.toHuman())); + console.log(' account_id', exposure.toHuman()); +}); +``` +```bash +# Output should look like this +pallet_id, scope_id, role_id: [ + '20', + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b' +] + account_id [ '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' ] +pallet_id, scope_id, role_id: [ + '20', + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', + '0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01' +] + account_id [ '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty' ] +``` + +## Errors + +```rust +/// Error names should be descriptive. +NoneValue, +/// The specified scope doesn't exists +ScopeNotFound, +/// The scope is already linked with the pallet +ScopeAlreadyExists, +/// The specified role doesn't exist +RoleNotFound, +/// The permission doesn't exist in the pallet +PermissionNotFound, +/// The specified user hasn't been asigned to this scope +UserNotFound, +/// The role is already linked in the pallet +DuplicateRole, +/// The permission is already linked to that role in that scope +DuplicatePermission, +/// The user has that role asigned in that scope +UserAlreadyHasRole, +/// The role exists but it hasn't been linked to the pallet +RoleNotLinkedToPallet, +/// The permission wasn't found in the roles capabilities +PermissionNotLinkedToRole, +/// The user doesn't have any roles in this pallet +UserHasNoRoles, +/// The role doesn't have any users assigned to it +RoleHasNoUsers, +/// The pallet has too many scopes +ExceedMaxScopesPerPallet, +/// The pallet cannot have more roles +ExceedMaxRolesPerPallet, +/// The specified role cannot have more permission in this scope +ExceedMaxPermissionsPerRole, +/// The user cannot have more roles in this scope +ExceedMaxRolesPerUser, +/// This role cannot have assigned to more users in this scope +ExceedMaxUsersPerRole, +/// The role string is too long +ExceedRoleMaxLen, +/// The permission string is too long +ExceedPermissionMaxLen, +/// The user does not have the specified role +NotAuthorized, +``` \ No newline at end of file diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 35452097..63cce72a 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -80,7 +80,7 @@ impl RoleBasedAccessControl for Pallet{ fn create_role(role: Vec)-> Result{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found - let b_role = Self::bound::<_,T::RoleMaxLen>(role, Error::::ExceedMaxRolesPerUser)?; + let b_role = Self::bound::<_,T::RoleMaxLen>(role, Error::::ExceedRoleMaxLen)?; ensure!(role_id == b_role.using_encoded(blake2_256), Error::::NoneValue); if !>::contains_key(role_id) {>::insert(role_id, b_role)}; Ok(role_id) diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index de836983..cc981391 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -149,8 +149,6 @@ pub mod pallet { pub enum Error { /// Error names should be descriptive. NoneValue, - /// The pallet doesn't have scopes associated - PalletNotFound, /// The specified scope doesn't exists ScopeNotFound, /// The scope is already linked with the pallet From 823098b352981586b4f17531929258e18a224bb4 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 14:29:51 -0500 Subject: [PATCH 063/103] Remove unused imports, comments and functions. --- pallets/gated-marketplace/src/functions.rs | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 16fa7a15..c7f3ffba 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,10 +1,10 @@ use super::*; -use frame_support::{pallet_prelude::*, traits::LockableCurrency}; +use frame_support::{pallet_prelude::*}; //use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -use frame_support::traits::{Currency, Contains}; +use frame_support::traits::{Currency}; use frame_support::traits::ExistenceRequirement::KeepAlive; impl Pallet { @@ -187,9 +187,11 @@ impl Pallet { //insert in OffersByItem Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + >::try_mutate(collection_id, item_id, |offers| { offers.try_push(offer_id) }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; + //TODO: chnage error messagem, it isn't the right one //insert in OffersByAccount >::try_mutate(authority.clone(), |offer| { @@ -587,6 +589,8 @@ impl Pallet { return Ok(date_as_u64_millis); } + //TODO: change this function to ask if the offertype consulted is the same as the one in the application + //signature: offer_id, offertype that we want to check. fn get_offer_status(offer_id: [u8;32],) -> OfferStatus{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. @@ -611,10 +615,6 @@ impl Pallet { Ok(()) } - //fn delete_offer_data_from_whole_marketplace(collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult{ - - // Ok(()) - // } fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { //we already know that the offer exists, so we don't need to check it here. @@ -636,17 +636,23 @@ impl Pallet { fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { - let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; - - for offer in offers { - let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; - //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. - if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - return Err(Error::::OfferAlreadyExists)?; + let offers = >::get(collection_id, item_id); + + //if len is == 0, it means that there is no offers for this item, maybe it's the first entry + if offers.len() == 0 { + return Ok(()) + } else if offers.len() > 0 { + for offer in offers { + let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; + //ensure the offer_type is SellOrder, because this vector also contains buy offers. + if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { + return Err(Error::::OfferAlreadyExists)?; + } } - } + } Ok(()) + } fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { From 8d7f8ad18ebacf4b657c4c83de4685d4dcbe17bc Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:04:39 -0500 Subject: [PATCH 064/103] Delete NotFound enums, they are no longer needed --- pallets/gated-marketplace/src/functions.rs | 13 +++++++------ pallets/gated-marketplace/src/lib.rs | 9 +++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index c7f3ffba..b262065f 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -226,7 +226,7 @@ impl Pallet { Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //ensure the offer is open and available - ensure!(Self::get_offer_status(offer_id) == OfferStatus::Open, Error::::OfferIsNotAvailable); + ensure!(Self::ask_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); //Transfer balance to the seller let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; @@ -589,18 +589,19 @@ impl Pallet { return Ok(date_as_u64_millis); } - //TODO: change this function to ask if the offertype consulted is the same as the one in the application - //signature: offer_id, offertype that we want to check. - fn get_offer_status(offer_id: [u8;32],) -> OfferStatus{ + + fn ask_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ //we already know that the offer exists, so we don't need to check it here. //we have added a NotFound status in case the storage source is corrupted. if let Some(offer) = >::get(offer_id) { - return offer.status; + return offer.status == offer_status; } else { - return OfferStatus::NotFound; + return false; } + } + fn update_offer_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ let offer_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; for offer_id in offer_ids { diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index b34cedeb..efbf6032 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -512,6 +512,15 @@ pub mod pallet { Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, offer_type, price) } + // #[transactional] + // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + // pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + // let who = ensure_signed(origin)?; + + // Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, offer_type, price) + // } + + #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] From dab785c3c0325ce538c6ccf4b6f653a0d282b992 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:04:46 -0500 Subject: [PATCH 065/103] Delete NotFound enums, they are no longer needed --- pallets/gated-marketplace/src/types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index d9be97f2..59a347bf 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -78,7 +78,6 @@ pub enum OfferStatus{ Open, Freezed, Closed, - NotFound, } impl Default for OfferStatus{ @@ -91,7 +90,6 @@ impl Default for OfferStatus{ pub enum OfferType{ SellOrder, BuyOrder, - NotFound, } #[derive(CloneNoBound, Encode, Decode, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen,)] From 26ef1a0a2b255fa20cd22ba5320ef63695238833 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:10:43 -0500 Subject: [PATCH 066/103] add new error CannotDeleteOffer when a user tries to delete a closed offer --- pallets/gated-marketplace/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index efbf6032..d0a15eb8 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -283,6 +283,8 @@ pub mod pallet { TimestampError, /// User does not have enough balance to buy the offer NotEnoughBalance, + /// User cannot delete the offer because is closed + CannotDeleteOffer, } #[pallet::call] From 218787b771a79dd65b8925a92c1d117f733eefc3 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:11:08 -0500 Subject: [PATCH 067/103] add validation to prevent users to delete an open offer --- pallets/gated-marketplace/src/functions.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index b262065f..82678967 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -314,6 +314,9 @@ impl Pallet { //ensure the offer_id exists in OffersByItem Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + //ensure the offer status is Open + ensure!(Self::ask_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); + //remove the offer from OfferInfo >::remove(offer_id); From a9a3b2df24f88546a72982916a40e7b91e3529f5 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:51:03 -0500 Subject: [PATCH 068/103] chamge error message to OfferStorageError in case the storagemap were corrupted or becasuse the system could not store the offer --- pallets/gated-marketplace/src/functions.rs | 48 ++++------------------ 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 82678967..a086780a 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -164,7 +164,7 @@ impl Pallet { Err(Error::::CollectionNotFound)?; } - //Add timestamp to offer + //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; //create an offer_sell_id @@ -190,13 +190,13 @@ impl Pallet { >::try_mutate(collection_id, item_id, |offers| { offers.try_push(offer_id) - }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; + }).map_err(|_| Error::::OfferStorageError)?; //TODO: chnage error messagem, it isn't the right one //insert in OffersByAccount >::try_mutate(authority.clone(), |offer| { offer.try_push(offer_id) - }).map_err(|_| Error::::OfferAlreadyExists)?; + }).map_err(|_| Error::::OfferStorageError)?; //insert in OffersInfo // validate offer already exists @@ -206,7 +206,7 @@ impl Pallet { //Insert in OffersByMarketplace >::try_mutate(marketplace_id, |offer| { offer.try_push(offer_id) - }).map_err(|_| Error::::OfferAlreadyExists)?; + }).map_err(|_| Error::::OfferStorageError)?; Self::deposit_event(Event::OfferStored(collection_id, item_id)); Ok(()) @@ -284,17 +284,17 @@ impl Pallet { //insert in OffersByMarketplace >::try_mutate(marketplace_id, |offer| { offer.try_push(new_offer_id) - }).map_err(|_| Error::::OfferAlreadyExists)?; + }).map_err(|_| Error::::OfferStorageError)?; //insert in OffersByAccount >::try_mutate(authority.clone(), |offer| { offer.try_push(new_offer_id) - }).map_err(|_| Error::::OfferAlreadyExists)?; + }).map_err(|_| Error::::OfferStorageError)?; //add the new offer_id to OffersByItem >::try_mutate(collection_id, item_id, |offers| { offers.try_push(new_offer_id) - }).map_err(|_| Error::::ExceedMaxRolesPerAuth)?; + }).map_err(|_| Error::::OfferStorageError)?; Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); @@ -667,40 +667,6 @@ impl Pallet { } fn delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { - //This could be easier if we had a separate storage source for offer_id where offer_type = SellOrder. - // let offers_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; - // let mut offers_ids_to_delete: Vec<[u8;32]> = Vec::new(); - - // for offer_id in offers_ids { - // let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - // //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. - // if offer_info.offer_type == OfferType::SellOrder { - // offers_ids_to_delete.push(offer_id); - // } - // } - - // for offer_id in offers_ids_to_delete { - // >::try_mutate(collection_id, item_id, |offers|{ - // let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; - // offers.remove(offer_index); - // Ok(()) - // }).map_err(|_:Error::| Error::::OfferNotFound)?; - // } - - //I think an alternative it could be the following: - // delete the offer_ids where offer_type == SellOrder, in the offers_ids vector. - // then insert a new boundedvec with the remaining offer_ids. - - //Im not sure if the cicle should be there or outsider. Because everytime we delete an offer_id, - //the vector is updated (?). - // >::try_mutate(collection_id, item_id, |offers|{ - // for offer_id in offers_ids_to_delete { - // let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; - // offers.remove(offer_index); - // } - // Ok(()) - // }).map_err(|_:Error::| Error::::OfferNotFound)?; - //ensure the item has offers associated with it. ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); let offers_ids = >::take(collection_id, item_id); From 78c4c0b071c91e0638f1b038247a895b50316709 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 15:52:32 -0500 Subject: [PATCH 069/103] add error OfferStorageError for cases were the offer could not be stored --- pallets/gated-marketplace/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index d0a15eb8..ff3588dc 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -285,6 +285,8 @@ pub mod pallet { NotEnoughBalance, /// User cannot delete the offer because is closed CannotDeleteOffer, + /// There was a problem storing the offer + OfferStorageError, } #[pallet::call] From 484f3d26b51df79afbfb6cbffcc9515f6e9a9bbd Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 18:17:03 -0500 Subject: [PATCH 070/103] Fix bug: When a sale offer is completed, it closes all the other offers of the same item regardless of the offer type --- pallets/gated-marketplace/src/functions.rs | 73 +++++++++++++--------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index a086780a..e5cd0f78 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -243,7 +243,7 @@ impl Pallet { //TODO: ensure the offer is not expired //update offer status from all marketplaces - Self::update_offer_status(buyer.clone(), collection_id, item_id, marketplace_id)?; + Self::update_sell_offers_status(buyer.clone(), collection_id, item_id, marketplace_id)?; //remove all SellOrder offer types from OffersByItem so the item it's available to beign sold again Self::delete_all_sell_orders_for_this_item(collection_id, item_id )?; @@ -592,6 +592,13 @@ impl Pallet { return Ok(date_as_u64_millis); } + fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { + let timestamp: ::Moment = >::get(); + let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); + let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); + + Some((timestamp2, timestamp3)) + } fn ask_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ //we already know that the offer exists, so we don't need to check it here. @@ -604,10 +611,45 @@ impl Pallet { } + fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { + //we already know that the offer exists, so we don't need to check it here. + //we have added a NotFound status in case the storage source is corrupted. + if let Some(offer) = >::get(offer_id) { + return Ok(offer.price); + } else { + return Err(Error::::OfferNotFound)?; + } + } + + fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { + let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + //find the offer_id in the vector of offers_ids + offers.iter().find(|&x| *x == offer_id).ok_or(Error::::OfferNotFound)?; + Ok(()) + } + + //sell orders here... - fn update_offer_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ + fn update_sell_offers_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ let offer_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; + + let mut sell_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); + for offer_id in offer_ids { + let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. + if offer_info.offer_type == OfferType::SellOrder { + sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; + } + // //version 2 + // if let Some(offer) = >::get(offer_id) { + // if offer.offer_type == OfferType::SellOrder { + // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; + // } + // } + } + + for offer_id in sell_offer_ids { >::try_mutate::<_,_,DispatchError,_>(offer_id, |offer|{ let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; offer.status = OfferStatus::Closed; @@ -619,26 +661,6 @@ impl Pallet { Ok(()) } - - fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { - //we already know that the offer exists, so we don't need to check it here. - //we have added a NotFound status in case the storage source is corrupted. - if let Some(offer) = >::get(offer_id) { - return Ok(offer.price); - } else { - return Err(Error::::OfferNotFound)?; - } - } - - fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { - let timestamp: ::Moment = >::get(); - let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); - let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); - - Some((timestamp2, timestamp3)) - } - - fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { let offers = >::get(collection_id, item_id); @@ -656,15 +678,8 @@ impl Pallet { } Ok(()) - } - fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { - let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; - //find the offer_id in the vector of offers_ids - offers.iter().find(|&x| *x == offer_id).ok_or(Error::::OfferNotFound)?; - Ok(()) - } fn delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { //ensure the item has offers associated with it. From 7b50a47e25084ebb79d153df6b332141992ce6ff Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 10 Aug 2022 19:07:08 -0500 Subject: [PATCH 071/103] Add validation to check if the offer price is greater than zero --- pallets/gated-marketplace/src/functions.rs | 18 ++++++++++++++++++ pallets/gated-marketplace/src/lib.rs | 2 ++ 2 files changed, 20 insertions(+) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index e5cd0f78..f94b2724 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -164,6 +164,10 @@ impl Pallet { Err(Error::::CollectionNotFound)?; } + //ensure the price is valid + Self::is_the_price_valid(price)?; + + //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; @@ -628,6 +632,20 @@ impl Pallet { Ok(()) } + pub fn u32_to_balance(input: u32) -> BalanceOf { + input.into() + } + + fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { + let minimun_amount: BalanceOf = Self::u32_to_balance(100); + + if price > minimun_amount { + return Ok(()); + } else { + return Err(Error::::PriceMustBeGreaterThanZero)?; + } + } + //sell orders here... fn update_sell_offers_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index ff3588dc..be884220 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -287,6 +287,8 @@ pub mod pallet { CannotDeleteOffer, /// There was a problem storing the offer OfferStorageError, + /// Price must be greater than 0 + PriceMustBeGreaterThanZero, } #[pallet::call] From f1bcdff1a665a8a979e1547cceee5f5539750204 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 11 Aug 2022 11:08:00 -0500 Subject: [PATCH 072/103] Add a validation to ensure a minimum amount in the offers. --- pallets/gated-marketplace/src/functions.rs | 15 ++++----------- runtime/src/lib.rs | 3 --- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index f94b2724..948fd0e8 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -230,7 +230,7 @@ impl Pallet { Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //ensure the offer is open and available - ensure!(Self::ask_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); + ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); //Transfer balance to the seller let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; @@ -319,7 +319,7 @@ impl Pallet { Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; //ensure the offer status is Open - ensure!(Self::ask_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); + ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); //remove the offer from OfferInfo @@ -604,9 +604,8 @@ impl Pallet { Some((timestamp2, timestamp3)) } - fn ask_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ + fn is_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ //we already know that the offer exists, so we don't need to check it here. - //we have added a NotFound status in case the storage source is corrupted. if let Some(offer) = >::get(offer_id) { return offer.status == offer_status; } else { @@ -617,7 +616,6 @@ impl Pallet { fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { //we already know that the offer exists, so we don't need to check it here. - //we have added a NotFound status in case the storage source is corrupted. if let Some(offer) = >::get(offer_id) { return Ok(offer.price); } else { @@ -632,13 +630,8 @@ impl Pallet { Ok(()) } - pub fn u32_to_balance(input: u32) -> BalanceOf { - input.into() - } - fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { - let minimun_amount: BalanceOf = Self::u32_to_balance(100); - + let minimun_amount: BalanceOf = 1000u32.into(); if price > minimun_amount { return Ok(()); } else { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b8ad137a..57e00343 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -572,10 +572,7 @@ impl pallet_gated_marketplace::Config for Runtime { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxMarketsPerItem = MaxMarketsPerItem; type MaxOffersPerMarket = MaxOffersPerMarket; - //type Balance = u128; type LocalCurrency = Balances; - - } parameter_types! { From f09df42a189edbff3c98cdda0dda82ecf1ffe74d Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 11 Aug 2022 13:46:36 -0500 Subject: [PATCH 073/103] updates rbacunit tests --- pallets/rbac/README.md | 2 +- pallets/rbac/src/functions.rs | 145 +++++++++++++++++++++++++++++++++- pallets/rbac/src/mock.rs | 23 +++++- pallets/rbac/src/tests.rs | 73 ++++++++++++++--- 4 files changed, 227 insertions(+), 16 deletions(-) diff --git a/pallets/rbac/README.md b/pallets/rbac/README.md index 0d273d6e..a986be4c 100644 --- a/pallets/rbac/README.md +++ b/pallets/rbac/README.md @@ -73,7 +73,7 @@ This module is intended to be used in conjunction with a pallet which loosely co - `is_authorized` is the suggested authorization mechanism, as it takes the pallet index, scope and the requested permission to be enforced. This function will search the users permissions and will validate if there's a role that has the permission enabled. - `has_role` a secondary authorization mechanism that takes the pallet index, scope, and a set of roles that the user tentatively has. This method is specially useful when its unclear which roles the user has and any of the specified roles will suffice the authorization. - `scope_exists` a validation function used internally by other methods, ensure the requested scope is registered in the specified pallet. - - `permission_exists` is a validation function used internally, as it provides, as it confirms if the permission is stored in the specified pallet. + - `permission_exists` is a validation function used internally, as it confirms if the permission is stored in the specified pallet. - `is_role_linked_to_pallet` validates if a role is registered in the pallet. This method doesn't validates if the role has been previously created and assumes it is. - `is_permission_linked_to_role` ensures the specified permission is linked to the role in a pallet context. This method assumes both the role and permission exists. - `get_role_users_len` returns the number of users that have the specified role, useful when implementing restrictions on the number of users that can have that role. diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 63cce72a..9a8fe084 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -9,6 +9,12 @@ use crate::types::*; impl RoleBasedAccessControl for Pallet{ /*---- Basic Insertion of individual storage maps ---*/ + /// Scope creation + /// + /// Creates a scope within a external pallet using the pallet index. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The newly generated scope identifier. fn create_scope(pallet_id: u64, scope_id: ScopeId)-> DispatchResult{ let pallet_id: u64 = pallet_id.try_into().unwrap(); >::try_mutate(pallet_id, |scopes|{ @@ -18,6 +24,13 @@ impl RoleBasedAccessControl for Pallet{ }) } + /// Scope removal + /// + /// Removes a scope within a external pallet using the pallet index. + /// Executing this function will delete all registered role users. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope identifier to remove. fn remove_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult{ // remove on scopes >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ @@ -43,6 +56,14 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// External pallet storage removal + /// + /// Removes all storage associated to a external pallet. + /// + /// Executing this function will delete all role lists and permissions linked + /// to that pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. fn remove_pallet_storage(pallet_id: u64) -> DispatchResult{ //remove all scopes let scopes = >::get(pallet_id); @@ -65,7 +86,13 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } - /// Inserts roles and links them to the pallet + /// Role creation and coupling with pallet. + /// + /// Creates the specified roles if needed and adds them to the pallet. + /// Recommended first step to enable RBAC on a external pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `roles`: A list of roles to create, encoded in bytes. fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> Result, DispatchError>{ let mut role_ids= Vec::<[u8;32]>::new(); @@ -77,6 +104,12 @@ impl RoleBasedAccessControl for Pallet{ Ok(bounded_ids) } + /// Role creation. + /// + /// Creates a role and returns its identifier, if its already created, + /// the function will return the preexisting one. + /// ### Parameters: + /// - `roles`: A role to create, encoded in bytes. fn create_role(role: Vec)-> Result{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found @@ -86,6 +119,13 @@ impl RoleBasedAccessControl for Pallet{ Ok(role_id) } + /// Role coupling with pallet. + /// + /// Assigns a previously created role to a pallet. + /// + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The unique role identifier. fn set_role_to_pallet(pallet_id: u64, role_id: RoleId )-> DispatchResult{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); @@ -96,6 +136,12 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Multiple role coupling with pallet. + /// + /// Assigns multiple, previously created roles to a pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `roles`: A list of unique role identifiers. fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult{ // checks for duplicates: let pallet_roles = >::get(&pallet_id); @@ -109,6 +155,14 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Role assignation to a user + /// + /// Assigns a role to a user in a scope context. + /// ### Parameters: + /// - `user`: The account which the role will be granted. + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope in which the role will be granted. + /// - `role_id`: The role identifier to grant for the user. fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ Self::scope_exists(pallet_id, scope_id)?; Self::is_role_linked_to_pallet(pallet_id, &role_id)?; @@ -125,6 +179,15 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Role removal from the user. + /// + /// Removes the specified role from a user in a scope context. the user will no longer + /// be able to enforce the removed role and its permissions. + /// ### Parameters: + /// - `user`: The account which the role will be removed. + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope in which the role will be removed. + /// - `role_id`: The role identifier to remove from the user. fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; @@ -147,6 +210,15 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Permission creation and coupling with a role. + /// + /// Creates the specified permissions if needed and assigns them to a role. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role identifier to which the permissions will + /// be linked to. + /// - `permissions`: A list of permissions to create and link, + /// encoded in bytes. fn create_and_set_permissions(pallet_id: u64, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { let mut permission_ids = Vec::<[u8;32]>::new(); @@ -158,6 +230,12 @@ impl RoleBasedAccessControl for Pallet{ Ok(b_permissions) } + /// Permission creation + /// + /// Creates the specified permission in the specified pallet.. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `permission`: The permission to insert, encoded in bytes. fn create_permission(pallet_id: u64, permission: Vec) -> Result{ let permission_id = permission.using_encoded(blake2_256); @@ -170,6 +248,13 @@ impl RoleBasedAccessControl for Pallet{ Ok(permission_id) } + /// Permission linking to role. + /// + /// Assigns a previously created permission to a role. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role identifier to which the permission will be added. + /// - `permission_id`: The permission to assign to the role. fn set_permission_to_role( pallet_id: u64, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); Self::is_role_linked_to_pallet(pallet_id, &role_id)?; @@ -181,6 +266,14 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Multiple permissions assignation to a role + /// + /// Assigns multiple, previously created permissions + /// to a role in a pallet context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role identifier to which the permissions will be added. + /// - `permissions`: A list of permission identifiers to assign to the role. fn set_multiple_permissions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: let role_permissions = >::get(&pallet_id, role_id); @@ -196,6 +289,14 @@ impl RoleBasedAccessControl for Pallet{ /*---- Helper functions ----*/ + /// Authorization function + /// + /// Checks if the user has a role that includes the specified permission. + /// ### Parameters: + /// - `user`: The account to validate. + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope context in which the permission will be validated. + /// - `permission_id`: The permission the user must have. fn is_authorized(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ Self::scope_exists(pallet_id, scope_id)?; Self::permission_exists(pallet_id, permission_id)?; @@ -208,6 +309,14 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// User role validation function + /// + /// Checks if the user has at least one of the specified roles. + /// ### Parameters: + /// - `user`: The account to validate. + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope context in which the permission will be validated. + /// - `role_ids`: A list of roles to validate. fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { Self::scope_exists(pallet_id, scope_id)?; @@ -218,17 +327,35 @@ impl RoleBasedAccessControl for Pallet{ ); Ok(()) } - /// Also checks if pallet is stored. Need this function to expose the check to other pallets + + /// Scope validation + /// + /// Checks if the scope exists in that pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope to validate. fn scope_exists(pallet_id: u64, scope_id:&ScopeId) -> DispatchResult{ ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); Ok(()) } + /// Permission validation. + /// + /// Checks if the permission exists in a pallet context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `permission_id`: The permission to validate. fn permission_exists(pallet_id: u64, permission_id: &PermissionId)->DispatchResult{ ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); Ok(()) } + /// Role validation + /// + /// Checks if the role is linked to the pallet. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role to validate fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) @@ -236,12 +363,26 @@ impl RoleBasedAccessControl for Pallet{ Ok(()) } + /// Permission linking validation + /// + /// Checks if the permission is linked to the role in the pallet context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `role_id`: The role which should have the permission. + /// - `permission_id`: The permission which the role should have. fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ let role_permissions = >::get(pallet_id, role_id); ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); Ok(()) } + /// Role list length + /// + /// Returns the number of user that have the specified role in a scope context. + /// ### Parameters: + /// - `pallet_id`: The unique pallet identifier. + /// - `scope_id`: The scope in which the users will be retrieved. + /// - `role_id`: The role in which the number of users will be counted. fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize{ >::get((pallet_id, scope_id, role_id)).len() } diff --git a/pallets/rbac/src/mock.rs b/pallets/rbac/src/mock.rs index 733ac79d..dc94ce8b 100644 --- a/pallets/rbac/src/mock.rs +++ b/pallets/rbac/src/mock.rs @@ -1,4 +1,4 @@ -use crate as pallet_template; +use crate as pallet_rbac; use frame_support::parameter_types; use frame_system as system; use sp_core::H256; @@ -18,7 +18,7 @@ frame_support::construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - TemplateModule: pallet_template::{Pallet, Call, Storage, Event}, + RBAC: pallet_rbac::{Pallet, Call, Storage, Event}, } ); @@ -54,10 +54,25 @@ impl system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_template::Config for Test { +parameter_types! { + pub const MaxScopesPerPallet: u32 = 2; + pub const MaxRolesPerPallet: u32 = 20; + pub const RoleMaxLen: u32 = 30; + pub const PermissionMaxLen: u32 = 30; + pub const MaxPermissionsPerRole: u32 = 10; + pub const MaxRolesPerUser: u32 = 10; + pub const MaxUsersPerRole: u32 = 10; +} +impl pallet_rbac::Config for Test { type Event = Event; + type MaxScopesPerPallet = MaxScopesPerPallet; + type MaxRolesPerPallet = MaxRolesPerPallet; + type RoleMaxLen = RoleMaxLen; + type PermissionMaxLen = PermissionMaxLen; + type MaxPermissionsPerRole = MaxPermissionsPerRole; + type MaxRolesPerUser = MaxRolesPerUser; + type MaxUsersPerRole = MaxUsersPerRole; } - // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { system::GenesisConfig::default().build_storage::().unwrap().into() diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 22056586..6964170f 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,20 +1,75 @@ -use crate::{mock::*, Error}; +use crate::{mock::*, Error, types::RoleBasedAccessControl, Config}; use frame_support::{assert_noop, assert_ok}; + +const PALLET_ID : u64 = 1; + +fn create_scope(n: u8){ + assert_ok!(RBAC::create_scope(PALLET_ID, [n;32])); + assert!(RBAC::scopes(1).contains(&[n;32])); +} + +fn create_role(role: &str){ + let r_id = RBAC::create_role(role.as_bytes().to_vec()); + assert_ok!(RBAC::create_role(role.as_bytes().to_vec())); +} + +fn remove_scope(n: u8){ + assert_ok!(RBAC::remove_scope(PALLET_ID, [n;32])); + assert!(!RBAC::scopes(PALLET_ID).contains(&[n;32])); + // TODO check that other storage maps were removed too +} + +fn remove_pallet_storage(){ + assert_ok!(RBAC::remove_pallet_storage(PALLET_ID)); + // TODO: Check that other storage maps were removed too +} + +#[test] +fn create_scope_works() { + new_test_ext().execute_with(|| { + create_scope(0); + }); +} + #[test] -fn it_works_for_default_value() { +fn create_scope_twice_should_fail() { new_test_ext().execute_with(|| { - // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(Origin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(TemplateModule::something(), Some(42)); + create_scope(0); + assert_noop!(RBAC::create_scope(1, [0;32]), Error::::ScopeAlreadyExists); }); } #[test] -fn correct_error_for_none_value() { +fn create_role_should_work() { new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!(TemplateModule::cause_error(Origin::signed(1)), Error::::NoneValue); + }); } + +#[test] +fn exceeding_max_scopes_per_pallet_should_fail() { + new_test_ext().execute_with(|| { + for n in 0..::MaxScopesPerPallet::get(){ + create_scope(n.try_into().unwrap()) + } + assert_noop!(RBAC::create_scope(1, [255;32]), Error::::ExceedMaxScopesPerPallet); + }); +} + +#[test] +fn remove_scope_works() { + new_test_ext().execute_with(|| { + create_scope(0); + // TODO: add roles to pallet and users to scope + remove_scope(0); + }); +} + +#[test] +fn remove_pallet_storage_works() { + new_test_ext().execute_with(|| { + create_scope(0); + remove_pallet_storage(); + }); +} \ No newline at end of file From df66e3aa76bedcdeab6d40eab2c8102f21326a47 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 11 Aug 2022 18:52:28 -0500 Subject: [PATCH 074/103] I added all the workflow for the buy offers. Extrinsics, validations, helper functions. --- pallets/gated-marketplace/src/functions.rs | 213 +++++++++++++++++---- pallets/gated-marketplace/src/lib.rs | 46 +++-- 2 files changed, 193 insertions(+), 66 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 948fd0e8..fc2318ec 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -151,7 +151,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { //TODO: ensure the user is a Marketparticipant //ensure the marketplace exists @@ -167,11 +167,10 @@ impl Pallet { //ensure the price is valid Self::is_the_price_valid(price)?; - //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; - //create an offer_sell_id + //create an offer_id //TODO: create an offer id generator, used in cases where the offer_id generated is not unique let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp, timestamp2).using_encoded(blake2_256); @@ -185,37 +184,105 @@ impl Pallet { creation_date: timestamp, expiration_date: timestamp2, status: OfferStatus::Open, - offer_type: offer_type, + offer_type: OfferType::SellOrder, buyer: None, }; - //insert in OffersByItem - Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + //ensure there is no a previous sell offer for this item + Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + + //insert in OffersByItem >::try_mutate(collection_id, item_id, |offers| { offers.try_push(offer_id) }).map_err(|_| Error::::OfferStorageError)?; - //TODO: chnage error messagem, it isn't the right one //insert in OffersByAccount - >::try_mutate(authority.clone(), |offer| { - offer.try_push(offer_id) + >::try_mutate(authority.clone(), |offers| { + offers.try_push(offer_id) }).map_err(|_| Error::::OfferStorageError)?; //insert in OffersInfo - // validate offer already exists + // ensure the offer_id doesn't exist ensure!(!>::contains_key(offer_id), Error::::OfferAlreadyExists); >::insert(offer_id, offer_data); //Insert in OffersByMarketplace - >::try_mutate(marketplace_id, |offer| { - offer.try_push(offer_id) + >::try_mutate(marketplace_id, |offers| { + offers.try_push(offer_id) }).map_err(|_| Error::::OfferStorageError)?; Self::deposit_event(Event::OfferStored(collection_id, item_id)); Ok(()) } + pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + //TODO: ensure the user is a Marketparticipant + + //ensure the marketplace exists + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //ensure the collection exists + //For this case user doesn't have to be the owner of the collection + //but the owner of the item cannot create a buy offer for their own collection + if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { + ensure!(a != authority, Error::::CannotCreateOffer); + } else { + Err(Error::::CollectionNotFound)?; + } + + //ensure user has enough balance to create the offer + let total_user_balance = T::LocalCurrency::total_balance(&authority); + ensure!(total_user_balance >= price, Error::::NotEnoughBalance); + + //ensure the price is valid + Self::is_the_price_valid(price)?; + + //Add timestamp to the offer + let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; + + //create an offer_id + let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp, timestamp2).using_encoded(blake2_256); + + //create offer structure + let offer_data = OfferData:: { + marketplace_id: marketplace_id, + collection_id: collection_id, + item_id: item_id, + creator: authority.clone(), + price: price, + creation_date: timestamp, + expiration_date: timestamp2, + status: OfferStatus::Open, + offer_type: OfferType::BuyOrder, + buyer: None, + }; + + //insert in OffersByItem + //An item can receive multiple buy offers + >::try_mutate(collection_id, item_id, |offers| { + offers.try_push(offer_id) + }).map_err(|_| Error::::OfferStorageError)?; + + //insert in OffersByAccount + >::try_mutate(authority.clone(), |offers| { + offers.try_push(offer_id) + }).map_err(|_| Error::::OfferStorageError)?; + + //insert in OffersInfo + // ensure the offer_id doesn't exist + ensure!(!>::contains_key(offer_id), Error::::OfferAlreadyExists); + >::insert(offer_id, offer_data); + + //Insert in OffersByMarketplace + >::try_mutate(marketplace_id, |offers| { + offers.try_push(offer_id) + }).map_err(|_| Error::::OfferStorageError)?; + + Self::deposit_event(Event::OfferStored(collection_id, item_id)); + Ok(()) + } + pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; @@ -229,32 +296,77 @@ impl Pallet { //ensure the offer_id exists in OffersByItem Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + //TODO: ensure the offer is not expired + //ensure the offer is open and available ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); - //Transfer balance to the seller - let price_item = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; + //Transfer balance to the owner of the item + let item_price = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; let total_user_balance = T::LocalCurrency::total_balance(&buyer); - ensure!(total_user_balance >= price_item, Error::::NotEnoughBalance); + ensure!(total_user_balance >= item_price, Error::::NotEnoughBalance); //Transfer the balance - T::LocalCurrency::transfer(&buyer, &owner_item, price_item, KeepAlive)?; + T::LocalCurrency::transfer(&buyer, &owner_item, item_price, KeepAlive)?; - //Use uniques transfer to transfer the item to the buyer + //Use uniques transfer function to transfer the item to the buyer pallet_uniques::Pallet::::do_transfer(collection_id, item_id, buyer.clone(), |_, _|{ Ok(()) })?; - //TODO: ensure the offer is not expired - //update offer status from all marketplaces - Self::update_sell_offers_status(buyer.clone(), collection_id, item_id, marketplace_id)?; + Self::update_offers_status(buyer.clone(), collection_id, item_id, marketplace_id)?; - //remove all SellOrder offer types from OffersByItem so the item it's available to beign sold again - Self::delete_all_sell_orders_for_this_item(collection_id, item_id )?; + //remove all the offers associated with the item + Self::delete_all_offers_for_this_item(collection_id, item_id )?; //TODO: add the offer_id from this offer to the buyer's history - Self::deposit_event(Event::OfferTransferred(offer_id, buyer)); + Self::deposit_event(Event::OfferWasAccepted(offer_id, buyer)); + Ok(()) + } + + pub fn do_take_buy_offer(offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + //ensure the collection & owner exists + let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + + // Get the account_id of the offer creator (the buyer) + let buy_offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; + + //ensure owner is not the same as the buy_offer_creator + ensure!(owner_item != buy_offer_creator.clone(), Error::::CannotTakeOffer); + + //ensure the selected item has a valid offer_id in OffersInfo + // This offer_id corresponds to the buy offer + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + + //ensure the offer_id exists in OffersByItem + Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + + //TODO: ensure the offer is not expired + + //ensure the offer is open and available + ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); + + //Get the offered price + let offerred_price = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; + let total_buy_offer_creator = T::LocalCurrency::total_balance(&buy_offer_creator); + //ensure the buy_offer_creator has enough balance to buy the item + ensure!(total_buy_offer_creator >= offerred_price, Error::::NotEnoughBalance); + //Transfer the balance to the owner of the item + T::LocalCurrency::transfer(&buy_offer_creator, &owner_item, offerred_price, KeepAlive)?; + + //Use uniques transfer function to transfer the item to the market_participant + pallet_uniques::Pallet::::do_transfer(collection_id, item_id, buy_offer_creator.clone(), |_, _|{ + Ok(()) + })?; + + //update offer status from all marketplaces + Self::update_offers_status(buy_offer_creator.clone(), collection_id, item_id, marketplace_id)?; + + //remove all the offers associated with the item + Self::delete_all_offers_for_this_item(collection_id, item_id )?; + + Self::deposit_event(Event::OfferWasAccepted(offer_id, buy_offer_creator)); Ok(()) } @@ -611,7 +723,6 @@ impl Pallet { } else { return false; } - } fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { @@ -639,28 +750,41 @@ impl Pallet { } } + fn get_offer_creator(offer_id: [u8;32],) -> Result { + //we already know that the offer exists, so we don't need to check it here. + if let Some(offer) = >::get(offer_id) { + return Ok(offer.creator); + } else { + return Err(Error::::OfferNotFound)?; + } + } + //sell orders here... - fn update_sell_offers_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ + fn update_offers_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ let offer_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; - let mut sell_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); - - for offer_id in offer_ids { - let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. - if offer_info.offer_type == OfferType::SellOrder { - sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; - } - // //version 2 - // if let Some(offer) = >::get(offer_id) { - // if offer.offer_type == OfferType::SellOrder { - // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; - // } - // } - } + //This logic is no longer needed, when an offer_id is accepted, + // all the other offers for this item are automatically closed. + // It doesn't matter the offertype. + + // let mut sell_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); + + // for offer_id in offer_ids { + // let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + // //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. + // if offer_info.offer_type == OfferType::SellOrder { + // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; + // } + // // //version 2 + // // if let Some(offer) = >::get(offer_id) { + // // if offer.offer_type == OfferType::SellOrder { + // // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; + // // } + // // } + // } - for offer_id in sell_offer_ids { + for offer_id in offer_ids { >::try_mutate::<_,_,DispatchError,_>(offer_id, |offer|{ let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; offer.status = OfferStatus::Closed; @@ -695,6 +819,7 @@ impl Pallet { fn delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { //ensure the item has offers associated with it. ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); + let offers_ids = >::take(collection_id, item_id); //let mut remaining_offer_ids: Vec<[u8;32]> = Vec::new(); let mut buy_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); @@ -706,7 +831,6 @@ impl Pallet { buy_offer_ids.try_push(offer_id).map_err(|_| Error::::LimitExceeded)?; } } - //ensure we already took the entry from the storage map, so we can insert it again. ensure!(!>::contains_key(collection_id, item_id), Error::::OfferNotFound); >::insert(collection_id, item_id, buy_offer_ids); @@ -714,6 +838,11 @@ impl Pallet { Ok(()) } + fn delete_all_offers_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { + >::remove(collection_id, item_id); + Ok(()) + } + diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index be884220..07b52984 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -208,8 +208,8 @@ pub mod pallet { MarketplaceRemoved([u8;32]), /// Offer stored. [collection_id, item_id] OfferStored(T::CollectionId, T::ItemId), - /// Offer was transferred to the specified account. [offer_id, account] - OfferTransferred([u8;32], T::AccountId), + /// Offer was accepted [offer_id, account] + OfferWasAccepted([u8;32], T::AccountId), /// Offer was duplicated. [new_offer_id, new_marketplace_id] OfferDuplicated([u8;32], [u8;32]), } @@ -287,8 +287,10 @@ pub mod pallet { CannotDeleteOffer, /// There was a problem storing the offer OfferStorageError, - /// Price must be greater than 0 + /// Price must be greater than zero PriceMustBeGreaterThanZero, + /// User cannot create buy offers for their own items + CannotCreateOffer, } #[pallet::call] @@ -508,26 +510,15 @@ pub mod pallet { Self::do_remove_marketplace(who, marketplace_id) } - #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { + pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, offer_type, price) + Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, price) } - // #[transactional] - // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - // pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { - // let who = ensure_signed(origin)?; - - // Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, offer_type, price) - // } - - - #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { @@ -544,7 +535,6 @@ pub mod pallet { Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) } - #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn remove_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { @@ -556,16 +546,24 @@ pub mod pallet { } - // #[transactional] - // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - // pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, offer_type: OfferType, price: BalanceOf,) -> DispatchResult { - // let who = ensure_signed(origin)?; - // Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, offer_type, price) - // } + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, price) + } + + #[transactional] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + let who = ensure_signed(origin.clone())?; + + Self::do_take_buy_offer(offer_id, marketplace_id, collection_id, item_id) + } //TODO: Add CRUD operations for the offers - //TODO: Add extrinsic to duplicate offers in other marketplace. /// Kill all the stored data. /// From d04f6cad68ab051ec36747431d588c69a65d99c9 Mon Sep 17 00:00:00 2001 From: didiermis Date: Fri, 12 Aug 2022 14:09:15 -0500 Subject: [PATCH 075/103] Add validations to prevent users to make offers for items thar are not for sale. --- pallets/gated-marketplace/src/functions.rs | 16 ++++++++++++---- pallets/gated-marketplace/src/lib.rs | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index fc2318ec..97e3a941 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -219,6 +219,9 @@ impl Pallet { pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { //TODO: ensure the user is a Marketparticipant + //ensure the item is for sale, if not, return error + ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); + //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -284,6 +287,7 @@ impl Pallet { } pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + //This extrisicn is called by the user who wants to buy the item //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; @@ -325,10 +329,14 @@ impl Pallet { Ok(()) } - pub fn do_take_buy_offer(offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + //This extrinsic is called by the owner of the item who accepts the buy offer from the interested user. //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + //ensure only owner of the item can call the extrinic + ensure!(owner_item == authority.clone(), Error::::NotOwner); + // Get the account_id of the offer creator (the buyer) let buy_offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; @@ -697,7 +705,8 @@ impl Pallet { } Ok(()) } - + + //TODO: merge the timestamp function to convert from moment to milliseconds. fn convert_moment_to_u64_in_milliseconds(date: T::Moment) -> Result { let date_as_u64_millis; if let Some(_date_as_u64) = TryInto::::try_into(date).ok() { @@ -811,12 +820,11 @@ impl Pallet { } } } - Ok(()) } - fn delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { + fn _delete_all_sell_orders_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId) -> DispatchResult { //ensure the item has offers associated with it. ensure!(>::contains_key(collection_id, item_id), Error::::OfferNotFound); diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 07b52984..cd148cc8 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -291,6 +291,8 @@ pub mod pallet { PriceMustBeGreaterThanZero, /// User cannot create buy offers for their own items CannotCreateOffer, + /// This items is not available for sale + ItemNotForSale, } #[pallet::call] @@ -559,7 +561,7 @@ pub mod pallet { pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_buy_offer(offer_id, marketplace_id, collection_id, item_id) + Self::do_take_buy_offer(who, offer_id, marketplace_id, collection_id, item_id) } From 58b680f8af6b0f7db919d3450499a13993a4247f Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 12 Aug 2022 21:56:11 -0500 Subject: [PATCH 076/103] adds more rbac unit tests --- pallets/rbac/src/functions.rs | 18 ++- pallets/rbac/src/lib.rs | 6 +- pallets/rbac/src/mock.rs | 8 +- pallets/rbac/src/tests.rs | 272 +++++++++++++++++++++++++++++++--- 4 files changed, 275 insertions(+), 29 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 9a8fe084..90e855b9 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -109,7 +109,7 @@ impl RoleBasedAccessControl for Pallet{ /// Creates a role and returns its identifier, if its already created, /// the function will return the preexisting one. /// ### Parameters: - /// - `roles`: A role to create, encoded in bytes. + /// - `role`: A role to create, encoded in bytes. fn create_role(role: Vec)-> Result{ let role_id = role.using_encoded(blake2_256); // no "get_or_insert" method found @@ -130,7 +130,7 @@ impl RoleBasedAccessControl for Pallet{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); >::try_mutate(pallet_id, |roles|{ - ensure!(!roles.contains(&role_id), Error::::DuplicateRole ); + ensure!(!roles.contains(&role_id), Error::::RoleAlreadyLinkedToPallet ); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerPallet) })?; Ok(()) @@ -144,9 +144,10 @@ impl RoleBasedAccessControl for Pallet{ /// - `roles`: A list of unique role identifiers. fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult{ // checks for duplicates: + ensure!(Self::has_unique_elements(roles.clone()), Error::::DuplicateRole); let pallet_roles = >::get(&pallet_id); for id in roles.clone(){ - ensure!(!pallet_roles.contains(&id), Error::::DuplicateRole ); + ensure!(!pallet_roles.contains(&id), Error::::RoleAlreadyLinkedToPallet ); } >::try_mutate(pallet_id, |pallet_roles|{ pallet_roles.try_extend(roles.into_iter()) @@ -222,8 +223,8 @@ impl RoleBasedAccessControl for Pallet{ fn create_and_set_permissions(pallet_id: u64, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { let mut permission_ids = Vec::<[u8;32]>::new(); - for permision in permissions{ - permission_ids.push( Self::create_permission(pallet_id, permision.to_owned())? ); + for permission in permissions{ + permission_ids.push( Self::create_permission(pallet_id, permission.to_owned())? ); } Self::set_multiple_permissions_to_role(pallet_id, role_id, permission_ids.clone())?; let b_permissions = Self::bound(permission_ids, Error::::ExceedMaxPermissionsPerRole)?; @@ -401,4 +402,11 @@ impl Pallet{ fn bound>(vec: Vec, err : Error )->Result, Error>{ BoundedVec::::try_from(vec).map_err(|_| err) } + + fn has_unique_elements(vec: Vec) -> bool{ + let mut filtered_vec = vec.clone(); + filtered_vec.sort(); + filtered_vec.dedup(); + vec.len() == filtered_vec.len() + } } \ No newline at end of file diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index cc981391..cd9f4ee7 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -153,18 +153,20 @@ pub mod pallet { ScopeNotFound, /// The scope is already linked with the pallet ScopeAlreadyExists, - /// The specified role doesn't exist + /// The specified role doesn't exist or it hasn't been set to the user RoleNotFound, /// The permission doesn't exist in the pallet PermissionNotFound, /// The specified user hasn't been asigned to this scope UserNotFound, - /// The role is already linked in the pallet + /// The provided role list must have unique elements DuplicateRole, /// The permission is already linked to that role in that scope DuplicatePermission, /// The user has that role asigned in that scope UserAlreadyHasRole, + /// The role is already linked in the pallet + RoleAlreadyLinkedToPallet, /// The role exists but it hasn't been linked to the pallet RoleNotLinkedToPallet, /// The permission wasn't found in the roles capabilities diff --git a/pallets/rbac/src/mock.rs b/pallets/rbac/src/mock.rs index dc94ce8b..26f37a6e 100644 --- a/pallets/rbac/src/mock.rs +++ b/pallets/rbac/src/mock.rs @@ -56,12 +56,12 @@ impl system::Config for Test { parameter_types! { pub const MaxScopesPerPallet: u32 = 2; - pub const MaxRolesPerPallet: u32 = 20; - pub const RoleMaxLen: u32 = 30; + pub const MaxRolesPerPallet: u32 = 3; + pub const RoleMaxLen: u32 = 10; pub const PermissionMaxLen: u32 = 30; pub const MaxPermissionsPerRole: u32 = 10; - pub const MaxRolesPerUser: u32 = 10; - pub const MaxUsersPerRole: u32 = 10; + pub const MaxRolesPerUser: u32 = 2; + pub const MaxUsersPerRole: u32 = 2; } impl pallet_rbac::Config for Test { type Event = Event; diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 6964170f..bba2c1e6 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,17 +1,36 @@ -use crate::{mock::*, Error, types::RoleBasedAccessControl, Config}; -use frame_support::{assert_noop, assert_ok}; - +use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId}, Config}; +use frame_support::{assert_noop, assert_ok, assert_err}; +type AccountId = ::AccountId; const PALLET_ID : u64 = 1; -fn create_scope(n: u8){ - assert_ok!(RBAC::create_scope(PALLET_ID, [n;32])); - assert!(RBAC::scopes(1).contains(&[n;32])); +fn create_scope(n: u8)->ScopeId{ + let scope_id = [n;32]; + assert_ok!(RBAC::create_scope(PALLET_ID, scope_id)); + assert!(RBAC::scopes(1).contains(&scope_id)); + scope_id +} + +fn gen_roles(n_roles: u32)-> Vec>{ + let mut v = Vec::new(); + for i in 0..n_roles{ + v.push(format!("role{}",i).into_bytes().to_vec()); + } + v +} + +fn create_role(role: Vec)->RoleId{ + let r_id = RBAC::create_role(role.clone()).unwrap(); + assert_eq!(RBAC::roles(r_id).unwrap().to_vec(), role); + r_id +} + +fn set_role_to_pallet(role_id: RoleId){ + assert_ok!(RBAC::set_role_to_pallet(PALLET_ID, role_id)); } -fn create_role(role: &str){ - let r_id = RBAC::create_role(role.as_bytes().to_vec()); - assert_ok!(RBAC::create_role(role.as_bytes().to_vec())); +fn set_multiple_pallet_roles(roles: Vec){ + assert_ok!(RBAC::set_multiple_pallet_roles(PALLET_ID, roles)); } fn remove_scope(n: u8){ @@ -20,11 +39,29 @@ fn remove_scope(n: u8){ // TODO check that other storage maps were removed too } +fn remove_role_from_user(user: AccountId, scope_id: &ScopeId, role_id: RoleId){ + assert_ok!(RBAC::remove_role_from_user(user, PALLET_ID, scope_id, role_id)); + let user_roles = RBAC::roles_by_user((user, PALLET_ID, scope_id)); + assert!(!user_roles.contains(&role_id)); + let role_users = RBAC::users_by_scope((PALLET_ID, scope_id, role_id)); + assert!(!role_users.contains(&user)); +} + fn remove_pallet_storage(){ assert_ok!(RBAC::remove_pallet_storage(PALLET_ID)); // TODO: Check that other storage maps were removed too } +fn assign_role_to_user(user: AccountId, scope_id : &ScopeId, role_id: RoleId){ + assert_ok!( + RBAC::assign_role_to_user(user, PALLET_ID, scope_id, role_id) + ); + let user_roles = RBAC::roles_by_user((user,PALLET_ID, scope_id)); + assert!(user_roles.contains(&role_id)); + let role_users = RBAC::users_by_scope((PALLET_ID, scope_id, role_id)); + assert!(role_users.contains(&user)); +} + #[test] fn create_scope_works() { new_test_ext().execute_with(|| { @@ -40,18 +77,11 @@ fn create_scope_twice_should_fail() { }); } -#[test] -fn create_role_should_work() { - new_test_ext().execute_with(|| { - - }); -} - #[test] fn exceeding_max_scopes_per_pallet_should_fail() { new_test_ext().execute_with(|| { for n in 0..::MaxScopesPerPallet::get(){ - create_scope(n.try_into().unwrap()) + create_scope(n.try_into().unwrap()); } assert_noop!(RBAC::create_scope(1, [255;32]), Error::::ExceedMaxScopesPerPallet); }); @@ -72,4 +102,210 @@ fn remove_pallet_storage_works() { create_scope(0); remove_pallet_storage(); }); -} \ No newline at end of file +} + +#[test] +fn create_role_should_work() { + new_test_ext().execute_with(|| { + create_role("owner".as_bytes().to_vec()); + }); +} + +#[test] +fn exceeding_role_max_len_should_fail() { + new_test_ext().execute_with(|| { + assert_noop!( + RBAC::create_role("0123456789A".as_bytes().to_vec()), + Error::::ExceedRoleMaxLen + ); + }); +} + +#[test] +fn set_role_to_pallet_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + }); +} + +#[test] +fn set_nonexistent_role_to_pallet_should_fail() { + new_test_ext().execute_with(|| { + assert_noop!( + RBAC::set_role_to_pallet(PALLET_ID, [0;32]), + Error::::RoleNotFound + ); + }); +} + +#[test] +fn set_role_to_pallet_twice_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assert_noop!( + RBAC::set_role_to_pallet(PALLET_ID, role_id), + Error::::RoleAlreadyLinkedToPallet + ); + }); +} + + +#[test] +fn exceeding_max_roles_per_pallet_should_fail() { + new_test_ext().execute_with(|| { + let role_max_len = ::MaxRolesPerPallet::get(); + gen_roles(role_max_len).iter().for_each(|role| { + let role_id = create_role(role.clone()); + set_role_to_pallet(role_id); + }); + let role_id = create_role("admin".as_bytes().to_vec()); + assert_noop!( + RBAC::set_role_to_pallet(PALLET_ID, role_id), + Error::::ExceedMaxRolesPerPallet + ); + }); +} + +#[test] +fn set_multiple_pallet_roles_should_work() { + new_test_ext().execute_with(|| { + let n_roles = ::MaxRolesPerPallet::get()-1; + let role_ids: Vec = gen_roles(n_roles).iter().map(|role|{ + create_role(role.clone()) + }).collect(); + set_multiple_pallet_roles(role_ids); + }); +} + +#[test] +fn set_multiple_duplicate_pallet_roles_should_fail() { + new_test_ext().execute_with(|| { + let n_roles = ::MaxRolesPerPallet::get()-1; + let mut roles = gen_roles(n_roles); + roles.push("role0".as_bytes().to_vec()); + let role_ids: Vec = roles.iter().map(|role|{ + create_role(role.clone()) + }).collect(); + assert_noop!( + RBAC::set_multiple_pallet_roles(PALLET_ID, role_ids), + Error::::DuplicateRole + ); + }); +} + +#[test] +fn set_multiple_pallet_roles_twice_should_fail() { + new_test_ext().execute_with(|| { + let n_roles = ::MaxRolesPerPallet::get(); + let roles = gen_roles(n_roles); + let role_ids: Vec = roles.iter().map(|role|{ + create_role(role.clone()) + }).collect(); + set_multiple_pallet_roles(role_ids.clone()); + assert_noop!( + RBAC::set_multiple_pallet_roles(PALLET_ID, role_ids), + Error::::RoleAlreadyLinkedToPallet + ); + }); +} + +#[test] +fn assign_role_to_user_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assign_role_to_user(0, &scope_id, role_id); + }); +} + +#[test] +fn assign_role_to_user_without_scope_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assert_noop!( + RBAC::assign_role_to_user(0, PALLET_ID, &[0;32], role_id), + Error::::ScopeNotFound + ); + }); +} + +#[test] +fn exceeding_max_roles_per_user_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let n_roles = ::MaxRolesPerUser::get(); + let roles = gen_roles(n_roles); + let role_ids: Vec = roles.iter().map(|role|{ + create_role(role.clone()) + }).collect(); + set_multiple_pallet_roles(role_ids.clone()); + role_ids.iter().for_each(|role_id|{ + assign_role_to_user(0, &scope_id, *role_id); + }); + let last_role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(last_role_id); + assert_noop!( + RBAC::assign_role_to_user(0, PALLET_ID, &scope_id, last_role_id), + Error::::ExceedMaxRolesPerUser + ); + }); +} + +#[test] +fn exceeding_max_users_per_role_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + let max_users_per_role = ::MaxUsersPerRole::get(); + set_role_to_pallet(role_id); + for i in 0..max_users_per_role{ + assign_role_to_user(i.into(), &scope_id, role_id) + } + // avoiding assert_noop because it checks if the storage mutated + assert_err!( + RBAC::assign_role_to_user((max_users_per_role+1).into(), PALLET_ID, &scope_id, role_id), + Error::::ExceedMaxUsersPerRole + ); + }); +} + +#[test] +fn remove_role_from_user_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assign_role_to_user(0, &scope_id, role_id); + remove_role_from_user(0, &scope_id, role_id); + }); +} + +#[test] +fn remove_non_assigned_role_from_user_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + assert_noop!( + RBAC::remove_role_from_user(0, PALLET_ID, &scope_id, [0;32]), + Error::::UserHasNoRoles + ); + }); +} + +#[test] +fn remove_non_existent_role_from_user_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assign_role_to_user(0, &scope_id, role_id); + assert_noop!( + RBAC::remove_role_from_user(0, PALLET_ID, &scope_id, [0;32]), + Error::::RoleNotFound + ); + }); +} + From ac6bbfa5460e3872ccca999831014afe42983db2 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Sun, 14 Aug 2022 21:08:39 -0500 Subject: [PATCH 077/103] adds more unit tests --- pallets/rbac/src/functions.rs | 9 +- pallets/rbac/src/lib.rs | 4 +- pallets/rbac/src/mock.rs | 4 +- pallets/rbac/src/tests.rs | 323 +++++++++++++++++++++++++++++++++- 4 files changed, 332 insertions(+), 8 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 90e855b9..6f35a490 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -222,6 +222,8 @@ impl RoleBasedAccessControl for Pallet{ /// encoded in bytes. fn create_and_set_permissions(pallet_id: u64, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { + ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); + Self::is_role_linked_to_pallet(pallet_id, &role_id )?; let mut permission_ids = Vec::<[u8;32]>::new(); for permission in permissions{ permission_ids.push( Self::create_permission(pallet_id, permission.to_owned())? ); @@ -261,7 +263,7 @@ impl RoleBasedAccessControl for Pallet{ Self::is_role_linked_to_pallet(pallet_id, &role_id)?; >::try_mutate(pallet_id, role_id, | role_permissions|{ - ensure!(role_permissions.contains(&permission_id), Error::::DuplicatePermission); + ensure!(!role_permissions.contains(&permission_id), Error::::DuplicatePermission); role_permissions.try_push(permission_id).map_err(|_| Error::::ExceedMaxPermissionsPerRole) })?; Ok(()) @@ -277,14 +279,15 @@ impl RoleBasedAccessControl for Pallet{ /// - `permissions`: A list of permission identifiers to assign to the role. fn set_multiple_permissions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: + ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); + Self::is_role_linked_to_pallet(pallet_id, &role_id )?; let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ - ensure!(!role_permissions.contains(&id), Error::::DuplicateRole ); + ensure!(!role_permissions.contains(&id), Error::::PermissionAlreadyLinkedToRole ); } >::try_mutate(pallet_id, role_id, |role_permissions|{ role_permissions.try_extend(permissions.into_iter()) }).map_err(|_| Error::::ExceedMaxPermissionsPerRole)?; - Ok(()) } diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index cd9f4ee7..e5ae45ad 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -161,7 +161,7 @@ pub mod pallet { UserNotFound, /// The provided role list must have unique elements DuplicateRole, - /// The permission is already linked to that role in that scope + /// The provided permission list must have unique elements DuplicatePermission, /// The user has that role asigned in that scope UserAlreadyHasRole, @@ -169,6 +169,8 @@ pub mod pallet { RoleAlreadyLinkedToPallet, /// The role exists but it hasn't been linked to the pallet RoleNotLinkedToPallet, + /// The permission is already linked to that role in that scope + PermissionAlreadyLinkedToRole, /// The permission wasn't found in the roles capabilities PermissionNotLinkedToRole, /// The user doesn't have any roles in this pallet diff --git a/pallets/rbac/src/mock.rs b/pallets/rbac/src/mock.rs index 26f37a6e..621662b9 100644 --- a/pallets/rbac/src/mock.rs +++ b/pallets/rbac/src/mock.rs @@ -58,8 +58,8 @@ parameter_types! { pub const MaxScopesPerPallet: u32 = 2; pub const MaxRolesPerPallet: u32 = 3; pub const RoleMaxLen: u32 = 10; - pub const PermissionMaxLen: u32 = 30; - pub const MaxPermissionsPerRole: u32 = 10; + pub const PermissionMaxLen: u32 = 15; + pub const MaxPermissionsPerRole: u32 = 3; pub const MaxRolesPerUser: u32 = 2; pub const MaxUsersPerRole: u32 = 2; } diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index bba2c1e6..47ee6258 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,9 +1,36 @@ -use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId}, Config}; -use frame_support::{assert_noop, assert_ok, assert_err}; +use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config}; +use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec}; type AccountId = ::AccountId; const PALLET_ID : u64 = 1; +// DONE: +/* +create_scope +create_role +set_role_to_pallet +set_multiple_pallet_roles +create_and_set_role +assign_role_to_user +remove_role_from_user +create_permission +set_permission_to_role +set_multiple_permissions_to_role +create_and_set_permissions +*/ + +// TODO: +/* +remove_scope +remove_pallet_storage +is_authorized +has_role +scope_exists +permission_exists +is_role_linked_to_pallet +is_permission_linked_to_role +get_role_users_len +*/ fn create_scope(n: u8)->ScopeId{ let scope_id = [n;32]; assert_ok!(RBAC::create_scope(PALLET_ID, scope_id)); @@ -19,12 +46,29 @@ fn gen_roles(n_roles: u32)-> Vec>{ v } +fn gen_permissions(n_permissions: u32)-> Vec>{ + let mut v = Vec::new(); + for i in 0..n_permissions{ + v.push(format!("permission{}",i).into_bytes().to_vec()); + } + v +} + fn create_role(role: Vec)->RoleId{ let r_id = RBAC::create_role(role.clone()).unwrap(); assert_eq!(RBAC::roles(r_id).unwrap().to_vec(), role); r_id } +fn create_and_set_roles(role_ids: Vec>)->BoundedVec::MaxRolesPerPallet >{ + let role_ids = RBAC::create_and_set_roles(PALLET_ID, role_ids).unwrap(); + let inserted_roles_list = RBAC::pallet_roles(PALLET_ID); + assert!( + role_ids.iter().all(|r_id| inserted_roles_list.contains(r_id)) + ); + role_ids +} + fn set_role_to_pallet(role_id: RoleId){ assert_ok!(RBAC::set_role_to_pallet(PALLET_ID, role_id)); } @@ -62,6 +106,38 @@ fn assign_role_to_user(user: AccountId, scope_id : &ScopeId, role_id: RoleId){ assert!(role_users.contains(&user)); } +fn create_permission(permission: Vec)-> PermissionId{ + let permission_id = RBAC::create_permission(PALLET_ID, permission.clone()).unwrap(); + assert_eq!( + RBAC::permissions(PALLET_ID, permission_id).to_vec(), + permission + ); + permission_id +} + +fn set_permission_to_role(role_id: RoleId, permission_id: PermissionId){ + assert_ok!(RBAC::set_permission_to_role(PALLET_ID, role_id, permission_id)); + assert!(RBAC::permissions_by_role(PALLET_ID, role_id).contains(&permission_id)); +} + +fn set_multiple_permissions_to_role(role_id: RoleId, permissions: Vec){ + assert_ok!( + RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permissions.clone()) + ); + let role_permissions = RBAC::permissions_by_role(PALLET_ID, role_id); + assert!( + permissions.iter().all(|p|{role_permissions.contains(p)}), + ); +} + +fn create_and_set_permissions(role_id: RoleId, permissions: Vec>){ + let permission_ids = RBAC::create_and_set_permissions(PALLET_ID, role_id,permissions).unwrap(); + let role_permissions = RBAC::permissions_by_role(PALLET_ID, role_id); + assert!( + permission_ids.iter().all(|p|{role_permissions.contains(p)}), + ); +} + #[test] fn create_scope_works() { new_test_ext().execute_with(|| { @@ -211,6 +287,36 @@ fn set_multiple_pallet_roles_twice_should_fail() { }); } +#[test] +fn create_and_set_role_should_work() { + new_test_ext().execute_with(|| { + create_and_set_roles(gen_roles(::MaxRolesPerPallet::get())); + }); +} + +#[test] +fn create_and_set_duplicate_role_should_fail() { + new_test_ext().execute_with(|| { + let mut roles = gen_roles(::MaxRolesPerPallet::get()-1); + roles.push("role0".as_bytes().to_vec()); + assert_err!( + RBAC::create_and_set_roles(PALLET_ID, roles), + Error::::DuplicateRole + ); + }); +} + +#[test] +fn exceeding_max_roles_per_pallet_from_create_and_set_role_should_fail() { + new_test_ext().execute_with(|| { + let exceed = ::MaxRolesPerPallet::get() + 1; + assert_err!( + RBAC::create_and_set_roles(PALLET_ID, gen_roles(exceed)), + Error::::ExceedMaxRolesPerPallet + ); + }); +} + #[test] fn assign_role_to_user_should_work() { new_test_ext().execute_with(|| { @@ -309,3 +415,216 @@ fn remove_non_existent_role_from_user_should_fail() { }); } +#[test] +fn create_permission_should_work() { + new_test_ext().execute_with(|| { + create_permission("enroll".as_bytes().to_vec()); + }); +} + +#[test] +fn exceeding_permission_max_len_should_fail() { + new_test_ext().execute_with(|| { + assert_noop!( + RBAC::create_permission(PALLET_ID, "0123456789ABCDFG".as_bytes().to_vec()), + Error::::ExceedPermissionMaxLen + ); + }); +} + +#[test] +fn set_permission_to_role_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permission_id = create_permission("enroll".as_bytes().to_vec()); + set_permission_to_role(role_id, permission_id); + }); +} + +#[test] +fn set_non_existent_permission_to_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assert_noop!( + RBAC::set_permission_to_role(PALLET_ID, role_id, [0;32]), + Error::::PermissionNotFound + ); + }); +} + + +#[test] +fn set_permission_to_role_twice_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permission_id = create_permission("enroll".as_bytes().to_vec()); + set_permission_to_role(role_id, permission_id); + assert_noop!( + RBAC::set_permission_to_role(PALLET_ID, role_id, permission_id), + Error::::DuplicatePermission + ); + }); +} + +#[test] +fn exceeding_max_permissions_per_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + let max_permissions_per_role = ::MaxPermissionsPerRole::get(); + set_role_to_pallet(role_id); + gen_permissions(max_permissions_per_role).iter() + .for_each(|permission|{ + let permission_id = create_permission(permission.clone()); + set_permission_to_role(role_id, permission_id); + }); + let last_permission_id = create_permission("enroll".as_bytes().to_vec()); + assert_noop!( + RBAC::set_permission_to_role(PALLET_ID,role_id, last_permission_id), + Error::::ExceedMaxPermissionsPerRole + ); + }); +} + +#[test] +fn set_multiple_permissions_to_role_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + let permission_ids: Vec = permissions.iter().map(|permission|{ + create_permission(permission.to_vec()) + }).collect(); + set_multiple_permissions_to_role(role_id, permission_ids); + }); +} + +#[test] +fn set_multiple_duplicate_permissions_to_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let mut permissions = gen_permissions(::MaxPermissionsPerRole::get()-1); + permissions.push("permission0".as_bytes().to_vec()); + let permission_ids: Vec = permissions.iter().map(|permission|{ + create_permission(permission.to_vec()) + }).collect(); + assert_noop!( + RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + Error::::DuplicatePermission + ); + }); +} + +#[test] +fn set_multiple_permissions_to_unlinked_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + let permission_ids: Vec = permissions.iter().map(|permission|{ + create_permission(permission.to_vec()) + }).collect(); + assert_noop!( + RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + Error::::RoleNotLinkedToPallet + ); + }); +} + +#[test] +fn set_multiple_permissions_to_role_twice_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + set_role_to_pallet(role_id); + let permission_ids: Vec = permissions.iter().map(|permission|{ + create_permission(permission.to_vec()) + }).collect(); + set_multiple_permissions_to_role(role_id, permission_ids.clone()); + assert_noop!( + RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + Error::::PermissionAlreadyLinkedToRole + ); + }); +} + +#[test] +fn exceeding_max_permissions_per_role_from_set_multiple_permissions_to_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()+1); + set_role_to_pallet(role_id); + let permission_ids: Vec = permissions.iter().map(|permission|{ + create_permission(permission.to_vec()) + }).collect(); + assert_noop!( + RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + Error::::ExceedMaxPermissionsPerRole + ); + }); +} + +#[test] +fn create_and_set_permissions_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + create_and_set_permissions(role_id, permissions); + }); +} + +#[test] +fn create_set_duplicate_permissions_to_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let mut permissions = gen_permissions(::MaxPermissionsPerRole::get()-1); + permissions.push("permission0".as_bytes().to_vec()); + assert_noop!( + RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + Error::::DuplicatePermission + ); + }); +} + +#[test] +fn create_and_set_permissions_to_unlinked_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + assert_noop!( + RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + Error::::RoleNotLinkedToPallet + ); + }); +} + +#[test] +fn create_and_set_multiple_permissions_to_role_twice_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()); + set_role_to_pallet(role_id); + create_and_set_permissions(role_id, permissions.clone()); + assert_noop!( + RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + Error::::PermissionAlreadyLinkedToRole + ); + }); +} + +#[test] +fn exceeding_max_permissions_per_role_from_create_and_set_permissions_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("admin".as_bytes().to_vec()); + let permissions = gen_permissions(::MaxPermissionsPerRole::get()+1); + set_role_to_pallet(role_id); + assert_err!( + RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + Error::::ExceedMaxPermissionsPerRole + ); + }); +} \ No newline at end of file From a769d9314409bfe901e9b0ccd48a655681cb9212 Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 15 Aug 2022 13:44:20 -0500 Subject: [PATCH 078/103] Delete freezed status from enum. We only use open or closed status. --- pallets/gated-marketplace/src/types.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 59a347bf..0575b8ff 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -76,7 +76,6 @@ impl PartialEq for ApplicationField{ #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, MaxEncodedLen, TypeInfo, Copy)] pub enum OfferStatus{ Open, - Freezed, Closed, } From 78149fdb4956847601e737c394ac093787927653 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 15 Aug 2022 14:02:41 -0500 Subject: [PATCH 079/103] finishes unit tests --- pallets/rbac/src/functions.rs | 2 +- pallets/rbac/src/tests.rs | 250 ++++++++++++++++++++++++++++++++-- 2 files changed, 236 insertions(+), 16 deletions(-) diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 6f35a490..680bf008 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -169,7 +169,7 @@ impl RoleBasedAccessControl for Pallet{ Self::is_role_linked_to_pallet(pallet_id, &role_id)?; >::try_mutate((&user, pallet_id, scope_id), | roles |{ - ensure!(!roles.contains(&role_id), Error::::DuplicateRole); + ensure!(!roles.contains(&role_id), Error::::UserAlreadyHasRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) })?; diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 47ee6258..3f28f0a8 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,5 +1,5 @@ -use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config}; -use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec}; +use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config, Roles, RolesByUser, UsersByScope, PermissionsByRole, Permissions}; +use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::{StorageNMap, DispatchResult}}; type AccountId = ::AccountId; const PALLET_ID : u64 = 1; @@ -17,10 +17,6 @@ create_permission set_permission_to_role set_multiple_permissions_to_role create_and_set_permissions -*/ - -// TODO: -/* remove_scope remove_pallet_storage is_authorized @@ -29,8 +25,15 @@ scope_exists permission_exists is_role_linked_to_pallet is_permission_linked_to_role +*/ + +// TODO: +/* get_role_users_len + +assign twice role to user */ + fn create_scope(n: u8)->ScopeId{ let scope_id = [n;32]; assert_ok!(RBAC::create_scope(PALLET_ID, scope_id)); @@ -60,8 +63,8 @@ fn create_role(role: Vec)->RoleId{ r_id } -fn create_and_set_roles(role_ids: Vec>)->BoundedVec::MaxRolesPerPallet >{ - let role_ids = RBAC::create_and_set_roles(PALLET_ID, role_ids).unwrap(); +fn create_and_set_roles(roles: Vec>)->BoundedVec::MaxRolesPerPallet >{ + let role_ids = RBAC::create_and_set_roles(PALLET_ID, roles).unwrap(); let inserted_roles_list = RBAC::pallet_roles(PALLET_ID); assert!( role_ids.iter().all(|r_id| inserted_roles_list.contains(r_id)) @@ -79,8 +82,7 @@ fn set_multiple_pallet_roles(roles: Vec){ fn remove_scope(n: u8){ assert_ok!(RBAC::remove_scope(PALLET_ID, [n;32])); - assert!(!RBAC::scopes(PALLET_ID).contains(&[n;32])); - // TODO check that other storage maps were removed too + assert!(RBAC::scope_exists(PALLET_ID,&[n;32]).is_err()); } fn remove_role_from_user(user: AccountId, scope_id: &ScopeId, role_id: RoleId){ @@ -93,7 +95,10 @@ fn remove_role_from_user(user: AccountId, scope_id: &ScopeId, role_id: RoleId){ fn remove_pallet_storage(){ assert_ok!(RBAC::remove_pallet_storage(PALLET_ID)); - // TODO: Check that other storage maps were removed too + assert!(RBAC::scopes(PALLET_ID).is_empty()); + assert!(RBAC::pallet_roles(PALLET_ID).is_empty()); + assert_eq!(>::iter_prefix(PALLET_ID).count(), 0); + assert_eq!(>::iter_prefix(PALLET_ID).count(), 0); } fn assign_role_to_user(user: AccountId, scope_id : &ScopeId, role_id: RoleId){ @@ -130,12 +135,41 @@ fn set_multiple_permissions_to_role(role_id: RoleId, permissions: Vec>){ +fn create_and_set_permissions(role_id: RoleId, permissions: Vec>)->BoundedVec::MaxPermissionsPerRole>{ let permission_ids = RBAC::create_and_set_permissions(PALLET_ID, role_id,permissions).unwrap(); let role_permissions = RBAC::permissions_by_role(PALLET_ID, role_id); assert!( permission_ids.iter().all(|p|{role_permissions.contains(p)}), ); + permission_ids +} + +fn is_authorized(user: AccountId, scope_id : &ScopeId, permission_id: &PermissionId) -> DispatchResult{ + RBAC::is_authorized(user, PALLET_ID, scope_id, permission_id) +} + +fn has_role(user: AccountId, scope_id : &ScopeId, role_ids: Vec) -> DispatchResult{ + RBAC::has_role(user, PALLET_ID, scope_id, role_ids) +} + +fn scope_exists(scope_id : &ScopeId) -> DispatchResult{ + RBAC::scope_exists(PALLET_ID, scope_id) +} + +fn permission_exists(permission_id: &PermissionId) -> DispatchResult { + RBAC::permission_exists(PALLET_ID, permission_id) +} + +fn is_role_linked_to_pallet(role_id: &RoleId) -> DispatchResult { + RBAC::is_role_linked_to_pallet(PALLET_ID, role_id) +} + +fn is_permission_linked_to_role(role_id: &RoleId, permission_id: &PermissionId) -> DispatchResult { + RBAC::is_permission_linked_to_role(PALLET_ID, role_id, permission_id) +} + +fn get_role_users_len(scope_id : &ScopeId, role_id: &RoleId)-> usize{ + RBAC::get_role_users_len(PALLET_ID, scope_id, role_id) } #[test] @@ -166,12 +200,28 @@ fn exceeding_max_scopes_per_pallet_should_fail() { #[test] fn remove_scope_works() { new_test_ext().execute_with(|| { - create_scope(0); - // TODO: add roles to pallet and users to scope + let n_roles = ::MaxRolesPerPallet::get(); + let scope_id = create_scope(0); + let role_ids = create_and_set_roles(gen_roles(n_roles)); + role_ids.iter().enumerate().for_each(|(i,role_id)|{ + assign_role_to_user(i.try_into().unwrap(), &scope_id, *role_id); + }); remove_scope(0); }); } +#[test] +fn remove_non_existent_scope_should_fail() { + new_test_ext().execute_with(|| { + let n_roles = ::MaxRolesPerPallet::get(); + create_and_set_roles(gen_roles(n_roles)); + assert_noop!( + RBAC::remove_scope(PALLET_ID, [0;32]), + Error::::ScopeNotFound + ); + }); +} + #[test] fn remove_pallet_storage_works() { new_test_ext().execute_with(|| { @@ -327,6 +377,20 @@ fn assign_role_to_user_should_work() { }); } +#[test] +fn assign_role_to_user_twice_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assign_role_to_user(0, &scope_id, role_id); + assert_noop!( + RBAC::assign_role_to_user(0, PALLET_ID, &scope_id, role_id), + Error::::UserAlreadyHasRole + ); + }); +} + #[test] fn assign_role_to_user_without_scope_should_fail() { new_test_ext().execute_with(|| { @@ -627,4 +691,160 @@ fn exceeding_max_permissions_per_role_from_create_and_set_permissions_should_fai Error::::ExceedMaxPermissionsPerRole ); }); -} \ No newline at end of file +} + +#[test] +fn is_authorized_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_ids = create_and_set_roles(["admin".as_bytes().to_vec()].to_vec()); + let mut permission_ids = create_and_set_permissions(*role_ids.get(0).unwrap(), ["enroll".as_bytes().to_vec()].to_vec()); + assign_role_to_user(0, &scope_id, *role_ids.get(0).unwrap()); + assert_ok!( + is_authorized(0, &scope_id, &permission_ids.pop().unwrap()) + ); + }); +} + +#[test] +fn unauthorized_user_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_ids = create_and_set_roles(["admin".as_bytes().to_vec()].to_vec()); + let mut permission_ids = create_and_set_permissions(*role_ids.get(0).unwrap(), ["enroll".as_bytes().to_vec()].to_vec()); + assert_noop!( + is_authorized(0, &scope_id, &permission_ids.pop().unwrap()), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn has_role_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_ids = create_and_set_roles(gen_roles(2)); + assign_role_to_user(0, &scope_id, *role_ids.get(0).unwrap()); + assert_ok!( + has_role(0, &scope_id, role_ids.to_vec()) + ); + }); +} + +#[test] +fn user_that_doesnt_have_role_should_fail() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_ids = create_and_set_roles(gen_roles(2)); + assert_noop!( + has_role(0, &scope_id, role_ids.to_vec()), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn scope_exists_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + assert_ok!( + scope_exists(&scope_id) + ); + }); +} + +#[test] +fn nonexistent_scope_should_fail() { + new_test_ext().execute_with(|| { + create_scope(0); + assert_noop!( + scope_exists(&[1;32]), + Error::::ScopeNotFound + ); + }); +} + +#[test] +fn permission_exists_should_work() { + new_test_ext().execute_with(|| { + let permission_id = create_permission("enroll".as_bytes().to_vec()); + assert_ok!( + permission_exists(&permission_id) + ); + }); +} + +#[test] +fn nonexistent_permission_should_fail() { + new_test_ext().execute_with(|| { + create_permission("enroll".as_bytes().to_vec()); + assert_noop!( + permission_exists(&[0;32]), + Error::::PermissionNotFound + ); + }); +} + +#[test] +fn is_role_linked_to_pallet_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + assert_ok!( + is_role_linked_to_pallet(&role_id) + ); + }); +} + +#[test] +fn unlinked_role_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + assert_noop!( + is_role_linked_to_pallet(&role_id), + Error::::RoleNotLinkedToPallet + ); + }); +} + +#[test] +fn is_permission_linked_to_role_should_work() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permission_id = create_permission("enroll".as_bytes().to_vec()); + set_permission_to_role(role_id, permission_id); + assert_ok!( + is_permission_linked_to_role(&role_id, &permission_id) + ); + }); +} + +#[test] +fn unlinked_permission_should_fail() { + new_test_ext().execute_with(|| { + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + let permission_id = create_permission("enroll".as_bytes().to_vec()); + assert_noop!( + is_permission_linked_to_role(&role_id, &permission_id), + Error::::PermissionNotLinkedToRole + ); + }); +} + +#[test] +fn get_role_users_len_should_work() { + new_test_ext().execute_with(|| { + let scope_id = create_scope(0); + let role_id = create_role("owner".as_bytes().to_vec()); + set_role_to_pallet(role_id); + + assert_eq!(get_role_users_len(&scope_id, &role_id), 0); + + assign_role_to_user(0, &scope_id, role_id); + assign_role_to_user(1, &scope_id, role_id); + + assert_eq!(get_role_users_len(&scope_id, &role_id), 2); + }); +} From 14b35e1e625f60c9306efb1dfb877dbc7c02dda3 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 15 Aug 2022 14:59:44 -0500 Subject: [PATCH 080/103] deletes unused dependencies --- pallets/rbac/src/tests.rs | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 3f28f0a8..203c73cb 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,39 +1,9 @@ -use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config, Roles, RolesByUser, UsersByScope, PermissionsByRole, Permissions}; -use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::{StorageNMap, DispatchResult}}; +use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config, PermissionsByRole, Permissions}; +use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::DispatchResult}; type AccountId = ::AccountId; const PALLET_ID : u64 = 1; -// DONE: -/* -create_scope -create_role -set_role_to_pallet -set_multiple_pallet_roles -create_and_set_role -assign_role_to_user -remove_role_from_user -create_permission -set_permission_to_role -set_multiple_permissions_to_role -create_and_set_permissions -remove_scope -remove_pallet_storage -is_authorized -has_role -scope_exists -permission_exists -is_role_linked_to_pallet -is_permission_linked_to_role -*/ - -// TODO: -/* -get_role_users_len - -assign twice role to user -*/ - fn create_scope(n: u8)->ScopeId{ let scope_id = [n;32]; assert_ok!(RBAC::create_scope(PALLET_ID, scope_id)); From 45be53eb0c745e797a9f188bdbf5d1c3be0e2619 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 15 Aug 2022 15:37:21 -0500 Subject: [PATCH 081/103] Merge adjustements --- Cargo.lock | 4 ++++ pallets/gated-marketplace/src/functions.rs | 1 - pallets/gated-marketplace/src/lib.rs | 6 ------ 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2cd51dd..41f04392 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4031,7 +4031,11 @@ dependencies = [ "frame-support", "frame-system", "log", + "pallet-balances", + "pallet-fruniques", "pallet-rbac", + "pallet-timestamp", + "pallet-uniques", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index fc32a8c5..52e2dc70 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,4 +1,3 @@ -use core::default; use super::*; use frame_support::{pallet_prelude::*}; diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index c154cbf2..f94837f0 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -24,12 +24,6 @@ pub mod pallet { //use sp_runtime::sp_std::vec::Vec; use crate::types::*; //use frame_support::traits::tokens::Balance; - //use std::fmt::Debug; - -use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; - use frame_system::pallet_prelude::*; - use sp_runtime::sp_std::vec::Vec; - use crate::types::*; use pallet_rbac::types::RoleBasedAccessControl; #[pallet::config] From b444edbd9ba2c077d929637bf7368ed770ae8fe4 Mon Sep 17 00:00:00 2001 From: didiermis Date: Mon, 15 Aug 2022 20:23:49 -0500 Subject: [PATCH 082/103] Update gated-marketplace pallet documentation --- pallets/gated-marketplace/README.md | 86 ++++++++++++++++---- pallets/gated-marketplace/src/functions.rs | 29 ++----- pallets/gated-marketplace/src/lib.rs | 94 +++++++++++++++++++++- 3 files changed, 171 insertions(+), 38 deletions(-) diff --git a/pallets/gated-marketplace/README.md b/pallets/gated-marketplace/README.md index 3d885d7b..a5256a7a 100644 --- a/pallets/gated-marketplace/README.md +++ b/pallets/gated-marketplace/README.md @@ -49,6 +49,7 @@ This module allows to: - Enroll or reject applicants to your marketplace. - Add or remove users as supported authorities to your marketplace, like administrators and/or asset appraisers - WIP: Assign a rating to assets as an Appraiser. +- Create sell or buy orders. Users can bid on the item if they don't like the sale price. ### Terminology - **Authority**: Refers to any user that has special faculties within the marketplace, like enroll new users or grade assets. @@ -60,6 +61,8 @@ This module allows to: - **Enroll**: When enrolled, the user's application becomes approved, gaining the ability to sell and buy assets. - **Reject**: If the user gets rejected, its application becomes rejected and won't have access to the marketplace. - **Custodian**: When submitting an application, the user can optionally specify a third account that will have access to the confidential documents. A custodian doesn't need to be an authority nor being part of the marketplace. +- **Sell order**: The owner of the item creates sales offer fot the item. +- **Buy order**: Users from the marketplace can bid for the item. ## Interface @@ -72,16 +75,28 @@ This module allows to: - `remove_authority` is only callable by the marketplace owner or administrator. Removes the authority enforcer from the marketplace. The marketplace owner cannot be removed and the administrator cannot remove itself. - `update_label_marketplace` is only callable by the marketplace owner or administrator. Changes the marketplace label. If the new label already exists, the old name won't be changed. - `remove_marketplace` is only callable by the marketplace owner or administrator. This action allows the user to remove a marketplace as well as all the information related to this marketplace. +- `enlist_sell_offer` is only callable by the owner of the item. It allows the user to sell an item in the selected marketplace. +- `take_sell_offer` any user interested to buy the item can call this extrinsic. User must have enough balance to buy it. When the transaction is completed, the item ownership is transferred to the buyer. +- `duplicate_offer` allows the owner of the item to duplicate an sell order in any marketplace. +- `remove_offer` is only callable by the creator of the offer, it deletes any offer type from all the storages. +- `enlist_buy_offer` is callable by any market participant, the owner of the item can't create buy orders for their own items. User must have the enough balance to call it. +- `take_buy_offer` is only callable by the owner of the item. If the owner accepts the offer, the buyer must have enough balance to finalize the transaction. ### Getters -- `marketplaces` -- `marketplaces_by_authority` (double storage map) -- `authorities_by_marketplace` (double storage map) -- `applications` -- `applications_by_account` (double storage map) -- `applicants_by_marketplace` (double storage map) -- `custodians` (double storage map) +|Name| Type | +|--|--| +|`marketplaces`| storage map| +|`marketplaces_by_authority`|double storage map| +|`authorities_by_marketplace`|double storage map| +|`applications`| storage map| +|`applications_by_account`|double storage map| +|`applicants_by_marketplace`|double storage map| +|`custodians`|double storage map| +|`offers_info` |storage map| +|`offers_by_item`|double storage map| +|`offers_by_account`|storage map| +|`offers_by_marketplace`|storage map| ## Usage @@ -544,19 +559,34 @@ const removeAuthority = await api.tx.gatedMarketplace.removeAuthority(dave.addre ```rust /// Marketplaces stored. [owner, admin, market_id] -MarketplaceStored(T::AccountId, T::AccountId, [u8;32]), +1. MarketplaceStored(T::AccountId, T::AccountId, [u8;32]) + /// Application stored on the specified marketplace. [application_id, market_id] -ApplicationStored([u8;32], [u8;32]), +2. ApplicationStored([u8;32], [u8;32]) + /// An applicant was accepted or rejected on the marketplace. [AccountOrApplication, market_id, status] -ApplicationProcessed(AccountOrApplication,[u8;32], ApplicationStatus), +3. ApplicationProcessed(AccountOrApplication,[u8;32], ApplicationStatus) + /// Add a new authority to the selected marketplace -AuthorityAdded(T::AccountId, MarketplaceAuthority), +4. AuthorityAdded(T::AccountId, MarketplaceAuthority) + /// Remove the selected authority from the selected marketplace -AuthorityRemoved(T::AccountId, MarketplaceAuthority), +5. AuthorityRemoved(T::AccountId, MarketplaceAuthority) + /// The label of the selected marketplace has been updated. [market_id] -MarketplaceLabelUpdated([u8;32]), +6. MarketplaceLabelUpdated([u8;32]) + /// The selected marketplace has been removed. [market_id] -MarketplaceRemoved([u8;32]) +7. MarketplaceRemoved([u8;32]) + +/// Offer stored. [collection_id, item_id] +8. OfferStored(T::CollectionId, T::ItemId) + +/// Offer was accepted [offer_id, account] +9. OfferWasAccepted([u8;32], T::AccountId) + +/// Offer was duplicated. [new_offer_id, new_marketplace_id] +10. OfferDuplicated([u8;32], [u8;32]) ``` ## Errors @@ -608,4 +638,32 @@ ApplicationIdNotFound, ApplicationStatusStillPending, /// The application has already been approved, application status is approved ApplicationHasAlreadyBeenApproved, +/// Collection not found +CollectionNotFound, +/// User who calls the function is not the owner of the collection +NotOwner, +/// Offer already exists +OfferAlreadyExists, +/// Offer not found +OfferNotFound, +/// Offer is not available at the moment +OfferIsNotAvailable, +/// Owner cannnot buy its own offer +CannotTakeOffer, +/// User cannot remove the offer from the marketplace +CannotRemoveOffer, +/// Error related to the timestamp +TimestampError, +/// User does not have enough balance to buy the offer +NotEnoughBalance, +/// User cannot delete the offer because is closed +CannotDeleteOffer, +/// There was a problem storing the offer +OfferStorageError, +/// Price must be greater than zero +PriceMustBeGreaterThanZero, +/// User cannot create buy offers for their own items +CannotCreateOffer, +/// This items is not available for sale +ItemNotForSale, ``` \ No newline at end of file diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 97e3a941..90d89f36 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -429,11 +429,13 @@ impl Pallet { //ensure marketplace_id exits ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - //ensure the offer_id exists & get the offer data - let copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + //ensure the offer_id exists + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + // ensure the owner is the same as the authority - ensure!(copy_offer_data.creator == authority.clone(), Error::::CannotRemoveOffer); + let offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; + ensure!(offer_creator == authority.clone(), Error::::CannotRemoveOffer); //ensure the offer_id exists in OffersByItem Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; @@ -719,6 +721,7 @@ impl Pallet { fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { let timestamp: ::Moment = >::get(); + let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); @@ -773,26 +776,6 @@ impl Pallet { fn update_offers_status(buyer: T::AccountId, collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult{ let offer_ids = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; - //This logic is no longer needed, when an offer_id is accepted, - // all the other offers for this item are automatically closed. - // It doesn't matter the offertype. - - // let mut sell_offer_ids: BoundedVec<[u8;32], T::MaxOffersPerMarket> = BoundedVec::default(); - - // for offer_id in offer_ids { - // let offer_info = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - // //ensure the offer_type is SellOrder, because this vector also contains offers of BuyOrder OfferType. - // if offer_info.offer_type == OfferType::SellOrder { - // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; - // } - // // //version 2 - // // if let Some(offer) = >::get(offer_id) { - // // if offer.offer_type == OfferType::SellOrder { - // // sell_offer_ids.try_push(offer_id).map_err(|_| Error::::OfferStorageError)?; - // // } - // // } - // } - for offer_id in offer_ids { >::try_mutate::<_,_,DispatchError,_>(offer_id, |offer|{ let offer = offer.as_mut().ok_or(Error::::OfferNotFound)?; diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index cd148cc8..968abd50 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -513,6 +513,22 @@ pub mod pallet { Self::do_remove_marketplace(who, marketplace_id) } + /// Enlist a sell order. + /// + /// This extrinsic creates a sell order in the selected marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we want to create the sell order. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// - `price`: The price of the item. + /// + /// ### Considerations: + /// - You can only create a sell order in the marketplace if you are the owner of the item. + /// - You can create only one sell order for each item per marketplace. + /// - If the selected marketplace doesn't exist, it will throw an error. + /// - If the selected collection doesn't exist, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { @@ -521,6 +537,20 @@ pub mod pallet { Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, price) } + /// Accepts a sell order. + /// + /// This extrinsic accepts a sell order in the selected marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we want to accept the sell order. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// + /// ### Considerations: + /// - You don't need to be the owner of the item to accept the sell order. + /// - Once the sell order is accepted, the ownership of the item is transferred to the buyer. + /// - If you don't have the enough balance to accept the sell order, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { @@ -529,6 +559,20 @@ pub mod pallet { Self::do_take_sell_offer(who, offer_id, marketplace_id, collection_id, item_id) } + /// Allows a user to duplicate a sell order. + /// + /// This extrinsic allows a user to duplicate a sell order in any marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we want to duplicate the sell order. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// + /// ### Considerations: + /// - You can only duplicate a sell order if you are the owner of the item. + /// - The expiration date of the sell order is the same as the original sell order. + /// - You can update the price of the sell order. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn duplicate_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult { @@ -537,6 +581,23 @@ pub mod pallet { Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) } + + /// Delete an offer. + /// + /// This extrinsic deletes an offer in the selected marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we want to delete the offer. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// + /// ### Considerations: + /// - You can delete sell orders or buy orders. + /// - You can only delete an offer if you are the creator of the offer. + /// - Only open offers can be deleted. + /// - If you need to delete multiple offers for the same item, you need to + /// delete them one by one. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn remove_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { @@ -547,7 +608,21 @@ pub mod pallet { Self::do_remove_offer(who, offer_id, marketplace_id, collection_id, item_id) } - + /// Enlist a buy order. + /// + /// This extrinsic creates a buy order in the selected marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we want to create the buy order. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// - `price`: The price of the item. + /// + /// ### Considerations: + /// - Any user can create a buy order in the marketplace. + /// - An item can receive multiple buy orders at a time. + /// - You need to have the enough balance to create the buy order. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { @@ -556,6 +631,23 @@ pub mod pallet { Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, price) } + + /// Accepts a buy order. + /// + /// This extrinsic accepts a buy order in the selected marketplace. + /// + /// ### Parameters: + /// - `origin`: The user who performs the action. + /// - `marketplace_id`: The id of the marketplace where we accept the buy order. + /// - `collection_id`: The id of the collection. + /// - `item_id`: The id of the item inside the collection. + /// + /// ### Considerations: + /// - You need to be the owner of the item to accept a buy order. + /// - Owner of the item can accept only one buy order at a time. + /// - When an offer is accepted, all the other offers for this item are closed. + /// - The buyer needs to have the enough balance to accept the buy order. + /// - Once the buy order is accepted, the ownership of the item is transferred to the buyer. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { From 694462b604c868b3d1eac70e714c596071153a2f Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 16 Aug 2022 12:41:34 -0500 Subject: [PATCH 083/103] partially updates gated_marketplace unit tests --- pallets/gated-marketplace/Cargo.toml | 4 + pallets/gated-marketplace/src/functions.rs | 4 + pallets/gated-marketplace/src/mock.rs | 28 ++++++- pallets/gated-marketplace/src/tests.rs | 85 ++++++++++++++-------- 4 files changed, 91 insertions(+), 30 deletions(-) diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index ae92beab..d9ae3cb4 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -45,6 +45,10 @@ std = [ "frame-support/std", "frame-system/std", "frame-benchmarking/std", + "pallet-balances/std", + "pallet-uniques/std", + "pallet-fruniques/std", + "pallet-timestamp/std", "pallet-rbac/std" ] diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 52e2dc70..c23c5636 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -24,6 +24,10 @@ impl Pallet { // participant role and permissions let participant_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Participant.to_vec()].to_vec())?; T::Rbac::create_and_set_permissions(pallet_id, participant_role_id[0],["buy".as_bytes().to_vec(),"sell".as_bytes().to_vec()].to_vec() )?; + // appraiser role and permissions + let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Appraiser.to_vec()].to_vec())?; + // redemption specialist role and permissions + let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; Ok(()) } diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 267859a2..c6e7d3dd 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -23,6 +23,7 @@ frame_support::construct_runtime!( Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, Fruniques: pallet_fruniques::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + RBAC: pallet_rbac::{Pallet, Call, Storage, Event}, } ); @@ -89,6 +90,7 @@ impl pallet_gated_marketplace::Config for Test { type MaxOffersPerMarket = MaxOffersPerMarket; type MaxMarketsPerItem = MaxMarketsPerItem; type LocalCurrency = Balances; + type Rbac = RBAC; } impl pallet_fruniques::Config for Test { @@ -145,9 +147,33 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } + +parameter_types! { + pub const MaxScopesPerPallet: u32 = 2; + pub const MaxRolesPerPallet: u32 = 6; + pub const RoleMaxLen: u32 = 25; + pub const PermissionMaxLen: u32 = 25; + pub const MaxPermissionsPerRole: u32 = 5; + pub const MaxRolesPerUser: u32 = 2; + pub const MaxUsersPerRole: u32 = 2; +} +impl pallet_rbac::Config for Test { + type Event = Event; + type MaxScopesPerPallet = MaxScopesPerPallet; + type MaxRolesPerPallet = MaxRolesPerPallet; + type RoleMaxLen = RoleMaxLen; + type PermissionMaxLen = PermissionMaxLen; + type MaxPermissionsPerRole = MaxPermissionsPerRole; + type MaxRolesPerUser = MaxRolesPerUser; + type MaxUsersPerRole = MaxUsersPerRole; +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default().build_storage::().unwrap().into() + // TODO: get initial conf? + let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::default().build_storage::().unwrap().into(); + t.execute_with(|| GatedMarketplace::do_initial_setup().expect("Error on configuring initial setup")); + t } impl pallet_timestamp::Config for Test { diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index 4ed3e719..1e789b9a 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -1,10 +1,13 @@ -use crate::{mock::*, Error, types::*, Custodians}; +use crate::{mock::*, Error, types::*, Custodians, Config}; use std::vec; use sp_runtime::sp_std::vec::Vec; use codec::Encode; use frame_support::{assert_ok, BoundedVec, traits::{Len, ConstU32}, assert_noop}; +use pallet_rbac::{types::RoleBasedAccessControl}; use sp_io::hashing::blake2_256; +type rbac_err = pallet_rbac::Error; + fn create_label( label : &str ) -> BoundedVec { let s: Vec = label.as_bytes().into(); s.try_into().unwrap_or_default() @@ -227,7 +230,7 @@ fn enroll_rejected_has_feedback_works() { assert_ok!(GatedMarketplace::apply(Origin::signed(4),m_id, create_application_fields(1), None )); let app_id = GatedMarketplace::applications_by_account(3,m_id).unwrap(); // reject with account - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), false, feedback("We need to reject this application"))); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, feedback("We need to accept this application"))); // reject with application assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), false, feedback("We need to reject this application"))); @@ -302,7 +305,8 @@ fn add_authority_appraiser_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::Appraiser.id())); }); } @@ -312,7 +316,8 @@ fn add_authority_admin_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Admin]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Admin]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::Admin.id())); }); } @@ -322,7 +327,8 @@ fn add_authority_redenmption_specialist_works() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::RedemptionSpecialist.id())); }); } @@ -332,6 +338,8 @@ fn add_authority_owner_shouldnt_work() { assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Owner, m_id), Error::::OnlyOneOwnerIsAllowed); + let n_owners = ::Rbac::get_role_users_len(GatedMarketplace::pallet_id(), &m_id, &MarketplaceRole::Owner.id()); + assert_eq!(n_owners, 1); }); } @@ -355,7 +363,8 @@ fn remove_authority_appraiser_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); }); } @@ -366,7 +375,8 @@ fn remove_authority_admin_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); }); } @@ -377,7 +387,8 @@ fn remove_authority_redemption_specialist_work() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); }); } @@ -454,7 +465,7 @@ fn update_marketplace_user_without_permission_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(3), m_id, create_label("my marketplace2")), Error::::CannotEnroll); + assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(3), m_id, create_label("my marketplace2")), rbac_err::NotAuthorized); }); } @@ -491,7 +502,7 @@ fn remove_marketplace_user_without_permission_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(3), m_id), Error::::CannotEnroll); + assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(3), m_id), rbac_err::NotAuthorized); }); } @@ -516,17 +527,25 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works(){ assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![MarketplaceRole::Owner]); - assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![MarketplaceRole::Admin]); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); - assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); - + //assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![MarketplaceRole::Owner]); + assert_ok!(::Rbac::has_role(1, GatedMarketplace::pallet_id(), &m_id, vec![MarketplaceRole::Owner.id()])); + //assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![MarketplaceRole::Admin]); + assert_ok!(::Rbac::has_role(2, GatedMarketplace::pallet_id(), &m_id, vec![MarketplaceRole::Admin.id()])); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); + assert_ok!(::Rbac::has_role(3, GatedMarketplace::pallet_id(), &m_id, vec![MarketplaceRole::Appraiser.id()])); + //assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); + assert_ok!(::Rbac::has_role(4, GatedMarketplace::pallet_id(), &m_id, vec![MarketplaceRole::RedemptionSpecialist.id()])); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); - assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![]); - assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![]); - assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![]); + //assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![]); + assert!(RBAC::roles_by_user((1, GatedMarketplace::pallet_id(), m_id)).is_empty()); + //assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![]); + assert!(RBAC::roles_by_user((2, GatedMarketplace::pallet_id(), m_id)).is_empty()); + //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); + assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); + //assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![]); + assert!(RBAC::roles_by_user((4, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); } @@ -541,17 +560,25 @@ fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works(){ assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![1]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![2]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![3]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![4]); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![1]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Owner.id())).contains(&1)); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![2]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Admin.id())).contains(&2)); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![3]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Appraiser.id())).contains(&3)); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![4]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).contains(&4)); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![]); - assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![]); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Owner.id())).is_empty()); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Admin.id())).is_empty()); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Appraiser.id())).is_empty()); + //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![]); + assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).is_empty()); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); } @@ -653,7 +680,7 @@ fn remove_marketplace_deletes_storage_from_applicantions_by_account_works(){ let app_id = GatedMarketplace::applications_by_account(3,m_id).unwrap(); assert!(GatedMarketplace::applicants_by_marketplace(m_id, ApplicationStatus::Pending) == vec![3]); assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, default_feedback())); - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), true, default_feedback())); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), false, default_feedback())); assert!(GatedMarketplace::applications_by_account(3, m_id).is_some()); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); @@ -676,7 +703,7 @@ fn remove_marketplace_deletes_storage_from_applications_works(){ assert!(GatedMarketplace::applicants_by_marketplace(m_id, ApplicationStatus::Pending) == vec![3]); assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true,default_feedback())); - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), true, default_feedback())); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), false, default_feedback())); assert!(GatedMarketplace::applications(app_id).is_some()); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); From 6638a143f610a3d4cfd99cf4a45f1abf1133cb63 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 16 Aug 2022 17:38:40 -0500 Subject: [PATCH 084/103] fixes marketplace unit tests --- pallets/gated-marketplace/src/lib.rs | 6 +--- pallets/gated-marketplace/src/tests.rs | 42 ++++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index f94837f0..cf9d0b94 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -195,10 +195,6 @@ pub mod pallet { // Errors inform users that something went wrong. #[pallet::error] pub enum Error { - /// Work In Progress - NotYetImplemented, - /// Error names should be descriptive. - NoneValue, ///Limit bounded vector exceeded LimitExceeded, /// The account supervises too many marketplaces @@ -222,7 +218,7 @@ pub mod pallet { /// The specified marketplace does not exist MarketplaceNotFound, /// You need to be an owner or an admin of the marketplace - CannotEnroll, + NotOwnerOrAdmin, /// There was no change regarding the application status AlreadyEnrolled, /// There cannot be more than one owner per marketplace diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index 1e789b9a..ca926cd1 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -1,12 +1,12 @@ -use crate::{mock::*, Error, types::*, Custodians, Config}; +use crate::{mock::*, Error, types::*, Config}; use std::vec; use sp_runtime::sp_std::vec::Vec; use codec::Encode; use frame_support::{assert_ok, BoundedVec, traits::{Len, ConstU32}, assert_noop}; -use pallet_rbac::{types::RoleBasedAccessControl}; +use pallet_rbac::types::RoleBasedAccessControl; use sp_io::hashing::blake2_256; -type rbac_err = pallet_rbac::Error; +type RbacErr = pallet_rbac::Error; fn create_label( label : &str ) -> BoundedVec { let s: Vec = label.as_bytes().into(); @@ -89,12 +89,16 @@ fn duplicate_marketplaces_shouldnt_work() { } #[test] -fn exceeding_max_markets_per_auth_shouldnt_work() { +fn exceeding_max_roles_per_auth_shouldnt_work() { new_test_ext().execute_with(|| { - assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); - assert_noop!(GatedMarketplace::create_marketplace(Origin::signed(3),3, create_label("my marketplace 2")), Error::::ExceedMaxRolesPerAuth ); + let m_label = create_label("my marketplace"); + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, m_label.clone() )); + assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 2, MarketplaceRole::Appraiser, m_label.using_encoded(blake2_256))); + assert_noop!( + GatedMarketplace::add_authority(Origin::signed(1), 2, MarketplaceRole::RedemptionSpecialist, m_label.using_encoded(blake2_256) ), + RbacErr::ExceedMaxRolesPerUser + ); - // TODO: test ExceedMaxMarketsPerAuth when its possible to add new authorities }); } @@ -200,7 +204,7 @@ fn enroll_works() { // enroll with account assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, default_feedback())); // enroll with application - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), true, default_feedback())); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), false, default_feedback())); }); } @@ -212,7 +216,7 @@ fn enroll_rejected_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), None )); assert_ok!(GatedMarketplace::apply(Origin::signed(4),m_id, create_application_fields(1), None )); - let app_id = GatedMarketplace::applications_by_account(3,m_id).unwrap(); + let app_id = GatedMarketplace::applications_by_account(4,m_id).unwrap(); // reject with account assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), false, default_feedback())); // reject with application @@ -246,13 +250,13 @@ fn enroll_approved_has_feedback_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), None )); assert_ok!(GatedMarketplace::apply(Origin::signed(4),m_id, create_application_fields(1), None )); - let app_id = GatedMarketplace::applications_by_account(3,m_id).unwrap(); + let app_id = GatedMarketplace::applications_by_account(4,m_id).unwrap(); // reject with account - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, feedback("We've accepted your publication"))); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, feedback("We've rejected your publication"))); // reject with application - assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), true, feedback("We've accepted your publication"))); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Application(app_id), true, feedback("We've rejected your publication"))); - assert_eq!(boundedvec_to_string(&GatedMarketplace::applications(app_id).unwrap().feedback), String::from("We've accepted your publication")); + assert_eq!(boundedvec_to_string(&GatedMarketplace::applications(app_id).unwrap().feedback), String::from("We've rejected your publication")); }); } @@ -280,7 +284,7 @@ fn non_authorized_user_enroll_shouldnt_work() { assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id, create_application_fields(2), None )); // external user tries to enroll someone - assert_noop!(GatedMarketplace::enroll(Origin::signed(4), m_id , AccountOrApplication::Account(3), true, default_feedback()), Error::::CannotEnroll); + assert_noop!(GatedMarketplace::enroll(Origin::signed(4), m_id , AccountOrApplication::Account(3), true, default_feedback()), RbacErr::NotAuthorized); }); } @@ -349,7 +353,7 @@ fn add_authority_cant_apply_twice_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id), Error::::AlreadyApplied); + assert_noop!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id), RbacErr::UserAlreadyHasRole); }); } @@ -419,7 +423,7 @@ fn remove_authority_user_tries_to_remove_non_existent_role_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id), Error::::AuthorityNotFoundForUser); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id), RbacErr::RoleNotFound); }); } @@ -430,7 +434,7 @@ fn remove_authority_user_is_not_admin_or_owner_shouldnt_work(){ let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::Admin, m_id)); - assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 4,MarketplaceRole::Appraiser, m_id), Error::::CannotEnroll); + assert_noop!(GatedMarketplace::remove_authority(Origin::signed(3), 4,MarketplaceRole::Appraiser, m_id), RbacErr::NotAuthorized); }); } @@ -465,7 +469,7 @@ fn update_marketplace_user_without_permission_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(3), m_id, create_label("my marketplace2")), rbac_err::NotAuthorized); + assert_noop!(GatedMarketplace::update_label_marketplace(Origin::signed(3), m_id, create_label("my marketplace2")), RbacErr::NotAuthorized); }); } @@ -502,7 +506,7 @@ fn remove_marketplace_user_without_permission_shouldnt_work(){ assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1),2, create_label("my marketplace") )); let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); - assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(3), m_id), rbac_err::NotAuthorized); + assert_noop!(GatedMarketplace::remove_marketplace(Origin::signed(3), m_id), RbacErr::NotAuthorized); }); } From 9c667db20694617889fff4cdd5d843b3c41fa724 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 16 Aug 2022 22:17:39 -0500 Subject: [PATCH 085/103] removes unused susbtrate-rbac pallet --- pallets/substrate-rbac/Cargo.toml | 49 ----- pallets/substrate-rbac/README.md | 1 - pallets/substrate-rbac/src/lib.rs | 295 ------------------------------ 3 files changed, 345 deletions(-) delete mode 100644 pallets/substrate-rbac/Cargo.toml delete mode 100644 pallets/substrate-rbac/README.md delete mode 100644 pallets/substrate-rbac/src/lib.rs diff --git a/pallets/substrate-rbac/Cargo.toml b/pallets/substrate-rbac/Cargo.toml deleted file mode 100644 index 088b963f..00000000 --- a/pallets/substrate-rbac/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "substrate-rbac" -version = "4.0.0-dev" -description = "" -authors = ["Hashed > + IsType<::Event>; - - /// Origin for adding or removing a roles and permissions. - type RbacAdminOrigin: EnsureOrigin; - } - - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet(_); - - // The pallet's storage items. - #[pallet::storage] - #[pallet::getter(fn super_admins)] - pub type SuperAdmins = StorageMap<_, Blake2_128Concat, T::AccountId, ()>; - - #[pallet::storage] - #[pallet::getter(fn permissions)] - pub type Permissions = StorageMap<_, Blake2_128Concat, (T::AccountId, Role), ()>; - - #[pallet::storage] - #[pallet::getter(fn roles)] - pub type Roles = StorageMap<_, Blake2_128Concat, Role, ()>; - - #[pallet::genesis_config] - pub struct GenesisConfig { - pub super_admins: Vec, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - Self { - super_admins: Vec::new(), - } - } - } - - // The build of genesis for the pallet. - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - for admin in &self.super_admins { - >::insert(admin, ()); - } - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - AccessRevoked(T::AccountId, Vec), - AccessGranted(T::AccountId, Vec), - SuperAdminAdded(T::AccountId), - } - - #[pallet::error] - pub enum Error { - AccessDenied, - } - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet { - #[pallet::weight(0)] - pub fn create_role( - origin: OriginFor, - pallet_name: Vec, - permission: Permission, - ) -> DispatchResult { - ensure_signed(origin)?; - - let role = Role { - pallet: pallet_name, - permission, - }; - - Roles::::insert(role, ()); - - Ok(()) - } - - #[pallet::weight(0)] - pub fn assign_role( - origin: OriginFor, - account_id: T::AccountId, - role: Role, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - if Self::verify_manage_access(who, role.pallet.clone()) { - Self::deposit_event(Event::AccessGranted( - account_id.clone(), - role.pallet.clone(), - )); - >::insert((account_id, role), ()); - } else { - return Err(Error::::AccessDenied.into()); - } - - Ok(()) - } - - #[pallet::weight(0)] - pub fn revoke_access( - origin: OriginFor, - account_id: T::AccountId, - role: Role, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - if Self::verify_manage_access(who, role.pallet.clone()) { - Self::deposit_event(Event::AccessRevoked( - account_id.clone(), - role.pallet.clone(), - )); - >::remove((account_id, role)); - } else { - return Err(Error::::AccessDenied.into()); - } - - Ok(()) - } - - /// Add a new Super Admin. - /// Super Admins have access to execute and manage all pallets. - /// - /// Only _root_ can add a Super Admin. - #[pallet::weight(0)] - pub fn add_super_admin(origin: OriginFor, account_id: T::AccountId) -> DispatchResult { - T::RbacAdminOrigin::ensure_origin(origin)?; - >::insert(&account_id, ()); - Self::deposit_event(Event::SuperAdminAdded(account_id)); - Ok(()) - } - } -} - -#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] -pub enum Permission { - Execute = 1, - Manage = 2, -} - -impl Default for Permission { - fn default() -> Self { - Permission::Execute - } -} - -#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] -pub struct Role { - pallet: Vec, - permission: Permission, -} - -impl Pallet { - pub fn verify_execute_access(account_id: T::AccountId, pallet: Vec) -> bool { - let role = Role { - pallet, - permission: Permission::Execute, - }; - - if >::contains_key(&role) && >::contains_key((account_id, role)) { - return true; - } - - false - } - - fn verify_manage_access(account_id: T::AccountId, pallet: Vec) -> bool { - let role = Role { - pallet, - permission: Permission::Manage, - }; - - if >::contains_key(&role) && >::contains_key((account_id, role)) { - return true; - } - - false - } -} - -/// The following section implements the `SignedExtension` trait -/// for the `Authorize` type. -/// `SignedExtension` is being used here to filter out the not authorized accounts -/// when they try to send extrinsics to the runtime. -/// Inside the `validate` function of the `SignedExtension` trait, -/// we check if the sender (origin) of the extrinsic has the execute permission or not. -/// The validation happens at the transaction queue level, -/// and the extrinsics are filtered out before they hit the pallet logic. - -/// The `Authorize` struct. -#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct Authorize(sp_std::marker::PhantomData); - -/// Debug impl for the `Authorize` struct. -impl sp_std::fmt::Debug for Authorize { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "Authorize") - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl Authorize { - pub fn new() -> Self { - Self(sp_std::marker::PhantomData) - } -} - -impl SignedExtension for Authorize -where - T::Call: Dispatchable + GetCallMetadata, -{ - type AccountId = T::AccountId; - type Call = T::Call; - type AdditionalSigned = (); - type Pre = (); - const IDENTIFIER: &'static str = "Authorize"; - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { - Ok(()) - } - - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - let md = call.get_call_metadata(); - - if >::contains_key(who.clone()) { - print("Access Granted!"); - Ok(Default::default()) - } else if >::verify_execute_access( - who.clone(), - md.pallet_name.as_bytes().to_vec(), - ) { - print("Access Granted!"); - Ok(Default::default()) - } else { - print("Access Denied!"); - Err(InvalidTransaction::Call.into()) - } - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf, - len: usize, - ) -> Result { - let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?; - Ok((self.0, who.clone(), imbalance)) - } - -} \ No newline at end of file From 2291d78969f92ff3f3f9b6e293aad9fe88135ec9 Mon Sep 17 00:00:00 2001 From: didiermis Date: Wed, 17 Aug 2022 09:44:47 -0500 Subject: [PATCH 086/103] Add Unit Tests for offers. --- pallets/gated-marketplace/Cargo.toml | 2 + pallets/gated-marketplace/src/functions.rs | 11 +- pallets/gated-marketplace/src/mock.rs | 2 +- pallets/gated-marketplace/src/tests.rs | 624 ++++++++++++++++++++- 4 files changed, 630 insertions(+), 9 deletions(-) diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index e1a6a23f..6b84381a 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -43,6 +43,8 @@ std = [ "frame-support/std", "frame-system/std", "frame-benchmarking/std", + "pallet-balances/std", + "pallet-timestamp/std", ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 90d89f36..126ca100 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -152,8 +152,7 @@ impl Pallet { } pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { - //TODO: ensure the user is a Marketparticipant - + //This function is only called by the owner of the marketplace //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -428,10 +427,13 @@ impl Pallet { pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, ) -> DispatchResult { //ensure marketplace_id exits ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - + //ensure the offer_id exists ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + //ensure the offer status is Open + ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); + // ensure the owner is the same as the authority let offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; @@ -440,9 +442,6 @@ impl Pallet { //ensure the offer_id exists in OffersByItem Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - //ensure the offer status is Open - ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); - //remove the offer from OfferInfo >::remove(offer_id); diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 267859a2..2f31cea6 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -23,7 +23,7 @@ frame_support::construct_runtime!( Uniques: pallet_uniques::{Pallet, Call, Storage, Event}, Fruniques: pallet_fruniques::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, } ); diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index c80837b3..fca5a3ed 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -1,8 +1,8 @@ -use crate::{mock::*, Error, types::*, Custodians}; +use crate::{mock::*, Error, types::*}; use std::vec; use sp_runtime::sp_std::vec::Vec; use codec::Encode; -use frame_support::{assert_ok, BoundedVec, traits::{Len, ConstU32}, assert_noop}; +use frame_support::{assert_ok, BoundedVec, traits::{Len, ConstU32, Currency}, assert_noop}; use sp_io::hashing::blake2_256; fn create_label( label : &str ) -> BoundedVec { @@ -28,6 +28,10 @@ fn boundedvec_to_string(boundedvec: &BoundedVec) -> String { s } +fn _find_id(vec_tor: BoundedVec<[u8;32], ConstU32<100>>, id:[u8;32]) -> bool { + vec_tor.iter().find(|&x| *x == id).ok_or(Error::::OfferNotFound).is_ok() +} + fn _create_file(name: &str, cid: &str, create_custodian_file: bool) -> ApplicationField { let display_name_vec: Vec = name.as_bytes().into(); let display_name: BoundedVec> = display_name_vec.try_into().unwrap_or_default(); @@ -757,3 +761,619 @@ fn reapply_works() { }); } +//Offers +#[test] +fn enlist_sell_offer_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().offer_type, OfferType::SellOrder); + + }); +} + + +#[test] +fn enlist_sell_offer_item_does_not_exist_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 1, 10000), Error::::CollectionNotFound); + }); +} + + +#[test] +fn enlist_sell_offer_item_already_enlisted_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000), Error::::OfferAlreadyExists); + }); +} + +#[test] +fn enlist_sell_offer_not_owner_tries_to_enlist_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(2), m_id, 0, 0, 10000), Error::::NotOwner); + }); +} + +#[test] +fn enlist_sell_offer_price_must_greater_than_zero_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_noop!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 0), Error::::PriceMustBeGreaterThanZero); + }); +} + +#[test] +fn enlist_sell_offer_price_must_greater_than_minimun_amount_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + let minimum_amount = 1001; + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, minimum_amount)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + }); +} + +#[test] +fn enlist_sell_offer_is_properly_stored_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + assert_eq!(GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(), offer_id); + assert_eq!(GatedMarketplace::offers_by_marketplace(m_id).iter().next().unwrap().clone(), offer_id); + + }); +} + + +#[test] +fn enlist_sell_offer_two_marketplaces(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace2"))); + let m_id2 = create_label("my marketplace2").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id2, 0, 0, 11000)); + + assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 2); + assert_eq!(GatedMarketplace::offers_by_account(1).len(), 2); + assert_eq!(GatedMarketplace::offers_by_marketplace(m_id).len(), 1); + assert_eq!(GatedMarketplace::offers_by_marketplace(m_id2).len(), 1); + + }); +} + +#[test] +fn enlist_buy_offer_works() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1100)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id2).is_some()); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + }); +} + +#[test] +fn enlist_buy_offer_item_is_not_for_sale_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_noop!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 1, 1100), Error::::ItemNotForSale); + }); +} + + +#[test] +fn enlist_buy_offer_owner_cannnot_create_buy_offers_for_their_own_items_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_noop!(GatedMarketplace::enlist_buy_offer(Origin::signed(1), m_id, 0, 0, 1100), Error::::CannotCreateOffer); + }); +} + +#[test] +fn enlist_buy_offer_user_does_not_have_enough_balance_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 100); + + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_noop!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 10000), Error::::NotEnoughBalance); + + }); +} + +#[test] +fn enlist_buy_offer_price_must_greater_than_zero_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_noop!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 0), Error::::PriceMustBeGreaterThanZero); + }); +} + + + +#[test] +fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1101); + Balances::make_free_balance_be(&3, 1201); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 10000)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1100)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id2).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(3), m_id, 0, 0, 1200)); + let offer_id3 = GatedMarketplace::offers_by_account(3).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id3).is_some()); + + assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 3); + + }); + +} + +#[test] +fn take_sell_offer_works(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + + assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id, 0, 0)); + assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); + assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); + + }); +} + +#[test] +fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id, m_id, 0, 0), Error::::CannotTakeOffer); + }); +} + +#[test] +fn take_sell_offer_id_does_not_exist_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + let offer_id2 = offer_id.using_encoded(blake2_256); + + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::OfferNotFound); + }); +} + + +#[test] +fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1100); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); + let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); + + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id, 0, 0), Error::::NotEnoughBalance); + }); +} + +#[test] +fn take_buy_offer_works(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0)); + assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); + assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); + }); +} + +#[test] +fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::NotOwner); + }); +} + +#[test] +fn take_buy_offer_id_does_not_exist_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + + let offer_id3 = offer_id2.using_encoded(blake2_256); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id3, m_id, 0, 0), Error::::OfferNotFound); + + }); +} + +#[test] +fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + + Balances::make_free_balance_be(&2, 0); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0), Error::::NotEnoughBalance); + }); +} + +#[test] +fn remove_sell_offer_works(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(1), offer_id, m_id, 0, 0)); + assert_eq!(GatedMarketplace::offers_by_account(1).len(), 0); + assert!(GatedMarketplace::offers_info(offer_id).is_none()); + }); +} + +#[test] +fn remove_buy_offer_works(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1001)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id2).is_some()); + + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0)); + assert_eq!(GatedMarketplace::offers_by_account(2).len(), 0); + assert!(GatedMarketplace::offers_info(offer_id2).is_none()); + }); +} + +#[test] +fn remove_offer_id_does_not_exist_sholdnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + let offer_id2 = offer_id.using_encoded(blake2_256); + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::OfferNotFound); + }); +} + +#[test] +fn remove_offer_creator_doesnt_match_sholdnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id, m_id, 0, 0), Error::::CannotRemoveOffer); + }); +} + +#[test] +fn remove_offer_status_is_closed_shouldnt_work(){ + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + Balances::make_free_balance_be(&2, 1300); + + assert_ok!(GatedMarketplace::create_marketplace(Origin::signed(1), 2, create_label("my marketplace"))); + let m_id = create_label("my marketplace").using_encoded(blake2_256); + + assert_ok!(Uniques::create(Origin::signed(1), 0, 1)); + assert_ok!(Uniques::mint(Origin::signed(1), 0, 0, 1)); + assert_eq!(Uniques::owner(0, 0).unwrap(), 1); + + assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1001)); + let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); + assert!(GatedMarketplace::offers_info(offer_id).is_some()); + + assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(2), m_id, 0, 0, 1200)); + let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); + assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); + + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0)); + assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); + assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); + + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::CannotDeleteOffer); + }); + +} \ No newline at end of file From 626f4f01524714fdaee9f69e21d62048ef573861 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 17 Aug 2022 11:01:18 -0500 Subject: [PATCH 087/103] partial marketplaces readme changes Before updating to the latest offer functionality --- pallets/gated-marketplace/README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pallets/gated-marketplace/README.md b/pallets/gated-marketplace/README.md index 3d885d7b..eb95e634 100644 --- a/pallets/gated-marketplace/README.md +++ b/pallets/gated-marketplace/README.md @@ -11,8 +11,8 @@ Create marketplaces that require previous authorization before placing sell and - [Polkadot-js CLI](#polkadot-js-cli) - [Create a marketplace](#create-a-marketplace) - [Get a marketplace](#get-a-marketplace) - - [Get what permissions does an account have on a marketplace](#get-what-permissions-does-an-account-have-on-a-marketplace) - - [Get all the accounts that have a certain permission on a marketplace](#get-all-the-accounts-that-have-a-certain-permission-on-a-marketplace) + - [Get what roles does an account have on a marketplace](#get-what-roles-does-an-account-have-on-a-marketplace) + - [Get all the accounts that have a certain role on a marketplace](#get-all-the-accounts-that-have-a-certain-role-on-a-marketplace) - [Apply to a marketplace (without custodian)](#apply-to-a-marketplace-without-custodian) - [Apply to a marketplace (with custodian)](#apply-to-a-marketplace-with-custodian) - [Get an application](#get-an-application) @@ -26,8 +26,8 @@ Create marketplaces that require previous authorization before placing sell and - [Polkadot-js api (javascript library)](#polkadot-js-api-javascript-library) - [Create a marketplace](#create-a-marketplace-1) - [Get a marketplace](#get-a-marketplace-1) - - [Get what permissions does an account have on a marketplace](#get-what-permissions-does-an-account-have-on-a-marketplace-1) - - [Get all the accounts that have a certain permission on a marketplace](#get-all-the-accounts-that-have-a-certain-permission-on-a-marketplace-1) + - [Get what permissions does an account have on a marketplace](#get-what-permissions-does-an-account-have-on-a-marketplace) + - [Get all the accounts that have a certain permission on a marketplace](#get-all-the-accounts-that-have-a-certain-permission-on-a-marketplace) - [Apply to a marketplace (without custodian)](#apply-to-a-marketplace-without-custodian-1) - [Apply to a marketplace (with custodian)](#apply-to-a-marketplace-with-custodian-1) - [Get an application](#get-an-application-1) @@ -76,8 +76,6 @@ This module allows to: ### Getters - `marketplaces` -- `marketplaces_by_authority` (double storage map) -- `authorities_by_marketplace` (double storage map) - `applications` - `applications_by_account` (double storage map) - `applicants_by_marketplace` (double storage map) @@ -131,7 +129,7 @@ polkadot-js-api query.gatedMarketplace.marketplaces "0xace33a53e2c1a5c7fa2f92033 } ``` -#### Get what permissions does an account have on a marketplace +#### Get what roles does an account have on a marketplace ```bash # account_id, marketplace_id polkadot-js-api query.gatedMarketplace.marketplacesByAuthority "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" @@ -145,7 +143,7 @@ polkadot-js-api query.gatedMarketplace.marketplacesByAuthority "5GrwvaEF5zXb26Fz } ``` -#### Get all the accounts that have a certain permission on a marketplace +#### Get all the accounts that have a certain role on a marketplace ```bash # marketplace_id, type of authoriry (it can be "Owner", "Admin" or "Appraiser") polkadot-js-api query.gatedMarketplace.authoritiesByMarketplace "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" "Admin" From 2dad5eab44fced314264fcd2bca01424d5a4f648 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 18 Aug 2022 19:21:42 -0500 Subject: [PATCH 088/103] adds offer tutorials --- pallets/gated-marketplace/README.md | 428 ++++++++++++++++++++++++---- 1 file changed, 380 insertions(+), 48 deletions(-) diff --git a/pallets/gated-marketplace/README.md b/pallets/gated-marketplace/README.md index fdc4bc12..b79159b0 100644 --- a/pallets/gated-marketplace/README.md +++ b/pallets/gated-marketplace/README.md @@ -23,6 +23,16 @@ Create marketplaces that require previous authorization before placing sell and - [Enroll an applicant (by its application id)](#enroll-an-applicant-by-its-application-id) - [Add authority user to marketplace](#add-authority-user-to-marketplace) - [Remove authority user to marketplace](#remove-authority-user-to-marketplace) + - [Put an asset on sale](#put-an-asset-on-sale) + - [Put a buy offer](#put-a-buy-offer) + - [Get offer details](#get-offer-details) + - [Get offers by item](#get-offers-by-item) + - [Get offers by account](#get-offers-by-account) + - [Get offers by marketplace](#get-offers-by-marketplace) + - [Duplicate offer](#duplicate-offer) + - [Remove offer](#remove-offer) + - [Take sell offer - direct purchase](#take-sell-offer---direct-purchase) + - [Take buy offer](#take-buy-offer) - [Polkadot-js api (javascript library)](#polkadot-js-api-javascript-library) - [Create a marketplace](#create-a-marketplace-1) - [Get a marketplace](#get-a-marketplace-1) @@ -38,6 +48,16 @@ Create marketplaces that require previous authorization before placing sell and - [Enroll an applicant (by its application id)](#enroll-an-applicant-by-its-application-id-1) - [Add authority user to marketplace](#add-authority-user-to-marketplace-1) - [Remove authority user to marketplace](#remove-authority-user-to-marketplace-1) + - [Put an asset on sale](#put-an-asset-on-sale-1) + - [Put a buy offer](#put-a-buy-offer-1) + - [Get offer details](#get-offer-details-1) + - [Get offers by item](#get-offers-by-item-1) + - [Get offers by account](#get-offers-by-account-1) + - [Get offers by marketplace](#get-offers-by-marketplace-1) + - [Duplicate offer in another marketplace](#duplicate-offer-in-another-marketplace) + - [Remove offer](#remove-offer-1) + - [Take sell offer - direct purchase](#take-sell-offer---direct-purchase-1) + - [Take buy offer](#take-buy-offer-1) - [Events](#events) - [Errors](#errors) @@ -87,8 +107,6 @@ This module allows to: |Name| Type | |--|--| |`marketplaces`| storage map| -|`marketplaces_by_authority`|double storage map| -|`authorities_by_marketplace`|double storage map| |`applications`| storage map| |`applications_by_account`|double storage map| |`applicants_by_marketplace`|double storage map| @@ -148,28 +166,28 @@ polkadot-js-api query.gatedMarketplace.marketplaces "0xace33a53e2c1a5c7fa2f92033 #### Get what roles does an account have on a marketplace ```bash -# account_id, marketplace_id -polkadot-js-api query.gatedMarketplace.marketplacesByAuthority "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" +# account_id, pallet_id, marketplace_id +polkadot-js-api query.rbac.rolesByUser "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" 20 "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" ``` ```bash # Output should look like this: { - "marketplacesByAuthority": [ - "Owner" + "rolesByUser": [ + "0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01" ] } ``` #### Get all the accounts that have a certain role on a marketplace ```bash -# marketplace_id, type of authoriry (it can be "Owner", "Admin" or "Appraiser") -polkadot-js-api query.gatedMarketplace.authoritiesByMarketplace "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" "Admin" +# pallet_id, marketplace_id, role_id +polkadot-js-api query.rbac.usersByScope 20 "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b" ``` ```bash # Output should look like this: { - "authoritiesByMarketplace": [ - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + "usersByScope": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" ] } ``` @@ -251,15 +269,15 @@ polkadot-js-api query.gatedMarketplace.custodians "5DAAnrj7VHTznn2AWBemMuyBwZWs6 #### Enroll an applicant (by its account) ```bash # It can only be called by the marketplace owner (Alice) or administrator (Bob) -# market_id, accountOrApplicationEnumerator, approve boolean -polkadot-js-api tx.gatedMarketplace.enroll "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" '{"Account":"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"}' true --seed "//Bob" +# market_id, accountOrApplicationEnumerator, feedback, approve boolean +polkadot-js-api tx.gatedMarketplace.enroll "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" '{"Account":"5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"}' "feedback" true --seed "//Bob" ``` #### Enroll an applicant (by its application id) ```bash # It can be called by the marketplace owner (Alice) or administrator (Bob) # market_id, accountOrApplicationEnumerator, approve boolean -polkadot-js-api tx.gatedMarketplace.enroll "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" '{"Application":"0x9ab75a44b507c0030296dd3660bd77d606807cf3415c3409b88c2cad36fd5483"}' true --seed "//Bob" +polkadot-js-api tx.gatedMarketplace.enroll "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" '{"Application":"0x9ab75a44b507c0030296dd3660bd77d606807cf3415c3409b88c2cad36fd5483"}' "feedback" true --seed "//Bob" ``` #### Add authority user to marketplace @@ -276,6 +294,112 @@ polkadot-js-api tx.gatedMarketplace.addAuthority "5DAAnrj7VHTznn2AWBemMuyBwZWs6F polkadot-js-api tx.gatedMarketplace.removeAuthority "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy" "Appraiser" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" --seed "//Alice" ``` +#### Put an asset on sale +```bash +# marketplace_id, collection_id, item_id, sell price +polkadot-js-api tx.gatedMarketplace.enlistSellOffer "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" 0 0 10000 --seed "//Charlie" +``` + +#### Put a buy offer +```bash +# marketplace_id, collection_id, item_id, buy price +polkadot-js-api tx.gatedMarketplace.enlistBuyOffer "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" 0 0 10001 --seed "//Dave" +``` + +#### Get offer details +```bash +polkadot-js-api query.gatedMarketplace.offersInfo "0x9abbb3e227dedf26a4a64705ffb924ef8d48dc47de981f4db799790ae2239e6b" +``` + +```bash +# Output should look like this +{ + "offersInfo": { + "marketplaceId": "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", + "collectionId": "0", + "itemId": "0", + "creator": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + "price": "10,000", + "status": "Open", + "creationDate": "1,660,778,892,000", + "expirationDate": "1,661,383,692,000", + "offerType": "SellOrder", + "buyer": null + } +} +``` + +#### Get offers by item +```bash +# collection_id, item_id +polkadot-js-api query.gatedMarketplace.offersByItem 0 0 +``` + +```bash +# Output should look similar +{ + "offersByItem": [ + "0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb", + "0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0" + ] +} +``` + +#### Get offers by account +```bash +# account_id +polkadot-js-api query.gatedMarketplace.offersByAccount 5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y +``` + +```bash +{ + "offersByAccount": [ + "0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb" + ] +} +``` + + +#### Get offers by marketplace +```bash +# marketplace_id +polkadot-js-api query.gatedMarketplace.offersByMarketplace 0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95 +``` + +```bash +# Output should llok like this +{ + "offersByMarketplace": [ + "0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb", + "0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0" + ] +} +``` + +#### Duplicate offer + +```bash +polkadot-js-api tx.gatedMarketplace.duplicateOffer "0x65c7f4fa353a2212c2db497a8a1ad073453aad2030be7f756cba42a2f976dc82" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" 0 0 10002 --seed "//Charlie" +``` + +#### Remove offer + +```bash +# offer_id, marketplace_id, collection_id, item_id +polkadot-js-api tx.gatedMarketplace.removeOffer "0x8cb8cc124e19fc58eaf9c6dbd0953a7fd955769e6d3983ce2ea83d64d742a62e" "0xa1c17609528fe2630b3be72d6ac8eafc5e0ef95ce78ddad70e83e5fa77ac7342" 0 0 --seed "//Charlie" +``` + +#### Take sell offer - direct purchase +```bash +polkadot-js-api tx.gatedMarketplace.takeSellOffer "0x65c7f4fa353a2212c2db497a8a1ad073453aad2030be7f756cba42a2f976dc82" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" 0 0 --seed "//Dave" +``` + +#### Take buy offer +```bash +# offer_id, marketplace_id, collection_id, item_id +polkadot-js-api tx.gatedMarketplace.takeBuyOffer "0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0" "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95" 0 0 --seed "//Charlie" +``` + ### Polkadot-js api (javascript library) While most of the data flow is almost identical to its CLI counter part, the javascript library is much more versatile regarding queries. The API setup will be ommited. @@ -312,42 +436,56 @@ key marketplace_id: [ #### Get what permissions does an account have on a marketplace ```js -const marketplacesByAuth = await api.query.gatedMarketplace.marketplacesByAuthority("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); - console.log(marketplacesByAuth.toHuman() ); +// account_id, pallet_id, scope_id +const rolesByUser = await api.query.rbac.rolesByUser(alice.address,20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); + console.log(rolesByUser.toHuman()); ``` ```bash # Output should look like this: -[ 'Owner' ] +['0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b'] ``` ```js // get all users permissions on all marketplaces -const allMarketplacesByAuth = await api.query.gatedMarketplace.marketplacesByAuthority.entries(); -allMarketplacesByAuth.forEach(([key, exposure]) => { - console.log('Authority account and marketplace_id:', key.args.map((k) => k.toHuman())); - console.log(' type of authority:', exposure.toHuman(),"\n"); +const all_roles_by_user = await api.query.rbac.rolesByUser.entries(); +all_roles_by_user.forEach(([key, exposure]) => { + console.log('account_id, pallet_id, scope_id:', key.args.map((k) => k.toHuman())); + console.log(' role_ids', exposure.toHuman()); }); ``` ```bash -Authority account and marketplace_id: [ +account_id, pallet_id, scope_id: [ '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + '20', '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' ] - type of authority: [ 'Admin' ] - -Authority account and marketplace_id: [ + role_ids [ + '0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01' +] +account_id, pallet_id, scope_id: [ + '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', + '20', + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' +] + role_ids [ + '0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b' +] +account_id, pallet_id, scope_id: [ '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + '20', '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' ] - type of authority: [ 'Owner' ] + role_ids [ + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b' +] ``` #### Get all the accounts that have a certain permission on a marketplace ```js -//marketplace_id, type of authoriry (it can be "Owner", "Admin" or "Appraiser") -const authoritiesByMarketplace = await api.query.gatedMarketplace.authoritiesByMarketplace("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95","Admin"); - console.log(authoritiesByMarketplace.toHuman()); +//pallet_id, marketplace_id, type of authoriry (it can be "Owner", "Admin" or "Appraiser") +const usersByScope = await api.query.rbac.usersByScope(20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", "0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b"); +console.log(usersByScope.toHuman()); ``` ```bash # Output should look like this: @@ -356,25 +494,32 @@ const authoritiesByMarketplace = await api.query.gatedMarketplace.authoritiesByM ```js // get all the accounts in a marketplace -const authoritiesByMarketplace = await api.query.gatedMarketplace.authoritiesByMarketplace.entries(); -authoritiesByMarketplace.forEach(([key, exposure]) => { - console.log('marketplace_id and type of authority:', key.args.map((k) => k.toHuman())); - console.log(' accounts that have the role within the marketplace:', exposure.toHuman(),"\n"); +const scope_users_by_role = await api.query.rbac.usersByScope.entries(20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); + scope_users_by_role.forEach(([key, exposure]) => { + console.log('pallet_id, scope_id, role_id:', key.args.map((k) => k.toHuman())); + console.log(' account_id', exposure.toHuman()); }); ``` ```bash # Expected output: -marketplace_id and type of authority: [ +pallet_id, scope_id, role_id: [ + '20', '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', - 'Admin' + '0xae9e025522f868c39b41b8a5ba513335a2a229690bd44c71c998d5a9ad38162b' ] - accounts that have the role within the marketplace: [ '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty' ] - -marketplace_id and type of authority: [ + account_id [ '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y' ] +pallet_id, scope_id, role_id: [ + '20', + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', + '0x08aef7203969e2467b33b14965dfab62e11b085610c798b3cac150b1d7ea033b' +] + account_id [ '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' ] +pallet_id, scope_id, role_id: [ + '20', '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', - 'Owner' + '0xc1237f9841c265fb722178da01a1e088c25fb892d6b7cd9634a20ac84bb3ee01' ] - accounts that have the role within the marketplace: [ '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' ] + account_id [ '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty' ] ``` #### Apply to a marketplace (without custodian) @@ -526,16 +671,16 @@ custodians.forEach(([key, exposure]) => { #### Enroll an applicant (by its account) ```bash # It can only be called by the marketplace owner (Alice) or administrator (Bob) -# market_id, accountOrApplicationEnumerator, approve boolean -const enroll = await api.tx.gatedMarketplace.enroll("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", {"Account":charlie.address}, true).signAndSend(alice); +# market_id, accountOrApplicationEnumerator, feedback, approve boolean +const enroll = await api.tx.gatedMarketplace.enroll("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", {"Account":charlie.address}, "feedback", true).signAndSend(alice); ``` #### Enroll an applicant (by its application id) ```bash # It can be called by the marketplace owner (Alice) or administrator (Bob) -# market_id, accountOrApplicationEnumerator, approve boolean -const enroll = await api.tx.gatedMarketplace.enroll("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", {"Application":"0x9ab75a44b507c0030296dd3660bd77d606807cf3415c3409b88c2cad36fd5483"}, true).signAndSend(alice); +# market_id, accountOrApplicationEnumerator, feedback, approve boolean +const enroll = await api.tx.gatedMarketplace.enroll("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", {"Application":"0x9ab75a44b507c0030296dd3660bd77d606807cf3415c3409b88c2cad36fd5483"}, "feedback", true).signAndSend(alice); ``` @@ -555,6 +700,193 @@ const removeAuthority = await api.tx.gatedMarketplace.removeAuthority(dave.addre ``` +#### Put an asset on sale +```js +// marketplace_id, collection_id, item_id, sell price +const sell = await api.tx.gatedMarketplace.enlistSellOffer("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95",0,0,10000).signAndSend(charlie); +``` + +#### Put a buy offer +```js +// marketplace_id, collection_id, item_id, buy price +const buy = await api.tx.gatedMarketplace.enlistBuyOffer("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", 0,0, 10001).signAndSend(dave); +``` + +#### Get offer details +```js +const offer_info = await api.query.gatedMarketplace.offersInfo("0x9abbb3e227dedf26a4a64705ffb924ef8d48dc47de981f4db799790ae2239e6b"); + console.log(offer_info.toHuman()); +``` + +```bash +# Output should look like this +{ + marketplaceId: '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', + collectionId: '0', + itemId: '0', + creator: '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', + price: '10,000', + status: 'Open', + creationDate: '1,660,778,892,000', + expirationDate: '1,661,383,692,000', + offerType: 'SellOrder', + buyer: null +} +``` + +```js +// Get details of all offers +const all_offers = await api.query.gatedMarketplace.offersInfo.entries() +all_offers.forEach(([key, exposure]) => { + console.log('offer_id:', key.args.map((k) => k.toHuman())); + console.log('offer details:', exposure.toHuman()); +}); +``` + +```bash +# Output should look like this +offer_id: [ + '0x9abbb3e227dedf26a4a64705ffb924ef8d48dc47de981f4db799790ae2239e6b' +] +offer details: { + marketplaceId: '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95', + collectionId: '0', + itemId: '0', + creator: '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y', + price: '10,000', + status: 'Open', + creationDate: '1,660,778,892,000', + expirationDate: '1,661,383,692,000', + offerType: 'SellOrder', + buyer: null +} +# ... +``` + +#### Get offers by item +```js +const offers_by_item = await api.query.gatedMarketplace.offersByItem(0,0); +console.log(offers_by_item.toHuman()); +``` + +```bash +[ + '0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb', + '0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0' +] +``` + +```js +// Get all offers in the whole collection +// collection_id, could get omitted to get all offers from all assets, grouped by collection. +const offers_by_collection = await api.query.gatedMarketplace.offersByItem.entries(0); +offers_by_collection.forEach(([key, exposure]) => { + console.log('collection_id, item_id:', key.args.map((k) => k.toHuman())); + console.log('offer_ids:', exposure.toHuman()); +}); +``` + +```bash +# Output should look like this +collection_id, item_id: [ '0', '0' ] +offer_ids: [ + '0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb', + '0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0' +] +``` + +#### Get offers by account +```js +// account_id +const offers_by_account = await api.query.gatedMarketplace.offersByAccount(charlie.address); +console.log(offers_by_account.toHuman()); +``` + +```bash +['0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb'] +``` + +```js +const all_offers_by_account = await api.query.gatedMarketplace.offersByAccount.entries(); +all_offers_by_account.forEach(([key, exposure]) => { + console.log('account_id:', key.args.map((k) => k.toHuman())); + console.log('offer_ids:', exposure.toHuman()); +}); +``` + +```bash +account_id: [ '5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy' ] +offer_ids: [ + '0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0' +] +account_id: [ '5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y' ] +offer_ids: [ + '0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb' +] +``` + + +#### Get offers by marketplace + +```js +// marketplace_id +const offers_by_market = await api.query.gatedMarketplace.offersByMarketplace("0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95") + console.log(offers_by_market.toHuman()); +``` + +```bash +# output should look like this +[ + '0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb', + '0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0' +] +``` + +```js +// All offers by marketplace +const all_offers_by_market = await api.query.gatedMarketplace.offersByMarketplace.entries(); +all_offers_by_market.forEach(([key, exposure]) => { + console.log('marketplace_id:', key.args.map((k) => k.toHuman())); + console.log('offer_ids:', exposure.toHuman()); +}); +``` + +```bash +# output should look like this +marketplace_id: [ + '0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95' +] +offer_ids: [ + '0x4508b428b15e1a0a0138d36efebe3382739726beca8d67239e02a56c19d378eb', + '0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0' +] +``` + +#### Duplicate offer in another marketplace +```js +/// offer_id, marketplace_id, collection_id, item_id, price on that marketplace +const duplicate_offer = await api.tx.gatedMarketplace.duplicateOffer("0x65c7f4fa353a2212c2db497a8a1ad073453aad2030be7f756cba42a2f976dc82","0xa1c17609528fe2630b3be72d6ac8eafc5e0ef95ce78ddad70e83e5fa77ac7342", 0, 0, 10002).signAndSend(charlie) +``` + +#### Remove offer +```js +/// offer_id, marketplace_id, collection_id, item_id +const remove_offer = await api.tx.gatedMarketplace.removeOffer("0x8cb8cc124e19fc58eaf9c6dbd0953a7fd955769e6d3983ce2ea83d64d742a62e", "0xa1c17609528fe2630b3be72d6ac8eafc5e0ef95ce78ddad70e83e5fa77ac7342", 0, 0).signAndSend(charlie) +``` + +#### Take sell offer - direct purchase +```js +/// offer_id, marketplace_id, collection_id, item_id +const take_sell_offer = await api.tx.gatedMarketplace.takeSellOffer("0x65c7f4fa353a2212c2db497a8a1ad073453aad2030be7f756cba42a2f976dc82", "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", 0, 0).signAndSend(dave) +``` + +#### Take buy offer + +```js +/// offer_id, marketplace_id, collection_id, item_id +const take_buy_offer = await api.tx.gatedMarketplace.takeBuyOffer("0x66fcfadc174a596d8f8dc1b067038ed0056c5c3127d6996bc54fa05148caccf0", "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95", 0, 0).signAndSend(charlie) +``` + ## Events ```rust @@ -592,10 +924,8 @@ const removeAuthority = await api.tx.gatedMarketplace.removeAuthority(dave.addre ## Errors ```rust -/// Work In Progress -NotYetImplemented, -/// Error names should be descriptive. -NoneValue, +///Limit bounded vector exceeded +LimitExceeded, /// The account supervises too many marketplaces ExceedMaxMarketsPerAuth, /// The account has too many roles in that marketplace @@ -617,7 +947,9 @@ AlreadyApplied, /// The specified marketplace does not exist MarketplaceNotFound, /// You need to be an owner or an admin of the marketplace -CannotEnroll, +NotOwnerOrAdmin, +/// There was no change regarding the application status +AlreadyEnrolled, /// There cannot be more than one owner per marketplace OnlyOneOwnerIsAllowed, /// Cannot remove the owner of the marketplace From 1df09a72f693b9723d83eef6f196dbe99e981e44 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 19 Aug 2022 08:42:55 -0500 Subject: [PATCH 089/103] adds market participant permissions --- pallets/gated-marketplace/src/functions.rs | 2 +- pallets/gated-marketplace/src/types.rs | 24 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index a914560a..3ddf9e9b 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -23,7 +23,7 @@ impl Pallet { } // participant role and permissions let participant_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Participant.to_vec()].to_vec())?; - T::Rbac::create_and_set_permissions(pallet_id, participant_role_id[0],["buy".as_bytes().to_vec(),"sell".as_bytes().to_vec()].to_vec() )?; + T::Rbac::create_and_set_permissions(pallet_id, participant_role_id[0], Permission::participant_permissions() )?; // appraiser role and permissions let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Appraiser.to_vec()].to_vec())?; // redemption specialist role and permissions diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 08f10865..a101e3bb 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -67,6 +67,12 @@ pub enum Permission{ RemoveAuth, UpdateLabel, RemoveMarketplace, + EnlistSellOffer, + TakeSellOffer, + DuplicateOffer, + RemoveOffer, + EnlistBuyOffer, + TakeBuyOffer, } impl Permission{ @@ -77,6 +83,12 @@ impl Permission{ Self::RemoveAuth => "RemoveAuth".as_bytes().to_vec(), Self::UpdateLabel => "UpdateLabel".as_bytes().to_vec(), Self::RemoveMarketplace => "RemoveMarketplace".as_bytes().to_vec(), + &Self::EnlistSellOffer=>"EnlistSellOffer".as_bytes().to_vec(), + &Self::TakeSellOffer=>"TakeSellOffer".as_bytes().to_vec(), + &Self::DuplicateOffer=>"DuplicateOffer".as_bytes().to_vec(), + &Self::RemoveOffer=>"RemoveOffer".as_bytes().to_vec(), + &Self::EnlistBuyOffer=>"EnlistBuyOffer".as_bytes().to_vec(), + &Self::TakeBuyOffer=>"TakeBuyOffer".as_bytes().to_vec(), } } @@ -92,6 +104,18 @@ impl Permission{ UpdateLabel.to_vec(), RemoveMarketplace.to_vec()].to_vec() } + + pub fn participant_permissions()->Vec>{ + use crate::types::Permission::*; + [ + EnlistSellOffer.to_vec(), + TakeSellOffer.to_vec(), + DuplicateOffer.to_vec(), + RemoveOffer.to_vec(), + EnlistBuyOffer.to_vec(), + TakeBuyOffer.to_vec(), + ].to_vec() + } } #[derive(CloneNoBound,Encode, Decode, Eq, PartialEq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] From dbbc91a876a0b430851ffffa4dfdbf5a8f995ae5 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 19 Aug 2022 13:57:03 -0500 Subject: [PATCH 090/103] adds RBAC validations on offer data flow --- pallets/gated-marketplace/src/benchmarking.rs | 20 ------------------- pallets/gated-marketplace/src/functions.rs | 17 ++++++++++------ pallets/gated-marketplace/src/lib.rs | 3 --- pallets/gated-marketplace/src/mock.rs | 2 +- pallets/gated-marketplace/src/tests.rs | 3 +++ pallets/gated-marketplace/src/types.rs | 6 ++++-- 6 files changed, 19 insertions(+), 32 deletions(-) delete mode 100644 pallets/gated-marketplace/src/benchmarking.rs diff --git a/pallets/gated-marketplace/src/benchmarking.rs b/pallets/gated-marketplace/src/benchmarking.rs deleted file mode 100644 index d496a9fc..00000000 --- a/pallets/gated-marketplace/src/benchmarking.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Benchmarking setup for pallet-template - -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_system::RawOrigin; - -benchmarks! { - do_something { - let s in 0 .. 100; - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); - } - - impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test); -} diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 3ddf9e9b..c8c7eb54 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -183,7 +183,7 @@ impl Pallet { //This function is only called by the owner of the marketplace //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - + Self::is_authorized(authority.clone(), &marketplace_id,Permission::EnlistSellOffer)?; //ensure the collection exists if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a == authority, Error::::NotOwner); @@ -244,13 +244,14 @@ impl Pallet { } pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { - //TODO: ensure the user is a Marketparticipant - + // ensure the user is a Marketparticipant + //ensure!(>::get(marketplace_id, ApplicationStatus::Approved).contains(&authority), Error::::ApplicantNotFound); //ensure the item is for sale, if not, return error ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); - + //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + Self::is_authorized(authority.clone(), &marketplace_id,Permission::EnlistBuyOffer)?; //ensure the collection exists //For this case user doesn't have to be the owner of the collection @@ -314,6 +315,8 @@ impl Pallet { } pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + Self::is_authorized(buyer.clone(), &marketplace_id,Permission::TakeSellOffer)?; //This extrisicn is called by the user who wants to buy the item //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; @@ -358,6 +361,8 @@ impl Pallet { pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { //This extrinsic is called by the owner of the item who accepts the buy offer from the interested user. + ensure!(>::contains_key(&marketplace_id), Error::::MarketplaceNotFound); + Self::is_authorized(authority.clone(), &marketplace_id,Permission::TakeBuyOffer)?; //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; @@ -408,7 +413,7 @@ impl Pallet { pub fn do_duplicate_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult{ //ensure new marketplace_id exits ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - + Self::is_authorized(authority.clone(), &marketplace_id,Permission::DuplicateOffer)?; //ensure that the offer_id exists ensure!(>::contains_key(offer_id), Error::::OfferNotFound); @@ -455,7 +460,7 @@ impl Pallet { pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, ) -> DispatchResult { //ensure marketplace_id exits ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - + Self::is_authorized(authority.clone(), &marketplace_id,Permission::RemoveOffer)?; //ensure the offer_id exists ensure!(>::contains_key(offer_id), Error::::OfferNotFound); diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 5993a6ed..78165004 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -8,9 +8,6 @@ mod mock; #[cfg(test)] mod tests; -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - mod functions; mod types; diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index a8bc1ddb..e8511063 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -153,7 +153,7 @@ parameter_types! { pub const MaxRolesPerPallet: u32 = 6; pub const RoleMaxLen: u32 = 25; pub const PermissionMaxLen: u32 = 25; - pub const MaxPermissionsPerRole: u32 = 5; + pub const MaxPermissionsPerRole: u32 = 11; pub const MaxRolesPerUser: u32 = 2; pub const MaxUsersPerRole: u32 = 2; } diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index f3b04696..d4edcea4 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -1085,6 +1085,9 @@ fn enlist_buy_offer_an_item_can_receive_multiple_buy_offers(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id2).is_some()); + // User 3 will buy the asset so it'll have to enter the marketplace first + assert_ok!(GatedMarketplace::apply(Origin::signed(3),m_id,create_application_fields(2), None )); + assert_ok!(GatedMarketplace::enroll(Origin::signed(1), m_id , AccountOrApplication::Account(3), true, default_feedback())); assert_ok!(GatedMarketplace::enlist_buy_offer(Origin::signed(3), m_id, 0, 0, 1200)); let offer_id3 = GatedMarketplace::offers_by_account(3).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id3).is_some()); diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index a101e3bb..a6d36ba6 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -98,11 +98,13 @@ impl Permission{ pub fn admin_permissions()-> Vec>{ use crate::types::Permission::*; - [Enroll.to_vec(), + let mut admin_permissions = [Enroll.to_vec(), AddAuth.to_vec(), RemoveAuth.to_vec(), UpdateLabel.to_vec(), - RemoveMarketplace.to_vec()].to_vec() + RemoveMarketplace.to_vec()].to_vec(); + admin_permissions.append(&mut Permission::participant_permissions()); + admin_permissions } pub fn participant_permissions()->Vec>{ From c5e52ee86520c0fb862732325801fab59346b6ef Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 22 Aug 2022 10:43:48 -0500 Subject: [PATCH 091/103] Increases maxPermissionsPerRole --- pallets/gated-marketplace/README.md | 4 ++-- pallets/gated-marketplace/src/functions.rs | 1 - runtime/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pallets/gated-marketplace/README.md b/pallets/gated-marketplace/README.md index b79159b0..f4a4c9a7 100644 --- a/pallets/gated-marketplace/README.md +++ b/pallets/gated-marketplace/README.md @@ -36,7 +36,7 @@ Create marketplaces that require previous authorization before placing sell and - [Polkadot-js api (javascript library)](#polkadot-js-api-javascript-library) - [Create a marketplace](#create-a-marketplace-1) - [Get a marketplace](#get-a-marketplace-1) - - [Get what permissions does an account have on a marketplace](#get-what-permissions-does-an-account-have-on-a-marketplace) + - [Get what roles does an account have on a marketplace](#get-what-roles-does-an-account-have-on-a-marketplace-1) - [Get all the accounts that have a certain permission on a marketplace](#get-all-the-accounts-that-have-a-certain-permission-on-a-marketplace) - [Apply to a marketplace (without custodian)](#apply-to-a-marketplace-without-custodian-1) - [Apply to a marketplace (with custodian)](#apply-to-a-marketplace-with-custodian-1) @@ -434,7 +434,7 @@ key marketplace_id: [ marketplace: { label: 'my marketplace' } ``` -#### Get what permissions does an account have on a marketplace +#### Get what roles does an account have on a marketplace ```js // account_id, pallet_id, scope_id const rolesByUser = await api.query.rbac.rolesByUser(alice.address,20, "0xace33a53e2c1a5c7fa2f920338136d0ddc3aba23eacaf708e3871bc856a34b95"); diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index c8c7eb54..83265549 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -28,7 +28,6 @@ impl Pallet { let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Appraiser.to_vec()].to_vec())?; // redemption specialist role and permissions let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; - Ok(()) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 119ed9c6..3c25a596 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -633,7 +633,7 @@ parameter_types! { pub const MaxRolesPerPallet: u32 = 20; pub const RoleMaxLen: u32 = 30; pub const PermissionMaxLen: u32 = 30; - pub const MaxPermissionsPerRole: u32 = 10; + pub const MaxPermissionsPerRole: u32 = 12; pub const MaxRolesPerUser: u32 = 10; pub const MaxUsersPerRole: u32 = 10; } From 8961c6474ed3abdcec6318b16fe1dab33c607b88 Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 23 Aug 2022 14:02:22 -0500 Subject: [PATCH 092/103] backup --- .devcontainer/devcontainer.json | 9 ++-- pallets/fruniques/src/tests.rs | 2 +- pallets/gated-marketplace/Cargo.toml | 2 + pallets/gated-marketplace/src/functions.rs | 48 ++++++++++++++-------- pallets/gated-marketplace/src/lib.rs | 2 + runtime/Cargo.toml | 1 + 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 778053e0..f067bb19 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,10 +6,11 @@ "lldb.executable": "/usr/bin/lldb" }, "extensions": [ - "rust-lang.rust", - "bungcip.better-toml", - "vadimcn.vscode-lldb" - ], + "rust-lang.rust", + "bungcip.better-toml", + "vadimcn.vscode-lldb", + "rust-lang.rust-analyzer" +], "forwardPorts": [ 3000, 9944 diff --git a/pallets/fruniques/src/tests.rs b/pallets/fruniques/src/tests.rs index 328e142d..f722cfdc 100644 --- a/pallets/fruniques/src/tests.rs +++ b/pallets/fruniques/src/tests.rs @@ -1,6 +1,6 @@ use crate::{mock::*, Error}; -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_noop, assert_ok}; use sp_runtime::Permill; pub struct ExtBuilder; diff --git a/pallets/gated-marketplace/Cargo.toml b/pallets/gated-marketplace/Cargo.toml index 6b84381a..2d83abff 100644 --- a/pallets/gated-marketplace/Cargo.toml +++ b/pallets/gated-marketplace/Cargo.toml @@ -45,6 +45,8 @@ std = [ "frame-benchmarking/std", "pallet-balances/std", "pallet-timestamp/std", + "pallet-uniques/std", + "pallet-fruniques/std" ] runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"] diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 126ca100..5bc3ff6e 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -187,9 +187,9 @@ impl Pallet { buyer: None, }; - + //TODO: FIX THIS HELPER FUCNTION //ensure there is no a previous sell offer for this item - Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + //Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; //insert in OffersByItem >::try_mutate(collection_id, item_id, |offers| { @@ -216,8 +216,6 @@ impl Pallet { } pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { - //TODO: ensure the user is a Marketparticipant - //ensure the item is for sale, if not, return error ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); @@ -299,8 +297,6 @@ impl Pallet { //ensure the offer_id exists in OffersByItem Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - //TODO: ensure the offer is not expired - //ensure the offer is open and available ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); @@ -322,8 +318,6 @@ impl Pallet { //remove all the offers associated with the item Self::delete_all_offers_for_this_item(collection_id, item_id )?; - //TODO: add the offer_id from this offer to the buyer's history - Self::deposit_event(Event::OfferWasAccepted(offer_id, buyer)); Ok(()) } @@ -467,6 +461,7 @@ impl Pallet { Ok(()) }).map_err(|_:Error::| Error::::OfferNotFound)?; + Self::deposit_event(Event::OfferRemoved(offer_id, marketplace_id)); Ok(()) } @@ -787,22 +782,39 @@ impl Pallet { Ok(()) } - fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { - let offers = >::get(collection_id, item_id); + // fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { + // let offers = >::get(collection_id, item_id); + + // //if len is == 0, it means that there is no offers for this item, maybe it's the first entry + // if offers.len() == 0 { + // return Ok(()); + // } else if offers.len() > 0 { + // for offer in offers { + // let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; + // //ensure the offer_type is SellOrder, because this vector also contains buy offers. + // if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { + // return Err(Error::::OfferAlreadyExists)?; + // } + // } + // } + // Ok(()) + // } + + fn _is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> bool { + + let offers = match >::try_get(collection_id, item_id){ + Ok(offers) => offers, + Err(_) => return false}; - //if len is == 0, it means that there is no offers for this item, maybe it's the first entry - if offers.len() == 0 { - return Ok(()) - } else if offers.len() > 0 { for offer in offers { - let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; + let offer_info = >::get(offer); //ensure the offer_type is SellOrder, because this vector also contains buy offers. if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - return Err(Error::::OfferAlreadyExists)?; + return true; } } - } - Ok(()) + + true } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 968abd50..d46ce097 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -212,6 +212,8 @@ pub mod pallet { OfferWasAccepted([u8;32], T::AccountId), /// Offer was duplicated. [new_offer_id, new_marketplace_id] OfferDuplicated([u8;32], [u8;32]), + /// Offer was removed. [offer_id], [marketplace_id] + OfferRemoved([u8;32], [u8;32]), } // Errors inform users that something went wrong. diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index dbb7231d..4cde8d83 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -113,6 +113,7 @@ std = [ "sp-transaction-pool/std", "sp-version/std", ] + runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", From aa950e6dcad53cf5dd142a34cc8a1a64d66f8c0b Mon Sep 17 00:00:00 2001 From: didiermis Date: Tue, 23 Aug 2022 18:17:46 -0500 Subject: [PATCH 093/103] Update signature functions to version 5. I removed all the unnecessary helper functions calls since we can obtain those parameters from Offers storagemap. --- pallets/gated-marketplace/src/functions.rs | 284 +++++++++++---------- pallets/gated-marketplace/src/lib.rs | 43 ++-- pallets/gated-marketplace/src/tests.rs | 28 +- pallets/gated-marketplace/src/types.rs | 2 +- 4 files changed, 186 insertions(+), 171 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 5bc3ff6e..36876017 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -175,11 +175,11 @@ impl Pallet { //create offer structure let offer_data = OfferData:: { - marketplace_id: marketplace_id, - collection_id: collection_id, - item_id: item_id, + marketplace_id, + collection_id, + item_id, creator: authority.clone(), - price: price, + price, creation_date: timestamp, expiration_date: timestamp2, status: OfferStatus::Open, @@ -187,9 +187,8 @@ impl Pallet { buyer: None, }; - //TODO: FIX THIS HELPER FUCNTION //ensure there is no a previous sell offer for this item - //Self::is_item_already_for_sale(collection_id, item_id, marketplace_id)?; + Self::can_this_item_receive_sell_orders(collection_id, item_id, marketplace_id)?; //insert in OffersByItem >::try_mutate(collection_id, item_id, |offers| { @@ -197,7 +196,7 @@ impl Pallet { }).map_err(|_| Error::::OfferStorageError)?; //insert in OffersByAccount - >::try_mutate(authority.clone(), |offers| { + >::try_mutate(authority, |offers| { offers.try_push(offer_id) }).map_err(|_| Error::::OfferStorageError)?; @@ -217,7 +216,7 @@ impl Pallet { pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { //ensure the item is for sale, if not, return error - ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); + Self::can_this_item_receive_buy_orders(collection_id, item_id, marketplace_id)?; //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -246,11 +245,11 @@ impl Pallet { //create offer structure let offer_data = OfferData:: { - marketplace_id: marketplace_id, - collection_id: collection_id, - item_id: item_id, + marketplace_id, + collection_id, + item_id, creator: authority.clone(), - price: price, + price, creation_date: timestamp, expiration_date: timestamp2, status: OfferStatus::Open, @@ -265,7 +264,7 @@ impl Pallet { }).map_err(|_| Error::::OfferStorageError)?; //insert in OffersByAccount - >::try_mutate(authority.clone(), |offers| { + >::try_mutate(authority, |offers| { offers.try_push(offer_id) }).map_err(|_| Error::::OfferStorageError)?; @@ -283,185 +282,197 @@ impl Pallet { Ok(()) } - pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { //This extrisicn is called by the user who wants to buy the item + //ensure offer exists + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + + //ensure the marketplace exists + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //get offer data + let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + + //ensure the marketplace_id where the extrinsic is being called is the same as the one in the offer + ensure!(offer_data.marketplace_id == marketplace_id, Error::::MarketplaceNotFound); + //ensure the collection & owner exists - let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + let owner_item = pallet_uniques::Pallet::::owner(offer_data.collection_id, offer_data.item_id).ok_or(Error::::OwnerNotFound)?; //ensure owner is not the same as the buyer - ensure!(owner_item != buyer.clone(), Error::::CannotTakeOffer); - - //ensure the selected item has a valid offer_id in OffersInfo - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + ensure!(owner_item != buyer, Error::::CannotTakeOffer); //ensure the offer_id exists in OffersByItem - Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + Self::does_exist_offer_id_for_this_item(offer_data.collection_id, offer_data.item_id, offer_id)?; //ensure the offer is open and available - ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); - - //Transfer balance to the owner of the item - let item_price = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; - let total_user_balance = T::LocalCurrency::total_balance(&buyer); - ensure!(total_user_balance >= item_price, Error::::NotEnoughBalance); + ensure!(offer_data.status == OfferStatus::Open, Error::::OfferIsNotAvailable); + + //TODO: Use free_balance instead of total_balance + //Get the buyer's balance + let total_amount_buyer = T::LocalCurrency::total_balance(&buyer); + //ensure the buyer has enough balance to buy the item + ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance - T::LocalCurrency::transfer(&buyer, &owner_item, item_price, KeepAlive)?; + T::LocalCurrency::transfer(&buyer, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the buyer - pallet_uniques::Pallet::::do_transfer(collection_id, item_id, buyer.clone(), |_, _|{ + pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, buyer.clone(), |_, _|{ Ok(()) })?; //update offer status from all marketplaces - Self::update_offers_status(buyer.clone(), collection_id, item_id, marketplace_id)?; + Self::update_offers_status(buyer.clone(), offer_data.collection_id, offer_data.item_id, marketplace_id)?; //remove all the offers associated with the item - Self::delete_all_offers_for_this_item(collection_id, item_id )?; + Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id)?; Self::deposit_event(Event::OfferWasAccepted(offer_id, buyer)); Ok(()) } - pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { - //This extrinsic is called by the owner of the item who accepts the buy offer from the interested user. + pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { + //This extrinsic is called by the owner of the item who accepts the buy offer created by a marketparticipant + //Ensure offer exists. This offer_id corresponds to the buy order + ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + + //ensure the marketplace exists + ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + + //get offer data + let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + + //ensure the marketplace_id where the extrinsic is being called is the same as the one in the offer + ensure!(offer_data.marketplace_id == marketplace_id, Error::::MarketplaceNotFound); + //ensure the collection & owner exists - let owner_item = pallet_uniques::Pallet::::owner(collection_id, item_id).ok_or(Error::::OwnerNotFound)?; + let owner_item = pallet_uniques::Pallet::::owner(offer_data.collection_id, offer_data.item_id).ok_or(Error::::OwnerNotFound)?; //ensure only owner of the item can call the extrinic - ensure!(owner_item == authority.clone(), Error::::NotOwner); - - // Get the account_id of the offer creator (the buyer) - let buy_offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; + ensure!(owner_item == authority, Error::::NotOwner); //ensure owner is not the same as the buy_offer_creator - ensure!(owner_item != buy_offer_creator.clone(), Error::::CannotTakeOffer); - - //ensure the selected item has a valid offer_id in OffersInfo - // This offer_id corresponds to the buy offer - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + ensure!(owner_item != offer_data.creator, Error::::CannotTakeOffer); //ensure the offer_id exists in OffersByItem - Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - - //TODO: ensure the offer is not expired + Self::does_exist_offer_id_for_this_item(offer_data.collection_id, offer_data.item_id, offer_id)?; //ensure the offer is open and available - ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::OfferIsNotAvailable); + ensure!(offer_data.status == OfferStatus::Open, Error::::OfferIsNotAvailable); - //Get the offered price - let offerred_price = Self::get_offer_price(offer_id).map_err(|_| Error::::OfferNotFound)?; - let total_buy_offer_creator = T::LocalCurrency::total_balance(&buy_offer_creator); + //TODO: Use free_balance instead of total_balance + //Get the buyer's balance + let total_amount_buyer = T::LocalCurrency::total_balance(&offer_data.creator); //ensure the buy_offer_creator has enough balance to buy the item - ensure!(total_buy_offer_creator >= offerred_price, Error::::NotEnoughBalance); + ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance to the owner of the item - T::LocalCurrency::transfer(&buy_offer_creator, &owner_item, offerred_price, KeepAlive)?; + T::LocalCurrency::transfer(&offer_data.creator, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the market_participant - pallet_uniques::Pallet::::do_transfer(collection_id, item_id, buy_offer_creator.clone(), |_, _|{ + pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, offer_data.creator.clone(), |_, _|{ Ok(()) })?; //update offer status from all marketplaces - Self::update_offers_status(buy_offer_creator.clone(), collection_id, item_id, marketplace_id)?; + Self::update_offers_status(offer_data.creator.clone(), offer_data.collection_id, offer_data.item_id, marketplace_id)?; //remove all the offers associated with the item - Self::delete_all_offers_for_this_item(collection_id, item_id )?; + Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id )?; - Self::deposit_event(Event::OfferWasAccepted(offer_id, buy_offer_creator)); + Self::deposit_event(Event::OfferWasAccepted(offer_id, offer_data.creator)); Ok(()) } - pub fn do_duplicate_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult{ - //ensure new marketplace_id exits - ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + //TODO: Under development + // pub fn do_duplicate_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult{ + // //ensure new marketplace_id exits + // ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - //ensure that the offer_id exists - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); + // //ensure that the offer_id exists + // ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - //ensure the offer_id exists in OffersByItem - Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; + // //ensure the offer_id exists in OffersByItem + // Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - //get the offer data - let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + // //get the offer data + // let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - //modify the offer data - //by know we only allow to modify its price by the user - //we modify its marketplace_id because the offer is duplicated to another marketplace - copy_offer_data.price = modified_price; - copy_offer_data.marketplace_id = marketplace_id; + // //modify the offer data + // //by know we only allow to modify its price by the user + // //we modify its marketplace_id because the offer is duplicated to another marketplace + // copy_offer_data.price = modified_price; + // copy_offer_data.marketplace_id = marketplace_id; - //generate a new offer_id - let new_offer_id = (marketplace_id, authority.clone(), collection_id, copy_offer_data.creation_date, copy_offer_data.expiration_date).using_encoded(blake2_256); + // //generate a new offer_id + // let new_offer_id = (marketplace_id, authority.clone(), collection_id, copy_offer_data.creation_date, copy_offer_data.expiration_date).using_encoded(blake2_256); - //insert in OffersInfo - // validate new offer_id does not exists - ensure!(!>::contains_key(new_offer_id), Error::::OfferAlreadyExists); - >::insert(new_offer_id, copy_offer_data); - - //insert in OffersByMarketplace - >::try_mutate(marketplace_id, |offer| { - offer.try_push(new_offer_id) - }).map_err(|_| Error::::OfferStorageError)?; + // //insert in OffersInfo + // // validate new offer_id does not exists + // ensure!(!>::contains_key(new_offer_id), Error::::OfferAlreadyExists); + // >::insert(new_offer_id, copy_offer_data); + + // //insert in OffersByMarketplace + // >::try_mutate(marketplace_id, |offer| { + // offer.try_push(new_offer_id) + // }).map_err(|_| Error::::OfferStorageError)?; - //insert in OffersByAccount - >::try_mutate(authority.clone(), |offer| { - offer.try_push(new_offer_id) - }).map_err(|_| Error::::OfferStorageError)?; + // //insert in OffersByAccount + // >::try_mutate(authority.clone(), |offer| { + // offer.try_push(new_offer_id) + // }).map_err(|_| Error::::OfferStorageError)?; - //add the new offer_id to OffersByItem - >::try_mutate(collection_id, item_id, |offers| { - offers.try_push(new_offer_id) - }).map_err(|_| Error::::OfferStorageError)?; + // //add the new offer_id to OffersByItem + // >::try_mutate(collection_id, item_id, |offers| { + // offers.try_push(new_offer_id) + // }).map_err(|_| Error::::OfferStorageError)?; - Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); + // Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); - Ok(()) - } + // Ok(()) + // } - pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, ) -> DispatchResult { - //ensure marketplace_id exits - ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); + pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32]) -> DispatchResult { //ensure the offer_id exists ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - //ensure the offer status is Open - ensure!(Self::is_offer_status(offer_id, OfferStatus::Open), Error::::CannotDeleteOffer); + //get offer data + let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; + //ensure the offer status is Open + ensure!(offer_data.status == OfferStatus::Open, Error::::CannotDeleteOffer); - // ensure the owner is the same as the authority - let offer_creator = Self::get_offer_creator(offer_id).map_err(|_| Error::::OfferNotFound)?; - ensure!(offer_creator == authority.clone(), Error::::CannotRemoveOffer); + // ensure the authority is the creator of the offer + ensure!(offer_data.creator == authority, Error::::CannotRemoveOffer); //ensure the offer_id exists in OffersByItem - Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - + Self::does_exist_offer_id_for_this_item(offer_data.collection_id, offer_data.item_id, offer_id)?; //remove the offer from OfferInfo >::remove(offer_id); //remove the offer from OffersByMarketplace - >::try_mutate(marketplace_id, |offers| { + >::try_mutate(offer_data.marketplace_id, |offers| { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) }).map_err(|_:Error::| Error::::OfferNotFound)?; //remove the offer from OffersByAccount - >::try_mutate(authority.clone(), |offers| { + >::try_mutate(authority, |offers| { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) }).map_err(|_:Error::| Error::::OfferNotFound)?; //remove the offer from OffersByItem - >::try_mutate(collection_id, item_id, |offers| { + >::try_mutate(offer_data.collection_id, offer_data.item_id, |offers| { let offer_index = offers.iter().position(|x| *x == offer_id).ok_or(Error::::OfferNotFound)?; offers.remove(offer_index); Ok(()) }).map_err(|_:Error::| Error::::OfferNotFound)?; - Self::deposit_event(Event::OfferRemoved(offer_id, marketplace_id)); + Self::deposit_event(Event::OfferRemoved(offer_id, offer_data.marketplace_id)); Ok(()) } @@ -710,7 +721,8 @@ impl Pallet { } else { return Err(Error::::TimestampError)?; } - return Ok(date_as_u64_millis); + + Ok(date_as_u64_millis) } fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { @@ -722,7 +734,7 @@ impl Pallet { Some((timestamp2, timestamp3)) } - fn is_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ + fn _is_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ //we already know that the offer exists, so we don't need to check it here. if let Some(offer) = >::get(offer_id) { return offer.status == offer_status; @@ -731,7 +743,7 @@ impl Pallet { } } - fn get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { + fn _get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { //we already know that the offer exists, so we don't need to check it here. if let Some(offer) = >::get(offer_id) { return Ok(offer.price); @@ -756,7 +768,7 @@ impl Pallet { } } - fn get_offer_creator(offer_id: [u8;32],) -> Result { + fn _get_offer_creator(offer_id: [u8;32],) -> Result { //we already know that the offer exists, so we don't need to check it here. if let Some(offer) = >::get(offer_id) { return Ok(offer.creator); @@ -782,39 +794,43 @@ impl Pallet { Ok(()) } - // fn is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { - // let offers = >::get(collection_id, item_id); - - // //if len is == 0, it means that there is no offers for this item, maybe it's the first entry - // if offers.len() == 0 { - // return Ok(()); - // } else if offers.len() > 0 { - // for offer in offers { - // let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; - // //ensure the offer_type is SellOrder, because this vector also contains buy offers. - // if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - // return Err(Error::::OfferAlreadyExists)?; - // } - // } - // } - // Ok(()) - // } - - fn _is_item_already_for_sale(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> bool { - let offers = match >::try_get(collection_id, item_id){ - Ok(offers) => offers, - Err(_) => return false}; + fn can_this_item_receive_sell_orders(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { + let offers = >::get(collection_id, item_id); + //if len is == 0, it means that there is no offers for this item, maybe it's the first entry + if offers.len() == 0 { + return Ok(()); + } else if offers.len() > 0 { for offer in offers { - let offer_info = >::get(offer); + let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; //ensure the offer_type is SellOrder, because this vector also contains buy offers. if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - return true; + return Err(Error::::OfferAlreadyExists)?; } } + } + + Ok(()) + } + + fn can_this_item_receive_buy_orders(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { + //TODO: optimize this function, when rust-analyzer pluggin is fixed, it will be possible to use the .iter().find() + //First we check if the item has is for sale, if not, return error + ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); + + //ensure the item can receive buy offers on the selected marketplace + let offers = >::get(collection_id, item_id); + + for offer in offers { + let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; + //ensure the offer_type is SellOrder, because this vector also contains buy offers. + if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { + return Ok(()); + } + } - true + return Err(Error::::ItemNotForSale)?; } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index d46ce097..d86c03fb 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -541,13 +541,13 @@ pub mod pallet { /// Accepts a sell order. /// - /// This extrinsic accepts a sell order in the selected marketplace. + /// This extrisicn is called by the user who wants to buy the item. + /// Aaccepts a sell order in the selected marketplace. /// /// ### Parameters: /// - `origin`: The user who performs the action. + /// - 'offer_id`: The id of the sell order to be accepted. /// - `marketplace_id`: The id of the marketplace where we want to accept the sell order. - /// - `collection_id`: The id of the collection. - /// - `item_id`: The id of the item inside the collection. /// /// ### Considerations: /// - You don't need to be the owner of the item to accept the sell order. @@ -555,12 +555,13 @@ pub mod pallet { /// - If you don't have the enough balance to accept the sell order, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32],) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_sell_offer(who, offer_id, marketplace_id, collection_id, item_id) + Self::do_take_sell_offer(who, offer_id, marketplace_id) } + //TODO: Under development /// Allows a user to duplicate a sell order. /// /// This extrinsic allows a user to duplicate a sell order in any marketplace. @@ -575,24 +576,22 @@ pub mod pallet { /// - You can only duplicate a sell order if you are the owner of the item. /// - The expiration date of the sell order is the same as the original sell order. /// - You can update the price of the sell order. - #[transactional] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn duplicate_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult { - let who = ensure_signed(origin.clone())?; - - Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) - } + // #[transactional] + // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] + // pub fn duplicate_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult { + // let who = ensure_signed(origin.clone())?; + // Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) + // } + /// Delete an offer. /// /// This extrinsic deletes an offer in the selected marketplace. /// /// ### Parameters: /// - `origin`: The user who performs the action. - /// - `marketplace_id`: The id of the marketplace where we want to delete the offer. - /// - `collection_id`: The id of the collection. - /// - `item_id`: The id of the item inside the collection. + /// - `offer_id`: The id of the offer to be deleted. /// /// ### Considerations: /// - You can delete sell orders or buy orders. @@ -602,12 +601,12 @@ pub mod pallet { /// delete them one by one. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn remove_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn remove_offer(origin: OriginFor, offer_id: [u8;32]) -> DispatchResult { //Currently, we can only remove one offer at a time. //TODO: Add support for removing multiple offers at a time. let who = ensure_signed(origin.clone())?; - Self::do_remove_offer(who, offer_id, marketplace_id, collection_id, item_id) + Self::do_remove_offer(who, offer_id) } /// Enlist a buy order. @@ -636,13 +635,13 @@ pub mod pallet { /// Accepts a buy order. /// - /// This extrinsic accepts a buy order in the selected marketplace. + /// This extrinsic is called by the owner of the item who accepts the buy offer created by a marketparticipant. + /// Accepts a buy order in the selected marketplace. /// /// ### Parameters: /// - `origin`: The user who performs the action. + /// - `offer_id`: The id of the buy order to be accepted. /// - `marketplace_id`: The id of the marketplace where we accept the buy order. - /// - `collection_id`: The id of the collection. - /// - `item_id`: The id of the item inside the collection. /// /// ### Considerations: /// - You need to be the owner of the item to accept a buy order. @@ -652,10 +651,10 @@ pub mod pallet { /// - Once the buy order is accepted, the ownership of the item is transferred to the buyer. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId,) -> DispatchResult { + pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_buy_offer(who, offer_id, marketplace_id, collection_id, item_id) + Self::do_take_buy_offer(who, offer_id, marketplace_id) } diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index fca5a3ed..d777263e 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -1080,7 +1080,7 @@ fn take_sell_offer_works(){ assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id, 0, 0)); + assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); @@ -1103,7 +1103,7 @@ fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id, m_id, 0, 0), Error::::CannotTakeOffer); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id, m_id), Error::::CannotTakeOffer); }); } @@ -1124,7 +1124,7 @@ fn take_sell_offer_id_does_not_exist_shouldnt_work(){ let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); let offer_id2 = offer_id.using_encoded(blake2_256); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::OfferNotFound); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id2, m_id), Error::::OfferNotFound); }); } @@ -1145,7 +1145,7 @@ fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work(){ assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id, 0, 0), Error::::NotEnoughBalance); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id), Error::::NotEnoughBalance); }); } @@ -1170,7 +1170,7 @@ fn take_buy_offer_works(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0)); + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); }); @@ -1198,7 +1198,7 @@ fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::NotOwner); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(2), offer_id2, m_id), Error::::NotOwner); }); } @@ -1224,7 +1224,7 @@ fn take_buy_offer_id_does_not_exist_shouldnt_work(){ assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); let offer_id3 = offer_id2.using_encoded(blake2_256); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id3, m_id, 0, 0), Error::::OfferNotFound); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id3, m_id), Error::::OfferNotFound); }); } @@ -1251,7 +1251,7 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work(){ assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); Balances::make_free_balance_be(&2, 0); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0), Error::::NotEnoughBalance); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id), Error::::NotEnoughBalance); }); } @@ -1272,7 +1272,7 @@ fn remove_sell_offer_works(){ let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - assert_ok!(GatedMarketplace::remove_offer(Origin::signed(1), offer_id, m_id, 0, 0)); + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(1), offer_id)); assert_eq!(GatedMarketplace::offers_by_account(1).len(), 0); assert!(GatedMarketplace::offers_info(offer_id).is_none()); }); @@ -1299,7 +1299,7 @@ fn remove_buy_offer_works(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id2).is_some()); - assert_ok!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0)); + assert_ok!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2)); assert_eq!(GatedMarketplace::offers_by_account(2).len(), 0); assert!(GatedMarketplace::offers_info(offer_id2).is_none()); }); @@ -1323,7 +1323,7 @@ fn remove_offer_id_does_not_exist_sholdnt_work(){ assert!(GatedMarketplace::offers_info(offer_id).is_some()); let offer_id2 = offer_id.using_encoded(blake2_256); - assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::OfferNotFound); + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2), Error::::OfferNotFound); }); } @@ -1344,7 +1344,7 @@ fn remove_offer_creator_doesnt_match_sholdnt_work(){ let offer_id = GatedMarketplace::offers_by_account(1).iter().next().unwrap().clone(); assert!(GatedMarketplace::offers_info(offer_id).is_some()); - assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id, m_id, 0, 0), Error::::CannotRemoveOffer); + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id), Error::::CannotRemoveOffer); }); } @@ -1369,11 +1369,11 @@ fn remove_offer_status_is_closed_shouldnt_work(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id, 0, 0)); + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); - assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2, m_id, 0, 0), Error::::CannotDeleteOffer); + assert_noop!(GatedMarketplace::remove_offer(Origin::signed(2), offer_id2), Error::::CannotDeleteOffer); }); } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 0575b8ff..769cd14b 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -35,7 +35,7 @@ impl Default for MarketplaceAuthority{ } } -#[derive(CloneNoBound,Encode, Decode, Eq, PartialEq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] +#[derive(CloneNoBound,Encode, Decode, PartialEq, Eq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct Application< T: Config >{ From afe6b93debe21f759cfdc51146dde9e484b5aa3c Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Wed, 24 Aug 2022 19:16:59 -0500 Subject: [PATCH 094/103] replaces pallet_id for hashed pallet name --- pallets/gated-marketplace/src/functions.rs | 16 +-- pallets/gated-marketplace/src/tests.rs | 36 +++--- pallets/rbac/src/functions.rs | 91 ++++++++------ pallets/rbac/src/lib.rs | 16 +-- pallets/rbac/src/tests.rs | 135 +++++++++++---------- pallets/rbac/src/types.rs | 40 +++--- 6 files changed, 181 insertions(+), 153 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 83265549..8047117f 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -17,17 +17,17 @@ impl Pallet { let mut super_roles = Vec::>::new(); super_roles.push(MarketplaceRole::Owner.to_vec()); super_roles.push(MarketplaceRole::Admin.to_vec()); - let super_role_ids = T::Rbac::create_and_set_roles(pallet_id, super_roles)?; + let super_role_ids = T::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?; for super_role in super_role_ids{ - T::Rbac::create_and_set_permissions(pallet_id, super_role, Permission::admin_permissions())?; + T::Rbac::create_and_set_permissions(pallet_id.clone(), super_role, Permission::admin_permissions())?; } // participant role and permissions - let participant_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Participant.to_vec()].to_vec())?; - T::Rbac::create_and_set_permissions(pallet_id, participant_role_id[0], Permission::participant_permissions() )?; + let participant_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Participant.to_vec()].to_vec())?; + T::Rbac::create_and_set_permissions(pallet_id.clone(), participant_role_id[0], Permission::participant_permissions() )?; // appraiser role and permissions - let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::Appraiser.to_vec()].to_vec())?; + let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Appraiser.to_vec()].to_vec())?; // redemption specialist role and permissions - let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; + let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; Ok(()) } @@ -853,8 +853,8 @@ impl Pallet { - pub fn pallet_id()->u64{ - Self::index().try_into().unwrap() + pub fn pallet_id()->Vec{ + Self::module_name().as_bytes().to_vec() } } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index d4edcea4..6cbdaacc 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -314,7 +314,7 @@ fn add_authority_appraiser_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::Appraiser.id())); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::Appraiser.id())); }); } @@ -325,7 +325,7 @@ fn add_authority_admin_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Admin]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::Admin.id())); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::Admin.id())); }); } @@ -336,7 +336,7 @@ fn add_authority_redenmption_specialist_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).contains(&MarketplaceRole::RedemptionSpecialist.id())); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::RedemptionSpecialist.id())); }); } @@ -372,7 +372,7 @@ fn remove_authority_appraiser_works() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); }); } @@ -384,7 +384,7 @@ fn remove_authority_admin_works() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); }); } @@ -396,7 +396,7 @@ fn remove_authority_redemption_specialist_work() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); }); } @@ -546,13 +546,13 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works(){ assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![]); - assert!(RBAC::roles_by_user((1, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((1, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![]); - assert!(RBAC::roles_by_user((2, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((2, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![]); - assert!(RBAC::roles_by_user((4, GatedMarketplace::pallet_id(), m_id)).is_empty()); + assert!(RBAC::roles_by_user((4, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); @@ -569,24 +569,24 @@ fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works(){ assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![1]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Owner.id())).contains(&1)); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Owner.id())).contains(&1)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![2]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Admin.id())).contains(&2)); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Admin.id())).contains(&2)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![3]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Appraiser.id())).contains(&3)); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Appraiser.id())).contains(&3)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![4]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).contains(&4)); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::RedemptionSpecialist.id())).contains(&4)); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Owner.id())).is_empty()); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Owner.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Admin.id())).is_empty()); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Admin.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::Appraiser.id())).is_empty()); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Appraiser.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![]); - assert!(RBAC::users_by_scope((GatedMarketplace::pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).is_empty()); + assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::RedemptionSpecialist.id())).is_empty()); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 680bf008..6426678d 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -15,8 +15,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The newly generated scope identifier. - fn create_scope(pallet_id: u64, scope_id: ScopeId)-> DispatchResult{ - let pallet_id: u64 = pallet_id.try_into().unwrap(); + fn create_scope(pallet_name: Vec, scope_id: ScopeId)-> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); >::try_mutate(pallet_id, |scopes|{ ensure!(!scopes.contains(&scope_id), Error::::ScopeAlreadyExists); scopes.try_push(scope_id).map_err(|_| Error::::ExceedMaxScopesPerPallet)?; @@ -31,7 +31,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope identifier to remove. - fn remove_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult{ + fn remove_scope(pallet_id:Vec, scope_id: ScopeId) -> DispatchResult{ + let pallet_id = Self::to_id(pallet_id); // remove on scopes >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ let scopes = scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; @@ -64,11 +65,12 @@ impl RoleBasedAccessControl for Pallet{ /// to that pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. - fn remove_pallet_storage(pallet_id: u64) -> DispatchResult{ + fn remove_pallet_storage(pallet_name: Vec) -> DispatchResult{ + let pallet_id = Self::to_id(pallet_name.clone()); //remove all scopes let scopes = >::get(pallet_id); for scope in scopes{ - Self::remove_scope(pallet_id, scope)?; + Self::remove_scope(pallet_name.clone(), scope)?; } // remove all roles let pallet_roles = >::take(pallet_id); @@ -93,13 +95,13 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `roles`: A list of roles to create, encoded in bytes. - fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> + fn create_and_set_roles(pallet_name: Vec, roles: Vec>) -> Result, DispatchError>{ let mut role_ids= Vec::<[u8;32]>::new(); for role in roles{ role_ids.push( Self::create_role(role.to_owned())? ); } - Self::set_multiple_pallet_roles(pallet_id, role_ids.clone())?; + Self::set_multiple_pallet_roles(pallet_name, role_ids.clone())?; let bounded_ids = Self::bound(role_ids, Error::::ExceedMaxRolesPerPallet)?; Ok(bounded_ids) } @@ -126,9 +128,9 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The unique role identifier. - fn set_role_to_pallet(pallet_id: u64, role_id: RoleId )-> DispatchResult{ + fn set_role_to_pallet(pallet_name: Vec, role_id: RoleId )-> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); ensure!(>::contains_key(role_id), Error::::RoleNotFound); - >::try_mutate(pallet_id, |roles|{ ensure!(!roles.contains(&role_id), Error::::RoleAlreadyLinkedToPallet ); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerPallet) @@ -142,7 +144,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `roles`: A list of unique role identifiers. - fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult{ + fn set_multiple_pallet_roles(pallet_name: Vec, roles: Vec)->DispatchResult{ + let pallet_id = Self::to_id(pallet_name); // checks for duplicates: ensure!(Self::has_unique_elements(roles.clone()), Error::::DuplicateRole); let pallet_roles = >::get(&pallet_id); @@ -164,10 +167,10 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the role will be granted. /// - `role_id`: The role identifier to grant for the user. - fn assign_role_to_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ - Self::scope_exists(pallet_id, scope_id)?; - Self::is_role_linked_to_pallet(pallet_id, &role_id)?; - + fn assign_role_to_user(user: T::AccountId, pallet_name: Vec , scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + Self::scope_exists(pallet_name.clone(), scope_id)?; + Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id)?; + let pallet_id = Self::to_id(pallet_name); >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::UserAlreadyHasRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) @@ -189,7 +192,8 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the role will be removed. /// - `role_id`: The role identifier to remove from the user. - fn remove_role_from_user(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + fn remove_role_from_user(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; @@ -220,15 +224,15 @@ impl RoleBasedAccessControl for Pallet{ /// be linked to. /// - `permissions`: A list of permissions to create and link, /// encoded in bytes. - fn create_and_set_permissions(pallet_id: u64, role_id: RoleId, permissions: Vec>)-> + fn create_and_set_permissions(pallet_name: Vec, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); - Self::is_role_linked_to_pallet(pallet_id, &role_id )?; + Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id )?; let mut permission_ids = Vec::<[u8;32]>::new(); for permission in permissions{ - permission_ids.push( Self::create_permission(pallet_id, permission.to_owned())? ); + permission_ids.push( Self::create_permission(pallet_name.clone(), permission.to_owned())? ); } - Self::set_multiple_permissions_to_role(pallet_id, role_id, permission_ids.clone())?; + Self::set_multiple_permissions_to_role(pallet_name, role_id, permission_ids.clone())?; let b_permissions = Self::bound(permission_ids, Error::::ExceedMaxPermissionsPerRole)?; Ok(b_permissions) } @@ -239,12 +243,12 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `permission`: The permission to insert, encoded in bytes. - fn create_permission(pallet_id: u64, permission: Vec) -> Result{ - let permission_id = permission.using_encoded(blake2_256); + fn create_permission(pallet_name: Vec, permission: Vec) -> Result{ + let permission_id = Self::to_id(permission.clone()); let b_permission = Self::bound:: <_,T::PermissionMaxLen>(permission, Error::::ExceedPermissionMaxLen)?; - + let pallet_id = Self::to_id(pallet_name); if !>::contains_key(pallet_id, permission_id){ >::insert(pallet_id, permission_id, b_permission); } @@ -258,9 +262,10 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role identifier to which the permission will be added. /// - `permission_id`: The permission to assign to the role. - fn set_permission_to_role( pallet_id: u64, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ + fn set_permission_to_role( pallet_name: Vec, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ + Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id)?; + let pallet_id = Self::to_id(pallet_name); ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); - Self::is_role_linked_to_pallet(pallet_id, &role_id)?; >::try_mutate(pallet_id, role_id, | role_permissions|{ ensure!(!role_permissions.contains(&permission_id), Error::::DuplicatePermission); @@ -277,10 +282,11 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role identifier to which the permissions will be added. /// - `permissions`: A list of permission identifiers to assign to the role. - fn set_multiple_permissions_to_role( pallet_id: u64, role_id: RoleId, permissions: Vec )-> DispatchResult{ + fn set_multiple_permissions_to_role( pallet_name: Vec, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); - Self::is_role_linked_to_pallet(pallet_id, &role_id )?; + Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id )?; + let pallet_id = Self::to_id(pallet_name); let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ ensure!(!role_permissions.contains(&id), Error::::PermissionAlreadyLinkedToRole ); @@ -301,10 +307,10 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope context in which the permission will be validated. /// - `permission_id`: The permission the user must have. - fn is_authorized(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ - Self::scope_exists(pallet_id, scope_id)?; - Self::permission_exists(pallet_id, permission_id)?; - + fn is_authorized(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ + Self::scope_exists(pallet_name.clone(), scope_id)?; + Self::permission_exists(pallet_name.clone(), permission_id)?; + let pallet_id = Self::to_id(pallet_name); // get roles the user has in this scope let user_roles = >::get((user, pallet_id, scope_id)); // determine if one of the roles has the requested permission @@ -321,9 +327,9 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope context in which the permission will be validated. /// - `role_ids`: A list of roles to validate. - fn has_role(user: T::AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { - Self::scope_exists(pallet_id, scope_id)?; - + fn has_role(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { + Self::scope_exists(pallet_name.clone(), scope_id)?; + let pallet_id = Self::to_id(pallet_name.clone()); let user_roles = >::get((user, pallet_id, scope_id)); ensure!( user_roles.iter().any(|r| role_ids.contains(r) ), @@ -338,7 +344,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope to validate. - fn scope_exists(pallet_id: u64, scope_id:&ScopeId) -> DispatchResult{ + fn scope_exists(pallet_name: Vec, scope_id:&ScopeId) -> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); Ok(()) } @@ -349,7 +356,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `permission_id`: The permission to validate. - fn permission_exists(pallet_id: u64, permission_id: &PermissionId)->DispatchResult{ + fn permission_exists(pallet_name: Vec, permission_id: &PermissionId)->DispatchResult{ + let pallet_id = Self::to_id(pallet_name); ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); Ok(()) } @@ -360,7 +368,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role to validate - fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult{ + fn is_role_linked_to_pallet(pallet_name: Vec, role_id: &RoleId)-> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); // The role exists, now check if the role is assigned to that pallet >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) .ok_or(Error::::RoleNotLinkedToPallet)?; @@ -374,7 +383,8 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role which should have the permission. /// - `permission_id`: The permission which the role should have. - fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ + fn is_permission_linked_to_role(pallet_name: Vec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ + let pallet_id = Self::to_id(pallet_name); let role_permissions = >::get(pallet_id, role_id); ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); Ok(()) @@ -387,10 +397,15 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the users will be retrieved. /// - `role_id`: The role in which the number of users will be counted. - fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize{ + fn get_role_users_len(pallet_name: Vec, scope_id:&ScopeId, role_id: &RoleId) ->usize{ + let pallet_id = Self::to_id(pallet_name); >::get((pallet_id, scope_id, role_id)).len() } + fn to_id(v: Vec)->[u8;32]{ + v.using_encoded(blake2_256) + } + type MaxRolesPerPallet = T::MaxRolesPerPallet; type MaxPermissionsPerRole = T::MaxPermissionsPerRole; diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index e5ae45ad..96c91b74 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -42,7 +42,7 @@ pub mod pallet { #[pallet::constant] type MaxRolesPerUser: Get; #[pallet::constant] - type MaxUsersPerRole: Get; + type MaxUsersPerRole: Get; } #[pallet::pallet] @@ -56,7 +56,7 @@ pub mod pallet { pub(super) type Scopes = StorageMap< _, Blake2_128Concat, - u64, // pallet_id + PalletId, // pallet_id BoundedVec, // scopes_id ValueQuery, >; @@ -76,7 +76,7 @@ pub mod pallet { pub(super) type PalletRoles = StorageMap< _, Blake2_128Concat, - u64, // pallet_id + PalletId, // pallet_id BoundedVec, // role_id ValueQuery, >; @@ -86,7 +86,7 @@ pub mod pallet { pub(super) type Permissions = StorageDoubleMap< _, Blake2_128Concat, - u64, // pallet_id + PalletId, // pallet_id Blake2_128Concat, PermissionId, // permission_id BoundedVec, // permission str @@ -98,7 +98,7 @@ pub mod pallet { pub(super) type PermissionsByRole = StorageDoubleMap< _, Blake2_128Concat, - u64, // pallet_id + PalletId, // pallet_id Blake2_128Concat, RoleId, // role_id BoundedVec, // permission_ids @@ -112,7 +112,7 @@ pub mod pallet { ( NMapKey,// user // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors - NMapKey, // pallet_id + NMapKey, // pallet_id NMapKey, // scope_id ), BoundedVec, // roles (ids) @@ -126,7 +126,7 @@ pub mod pallet { ( // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors // on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes. - NMapKey, // pallet_id + NMapKey, // pallet_id NMapKey, // scope_id NMapKey, // role_id ), @@ -177,6 +177,8 @@ pub mod pallet { UserHasNoRoles, /// The role doesn't have any users assigned to it RoleHasNoUsers, + /// The pallet name is too long + ExceedPalletNameMaxLen, /// The pallet has too many scopes ExceedMaxScopesPerPallet, /// The pallet cannot have more roles diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 203c73cb..a2f31941 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,13 +1,22 @@ use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config, PermissionsByRole, Permissions}; +use codec::Encode; use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::DispatchResult}; +use sp_io::hashing::blake2_256; type AccountId = ::AccountId; -const PALLET_ID : u64 = 1; + +fn pallet_name()->Vec{ + "pallet_test".as_bytes().to_vec() +} + +fn pallet_id()->[u8;32]{ + pallet_name().using_encoded(blake2_256) +} fn create_scope(n: u8)->ScopeId{ let scope_id = [n;32]; - assert_ok!(RBAC::create_scope(PALLET_ID, scope_id)); - assert!(RBAC::scopes(1).contains(&scope_id)); + assert_ok!(RBAC::create_scope(pallet_name(), scope_id)); + assert!(RBAC::scopes(pallet_id()).contains(&scope_id)); scope_id } @@ -34,8 +43,8 @@ fn create_role(role: Vec)->RoleId{ } fn create_and_set_roles(roles: Vec>)->BoundedVec::MaxRolesPerPallet >{ - let role_ids = RBAC::create_and_set_roles(PALLET_ID, roles).unwrap(); - let inserted_roles_list = RBAC::pallet_roles(PALLET_ID); + let role_ids = RBAC::create_and_set_roles(pallet_name(), roles).unwrap(); + let inserted_roles_list = RBAC::pallet_roles(pallet_id()); assert!( role_ids.iter().all(|r_id| inserted_roles_list.contains(r_id)) ); @@ -43,71 +52,71 @@ fn create_and_set_roles(roles: Vec>)->BoundedVec){ - assert_ok!(RBAC::set_multiple_pallet_roles(PALLET_ID, roles)); + assert_ok!(RBAC::set_multiple_pallet_roles(pallet_name(), roles)); } fn remove_scope(n: u8){ - assert_ok!(RBAC::remove_scope(PALLET_ID, [n;32])); - assert!(RBAC::scope_exists(PALLET_ID,&[n;32]).is_err()); + assert_ok!(RBAC::remove_scope(pallet_name(), [n;32])); + assert!(RBAC::scope_exists(pallet_name(),&[n;32]).is_err()); } fn remove_role_from_user(user: AccountId, scope_id: &ScopeId, role_id: RoleId){ - assert_ok!(RBAC::remove_role_from_user(user, PALLET_ID, scope_id, role_id)); - let user_roles = RBAC::roles_by_user((user, PALLET_ID, scope_id)); + assert_ok!(RBAC::remove_role_from_user(user, pallet_name(), scope_id, role_id)); + let user_roles = RBAC::roles_by_user((user, pallet_id(), scope_id)); assert!(!user_roles.contains(&role_id)); - let role_users = RBAC::users_by_scope((PALLET_ID, scope_id, role_id)); + let role_users = RBAC::users_by_scope((pallet_id(), scope_id, role_id)); assert!(!role_users.contains(&user)); } fn remove_pallet_storage(){ - assert_ok!(RBAC::remove_pallet_storage(PALLET_ID)); - assert!(RBAC::scopes(PALLET_ID).is_empty()); - assert!(RBAC::pallet_roles(PALLET_ID).is_empty()); - assert_eq!(>::iter_prefix(PALLET_ID).count(), 0); - assert_eq!(>::iter_prefix(PALLET_ID).count(), 0); + assert_ok!(RBAC::remove_pallet_storage(pallet_name())); + assert!(RBAC::scopes(pallet_id()).is_empty()); + assert!(RBAC::pallet_roles(pallet_id()).is_empty()); + assert_eq!(>::iter_prefix(pallet_id()).count(), 0); + assert_eq!(>::iter_prefix(pallet_id()).count(), 0); } fn assign_role_to_user(user: AccountId, scope_id : &ScopeId, role_id: RoleId){ assert_ok!( - RBAC::assign_role_to_user(user, PALLET_ID, scope_id, role_id) + RBAC::assign_role_to_user(user, pallet_name(), scope_id, role_id) ); - let user_roles = RBAC::roles_by_user((user,PALLET_ID, scope_id)); + let user_roles = RBAC::roles_by_user((user,pallet_id(), scope_id)); assert!(user_roles.contains(&role_id)); - let role_users = RBAC::users_by_scope((PALLET_ID, scope_id, role_id)); + let role_users = RBAC::users_by_scope((pallet_id(), scope_id, role_id)); assert!(role_users.contains(&user)); } fn create_permission(permission: Vec)-> PermissionId{ - let permission_id = RBAC::create_permission(PALLET_ID, permission.clone()).unwrap(); + let permission_id = RBAC::create_permission(pallet_name(), permission.clone()).unwrap(); assert_eq!( - RBAC::permissions(PALLET_ID, permission_id).to_vec(), + RBAC::permissions(pallet_id(), permission_id).to_vec(), permission ); permission_id } fn set_permission_to_role(role_id: RoleId, permission_id: PermissionId){ - assert_ok!(RBAC::set_permission_to_role(PALLET_ID, role_id, permission_id)); - assert!(RBAC::permissions_by_role(PALLET_ID, role_id).contains(&permission_id)); + assert_ok!(RBAC::set_permission_to_role(pallet_name(), role_id, permission_id)); + assert!(RBAC::permissions_by_role(pallet_id(), role_id).contains(&permission_id)); } fn set_multiple_permissions_to_role(role_id: RoleId, permissions: Vec){ assert_ok!( - RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permissions.clone()) + RBAC::set_multiple_permissions_to_role(pallet_name(), role_id, permissions.clone()) ); - let role_permissions = RBAC::permissions_by_role(PALLET_ID, role_id); + let role_permissions = RBAC::permissions_by_role(pallet_id(), role_id); assert!( permissions.iter().all(|p|{role_permissions.contains(p)}), ); } fn create_and_set_permissions(role_id: RoleId, permissions: Vec>)->BoundedVec::MaxPermissionsPerRole>{ - let permission_ids = RBAC::create_and_set_permissions(PALLET_ID, role_id,permissions).unwrap(); - let role_permissions = RBAC::permissions_by_role(PALLET_ID, role_id); + let permission_ids = RBAC::create_and_set_permissions(pallet_name(), role_id,permissions).unwrap(); + let role_permissions = RBAC::permissions_by_role(pallet_id(), role_id); assert!( permission_ids.iter().all(|p|{role_permissions.contains(p)}), ); @@ -115,31 +124,31 @@ fn create_and_set_permissions(role_id: RoleId, permissions: Vec>)->Bound } fn is_authorized(user: AccountId, scope_id : &ScopeId, permission_id: &PermissionId) -> DispatchResult{ - RBAC::is_authorized(user, PALLET_ID, scope_id, permission_id) + RBAC::is_authorized(user, pallet_name(), scope_id, permission_id) } fn has_role(user: AccountId, scope_id : &ScopeId, role_ids: Vec) -> DispatchResult{ - RBAC::has_role(user, PALLET_ID, scope_id, role_ids) + RBAC::has_role(user, pallet_name(), scope_id, role_ids) } fn scope_exists(scope_id : &ScopeId) -> DispatchResult{ - RBAC::scope_exists(PALLET_ID, scope_id) + RBAC::scope_exists(pallet_name(), scope_id) } fn permission_exists(permission_id: &PermissionId) -> DispatchResult { - RBAC::permission_exists(PALLET_ID, permission_id) + RBAC::permission_exists(pallet_name(), permission_id) } fn is_role_linked_to_pallet(role_id: &RoleId) -> DispatchResult { - RBAC::is_role_linked_to_pallet(PALLET_ID, role_id) + RBAC::is_role_linked_to_pallet(pallet_name(), role_id) } fn is_permission_linked_to_role(role_id: &RoleId, permission_id: &PermissionId) -> DispatchResult { - RBAC::is_permission_linked_to_role(PALLET_ID, role_id, permission_id) + RBAC::is_permission_linked_to_role(pallet_name(), role_id, permission_id) } fn get_role_users_len(scope_id : &ScopeId, role_id: &RoleId)-> usize{ - RBAC::get_role_users_len(PALLET_ID, scope_id, role_id) + RBAC::get_role_users_len(pallet_name(), scope_id, role_id) } #[test] @@ -153,7 +162,7 @@ fn create_scope_works() { fn create_scope_twice_should_fail() { new_test_ext().execute_with(|| { create_scope(0); - assert_noop!(RBAC::create_scope(1, [0;32]), Error::::ScopeAlreadyExists); + assert_noop!(RBAC::create_scope(pallet_name(), [0;32]), Error::::ScopeAlreadyExists); }); } @@ -163,7 +172,7 @@ fn exceeding_max_scopes_per_pallet_should_fail() { for n in 0..::MaxScopesPerPallet::get(){ create_scope(n.try_into().unwrap()); } - assert_noop!(RBAC::create_scope(1, [255;32]), Error::::ExceedMaxScopesPerPallet); + assert_noop!(RBAC::create_scope(pallet_name(), [255;32]), Error::::ExceedMaxScopesPerPallet); }); } @@ -186,7 +195,7 @@ fn remove_non_existent_scope_should_fail() { let n_roles = ::MaxRolesPerPallet::get(); create_and_set_roles(gen_roles(n_roles)); assert_noop!( - RBAC::remove_scope(PALLET_ID, [0;32]), + RBAC::remove_scope(pallet_name(), [0;32]), Error::::ScopeNotFound ); }); @@ -229,7 +238,7 @@ fn set_role_to_pallet_should_work() { fn set_nonexistent_role_to_pallet_should_fail() { new_test_ext().execute_with(|| { assert_noop!( - RBAC::set_role_to_pallet(PALLET_ID, [0;32]), + RBAC::set_role_to_pallet(pallet_name(), [0;32]), Error::::RoleNotFound ); }); @@ -241,7 +250,7 @@ fn set_role_to_pallet_twice_should_fail() { let role_id = create_role("owner".as_bytes().to_vec()); set_role_to_pallet(role_id); assert_noop!( - RBAC::set_role_to_pallet(PALLET_ID, role_id), + RBAC::set_role_to_pallet(pallet_name(), role_id), Error::::RoleAlreadyLinkedToPallet ); }); @@ -258,7 +267,7 @@ fn exceeding_max_roles_per_pallet_should_fail() { }); let role_id = create_role("admin".as_bytes().to_vec()); assert_noop!( - RBAC::set_role_to_pallet(PALLET_ID, role_id), + RBAC::set_role_to_pallet(pallet_name(), role_id), Error::::ExceedMaxRolesPerPallet ); }); @@ -285,7 +294,7 @@ fn set_multiple_duplicate_pallet_roles_should_fail() { create_role(role.clone()) }).collect(); assert_noop!( - RBAC::set_multiple_pallet_roles(PALLET_ID, role_ids), + RBAC::set_multiple_pallet_roles(pallet_name(), role_ids), Error::::DuplicateRole ); }); @@ -301,7 +310,7 @@ fn set_multiple_pallet_roles_twice_should_fail() { }).collect(); set_multiple_pallet_roles(role_ids.clone()); assert_noop!( - RBAC::set_multiple_pallet_roles(PALLET_ID, role_ids), + RBAC::set_multiple_pallet_roles(pallet_name(), role_ids), Error::::RoleAlreadyLinkedToPallet ); }); @@ -320,7 +329,7 @@ fn create_and_set_duplicate_role_should_fail() { let mut roles = gen_roles(::MaxRolesPerPallet::get()-1); roles.push("role0".as_bytes().to_vec()); assert_err!( - RBAC::create_and_set_roles(PALLET_ID, roles), + RBAC::create_and_set_roles(pallet_name(), roles), Error::::DuplicateRole ); }); @@ -331,7 +340,7 @@ fn exceeding_max_roles_per_pallet_from_create_and_set_role_should_fail() { new_test_ext().execute_with(|| { let exceed = ::MaxRolesPerPallet::get() + 1; assert_err!( - RBAC::create_and_set_roles(PALLET_ID, gen_roles(exceed)), + RBAC::create_and_set_roles(pallet_name(), gen_roles(exceed)), Error::::ExceedMaxRolesPerPallet ); }); @@ -355,7 +364,7 @@ fn assign_role_to_user_twice_should_fail() { set_role_to_pallet(role_id); assign_role_to_user(0, &scope_id, role_id); assert_noop!( - RBAC::assign_role_to_user(0, PALLET_ID, &scope_id, role_id), + RBAC::assign_role_to_user(0, pallet_name(), &scope_id, role_id), Error::::UserAlreadyHasRole ); }); @@ -367,7 +376,7 @@ fn assign_role_to_user_without_scope_should_fail() { let role_id = create_role("owner".as_bytes().to_vec()); set_role_to_pallet(role_id); assert_noop!( - RBAC::assign_role_to_user(0, PALLET_ID, &[0;32], role_id), + RBAC::assign_role_to_user(0, pallet_name(), &[0;32], role_id), Error::::ScopeNotFound ); }); @@ -389,7 +398,7 @@ fn exceeding_max_roles_per_user_should_fail() { let last_role_id = create_role("owner".as_bytes().to_vec()); set_role_to_pallet(last_role_id); assert_noop!( - RBAC::assign_role_to_user(0, PALLET_ID, &scope_id, last_role_id), + RBAC::assign_role_to_user(0, pallet_name(), &scope_id, last_role_id), Error::::ExceedMaxRolesPerUser ); }); @@ -407,7 +416,7 @@ fn exceeding_max_users_per_role_should_fail() { } // avoiding assert_noop because it checks if the storage mutated assert_err!( - RBAC::assign_role_to_user((max_users_per_role+1).into(), PALLET_ID, &scope_id, role_id), + RBAC::assign_role_to_user((max_users_per_role+1).into(), pallet_name(), &scope_id, role_id), Error::::ExceedMaxUsersPerRole ); }); @@ -429,7 +438,7 @@ fn remove_non_assigned_role_from_user_should_fail() { new_test_ext().execute_with(|| { let scope_id = create_scope(0); assert_noop!( - RBAC::remove_role_from_user(0, PALLET_ID, &scope_id, [0;32]), + RBAC::remove_role_from_user(0, pallet_name(), &scope_id, [0;32]), Error::::UserHasNoRoles ); }); @@ -443,7 +452,7 @@ fn remove_non_existent_role_from_user_should_fail() { set_role_to_pallet(role_id); assign_role_to_user(0, &scope_id, role_id); assert_noop!( - RBAC::remove_role_from_user(0, PALLET_ID, &scope_id, [0;32]), + RBAC::remove_role_from_user(0, pallet_name(), &scope_id, [0;32]), Error::::RoleNotFound ); }); @@ -460,7 +469,7 @@ fn create_permission_should_work() { fn exceeding_permission_max_len_should_fail() { new_test_ext().execute_with(|| { assert_noop!( - RBAC::create_permission(PALLET_ID, "0123456789ABCDFG".as_bytes().to_vec()), + RBAC::create_permission(pallet_name(), "0123456789ABCDFG".as_bytes().to_vec()), Error::::ExceedPermissionMaxLen ); }); @@ -482,7 +491,7 @@ fn set_non_existent_permission_to_role_should_fail() { let role_id = create_role("admin".as_bytes().to_vec()); set_role_to_pallet(role_id); assert_noop!( - RBAC::set_permission_to_role(PALLET_ID, role_id, [0;32]), + RBAC::set_permission_to_role(pallet_name(), role_id, [0;32]), Error::::PermissionNotFound ); }); @@ -497,7 +506,7 @@ fn set_permission_to_role_twice_should_fail() { let permission_id = create_permission("enroll".as_bytes().to_vec()); set_permission_to_role(role_id, permission_id); assert_noop!( - RBAC::set_permission_to_role(PALLET_ID, role_id, permission_id), + RBAC::set_permission_to_role(pallet_name(), role_id, permission_id), Error::::DuplicatePermission ); }); @@ -516,7 +525,7 @@ fn exceeding_max_permissions_per_role_should_fail() { }); let last_permission_id = create_permission("enroll".as_bytes().to_vec()); assert_noop!( - RBAC::set_permission_to_role(PALLET_ID,role_id, last_permission_id), + RBAC::set_permission_to_role(pallet_name(),role_id, last_permission_id), Error::::ExceedMaxPermissionsPerRole ); }); @@ -546,7 +555,7 @@ fn set_multiple_duplicate_permissions_to_role_should_fail() { create_permission(permission.to_vec()) }).collect(); assert_noop!( - RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + RBAC::set_multiple_permissions_to_role(pallet_name(), role_id, permission_ids), Error::::DuplicatePermission ); }); @@ -561,7 +570,7 @@ fn set_multiple_permissions_to_unlinked_role_should_fail() { create_permission(permission.to_vec()) }).collect(); assert_noop!( - RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + RBAC::set_multiple_permissions_to_role(pallet_name(), role_id, permission_ids), Error::::RoleNotLinkedToPallet ); }); @@ -578,7 +587,7 @@ fn set_multiple_permissions_to_role_twice_should_fail() { }).collect(); set_multiple_permissions_to_role(role_id, permission_ids.clone()); assert_noop!( - RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + RBAC::set_multiple_permissions_to_role(pallet_name(), role_id, permission_ids), Error::::PermissionAlreadyLinkedToRole ); }); @@ -594,7 +603,7 @@ fn exceeding_max_permissions_per_role_from_set_multiple_permissions_to_role_shou create_permission(permission.to_vec()) }).collect(); assert_noop!( - RBAC::set_multiple_permissions_to_role(PALLET_ID, role_id, permission_ids), + RBAC::set_multiple_permissions_to_role(pallet_name(), role_id, permission_ids), Error::::ExceedMaxPermissionsPerRole ); }); @@ -618,7 +627,7 @@ fn create_set_duplicate_permissions_to_role_should_fail() { let mut permissions = gen_permissions(::MaxPermissionsPerRole::get()-1); permissions.push("permission0".as_bytes().to_vec()); assert_noop!( - RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + RBAC::create_and_set_permissions(pallet_name(), role_id, permissions), Error::::DuplicatePermission ); }); @@ -630,7 +639,7 @@ fn create_and_set_permissions_to_unlinked_role_should_fail() { let role_id = create_role("admin".as_bytes().to_vec()); let permissions = gen_permissions(::MaxPermissionsPerRole::get()); assert_noop!( - RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + RBAC::create_and_set_permissions(pallet_name(), role_id, permissions), Error::::RoleNotLinkedToPallet ); }); @@ -644,7 +653,7 @@ fn create_and_set_multiple_permissions_to_role_twice_should_fail() { set_role_to_pallet(role_id); create_and_set_permissions(role_id, permissions.clone()); assert_noop!( - RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + RBAC::create_and_set_permissions(pallet_name(), role_id, permissions), Error::::PermissionAlreadyLinkedToRole ); }); @@ -657,7 +666,7 @@ fn exceeding_max_permissions_per_role_from_create_and_set_permissions_should_fai let permissions = gen_permissions(::MaxPermissionsPerRole::get()+1); set_role_to_pallet(role_id); assert_err!( - RBAC::create_and_set_permissions(PALLET_ID, role_id, permissions), + RBAC::create_and_set_permissions(pallet_name(), role_id, permissions), Error::::ExceedMaxPermissionsPerRole ); }); diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 712058b7..3e310b23 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -3,6 +3,7 @@ use frame_support::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; +pub type PalletId = [u8;32]; pub type RoleId = [u8;32]; pub type ScopeId = [u8;32]; pub type PermissionId = [u8;32]; @@ -19,33 +20,34 @@ pub trait RoleBasedAccessControl{ type RoleMaxLen: Get; type PermissionMaxLen: Get; // scopes - fn create_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult; + fn create_scope(pallet_name: Vec, scope_id: ScopeId) -> DispatchResult; // scope removal - fn remove_scope(pallet_id: u64, scope_id: ScopeId) -> DispatchResult; + fn remove_scope(pallet_name: Vec, scope_id: ScopeId) -> DispatchResult; // removes all from one pallet/application - fn remove_pallet_storage(pallet_id: u64) -> DispatchResult; + fn remove_pallet_storage(pallet_name: Vec) -> DispatchResult; // roles creation and setting - fn create_and_set_roles(pallet_id: u64, roles: Vec>) -> + fn create_and_set_roles(pallet_name: Vec, roles: Vec>) -> Result, DispatchError>; fn create_role(role: Vec)-> Result; - fn set_role_to_pallet(pallet_id: u64, role_id: RoleId )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_id: u64, roles: Vec)->DispatchResult; - fn assign_role_to_user(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + fn set_role_to_pallet(pallet_name: Vec, role_id: RoleId )-> DispatchResult; + fn set_multiple_pallet_roles(pallet_name: Vec, roles: Vec)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // role removal - fn remove_role_from_user(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + fn remove_role_from_user(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // permissions - fn create_and_set_permissions(pallet_id: u64, role: RoleId, permissions: Vec>)-> + fn create_and_set_permissions(pallet_name: Vec, role: RoleId, permissions: Vec>)-> Result, DispatchError>; - fn create_permission(pallet_id: u64, permissions: Vec) -> Result; - fn set_permission_to_role( pallet_id: u64, role: RoleId, permission: PermissionId ) -> DispatchResult; - fn set_multiple_permissions_to_role( pallet_id: u64, role: RoleId, permission: Vec )-> DispatchResult; + fn create_permission(pallet_name: Vec, permissions: Vec) -> Result; + fn set_permission_to_role( pallet_name: Vec, role: RoleId, permission: PermissionId ) -> DispatchResult; + fn set_multiple_permissions_to_role( pallet_name: Vec, role: RoleId, permission: Vec )-> DispatchResult; // helpers - fn is_authorized(user: AccountId, pallet_id: u64, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; - fn has_role(user: AccountId, pallet_id: u64, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; - fn scope_exists(pallet_id: u64, scope_id:&ScopeId) -> DispatchResult; - fn permission_exists(pallet_id: u64, permission_id: &PermissionId)->DispatchResult; - fn is_role_linked_to_pallet(pallet_id: u64, role_id: &RoleId)-> DispatchResult; - fn is_permission_linked_to_role(pallet_id: u64, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; - fn get_role_users_len(pallet_id: u64, scope_id:&ScopeId, role_id: &RoleId) -> usize; + fn is_authorized(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; + fn has_role(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; + fn scope_exists(pallpallet_nameet_id: Vec, scope_id:&ScopeId) -> DispatchResult; + fn permission_exists(pallet_name: Vec, permission_id: &PermissionId)->DispatchResult; + fn is_role_linked_to_pallet(pallet_name: Vec, role_id: &RoleId)-> DispatchResult; + fn is_permission_linked_to_role(pallet_name: Vec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; + fn get_role_users_len(pallet_name: Vec, scope_id:&ScopeId, role_id: &RoleId) -> usize; + fn to_id(v: Vec)->[u8;32]; } \ No newline at end of file From 50b6e4551cd07e34bac5e447eae4f72cceae95ee Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 25 Aug 2022 09:26:31 -0500 Subject: [PATCH 095/103] hide all currency uses & imports --- pallets/gated-marketplace/src/functions.rs | 60 +++++++++++----------- pallets/gated-marketplace/src/lib.rs | 8 +-- pallets/gated-marketplace/src/mock.rs | 2 +- pallets/gated-marketplace/src/types.rs | 4 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 36876017..2d0b32f7 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,8 +4,8 @@ use frame_support::{pallet_prelude::*}; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; -use frame_support::traits::{Currency}; -use frame_support::traits::ExistenceRequirement::KeepAlive; +//use frame_support::traits::{Currency}; +//use frame_support::traits::ExistenceRequirement::KeepAlive; impl Pallet { @@ -151,7 +151,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { //This function is only called by the owner of the marketplace //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -164,7 +164,7 @@ impl Pallet { } //ensure the price is valid - Self::is_the_price_valid(price)?; + // Self::is_the_price_valid(price)?; //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; @@ -214,7 +214,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { //ensure the item is for sale, if not, return error Self::can_this_item_receive_buy_orders(collection_id, item_id, marketplace_id)?; @@ -231,11 +231,11 @@ impl Pallet { } //ensure user has enough balance to create the offer - let total_user_balance = T::LocalCurrency::total_balance(&authority); - ensure!(total_user_balance >= price, Error::::NotEnoughBalance); + // let total_user_balance = T::LocalCurrency::total_balance(&authority); + // ensure!(total_user_balance >= price, Error::::NotEnoughBalance); //ensure the price is valid - Self::is_the_price_valid(price)?; + // Self::is_the_price_valid(price)?; //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; @@ -310,11 +310,11 @@ impl Pallet { //TODO: Use free_balance instead of total_balance //Get the buyer's balance - let total_amount_buyer = T::LocalCurrency::total_balance(&buyer); + // let total_amount_buyer = T::LocalCurrency::total_balance(&buyer); //ensure the buyer has enough balance to buy the item - ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); + // ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance - T::LocalCurrency::transfer(&buyer, &owner_item, offer_data.price, KeepAlive)?; + // T::LocalCurrency::transfer(&buyer, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the buyer pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, buyer.clone(), |_, _|{ @@ -362,11 +362,11 @@ impl Pallet { //TODO: Use free_balance instead of total_balance //Get the buyer's balance - let total_amount_buyer = T::LocalCurrency::total_balance(&offer_data.creator); + // let total_amount_buyer = T::LocalCurrency::total_balance(&offer_data.creator); //ensure the buy_offer_creator has enough balance to buy the item - ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); + // ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance to the owner of the item - T::LocalCurrency::transfer(&offer_data.creator, &owner_item, offer_data.price, KeepAlive)?; + // T::LocalCurrency::transfer(&offer_data.creator, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the market_participant pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, offer_data.creator.clone(), |_, _|{ @@ -743,14 +743,14 @@ impl Pallet { } } - fn _get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { - //we already know that the offer exists, so we don't need to check it here. - if let Some(offer) = >::get(offer_id) { - return Ok(offer.price); - } else { - return Err(Error::::OfferNotFound)?; - } - } + // fn _get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { + // //we already know that the offer exists, so we don't need to check it here. + // if let Some(offer) = >::get(offer_id) { + // return Ok(offer.price); + // } else { + // return Err(Error::::OfferNotFound)?; + // } + // } fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; @@ -759,14 +759,14 @@ impl Pallet { Ok(()) } - fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { - let minimun_amount: BalanceOf = 1000u32.into(); - if price > minimun_amount { - return Ok(()); - } else { - return Err(Error::::PriceMustBeGreaterThanZero)?; - } - } + // fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { + // let minimun_amount: BalanceOf = 1000u32.into(); + // if price > minimun_amount { + // return Ok(()); + // } else { + // return Err(Error::::PriceMustBeGreaterThanZero)?; + // } + // } fn _get_offer_creator(offer_id: [u8;32],) -> Result { //we already know that the offer exists, so we don't need to check it here. diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index d86c03fb..cc95b129 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -20,7 +20,7 @@ mod types; pub mod pallet { use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; use frame_system::pallet_prelude::*; - use frame_support::traits::Currency; + //use frame_support::traits::Currency; //use sp_runtime::sp_std::vec::Vec; use crate::types::*; //use frame_support::traits::tokens::Balance; @@ -29,7 +29,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config + pallet_timestamp::Config{ type Event: From> + IsType<::Event>; - type LocalCurrency: Currency; + //type LocalCurrency: Currency; //type Balance: Balance + MaybeSerializeDeserialize + Debug + MaxEncodedLen; type RemoveOrigin: EnsureOrigin; @@ -533,7 +533,7 @@ pub mod pallet { /// - If the selected collection doesn't exist, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, price) @@ -626,7 +626,7 @@ pub mod pallet { /// - You need to have the enough balance to create the buy order. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { + pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, price) diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 2f31cea6..00bc9901 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -88,7 +88,7 @@ impl pallet_gated_marketplace::Config for Test { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxOffersPerMarket = MaxOffersPerMarket; type MaxMarketsPerItem = MaxMarketsPerItem; - type LocalCurrency = Balances; + //type LocalCurrency = Balances; } impl pallet_fruniques::Config for Test { diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 769cd14b..ca2f34dc 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -4,7 +4,7 @@ use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; use frame_support::traits::Currency; -pub type BalanceOf = <::LocalCurrency as Currency<::AccountId>>::Balance; +//pub type BalanceOf = <::LocalCurrency as Currency<::AccountId>>::Balance; #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -99,7 +99,7 @@ pub struct OfferData{ pub collection_id: T::CollectionId, pub item_id: T::ItemId, pub creator: T::AccountId, - pub price: BalanceOf, + pub price: u128, pub status: OfferStatus, pub creation_date: u64, pub expiration_date: u64, From fd994bcaf38431e70caf1d98d4fbf215348935a0 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 25 Aug 2022 09:32:26 -0500 Subject: [PATCH 096/103] hide all currency uses & imports --- pallets/gated-marketplace/src/types.rs | 2 +- runtime/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index ca2f34dc..944d1b27 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -2,7 +2,7 @@ use super::*; use frame_support::pallet_prelude::*; //use frame_system::pallet_prelude::*; -use frame_support::traits::Currency; +// use frame_support::traits::Currency; //pub type BalanceOf = <::LocalCurrency as Currency<::AccountId>>::Balance; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 57e00343..4c492cce 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -572,7 +572,7 @@ impl pallet_gated_marketplace::Config for Runtime { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxMarketsPerItem = MaxMarketsPerItem; type MaxOffersPerMarket = MaxOffersPerMarket; - type LocalCurrency = Balances; + //type LocalCurrency = Balances; } parameter_types! { From c469b1dd62023e692b042135f3e50e6eed9a8e99 Mon Sep 17 00:00:00 2001 From: didiermis Date: Thu, 25 Aug 2022 14:26:15 -0500 Subject: [PATCH 097/103] I replaced the tight coupling for the timestamp pallet, with a loosely coupling instead. --- pallets/gated-marketplace/src/functions.rs | 38 +++------------------- pallets/gated-marketplace/src/lib.rs | 25 ++++++++++---- pallets/gated-marketplace/src/mock.rs | 3 +- runtime/src/lib.rs | 6 +++- 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 2d0b32f7..4b817d8c 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -4,6 +4,7 @@ use frame_support::{pallet_prelude::*}; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; +use frame_support::traits::Time; //use frame_support::traits::{Currency}; //use frame_support::traits::ExistenceRequirement::KeepAlive; @@ -712,26 +713,13 @@ impl Pallet { } Ok(()) } - - //TODO: merge the timestamp function to convert from moment to milliseconds. - fn convert_moment_to_u64_in_milliseconds(date: T::Moment) -> Result { - let date_as_u64_millis; - if let Some(_date_as_u64) = TryInto::::try_into(date).ok() { - date_as_u64_millis = _date_as_u64; - } else { - return Err(Error::::TimestampError)?; - } - - Ok(date_as_u64_millis) - } + fn get_timestamp_in_milliseconds() -> Option<(u64, u64)> { - let timestamp: ::Moment = >::get(); - - let timestamp2 = Self::convert_moment_to_u64_in_milliseconds(timestamp).unwrap_or(0); - let timestamp3 = timestamp2 + (7 * 24 * 60 * 60 * 1000); + let timestamp:u64 = T::Timestamp::now().into(); + let timestamp2 = timestamp + (7 * 24 * 60 * 60 * 1000); - Some((timestamp2, timestamp3)) + Some((timestamp, timestamp2)) } fn _is_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ @@ -743,14 +731,6 @@ impl Pallet { } } - // fn _get_offer_price(offer_id: [u8;32],) -> Result, DispatchError> { - // //we already know that the offer exists, so we don't need to check it here. - // if let Some(offer) = >::get(offer_id) { - // return Ok(offer.price); - // } else { - // return Err(Error::::OfferNotFound)?; - // } - // } fn does_exist_offer_id_for_this_item(collection_id: T::CollectionId, item_id: T::ItemId, offer_id: [u8;32]) -> DispatchResult { let offers = >::try_get(collection_id, item_id).map_err(|_| Error::::OfferNotFound)?; @@ -759,14 +739,6 @@ impl Pallet { Ok(()) } - // fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { - // let minimun_amount: BalanceOf = 1000u32.into(); - // if price > minimun_amount { - // return Ok(()); - // } else { - // return Err(Error::::PriceMustBeGreaterThanZero)?; - // } - // } fn _get_offer_creator(offer_id: [u8;32],) -> Result { //we already know that the offer exists, so we don't need to check it here. diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index cc95b129..99aedc96 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -18,19 +18,32 @@ mod types; #[frame_support::pallet] pub mod pallet { - use frame_support::{pallet_prelude::{*, OptionQuery}, transactional}; + use frame_support::pallet_prelude::*; + use frame_support::transactional; use frame_system::pallet_prelude::*; + use sp_runtime::traits::Scale; + use frame_support::traits::Time; //use frame_support::traits::Currency; - //use sp_runtime::sp_std::vec::Vec; + + use crate::types::*; - //use frame_support::traits::tokens::Balance; - //use std::fmt::Debug; #[pallet::config] - pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config + pallet_timestamp::Config{ + pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config{ type Event: From> + IsType<::Event>; + //type LocalCurrency: Currency; - //type Balance: Balance + MaybeSerializeDeserialize + Debug + MaxEncodedLen; + + type Moment: Parameter + + Default + + Scale + + Copy + + MaxEncodedLen + + scale_info::StaticTypeInfo + + Into; + + type Timestamp: Time; + type RemoveOrigin: EnsureOrigin; #[pallet::constant] diff --git a/pallets/gated-marketplace/src/mock.rs b/pallets/gated-marketplace/src/mock.rs index 00bc9901..095f37b1 100644 --- a/pallets/gated-marketplace/src/mock.rs +++ b/pallets/gated-marketplace/src/mock.rs @@ -70,7 +70,6 @@ parameter_types! { pub const MaxFiles: u32 = 10; pub const MaxApplicationsPerCustodian: u32 = 2; pub const MaxMarketsPerItem: u32 = 10; - pub const MaxOffersPerMarket: u32 = 100; } @@ -88,6 +87,8 @@ impl pallet_gated_marketplace::Config for Test { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxOffersPerMarket = MaxOffersPerMarket; type MaxMarketsPerItem = MaxMarketsPerItem; + type Timestamp = Timestamp; + type Moment = u64; //type LocalCurrency = Balances; } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 4c492cce..b44f6fa1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -68,6 +68,8 @@ pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; +pub type Moment = u64; + /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -572,7 +574,9 @@ impl pallet_gated_marketplace::Config for Runtime { type MaxApplicationsPerCustodian = MaxApplicationsPerCustodian; type MaxMarketsPerItem = MaxMarketsPerItem; type MaxOffersPerMarket = MaxOffersPerMarket; - //type LocalCurrency = Balances; + type Timestamp = Timestamp; + type Moment = Moment; + } parameter_types! { From 6fcf792acc988e1fae7380dec6190e85dc1c5fa0 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Thu, 25 Aug 2022 21:22:30 -0500 Subject: [PATCH 098/103] adds id or name enum minor clippy optimizations --- pallets/gated-marketplace/src/functions.rs | 6 +- pallets/gated-marketplace/src/tests.rs | 40 +++---- pallets/rbac/src/functions.rs | 117 +++++++++++---------- pallets/rbac/src/tests.rs | 10 +- pallets/rbac/src/types.rs | 61 +++++++---- 5 files changed, 131 insertions(+), 103 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 8047117f..1533fffa 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -853,8 +853,10 @@ impl Pallet { - pub fn pallet_id()->Vec{ - Self::module_name().as_bytes().to_vec() + pub fn pallet_id()->IdOrVec{ + IdOrVec::Vec( + Self::module_name().as_bytes().to_vec() + ) } } \ No newline at end of file diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index 6cbdaacc..7603d8b3 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -8,6 +8,10 @@ use sp_io::hashing::blake2_256; type RbacErr = pallet_rbac::Error; + +fn pallet_id() ->[u8;32]{ + GatedMarketplace::pallet_id().to_id() +} fn create_label( label : &str ) -> BoundedVec { let s: Vec = label.as_bytes().into(); s.try_into().unwrap_or_default() @@ -314,7 +318,7 @@ fn add_authority_appraiser_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Appraiser]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::Appraiser.id())); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).contains(&MarketplaceRole::Appraiser.id())); }); } @@ -325,7 +329,7 @@ fn add_authority_admin_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::Admin]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::Admin.id())); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).contains(&MarketplaceRole::Admin.id())); }); } @@ -336,7 +340,7 @@ fn add_authority_redenmption_specialist_works() { let m_id = create_label("my marketplace").using_encoded(blake2_256); assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![MarketplaceRole::RedemptionSpecialist]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).contains(&MarketplaceRole::RedemptionSpecialist.id())); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).contains(&MarketplaceRole::RedemptionSpecialist.id())); }); } @@ -372,7 +376,7 @@ fn remove_authority_appraiser_works() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Appraiser, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).is_empty()); }); } @@ -384,7 +388,7 @@ fn remove_authority_admin_works() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::Admin, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).is_empty()); }); } @@ -396,7 +400,7 @@ fn remove_authority_redemption_specialist_work() { assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); assert_ok!(GatedMarketplace::remove_authority(Origin::signed(1), 3, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).is_empty()); }); } @@ -546,13 +550,13 @@ fn remove_marketplace_deletes_storage_from_marketplaces_by_authority_works(){ assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); //assert!(GatedMarketplace::marketplaces_by_authority(1, m_id) == vec![]); - assert!(RBAC::roles_by_user((1, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((1, pallet_id(), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(2, m_id) == vec![]); - assert!(RBAC::roles_by_user((2, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((2, pallet_id(), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(3, m_id) == vec![]); - assert!(RBAC::roles_by_user((3, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((3, pallet_id(), m_id)).is_empty()); //assert!(GatedMarketplace::marketplaces_by_authority(4, m_id) == vec![]); - assert!(RBAC::roles_by_user((4, RBAC::to_id(GatedMarketplace::pallet_id()), m_id)).is_empty()); + assert!(RBAC::roles_by_user((4, pallet_id(), m_id)).is_empty()); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); @@ -569,24 +573,24 @@ fn remove_marketplace_deletes_storage_from_authorities_by_marketplace_works(){ assert_ok!(GatedMarketplace::add_authority(Origin::signed(1), 4, MarketplaceRole::RedemptionSpecialist, m_id)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![1]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Owner.id())).contains(&1)); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Owner.id())).contains(&1)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![2]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Admin.id())).contains(&2)); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Admin.id())).contains(&2)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![3]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Appraiser.id())).contains(&3)); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Appraiser.id())).contains(&3)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![4]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::RedemptionSpecialist.id())).contains(&4)); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).contains(&4)); assert_ok!(GatedMarketplace::remove_marketplace(Origin::signed(1), m_id)); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Owner) == vec![]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Owner.id())).is_empty()); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Owner.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Admin) == vec![]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Admin.id())).is_empty()); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Admin.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::Appraiser) == vec![]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::Appraiser.id())).is_empty()); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::Appraiser.id())).is_empty()); //assert!(GatedMarketplace::authorities_by_marketplace(m_id, MarketplaceRole::RedemptionSpecialist) == vec![]); - assert!(RBAC::users_by_scope((RBAC::to_id(GatedMarketplace::pallet_id()), m_id, MarketplaceRole::RedemptionSpecialist.id())).is_empty()); + assert!(RBAC::users_by_scope((pallet_id(), m_id, MarketplaceRole::RedemptionSpecialist.id())).is_empty()); assert!(GatedMarketplace::marketplaces(m_id).is_none()); }); } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 6426678d..2ecef9ad 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -15,8 +15,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The newly generated scope identifier. - fn create_scope(pallet_name: Vec, scope_id: ScopeId)-> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); + fn create_scope(pallet: IdOrVec, scope_id: ScopeId)-> DispatchResult{ + let pallet_id = pallet.to_id(); >::try_mutate(pallet_id, |scopes|{ ensure!(!scopes.contains(&scope_id), Error::::ScopeAlreadyExists); scopes.try_push(scope_id).map_err(|_| Error::::ExceedMaxScopesPerPallet)?; @@ -31,8 +31,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope identifier to remove. - fn remove_scope(pallet_id:Vec, scope_id: ScopeId) -> DispatchResult{ - let pallet_id = Self::to_id(pallet_id); + fn remove_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult{ + let pallet_id = pallet.to_id(); // remove on scopes >::try_mutate_exists::<_,(),DispatchError,_>(pallet_id, |scopes_option|{ let scopes = scopes_option.as_mut().ok_or(Error::::ScopeNotFound)?; @@ -43,8 +43,8 @@ impl RoleBasedAccessControl for Pallet{ } Ok(()) })?; - let mut scope_users = >::iter_prefix((pallet_id, scope_id)).map( - |(_role, users)|users).flatten().collect::>(); + let mut scope_users = >::iter_prefix((pallet_id, scope_id)) + .flat_map(|(_role, users)|users).collect::>(); // exclude duplicate users scope_users.sort(); scope_users.dedup(); // remove on RolesByUser @@ -65,12 +65,13 @@ impl RoleBasedAccessControl for Pallet{ /// to that pallet. /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. - fn remove_pallet_storage(pallet_name: Vec) -> DispatchResult{ - let pallet_id = Self::to_id(pallet_name.clone()); + fn remove_pallet_storage(pallet: IdOrVec) -> DispatchResult{ + let pallet_id_enum = pallet.to_id_enum(); + let pallet_id = pallet_id_enum.to_id(); //remove all scopes let scopes = >::get(pallet_id); for scope in scopes{ - Self::remove_scope(pallet_name.clone(), scope)?; + Self::remove_scope(pallet_id_enum.clone(), scope)?; } // remove all roles let pallet_roles = >::take(pallet_id); @@ -95,13 +96,13 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `roles`: A list of roles to create, encoded in bytes. - fn create_and_set_roles(pallet_name: Vec, roles: Vec>) -> + fn create_and_set_roles(pallet: IdOrVec, roles: Vec>) -> Result, DispatchError>{ let mut role_ids= Vec::<[u8;32]>::new(); for role in roles{ role_ids.push( Self::create_role(role.to_owned())? ); } - Self::set_multiple_pallet_roles(pallet_name, role_ids.clone())?; + Self::set_multiple_pallet_roles(pallet.to_id_enum(), role_ids.clone())?; let bounded_ids = Self::bound(role_ids, Error::::ExceedMaxRolesPerPallet)?; Ok(bounded_ids) } @@ -128,10 +129,9 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The unique role identifier. - fn set_role_to_pallet(pallet_name: Vec, role_id: RoleId )-> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); + fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId )-> DispatchResult{ ensure!(>::contains_key(role_id), Error::::RoleNotFound); - >::try_mutate(pallet_id, |roles|{ + >::try_mutate(pallet.to_id(), |roles|{ ensure!(!roles.contains(&role_id), Error::::RoleAlreadyLinkedToPallet ); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerPallet) })?; @@ -144,8 +144,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `roles`: A list of unique role identifiers. - fn set_multiple_pallet_roles(pallet_name: Vec, roles: Vec)->DispatchResult{ - let pallet_id = Self::to_id(pallet_name); + fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec)->DispatchResult{ + let pallet_id = pallet.to_id(); // checks for duplicates: ensure!(Self::has_unique_elements(roles.clone()), Error::::DuplicateRole); let pallet_roles = >::get(&pallet_id); @@ -167,10 +167,11 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the role will be granted. /// - `role_id`: The role identifier to grant for the user. - fn assign_role_to_user(user: T::AccountId, pallet_name: Vec , scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ - Self::scope_exists(pallet_name.clone(), scope_id)?; - Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id)?; - let pallet_id = Self::to_id(pallet_name); + fn assign_role_to_user(user: T::AccountId, pallet: IdOrVec , scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + let pallet_id_enum = pallet.to_id_enum(); + let pallet_id = pallet_id_enum.to_id(); + Self::scope_exists(pallet_id_enum.clone(), scope_id)?; + Self::is_role_linked_to_pallet(pallet_id_enum, &role_id)?; >::try_mutate((&user, pallet_id, scope_id), | roles |{ ensure!(!roles.contains(&role_id), Error::::UserAlreadyHasRole); roles.try_push(role_id).map_err(|_| Error::::ExceedMaxRolesPerUser) @@ -192,8 +193,8 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the role will be removed. /// - `role_id`: The role identifier to remove from the user. - fn remove_role_from_user(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); + fn remove_role_from_user(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult{ + let pallet_id = pallet.to_id(); >::try_mutate_exists::<_,(),DispatchError,_>((user.clone(), pallet_id, scope_id), |user_roles_option|{ let user_roles = user_roles_option.as_mut().ok_or(Error::::UserHasNoRoles)?; let r_pos = user_roles.iter().position(|&r| r==role_id).ok_or(Error::::RoleNotFound)?; @@ -224,15 +225,16 @@ impl RoleBasedAccessControl for Pallet{ /// be linked to. /// - `permissions`: A list of permissions to create and link, /// encoded in bytes. - fn create_and_set_permissions(pallet_name: Vec, role_id: RoleId, permissions: Vec>)-> + fn create_and_set_permissions(pallet: IdOrVec, role_id: RoleId, permissions: Vec>)-> Result, DispatchError> { ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); - Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id )?; + let pallet_id_enum = pallet.to_id_enum(); + Self::is_role_linked_to_pallet(pallet_id_enum.clone(), &role_id )?; let mut permission_ids = Vec::<[u8;32]>::new(); for permission in permissions{ - permission_ids.push( Self::create_permission(pallet_name.clone(), permission.to_owned())? ); + permission_ids.push( Self::create_permission(pallet_id_enum.clone(), permission.to_owned())? ); } - Self::set_multiple_permissions_to_role(pallet_name, role_id, permission_ids.clone())?; + Self::set_multiple_permissions_to_role(pallet_id_enum, role_id, permission_ids.clone())?; let b_permissions = Self::bound(permission_ids, Error::::ExceedMaxPermissionsPerRole)?; Ok(b_permissions) } @@ -243,12 +245,13 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `permission`: The permission to insert, encoded in bytes. - fn create_permission(pallet_name: Vec, permission: Vec) -> Result{ + fn create_permission(pallet: IdOrVec, permission: Vec) -> Result{ let permission_id = Self::to_id(permission.clone()); + let pallet_id = pallet.to_id(); let b_permission = Self::bound:: <_,T::PermissionMaxLen>(permission, Error::::ExceedPermissionMaxLen)?; - let pallet_id = Self::to_id(pallet_name); + if !>::contains_key(pallet_id, permission_id){ >::insert(pallet_id, permission_id, b_permission); } @@ -262,9 +265,11 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role identifier to which the permission will be added. /// - `permission_id`: The permission to assign to the role. - fn set_permission_to_role( pallet_name: Vec, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ - Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id)?; - let pallet_id = Self::to_id(pallet_name); + fn set_permission_to_role( pallet: IdOrVec, role_id: RoleId, permission_id: PermissionId ) -> DispatchResult{ + let pallet_id_enum = pallet.to_id_enum(); + let pallet_id = pallet_id_enum.to_id(); + Self::is_role_linked_to_pallet(pallet_id_enum, &role_id)?; + ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); >::try_mutate(pallet_id, role_id, | role_permissions|{ @@ -282,11 +287,13 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role identifier to which the permissions will be added. /// - `permissions`: A list of permission identifiers to assign to the role. - fn set_multiple_permissions_to_role( pallet_name: Vec, role_id: RoleId, permissions: Vec )-> DispatchResult{ + fn set_multiple_permissions_to_role( pallet: IdOrVec, role_id: RoleId, permissions: Vec )-> DispatchResult{ // checks for duplicates: ensure!(Self::has_unique_elements(permissions.clone()), Error::::DuplicatePermission); - Self::is_role_linked_to_pallet(pallet_name.clone(), &role_id )?; - let pallet_id = Self::to_id(pallet_name); + let pallet_id_enum = pallet.to_id_enum(); + let pallet_id = pallet_id_enum.to_id(); + Self::is_role_linked_to_pallet(pallet_id_enum, &role_id )?; + let role_permissions = >::get(&pallet_id, role_id); for id in permissions.clone(){ ensure!(!role_permissions.contains(&id), Error::::PermissionAlreadyLinkedToRole ); @@ -307,10 +314,11 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope context in which the permission will be validated. /// - `permission_id`: The permission the user must have. - fn is_authorized(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ - Self::scope_exists(pallet_name.clone(), scope_id)?; - Self::permission_exists(pallet_name.clone(), permission_id)?; - let pallet_id = Self::to_id(pallet_name); + fn is_authorized(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId, permission_id: &PermissionId) -> DispatchResult{ + let pallet_id_enum = pallet.to_id_enum(); + let pallet_id = pallet_id_enum.to_id(); + Self::scope_exists(pallet_id_enum.clone(), scope_id)?; + Self::permission_exists(pallet_id_enum, permission_id)?; // get roles the user has in this scope let user_roles = >::get((user, pallet_id, scope_id)); // determine if one of the roles has the requested permission @@ -327,10 +335,10 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope context in which the permission will be validated. /// - `role_ids`: A list of roles to validate. - fn has_role(user: T::AccountId, pallet_name: Vec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { - Self::scope_exists(pallet_name.clone(), scope_id)?; - let pallet_id = Self::to_id(pallet_name.clone()); - let user_roles = >::get((user, pallet_id, scope_id)); + fn has_role(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult { + let pallet_id_enum = pallet.to_id_enum(); + Self::scope_exists(pallet_id_enum.clone(), scope_id)?; + let user_roles = >::get((user, pallet_id_enum.to_id(), scope_id)); ensure!( user_roles.iter().any(|r| role_ids.contains(r) ), Error::::NotAuthorized @@ -344,9 +352,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope to validate. - fn scope_exists(pallet_name: Vec, scope_id:&ScopeId) -> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); - ensure!(>::get(pallet_id).contains(&scope_id), Error::::ScopeNotFound); + fn scope_exists(pallet: IdOrVec, scope_id:&ScopeId) -> DispatchResult{ + ensure!(>::get(pallet.to_id()).contains(scope_id), Error::::ScopeNotFound); Ok(()) } @@ -356,9 +363,8 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `permission_id`: The permission to validate. - fn permission_exists(pallet_name: Vec, permission_id: &PermissionId)->DispatchResult{ - let pallet_id = Self::to_id(pallet_name); - ensure!(>::contains_key(pallet_id, permission_id), Error::::PermissionNotFound); + fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId)->DispatchResult{ + ensure!(>::contains_key(pallet.to_id(), permission_id), Error::::PermissionNotFound); Ok(()) } @@ -368,10 +374,9 @@ impl RoleBasedAccessControl for Pallet{ /// ### Parameters: /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role to validate - fn is_role_linked_to_pallet(pallet_name: Vec, role_id: &RoleId)-> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); + fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId)-> DispatchResult{ // The role exists, now check if the role is assigned to that pallet - >::get(pallet_id).iter().find(|pallet_role| *pallet_role==role_id ) + >::get(pallet.to_id()).iter().find(|pallet_role| *pallet_role==role_id ) .ok_or(Error::::RoleNotLinkedToPallet)?; Ok(()) } @@ -383,9 +388,8 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `role_id`: The role which should have the permission. /// - `permission_id`: The permission which the role should have. - fn is_permission_linked_to_role(pallet_name: Vec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ - let pallet_id = Self::to_id(pallet_name); - let role_permissions = >::get(pallet_id, role_id); + fn is_permission_linked_to_role(pallet: IdOrVec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult{ + let role_permissions = >::get(pallet.to_id(), role_id); ensure!(role_permissions.contains(permission_id), Error::::PermissionNotLinkedToRole ); Ok(()) } @@ -397,9 +401,8 @@ impl RoleBasedAccessControl for Pallet{ /// - `pallet_id`: The unique pallet identifier. /// - `scope_id`: The scope in which the users will be retrieved. /// - `role_id`: The role in which the number of users will be counted. - fn get_role_users_len(pallet_name: Vec, scope_id:&ScopeId, role_id: &RoleId) ->usize{ - let pallet_id = Self::to_id(pallet_name); - >::get((pallet_id, scope_id, role_id)).len() + fn get_role_users_len(pallet: IdOrVec, scope_id:&ScopeId, role_id: &RoleId) ->usize{ + >::get((pallet.to_id(), scope_id, role_id)).len() } fn to_id(v: Vec)->[u8;32]{ diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index a2f31941..5055edfe 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,16 +1,18 @@ -use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId}, Config, PermissionsByRole, Permissions}; +use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId, IdOrVec}, Config, PermissionsByRole, Permissions}; use codec::Encode; use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::DispatchResult}; use sp_io::hashing::blake2_256; type AccountId = ::AccountId; -fn pallet_name()->Vec{ - "pallet_test".as_bytes().to_vec() +fn pallet_name()->IdOrVec{ + IdOrVec::Vec( + "pallet_test".as_bytes().to_vec() + ) } fn pallet_id()->[u8;32]{ - pallet_name().using_encoded(blake2_256) + pallet_name().to_id() } fn create_scope(n: u8)->ScopeId{ diff --git a/pallets/rbac/src/types.rs b/pallets/rbac/src/types.rs index 3e310b23..1fc7891a 100644 --- a/pallets/rbac/src/types.rs +++ b/pallets/rbac/src/types.rs @@ -1,6 +1,7 @@ //use super::*; use frame_support::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; +use frame_support::sp_io::hashing::blake2_256; pub type PalletId = [u8;32]; @@ -8,10 +9,26 @@ pub type RoleId = [u8;32]; pub type ScopeId = [u8;32]; pub type PermissionId = [u8;32]; -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebugNoBound, TypeInfo,)] -pub enum IdOrString >{ +#[derive(Encode, Decode, Clone, Eq, PartialEq,)] +pub enum IdOrVec{ Id([u8;32]), - String(BoundedVec) + Vec(Vec) +} + +impl IdOrVec{ + pub fn to_id_enum(&self)->Self{ + match self{ + Self::Id(_) => self.clone(), + Self::Vec(_) => Self::Id(Self::to_id(self)) + } + } + + pub fn to_id(&self)->[u8;32]{ + match self{ + Self::Id(id) => *id, + Self::Vec(v) => v.clone().using_encoded(blake2_256) + } + } } pub trait RoleBasedAccessControl{ @@ -20,34 +37,34 @@ pub trait RoleBasedAccessControl{ type RoleMaxLen: Get; type PermissionMaxLen: Get; // scopes - fn create_scope(pallet_name: Vec, scope_id: ScopeId) -> DispatchResult; + fn create_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; // scope removal - fn remove_scope(pallet_name: Vec, scope_id: ScopeId) -> DispatchResult; + fn remove_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult; // removes all from one pallet/application - fn remove_pallet_storage(pallet_name: Vec) -> DispatchResult; + fn remove_pallet_storage(pallet: IdOrVec) -> DispatchResult; // roles creation and setting - fn create_and_set_roles(pallet_name: Vec, roles: Vec>) -> + fn create_and_set_roles(pallet: IdOrVec, roles: Vec>) -> Result, DispatchError>; fn create_role(role: Vec)-> Result; - fn set_role_to_pallet(pallet_name: Vec, role_id: RoleId )-> DispatchResult; - fn set_multiple_pallet_roles(pallet_name: Vec, roles: Vec)->DispatchResult; - fn assign_role_to_user(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId )-> DispatchResult; + fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec)->DispatchResult; + fn assign_role_to_user(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // role removal - fn remove_role_from_user(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; + fn remove_role_from_user(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_id: RoleId) -> DispatchResult; // permissions - fn create_and_set_permissions(pallet_name: Vec, role: RoleId, permissions: Vec>)-> + fn create_and_set_permissions(pallet: IdOrVec, role: RoleId, permissions: Vec>)-> Result, DispatchError>; - fn create_permission(pallet_name: Vec, permissions: Vec) -> Result; - fn set_permission_to_role( pallet_name: Vec, role: RoleId, permission: PermissionId ) -> DispatchResult; - fn set_multiple_permissions_to_role( pallet_name: Vec, role: RoleId, permission: Vec )-> DispatchResult; + fn create_permission(pallet: IdOrVec, permissions: Vec) -> Result; + fn set_permission_to_role( pallet: IdOrVec, role: RoleId, permission: PermissionId ) -> DispatchResult; + fn set_multiple_permissions_to_role( pallet: IdOrVec, role: RoleId, permission: Vec )-> DispatchResult; // helpers - fn is_authorized(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; - fn has_role(user: AccountId, pallet_name: Vec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; - fn scope_exists(pallpallet_nameet_id: Vec, scope_id:&ScopeId) -> DispatchResult; - fn permission_exists(pallet_name: Vec, permission_id: &PermissionId)->DispatchResult; - fn is_role_linked_to_pallet(pallet_name: Vec, role_id: &RoleId)-> DispatchResult; - fn is_permission_linked_to_role(pallet_name: Vec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; - fn get_role_users_len(pallet_name: Vec, scope_id:&ScopeId, role_id: &RoleId) -> usize; + fn is_authorized(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, permission_id: &PermissionId ) -> DispatchResult; + fn has_role(user: AccountId, pallet: IdOrVec, scope_id: &ScopeId, role_ids: Vec)->DispatchResult; + fn scope_exists(pallet: IdOrVec, scope_id:&ScopeId) -> DispatchResult; + fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId)->DispatchResult; + fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId)-> DispatchResult; + fn is_permission_linked_to_role(pallet: IdOrVec, role_id: &RoleId, permission_id: &PermissionId)-> DispatchResult; + fn get_role_users_len(pallet: IdOrVec, scope_id:&ScopeId, role_id: &RoleId) -> usize; fn to_id(v: Vec)->[u8;32]; } \ No newline at end of file From 2a39951537812a542e6b97426d768f45da3ac1f0 Mon Sep 17 00:00:00 2001 From: didiermis Date: Fri, 26 Aug 2022 16:22:07 -0500 Subject: [PATCH 099/103] Update signature extrinsics to version 5: I removed unnecessary parameters, I changed the double hashing storagemaps. I removed the tight coupling of timestamp & uniques pallets. I deleted all unused imports. Update Unit tests. --- pallets/gated-marketplace/src/functions.rs | 136 ++++++--------------- pallets/gated-marketplace/src/lib.rs | 64 +++------- pallets/gated-marketplace/src/tests.rs | 20 +-- pallets/gated-marketplace/src/types.rs | 11 +- 4 files changed, 70 insertions(+), 161 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index 4b817d8c..9c2badef 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -1,12 +1,11 @@ use super::*; use frame_support::{pallet_prelude::*}; -//use frame_system::pallet_prelude::*; use frame_support::sp_io::hashing::blake2_256; use sp_runtime::sp_std::vec::Vec; use crate::types::*; use frame_support::traits::Time; -//use frame_support::traits::{Currency}; -//use frame_support::traits::ExistenceRequirement::KeepAlive; +use frame_support::traits::{Currency}; +use frame_support::traits::ExistenceRequirement::KeepAlive; impl Pallet { @@ -47,7 +46,7 @@ impl Pallet { Ok(()) } - pub fn do_enroll(authority: T::AccountId,marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ + pub fn do_enroll(authority: T::AccountId, marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ // ensure the origin is owner or admin Self::can_enroll(authority, marketplace_id)?; let next_status = match approved{ @@ -152,7 +151,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { + pub fn do_enlist_sell_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { //This function is only called by the owner of the marketplace //ensure the marketplace exists ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); @@ -165,13 +164,12 @@ impl Pallet { } //ensure the price is valid - // Self::is_the_price_valid(price)?; + Self::is_the_price_valid(price)?; //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; //create an offer_id - //TODO: create an offer id generator, used in cases where the offer_id generated is not unique let offer_id = (marketplace_id, authority.clone(), collection_id, timestamp, timestamp2).using_encoded(blake2_256); //create offer structure @@ -215,7 +213,7 @@ impl Pallet { Ok(()) } - pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { + pub fn do_enlist_buy_offer(authority: T::AccountId, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { //ensure the item is for sale, if not, return error Self::can_this_item_receive_buy_orders(collection_id, item_id, marketplace_id)?; @@ -223,7 +221,7 @@ impl Pallet { ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); //ensure the collection exists - //For this case user doesn't have to be the owner of the collection + //For this case user doesn't need to be the owner of the collection //but the owner of the item cannot create a buy offer for their own collection if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a != authority, Error::::CannotCreateOffer); @@ -232,11 +230,11 @@ impl Pallet { } //ensure user has enough balance to create the offer - // let total_user_balance = T::LocalCurrency::total_balance(&authority); - // ensure!(total_user_balance >= price, Error::::NotEnoughBalance); + let total_user_balance = T::Currency::total_balance(&authority); + ensure!(total_user_balance >= price, Error::::NotEnoughBalance); //ensure the price is valid - // Self::is_the_price_valid(price)?; + Self::is_the_price_valid(price)?; //Add timestamp to the offer let(timestamp, timestamp2) = Self::get_timestamp_in_milliseconds().ok_or(Error::::TimestampError)?; @@ -283,20 +281,11 @@ impl Pallet { Ok(()) } - pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { + pub fn do_take_sell_offer(buyer: T::AccountId, offer_id: [u8;32]) -> DispatchResult { //This extrisicn is called by the user who wants to buy the item - //ensure offer exists - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - - //ensure the marketplace exists - ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - //get offer data let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - //ensure the marketplace_id where the extrinsic is being called is the same as the one in the offer - ensure!(offer_data.marketplace_id == marketplace_id, Error::::MarketplaceNotFound); - //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(offer_data.collection_id, offer_data.item_id).ok_or(Error::::OwnerNotFound)?; @@ -311,11 +300,11 @@ impl Pallet { //TODO: Use free_balance instead of total_balance //Get the buyer's balance - // let total_amount_buyer = T::LocalCurrency::total_balance(&buyer); + let total_amount_buyer = T::Currency::total_balance(&buyer); //ensure the buyer has enough balance to buy the item - // ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); + ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance - // T::LocalCurrency::transfer(&buyer, &owner_item, offer_data.price, KeepAlive)?; + T::Currency::transfer(&buyer, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the buyer pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, buyer.clone(), |_, _|{ @@ -323,7 +312,7 @@ impl Pallet { })?; //update offer status from all marketplaces - Self::update_offers_status(buyer.clone(), offer_data.collection_id, offer_data.item_id, marketplace_id)?; + Self::update_offers_status(buyer.clone(), offer_data.collection_id, offer_data.item_id, offer_data.marketplace_id)?; //remove all the offers associated with the item Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id)?; @@ -332,20 +321,12 @@ impl Pallet { Ok(()) } - pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { + pub fn do_take_buy_offer(authority: T::AccountId, offer_id: [u8;32]) -> DispatchResult { //This extrinsic is called by the owner of the item who accepts the buy offer created by a marketparticipant - //Ensure offer exists. This offer_id corresponds to the buy order - ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - - //ensure the marketplace exists - ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); //get offer data let offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - //ensure the marketplace_id where the extrinsic is being called is the same as the one in the offer - ensure!(offer_data.marketplace_id == marketplace_id, Error::::MarketplaceNotFound); - //ensure the collection & owner exists let owner_item = pallet_uniques::Pallet::::owner(offer_data.collection_id, offer_data.item_id).ok_or(Error::::OwnerNotFound)?; @@ -363,19 +344,18 @@ impl Pallet { //TODO: Use free_balance instead of total_balance //Get the buyer's balance - // let total_amount_buyer = T::LocalCurrency::total_balance(&offer_data.creator); + let total_amount_buyer = T::Currency::total_balance(&offer_data.creator); //ensure the buy_offer_creator has enough balance to buy the item - // ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); + ensure!(total_amount_buyer > offer_data.price, Error::::NotEnoughBalance); //Transfer the balance to the owner of the item - // T::LocalCurrency::transfer(&offer_data.creator, &owner_item, offer_data.price, KeepAlive)?; - + T::Currency::transfer(&offer_data.creator, &owner_item, offer_data.price, KeepAlive)?; //Use uniques transfer function to transfer the item to the market_participant pallet_uniques::Pallet::::do_transfer(offer_data.collection_id, offer_data.item_id, offer_data.creator.clone(), |_, _|{ Ok(()) })?; //update offer status from all marketplaces - Self::update_offers_status(offer_data.creator.clone(), offer_data.collection_id, offer_data.item_id, marketplace_id)?; + Self::update_offers_status(offer_data.creator.clone(), offer_data.collection_id, offer_data.item_id, offer_data.marketplace_id)?; //remove all the offers associated with the item Self::delete_all_offers_for_this_item(offer_data.collection_id, offer_data.item_id )?; @@ -384,53 +364,6 @@ impl Pallet { Ok(()) } - //TODO: Under development - // pub fn do_duplicate_offer(authority: T::AccountId, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult{ - // //ensure new marketplace_id exits - // ensure!(>::contains_key(marketplace_id), Error::::MarketplaceNotFound); - - // //ensure that the offer_id exists - // ensure!(>::contains_key(offer_id), Error::::OfferNotFound); - - // //ensure the offer_id exists in OffersByItem - // Self::does_exist_offer_id_for_this_item(collection_id, item_id, offer_id)?; - - // //get the offer data - // let mut copy_offer_data = >::get(offer_id).ok_or(Error::::OfferNotFound)?; - - // //modify the offer data - // //by know we only allow to modify its price by the user - // //we modify its marketplace_id because the offer is duplicated to another marketplace - // copy_offer_data.price = modified_price; - // copy_offer_data.marketplace_id = marketplace_id; - - // //generate a new offer_id - // let new_offer_id = (marketplace_id, authority.clone(), collection_id, copy_offer_data.creation_date, copy_offer_data.expiration_date).using_encoded(blake2_256); - - // //insert in OffersInfo - // // validate new offer_id does not exists - // ensure!(!>::contains_key(new_offer_id), Error::::OfferAlreadyExists); - // >::insert(new_offer_id, copy_offer_data); - - // //insert in OffersByMarketplace - // >::try_mutate(marketplace_id, |offer| { - // offer.try_push(new_offer_id) - // }).map_err(|_| Error::::OfferStorageError)?; - - // //insert in OffersByAccount - // >::try_mutate(authority.clone(), |offer| { - // offer.try_push(new_offer_id) - // }).map_err(|_| Error::::OfferStorageError)?; - - // //add the new offer_id to OffersByItem - // >::try_mutate(collection_id, item_id, |offers| { - // offers.try_push(new_offer_id) - // }).map_err(|_| Error::::OfferStorageError)?; - - // Self::deposit_event(Event::OfferDuplicated(new_offer_id, marketplace_id)); - - // Ok(()) - // } pub fn do_remove_offer(authority: T::AccountId, offer_id: [u8;32]) -> DispatchResult { @@ -483,7 +416,7 @@ impl Pallet { /*---- Helper functions ----*/ pub fn set_up_application( - fields : BoundedVec<(BoundedVec >,BoundedVec> ), T::MaxFiles>, + fields : Fields, custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> )-> (Option, BoundedVec ){ let mut f: Vec= fields.iter().map(|tuple|{ @@ -647,7 +580,8 @@ impl Pallet { //First we need to get the list of all the authorities for the marketplace. let _users = >::iter_prefix(marketplace_id) - .map(|(_authority, users)| users).flatten().collect::>(); + .flat_map(|(_authority, users)| users).collect::>(); + //1. remove from MarketplacesByAuthority _users.iter().for_each(|user|{ @@ -725,9 +659,9 @@ impl Pallet { fn _is_offer_status(offer_id: [u8;32], offer_status: OfferStatus,) -> bool{ //we already know that the offer exists, so we don't need to check it here. if let Some(offer) = >::get(offer_id) { - return offer.status == offer_status; + offer.status == offer_status } else { - return false; + false } } @@ -740,14 +674,6 @@ impl Pallet { } - fn _get_offer_creator(offer_id: [u8;32],) -> Result { - //we already know that the offer exists, so we don't need to check it here. - if let Some(offer) = >::get(offer_id) { - return Ok(offer.creator); - } else { - return Err(Error::::OfferNotFound)?; - } - } //sell orders here... @@ -766,6 +692,14 @@ impl Pallet { Ok(()) } + fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { + let minimun_amount: BalanceOf = 1000u32.into(); + if price > minimun_amount { + Ok(()) + } else { + Err(Error::::PriceMustBeGreaterThanZero)? + } + } fn can_this_item_receive_sell_orders(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { let offers = >::get(collection_id, item_id); @@ -798,11 +732,11 @@ impl Pallet { let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; //ensure the offer_type is SellOrder, because this vector also contains buy offers. if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - return Ok(()); + return Ok(()) } } - return Err(Error::::ItemNotForSale)?; + Err(Error::::ItemNotForSale)? } diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 99aedc96..b04383a1 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -22,17 +22,16 @@ pub mod pallet { use frame_support::transactional; use frame_system::pallet_prelude::*; use sp_runtime::traits::Scale; - use frame_support::traits::Time; - //use frame_support::traits::Currency; - - + use frame_support::traits::{Currency, Time}; + use crate::types::*; + pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; + #[pallet::config] - pub trait Config: frame_system::Config + pallet_fruniques::Config + pallet_uniques::Config{ - type Event: From> + IsType<::Event>; + pub trait Config: frame_system::Config + pallet_fruniques::Config{ - //type LocalCurrency: Currency; + type Event: From> + IsType<::Event>; type Moment: Parameter + Default @@ -41,9 +40,8 @@ pub mod pallet { + MaxEncodedLen + scale_info::StaticTypeInfo + Into; - - type Timestamp: Time; + type Timestamp: Time; type RemoveOrigin: EnsureOrigin; #[pallet::constant] @@ -92,7 +90,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, // K1: Authority - Blake2_128Concat, + Identity, [u8;32], // K2: marketplace_id BoundedVec, // scales with MarketplaceAuthority cardinality ValueQuery @@ -126,7 +124,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, // K1: account_id - Blake2_128Concat, + Identity, [u8;32], // k2: marketplace_id [u8;32], //application_id OptionQuery @@ -151,7 +149,7 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, //custodians - Blake2_128Concat, + Identity, [u8;32], //marketplace_id BoundedVec, //applicants ValueQuery @@ -173,7 +171,7 @@ pub mod pallet { #[pallet::getter(fn offers_by_account)] pub(super) type OffersByAccount = StorageMap< _, - Identity, + Blake2_128Concat, T::AccountId, // account_id BoundedVec<[u8;32], T::MaxOffersPerMarket>, // offer_id's ValueQuery, @@ -355,7 +353,7 @@ pub mod pallet { origin: OriginFor, marketplace_id: [u8;32], // Getting encoding errors from polkadotjs if an object vector have optional fields - fields : BoundedVec<(BoundedVec >,BoundedVec> ), T::MaxFiles>, + fields : Fields, custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -393,7 +391,7 @@ pub mod pallet { origin: OriginFor, marketplace_id: [u8;32], // Getting encoding errors from polkadotjs if an object vector have optional fields - fields : BoundedVec<(BoundedVec >,BoundedVec> ), T::MaxFiles>, + fields : Fields, custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -546,7 +544,7 @@ pub mod pallet { /// - If the selected collection doesn't exist, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { + pub fn enlist_sell_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_enlist_sell_offer(who, marketplace_id, collection_id, item_id, price) @@ -568,35 +566,11 @@ pub mod pallet { /// - If you don't have the enough balance to accept the sell order, it will throw an error. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32],) -> DispatchResult { + pub fn take_sell_offer(origin: OriginFor, offer_id: [u8;32]) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_sell_offer(who, offer_id, marketplace_id) + Self::do_take_sell_offer(who, offer_id) } - - //TODO: Under development - /// Allows a user to duplicate a sell order. - /// - /// This extrinsic allows a user to duplicate a sell order in any marketplace. - /// - /// ### Parameters: - /// - `origin`: The user who performs the action. - /// - `marketplace_id`: The id of the marketplace where we want to duplicate the sell order. - /// - `collection_id`: The id of the collection. - /// - `item_id`: The id of the item inside the collection. - /// - /// ### Considerations: - /// - You can only duplicate a sell order if you are the owner of the item. - /// - The expiration date of the sell order is the same as the original sell order. - /// - You can update the price of the sell order. - // #[transactional] - // #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - // pub fn duplicate_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, modified_price: BalanceOf) -> DispatchResult { - // let who = ensure_signed(origin.clone())?; - - // Self::do_duplicate_offer(who, offer_id, marketplace_id, collection_id, item_id, modified_price) - // } - /// Delete an offer. /// @@ -639,7 +613,7 @@ pub mod pallet { /// - You need to have the enough balance to create the buy order. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: u128,) -> DispatchResult { + pub fn enlist_buy_offer(origin: OriginFor, marketplace_id: [u8;32], collection_id: T::CollectionId, item_id: T::ItemId, price: BalanceOf,) -> DispatchResult { let who = ensure_signed(origin)?; Self::do_enlist_buy_offer(who, marketplace_id, collection_id, item_id, price) @@ -664,10 +638,10 @@ pub mod pallet { /// - Once the buy order is accepted, the ownership of the item is transferred to the buyer. #[transactional] #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32], marketplace_id: [u8;32]) -> DispatchResult { + pub fn take_buy_offer(origin: OriginFor, offer_id: [u8;32]) -> DispatchResult { let who = ensure_signed(origin.clone())?; - Self::do_take_buy_offer(who, offer_id, marketplace_id) + Self::do_take_buy_offer(who, offer_id) } diff --git a/pallets/gated-marketplace/src/tests.rs b/pallets/gated-marketplace/src/tests.rs index d777263e..0c02e8fa 100644 --- a/pallets/gated-marketplace/src/tests.rs +++ b/pallets/gated-marketplace/src/tests.rs @@ -46,7 +46,7 @@ fn _create_file(name: &str, cid: &str, create_custodian_file: bool) -> Applicati // due to encoding problems with polkadot-js, the custodians_cid generation will be done in another function fn create_application_fields( n_files: u32) -> - BoundedVec<(BoundedVec >,BoundedVec> ), MaxFiles> { + BoundedVec<(BoundedVec >,BoundedVec> ), MaxFiles> { let mut files = Vec::<(BoundedVec >,BoundedVec> )>::default(); for i in 0..n_files{ let file_name = format!("file{}",i.to_string()); @@ -1080,7 +1080,7 @@ fn take_sell_offer_works(){ assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id)); + assert_ok!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); @@ -1103,7 +1103,7 @@ fn take_sell_offer_owner_cannnot_be_the_buyer_shouldnt_work() { assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id, m_id), Error::::CannotTakeOffer); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(1), offer_id), Error::::CannotTakeOffer); }); } @@ -1124,7 +1124,7 @@ fn take_sell_offer_id_does_not_exist_shouldnt_work(){ let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); let offer_id2 = offer_id.using_encoded(blake2_256); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id2, m_id), Error::::OfferNotFound); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id2), Error::::OfferNotFound); }); } @@ -1145,7 +1145,7 @@ fn take_sell_offer_buyer_does_not_have_enough_balance_shouldnt_work(){ assert_ok!(GatedMarketplace::enlist_sell_offer(Origin::signed(1), m_id, 0, 0, 1200)); let offer_id = GatedMarketplace::offers_by_item(0, 0).iter().next().unwrap().clone(); - assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id, m_id), Error::::NotEnoughBalance); + assert_noop!(GatedMarketplace::take_sell_offer(Origin::signed(2), offer_id), Error::::NotEnoughBalance); }); } @@ -1170,7 +1170,7 @@ fn take_buy_offer_works(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id)); + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); }); @@ -1198,7 +1198,7 @@ fn take_buy_offer_only_owner_can_accept_buy_offers_shouldnt_work(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(2), offer_id2, m_id), Error::::NotOwner); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(2), offer_id2), Error::::NotOwner); }); } @@ -1224,7 +1224,7 @@ fn take_buy_offer_id_does_not_exist_shouldnt_work(){ assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); let offer_id3 = offer_id2.using_encoded(blake2_256); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id3, m_id), Error::::OfferNotFound); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id3), Error::::OfferNotFound); }); } @@ -1251,7 +1251,7 @@ fn take_buy_offer_user_does_not_have_enough_balance_shouldnt_work(){ assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); Balances::make_free_balance_be(&2, 0); - assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id), Error::::NotEnoughBalance); + assert_noop!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2), Error::::NotEnoughBalance); }); } @@ -1369,7 +1369,7 @@ fn remove_offer_status_is_closed_shouldnt_work(){ let offer_id2 = GatedMarketplace::offers_by_account(2).iter().next().unwrap().clone(); assert_eq!(GatedMarketplace::offers_info(offer_id2).unwrap().offer_type, OfferType::BuyOrder); - assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2, m_id)); + assert_ok!(GatedMarketplace::take_buy_offer(Origin::signed(1), offer_id2)); assert_eq!(GatedMarketplace::offers_by_item(0, 0).len(), 0); assert_eq!(GatedMarketplace::offers_info(offer_id).unwrap().status, OfferStatus::Closed); diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 944d1b27..1d67f22b 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -1,10 +1,11 @@ use super::*; use frame_support::pallet_prelude::*; -//use frame_system::pallet_prelude::*; -// use frame_support::traits::Currency; -//pub type BalanceOf = <::LocalCurrency as Currency<::AccountId>>::Balance; +pub type Fields = BoundedVec<(BoundedVec >,BoundedVec> ), ::MaxFiles>; + +//Todo: fix AccountId import +//pub type CustodianFields = Option<(RawOrigin, BoundedVec>, ::MaxFiles>)>; #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -35,7 +36,7 @@ impl Default for MarketplaceAuthority{ } } -#[derive(CloneNoBound,Encode, Decode, PartialEq, Eq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] +#[derive(CloneNoBound, Encode, Decode, Eq, PartialEq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] pub struct Application< T: Config >{ @@ -99,7 +100,7 @@ pub struct OfferData{ pub collection_id: T::CollectionId, pub item_id: T::ItemId, pub creator: T::AccountId, - pub price: u128, + pub price: BalanceOf, pub status: OfferStatus, pub creation_date: u64, pub expiration_date: u64, From 06b9ea0c18c9fd7ae98ab408afe5e0a14d44f540 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 26 Aug 2022 16:59:04 -0500 Subject: [PATCH 100/103] Storagemap optimizations and adds initial setup in RBAC readme --- pallets/gated-marketplace/README.md | 19 +++++++++++++-- pallets/rbac/src/lib.rs | 36 +++++++++-------------------- pallets/rbac/src/tests.rs | 2 -- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/pallets/gated-marketplace/README.md b/pallets/gated-marketplace/README.md index f4a4c9a7..54f3d7e9 100644 --- a/pallets/gated-marketplace/README.md +++ b/pallets/gated-marketplace/README.md @@ -9,6 +9,7 @@ Create marketplaces that require previous authorization before placing sell and - [Getters](#getters) - [Usage](#usage) - [Polkadot-js CLI](#polkadot-js-cli) + - [Submit initial role setup (needs sudo](#submit-initial-role-setup-needs-sudo) - [Create a marketplace](#create-a-marketplace) - [Get a marketplace](#get-a-marketplace) - [Get what roles does an account have on a marketplace](#get-what-roles-does-an-account-have-on-a-marketplace) @@ -34,6 +35,7 @@ Create marketplaces that require previous authorization before placing sell and - [Take sell offer - direct purchase](#take-sell-offer---direct-purchase) - [Take buy offer](#take-buy-offer) - [Polkadot-js api (javascript library)](#polkadot-js-api-javascript-library) + - [Submit initial role setup (needs sudo)](#submit-initial-role-setup-needs-sudo-1) - [Create a marketplace](#create-a-marketplace-1) - [Get a marketplace](#get-a-marketplace-1) - [Get what roles does an account have on a marketplace](#get-what-roles-does-an-account-have-on-a-marketplace-1) @@ -87,8 +89,10 @@ This module allows to: ## Interface ### Dispachable functions + +- `initial_setup` enables all the permission related functionality using the `RBAC` pallet, it can only be called by the sudo account or a majority of the Council (60%). It is essential to call this extrinsic before using other extrinsics. - `create_marketplace` creates an on-chain marketplace record, it takes a `label` and an account that will fulfill the role of `administrator`, the extrinsic origin will be set up automatically as the marketplace owner. -- `apply` starts the process to enter the specidied `marketplace`. +- `apply` starts the process to enter the specified `marketplace`. - `reapply` allows the applicant to apply again for the selected marketplace. - `enroll` is only callable by the marketplace owner or administrator, as it finishes the application process. It takes a `marketplace` identification, and `account` or `application` identification to enroll or reject, and an `approved` boolean flag which approves the application if set to `true`. Owner/admin can add a feedback regarding the user's application. - `add_authority` is only callable by the marketplace owner or administrator. As it name implies, adds a new user that will have special permission within the marketplace. It takes the `account` which will have the permissions, the type of `authority` it will have, and the `marketplace` identification in which the permissions will be enforced. @@ -145,6 +149,11 @@ The following examples will be using these prefunded accounts and testing data: ### Polkadot-js CLI +#### Submit initial role setup (needs sudo +```bash +polkadot-js-api tx.gatedMarketplace.initialSetup --sudo --seed "//Alice" +``` + #### Create a marketplace ```bash # Administrator account and marketplace label @@ -401,7 +410,13 @@ polkadot-js-api tx.gatedMarketplace.takeBuyOffer "0x66fcfadc174a596d8f8dc1b06703 ``` ### Polkadot-js api (javascript library) -While most of the data flow is almost identical to its CLI counter part, the javascript library is much more versatile regarding queries. The API setup will be ommited. +While most of the data flow is almost identical to its CLI counter part, the javascript library is much more versatile regarding queries. + + +#### Submit initial role setup (needs sudo) +```js +const initial_set_up = await api.tx.sudo.sudo(api.tx.gatedMarketplace.initialSetup()).signAndSend(alice); +``` #### Create a marketplace ```js diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 96c91b74..c3500e59 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -55,7 +55,7 @@ pub mod pallet { #[pallet::getter(fn scopes)] pub(super) type Scopes = StorageMap< _, - Blake2_128Concat, + Identity, PalletId, // pallet_id BoundedVec, // scopes_id ValueQuery, @@ -75,7 +75,7 @@ pub mod pallet { #[pallet::getter(fn pallet_roles)] pub(super) type PalletRoles = StorageMap< _, - Blake2_128Concat, + Identity, PalletId, // pallet_id BoundedVec, // role_id ValueQuery, @@ -85,9 +85,9 @@ pub mod pallet { #[pallet::getter(fn permissions)] pub(super) type Permissions = StorageDoubleMap< _, - Blake2_128Concat, + Identity, PalletId, // pallet_id - Blake2_128Concat, + Identity, PermissionId, // permission_id BoundedVec, // permission str ValueQuery, @@ -97,9 +97,9 @@ pub mod pallet { #[pallet::getter(fn permissions_by_role)] pub(super) type PermissionsByRole = StorageDoubleMap< _, - Blake2_128Concat, + Identity, PalletId, // pallet_id - Blake2_128Concat, + Identity, RoleId, // role_id BoundedVec, // permission_ids ValueQuery, @@ -112,8 +112,8 @@ pub mod pallet { ( NMapKey,// user // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors - NMapKey, // pallet_id - NMapKey, // scope_id + NMapKey, // pallet_id + NMapKey, // scope_id ), BoundedVec, // roles (ids) ValueQuery, @@ -126,9 +126,9 @@ pub mod pallet { ( // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors // on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes. - NMapKey, // pallet_id - NMapKey, // scope_id - NMapKey, // role_id + NMapKey, // pallet_id + NMapKey, // scope_id + NMapKey, // role_id ), BoundedVec, // users ValueQuery, @@ -199,19 +199,5 @@ pub mod pallet { #[pallet::call] impl Pallet { - - #[transactional] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1))] - pub fn get_pallet_id( - origin: OriginFor, - ) -> DispatchResult { - let who = ensure_signed(origin.clone())?; - let a = Self::index(); - log::info!("henlo {:?}", a); - log::warn!("Name: {:?} Module Name: {:?}",Self::name(), Self::module_name()); - Self::deposit_event(Event::SomethingStored(a.try_into().unwrap(), who)); - - Ok(()) - } } } \ No newline at end of file diff --git a/pallets/rbac/src/tests.rs b/pallets/rbac/src/tests.rs index 5055edfe..f69b7c68 100644 --- a/pallets/rbac/src/tests.rs +++ b/pallets/rbac/src/tests.rs @@ -1,7 +1,5 @@ use crate::{mock::*, Error, types::{RoleBasedAccessControl, RoleId, ScopeId, PermissionId, IdOrVec}, Config, PermissionsByRole, Permissions}; -use codec::Encode; use frame_support::{assert_noop, assert_ok, assert_err, BoundedVec, pallet_prelude::DispatchResult}; -use sp_io::hashing::blake2_256; type AccountId = ::AccountId; From 0e24fbf17439512b44ad75ec099fc0bd610d1834 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Fri, 26 Aug 2022 17:42:24 -0500 Subject: [PATCH 101/103] removes unused dependencies --- pallets/rbac/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index c3500e59..353a86ce 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -21,9 +21,6 @@ pub mod types; #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::{*, ValueQuery}; - use frame_support::traits::{PalletInfoAccess}; - use frame_support::{transactional}; - use frame_system::pallet_prelude::*; use crate::types::*; #[pallet::config] From 6c1e122da7e2df16d56482f9fabaf261dbb38968 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Mon, 29 Aug 2022 18:47:10 -0500 Subject: [PATCH 102/103] adds marketplace optimizations --- pallets/gated-marketplace/src/functions.rs | 53 ++++++++++------------ pallets/gated-marketplace/src/types.rs | 16 +++---- pallets/rbac/src/functions.rs | 1 + pallets/rbac/src/lib.rs | 5 +- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index b41b058d..f95bcb6c 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -2,7 +2,8 @@ use super::*; use frame_support::{pallet_prelude::*}; use frame_support::sp_io::hashing::blake2_256; -use sp_runtime::sp_std::vec::Vec; +use sp_runtime::sp_std::vec::Vec; // vec primitive +use scale_info::prelude::vec; // vec![] macro use crate::types::*; use pallet_rbac::types::*; @@ -14,9 +15,7 @@ impl Pallet { pub fn do_initial_setup()->DispatchResult{ let pallet_id = Self::pallet_id(); - let mut super_roles = Vec::>::new(); - super_roles.push(MarketplaceRole::Owner.to_vec()); - super_roles.push(MarketplaceRole::Admin.to_vec()); + let super_roles = vec![MarketplaceRole::Owner.to_vec(), MarketplaceRole::Admin.to_vec()]; let super_role_ids = T::Rbac::create_and_set_roles(pallet_id.clone(), super_roles)?; for super_role in super_role_ids{ T::Rbac::create_and_set_permissions(pallet_id.clone(), super_role, Permission::admin_permissions())?; @@ -27,7 +26,7 @@ impl Pallet { // appraiser role and permissions let _appraiser_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::Appraiser.to_vec()].to_vec())?; // redemption specialist role and permissions - let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id.clone(), [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; + let _redemption_role_id = T::Rbac::create_and_set_roles(pallet_id, [MarketplaceRole::RedemptionSpecialist.to_vec()].to_vec())?; Ok(()) } @@ -37,7 +36,7 @@ impl Pallet { // ensure the generated id is unique ensure!(!>::contains_key(marketplace_id), Error::::MarketplaceAlreadyExists); //Insert on marketplaces and marketplaces by auth - T::Rbac::create_scope(Self::pallet_id(),marketplace_id.clone())?; + T::Rbac::create_scope(Self::pallet_id(),marketplace_id)?; Self::insert_in_auth_market_lists(owner.clone(), MarketplaceRole::Owner, marketplace_id)?; Self::insert_in_auth_market_lists(admin.clone(), MarketplaceRole::Admin, marketplace_id)?; >::insert(marketplace_id, marketplace); @@ -72,7 +71,7 @@ impl Pallet { pub fn do_enroll(authority: T::AccountId, marketplace_id: [u8;32], account_or_application: AccountOrApplication, approved: bool, feedback: BoundedVec,)->DispatchResult{ // ensure the origin is owner or admin //Self::can_enroll(authority, marketplace_id)?; - Self::is_authorized(authority.clone(), &marketplace_id,Permission::Enroll)?; + Self::is_authorized(authority, &marketplace_id,Permission::Enroll)?; let next_status = match approved{ true => ApplicationStatus::Approved, false => ApplicationStatus::Rejected, @@ -99,7 +98,7 @@ impl Pallet { //ensure the origin is owner or admin //TODO: implement copy trait for MarketplaceAuthority & T::AccountId //Self::can_enroll(authority, marketplace_id)?; - Self::is_authorized(authority.clone(), &marketplace_id,Permission::AddAuth)?; + Self::is_authorized(authority, &marketplace_id,Permission::AddAuth)?; //ensure the account is not already an authority // handled by T::Rbac::assign_role_to_user //ensure!(!Self::does_exist_authority(account.clone(), marketplace_id, authority_type), Error::::AlreadyApplied); @@ -130,7 +129,7 @@ impl Pallet { match authority_type{ MarketplaceRole::Owner => { ensure!(Self::owner_exist(marketplace_id), Error::::OwnerNotFound); - Err(Error::::CantRemoveOwner)?; + return Err(Error::::CantRemoveOwner.into()); }, MarketplaceRole::Admin => { // Admins can not delete themselves @@ -187,7 +186,7 @@ impl Pallet { if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a == authority, Error::::NotOwner); } else { - Err(Error::::CollectionNotFound)?; + return Err(Error::::CollectionNotFound.into()); } //ensure the price is valid @@ -254,7 +253,7 @@ impl Pallet { if let Some(a) = pallet_uniques::Pallet::::owner(collection_id, item_id) { ensure!(a != authority, Error::::CannotCreateOffer); } else { - Err(Error::::CollectionNotFound)?; + return Err(Error::::CollectionNotFound.into()); } //ensure user has enough balance to create the offer @@ -457,8 +456,8 @@ impl Pallet { }).collect(); let custodian = match custodian_fields{ Some(c_fields)=>{ - for i in 0..f.len(){ - f[i].custodian_cid = Some(c_fields.1[i].clone()); + for (i, field) in f.iter_mut().enumerate(){ + field.custodian_cid = Some(c_fields.1[i].clone()); } Some(c_fields.0) @@ -470,7 +469,7 @@ impl Pallet { fn insert_in_auth_market_lists(authority: T::AccountId, role: MarketplaceRole, marketplace_id: [u8;32])->DispatchResult{ - T::Rbac::assign_role_to_user(authority.clone(), Self::pallet_id(), + T::Rbac::assign_role_to_user(authority, Self::pallet_id(), &marketplace_id, role.id())?; Ok(()) @@ -546,7 +545,7 @@ impl Pallet { T::Rbac::is_authorized( authority, Self::pallet_id(), - &marketplace_id, + marketplace_id, &permission.id(), ) } @@ -651,8 +650,8 @@ impl Pallet { .map_err(|_| Error::::ApplicationNotFound)?; match application.status { - ApplicationStatus::Pending => Err(Error::::ApplicationStatusStillPending)?, - ApplicationStatus::Approved => Err(Error::::ApplicationHasAlreadyBeenApproved)?, + ApplicationStatus::Pending => return Err(Error::::ApplicationStatusStillPending.into()), + ApplicationStatus::Approved => return Err(Error::::ApplicationHasAlreadyBeenApproved.into()), ApplicationStatus::Rejected => { //If status is Rejected, we need to delete the previous application from all the storage sources. >::remove(application_id); @@ -709,34 +708,28 @@ impl Pallet { fn is_the_price_valid(price: BalanceOf,) -> DispatchResult { let minimun_amount: BalanceOf = 1000u32.into(); - if price > minimun_amount { - Ok(()) - } else { - Err(Error::::PriceMustBeGreaterThanZero)? - } + ensure!(price > minimun_amount, Error::::PriceMustBeGreaterThanZero); + Ok(()) } fn can_this_item_receive_sell_orders(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { let offers = >::get(collection_id, item_id); //if len is == 0, it means that there is no offers for this item, maybe it's the first entry - if offers.len() == 0 { - return Ok(()); - } else if offers.len() > 0 { + if offers.len() > 0 { for offer in offers { let offer_info = >::get(offer).ok_or(Error::::OfferNotFound)?; //ensure the offer_type is SellOrder, because this vector also contains buy offers. if offer_info.marketplace_id == marketplace_id && offer_info.offer_type == OfferType::SellOrder { - return Err(Error::::OfferAlreadyExists)?; + return Err(Error::::OfferAlreadyExists.into()); } } - } - + } + Ok(()) } fn can_this_item_receive_buy_orders(collection_id: T::CollectionId, item_id: T::ItemId, marketplace_id: [u8;32]) -> DispatchResult { - //TODO: optimize this function, when rust-analyzer pluggin is fixed, it will be possible to use the .iter().find() //First we check if the item has is for sale, if not, return error ensure!(>::contains_key(collection_id, item_id), Error::::ItemNotForSale); @@ -751,7 +744,7 @@ impl Pallet { } } - Err(Error::::ItemNotForSale)? + Err(Error::::ItemNotForSale.into()) } diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index e7d7b420..33d21849 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -41,7 +41,7 @@ impl Default for MarketplaceRole{ } impl MarketplaceRole{ - pub fn to_vec(&self) -> Vec{ + pub fn to_vec(self) -> Vec{ match self{ Self::Owner => "Owner".as_bytes().to_vec(), Self::Admin => "Admin".as_bytes().to_vec(), @@ -78,19 +78,19 @@ pub enum Permission{ } impl Permission{ - pub fn to_vec(&self) -> Vec{ + pub fn to_vec(self) -> Vec{ match self{ Self::Enroll => "Enroll".as_bytes().to_vec(), Self::AddAuth => "AddAuth".as_bytes().to_vec(), Self::RemoveAuth => "RemoveAuth".as_bytes().to_vec(), Self::UpdateLabel => "UpdateLabel".as_bytes().to_vec(), Self::RemoveMarketplace => "RemoveMarketplace".as_bytes().to_vec(), - &Self::EnlistSellOffer=>"EnlistSellOffer".as_bytes().to_vec(), - &Self::TakeSellOffer=>"TakeSellOffer".as_bytes().to_vec(), - &Self::DuplicateOffer=>"DuplicateOffer".as_bytes().to_vec(), - &Self::RemoveOffer=>"RemoveOffer".as_bytes().to_vec(), - &Self::EnlistBuyOffer=>"EnlistBuyOffer".as_bytes().to_vec(), - &Self::TakeBuyOffer=>"TakeBuyOffer".as_bytes().to_vec(), + Self::EnlistSellOffer=>"EnlistSellOffer".as_bytes().to_vec(), + Self::TakeSellOffer=>"TakeSellOffer".as_bytes().to_vec(), + Self::DuplicateOffer=>"DuplicateOffer".as_bytes().to_vec(), + Self::RemoveOffer=>"RemoveOffer".as_bytes().to_vec(), + Self::EnlistBuyOffer=>"EnlistBuyOffer".as_bytes().to_vec(), + Self::TakeBuyOffer=>"TakeBuyOffer".as_bytes().to_vec(), } } diff --git a/pallets/rbac/src/functions.rs b/pallets/rbac/src/functions.rs index 2ecef9ad..95bbcd55 100644 --- a/pallets/rbac/src/functions.rs +++ b/pallets/rbac/src/functions.rs @@ -104,6 +104,7 @@ impl RoleBasedAccessControl for Pallet{ } Self::set_multiple_pallet_roles(pallet.to_id_enum(), role_ids.clone())?; let bounded_ids = Self::bound(role_ids, Error::::ExceedMaxRolesPerPallet)?; + Self::deposit_event(Event::RolesStored(pallet.to_id())); Ok(bounded_ids) } diff --git a/pallets/rbac/src/lib.rs b/pallets/rbac/src/lib.rs index 353a86ce..db325af0 100644 --- a/pallets/rbac/src/lib.rs +++ b/pallets/rbac/src/lib.rs @@ -108,7 +108,6 @@ pub mod pallet { _, ( NMapKey,// user - // getting "the trait bound `usize: scale_info::TypeInfo` is not satisfied" errors NMapKey, // pallet_id NMapKey, // scope_id ), @@ -137,8 +136,8 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// Event documentation should end with an array that provides descriptive names for event - SomethingStored(u32, T::AccountId), + /// An initial roles config was stored [pallet_id] + RolesStored(PalletId), } // Errors inform users that something went wrong. From 92a114062be22134f939f5a2f9751b2f13808386 Mon Sep 17 00:00:00 2001 From: amatsonkali Date: Tue, 30 Aug 2022 11:15:13 -0500 Subject: [PATCH 103/103] adds marketplace type aliases --- pallets/gated-marketplace/src/functions.rs | 2 +- pallets/gated-marketplace/src/lib.rs | 4 ++-- pallets/gated-marketplace/src/types.rs | 14 ++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pallets/gated-marketplace/src/functions.rs b/pallets/gated-marketplace/src/functions.rs index f95bcb6c..6d5ccba4 100644 --- a/pallets/gated-marketplace/src/functions.rs +++ b/pallets/gated-marketplace/src/functions.rs @@ -447,7 +447,7 @@ impl Pallet { pub fn set_up_application( fields : Fields, - custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> + custodian_fields: Option> )-> (Option, BoundedVec ){ let mut f: Vec= fields.iter().map(|tuple|{ ApplicationField{ diff --git a/pallets/gated-marketplace/src/lib.rs b/pallets/gated-marketplace/src/lib.rs index 7b48e328..c2e4224b 100644 --- a/pallets/gated-marketplace/src/lib.rs +++ b/pallets/gated-marketplace/src/lib.rs @@ -336,7 +336,7 @@ pub mod pallet { marketplace_id: [u8;32], // Getting encoding errors from polkadotjs if an object vector have optional fields fields : Fields, - custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> + custodian_fields: Option> ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -374,7 +374,7 @@ pub mod pallet { marketplace_id: [u8;32], // Getting encoding errors from polkadotjs if an object vector have optional fields fields : Fields, - custodian_fields: Option<(T::AccountId, BoundedVec>, T::MaxFiles> )> + custodian_fields: Option> ) -> DispatchResult { let who = ensure_signed(origin)?; diff --git a/pallets/gated-marketplace/src/types.rs b/pallets/gated-marketplace/src/types.rs index 33d21849..875d4c44 100644 --- a/pallets/gated-marketplace/src/types.rs +++ b/pallets/gated-marketplace/src/types.rs @@ -5,10 +5,12 @@ use frame_support::pallet_prelude::*; use sp_runtime::sp_std::vec::Vec; use frame_support::sp_io::hashing::blake2_256; -pub type Fields = BoundedVec<(BoundedVec >,BoundedVec> ), ::MaxFiles>; - -//Todo: fix AccountId import -//pub type CustodianFields = Option<(RawOrigin, BoundedVec>, ::MaxFiles>)>; +pub type Fields = BoundedVec<(FieldName,Cid), ::MaxFiles>; +type AccountIdOf = ::AccountId; +pub type FieldName = BoundedVec>; +pub type Cid = BoundedVec>; +pub type Cids = BoundedVec; +pub type CustodianFields = ( AccountIdOf, Cids<::MaxFiles>); #[derive(CloneNoBound,Encode, Decode, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen,)] #[scale_info(skip_type_params(T))] @@ -148,8 +150,8 @@ impl Default for ApplicationStatus{ #[derive(CloneNoBound, Encode ,Decode, Eq, RuntimeDebugNoBound, Default, TypeInfo, MaxEncodedLen)] pub struct ApplicationField{ pub display_name: BoundedVec >, - pub cid: BoundedVec >, - pub custodian_cid: Option > >, + pub cid: Cid, + pub custodian_cid: Option, } // Eq macro didnt work (binary operation `==` cannot be applied to type...) impl PartialEq for ApplicationField{