@@ -1031,6 +1031,78 @@ fn test_create_tx_global_xpubs_with_origin() {
10311031    assert_eq ! ( psbt. xpub. get( & key) ,  Some ( & ( fingerprint,  path) ) ) ; 
10321032} 
10331033
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+ 
10341106#[ test]  
10351107fn  test_add_foreign_utxo ( )  { 
10361108    let  ( mut  wallet1,  _)  = get_funded_wallet ( get_test_wpkh ( ) ) ; 
@@ -1713,6 +1785,73 @@ fn test_bump_fee_remove_output_manually_selected_only() {
17131785    builder. finish ( ) . unwrap ( ) ; 
17141786} 
17151787
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+ 
17161855#[ test]  
17171856fn  test_bump_fee_add_input ( )  { 
17181857    let  ( mut  wallet,  _)  = get_funded_wallet ( get_test_wpkh ( ) ) ; 
@@ -1963,6 +2102,65 @@ fn test_bump_fee_add_input_change_dust() {
19632102    assert_fee_rate ! ( psbt,  fee. unwrap_or( 0 ) ,  FeeRate :: from_sat_per_vb( 140.0 ) ,  @dust_change,  @add_signature) ; 
19642103} 
19652104
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+ 
19662164#[ test]  
19672165fn  test_bump_fee_force_add_input ( )  { 
19682166    let  ( mut  wallet,  _)  = get_funded_wallet ( get_test_wpkh ( ) ) ; 
0 commit comments