@@ -467,19 +467,50 @@ where
467467 }
468468}
469469
470- pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
471- bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
472- ) {
470+ pub ( crate ) fn premine_blocks < E : ElectrumApi > ( bitcoind : & BitcoindClient , electrs : & E ) {
473471 let _ = bitcoind. create_wallet ( "ldk_node_test" ) ;
474472 let _ = bitcoind. load_wallet ( "ldk_node_test" ) ;
475473 generate_blocks_and_wait ( bitcoind, electrs, 101 ) ;
474+ }
476475
477- for addr in addrs {
478- let txid = bitcoind. send_to_address ( & addr, amount) . unwrap ( ) . 0 . parse ( ) . unwrap ( ) ;
479- wait_for_tx ( electrs, txid) ;
476+ pub ( crate ) fn distribute_funds_unconfirmed < E : ElectrumApi > (
477+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
478+ ) -> Txid {
479+ let mut amounts = HashMap :: < String , f64 > :: new ( ) ;
480+ for addr in & addrs {
481+ amounts. insert ( addr. to_string ( ) , amount. to_btc ( ) ) ;
480482 }
481483
484+ let empty_account = json ! ( "" ) ;
485+ let amounts_json = json ! ( amounts) ;
486+ let txid = bitcoind
487+ . call :: < Value > ( "sendmany" , & [ empty_account, amounts_json] )
488+ . unwrap ( )
489+ . as_str ( )
490+ . unwrap ( )
491+ . parse ( )
492+ . unwrap ( ) ;
493+
494+ wait_for_tx ( electrs, txid) ;
495+
496+ txid
497+ }
498+
499+ pub ( crate ) fn distribute_funds < E : ElectrumApi > (
500+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
501+ ) -> Txid {
502+ let address_txid_map = distribute_funds_unconfirmed ( bitcoind, electrs, addrs, amount) ;
482503 generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
504+
505+ address_txid_map
506+ }
507+
508+ pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
509+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
510+ ) {
511+ premine_blocks ( bitcoind, electrs) ;
512+
513+ distribute_funds ( bitcoind, electrs, addrs, amount) ;
483514}
484515
485516pub fn open_channel (
@@ -1074,6 +1105,161 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
10741105 println ! ( "\n B stopped" ) ;
10751106}
10761107
1108+ pub ( crate ) struct SetupRBF {
1109+ pub nodes_a : Vec < TestNode > ,
1110+ pub nodes_b : Vec < TestNode > ,
1111+ pub addrs_a : Vec < Address > ,
1112+ pub addrs_b : Vec < Address > ,
1113+ }
1114+
1115+ impl SetupRBF {
1116+ pub ( crate ) fn new ( bitcoind : & BitcoinD , electrsd : & ElectrsD ) -> Self {
1117+ let chain_source_bitcoind = TestChainSource :: BitcoindRpcSync ( bitcoind) ;
1118+ let chain_source_electrum = TestChainSource :: Electrum ( electrsd) ;
1119+ let chain_source_esplora = TestChainSource :: Esplora ( electrsd) ;
1120+
1121+ let ( node_bitcoind_a, node_bitcoind_b) =
1122+ setup_two_nodes ( & chain_source_bitcoind, false , false , false ) ;
1123+ let ( node_electrsd_a, node_electrsd_b) =
1124+ setup_two_nodes ( & chain_source_electrum, false , false , false ) ;
1125+ let ( node_esplora_a, node_esplora_b) =
1126+ setup_two_nodes ( & chain_source_esplora, false , false , false ) ;
1127+
1128+ let nodes_a = vec ! [ node_bitcoind_a, node_electrsd_a, node_esplora_a] ;
1129+ let nodes_b = vec ! [ node_bitcoind_b, node_electrsd_b, node_esplora_b] ;
1130+
1131+ let addrs_a = nodes_a
1132+ . iter ( )
1133+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1134+ . collect :: < Vec < _ > > ( ) ;
1135+ let addrs_b = nodes_b
1136+ . iter ( )
1137+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1138+ . collect :: < Vec < _ > > ( ) ;
1139+
1140+ Self { nodes_a, nodes_b, addrs_a, addrs_b }
1141+ }
1142+
1143+ pub ( crate ) fn sync_wallets ( & self ) {
1144+ for node in & self . nodes_a {
1145+ node. sync_wallets ( ) . unwrap ( ) ;
1146+ }
1147+ for node in & self . nodes_b {
1148+ node. sync_wallets ( ) . unwrap ( ) ;
1149+ }
1150+ }
1151+
1152+ pub ( crate ) fn validate_balances (
1153+ & self , nodes : & [ TestNode ] , expected_balance : u64 , is_spendable : bool ,
1154+ ) {
1155+ let spend_balance = if is_spendable { expected_balance } else { 0 } ;
1156+ for node in nodes. iter ( ) {
1157+ assert_eq ! ( node. list_balances( ) . total_onchain_balance_sats, expected_balance) ;
1158+ assert_eq ! ( node. list_balances( ) . spendable_onchain_balance_sats, spend_balance) ;
1159+ }
1160+ }
1161+
1162+ pub ( crate ) fn setup_initial_funding < E : ElectrumApi > (
1163+ & self , bitcoind : & BitcoindClient , electrs : & E , amount : u64 ,
1164+ ) -> bitcoin:: Txid {
1165+ premine_blocks ( bitcoind, electrs) ;
1166+ let all_addrs = self . addrs_a . iter ( ) . chain ( self . addrs_b . iter ( ) ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
1167+ distribute_funds_unconfirmed ( bitcoind, electrs, all_addrs, Amount :: from_sat ( amount) )
1168+ }
1169+
1170+ pub ( crate ) fn pump_fee_with_rbf_replacement < E : ElectrumApi > (
1171+ & self , bitcoind : & BitcoindClient , electrs : & E , original_tx : & mut Transaction ,
1172+ fee_output_index : usize ,
1173+ ) -> Txid {
1174+ let mut pump_fee_amount = 1 * original_tx. vsize ( ) as u64 ;
1175+
1176+ macro_rules! pump_fee {
1177+ ( ) => { {
1178+ let fee_output = & mut original_tx. output[ fee_output_index] ;
1179+ let new_fee_value = fee_output. value. to_sat( ) . saturating_sub( pump_fee_amount) ;
1180+ fee_output. value = Amount :: from_sat( new_fee_value) ;
1181+
1182+ if new_fee_value < 546 {
1183+ // dust limit
1184+ panic!( "Warning: Fee output approaching dust limit ({} sats)" , new_fee_value) ;
1185+ }
1186+
1187+ pump_fee_amount += pump_fee_amount * 5 ;
1188+
1189+ for input in & mut original_tx. input {
1190+ input. sequence = Sequence :: ENABLE_RBF_NO_LOCKTIME ;
1191+ input. script_sig = ScriptBuf :: new( ) ;
1192+ input. witness = Witness :: new( ) ;
1193+ }
1194+
1195+ let tx_hex = bitcoin:: consensus:: encode:: serialize_hex( & original_tx) ;
1196+ let signed_result = bitcoind. sign_raw_transaction_with_wallet( & tx_hex) . unwrap( ) ;
1197+ assert!( signed_result. complete, "Failed to sign RBF transaction" ) ;
1198+
1199+ let tx_bytes = Vec :: <u8 >:: from_hex( & signed_result. hex) . unwrap( ) ;
1200+ let tx = bitcoin:: consensus:: encode:: deserialize:: <Transaction >( & tx_bytes) . unwrap( ) ;
1201+
1202+ tx
1203+ } } ;
1204+ }
1205+
1206+ for _attempt in 0 ..3 {
1207+ let tx = pump_fee ! ( ) ;
1208+ match bitcoind. send_raw_transaction ( & tx) {
1209+ Ok ( res) => {
1210+ let new_txid = res. 0 . parse ( ) . unwrap ( ) ;
1211+ wait_for_tx ( electrs, new_txid) ;
1212+ println ! ( "New txid from the RBF: {}" , new_txid) ;
1213+ return new_txid;
1214+ } ,
1215+ Err ( _) => {
1216+ if original_tx. output [ fee_output_index] . value . to_sat ( ) < pump_fee_amount {
1217+ panic ! ( "Insufficient funds to increase fee" ) ;
1218+ }
1219+ } ,
1220+ }
1221+ }
1222+
1223+ panic ! ( "Failed to pump fee after 3 attempts" ) ;
1224+ }
1225+
1226+ pub ( crate ) fn init_rbf < E : ElectrumApi > (
1227+ & self , electrs : & E , original_txid : Txid ,
1228+ ) -> ( Transaction , HashSet < ScriptBuf > , HashSet < ScriptBuf > , usize ) {
1229+ let original_tx: Transaction = electrs. transaction_get ( & original_txid) . unwrap ( ) ;
1230+
1231+ let total_addresses_to_modify = & self . addrs_a . len ( ) + & self . addrs_b . len ( ) ;
1232+ if original_tx. output . len ( ) <= total_addresses_to_modify {
1233+ panic ! (
1234+ "Transaction must have more outputs ({}) than addresses to modify ({}) to allow fee pumping" ,
1235+ original_tx. output. len( ) ,
1236+ total_addresses_to_modify
1237+ ) ;
1238+ }
1239+
1240+ let scripts_a: HashSet < ScriptBuf > =
1241+ self . addrs_a . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1242+ let scripts_b: HashSet < ScriptBuf > =
1243+ self . addrs_b . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1244+
1245+ let mut fee_output_index: Option < usize > = None ;
1246+ for ( index, output) in original_tx. output . iter ( ) . enumerate ( ) {
1247+ if !scripts_a. contains ( & output. script_pubkey )
1248+ && !scripts_b. contains ( & output. script_pubkey )
1249+ {
1250+ fee_output_index = Some ( index) ;
1251+ break ;
1252+ }
1253+ }
1254+
1255+ let fee_output_index = fee_output_index. expect (
1256+ "No output available for fee pumping. Need at least one output not being modified." ,
1257+ ) ;
1258+
1259+ ( original_tx, scripts_a, scripts_b, fee_output_index)
1260+ }
1261+ }
1262+
10771263// A `KVStore` impl for testing purposes that wraps all our `KVStore`s and asserts their synchronicity.
10781264pub ( crate ) struct TestSyncStore {
10791265 serializer : RwLock < ( ) > ,
0 commit comments