Skip to content

Commit

Permalink
Merge branch 'develop' into pallet_bonded_coins
Browse files Browse the repository at this point in the history
  • Loading branch information
Ad96el authored Dec 11, 2024
2 parents ae280de + e5eb916 commit 9bf5df3
Show file tree
Hide file tree
Showing 17 changed files with 1,178 additions and 22 deletions.
8 changes: 8 additions & 0 deletions pallets/pallet-deposit-storage/src/deposit/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ impl ExtBuilder {
self
}

pub(crate) fn build_and_execute_with_sanity_tests(self, run: impl FnOnce()) {
let mut ext = self.build();
ext.execute_with(|| {
run();
crate::try_state::try_state::<TestRuntime>(System::block_number()).unwrap();
});
}

pub(crate) fn build(self) -> sp_io::TestExternalities {
let mut ext = sp_io::TestExternalities::default();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ fn on_commitment_removed_successful() {
reason: HoldReason::Deposit.into(),
},
)])
.build()
.execute_with(|| {
.build_and_execute_with_sanity_tests(|| {
assert_eq!(
Pallet::<TestRuntime>::deposits(&namespace, &key),
Some(DepositEntry {
Expand Down Expand Up @@ -103,8 +102,7 @@ fn on_commitment_removed_different_owner_successful() {
reason: HoldReason::Deposit.into(),
},
)])
.build()
.execute_with(|| {
.build_and_execute_with_sanity_tests(|| {
assert_eq!(
Pallet::<TestRuntime>::deposits(&namespace, &key),
Some(DepositEntry {
Expand Down Expand Up @@ -139,7 +137,7 @@ fn on_commitment_removed_different_owner_successful() {

#[test]
fn on_commitment_removed_deposit_not_found() {
ExtBuilder::default().build().execute_with(|| {
ExtBuilder::default().build_and_execute_with_sanity_tests(|| {
assert_noop!(
<DepositCollectorHook::<TestRuntime> as ProviderHooks<TestRuntime>>::on_commitment_removed(
&SUBJECT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ use crate::{
fn on_identity_committed_successful() {
ExtBuilder::default()
.with_balances(vec![(SUBMITTER, 100_000)])
.build()
.execute_with(|| {
.build_and_execute_with_sanity_tests(|| {
let namespace = DepositNamespaces::get();
let key: DepositKeyOf<TestRuntime> = (SUBJECT, SUBMITTER, 0 as IdentityCommitmentVersion)
.encode()
Expand Down Expand Up @@ -94,8 +93,7 @@ fn on_identity_committed_existing_deposit() {
},
},
)])
.build()
.execute_with(|| {
.build_and_execute_with_sanity_tests(|| {
assert_noop!(
<DepositCollectorHook::<TestRuntime> as ProviderHooks<TestRuntime>>::on_identity_committed(
&SUBJECT,
Expand All @@ -110,7 +108,7 @@ fn on_identity_committed_existing_deposit() {

#[test]
fn on_identity_committed_insufficient_balance() {
ExtBuilder::default().build().execute_with(|| {
ExtBuilder::default().build_and_execute_with_sanity_tests(|| {
assert_noop!(
<DepositCollectorHook::<TestRuntime> as ProviderHooks<TestRuntime>>::on_identity_committed(
&SUBJECT,
Expand Down
242 changes: 242 additions & 0 deletions pallets/pallet-deposit-storage/src/fungible/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// KILT Blockchain – https://botlabs.org
// Copyright (C) 2019-2024 BOTLabs GmbH

// The KILT Blockchain is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The KILT Blockchain is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

// If you feel like getting in touch with us, you can do so at [email protected]

use frame_support::traits::{
fungible::{Dust, Inspect, InspectHold, MutateHold, Unbalanced, UnbalancedHold},
tokens::{Fortitude, Precision, Preservation, Provenance, WithdrawConsequence},
};
use kilt_support::Deposit;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::{
traits::{CheckedSub, Zero},
DispatchError, DispatchResult,
};

use crate::{deposit::DepositEntry, Config, DepositKeyOf, Error, HoldReason, Pallet, SystemDeposits};

const LOG_TARGET: &str = "runtime::pallet_deposit_storage::mutate-hold";

#[cfg(test)]
mod tests;

#[cfg(any(test, feature = "try-runtime"))]
pub(super) mod try_state;

// This trait is implemented by forwarding everything to the `Currency`
// implementation.
impl<T> Inspect<T::AccountId> for Pallet<T>
where
T: Config,
{
type Balance = <T::Currency as Inspect<T::AccountId>>::Balance;

fn total_issuance() -> Self::Balance {
<T::Currency as Inspect<T::AccountId>>::total_issuance()
}

fn minimum_balance() -> Self::Balance {
<T::Currency as Inspect<T::AccountId>>::minimum_balance()
}

fn total_balance(who: &T::AccountId) -> Self::Balance {
<T::Currency as Inspect<T::AccountId>>::total_balance(who)
}

fn balance(who: &T::AccountId) -> Self::Balance {
<T::Currency as Inspect<T::AccountId>>::balance(who)
}

fn reducible_balance(who: &T::AccountId, preservation: Preservation, force: Fortitude) -> Self::Balance {
<T::Currency as Inspect<T::AccountId>>::reducible_balance(who, preservation, force)
}

fn can_deposit(
who: &T::AccountId,
amount: Self::Balance,
provenance: Provenance,
) -> frame_support::traits::tokens::DepositConsequence {
<T::Currency as Inspect<T::AccountId>>::can_deposit(who, amount, provenance)
}

fn can_withdraw(who: &T::AccountId, amount: Self::Balance) -> WithdrawConsequence<Self::Balance> {
<T::Currency as Inspect<T::AccountId>>::can_withdraw(who, amount)
}
}

/// The `HoldReason` consumers must use in order to rely on this pallet's
/// implementation of `MutateHold`.
#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, Clone, Copy, Debug, Default)]
pub struct PalletDepositStorageReason<Namespace, Key> {
pub(crate) namespace: Namespace,
pub(crate) key: Key,
}

impl<Namespace, Key> From<PalletDepositStorageReason<Namespace, Key>> for HoldReason {
fn from(_value: PalletDepositStorageReason<Namespace, Key>) -> Self {
// All the deposits ever taken like this will count towards the same hold
// reason.
Self::FungibleImpl
}
}

impl<T> InspectHold<T::AccountId> for Pallet<T>
where
T: Config,
{
type Reason = PalletDepositStorageReason<T::Namespace, DepositKeyOf<T>>;

fn total_balance_on_hold(who: &T::AccountId) -> Self::Balance {
<T::Currency as InspectHold<T::AccountId>>::total_balance_on_hold(who)
}

// We return the balance of hold of a specific (namespace, key), which prevents
// mistakes since everything in the end is converted to `HoldReason`.
fn balance_on_hold(reason: &Self::Reason, who: &T::AccountId) -> Self::Balance {
let Some(deposit_entry) = SystemDeposits::<T>::get(&reason.namespace, &reason.key) else {
return Zero::zero();
};
if deposit_entry.deposit.owner == *who {
deposit_entry.deposit.amount
} else {
Zero::zero()
}
}
}

// This trait is implemented by forwarding everything to the `Currency`
// implementation.
impl<T> Unbalanced<T::AccountId> for Pallet<T>
where
T: Config,
{
fn handle_dust(dust: Dust<T::AccountId, Self>) {
<T::Currency as Unbalanced<T::AccountId>>::handle_dust(Dust(dust.0));
}

fn write_balance(who: &T::AccountId, amount: Self::Balance) -> Result<Option<Self::Balance>, DispatchError> {
<T::Currency as Unbalanced<T::AccountId>>::write_balance(who, amount)
}

fn set_total_issuance(amount: Self::Balance) {
<T::Currency as Unbalanced<T::AccountId>>::set_total_issuance(amount);
}
}

impl<T> UnbalancedHold<T::AccountId> for Pallet<T>
where
T: Config,
{
/// This function gets called inside all invocations of `hold`, `release`,
/// and `release_all`. The value of `amount` is always the final amount that
/// should be kept on hold, meaning that a `release_all` will result in a
/// value of `0`. An hold increase from `1` to `2` will imply a value of
/// `amount` of `2`, i.e., the total amount held at the end of the
/// evaluation. Similarly, a decrease from `3` to `1` will imply a `amount`
/// value of `1`.
fn set_balance_on_hold(reason: &Self::Reason, who: &T::AccountId, amount: Self::Balance) -> DispatchResult {
let runtime_reason = T::RuntimeHoldReason::from(reason.clone().into());
SystemDeposits::<T>::try_mutate_exists(&reason.namespace, &reason.key, |maybe_existing_deposit_entry| {
match maybe_existing_deposit_entry {
// If this is the first registered deposit for this reason, create a new storage entry.
None => {
if amount > Zero::zero() {
// Increase the held amount for the final runtime hold reason by `amount`.
<T::Currency as UnbalancedHold<T::AccountId>>::increase_balance_on_hold(
&runtime_reason,
who,
amount,
Precision::Exact,
)?;
*maybe_existing_deposit_entry = Some(DepositEntry {
deposit: Deposit {
amount,
owner: who.clone(),
},
reason: runtime_reason,
})
}
Ok(())
}
Some(existing_deposit_entry) => {
// The pallet assumes each (namespace, key) points to a unique deposit, so the
// same combination cannot be used for a different account.
if existing_deposit_entry.deposit.owner != *who {
return Err(DispatchError::from(Error::<T>::DepositExisting));
}
if amount.is_zero() {
// If trying to remove all holds for this reason (`amount` == 0), decrease the
// held amount for the final runtime hold reason by the amount that was held by
// this reason.
<T::Currency as UnbalancedHold<T::AccountId>>::decrease_balance_on_hold(
&runtime_reason,
who,
existing_deposit_entry.deposit.amount,
Precision::Exact,
)?;
// Since we're setting the held amount to `0`, remove the storage entry.
*maybe_existing_deposit_entry = None;
// We're trying to update (i.e., not creating, not deleting)
// the held amount for this hold reason.
} else {
// If trying to increase the amount held, increase the held amount for the final
// runtime hold reason by the (amount - stored amount) difference.
if amount >= existing_deposit_entry.deposit.amount {
let amount_to_hold = amount
.checked_sub(&existing_deposit_entry.deposit.amount)
.ok_or_else(|| {
log::error!(target: LOG_TARGET, "Failed to evaluate {:?} - {:?}", amount, existing_deposit_entry.deposit.amount);
Error::<T>::Internal
})?;
<T::Currency as UnbalancedHold<T::AccountId>>::increase_balance_on_hold(
&runtime_reason,
who,
amount_to_hold,
Precision::Exact,
)?;
// Else, decrease the held amount for the final runtime
// hold reason by the (stored amount - amount)
// difference.
} else {
let amount_to_release = existing_deposit_entry
.deposit
.amount
.checked_sub(&amount)
.ok_or_else(|| {
log::error!(target: LOG_TARGET, "Failed to evaluate {:?} - {:?}", existing_deposit_entry.deposit.amount, amount);
Error::<T>::Internal
})?;
<T::Currency as UnbalancedHold<T::AccountId>>::decrease_balance_on_hold(
&runtime_reason,
who,
amount_to_release,
Precision::Exact,
)?;
}
// In either case, update the storage entry with the specified amount.
existing_deposit_entry.deposit.amount = amount;
}
Ok(())
}
}
})?;
Ok(())
}
}

impl<T> MutateHold<T::AccountId> for Pallet<T> where T: Config {}
Loading

0 comments on commit 9bf5df3

Please sign in to comment.