|
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