diff --git a/crates/pallet-domains/src/bundle_storage_fund.rs b/crates/pallet-domains/src/bundle_storage_fund.rs index 2cf2c1f7d2..76376668e3 100644 --- a/crates/pallet-domains/src/bundle_storage_fund.rs +++ b/crates/pallet-domains/src/bundle_storage_fund.rs @@ -27,6 +27,7 @@ pub enum Error { FailToDeposit, WithdrawAndHold, BalanceTransfer, + FailToWithdraw, } /// The type of system account being created. @@ -184,7 +185,7 @@ pub fn withdraw_and_hold( } let storage_fund_acc = storage_fund_account::(operator_id); - let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(operator_id); + let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(); T::Currency::transfer_and_hold( &storage_fund_hold_id, &storage_fund_acc, @@ -197,6 +198,27 @@ pub fn withdraw_and_hold( .map_err(|_| Error::WithdrawAndHold) } +/// Transfer the given `withdraw_amount` of balance from the bundle storage fund to the +/// given `dest_account` +pub fn withdraw_to( + operator_id: OperatorId, + dest_account: &T::AccountId, + withdraw_amount: BalanceOf, +) -> Result, Error> { + if withdraw_amount.is_zero() { + return Ok(Zero::zero()); + } + + let storage_fund_acc = storage_fund_account::(operator_id); + T::Currency::transfer( + &storage_fund_acc, + dest_account, + withdraw_amount, + Preservation::Expendable, + ) + .map_err(|_| Error::FailToWithdraw) +} + /// Return the total balance of the bundle storage fund the given `operator_id` pub fn total_balance(operator_id: OperatorId) -> BalanceOf { let storage_fund_acc = storage_fund_account::(operator_id); diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index 6a8b60b3a4..f0d8c8eaf9 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -147,6 +147,8 @@ pub struct DomainObject { pub domain_config: DomainConfig, /// Domain runtime specific information. pub domain_runtime_info: DomainRuntimeInfo, + /// The amount of balance hold on the domain owner account + pub domain_instantiation_deposit: Balance, } pub(crate) fn can_instantiate_domain( @@ -205,6 +207,7 @@ pub(crate) fn do_instantiate_domain( ) -> Result { can_instantiate_domain::(&owner_account_id, &domain_config)?; + let domain_instantiation_deposit = T::DomainInstantiationDeposit::get(); let domain_id = NextDomainId::::get(); let runtime_obj = RuntimeRegistry::::mutate(domain_config.runtime_id, |maybe_runtime_obj| { let mut runtime_object = maybe_runtime_obj @@ -277,17 +280,18 @@ pub(crate) fn do_instantiate_domain( genesis_receipt_hash, domain_config, domain_runtime_info, + domain_instantiation_deposit, }; DomainRegistry::::insert(domain_id, domain_obj); let next_domain_id = domain_id.checked_add(&1.into()).ok_or(Error::MaxDomainId)?; NextDomainId::::set(next_domain_id); - // Lock up fund of the domain instance creator + // Lock up `domain_instantiation_deposit` amount of fund of the domain instance creator T::Currency::hold( - &T::HoldIdentifier::domain_instantiation_id(domain_id), + &T::HoldIdentifier::domain_instantiation_id(), &owner_account_id, - T::DomainInstantiationDeposit::get(), + domain_instantiation_deposit, ) .map_err(|_| Error::BalanceFreeze)?; diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 47681d3360..6a6c327940 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -97,9 +97,9 @@ pub(crate) type FungibleHoldId = pub(crate) type NominatorId = ::AccountId; pub trait HoldIdentifier { - fn staking_staked(operator_id: OperatorId) -> FungibleHoldId; - fn domain_instantiation_id(domain_id: DomainId) -> FungibleHoldId; - fn storage_fund_withdrawal(operator_id: OperatorId) -> FungibleHoldId; + fn staking_staked() -> FungibleHoldId; + fn domain_instantiation_id() -> FungibleHoldId; + fn storage_fund_withdrawal() -> FungibleHoldId; } pub trait BlockSlot { @@ -545,6 +545,11 @@ mod pallet { OptionQuery, >; + /// The amount of balance the nominator hold for a given operator + #[pallet::storage] + pub(super) type DepositOnHold = + StorageMap<_, Identity, (OperatorId, NominatorId), BalanceOf, ValueQuery>; + /// Tracks the nominator count under given operator. /// This storage is necessary since CountedStorageNMap does not support prefix key count, so /// cannot use that storage type for `Nominators` storage. diff --git a/crates/pallet-domains/src/staking.rs b/crates/pallet-domains/src/staking.rs index 0fea28948c..701aa8eb2d 100644 --- a/crates/pallet-domains/src/staking.rs +++ b/crates/pallet-domains/src/staking.rs @@ -11,11 +11,11 @@ use crate::pallet::{ }; use crate::staking_epoch::{mint_funds, mint_into_treasury}; use crate::{ - BalanceOf, Config, DomainBlockNumberFor, Event, HoldIdentifier, NominatorId, + BalanceOf, Config, DepositOnHold, DomainBlockNumberFor, Event, HoldIdentifier, NominatorId, OperatorEpochSharePrice, Pallet, ReceiptHashFor, SlashedReason, }; use codec::{Decode, Encode}; -use frame_support::traits::fungible::{Inspect, InspectHold, MutateHold}; +use frame_support::traits::fungible::{Inspect, MutateHold}; use frame_support::traits::tokens::{Fortitude, Precision, Preservation}; use frame_support::{ensure, PalletError}; use scale_info::TypeInfo; @@ -122,7 +122,10 @@ pub(crate) struct Withdrawal { /// Total withdrawal amount requested by the nominator that are in unlocking state excluding withdrawal /// in shares and the storage fee pub(crate) total_withdrawal_amount: Balance, + /// Total amount of storage fee on withdraw (including withdrawal in shares) + pub(crate) total_storage_fee_withdrawal: Balance, /// Individual withdrawal amounts with their unlocking block for a given domain + // TODO: fixed number of withdrawal & uhlock all ready withdrawal at once pub(crate) withdrawals: VecDeque>, /// Withdrawal that was initiated by nominator and not converted to balance due to /// unfinished domain epoch. @@ -660,7 +663,14 @@ pub(crate) fn hold_deposit( Error::InsufficientBalance ); - let pending_deposit_hold_id = T::HoldIdentifier::staking_staked(operator_id); + DepositOnHold::::try_mutate((operator_id, who), |deposit_on_hold| { + *deposit_on_hold = deposit_on_hold + .checked_add(&amount) + .ok_or(Error::BalanceOverflow)?; + Ok(()) + })?; + + let pending_deposit_hold_id = T::HoldIdentifier::staking_staked(); T::Currency::hold(&pending_deposit_hold_id, who, amount).map_err(|_| Error::BalanceFreeze)?; Ok(()) @@ -952,6 +962,10 @@ pub(crate) fn do_withdraw_stake( }, }; withdrawal.withdrawal_in_shares = Some(new_withdrawal_in_shares); + withdrawal.total_storage_fee_withdrawal = withdrawal + .total_storage_fee_withdrawal + .checked_add(&withdraw_storage_fee) + .ok_or(Error::BalanceOverflow)?; *maybe_withdrawal = Some(withdrawal); Ok(()) @@ -997,28 +1011,40 @@ pub(crate) fn do_unlock_funds( .checked_sub(&amount_to_unlock) .ok_or(Error::BalanceUnderflow)?; - let staked_hold_id = T::HoldIdentifier::staking_staked(operator_id); - let locked_amount = T::Currency::balance_on_hold(&staked_hold_id, &nominator_id); - let amount_to_release: BalanceOf = { - // if the amount to release is more than currently locked, - // mint the diff and release the rest - if let Some(amount_to_mint) = amount_to_unlock.checked_sub(&locked_amount) { - // mint any gains - mint_funds::(&nominator_id, amount_to_mint)?; - locked_amount - } else { - amount_to_unlock - } - }; + withdrawal.total_storage_fee_withdrawal = withdrawal + .total_storage_fee_withdrawal + .checked_sub(&storage_fee_refund) + .ok_or(Error::BalanceUnderflow)?; + + // If the amount to release is more than currently locked, + // mint the diff and release the rest + let (amount_to_mint, amount_to_release) = DepositOnHold::::try_mutate( + (operator_id, nominator_id.clone()), + |deposit_on_hold| { + let amount_to_release = amount_to_unlock.min(*deposit_on_hold); + let amount_to_mint = amount_to_unlock.saturating_sub(*deposit_on_hold); + + *deposit_on_hold = deposit_on_hold.saturating_sub(amount_to_release); + Ok((amount_to_mint, amount_to_release)) + }, + )?; + + // Mint any gains + if !amount_to_mint.is_zero() { + mint_funds::(&nominator_id, amount_to_mint)?; + } // Release staking fund - T::Currency::release( - &staked_hold_id, - &nominator_id, - amount_to_release, - Precision::Exact, - ) - .map_err(|_| Error::RemoveLock)?; + if !amount_to_release.is_zero() { + let staked_hold_id = T::HoldIdentifier::staking_staked(); + T::Currency::release( + &staked_hold_id, + &nominator_id, + amount_to_release, + Precision::Exact, + ) + .map_err(|_| Error::RemoveLock)?; + } Pallet::::deposit_event(Event::NominatedStakedUnlocked { operator_id, @@ -1027,7 +1053,7 @@ pub(crate) fn do_unlock_funds( }); // Release storage fund - let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(operator_id); + let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(); T::Currency::release( &storage_fund_hold_id, &nominator_id, @@ -1097,39 +1123,38 @@ pub(crate) fn do_unlock_nominator( let share_price = SharePrice::new::(total_shares, total_stake); - let staked_hold_id = T::HoldIdentifier::staking_staked(operator_id); - let mut total_storage_fee_deposit = operator.total_storage_fee_deposit; let storage_fund_redeem_price = bundle_storage_fund::storage_fund_redeem_price::( operator_id, total_storage_fee_deposit, ); - let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(operator_id); let mut deposit = Deposits::::take(operator_id, nominator_id.clone()) .ok_or(Error::UnknownNominator)?; // convert any deposits from the previous epoch to shares do_convert_previous_epoch_deposits::(operator_id, &mut deposit)?; - let current_locked_amount = T::Currency::balance_on_hold(&staked_hold_id, &nominator_id); - // if there are any withdrawals from this operator, account for them // if the withdrawals has share price noted, then convert them to SSC // if no share price, then it must be intitated in the epoch before operator de-registered, // so get the shares as is and include them in the total staked shares. - let (amount_ready_to_withdraw, shares_withdrew_in_current_epoch) = - Withdrawals::::take(operator_id, nominator_id.clone()) - .map(|mut withdrawal| { - do_convert_previous_epoch_withdrawal::(operator_id, &mut withdrawal)?; - Ok(( - withdrawal.total_withdrawal_amount, - withdrawal - .withdrawal_in_shares - .map(|WithdrawalInShares { shares, .. }| shares) - .unwrap_or_default(), - )) - }) - .unwrap_or(Ok((Zero::zero(), Zero::zero())))?; + let ( + amount_ready_to_withdraw, + total_storage_fee_withdrawal, + shares_withdrew_in_current_epoch, + ) = Withdrawals::::take(operator_id, nominator_id.clone()) + .map(|mut withdrawal| { + do_convert_previous_epoch_withdrawal::(operator_id, &mut withdrawal)?; + Ok(( + withdrawal.total_withdrawal_amount, + withdrawal.total_storage_fee_withdrawal, + withdrawal + .withdrawal_in_shares + .map(|WithdrawalInShares { shares, .. }| shares) + .unwrap_or_default(), + )) + }) + .unwrap_or(Ok((Zero::zero(), Zero::zero(), Zero::zero())))?; // include all the known shares and shares that were withdrawn in the current epoch let nominator_shares = deposit @@ -1152,20 +1177,21 @@ pub(crate) fn do_unlock_nominator( .and_then(|amount| amount.checked_add(&amount_deposited_in_epoch)) .ok_or(Error::BalanceOverflow)?; - let amount_to_mint = total_amount_to_unlock - .checked_sub(¤t_locked_amount) - .unwrap_or(Zero::zero()); - - // remove the lock and mint any gains - mint_funds::(&nominator_id, amount_to_mint)?; - - T::Currency::release( - &staked_hold_id, - &nominator_id, - current_locked_amount, - Precision::Exact, - ) - .map_err(|_| Error::RemoveLock)?; + // Remove the lock and mint any gains + let current_locked_amount = DepositOnHold::::take((operator_id, nominator_id.clone())); + if let Some(amount_to_mint) = total_amount_to_unlock.checked_sub(¤t_locked_amount) { + mint_funds::(&nominator_id, amount_to_mint)?; + } + if !current_locked_amount.is_zero() { + let staked_hold_id = T::HoldIdentifier::staking_staked(); + T::Currency::release( + &staked_hold_id, + &nominator_id, + current_locked_amount, + Precision::Exact, + ) + .map_err(|_| Error::RemoveLock)?; + } Pallet::::deposit_event(Event::NominatedStakedUnlocked { operator_id, @@ -1184,22 +1210,27 @@ pub(crate) fn do_unlock_nominator( .checked_add(&deposit.known.storage_fee_deposit) .ok_or(Error::BalanceOverflow)?; - bundle_storage_fund::withdraw_and_hold::( + bundle_storage_fund::withdraw_to::( operator_id, &nominator_id, storage_fund_redeem_price.redeem(nominator_total_storage_fee_deposit), ) .map_err(Error::BundleStorageFund)?; - // Release all storage fee that of the nominator. - let storage_fee_refund = - T::Currency::release_all(&storage_fund_hold_id, &nominator_id, Precision::Exact) - .map_err(|_| Error::RemoveLock)?; + // Release all storage fee on withdraw of the nominator + let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(); + T::Currency::release( + &storage_fund_hold_id, + &nominator_id, + total_storage_fee_withdrawal, + Precision::Exact, + ) + .map_err(|_| Error::RemoveLock)?; Pallet::::deposit_event(Event::StorageFeeUnlocked { operator_id, nominator_id: nominator_id.clone(), - storage_fee: storage_fee_refund, + storage_fee: total_storage_fee_withdrawal, }); // reduce total storage fee deposit with nominator total fee deposit @@ -1464,6 +1495,7 @@ pub(crate) mod tests { genesis_receipt_hash: Default::default(), domain_config, domain_runtime_info: Default::default(), + domain_instantiation_deposit: Default::default(), }; DomainRegistry::::insert(domain_id, domain_obj); @@ -1860,6 +1892,7 @@ pub(crate) mod tests { genesis_receipt_hash: Default::default(), domain_config, domain_runtime_info: Default::default(), + domain_instantiation_deposit: Default::default(), }; DomainRegistry::::insert(new_domain_id, domain_obj); diff --git a/crates/pallet-domains/src/staking_epoch.rs b/crates/pallet-domains/src/staking_epoch.rs index aef345087e..09db7bd79e 100644 --- a/crates/pallet-domains/src/staking_epoch.rs +++ b/crates/pallet-domains/src/staking_epoch.rs @@ -10,13 +10,13 @@ use crate::staking::{ DomainEpoch, Error as TransitionError, OperatorStatus, SharePrice, WithdrawalInShares, }; use crate::{ - bundle_storage_fund, BalanceOf, Config, ElectionVerificationParams, Event, HoldIdentifier, - OperatorEpochSharePrice, Pallet, + bundle_storage_fund, BalanceOf, Config, DepositOnHold, ElectionVerificationParams, Event, + HoldIdentifier, OperatorEpochSharePrice, Pallet, }; #[cfg(not(feature = "std"))] use alloc::vec; use codec::{Decode, Encode}; -use frame_support::traits::fungible::{Inspect, InspectHold, Mutate, MutateHold}; +use frame_support::traits::fungible::{Inspect, Mutate, MutateHold}; use frame_support::traits::tokens::{ DepositConsequence, Fortitude, Precision, Provenance, Restriction, }; @@ -333,7 +333,7 @@ pub(crate) fn do_slash_operator( domain_id: DomainId, max_nominator_count: u32, ) -> Result { - let mut slashed_nominators = vec![]; + let mut slashed_nominator_count = 0u32; let (operator_id, slashed_operators) = match PendingSlashes::::get(domain_id) { None => return Ok(0), Some(mut slashed_operators) => match slashed_operators.pop_first() { @@ -354,7 +354,7 @@ pub(crate) fn do_slash_operator( let operator_owner = OperatorIdOwner::::get(operator_id).ok_or(TransitionError::UnknownOperator)?; - let staked_hold_id = T::HoldIdentifier::staking_staked(operator_id); + let staked_hold_id = T::HoldIdentifier::staking_staked(); let mut total_stake = operator .current_total_stake @@ -366,31 +366,34 @@ pub(crate) fn do_slash_operator( let share_price = SharePrice::new::(total_shares, total_stake); let mut total_storage_fee_deposit = operator.total_storage_fee_deposit; - let storage_fund_hold_id = T::HoldIdentifier::storage_fund_withdrawal(operator_id); // transfer all the staked funds to the treasury account // any gains will be minted to treasury account - for (nominator_id, mut deposit) in Deposits::::iter_prefix(operator_id) { - let locked_amount = T::Currency::balance_on_hold(&staked_hold_id, &nominator_id); + for (nominator_id, mut deposit) in Deposits::::drain_prefix(operator_id) { + let locked_amount = DepositOnHold::::take((operator_id, nominator_id.clone())); // convert any previous epoch deposits do_convert_previous_epoch_deposits::(operator_id, &mut deposit)?; // there maybe some withdrawals that are initiated in this epoch where operator was slashed // then collect and include them to find the final stake amount - let (amount_ready_to_withdraw, shares_withdrew_in_current_epoch) = - Withdrawals::::take(operator_id, nominator_id.clone()) - .map(|mut withdrawal| { - do_convert_previous_epoch_withdrawal::(operator_id, &mut withdrawal)?; - Ok(( - withdrawal.total_withdrawal_amount, - withdrawal - .withdrawal_in_shares - .map(|WithdrawalInShares { shares, .. }| shares) - .unwrap_or_default(), - )) - }) - .unwrap_or(Ok((Zero::zero(), Zero::zero())))?; + let ( + amount_ready_to_withdraw, + withdraw_storage_fee_on_hold, + shares_withdrew_in_current_epoch, + ) = Withdrawals::::take(operator_id, nominator_id.clone()) + .map(|mut withdrawal| { + do_convert_previous_epoch_withdrawal::(operator_id, &mut withdrawal)?; + Ok(( + withdrawal.total_withdrawal_amount, + withdrawal.total_storage_fee_withdrawal, + withdrawal + .withdrawal_in_shares + .map(|WithdrawalInShares { shares, .. }| shares) + .unwrap_or_default(), + )) + }) + .unwrap_or(Ok((Zero::zero(), Zero::zero(), Zero::zero())))?; // include all the known shares and shares that were withdrawn in the current epoch let nominator_shares = deposit @@ -402,14 +405,14 @@ pub(crate) fn do_slash_operator( // current staked amount let nominator_staked_amount = share_price.shares_to_stake::(nominator_shares); + let pending_deposit = deposit + .pending + .map(|pending_deposit| pending_deposit.amount) + .unwrap_or_default(); + // do not slash the deposit that is not staked yet let amount_to_slash_in_holding = locked_amount - .checked_sub( - &deposit - .pending - .map(|pending_deposit| pending_deposit.amount) - .unwrap_or_default(), - ) + .checked_sub(&pending_deposit) .ok_or(TransitionError::BalanceUnderflow)?; T::Currency::transfer_on_hold( @@ -423,6 +426,15 @@ pub(crate) fn do_slash_operator( ) .map_err(|_| TransitionError::RemoveLock)?; + // release rest of the deposited un staked amount back to nominator + T::Currency::release( + &staked_hold_id, + &nominator_id, + pending_deposit, + Precision::BestEffort, + ) + .map_err(|_| TransitionError::RemoveLock)?; + // these are nominator rewards that will be minted to treasury // include amount ready to be withdrawn to calculate the final reward let nominator_reward = nominator_staked_amount @@ -436,10 +448,6 @@ pub(crate) fn do_slash_operator( total_stake = total_stake.saturating_sub(nominator_staked_amount); total_shares = total_shares.saturating_sub(nominator_shares); - // release rest of the deposited un staked amount back to nominator - T::Currency::release_all(&staked_hold_id, &nominator_id, Precision::BestEffort) - .map_err(|_| TransitionError::RemoveLock)?; - // Transfer the deposited non-staked storage fee back to nominator if let Some(pending_deposit) = deposit.pending { let storage_fund_redeem_price = bundle_storage_fund::storage_fund_redeem_price::( @@ -447,31 +455,20 @@ pub(crate) fn do_slash_operator( total_storage_fee_deposit, ); - let storage_fee_deposit = bundle_storage_fund::withdraw_and_hold::( + bundle_storage_fund::withdraw_to::( operator_id, &nominator_id, storage_fund_redeem_price.redeem(pending_deposit.storage_fee_deposit), ) .map_err(TransitionError::BundleStorageFund)?; - T::Currency::release( - &storage_fund_hold_id, - &nominator_id, - storage_fee_deposit, - Precision::Exact, - ) - .map_err(|_| TransitionError::RemoveLock)?; - total_storage_fee_deposit = total_storage_fee_deposit.saturating_sub(pending_deposit.storage_fee_deposit); } // Transfer all the storage fee on withdraw to the treasury - let withdraw_storage_fee_on_hold = - T::Currency::balance_on_hold(&storage_fund_hold_id, &nominator_id); - T::Currency::transfer_on_hold( - &storage_fund_hold_id, + &T::HoldIdentifier::storage_fund_withdrawal(), &nominator_id, &T::TreasuryAccount::get(), withdraw_storage_fee_on_hold, @@ -487,18 +484,12 @@ pub(crate) fn do_slash_operator( NominatorCount::::set(operator_id, nominator_count - 1); } - slashed_nominators.push(nominator_id); - if slashed_nominators.len() as u32 >= max_nominator_count { + slashed_nominator_count += 1; + if slashed_nominator_count >= max_nominator_count { break; } } - // for all slashed nominators, remove their deposits - let slashed_nominator_count = slashed_nominators.len() as u32; - slashed_nominators.into_iter().for_each(|nominator_id| { - Deposits::::remove(operator_id, nominator_id); - }); - let nominator_count = NominatorCount::::get(operator_id); let cleanup_operator = nominator_count == 0 && !Deposits::::contains_key(operator_id, operator_owner); @@ -669,7 +660,7 @@ mod tests { assert_ok!(do_unlock_nominator::(operator_id, operator_account)); - let hold_id = crate::tests::HoldIdentifier::staking_staked(operator_id); + let hold_id = crate::tests::HoldIdentifierWrapper::staking_staked(); for (nominator_id, mut expected_usable_balance) in expected_usable_balances { expected_usable_balance += minimum_free_balance; assert_eq!(Deposits::::get(operator_id, nominator_id), None); diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 606130f2f9..8ca6980c7a 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -11,6 +11,7 @@ use crate::{ RuntimeRegistry, ScheduledRuntimeUpgrades, }; use codec::{Decode, Encode, MaxEncodedLen}; +use core::mem; use domain_runtime_primitives::opaque::Header as DomainHeader; use domain_runtime_primitives::BlockNumber as DomainBlockNumber; use frame_support::dispatch::{DispatchInfo, RawOrigin}; @@ -26,9 +27,9 @@ use sp_core::{Get, H256, U256}; use sp_domains::merkle_tree::MerkleTree; use sp_domains::storage::RawGenesis; use sp_domains::{ - BundleHeader, ChainId, DomainId, DomainsHoldIdentifier, ExecutionReceipt, InboxedBundle, - OpaqueBundle, OperatorAllowList, OperatorId, OperatorPair, ProofOfElection, RuntimeId, - RuntimeType, SealedBundleHeader, StakingHoldIdentifier, + BundleHeader, ChainId, DomainId, ExecutionReceipt, InboxedBundle, OpaqueBundle, + OperatorAllowList, OperatorId, OperatorPair, ProofOfElection, RuntimeId, RuntimeType, + SealedBundleHeader, }; use sp_domains_fraud_proof::fraud_proof::FraudProof; use sp_runtime::traits::{ @@ -38,7 +39,7 @@ use sp_runtime::transaction_validity::TransactionValidityError; use sp_runtime::{BuildStorage, OpaqueExtrinsic, Saturating}; use sp_version::RuntimeVersion; use subspace_core_primitives::U256 as P256; -use subspace_runtime_primitives::{Moment, StorageFee, SSC}; +use subspace_runtime_primitives::{HoldIdentifier, Moment, StorageFee, SSC}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -100,31 +101,24 @@ impl Get for ConfirmationDepthK { #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Domains(DomainsHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl pallet_domains::HoldIdentifier for HoldIdentifier { - fn staking_staked(operator_id: OperatorId) -> FungibleHoldId { - Self::Domains(DomainsHoldIdentifier::Staking( - StakingHoldIdentifier::Staked(operator_id), - )) +impl pallet_domains::HoldIdentifier for HoldIdentifierWrapper { + fn staking_staked() -> FungibleHoldId { + Self(HoldIdentifier::DomainStaking) } - fn domain_instantiation_id(domain_id: DomainId) -> FungibleHoldId { - Self::Domains(DomainsHoldIdentifier::DomainInstantiation(domain_id)) + fn domain_instantiation_id() -> FungibleHoldId { + Self(HoldIdentifier::DomainInstantiation) } - fn storage_fund_withdrawal(operator_id: OperatorId) -> Self { - Self::Domains(DomainsHoldIdentifier::StorageFund(operator_id)) + fn storage_fund_withdrawal() -> Self { + Self(HoldIdentifier::DomainStorageFund) } } -impl VariantCount for HoldIdentifier { - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 10; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } parameter_types! { @@ -136,7 +130,7 @@ impl pallet_balances::Config for Test { type Balance = Balance; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; type DustRemoval = (); } @@ -250,7 +244,7 @@ impl pallet_domains::Config for Test { type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type WeightInfo = pallet_domains::weights::SubstrateWeight; type InitialDomainTxRange = InitialDomainTxRange; type DomainTxRangeAdjustmentInterval = DomainTxRangeAdjustmentInterval; @@ -643,6 +637,7 @@ fn test_bundle_fromat_verification() { genesis_receipt_hash: Default::default(), domain_config, domain_runtime_info: Default::default(), + domain_instantiation_deposit: Default::default(), }; DomainRegistry::::insert(domain_id, domain_obj); diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 3495acf64c..1214e799ec 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -948,37 +948,9 @@ pub type EpochIndex = u32; /// Type representing operator ID pub type OperatorId = u64; -/// Staking specific hold identifier -#[derive( - PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, -)] -pub enum StakingHoldIdentifier { - /// Holds all the currently staked funds to an Operator. - Staked(OperatorId), -} - /// Channel identity. pub type ChannelId = sp_core::U256; -/// Messenger specific hold identifier -#[derive( - PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, -)] -pub enum MessengerHoldIdentifier { - /// Holds the current reserved balance for channel opening - Channel((ChainId, ChannelId)), -} - -/// Domains specific Identifier for Balances holds. -#[derive( - PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, -)] -pub enum DomainsHoldIdentifier { - Staking(StakingHoldIdentifier), - DomainInstantiation(DomainId), - StorageFund(OperatorId), -} - /// Domains specific digest item. #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] pub enum DomainDigestItem { diff --git a/crates/subspace-runtime-primitives/src/lib.rs b/crates/subspace-runtime-primitives/src/lib.rs index e14c02cca0..3e765e69bd 100644 --- a/crates/subspace-runtime-primitives/src/lib.rs +++ b/crates/subspace-runtime-primitives/src/lib.rs @@ -243,6 +243,17 @@ impl Default for BlockTransactionByteFee for HoldIdentifier { - fn staking_staked(operator_id: OperatorId) -> Self { - Self::Domains(DomainsHoldIdentifier::Staking( - StakingHoldIdentifier::Staked(operator_id), - )) +impl pallet_domains::HoldIdentifier for HoldIdentifierWrapper { + fn staking_staked() -> Self { + Self(HoldIdentifier::DomainStaking) } - fn domain_instantiation_id(domain_id: DomainId) -> Self { - Self::Domains(DomainsHoldIdentifier::DomainInstantiation(domain_id)) + fn domain_instantiation_id() -> Self { + Self(HoldIdentifier::DomainInstantiation) } - fn storage_fund_withdrawal(operator_id: OperatorId) -> Self { - Self::Domains(DomainsHoldIdentifier::StorageFund(operator_id)) + fn storage_fund_withdrawal() -> Self { + Self(HoldIdentifier::DomainStorageFund) } } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } -impl VariantCount for HoldIdentifier { - // TODO: revist this value, it is used as the max number of hold an account can - // create. Currently, nomination an operator will create 2 holds and opening an - // XDM channel will create 1 hold, so this value also used as the limit of how - // many operator/channel an account can nominate/open. - // - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 100; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } impl pallet_balances::Config for Runtime { @@ -381,7 +369,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } parameter_types! { @@ -471,7 +459,7 @@ impl pallet_collective::Config for Runtime { parameter_types! { pub PreimageBaseDeposit: Balance = 100 * SSC; pub PreimageByteDeposit: Balance = SSC; - pub const PreImageHoldReason: HoldIdentifier = HoldIdentifier::Preimage; + pub const PreImageHoldReason: HoldIdentifierWrapper = HoldIdentifierWrapper(HoldIdentifier::Preimage); } impl pallet_preimage::Config for Runtime { @@ -705,7 +693,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = Domains; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = DomainRegistration; @@ -828,7 +816,7 @@ impl pallet_domains::Config for Runtime { type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type WeightInfo = pallet_domains::weights::SubstrateWeight; type InitialDomainTxRange = InitialDomainTxRange; type DomainTxRangeAdjustmentInterval = DomainTxRangeAdjustmentInterval; diff --git a/domains/pallets/messenger/src/benchmarking.rs b/domains/pallets/messenger/src/benchmarking.rs index 2a933f2f80..8616d2b3c4 100644 --- a/domains/pallets/messenger/src/benchmarking.rs +++ b/domains/pallets/messenger/src/benchmarking.rs @@ -19,6 +19,7 @@ use sp_messenger::messages::{ RequestResponse, VersionedPayload, }; use sp_mmr_primitives::{EncodableOpaqueLeaf, LeafProof as MmrProof}; +use sp_runtime::traits::Zero; use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_trie::StorageProof; #[cfg(feature = "std")] @@ -87,6 +88,7 @@ mod benchmarks { dummy_channel_params::(), None, true, + Zero::zero(), )); let channel = Channels::::get(dst_chain_id, channel_id).expect("channel should exist"); assert_eq!(channel.state, ChannelState::Initiated); @@ -255,7 +257,8 @@ mod benchmarks { dst_chain_id, params, None, - true + true, + Zero::zero(), )); let channel = Channels::::get(dst_chain_id, channel_id).expect("channel should exist"); assert_eq!(channel.state, ChannelState::Initiated); diff --git a/domains/pallets/messenger/src/lib.rs b/domains/pallets/messenger/src/lib.rs index be360aaf73..4c01990382 100644 --- a/domains/pallets/messenger/src/lib.rs +++ b/domains/pallets/messenger/src/lib.rs @@ -106,7 +106,7 @@ pub struct InitiateChannelParams { /// Hold identifier trait for messenger specific balance holds pub trait HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> FungibleHoldId; + fn messenger_channel() -> FungibleHoldId; } #[frame_support::pallet] @@ -142,6 +142,7 @@ mod pallet { DomainRegistration, InherentError, InherentType, OnXDMRewards, StorageKeys, INHERENT_IDENTIFIER, }; + use sp_runtime::traits::Zero; use sp_runtime::{ArithmeticError, Perbill, Saturating}; use sp_subspace_mmr::MmrProofVerifier; #[cfg(feature = "std")] @@ -541,6 +542,9 @@ mod pallet { /// Failed to unlock the balance BalanceUnlock, + + /// Invalid channel reserve fee + InvalidChannelReserveFee, } #[pallet::call] @@ -556,21 +560,9 @@ mod pallet { params: InitiateChannelParams, ) -> DispatchResult { let owner = ensure_signed(origin)?; - let channel_open_params = ChannelOpenParams { - max_outgoing_messages: params.max_outgoing_messages, - fee_model: T::ChannelFeeModel::get(), - }; - - // initiate the channel config - let channel_id = Self::do_init_channel( - dst_chain_id, - channel_open_params, - Some(owner.clone()), - true, - )?; // reserve channel open fees - let hold_id = T::HoldIdentifier::messenger_channel(dst_chain_id, channel_id); + let hold_id = T::HoldIdentifier::messenger_channel(); let amount = T::ChannelReserveFee::get(); // ensure there is enough free balance to lock @@ -581,6 +573,19 @@ mod pallet { ); T::Currency::hold(&hold_id, &owner, amount).map_err(|_| Error::::BalanceHold)?; + // initiate the channel config + let channel_open_params = ChannelOpenParams { + max_outgoing_messages: params.max_outgoing_messages, + fee_model: T::ChannelFeeModel::get(), + }; + let channel_id = Self::do_init_channel( + dst_chain_id, + channel_open_params, + Some(owner.clone()), + true, + amount, + )?; + // send message to dst_chain Self::new_outbox_message( T::SelfChainId::get(), @@ -864,7 +869,8 @@ mod pallet { fee_model, }; ChainAllowlist::::mutate(|list| list.insert(dst_chain_id)); - let channel_id = Self::do_init_channel(dst_chain_id, init_params, None, true)?; + let channel_id = + Self::do_init_channel(dst_chain_id, init_params, None, true, Zero::zero())?; Self::do_open_channel(dst_chain_id, channel_id)?; Ok(()) } @@ -953,8 +959,8 @@ mod pallet { } if let Some(owner) = &channel.maybe_owner { - let hold_id = T::HoldIdentifier::messenger_channel(chain_id, channel_id); - let locked_amount = T::Currency::balance_on_hold(&hold_id, owner); + let hold_id = T::HoldIdentifier::messenger_channel(); + let locked_amount = channel.channel_reserve_fee; let amount_to_release = { if channel.state == ChannelState::Open { locked_amount @@ -993,12 +999,20 @@ mod pallet { init_params: ChannelOpenParams>, maybe_owner: Option, check_allowlist: bool, + channel_reserve_fee: BalanceOf, ) -> Result { ensure!( T::SelfChainId::get() != dst_chain_id, Error::::InvalidChain, ); + // If the channel owner is in this chain then the channel reserve fee + // must not be empty + ensure!( + maybe_owner.is_none() || !channel_reserve_fee.is_zero(), + Error::::InvalidChannelReserveFee, + ); + if check_allowlist { let chain_allowlist = ChainAllowlist::::get(); ensure!( @@ -1024,6 +1038,7 @@ mod pallet { max_outgoing_messages: init_params.max_outgoing_messages, fee: init_params.fee_model, maybe_owner, + channel_reserve_fee, }, ); @@ -1144,8 +1159,8 @@ mod pallet { // channel is being opened without an owner since this is a relay message // from other chain // we do not check the allowlist to finish the end to end flow - Self::do_init_channel(msg.src_chain_id, params, None, false).map_err( - |err| { + Self::do_init_channel(msg.src_chain_id, params, None, false, Zero::zero()) + .map_err(|err| { log::error!( "Error initiating channel: {:?} with chain: {:?}: {:?}", msg.channel_id, @@ -1153,8 +1168,7 @@ mod pallet { err ); InvalidTransaction::Call - }, - )?; + })?; } else { log::error!("Unexpected call instead of channel open request: {:?}", msg,); return Err(InvalidTransaction::Call.into()); diff --git a/domains/pallets/messenger/src/mock.rs b/domains/pallets/messenger/src/mock.rs index 73f458c3b4..d6cd143682 100644 --- a/domains/pallets/messenger/src/mock.rs +++ b/domains/pallets/messenger/src/mock.rs @@ -21,7 +21,7 @@ macro_rules! impl_runtime { use crate::mock::MockEndpoint; use crate::mock::{AccountId, Balance, MessageId, TestExternalities}; use codec::{Decode, Encode}; - use domain_runtime_primitives::{MultiAccountId, TryConvertBack}; + use domain_runtime_primitives::{MultiAccountId, TryConvertBack, HoldIdentifier}; #[cfg(not(feature = "runtime-benchmarks"))] use frame_support::pallet_prelude::*; use frame_support::{derive_impl, parameter_types}; @@ -31,11 +31,8 @@ macro_rules! impl_runtime { use sp_messenger::messages::{ChainId, FeeModel}; use sp_runtime::traits::Convert; use sp_runtime::BuildStorage; - use crate::HoldIdentifier; - use sp_domains::ChannelId; use scale_info::TypeInfo; use codec::MaxEncodedLen; - use sp_domains::MessengerHoldIdentifier; use frame_support::traits::VariantCount; use core::mem; use sp_runtime::Perbill; @@ -69,16 +66,16 @@ macro_rules! impl_runtime { PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] pub enum MockHoldIdentifer { - Messenger(MessengerHoldIdentifier) + Messenger(HoldIdentifier) } impl VariantCount for MockHoldIdentifer { - const VARIANT_COUNT: u32 = mem::variant_count::() as u32; + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } - impl HoldIdentifier<$runtime> for MockHoldIdentifer { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - MockHoldIdentifer::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) + impl crate::HoldIdentifier<$runtime> for MockHoldIdentifer { + fn messenger_channel() -> Self { + MockHoldIdentifer::Messenger(HoldIdentifier::MessengerChannel) } } diff --git a/domains/pallets/messenger/src/tests.rs b/domains/pallets/messenger/src/tests.rs index 35569a425f..8559c4db3a 100644 --- a/domains/pallets/messenger/src/tests.rs +++ b/domains/pallets/messenger/src/tests.rs @@ -25,7 +25,7 @@ use sp_messenger::messages::{ ProtocolMessageRequest, RequestResponse, VersionedPayload, }; use sp_mmr_primitives::{EncodableOpaqueLeaf, LeafProof as MmrProof}; -use sp_runtime::traits::Convert; +use sp_runtime::traits::{Convert, Zero}; use sp_subspace_mmr::ConsensusChainMmrLeafProof; use sp_trie::StorageProof; use std::collections::BTreeSet; @@ -490,8 +490,14 @@ fn force_toggle_channel_state( if add_to_allow_list { ChainAllowlist::::put(list); } - Pallet::::do_init_channel(dst_chain_id, init_params, None, add_to_allow_list) - .unwrap(); + Pallet::::do_init_channel( + dst_chain_id, + init_params, + None, + add_to_allow_list, + Zero::zero(), + ) + .unwrap(); Pallet::::channels(dst_chain_id, channel_id).unwrap() }); @@ -765,6 +771,7 @@ fn test_update_consensus_channel_allowlist() { relay_fee: Default::default(), }, maybe_owner: None, + channel_reserve_fee: Default::default(), }), ); }); diff --git a/domains/pallets/transporter/src/mock.rs b/domains/pallets/transporter/src/mock.rs index 35278ad4b9..cf99ce9426 100644 --- a/domains/pallets/transporter/src/mock.rs +++ b/domains/pallets/transporter/src/mock.rs @@ -1,16 +1,15 @@ use crate as pallet_transporter; use crate::{Config, TryConvertBack}; use codec::{Decode, Encode}; -use domain_runtime_primitives::MultiAccountId; +use domain_runtime_primitives::{HoldIdentifier, MultiAccountId}; use frame_support::pallet_prelude::{MaxEncodedLen, TypeInfo}; use frame_support::traits::VariantCount; use frame_support::{derive_impl, parameter_types}; use pallet_balances::AccountData; -use pallet_messenger::HoldIdentifier; use sp_core::U256; -use sp_domains::{DomainId, MessengerHoldIdentifier}; +use sp_domains::DomainId; use sp_messenger::endpoint::{Endpoint, EndpointHandler, EndpointId, EndpointRequest, Sender}; -use sp_messenger::messages::{ChainId, ChannelId, FeeModel, MessageId}; +use sp_messenger::messages::{ChainId, FeeModel, MessageId}; use sp_runtime::traits::{Convert, IdentityLookup}; use sp_runtime::{BuildStorage, DispatchError, Perbill}; @@ -61,7 +60,7 @@ parameter_types! { PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] pub enum MockHoldIdentifer { - Messenger(MessengerHoldIdentifier), + Messenger(HoldIdentifier), } impl VariantCount for MockHoldIdentifer { @@ -76,9 +75,9 @@ impl sp_messenger::DomainRegistration for DomainRegistration { } } -impl HoldIdentifier for MockHoldIdentifer { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - MockHoldIdentifer::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for MockHoldIdentifer { + fn messenger_channel() -> Self { + MockHoldIdentifer::Messenger(HoldIdentifier::MessengerChannel) } } diff --git a/domains/primitives/messenger/src/messages.rs b/domains/primitives/messenger/src/messages.rs index 84f00b475c..e353399ab8 100644 --- a/domains/primitives/messenger/src/messages.rs +++ b/domains/primitives/messenger/src/messages.rs @@ -61,6 +61,8 @@ pub struct Channel { /// Owner of the channel /// Owner maybe None if the channel was initiated on the other chain. pub maybe_owner: Option, + /// The amount of funds put on hold by the owner account for this channel + pub channel_reserve_fee: Balance, } /// Channel open parameters diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 02cb53e935..cff7e01485 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -37,6 +37,7 @@ use sp_runtime::transaction_validity::TransactionValidityError; use sp_runtime::{MultiAddress, MultiSignature, Perbill}; use sp_weights::constants::WEIGHT_REF_TIME_PER_SECOND; use sp_weights::Weight; +pub use subspace_runtime_primitives::HoldIdentifier; use subspace_runtime_primitives::{MAX_BLOCK_LENGTH, SHANNON, SLOT_PROBABILITY}; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index 8e2d4ed0ff..4cfde5ace7 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -13,14 +13,15 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::format; use codec::{Decode, Encode, MaxEncodedLen}; +use core::mem; use domain_runtime_primitives::opaque::Header; pub use domain_runtime_primitives::{ block_weights, maximum_block_length, opaque, Balance, BlockNumber, Hash, Nonce, EXISTENTIAL_DEPOSIT, }; use domain_runtime_primitives::{ - AccountId, Address, CheckExtrinsicsValidityError, DecodeExtrinsicError, Signature, - ERR_BALANCE_OVERFLOW, SLOT_DURATION, + AccountId, Address, CheckExtrinsicsValidityError, DecodeExtrinsicError, HoldIdentifier, + Signature, ERR_BALANCE_OVERFLOW, SLOT_DURATION, }; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo}; use frame_support::genesis_builder_helper::{build_state, get_preset}; @@ -39,7 +40,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata}; -use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, MessengerHoldIdentifier, Transfers}; +use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, FeeModel, MessageId, MessageKey, @@ -227,7 +228,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } parameter_types! { @@ -374,24 +375,15 @@ impl sp_messenger::StorageKeys for StorageKeys { #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Messenger(MessengerHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl VariantCount for HoldIdentifier { - // TODO: revist this value, it is used as the max number of hold an account can - // create. Currently, opening an XDM channel will create 1 hold, so this value - // also used as the limit of how many channel an account can open. - // - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 100; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } @@ -422,7 +414,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = (); - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = (); diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index ea709c4b96..7242a2ed6b 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -15,14 +15,15 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::format; use codec::{Decode, Encode, MaxEncodedLen}; +use core::mem; use domain_runtime_primitives::opaque::Header; pub use domain_runtime_primitives::{ block_weights, maximum_block_length, maximum_domain_block_weight, opaque, Balance, BlockNumber, Hash, Nonce, EXISTENTIAL_DEPOSIT, }; use domain_runtime_primitives::{ - CheckExtrinsicsValidityError, DecodeExtrinsicError, ERR_BALANCE_OVERFLOW, ERR_NONCE_OVERFLOW, - SLOT_DURATION, + CheckExtrinsicsValidityError, DecodeExtrinsicError, HoldIdentifier, ERR_BALANCE_OVERFLOW, + ERR_NONCE_OVERFLOW, SLOT_DURATION, }; use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; @@ -52,7 +53,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata, H160, H256, U256}; -use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, MessengerHoldIdentifier, Transfers}; +use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, FeeModel, MessageId, MessageKey, @@ -359,7 +360,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } parameter_types! { @@ -503,24 +504,15 @@ impl sp_messenger::StorageKeys for StorageKeys { #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Messenger(MessengerHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl VariantCount for HoldIdentifier { - // TODO: revist this value, it is used as the max number of hold an account can - // create. Currently, opening an XDM channel will create 1 hold, so this value - // also used as the limit of how many channel an account can open. - // - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 100; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } @@ -551,7 +543,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = (); - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = (); diff --git a/domains/test/runtime/auto-id/src/lib.rs b/domains/test/runtime/auto-id/src/lib.rs index 09a8b226bd..d549d9c1b5 100644 --- a/domains/test/runtime/auto-id/src/lib.rs +++ b/domains/test/runtime/auto-id/src/lib.rs @@ -13,13 +13,15 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::format; use codec::{Decode, Encode, MaxEncodedLen}; +use core::mem; use domain_runtime_primitives::opaque::Header; pub use domain_runtime_primitives::{ block_weights, maximum_block_length, opaque, AccountId, Address, Balance, BlockNumber, Hash, Nonce, Signature, EXISTENTIAL_DEPOSIT, }; use domain_runtime_primitives::{ - CheckExtrinsicsValidityError, DecodeExtrinsicError, ERR_BALANCE_OVERFLOW, SLOT_DURATION, + CheckExtrinsicsValidityError, DecodeExtrinsicError, HoldIdentifier, ERR_BALANCE_OVERFLOW, + SLOT_DURATION, }; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo}; use frame_support::genesis_builder_helper::{build_state, get_preset}; @@ -38,7 +40,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata}; -use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, MessengerHoldIdentifier, Transfers}; +use sp_domains::{ChannelId, DomainAllowlistUpdates, DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, FeeModel, MessageId, MessageKey, @@ -226,7 +228,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } parameter_types! { @@ -372,24 +374,15 @@ impl sp_messenger::StorageKeys for StorageKeys { #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Messenger(MessengerHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl VariantCount for HoldIdentifier { - // TODO: revist this value, it is used as the max number of hold an account can - // create. Currently, opening an XDM channel will create 1 hold, so this value - // also used as the limit of how many channel an account can open. - // - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 100; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } @@ -420,7 +413,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = (); - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = (); diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 907ce2ceaf..cd3aaf4a9f 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -15,13 +15,15 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::format; use codec::{Decode, Encode, MaxEncodedLen}; +use core::mem; pub use domain_runtime_primitives::opaque::Header; use domain_runtime_primitives::{ block_weights, maximum_block_length, maximum_domain_block_weight, ERR_BALANCE_OVERFLOW, ERR_NONCE_OVERFLOW, EXISTENTIAL_DEPOSIT, SLOT_DURATION, }; pub use domain_runtime_primitives::{ - opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, Nonce, + opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, + HoldIdentifier, Nonce, }; use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; @@ -50,7 +52,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata, H160, H256, U256}; -use sp_domains::{DomainAllowlistUpdates, DomainId, MessengerHoldIdentifier, Transfers}; +use sp_domains::{DomainAllowlistUpdates, DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockMessagesWithStorageKey, ChainId, ChannelId, CrossDomainMessage, FeeModel, MessageId, @@ -348,7 +350,7 @@ impl pallet_balances::Config for Runtime { type ReserveIdentifier = [u8; 8]; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } parameter_types! { @@ -467,20 +469,15 @@ impl sp_subspace_mmr::MmrProofVerifier, Hash> for MmrP #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Messenger(MessengerHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl VariantCount for HoldIdentifier { - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 10; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } @@ -532,7 +529,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = (); - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = (); diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 094ecf9952..0d7d9721b5 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -27,6 +27,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use codec::{Compact, CompactLen, Decode, Encode, MaxEncodedLen}; +use core::mem; use core::num::NonZeroU64; use domain_runtime_primitives::opaque::Header as DomainHeader; use domain_runtime_primitives::{ @@ -56,9 +57,8 @@ use sp_core::crypto::KeyTypeId; use sp_core::{OpaqueMetadata, H256}; use sp_domains::bundle_producer_election::BundleProducerElectionParams; use sp_domains::{ - DomainAllowlistUpdates, DomainId, DomainInstanceData, DomainsHoldIdentifier, - ExecutionReceiptFor, MessengerHoldIdentifier, OpaqueBundle, OpaqueBundles, OperatorId, - OperatorPublicKey, StakingHoldIdentifier, DOMAIN_STORAGE_FEE_MULTIPLIER, + DomainAllowlistUpdates, DomainId, DomainInstanceData, ExecutionReceiptFor, OpaqueBundle, + OpaqueBundles, OperatorId, OperatorPublicKey, DOMAIN_STORAGE_FEE_MULTIPLIER, INITIAL_DOMAIN_TX_RANGE, }; use sp_domains_fraud_proof::fraud_proof::FraudProof; @@ -95,8 +95,8 @@ use subspace_core_primitives::{ SegmentIndex, SlotNumber, SolutionRange, U256, }; use subspace_runtime_primitives::{ - AccountId, Balance, BlockNumber, FindBlockRewardAddress, Hash, Moment, Nonce, Signature, - MIN_REPLICATION_FACTOR, + AccountId, Balance, BlockNumber, FindBlockRewardAddress, Hash, HoldIdentifier, Moment, Nonce, + Signature, MIN_REPLICATION_FACTOR, }; sp_runtime::impl_opaque_keys! { @@ -327,38 +327,30 @@ impl pallet_timestamp::Config for Runtime { #[derive( PartialEq, Eq, Clone, Encode, Decode, TypeInfo, MaxEncodedLen, Ord, PartialOrd, Copy, Debug, )] -pub enum HoldIdentifier { - Domains(DomainsHoldIdentifier), - Messenger(MessengerHoldIdentifier), -} +pub struct HoldIdentifierWrapper(HoldIdentifier); -impl pallet_domains::HoldIdentifier for HoldIdentifier { - fn staking_staked(operator_id: OperatorId) -> Self { - Self::Domains(DomainsHoldIdentifier::Staking( - StakingHoldIdentifier::Staked(operator_id), - )) +impl pallet_domains::HoldIdentifier for HoldIdentifierWrapper { + fn staking_staked() -> Self { + Self(HoldIdentifier::DomainStaking) } - fn domain_instantiation_id(domain_id: DomainId) -> Self { - Self::Domains(DomainsHoldIdentifier::DomainInstantiation(domain_id)) + fn domain_instantiation_id() -> Self { + Self(HoldIdentifier::DomainInstantiation) } - fn storage_fund_withdrawal(operator_id: OperatorId) -> Self { - Self::Domains(DomainsHoldIdentifier::StorageFund(operator_id)) + fn storage_fund_withdrawal() -> Self { + Self(HoldIdentifier::DomainStorageFund) } } -impl pallet_messenger::HoldIdentifier for HoldIdentifier { - fn messenger_channel(dst_chain_id: ChainId, channel_id: ChannelId) -> Self { - Self::Messenger(MessengerHoldIdentifier::Channel((dst_chain_id, channel_id))) +impl pallet_messenger::HoldIdentifier for HoldIdentifierWrapper { + fn messenger_channel() -> Self { + Self(HoldIdentifier::MessengerChannel) } } -impl VariantCount for HoldIdentifier { - // TODO: HACK this is not the actual variant count but it is required see - // https://github.com/autonomys/subspace/issues/2674 for more details. It - // will be resolved as https://github.com/paritytech/polkadot-sdk/issues/4033. - const VARIANT_COUNT: u32 = 10; +impl VariantCount for HoldIdentifierWrapper { + const VARIANT_COUNT: u32 = mem::variant_count::() as u32; } impl pallet_balances::Config for Runtime { @@ -377,7 +369,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = HoldIdentifier; + type RuntimeHoldReason = HoldIdentifierWrapper; } pub struct CreditSupply; @@ -631,7 +623,7 @@ impl pallet_messenger::Config for Runtime { type MmrProofVerifier = MmrProofVerifier; type StorageKeys = StorageKeys; type DomainOwner = Domains; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type ChannelReserveFee = ChannelReserveFee; type ChannelInitReservePortion = ChannelInitReservePortion; type DomainRegistration = DomainRegistration; @@ -744,7 +736,7 @@ impl pallet_domains::Config for Runtime { type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldIdentifierWrapper; type WeightInfo = pallet_domains::weights::SubstrateWeight; type InitialDomainTxRange = InitialDomainTxRange; type DomainTxRangeAdjustmentInterval = DomainTxRangeAdjustmentInterval;