From 73f4bc8443fef38576f22c7014b7ec81bcd75fb8 Mon Sep 17 00:00:00 2001
From: Maxim Shishmarev <msgmaxim@gmail.com>
Date: Tue, 3 Oct 2023 11:26:34 +1100
Subject: [PATCH] Feat: don't include dust btc amounts on rotation (#4063)

* feat: don't include dust btc amounts on rotation

* chore: return None early on empty utxo list

---------

Co-authored-by: dandanlen <3168260+dandanlen@users.noreply.github.com>
---
 state-chain/pallets/cf-environment/src/lib.rs | 32 +++++++++++--------
 .../pallets/cf-environment/src/tests.rs       |  6 ++++
 2 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/state-chain/pallets/cf-environment/src/lib.rs b/state-chain/pallets/cf-environment/src/lib.rs
index 17fb26a09a..e84c483d2b 100644
--- a/state-chain/pallets/cf-environment/src/lib.rs
+++ b/state-chain/pallets/cf-environment/src/lib.rs
@@ -424,20 +424,24 @@ impl<T: Config> Pallet<T> {
 			T::BitcoinFeeInfo::bitcoin_fee_info();
 		match utxo_selection_type {
 			UtxoSelectionType::SelectAllForRotation => {
-				let available_utxos = BitcoinAvailableUtxos::<T>::take();
-				(!available_utxos.is_empty()).then_some(available_utxos).and_then(
-					|available_utxos| {
-						available_utxos
-							.iter()
-							.map(|Utxo { amount, .. }| *amount)
-							.sum::<u64>()
-							.checked_sub(
-								((available_utxos.len() as u64) * fee_per_input_utxo) +
-									fee_per_output_utxo + min_fee_required_per_tx,
-							)
-							.map(|change_amount| (available_utxos, change_amount))
-					},
-				)
+				let spendable_utxos: Vec<_> = BitcoinAvailableUtxos::<T>::take()
+					.into_iter()
+					.filter(|utxo| utxo.amount > fee_per_input_utxo)
+					.collect();
+
+				if spendable_utxos.is_empty() {
+					return None
+				}
+
+				let total_fee = spendable_utxos.len() as u64 * fee_per_input_utxo +
+					fee_per_output_utxo + min_fee_required_per_tx;
+
+				spendable_utxos
+					.iter()
+					.map(|utxo| utxo.amount)
+					.sum::<u64>()
+					.checked_sub(total_fee)
+					.map(|change_amount| (spendable_utxos, change_amount))
 			},
 			UtxoSelectionType::Some { output_amount, number_of_outputs } =>
 				BitcoinAvailableUtxos::<T>::try_mutate(|available_utxos| {
diff --git a/state-chain/pallets/cf-environment/src/tests.rs b/state-chain/pallets/cf-environment/src/tests.rs
index e3aa2754b5..058343229d 100644
--- a/state-chain/pallets/cf-environment/src/tests.rs
+++ b/state-chain/pallets/cf-environment/src/tests.rs
@@ -46,6 +46,12 @@ fn test_btc_utxo_selection() {
 		add_utxo_amount(100000);
 		add_utxo_amount(5000000);
 		add_utxo_amount(25000);
+		// dust amount should be ignored in all cases
+		let dust_amount = {
+			use cf_traits::GetBitcoinFeeInfo;
+			<Test as crate::Config>::BitcoinFeeInfo::bitcoin_fee_info().fee_per_input_utxo
+		};
+		add_utxo_amount(dust_amount);
 
 		// select some utxos for a tx
 		assert_eq!(