Skip to content

Commit

Permalink
Mesh 2104/add mediator (#1584)
Browse files Browse the repository at this point in the history
* Add add_mandatory_mediators and remove_mediators extrinsics

* Add mediators to InstructionInfo; Derive default

* Add InstructionMediatorsAffirmations storage; Set storage when adding a new instruction; Check mediator's affirmation expiry before executing

* Add affirm_instruction_as_mediator and remove_affirmation_as_mediator extrinsics; Remove migrations

* Add add_instruction_with_mediators; Add benchmarks; Change prune instruction

* Cargo fmt

* Add unit tests for the new extrinsics

* Add events

* Add expired_affirmation_execution unit test

* Add missing weights; Add add_and_affirm_with_mediators extrinsic

* Add mediators to execute instruction benchmarks; Rename functions

* Fix test function

* Add reject_instruction_as_mediator extrinsic

* Add mediators set to events

* Remove duplicated import

* Remove duplicated line in tests

---------

Co-authored-by: Robert Gabriel Jakabosky <[email protected]>
  • Loading branch information
HenriqueNogara and Neopallium authored Jan 16, 2024
1 parent 4cd58f7 commit b5ba6a3
Show file tree
Hide file tree
Showing 19 changed files with 1,757 additions and 385 deletions.
75 changes: 71 additions & 4 deletions pallets/asset/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use frame_benchmarking::benchmarks;
use frame_support::StorageValue;
use frame_system::RawOrigin;
use scale_info::prelude::format;
use sp_std::collections::btree_set::BTreeSet;
use sp_std::{convert::TryInto, iter, prelude::*};

use pallet_portfolio::{NextPortfolioNumber, PortfolioAssetBalances};
Expand Down Expand Up @@ -185,7 +187,8 @@ pub fn setup_asset_transfer<T>(
receiver_portolfio_name: Option<&str>,
pause_compliance: bool,
pause_restrictions: bool,
) -> (PortfolioId, PortfolioId)
n_mediators: u8,
) -> (PortfolioId, PortfolioId, Vec<User<T>>)
where
T: Config + TestUtilsFn<AccountIdOf<T>>,
{
Expand All @@ -198,6 +201,26 @@ where
make_asset::<T>(sender, Some(ticker.as_ref()));
move_from_default_portfolio::<T>(sender, ticker, ONE_UNIT * POLY, sender_portfolio);

// Sets mandatory mediators
let mut asset_mediators = Vec::new();
if n_mediators > 0 {
let mediators_identity: BTreeSet<IdentityId> = (0..n_mediators)
.map(|i| {
let mediator = UserBuilder::<T>::default()
.generate_did()
.build(&format!("Mediator{:?}{}", ticker, i));
asset_mediators.push(mediator.clone());
mediator.did()
})
.collect();
Module::<T>::add_mandatory_mediators(
sender.origin().into(),
ticker,
mediators_identity.try_into().unwrap(),
)
.unwrap();
}

// Adds the maximum number of compliance requirement
// If pause_compliance is true, only the decoding cost will be considered.
T::ComplianceManager::setup_ticker_compliance(sender.did(), ticker, 50, pause_compliance);
Expand All @@ -212,7 +235,7 @@ where
pause_restrictions,
);

(sender_portfolio, receiver_portfolio)
(sender_portfolio, receiver_portfolio, asset_mediators)
}

/// Creates a user portfolio for `user`.
Expand Down Expand Up @@ -618,8 +641,8 @@ benchmarks! {
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
let mut weight_meter = WeightMeter::max_limit_no_minimum();

let (sender_portfolio, receiver_portfolio) =
setup_asset_transfer::<T>(&alice, &bob, ticker, None, None, true, true);
let (sender_portfolio, receiver_portfolio, _) =
setup_asset_transfer::<T>(&alice, &bob, ticker, None, None, true, true, 0);
}: {
Module::<T>::base_transfer(
sender_portfolio,
Expand Down Expand Up @@ -653,4 +676,48 @@ benchmarks! {
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
Module::<T>::pre_approve_ticker(alice.clone().origin().into(), ticker).unwrap();
}: _(alice.origin, ticker)

add_mandatory_mediators {
let n in 1 .. T::MaxAssetMediators::get() as u32;

let alice = UserBuilder::<T>::default().generate_did().build("Alice");
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
let mediators: BTreeSet<IdentityId> = (0..n).map(|i| IdentityId::from(i as u128)).collect();

Module::<T>::create_asset(
alice.clone().origin().into(),
ticker.as_ref().into(),
ticker,
false,
AssetType::NonFungible(NonFungibleType::Derivative),
Vec::new(),
None,
).unwrap();

}: _(alice.origin, ticker, mediators.try_into().unwrap())

remove_mandatory_mediators {
let n in 1 .. T::MaxAssetMediators::get() as u32;

let alice = UserBuilder::<T>::default().generate_did().build("Alice");
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
let mediators: BTreeSet<IdentityId> = (0..n).map(|i| IdentityId::from(i as u128)).collect();

Module::<T>::create_asset(
alice.clone().origin().into(),
ticker.as_ref().into(),
ticker,
false,
AssetType::NonFungible(NonFungibleType::Derivative),
Vec::new(),
None,
).unwrap();

Module::<T>::add_mandatory_mediators(
alice.clone().origin().into(),
ticker,
mediators.clone().try_into().unwrap()
).unwrap();
}: _(alice.origin, ticker, mediators.try_into().unwrap())

}
103 changes: 103 additions & 0 deletions pallets/asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,17 @@ pub mod checkpoint;
#[cfg(feature = "std")]
use sp_runtime::{Deserialize, Serialize};

#[cfg(feature = "runtime-benchmarks")]
use sp_std::collections::btree_set::BTreeSet;

use arrayvec::ArrayVec;
use codec::{Decode, Encode};
use core::mem;
use core::result::Result as StdResult;
use currency::*;
use frame_support::dispatch::{DispatchError, DispatchResult, Weight};
use frame_support::traits::{Get, PalletInfoAccess};
use frame_support::BoundedBTreeSet;
use frame_support::{decl_error, decl_module, decl_storage, ensure, fail};
use frame_system::ensure_root;
use scale_info::TypeInfo;
Expand Down Expand Up @@ -291,6 +295,10 @@ decl_storage! {
pub PreApprovedTicker get(fn pre_approved_tickers):
double_map hasher(identity) IdentityId, hasher(blake2_128_concat) Ticker => bool;

/// The list of mandatory mediators for every ticker.
pub MandatoryMediators get(fn mandatory_mediators):
map hasher(blake2_128_concat) Ticker => BoundedBTreeSet<IdentityId, T::MaxAssetMediators>;

/// Storage version.
StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version;
}
Expand Down Expand Up @@ -893,6 +901,42 @@ decl_module! {
pub fn remove_ticker_pre_approval(origin, ticker: Ticker) -> DispatchResult {
Self::base_remove_ticker_pre_approval(origin, ticker)
}

/// Sets all identities in the `mediators` set as mandatory mediators for any instruction transfering `ticker`.
///
/// # Arguments
/// * `origin`: The secondary key of the sender.
/// * `ticker`: The [`Ticker`] of the asset that will require the mediators.
/// * `mediators`: A set of [`IdentityId`] of all the mandatory mediators for the given ticker.
///
/// # Permissions
/// * Asset
#[weight = <T as Config>::WeightInfo::add_mandatory_mediators(mediators.len() as u32)]
pub fn add_mandatory_mediators(
origin,
ticker: Ticker,
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>
) {
Self::base_add_mandatory_mediators(origin, ticker, mediators)?;
}

/// Removes all identities in the `mediators` set from the mandatory mediators list for the given `ticker`.
///
/// # Arguments
/// * `origin`: The secondary key of the sender.
/// * `ticker`: The [`Ticker`] of the asset that will have mediators removed.
/// * `mediators`: A set of [`IdentityId`] of all the mediators that will be removed from the mandatory mediators list.
///
/// # Permissions
/// * Asset
#[weight = <T as Config>::WeightInfo::remove_mandatory_mediators(mediators.len() as u32)]
pub fn remove_mandatory_mediators(
origin,
ticker: Ticker,
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>
) {
Self::base_remove_mandatory_mediators(origin, ticker, mediators)?;
}
}
}

Expand Down Expand Up @@ -972,6 +1016,8 @@ decl_error! {
AssetMetadataKeyBelongsToNFTCollection,
/// Attempt to lock a metadata value that is empty.
AssetMetadataValueIsEmpty,
/// Number of asset mediators would exceed the maximum allowed.
NumberOfAssetMediatorsExceeded,
}
}

Expand Down Expand Up @@ -2510,6 +2556,54 @@ impl<T: Config> Module<T> {
Self::deposit_event(RawEvent::RemovePreApprovedAsset(caller_did, ticker));
Ok(())
}

/// Sets all identities in the `mediators` set as mandatory mediators for any instruction transfering `ticker`.
fn base_add_mandatory_mediators(
origin: T::RuntimeOrigin,
ticker: Ticker,
new_mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>,
) -> DispatchResult {
// Verifies if the caller has the correct permissions for this asset
let caller_did = <ExternalAgents<T>>::ensure_perms(origin, ticker)?;
// Tries to add all new identities as mandatory mediators for the asset
MandatoryMediators::<T>::try_mutate(ticker, |mandatory_mediators| -> DispatchResult {
for new_mediator in &new_mediators {
mandatory_mediators
.try_insert(*new_mediator)
.map_err(|_| Error::<T>::NumberOfAssetMediatorsExceeded)?;
}
Ok(())
})?;

Self::deposit_event(RawEvent::AssetMediatorsAdded(
caller_did,
ticker,
new_mediators.into_inner(),
));
Ok(())
}

/// Removes all identities in the `mediators` set from the mandatory mediators list for the given `ticker`.
fn base_remove_mandatory_mediators(
origin: T::RuntimeOrigin,
ticker: Ticker,
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>,
) -> DispatchResult {
// Verifies if the caller has the correct permissions for this asset
let caller_did = <ExternalAgents<T>>::ensure_perms(origin, ticker)?;
// Removes the identities from the mandatory mediators list
MandatoryMediators::<T>::mutate(ticker, |mandatory_mediators| {
for mediator in &mediators {
mandatory_mediators.remove(mediator);
}
});
Self::deposit_event(RawEvent::AssetMediatorsRemoved(
caller_did,
ticker,
mediators.into_inner(),
));
Ok(())
}
}

impl<T: Config> AssetFnTrait<T::AccountId, T::RuntimeOrigin> for Module<T> {
Expand Down Expand Up @@ -2578,4 +2672,13 @@ impl<T: Config> AssetFnTrait<T::AccountId, T::RuntimeOrigin> for Module<T> {
None => Self::register_asset_metadata_global_type(origin, name, spec),
}
}

#[cfg(feature = "runtime-benchmarks")]
fn add_mandatory_mediators(
origin: T::RuntimeOrigin,
ticker: Ticker,
mediators: BTreeSet<IdentityId>,
) -> DispatchResult {
Self::add_mandatory_mediators(origin, ticker, mediators.try_into().unwrap_or_default())
}
}
20 changes: 20 additions & 0 deletions pallets/common/src/traits/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use frame_support::decl_event;
use frame_support::dispatch::DispatchResult;
use frame_support::traits::{Currency, Get, UnixTime};
use frame_support::weights::Weight;
use sp_std::collections::btree_set::BTreeSet;
use sp_std::prelude::Vec;

use polymesh_primitives::asset::{AssetName, AssetType, CustomAssetTypeId, FundingRoundName};
Expand Down Expand Up @@ -70,9 +71,13 @@ pub trait Config:
type AssetFn: AssetFnTrait<Self::AccountId, Self::RuntimeOrigin>;

type WeightInfo: WeightInfo;

type CPWeightInfo: crate::traits::checkpoint::WeightInfo;

type NFTFn: NFTTrait<Self::RuntimeOrigin>;

/// Maximum number of mediators for an asset.
type MaxAssetMediators: Get<u32>;
}

decl_event! {
Expand Down Expand Up @@ -177,6 +182,12 @@ decl_event! {
/// An identity has removed an asset to the list of pre aprroved receivement.
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset.
RemovePreApprovedAsset(IdentityId, Ticker),
/// An identity has added mandatory mediators to an asset.
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset, the identity of all mediators added.
AssetMediatorsAdded(IdentityId, Ticker, BTreeSet<IdentityId>),
/// An identity has removed mediators from an asset.
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset, the identity of all mediators removed.
AssetMediatorsRemoved(IdentityId, Ticker, BTreeSet<IdentityId>)
}
}

Expand Down Expand Up @@ -211,6 +222,8 @@ pub trait WeightInfo {
fn remove_ticker_affirmation_exemption() -> Weight;
fn pre_approve_ticker() -> Weight;
fn remove_ticker_pre_approval() -> Weight;
fn add_mandatory_mediators(n: u32) -> Weight;
fn remove_mandatory_mediators(n: u32) -> Weight;
}

pub trait AssetFnTrait<Account, Origin> {
Expand Down Expand Up @@ -252,4 +265,11 @@ pub trait AssetFnTrait<Account, Origin> {
name: AssetMetadataName,
spec: AssetMetadataSpec,
) -> DispatchResult;

#[cfg(feature = "runtime-benchmarks")]
fn add_mandatory_mediators(
origin: Origin,
ticker: Ticker,
mediators: BTreeSet<IdentityId>,
) -> DispatchResult;
}
48 changes: 41 additions & 7 deletions pallets/common/src/traits/settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ decl_event!(
/// An instruction has been automatically affirmed.
/// Parameters: [`IdentityId`] of the caller, [`PortfolioId`] of the receiver, and [`InstructionId`] of the instruction.
InstructionAutomaticallyAffirmed(IdentityId, PortfolioId, InstructionId),
/// An instruction has affirmed by a mediator.
/// Parameters: [`IdentityId`] of the mediator and [`InstructionId`] of the instruction.
MediatorAffirmationReceived(IdentityId, InstructionId),
/// An instruction affirmation has been withdrawn by a mediator.
/// Parameters: [`IdentityId`] of the mediator and [`InstructionId`] of the instruction.
MediatorAffirmationWithdrawn(IdentityId, InstructionId),
}
);

Expand All @@ -101,7 +107,20 @@ pub trait WeightInfo {
fn affirm_with_receipts_rcv(f: u32, n: u32, o: u32) -> Weight;
fn affirm_instruction_rcv(f: u32, n: u32) -> Weight;
fn withdraw_affirmation_rcv(f: u32, n: u32, o: u32) -> Weight;
fn add_instruction_with_mediators(f: u32, n: u32, o: u32, m: u32) -> Weight;
fn add_and_affirm_with_mediators(f: u32, n: u32, o: u32, m: u32) -> Weight;
fn affirm_instruction_as_mediator() -> Weight;
fn withdraw_affirmation_as_mediator() -> Weight;
fn reject_instruction_as_mediator(f: u32, n: u32, o: u32) -> Weight;

fn add_and_affirm_with_mediators_legs(legs: &[Leg], n_mediators: u32) -> Weight {
let (f, n, o) = Self::get_transfer_by_asset(legs);
Self::add_and_affirm_with_mediators(f, n, o, n_mediators)
}
fn add_instruction_with_mediators_legs(legs: &[Leg], n_mediators: u32) -> Weight {
let (f, n, o) = Self::get_transfer_by_asset(legs);
Self::add_instruction_with_mediators(f, n, o, n_mediators)
}
fn add_instruction_legs(legs: &[Leg]) -> Weight {
let (f, n, o) = Self::get_transfer_by_asset(legs);
Self::add_instruction(f, n, o)
Expand Down Expand Up @@ -209,14 +228,29 @@ pub trait WeightInfo {
None => Self::withdraw_affirmation(10, 100, 10),
}
}
fn reject_instruction_input(asset_count: Option<AssetCount>) -> Weight {
fn reject_instruction_input(asset_count: Option<AssetCount>, as_mediator: bool) -> Weight {
match asset_count {
Some(asset_count) => Self::reject_instruction(
asset_count.fungible(),
asset_count.non_fungible(),
asset_count.off_chain(),
),
None => Self::reject_instruction(10, 100, 10),
Some(asset_count) => {
if as_mediator {
return Self::reject_instruction_as_mediator(
asset_count.fungible(),
asset_count.non_fungible(),
asset_count.off_chain(),
);
}
Self::reject_instruction(
asset_count.fungible(),
asset_count.non_fungible(),
asset_count.off_chain(),
)
}
None => {
let (f, n, o) = (10, 100, 10);
if as_mediator {
return Self::reject_instruction_as_mediator(f, n, o);
}
Self::reject_instruction(f, n, o)
}
}
}
}
Loading

0 comments on commit b5ba6a3

Please sign in to comment.