@@ -1031,6 +1031,78 @@ fn test_create_tx_global_xpubs_with_origin() {
1031
1031
assert_eq ! ( psbt. xpub. get( & key) , Some ( & ( fingerprint, path) ) ) ;
1032
1032
}
1033
1033
1034
+ #[ test]
1035
+ fn test_legacy_add_foreign_utxo ( ) {
1036
+ let ( mut wallet1, _) = get_funded_wallet ( get_test_pkh ( ) ) ; // legacy wallet using PKH
1037
+ let ( wallet2, _) =
1038
+ get_funded_wallet ( "wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)" ) ;
1039
+
1040
+ let addr = Address :: from_str ( "2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX" )
1041
+ . unwrap ( )
1042
+ . assume_checked ( ) ;
1043
+ let utxo = wallet2. list_unspent ( ) . next ( ) . expect ( "must take!" ) ;
1044
+ let foreign_utxo_satisfaction = wallet2
1045
+ . get_descriptor_for_keychain ( KeychainKind :: External )
1046
+ . max_weight_to_satisfy ( )
1047
+ . unwrap ( ) ;
1048
+
1049
+ let psbt_input = psbt:: Input {
1050
+ witness_utxo : Some ( utxo. txout . clone ( ) ) ,
1051
+ ..Default :: default ( )
1052
+ } ;
1053
+
1054
+ let mut builder = wallet1. build_tx ( ) ;
1055
+ builder
1056
+ . add_recipient ( addr. script_pubkey ( ) , 60_000 )
1057
+ . only_witness_utxo ( )
1058
+ . add_foreign_utxo ( utxo. outpoint , psbt_input, foreign_utxo_satisfaction)
1059
+ . unwrap ( ) ;
1060
+ let mut psbt = builder. finish ( ) . unwrap ( ) ;
1061
+ wallet1. insert_txout ( utxo. outpoint , utxo. txout ) ;
1062
+ let fee = check_fee ! ( wallet1, psbt) ;
1063
+ let sent_received = wallet1. sent_and_received ( & psbt. clone ( ) . extract_tx ( ) ) ;
1064
+
1065
+ assert_eq ! (
1066
+ sent_received. 0 - sent_received. 1 ,
1067
+ 10_000 + fee. unwrap_or( 0 ) ,
1068
+ "we should have only net spent ~10_000"
1069
+ ) ;
1070
+
1071
+ assert ! (
1072
+ psbt. unsigned_tx
1073
+ . input
1074
+ . iter( )
1075
+ . any( |input| input. previous_output == utxo. outpoint) ,
1076
+ "foreign_utxo should be in there"
1077
+ ) ;
1078
+
1079
+ let finished = wallet1
1080
+ . sign (
1081
+ & mut psbt,
1082
+ SignOptions {
1083
+ trust_witness_utxo : true ,
1084
+ ..Default :: default ( )
1085
+ } ,
1086
+ )
1087
+ . unwrap ( ) ;
1088
+
1089
+ assert ! (
1090
+ !finished,
1091
+ "only one of the inputs should have been signed so far"
1092
+ ) ;
1093
+
1094
+ let finished = wallet2
1095
+ . sign (
1096
+ & mut psbt,
1097
+ SignOptions {
1098
+ trust_witness_utxo : true ,
1099
+ ..Default :: default ( )
1100
+ } ,
1101
+ )
1102
+ . unwrap ( ) ;
1103
+ assert ! ( finished, "all the inputs should have been signed now" ) ;
1104
+ }
1105
+
1034
1106
#[ test]
1035
1107
fn test_add_foreign_utxo ( ) {
1036
1108
let ( mut wallet1, _) = get_funded_wallet ( get_test_wpkh ( ) ) ;
@@ -1713,6 +1785,73 @@ fn test_bump_fee_remove_output_manually_selected_only() {
1713
1785
builder. finish ( ) . unwrap ( ) ;
1714
1786
}
1715
1787
1788
+ #[ test]
1789
+ fn test_legacy_bump_fee_add_input ( ) {
1790
+ let ( mut wallet, _) = get_funded_wallet ( get_test_pkh ( ) ) ; // legacy wallet PKH
1791
+ let init_tx = Transaction {
1792
+ version : 1 ,
1793
+ lock_time : absolute:: LockTime :: ZERO ,
1794
+ input : vec ! [ ] ,
1795
+ output : vec ! [ TxOut {
1796
+ script_pubkey: wallet. get_address( New ) . script_pubkey( ) ,
1797
+ value: 25_000 ,
1798
+ } ] ,
1799
+ } ;
1800
+ let pos = wallet
1801
+ . transactions ( )
1802
+ . last ( )
1803
+ . unwrap ( )
1804
+ . chain_position
1805
+ . cloned ( )
1806
+ . into ( ) ;
1807
+ wallet. insert_tx ( init_tx, pos) . unwrap ( ) ;
1808
+
1809
+ let addr = Address :: from_str ( "2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX" )
1810
+ . unwrap ( )
1811
+ . assume_checked ( ) ;
1812
+ let mut builder = wallet. build_tx ( ) . coin_selection ( LargestFirstCoinSelection ) ;
1813
+ builder
1814
+ . add_recipient ( addr. script_pubkey ( ) , 45_000 )
1815
+ . enable_rbf ( ) ;
1816
+ let psbt = builder. finish ( ) . unwrap ( ) ;
1817
+ let tx = psbt. extract_tx ( ) ;
1818
+ let original_details = wallet. sent_and_received ( & tx) ;
1819
+ let txid = tx. txid ( ) ;
1820
+ wallet
1821
+ . insert_tx ( tx, ConfirmationTime :: Unconfirmed { last_seen : 0 } )
1822
+ . unwrap ( ) ;
1823
+
1824
+ let mut builder = wallet. build_fee_bump ( txid) . unwrap ( ) ;
1825
+ builder. fee_rate ( FeeRate :: from_sat_per_vb ( 50.0 ) ) ;
1826
+ let psbt = builder. finish ( ) . unwrap ( ) ;
1827
+ let sent_received = wallet. sent_and_received ( & psbt. clone ( ) . extract_tx ( ) ) ;
1828
+ let fee = check_fee ! ( wallet, psbt) ;
1829
+ assert_eq ! ( sent_received. 0 , original_details. 0 + 25_000 ) ;
1830
+ assert_eq ! ( fee. unwrap_or( 0 ) + sent_received. 1 , 30_000 ) ;
1831
+
1832
+ let tx = & psbt. unsigned_tx ;
1833
+ assert_eq ! ( tx. input. len( ) , 2 ) ;
1834
+ assert_eq ! ( tx. output. len( ) , 2 ) ;
1835
+ assert_eq ! (
1836
+ tx. output
1837
+ . iter( )
1838
+ . find( |txout| txout. script_pubkey == addr. script_pubkey( ) )
1839
+ . unwrap( )
1840
+ . value,
1841
+ 45_000
1842
+ ) ;
1843
+ assert_eq ! (
1844
+ tx. output
1845
+ . iter( )
1846
+ . find( |txout| txout. script_pubkey != addr. script_pubkey( ) )
1847
+ . unwrap( )
1848
+ . value,
1849
+ sent_received. 1
1850
+ ) ;
1851
+
1852
+ assert_fee_rate ! ( psbt, fee. unwrap_or( 0 ) , FeeRate :: from_sat_per_vb( 50.0 ) , @add_signature) ;
1853
+ }
1854
+
1716
1855
#[ test]
1717
1856
fn test_bump_fee_add_input ( ) {
1718
1857
let ( mut wallet, _) = get_funded_wallet ( get_test_wpkh ( ) ) ;
@@ -1963,6 +2102,65 @@ fn test_bump_fee_add_input_change_dust() {
1963
2102
assert_fee_rate ! ( psbt, fee. unwrap_or( 0 ) , FeeRate :: from_sat_per_vb( 140.0 ) , @dust_change, @add_signature) ;
1964
2103
}
1965
2104
2105
+ #[ test]
2106
+ fn test_legacy_bump_fee_force_add_input ( ) {
2107
+ let ( mut wallet, _) = get_funded_wallet ( get_test_pkh ( ) ) ; // legacy wallet using PKH
2108
+ let incoming_op = receive_output_in_latest_block ( & mut wallet, 25_000 ) ;
2109
+
2110
+ let addr = Address :: from_str ( "2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX" )
2111
+ . unwrap ( )
2112
+ . assume_checked ( ) ;
2113
+ let mut builder = wallet. build_tx ( ) . coin_selection ( LargestFirstCoinSelection ) ;
2114
+ builder
2115
+ . add_recipient ( addr. script_pubkey ( ) , 45_000 )
2116
+ . enable_rbf ( ) ;
2117
+ let psbt = builder. finish ( ) . unwrap ( ) ;
2118
+ let mut tx = psbt. extract_tx ( ) ;
2119
+ let original_sent_received = wallet. sent_and_received ( & tx) ;
2120
+ let txid = tx. txid ( ) ;
2121
+ for txin in & mut tx. input {
2122
+ txin. witness . push ( [ 0x00 ; P2WPKH_FAKE_WITNESS_SIZE ] ) ; // fake signature
2123
+ }
2124
+ wallet
2125
+ . insert_tx ( tx. clone ( ) , ConfirmationTime :: Unconfirmed { last_seen : 0 } )
2126
+ . unwrap ( ) ;
2127
+ // the new fee_rate is low enough that just reducing the change would be fine, but we force
2128
+ // the addition of an extra input with `add_utxo()`
2129
+ let mut builder = wallet. build_fee_bump ( txid) . unwrap ( ) ;
2130
+ builder
2131
+ . add_utxo ( incoming_op)
2132
+ . unwrap ( )
2133
+ . fee_rate ( FeeRate :: from_sat_per_vb ( 5.0 ) ) ;
2134
+ let psbt = builder. finish ( ) . unwrap ( ) ;
2135
+ let sent_received = wallet. sent_and_received ( & psbt. clone ( ) . extract_tx ( ) ) ;
2136
+ let fee = check_fee ! ( wallet, psbt) ;
2137
+
2138
+ assert_eq ! ( sent_received. 0 , original_sent_received. 0 + 25_000 ) ;
2139
+ assert_eq ! ( fee. unwrap_or( 0 ) + sent_received. 1 , 30_000 ) ;
2140
+
2141
+ let tx = & psbt. unsigned_tx ;
2142
+ assert_eq ! ( tx. input. len( ) , 2 ) ;
2143
+ assert_eq ! ( tx. output. len( ) , 2 ) ;
2144
+ assert_eq ! (
2145
+ tx. output
2146
+ . iter( )
2147
+ . find( |txout| txout. script_pubkey == addr. script_pubkey( ) )
2148
+ . unwrap( )
2149
+ . value,
2150
+ 45_000
2151
+ ) ;
2152
+ assert_eq ! (
2153
+ tx. output
2154
+ . iter( )
2155
+ . find( |txout| txout. script_pubkey != addr. script_pubkey( ) )
2156
+ . unwrap( )
2157
+ . value,
2158
+ sent_received. 1
2159
+ ) ;
2160
+
2161
+ assert_fee_rate ! ( psbt, fee. unwrap_or( 0 ) , FeeRate :: from_sat_per_vb( 5.0 ) , @add_signature) ;
2162
+ }
2163
+
1966
2164
#[ test]
1967
2165
fn test_bump_fee_force_add_input ( ) {
1968
2166
let ( mut wallet, _) = get_funded_wallet ( get_test_wpkh ( ) ) ;
0 commit comments