|
101 | 101 | //! # Ok::<(), anyhow::Error>(())
|
102 | 102 | //! ```
|
103 | 103 |
|
104 |
| -use crate::chain::collections::HashSet; |
105 | 104 | use crate::wallet::utils::IsDust;
|
106 | 105 | use crate::Utxo;
|
107 | 106 | use crate::WeightedUtxo;
|
108 | 107 | use bitcoin::{Amount, FeeRate, SignedAmount};
|
109 | 108 |
|
110 | 109 | use alloc::vec::Vec;
|
111 | 110 | use bitcoin::consensus::encode::serialize;
|
112 |
| -use bitcoin::OutPoint; |
113 | 111 | use bitcoin::TxIn;
|
114 | 112 | use bitcoin::{Script, Weight};
|
115 | 113 |
|
@@ -720,38 +718,19 @@ fn calculate_cs_result(
|
720 | 718 | }
|
721 | 719 | }
|
722 | 720 |
|
723 |
| -/// Remove duplicate UTXOs. |
724 |
| -/// |
725 |
| -/// If a UTXO appears in both `required` and `optional`, the appearance in `required` is kept. |
726 |
| -pub(crate) fn filter_duplicates<I>(required: I, optional: I) -> (I, I) |
727 |
| -where |
728 |
| - I: IntoIterator<Item = WeightedUtxo> + FromIterator<WeightedUtxo>, |
729 |
| -{ |
730 |
| - let mut visited = HashSet::<OutPoint>::new(); |
731 |
| - let required = required |
732 |
| - .into_iter() |
733 |
| - .filter(|utxo| visited.insert(utxo.utxo.outpoint())) |
734 |
| - .collect::<I>(); |
735 |
| - let optional = optional |
736 |
| - .into_iter() |
737 |
| - .filter(|utxo| visited.insert(utxo.utxo.outpoint())) |
738 |
| - .collect::<I>(); |
739 |
| - (required, optional) |
740 |
| -} |
741 |
| - |
742 | 721 | #[cfg(test)]
|
743 | 722 | mod test {
|
744 | 723 | use assert_matches::assert_matches;
|
745 | 724 | use bitcoin::hashes::Hash;
|
746 |
| - use chain::{BlockId, ChainPosition, ConfirmationBlockTime}; |
| 725 | + use bitcoin::OutPoint; |
| 726 | + use chain::{ChainPosition, ConfirmationBlockTime}; |
747 | 727 | use core::str::FromStr;
|
748 | 728 | use rand::rngs::StdRng;
|
749 | 729 |
|
750 | 730 | use bitcoin::{Amount, BlockHash, ScriptBuf, TxIn, TxOut};
|
751 | 731 |
|
752 | 732 | use super::*;
|
753 | 733 | use crate::types::*;
|
754 |
| - use crate::wallet::coin_selection::filter_duplicates; |
755 | 734 |
|
756 | 735 | use rand::prelude::SliceRandom;
|
757 | 736 | use rand::{thread_rng, Rng, RngCore, SeedableRng};
|
@@ -1618,102 +1597,6 @@ mod test {
|
1618 | 1597 | assert_eq!(res.selected_amount(), Amount::from_sat(200_000));
|
1619 | 1598 | }
|
1620 | 1599 |
|
1621 |
| - #[test] |
1622 |
| - fn test_filter_duplicates() { |
1623 |
| - fn utxo(txid: &str, value: u64) -> WeightedUtxo { |
1624 |
| - WeightedUtxo { |
1625 |
| - satisfaction_weight: Weight::ZERO, |
1626 |
| - utxo: Utxo::Local(LocalOutput { |
1627 |
| - outpoint: OutPoint::new(bitcoin::hashes::Hash::hash(txid.as_bytes()), 0), |
1628 |
| - txout: TxOut { |
1629 |
| - value: Amount::from_sat(value), |
1630 |
| - script_pubkey: ScriptBuf::new(), |
1631 |
| - }, |
1632 |
| - keychain: KeychainKind::External, |
1633 |
| - is_spent: false, |
1634 |
| - derivation_index: 0, |
1635 |
| - chain_position: ChainPosition::Confirmed { |
1636 |
| - anchor: ConfirmationBlockTime { |
1637 |
| - block_id: BlockId { |
1638 |
| - height: 12345, |
1639 |
| - hash: BlockHash::all_zeros(), |
1640 |
| - }, |
1641 |
| - confirmation_time: 12345, |
1642 |
| - }, |
1643 |
| - transitively: None, |
1644 |
| - }, |
1645 |
| - }), |
1646 |
| - } |
1647 |
| - } |
1648 |
| - |
1649 |
| - fn to_utxo_vec(utxos: &[(&str, u64)]) -> Vec<WeightedUtxo> { |
1650 |
| - let mut v = utxos |
1651 |
| - .iter() |
1652 |
| - .map(|&(txid, value)| utxo(txid, value)) |
1653 |
| - .collect::<Vec<_>>(); |
1654 |
| - v.sort_by_key(|u| u.utxo.outpoint()); |
1655 |
| - v |
1656 |
| - } |
1657 |
| - |
1658 |
| - struct TestCase<'a> { |
1659 |
| - name: &'a str, |
1660 |
| - required: &'a [(&'a str, u64)], |
1661 |
| - optional: &'a [(&'a str, u64)], |
1662 |
| - exp_required: &'a [(&'a str, u64)], |
1663 |
| - exp_optional: &'a [(&'a str, u64)], |
1664 |
| - } |
1665 |
| - |
1666 |
| - let test_cases = [ |
1667 |
| - TestCase { |
1668 |
| - name: "no_duplicates", |
1669 |
| - required: &[("A", 1000), ("B", 2100)], |
1670 |
| - optional: &[("C", 1000)], |
1671 |
| - exp_required: &[("A", 1000), ("B", 2100)], |
1672 |
| - exp_optional: &[("C", 1000)], |
1673 |
| - }, |
1674 |
| - TestCase { |
1675 |
| - name: "duplicate_required_utxos", |
1676 |
| - required: &[("A", 3000), ("B", 1200), ("C", 1234), ("A", 3000)], |
1677 |
| - optional: &[("D", 2100)], |
1678 |
| - exp_required: &[("A", 3000), ("B", 1200), ("C", 1234)], |
1679 |
| - exp_optional: &[("D", 2100)], |
1680 |
| - }, |
1681 |
| - TestCase { |
1682 |
| - name: "duplicate_optional_utxos", |
1683 |
| - required: &[("A", 3000), ("B", 1200)], |
1684 |
| - optional: &[("C", 5000), ("D", 1300), ("C", 5000)], |
1685 |
| - exp_required: &[("A", 3000), ("B", 1200)], |
1686 |
| - exp_optional: &[("C", 5000), ("D", 1300)], |
1687 |
| - }, |
1688 |
| - TestCase { |
1689 |
| - name: "duplicate_across_required_and_optional_utxos", |
1690 |
| - required: &[("A", 3000), ("B", 1200), ("C", 2100)], |
1691 |
| - optional: &[("A", 3000), ("D", 1200), ("E", 5000)], |
1692 |
| - exp_required: &[("A", 3000), ("B", 1200), ("C", 2100)], |
1693 |
| - exp_optional: &[("D", 1200), ("E", 5000)], |
1694 |
| - }, |
1695 |
| - ]; |
1696 |
| - |
1697 |
| - for (i, t) in test_cases.into_iter().enumerate() { |
1698 |
| - let (required, optional) = |
1699 |
| - filter_duplicates(to_utxo_vec(t.required), to_utxo_vec(t.optional)); |
1700 |
| - assert_eq!( |
1701 |
| - required, |
1702 |
| - to_utxo_vec(t.exp_required), |
1703 |
| - "[{}:{}] unexpected `required` result", |
1704 |
| - i, |
1705 |
| - t.name |
1706 |
| - ); |
1707 |
| - assert_eq!( |
1708 |
| - optional, |
1709 |
| - to_utxo_vec(t.exp_optional), |
1710 |
| - "[{}:{}] unexpected `optional` result", |
1711 |
| - i, |
1712 |
| - t.name |
1713 |
| - ); |
1714 |
| - } |
1715 |
| - } |
1716 |
| - |
1717 | 1600 | #[test]
|
1718 | 1601 | fn test_deterministic_coin_selection_picks_same_utxos() {
|
1719 | 1602 | enum CoinSelectionAlgo {
|
|
0 commit comments