Skip to content

Settlement Locking #1818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions pallets/asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3293,8 +3293,8 @@ impl<T: AssetConfig> Pallet<T> {
)?;

// Updates the balance in the asset pallet
let sender_new_balance = sender_current_balance - transfer_value;
let receiver_new_balance = receiver_current_balance + transfer_value;
let sender_new_balance = sender_current_balance.saturating_sub(transfer_value);
let receiver_new_balance = receiver_current_balance.saturating_add(transfer_value);
BalanceOf::<T>::insert(asset_id, sender_portfolio.did, sender_new_balance);
BalanceOf::<T>::insert(asset_id, receiver_portfolio.did, receiver_new_balance);

Expand Down Expand Up @@ -3461,6 +3461,39 @@ impl<T: AssetConfig> Pallet<T> {
}
})
}

/// Transfers `transfer_value` of `asset_id` from `sender_pid` to `receiver_pid`.
/// Note: This functions skips all compliance and statistics checks, only checking for balance.
pub fn simplified_fungible_transfer(
asset_id: AssetId,
sender_pid: PortfolioId,
receiver_pid: PortfolioId,
transfer_value: Balance,
inst_id: InstructionId,
inst_memo: Option<Memo>,
caller_did: IdentityId,
weight_meter: &mut WeightMeter,
) -> DispatchResult {
ensure!(
BalanceOf::<T>::get(&asset_id, &sender_pid.did) >= transfer_value,
Error::<T>::InsufficientBalance
);
Portfolio::<T>::ensure_portfolio_validity(&receiver_pid)?;
Portfolio::<T>::ensure_sufficient_balance(&sender_pid, &asset_id, transfer_value)?;

Self::unverified_transfer_asset(
sender_pid,
receiver_pid,
asset_id,
transfer_value,
Some(inst_id),
inst_memo,
caller_did,
weight_meter,
)?;

Ok(())
}
}

//==========================================================================
Expand Down
41 changes: 39 additions & 2 deletions pallets/nft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,10 +712,10 @@ impl<T: Config> Pallet<T> {
// Update the balance of the sender and the receiver
let transferred_amount = nfts.len() as u64;
NumberOfNFTs::<T>::mutate(nfts.asset_id(), sender_portfolio.did, |balance| {
*balance -= transferred_amount
*balance = balance.saturating_sub(transferred_amount)
});
NumberOfNFTs::<T>::mutate(nfts.asset_id(), receiver_portfolio.did, |balance| {
*balance += transferred_amount
*balance = balance.saturating_add(transferred_amount)
});
// Update the portfolio of the sender and the receiver
for nft_id in nfts.ids() {
Expand Down Expand Up @@ -870,6 +870,43 @@ impl<T: Config> Pallet<T> {
}
})
}

/// Transfers all `nfts` from `sender_pid` to `receiver_pid`.
/// Note: This functions skips all compliance checks and only checks for onwership.
pub fn simplified_nft_transfer(
sender_pid: PortfolioId,
receiver_pid: PortfolioId,
nfts: NFTs,
inst_id: InstructionId,
inst_memo: Option<Memo>,
caller_did: IdentityId,
) -> DispatchResult {
Portfolio::<T>::ensure_portfolio_validity(&receiver_pid)?;
Self::ensure_sender_owns_nfts(&sender_pid, &nfts)?;
Self::unverified_nfts_transfer(&sender_pid, &receiver_pid, &nfts);
Self::deposit_event(Event::NFTPortfolioUpdated(
caller_did,
nfts,
Some(sender_pid),
Some(receiver_pid),
PortfolioUpdateReason::Transferred {
instruction_id: Some(inst_id),
instruction_memo: inst_memo,
},
));
Ok(())
}

/// Returns `Ok` if `sender_pid` holds all nfts.
fn ensure_sender_owns_nfts(sender_pid: &PortfolioId, nfts: &NFTs) -> DispatchResult {
for nft_id in nfts.ids() {
ensure!(
PortfolioNFT::<T>::contains_key(sender_pid, (nfts.asset_id(), nft_id)),
Error::<T>::InvalidNFTTransferNFTNotOwned
);
}
Ok(())
}
}

impl<T: Config> NFTTrait<T::RuntimeOrigin> for Pallet<T> {
Expand Down
31 changes: 20 additions & 11 deletions pallets/runtime/common/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ macro_rules! misc_pallet_impls {
type MaxNumberOfPortfolios = MaxNumberOfPortfolios;
type MaxNumberOfVenueSigners = MaxNumberOfVenueSigners;
type MaxInstructionMediators = MaxInstructionMediators;
type MaximumLockPeriod = MaximumLockPeriod;
}

impl pallet_sto::Config for Runtime {
Expand Down Expand Up @@ -736,17 +737,17 @@ macro_rules! runtime_apis {
use frame_support::dispatch::result::Result as FrameResult;
use node_rpc_runtime_api::asset as rpc_api_asset;

use pallet_identity::types::{AssetDidResult, CddStatus, RpcDidRecords, DidStatus, KeyIdentityData};
use pallet_identity::types::{AssetDidResult, CddStatus, RpcDidRecords};
use pallet_identity::types::{DidStatus, KeyIdentityData};
use pallet_pips::{Vote, VoteCount};
use pallet_protocol_fee_rpc_runtime_api::CappedFee;
use polymesh_primitives::asset::AssetId;
use polymesh_primitives::settlement::{InstructionId, ExecuteInstructionInfo, AffirmationCount};
use polymesh_primitives::asset::{AssetId, CheckpointId};
use polymesh_primitives::settlement::{ AssetCount, AffirmationCount};
use polymesh_primitives::settlement::{InstructionId, ExecuteInstructionInfo};
use polymesh_primitives::transfer_compliance::TransferCondition;
use polymesh_primitives::compliance_manager::{AssetComplianceResult, ComplianceReport};
use polymesh_primitives::{
asset::CheckpointId, IdentityId, Index, NFTs,PortfolioId, Signatory, Ticker,
WeightMeter, IdentityClaim
};
use polymesh_primitives::{IdentityId, Index, NFTs, PortfolioId};
use polymesh_primitives::{Signatory, Ticker, WeightMeter, IdentityClaim};

/// The address format for describing accounts.
pub type Address = <Indices as StaticLookup>::Source;
Expand Down Expand Up @@ -1162,9 +1163,9 @@ macro_rules! runtime_apis {
impl node_rpc_runtime_api::settlement::SettlementApi<Block> for Runtime {
#[inline]
fn get_execute_instruction_info(
instruction_id: &InstructionId
instruction_id: InstructionId
) -> Option<ExecuteInstructionInfo> {
Settlement::execute_instruction_info(instruction_id)
Settlement::manual_execution_weight(instruction_id)
}

#[inline]
Expand All @@ -1183,10 +1184,18 @@ macro_rules! runtime_apis {

#[inline]
fn get_execute_instruction_report(instruction_id: InstructionId) -> Vec<DispatchError> {
let mut weight_meter = WeightMeter::max_limit_no_minimum();
Settlement::execute_instruction_report(&instruction_id, &mut weight_meter)
Settlement::execute_instruction_report(&instruction_id)
}

#[inline]
fn lock_instruction_weight(instruction_id: InstructionId) -> Result<Weight, DispatchError> {
Settlement::lock_instruction_weight(instruction_id)
}

#[inline]
fn instruction_asset_count(instruction_id: InstructionId) -> AssetCount {
Settlement::instruction_asset_count(&instruction_id)
}
}

impl node_rpc_runtime_api::compliance::ComplianceApi<Block> for Runtime {
Expand Down
1 change: 1 addition & 0 deletions pallets/runtime/develop/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ parameter_types! {
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
pub const MaxNumberOfVenueSigners: u32 = 50;
pub const MaxInstructionMediators: u32 = 4;
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours

// Multisig
pub const MaxMultiSigSigners: u32 = 50;
Expand Down
1 change: 1 addition & 0 deletions pallets/runtime/mainnet/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ parameter_types! {
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
pub const MaxNumberOfVenueSigners: u32 = 50;
pub const MaxInstructionMediators: u32 = 4;
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours

// Multisig
pub const MaxMultiSigSigners: u32 = 50;
Expand Down
1 change: 1 addition & 0 deletions pallets/runtime/testnet/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ parameter_types! {
pub const MaxNumberOfPortfolios: u32 = (10 + 100) * 2;
pub const MaxNumberOfVenueSigners: u32 = 50;
pub const MaxInstructionMediators: u32 = 4;
pub const MaximumLockPeriod: Moment = 1_440_000; // 24 hours

// Multisig
pub const MaxMultiSigSigners: u32 = 50;
Expand Down
94 changes: 30 additions & 64 deletions pallets/runtime/tests/src/settlement_pallet/execute_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,113 +3,79 @@ use sp_keyring::AccountKeyring;

use pallet_asset::BalanceOf;
use pallet_portfolio::PortfolioLockedAssets;
use pallet_settlement::{
AffirmsReceived, Error, Event, InstructionAffirmsPending, InstructionDetails,
InstructionLegStatus, InstructionLegs, InstructionMediatorsAffirmations, InstructionStatuses,
OffChainAffirmations, UserAffirmations, VenueInstructions,
};
use polymesh_primitives::settlement::{
AffirmationStatus, Instruction, InstructionId, InstructionStatus, Leg, LegId, SettlementType,
};
use pallet_settlement::{AffirmsReceived, Error, Event, InstructionAffirmsPending};
use pallet_settlement::{InstructionDetails, InstructionLegStatus, InstructionLegs};
use pallet_settlement::{InstructionMediatorsAffirmations, InstructionStatuses};
use pallet_settlement::{OffChainAffirmations, UserAffirmations, VenueInstructions};
use polymesh_primitives::settlement::{AffirmationStatus, Instruction, InstructionId};
use polymesh_primitives::settlement::{InstructionStatus, Leg, LegId, SettlementType};
use polymesh_primitives::PortfolioId;
use polymesh_primitives::SystematicIssuers::Settlement as SettlementDID;

use super::setup::create_and_issue_sample_asset_with_venue;
use super::setup::{add_and_affirm_simple_instruction, create_and_issue_sample_asset_with_venue};
use crate::asset_pallet::setup::create_and_issue_sample_asset;
use crate::storage::{default_portfolio_btreeset, User};
use crate::{next_block, ExtBuilder, TestStorage};

type Settlement = pallet_settlement::Pallet<TestStorage>;
type System = frame_system::Pallet<TestStorage>;
type Timestamp = pallet_timestamp::Pallet<TestStorage>;

type PortfolioError = pallet_portfolio::Error<TestStorage>;

#[test]
fn execute_instruction_storage_pruning() {
fn storage_pruning() {
ExtBuilder::default().build().execute_with(|| {
let instruction_id = InstructionId(0);
let inst_id = InstructionId(0);
let bob = User::new(AccountKeyring::Bob);
let dave = User::new(AccountKeyring::Dave);
let alice = User::new(AccountKeyring::Alice);
let bob_default_portfolio = PortfolioId::default_portfolio(bob.did);
let alice_default_portfolio = PortfolioId::default_portfolio(alice.did);

let (asset_id, venue_id) = create_and_issue_sample_asset_with_venue(&alice);
let legs: Vec<Leg> = vec![Leg::Fungible {
sender: PortfolioId::default_portfolio(alice.did),
receiver: PortfolioId::default_portfolio(bob.did),
asset_id,
amount: 1_000,
}];
assert_ok!(Settlement::add_instruction(
alice.origin(),
venue_id,
let _ = add_and_affirm_simple_instruction(
alice,
bob,
dave,
SettlementType::SettleOnAffirmation,
None,
None,
legs.clone(),
None,
));
assert_ok!(Settlement::affirm_instruction(
alice.origin(),
instruction_id,
default_portfolio_btreeset(alice.did),
));
assert_ok!(Settlement::affirm_instruction(
bob.origin(),
instruction_id,
default_portfolio_btreeset(bob.did),
));
);
next_block();

// Asserts all storage have been pruned
assert_eq!(InstructionAffirmsPending::<TestStorage>::get(inst_id), 0);
assert_eq!(VenueInstructions::<TestStorage>::iter().next(), None);
assert_eq!(
InstructionAffirmsPending::<TestStorage>::get(instruction_id),
0
);
assert_eq!(
VenueInstructions::<TestStorage>::iter_prefix_values(venue_id.unwrap()).next(),
None
);
assert_eq!(
InstructionLegs::<TestStorage>::iter_prefix_values(instruction_id).next(),
InstructionLegs::<TestStorage>::iter_prefix_values(inst_id).next(),
None
);
assert_eq!(
InstructionDetails::<TestStorage>::get(instruction_id),
InstructionDetails::<TestStorage>::get(inst_id),
Instruction::default()
);
assert_eq!(
InstructionLegStatus::<TestStorage>::iter_prefix_values(instruction_id).next(),
InstructionLegStatus::<TestStorage>::iter_prefix_values(inst_id).next(),
None
);
assert_eq!(
OffChainAffirmations::<TestStorage>::iter_prefix_values(instruction_id).next(),
OffChainAffirmations::<TestStorage>::iter_prefix_values(inst_id).next(),
None
);
assert_eq!(
AffirmsReceived::<TestStorage>::iter_prefix_values(instruction_id).next(),
AffirmsReceived::<TestStorage>::iter_prefix_values(inst_id).next(),
None
);
assert_eq!(
InstructionMediatorsAffirmations::<TestStorage>::iter_prefix_values(instruction_id)
.next(),
InstructionMediatorsAffirmations::<TestStorage>::iter_prefix_values(inst_id).next(),
None
);
assert_eq!(UserAffirmations::<TestStorage>::iter().next(), None);
assert_eq!(
UserAffirmations::<TestStorage>::get(alice_default_portfolio, instruction_id),
AffirmationStatus::Unknown
);
assert_eq!(
UserAffirmations::<TestStorage>::get(bob_default_portfolio, instruction_id),
AffirmationStatus::Unknown
);
assert_eq!(
InstructionStatuses::<TestStorage>::get(instruction_id),
InstructionStatuses::<TestStorage>::get(inst_id),
InstructionStatus::Success(1)
);
});
}

#[test]
fn execute_instruction_storage_rollback() {
fn storage_rollback() {
ExtBuilder::default().build().execute_with(|| {
System::set_block_number(1);

Expand Down Expand Up @@ -188,7 +154,7 @@ fn execute_instruction_storage_rollback() {
system_events.pop().unwrap().event,
crate::storage::EventTest::Settlement(Event::FailedToExecuteInstruction(
instruction_id,
Error::<TestStorage>::FailedToReleaseLockOrTransferAssets.into()
Error::<TestStorage>::FailedAssetTransferringConditions.into()
))
);
assert_eq!(
Expand Down
Loading