Skip to content

Commit b5ba6a3

Browse files
Mesh 2104/add mediator (#1584)
* 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 <rjakabosky+neopallium@neoawareness.com>
1 parent 4cd58f7 commit b5ba6a3

File tree

19 files changed

+1757
-385
lines changed

19 files changed

+1757
-385
lines changed

pallets/asset/src/benchmarking.rs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use frame_benchmarking::benchmarks;
1717
use frame_support::StorageValue;
1818
use frame_system::RawOrigin;
19+
use scale_info::prelude::format;
20+
use sp_std::collections::btree_set::BTreeSet;
1921
use sp_std::{convert::TryInto, iter, prelude::*};
2022

2123
use pallet_portfolio::{NextPortfolioNumber, PortfolioAssetBalances};
@@ -185,7 +187,8 @@ pub fn setup_asset_transfer<T>(
185187
receiver_portolfio_name: Option<&str>,
186188
pause_compliance: bool,
187189
pause_restrictions: bool,
188-
) -> (PortfolioId, PortfolioId)
190+
n_mediators: u8,
191+
) -> (PortfolioId, PortfolioId, Vec<User<T>>)
189192
where
190193
T: Config + TestUtilsFn<AccountIdOf<T>>,
191194
{
@@ -198,6 +201,26 @@ where
198201
make_asset::<T>(sender, Some(ticker.as_ref()));
199202
move_from_default_portfolio::<T>(sender, ticker, ONE_UNIT * POLY, sender_portfolio);
200203

204+
// Sets mandatory mediators
205+
let mut asset_mediators = Vec::new();
206+
if n_mediators > 0 {
207+
let mediators_identity: BTreeSet<IdentityId> = (0..n_mediators)
208+
.map(|i| {
209+
let mediator = UserBuilder::<T>::default()
210+
.generate_did()
211+
.build(&format!("Mediator{:?}{}", ticker, i));
212+
asset_mediators.push(mediator.clone());
213+
mediator.did()
214+
})
215+
.collect();
216+
Module::<T>::add_mandatory_mediators(
217+
sender.origin().into(),
218+
ticker,
219+
mediators_identity.try_into().unwrap(),
220+
)
221+
.unwrap();
222+
}
223+
201224
// Adds the maximum number of compliance requirement
202225
// If pause_compliance is true, only the decoding cost will be considered.
203226
T::ComplianceManager::setup_ticker_compliance(sender.did(), ticker, 50, pause_compliance);
@@ -212,7 +235,7 @@ where
212235
pause_restrictions,
213236
);
214237

215-
(sender_portfolio, receiver_portfolio)
238+
(sender_portfolio, receiver_portfolio, asset_mediators)
216239
}
217240

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

621-
let (sender_portfolio, receiver_portfolio) =
622-
setup_asset_transfer::<T>(&alice, &bob, ticker, None, None, true, true);
644+
let (sender_portfolio, receiver_portfolio, _) =
645+
setup_asset_transfer::<T>(&alice, &bob, ticker, None, None, true, true, 0);
623646
}: {
624647
Module::<T>::base_transfer(
625648
sender_portfolio,
@@ -653,4 +676,48 @@ benchmarks! {
653676
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
654677
Module::<T>::pre_approve_ticker(alice.clone().origin().into(), ticker).unwrap();
655678
}: _(alice.origin, ticker)
679+
680+
add_mandatory_mediators {
681+
let n in 1 .. T::MaxAssetMediators::get() as u32;
682+
683+
let alice = UserBuilder::<T>::default().generate_did().build("Alice");
684+
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
685+
let mediators: BTreeSet<IdentityId> = (0..n).map(|i| IdentityId::from(i as u128)).collect();
686+
687+
Module::<T>::create_asset(
688+
alice.clone().origin().into(),
689+
ticker.as_ref().into(),
690+
ticker,
691+
false,
692+
AssetType::NonFungible(NonFungibleType::Derivative),
693+
Vec::new(),
694+
None,
695+
).unwrap();
696+
697+
}: _(alice.origin, ticker, mediators.try_into().unwrap())
698+
699+
remove_mandatory_mediators {
700+
let n in 1 .. T::MaxAssetMediators::get() as u32;
701+
702+
let alice = UserBuilder::<T>::default().generate_did().build("Alice");
703+
let ticker: Ticker = Ticker::from_slice_truncated(b"TICKER".as_ref());
704+
let mediators: BTreeSet<IdentityId> = (0..n).map(|i| IdentityId::from(i as u128)).collect();
705+
706+
Module::<T>::create_asset(
707+
alice.clone().origin().into(),
708+
ticker.as_ref().into(),
709+
ticker,
710+
false,
711+
AssetType::NonFungible(NonFungibleType::Derivative),
712+
Vec::new(),
713+
None,
714+
).unwrap();
715+
716+
Module::<T>::add_mandatory_mediators(
717+
alice.clone().origin().into(),
718+
ticker,
719+
mediators.clone().try_into().unwrap()
720+
).unwrap();
721+
}: _(alice.origin, ticker, mediators.try_into().unwrap())
722+
656723
}

pallets/asset/src/lib.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,17 @@ pub mod checkpoint;
8484
#[cfg(feature = "std")]
8585
use sp_runtime::{Deserialize, Serialize};
8686

87+
#[cfg(feature = "runtime-benchmarks")]
88+
use sp_std::collections::btree_set::BTreeSet;
89+
8790
use arrayvec::ArrayVec;
8891
use codec::{Decode, Encode};
8992
use core::mem;
9093
use core::result::Result as StdResult;
9194
use currency::*;
9295
use frame_support::dispatch::{DispatchError, DispatchResult, Weight};
9396
use frame_support::traits::{Get, PalletInfoAccess};
97+
use frame_support::BoundedBTreeSet;
9498
use frame_support::{decl_error, decl_module, decl_storage, ensure, fail};
9599
use frame_system::ensure_root;
96100
use scale_info::TypeInfo;
@@ -291,6 +295,10 @@ decl_storage! {
291295
pub PreApprovedTicker get(fn pre_approved_tickers):
292296
double_map hasher(identity) IdentityId, hasher(blake2_128_concat) Ticker => bool;
293297

298+
/// The list of mandatory mediators for every ticker.
299+
pub MandatoryMediators get(fn mandatory_mediators):
300+
map hasher(blake2_128_concat) Ticker => BoundedBTreeSet<IdentityId, T::MaxAssetMediators>;
301+
294302
/// Storage version.
295303
StorageVersion get(fn storage_version) build(|_| Version::new(3)): Version;
296304
}
@@ -893,6 +901,42 @@ decl_module! {
893901
pub fn remove_ticker_pre_approval(origin, ticker: Ticker) -> DispatchResult {
894902
Self::base_remove_ticker_pre_approval(origin, ticker)
895903
}
904+
905+
/// Sets all identities in the `mediators` set as mandatory mediators for any instruction transfering `ticker`.
906+
///
907+
/// # Arguments
908+
/// * `origin`: The secondary key of the sender.
909+
/// * `ticker`: The [`Ticker`] of the asset that will require the mediators.
910+
/// * `mediators`: A set of [`IdentityId`] of all the mandatory mediators for the given ticker.
911+
///
912+
/// # Permissions
913+
/// * Asset
914+
#[weight = <T as Config>::WeightInfo::add_mandatory_mediators(mediators.len() as u32)]
915+
pub fn add_mandatory_mediators(
916+
origin,
917+
ticker: Ticker,
918+
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>
919+
) {
920+
Self::base_add_mandatory_mediators(origin, ticker, mediators)?;
921+
}
922+
923+
/// Removes all identities in the `mediators` set from the mandatory mediators list for the given `ticker`.
924+
///
925+
/// # Arguments
926+
/// * `origin`: The secondary key of the sender.
927+
/// * `ticker`: The [`Ticker`] of the asset that will have mediators removed.
928+
/// * `mediators`: A set of [`IdentityId`] of all the mediators that will be removed from the mandatory mediators list.
929+
///
930+
/// # Permissions
931+
/// * Asset
932+
#[weight = <T as Config>::WeightInfo::remove_mandatory_mediators(mediators.len() as u32)]
933+
pub fn remove_mandatory_mediators(
934+
origin,
935+
ticker: Ticker,
936+
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>
937+
) {
938+
Self::base_remove_mandatory_mediators(origin, ticker, mediators)?;
939+
}
896940
}
897941
}
898942

@@ -972,6 +1016,8 @@ decl_error! {
9721016
AssetMetadataKeyBelongsToNFTCollection,
9731017
/// Attempt to lock a metadata value that is empty.
9741018
AssetMetadataValueIsEmpty,
1019+
/// Number of asset mediators would exceed the maximum allowed.
1020+
NumberOfAssetMediatorsExceeded,
9751021
}
9761022
}
9771023

@@ -2510,6 +2556,54 @@ impl<T: Config> Module<T> {
25102556
Self::deposit_event(RawEvent::RemovePreApprovedAsset(caller_did, ticker));
25112557
Ok(())
25122558
}
2559+
2560+
/// Sets all identities in the `mediators` set as mandatory mediators for any instruction transfering `ticker`.
2561+
fn base_add_mandatory_mediators(
2562+
origin: T::RuntimeOrigin,
2563+
ticker: Ticker,
2564+
new_mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>,
2565+
) -> DispatchResult {
2566+
// Verifies if the caller has the correct permissions for this asset
2567+
let caller_did = <ExternalAgents<T>>::ensure_perms(origin, ticker)?;
2568+
// Tries to add all new identities as mandatory mediators for the asset
2569+
MandatoryMediators::<T>::try_mutate(ticker, |mandatory_mediators| -> DispatchResult {
2570+
for new_mediator in &new_mediators {
2571+
mandatory_mediators
2572+
.try_insert(*new_mediator)
2573+
.map_err(|_| Error::<T>::NumberOfAssetMediatorsExceeded)?;
2574+
}
2575+
Ok(())
2576+
})?;
2577+
2578+
Self::deposit_event(RawEvent::AssetMediatorsAdded(
2579+
caller_did,
2580+
ticker,
2581+
new_mediators.into_inner(),
2582+
));
2583+
Ok(())
2584+
}
2585+
2586+
/// Removes all identities in the `mediators` set from the mandatory mediators list for the given `ticker`.
2587+
fn base_remove_mandatory_mediators(
2588+
origin: T::RuntimeOrigin,
2589+
ticker: Ticker,
2590+
mediators: BoundedBTreeSet<IdentityId, T::MaxAssetMediators>,
2591+
) -> DispatchResult {
2592+
// Verifies if the caller has the correct permissions for this asset
2593+
let caller_did = <ExternalAgents<T>>::ensure_perms(origin, ticker)?;
2594+
// Removes the identities from the mandatory mediators list
2595+
MandatoryMediators::<T>::mutate(ticker, |mandatory_mediators| {
2596+
for mediator in &mediators {
2597+
mandatory_mediators.remove(mediator);
2598+
}
2599+
});
2600+
Self::deposit_event(RawEvent::AssetMediatorsRemoved(
2601+
caller_did,
2602+
ticker,
2603+
mediators.into_inner(),
2604+
));
2605+
Ok(())
2606+
}
25132607
}
25142608

25152609
impl<T: Config> AssetFnTrait<T::AccountId, T::RuntimeOrigin> for Module<T> {
@@ -2578,4 +2672,13 @@ impl<T: Config> AssetFnTrait<T::AccountId, T::RuntimeOrigin> for Module<T> {
25782672
None => Self::register_asset_metadata_global_type(origin, name, spec),
25792673
}
25802674
}
2675+
2676+
#[cfg(feature = "runtime-benchmarks")]
2677+
fn add_mandatory_mediators(
2678+
origin: T::RuntimeOrigin,
2679+
ticker: Ticker,
2680+
mediators: BTreeSet<IdentityId>,
2681+
) -> DispatchResult {
2682+
Self::add_mandatory_mediators(origin, ticker, mediators.try_into().unwrap_or_default())
2683+
}
25812684
}

pallets/common/src/traits/asset.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use frame_support::decl_event;
1717
use frame_support::dispatch::DispatchResult;
1818
use frame_support::traits::{Currency, Get, UnixTime};
1919
use frame_support::weights::Weight;
20+
use sp_std::collections::btree_set::BTreeSet;
2021
use sp_std::prelude::Vec;
2122

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

7273
type WeightInfo: WeightInfo;
74+
7375
type CPWeightInfo: crate::traits::checkpoint::WeightInfo;
7476

7577
type NFTFn: NFTTrait<Self::RuntimeOrigin>;
78+
79+
/// Maximum number of mediators for an asset.
80+
type MaxAssetMediators: Get<u32>;
7681
}
7782

7883
decl_event! {
@@ -177,6 +182,12 @@ decl_event! {
177182
/// An identity has removed an asset to the list of pre aprroved receivement.
178183
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset.
179184
RemovePreApprovedAsset(IdentityId, Ticker),
185+
/// An identity has added mandatory mediators to an asset.
186+
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset, the identity of all mediators added.
187+
AssetMediatorsAdded(IdentityId, Ticker, BTreeSet<IdentityId>),
188+
/// An identity has removed mediators from an asset.
189+
/// Parameters: [`IdentityId`] of caller, [`Ticker`] of the asset, the identity of all mediators removed.
190+
AssetMediatorsRemoved(IdentityId, Ticker, BTreeSet<IdentityId>)
180191
}
181192
}
182193

@@ -211,6 +222,8 @@ pub trait WeightInfo {
211222
fn remove_ticker_affirmation_exemption() -> Weight;
212223
fn pre_approve_ticker() -> Weight;
213224
fn remove_ticker_pre_approval() -> Weight;
225+
fn add_mandatory_mediators(n: u32) -> Weight;
226+
fn remove_mandatory_mediators(n: u32) -> Weight;
214227
}
215228

216229
pub trait AssetFnTrait<Account, Origin> {
@@ -252,4 +265,11 @@ pub trait AssetFnTrait<Account, Origin> {
252265
name: AssetMetadataName,
253266
spec: AssetMetadataSpec,
254267
) -> DispatchResult;
268+
269+
#[cfg(feature = "runtime-benchmarks")]
270+
fn add_mandatory_mediators(
271+
origin: Origin,
272+
ticker: Ticker,
273+
mediators: BTreeSet<IdentityId>,
274+
) -> DispatchResult;
255275
}

pallets/common/src/traits/settlement.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ decl_event!(
7777
/// An instruction has been automatically affirmed.
7878
/// Parameters: [`IdentityId`] of the caller, [`PortfolioId`] of the receiver, and [`InstructionId`] of the instruction.
7979
InstructionAutomaticallyAffirmed(IdentityId, PortfolioId, InstructionId),
80+
/// An instruction has affirmed by a mediator.
81+
/// Parameters: [`IdentityId`] of the mediator and [`InstructionId`] of the instruction.
82+
MediatorAffirmationReceived(IdentityId, InstructionId),
83+
/// An instruction affirmation has been withdrawn by a mediator.
84+
/// Parameters: [`IdentityId`] of the mediator and [`InstructionId`] of the instruction.
85+
MediatorAffirmationWithdrawn(IdentityId, InstructionId),
8086
}
8187
);
8288

@@ -101,7 +107,20 @@ pub trait WeightInfo {
101107
fn affirm_with_receipts_rcv(f: u32, n: u32, o: u32) -> Weight;
102108
fn affirm_instruction_rcv(f: u32, n: u32) -> Weight;
103109
fn withdraw_affirmation_rcv(f: u32, n: u32, o: u32) -> Weight;
110+
fn add_instruction_with_mediators(f: u32, n: u32, o: u32, m: u32) -> Weight;
111+
fn add_and_affirm_with_mediators(f: u32, n: u32, o: u32, m: u32) -> Weight;
112+
fn affirm_instruction_as_mediator() -> Weight;
113+
fn withdraw_affirmation_as_mediator() -> Weight;
114+
fn reject_instruction_as_mediator(f: u32, n: u32, o: u32) -> Weight;
104115

116+
fn add_and_affirm_with_mediators_legs(legs: &[Leg], n_mediators: u32) -> Weight {
117+
let (f, n, o) = Self::get_transfer_by_asset(legs);
118+
Self::add_and_affirm_with_mediators(f, n, o, n_mediators)
119+
}
120+
fn add_instruction_with_mediators_legs(legs: &[Leg], n_mediators: u32) -> Weight {
121+
let (f, n, o) = Self::get_transfer_by_asset(legs);
122+
Self::add_instruction_with_mediators(f, n, o, n_mediators)
123+
}
105124
fn add_instruction_legs(legs: &[Leg]) -> Weight {
106125
let (f, n, o) = Self::get_transfer_by_asset(legs);
107126
Self::add_instruction(f, n, o)
@@ -209,14 +228,29 @@ pub trait WeightInfo {
209228
None => Self::withdraw_affirmation(10, 100, 10),
210229
}
211230
}
212-
fn reject_instruction_input(asset_count: Option<AssetCount>) -> Weight {
231+
fn reject_instruction_input(asset_count: Option<AssetCount>, as_mediator: bool) -> Weight {
213232
match asset_count {
214-
Some(asset_count) => Self::reject_instruction(
215-
asset_count.fungible(),
216-
asset_count.non_fungible(),
217-
asset_count.off_chain(),
218-
),
219-
None => Self::reject_instruction(10, 100, 10),
233+
Some(asset_count) => {
234+
if as_mediator {
235+
return Self::reject_instruction_as_mediator(
236+
asset_count.fungible(),
237+
asset_count.non_fungible(),
238+
asset_count.off_chain(),
239+
);
240+
}
241+
Self::reject_instruction(
242+
asset_count.fungible(),
243+
asset_count.non_fungible(),
244+
asset_count.off_chain(),
245+
)
246+
}
247+
None => {
248+
let (f, n, o) = (10, 100, 10);
249+
if as_mediator {
250+
return Self::reject_instruction_as_mediator(f, n, o);
251+
}
252+
Self::reject_instruction(f, n, o)
253+
}
220254
}
221255
}
222256
}

0 commit comments

Comments
 (0)