Repository: interlay/interbtc (language: rust)

Issue:
Forward griefing collateral of unfulfilled requests to the DAO
To prohibit vaults from blocking their mint capacity by placing but not fulfilling issue/replace request with themselves, the griefing collateral either has to be burned or sent to the DAO. If the griefing collateral stays with the vault and the vault and user are the same entity, there is no costs in doing this kind of exploit. 

**Describe the solution you'd like**
Send griefing collateral to the DAO.

**Describe alternatives you've considered**
Burn the griefing collateral.


Discussion / hints:

Fix patch:
diff --git a/crates/issue/src/lib.rs b/crates/issue/src/lib.rs
index 06bfd0a9c5..3c88968d66 100644
--- a/crates/issue/src/lib.rs
+++ b/crates/issue/src/lib.rs
@@ -32,11 +32,11 @@ pub use crate::types::{DefaultIssueRequest, IssueRequest, IssueRequestStatus};
 use crate::types::{BalanceOf, DefaultVaultId, Version};
 use btc_relay::{BtcAddress, BtcPublicKey};
 use currency::Amount;
-use frame_support::{dispatch::DispatchError, ensure, traits::Get, transactional};
+use frame_support::{dispatch::DispatchError, ensure, traits::Get, transactional, PalletId};
 use frame_system::{ensure_root, ensure_signed};
 pub use pallet::*;
 use sp_core::H256;
-use sp_runtime::traits::{Convert, Saturating};
+use sp_runtime::traits::{AccountIdConversion, Convert, Saturating};
 use sp_std::vec::Vec;
 use types::IssueRequestExt;
 use vault_registry::{types::CurrencyId, CurrencySource, VaultStatus};
@@ -57,6 +57,10 @@ pub mod pallet {
         + oracle::Config
         + fee::Config<UnsignedInner = BalanceOf<Self>>
     {
+        /// The treasury pallet account for slashed griefing collateral.
+        #[pallet::constant]
+        type TreasuryPalletId: Get<PalletId>;
+
         /// The overarching event type.
         type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
 
@@ -263,6 +267,10 @@ pub mod pallet {
 // "Internal" functions, callable by code.
 #[cfg_attr(test, mockable)]
 impl<T: Config> Pallet<T> {
+    pub fn treasury_account_id() -> T::AccountId {
+        T::TreasuryPalletId::get().into_account_truncating()
+    }
+
     /// Requests CBA issuance, returns unique tracking ID.
     fn _request_issue(
         requester: T::AccountId,
@@ -475,10 +483,11 @@ impl<T: Config> Pallet<T> {
             // return slashed griefing collateral if the vault is liquidated
             to_be_slashed_collateral.unlock_on(&issue.requester)?;
         } else {
-            // otherwise give all slashed griefing collateral to the vault
+            // otherwise give slashed griefing collateral to the treasury
+            // since the vault may have purposely blocked minting
             ext::vault_registry::transfer_funds::<T>(
                 CurrencySource::UserGriefing(issue.requester.clone()),
-                CurrencySource::FreeBalance(issue.vault.account_id.clone()),
+                CurrencySource::FreeBalance(Self::treasury_account_id()),
                 &to_be_slashed_collateral,
             )?;
         }
@@ -516,7 +525,7 @@ impl<T: Config> Pallet<T> {
         let slashed_collateral = issue.griefing_collateral().checked_sub(&to_release_collateral)?;
         ext::vault_registry::transfer_funds::<T>(
             CurrencySource::UserGriefing(issue.requester.clone()),
-            CurrencySource::FreeBalance(issue.vault.account_id.clone()),
+            CurrencySource::FreeBalance(Self::treasury_account_id()),
             &slashed_collateral,
         )?;
 
diff --git a/crates/issue/src/mock.rs b/crates/issue/src/mock.rs
index cf9468434a..ac17532c59 100644
--- a/crates/issue/src/mock.rs
+++ b/crates/issue/src/mock.rs
@@ -259,6 +259,10 @@ impl fee::Config for Test {
     type MaxExpectedValue = MaxExpectedValue;
 }
 
+parameter_types! {
+    pub const TreasuryPalletId: PalletId = PalletId(*b"mod/trsy");
+}
+
 pub struct BlockNumberToBalance;
 
 impl Convert<BlockNumber, Balance> for BlockNumberToBalance {
@@ -268,6 +272,7 @@ impl Convert<BlockNumber, Balance> for BlockNumberToBalance {
 }
 
 impl Config for Test {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();
diff --git a/parachain/runtime/interlay/src/lib.rs b/parachain/runtime/interlay/src/lib.rs
index cca8b76a19..3e5b794fac 100644
--- a/parachain/runtime/interlay/src/lib.rs
+++ b/parachain/runtime/interlay/src/lib.rs
@@ -1028,6 +1028,7 @@ impl fee::Config for Runtime {
 pub use issue::{Event as IssueEvent, IssueRequest};
 
 impl issue::Config for Runtime {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();
diff --git a/parachain/runtime/kintsugi/src/lib.rs b/parachain/runtime/kintsugi/src/lib.rs
index 192fca05f8..0cd2bbbffc 100644
--- a/parachain/runtime/kintsugi/src/lib.rs
+++ b/parachain/runtime/kintsugi/src/lib.rs
@@ -1028,6 +1028,7 @@ impl fee::Config for Runtime {
 pub use issue::{Event as IssueEvent, IssueRequest};
 
 impl issue::Config for Runtime {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();
diff --git a/standalone/runtime/src/lib.rs b/standalone/runtime/src/lib.rs
index 2010718263..cf3337183e 100644
--- a/standalone/runtime/src/lib.rs
+++ b/standalone/runtime/src/lib.rs
@@ -926,6 +926,7 @@ impl fee::Config for Runtime {
 pub use issue::{Event as IssueEvent, IssueRequest};
 
 impl issue::Config for Runtime {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();


Test patch:
diff --git a/crates/issue/src/tests.rs b/crates/issue/src/tests.rs
index d9edee3754..77b0fdd0eb 100644
--- a/crates/issue/src/tests.rs
+++ b/crates/issue/src/tests.rs
@@ -271,7 +271,7 @@ fn test_execute_issue_underpayment_succeeds() {
             ext::vault_registry::transfer_funds::<Test>.mock_raw(|from, to, amount| {
                 transfer_funds_called = true;
                 assert_eq!(from.account_id(), USER);
-                assert_eq!(to.account_id(), VAULT.account_id);
+                assert_eq!(to.account_id(), Issue::treasury_account_id());
                 // to_release_collateral = (griefing_collateral * amount_transferred) / expected_total_amount
                 // slashed_collateral = griefing_collateral - to_release_collateral
                 // 20 - (20 * 1 / 10) = 18
diff --git a/parachain/runtime/testnet-interlay/src/lib.rs b/parachain/runtime/testnet-interlay/src/lib.rs
index a8eddb274c..04fe7705de 100644
--- a/parachain/runtime/testnet-interlay/src/lib.rs
+++ b/parachain/runtime/testnet-interlay/src/lib.rs
@@ -997,6 +997,7 @@ impl fee::Config for Runtime {
 pub use issue::{Event as IssueEvent, IssueRequest};
 
 impl issue::Config for Runtime {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();
diff --git a/parachain/runtime/testnet-kintsugi/src/lib.rs b/parachain/runtime/testnet-kintsugi/src/lib.rs
index b4a0e6216b..39a200a9f8 100644
--- a/parachain/runtime/testnet-kintsugi/src/lib.rs
+++ b/parachain/runtime/testnet-kintsugi/src/lib.rs
@@ -999,6 +999,7 @@ impl fee::Config for Runtime {
 pub use issue::{Event as IssueEvent, IssueRequest};
 
 impl issue::Config for Runtime {
+    type TreasuryPalletId = TreasuryPalletId;
     type RuntimeEvent = RuntimeEvent;
     type BlockNumberToBalance = BlockNumberToBalance;
     type WeightInfo = ();
diff --git a/standalone/runtime/tests/test_issue.rs b/standalone/runtime/tests/test_issue.rs
index 43ad9df17e..6c13992b60 100644
--- a/standalone/runtime/tests/test_issue.rs
+++ b/standalone/runtime/tests/test_issue.rs
@@ -754,7 +754,7 @@ mod execute_pending_issue_tests {
                     // user loses 75% of griefing collateral for having only fulfilled 25%
                     (*user.balances.get_mut(&DEFAULT_GRIEFING_CURRENCY).unwrap()).locked -= issue.griefing_collateral();
                     (*user.balances.get_mut(&DEFAULT_GRIEFING_CURRENCY).unwrap()).free += returned_griefing_collateral;
-                    *vault.free_balance.get_mut(&DEFAULT_GRIEFING_CURRENCY).unwrap() += slashed_griefing_collateral;
+                    // TODO: check treasury balance includes griefing collateral
 
                     // token updating as if only 25% was requested
                     (*user.balances.get_mut(&vault_id.wrapped_currency()).unwrap()).free += issue.amount() / 4;
@@ -1095,7 +1095,7 @@ mod cancel_issue_tests {
                 ParachainState::get(&vault_id),
                 post_request_state.with_changes(|user, vault, _, _| {
                     (*user.balances.get_mut(&DEFAULT_GRIEFING_CURRENCY).unwrap()).locked -= issue.griefing_collateral();
-                    *vault.free_balance.get_mut(&DEFAULT_GRIEFING_CURRENCY).unwrap() += issue.griefing_collateral();
+                    // TODO: check treasury balance includes griefing collateral
                     vault.to_be_issued -= issue.amount() + issue.fee();
                 })
             );