@@ -29,9 +29,12 @@ use lightning_types::payment::{PaymentHash, PaymentPreimage};
2929
3030use lightning_persister:: fs_store:: FilesystemStore ;
3131
32+ use bitcoin:: hashes:: hex:: FromHex ;
3233use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
3334use bitcoin:: hashes:: Hash ;
34- use bitcoin:: { Address , Amount , Network , OutPoint , Txid } ;
35+ use bitcoin:: {
36+ Address , Amount , Network , OutPoint , ScriptBuf , Sequence , Transaction , Txid , Witness ,
37+ } ;
3538
3639use electrsd:: corepc_node:: Client as BitcoindClient ;
3740use electrsd:: corepc_node:: Node as BitcoinD ;
@@ -40,7 +43,10 @@ use electrum_client::ElectrumApi;
4043
4144use rand:: distributions:: Alphanumeric ;
4245use rand:: { thread_rng, Rng } ;
46+ use serde_json:: { json, Value } ;
4347
48+ use std:: collections:: HashMap ;
49+ use std:: collections:: HashSet ;
4450use std:: env;
4551use std:: path:: PathBuf ;
4652use std:: sync:: { Arc , RwLock } ;
@@ -392,7 +398,22 @@ pub(crate) fn generate_blocks_and_wait<E: ElectrumApi>(
392398 let _block_hashes_res = bitcoind. generate_to_address ( num, & address) ;
393399 wait_for_block ( electrs, cur_height as usize + num) ;
394400 print ! ( " Done!" ) ;
395- println ! ( "\n " ) ;
401+ println ! ( "\n " )
402+ }
403+
404+ pub ( crate ) fn invalidate_blocks ( bitcoind : & BitcoindClient , num_blocks : usize ) {
405+ let blockchain_info = bitcoind. get_blockchain_info ( ) . expect ( "failed to get blockchain info" ) ;
406+ let cur_height = blockchain_info. blocks as usize ;
407+ let target_height = cur_height - num_blocks + 1 ;
408+ let block_hash = bitcoind
409+ . get_block_hash ( target_height as u64 )
410+ . expect ( "failed to get block hash" )
411+ . block_hash ( )
412+ . expect ( "block hash should be present" ) ;
413+ bitcoind. invalidate_block ( block_hash) . expect ( "failed to invalidate block" ) ;
414+ let blockchain_info = bitcoind. get_blockchain_info ( ) . expect ( "failed to get blockchain info" ) ;
415+ let new_cur_height = blockchain_info. blocks as usize ;
416+ assert ! ( new_cur_height + num_blocks == cur_height) ;
396417}
397418
398419pub ( crate ) fn wait_for_block < E : ElectrumApi > ( electrs : & E , min_height : usize ) {
@@ -467,25 +488,56 @@ where
467488 }
468489}
469490
470- pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
471- bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
472- ) {
491+ pub ( crate ) fn premine_blocks < E : ElectrumApi > ( bitcoind : & BitcoindClient , electrs : & E ) {
473492 let _ = bitcoind. create_wallet ( "ldk_node_test" ) ;
474493 let _ = bitcoind. load_wallet ( "ldk_node_test" ) ;
475494 generate_blocks_and_wait ( bitcoind, electrs, 101 ) ;
495+ }
476496
477- for addr in addrs {
478- let txid = bitcoind. send_to_address ( & addr, amount) . unwrap ( ) . 0 . parse ( ) . unwrap ( ) ;
479- wait_for_tx ( electrs, txid) ;
497+ pub ( crate ) fn distribute_funds_unconfirmed < E : ElectrumApi > (
498+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
499+ ) -> Txid {
500+ let mut amounts = HashMap :: < String , f64 > :: new ( ) ;
501+ for addr in & addrs {
502+ amounts. insert ( addr. to_string ( ) , amount. to_btc ( ) ) ;
480503 }
481504
505+ let empty_account = json ! ( "" ) ;
506+ let amounts_json = json ! ( amounts) ;
507+ let txid = bitcoind
508+ . call :: < Value > ( "sendmany" , & [ empty_account, amounts_json] )
509+ . unwrap ( )
510+ . as_str ( )
511+ . unwrap ( )
512+ . parse ( )
513+ . unwrap ( ) ;
514+
515+ wait_for_tx ( electrs, txid) ;
516+
517+ txid
518+ }
519+
520+ pub ( crate ) fn distribute_funds < E : ElectrumApi > (
521+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
522+ ) -> Txid {
523+ let address_txid_map = distribute_funds_unconfirmed ( bitcoind, electrs, addrs, amount) ;
482524 generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
525+
526+ address_txid_map
527+ }
528+
529+ pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
530+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
531+ ) {
532+ premine_blocks ( bitcoind, electrs) ;
533+
534+ distribute_funds ( bitcoind, electrs, addrs, amount) ;
483535}
484536
485537pub fn open_channel (
486538 node_a : & TestNode , node_b : & TestNode , funding_amount_sat : u64 , should_announce : bool ,
487539 electrsd : & ElectrsD ,
488- ) {
540+ ) -> OutPoint {
489541 if should_announce {
490542 node_a
491543 . open_announced_channel (
@@ -513,6 +565,8 @@ pub fn open_channel(
513565 let funding_txo_b = expect_channel_pending_event ! ( node_b, node_a. node_id( ) ) ;
514566 assert_eq ! ( funding_txo_a, funding_txo_b) ;
515567 wait_for_tx ( & electrsd. client , funding_txo_a. txid ) ;
568+
569+ funding_txo_a
516570}
517571
518572pub ( crate ) fn do_channel_full_cycle < E : ElectrumApi > (
@@ -1074,6 +1128,161 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
10741128 println ! ( "\n B stopped" ) ;
10751129}
10761130
1131+ pub ( crate ) struct SetupRBF {
1132+ pub nodes_a : Vec < TestNode > ,
1133+ pub nodes_b : Vec < TestNode > ,
1134+ pub addrs_a : Vec < Address > ,
1135+ pub addrs_b : Vec < Address > ,
1136+ }
1137+
1138+ impl SetupRBF {
1139+ pub ( crate ) fn new ( bitcoind : & BitcoinD , electrsd : & ElectrsD ) -> Self {
1140+ let chain_source_bitcoind = TestChainSource :: BitcoindRpcSync ( bitcoind) ;
1141+ let chain_source_electrum = TestChainSource :: Electrum ( electrsd) ;
1142+ let chain_source_esplora = TestChainSource :: Esplora ( electrsd) ;
1143+
1144+ let ( node_bitcoind_a, node_bitcoind_b) =
1145+ setup_two_nodes ( & chain_source_bitcoind, false , false , false ) ;
1146+ let ( node_electrsd_a, node_electrsd_b) =
1147+ setup_two_nodes ( & chain_source_electrum, false , false , false ) ;
1148+ let ( node_esplora_a, node_esplora_b) =
1149+ setup_two_nodes ( & chain_source_esplora, false , false , false ) ;
1150+
1151+ let nodes_a = vec ! [ node_bitcoind_a, node_electrsd_a, node_esplora_a] ;
1152+ let nodes_b = vec ! [ node_bitcoind_b, node_electrsd_b, node_esplora_b] ;
1153+
1154+ let addrs_a = nodes_a
1155+ . iter ( )
1156+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1157+ . collect :: < Vec < _ > > ( ) ;
1158+ let addrs_b = nodes_b
1159+ . iter ( )
1160+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1161+ . collect :: < Vec < _ > > ( ) ;
1162+
1163+ Self { nodes_a, nodes_b, addrs_a, addrs_b }
1164+ }
1165+
1166+ pub ( crate ) fn sync_wallets ( & self ) {
1167+ for node in & self . nodes_a {
1168+ node. sync_wallets ( ) . unwrap ( ) ;
1169+ }
1170+ for node in & self . nodes_b {
1171+ node. sync_wallets ( ) . unwrap ( ) ;
1172+ }
1173+ }
1174+
1175+ pub ( crate ) fn validate_balances (
1176+ & self , nodes : & [ TestNode ] , expected_balance : u64 , is_spendable : bool ,
1177+ ) {
1178+ let spend_balance = if is_spendable { expected_balance } else { 0 } ;
1179+ for node in nodes. iter ( ) {
1180+ assert_eq ! ( node. list_balances( ) . total_onchain_balance_sats, expected_balance) ;
1181+ assert_eq ! ( node. list_balances( ) . spendable_onchain_balance_sats, spend_balance) ;
1182+ }
1183+ }
1184+
1185+ pub ( crate ) fn setup_initial_funding < E : ElectrumApi > (
1186+ & self , bitcoind : & BitcoindClient , electrs : & E , amount : u64 ,
1187+ ) -> bitcoin:: Txid {
1188+ premine_blocks ( bitcoind, electrs) ;
1189+ let all_addrs = self . addrs_a . iter ( ) . chain ( self . addrs_b . iter ( ) ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
1190+ distribute_funds_unconfirmed ( bitcoind, electrs, all_addrs, Amount :: from_sat ( amount) )
1191+ }
1192+
1193+ pub ( crate ) fn pump_fee_with_rbf_replacement < E : ElectrumApi > (
1194+ & self , bitcoind : & BitcoindClient , electrs : & E , original_tx : & mut Transaction ,
1195+ fee_output_index : usize ,
1196+ ) -> Txid {
1197+ let mut pump_fee_amount = 1 * original_tx. vsize ( ) as u64 ;
1198+
1199+ macro_rules! pump_fee {
1200+ ( ) => { {
1201+ let fee_output = & mut original_tx. output[ fee_output_index] ;
1202+ let new_fee_value = fee_output. value. to_sat( ) . saturating_sub( pump_fee_amount) ;
1203+ fee_output. value = Amount :: from_sat( new_fee_value) ;
1204+
1205+ if new_fee_value < 546 {
1206+ // dust limit
1207+ panic!( "Warning: Fee output approaching dust limit ({} sats)" , new_fee_value) ;
1208+ }
1209+
1210+ pump_fee_amount += pump_fee_amount * 5 ;
1211+
1212+ for input in & mut original_tx. input {
1213+ input. sequence = Sequence :: ENABLE_RBF_NO_LOCKTIME ;
1214+ input. script_sig = ScriptBuf :: new( ) ;
1215+ input. witness = Witness :: new( ) ;
1216+ }
1217+
1218+ let tx_hex = bitcoin:: consensus:: encode:: serialize_hex( & original_tx) ;
1219+ let signed_result = bitcoind. sign_raw_transaction_with_wallet( & tx_hex) . unwrap( ) ;
1220+ assert!( signed_result. complete, "Failed to sign RBF transaction" ) ;
1221+
1222+ let tx_bytes = Vec :: <u8 >:: from_hex( & signed_result. hex) . unwrap( ) ;
1223+ let tx = bitcoin:: consensus:: encode:: deserialize:: <Transaction >( & tx_bytes) . unwrap( ) ;
1224+
1225+ tx
1226+ } } ;
1227+ }
1228+
1229+ for _attempt in 0 ..3 {
1230+ let tx = pump_fee ! ( ) ;
1231+ match bitcoind. send_raw_transaction ( & tx) {
1232+ Ok ( res) => {
1233+ let new_txid = res. 0 . parse ( ) . unwrap ( ) ;
1234+ wait_for_tx ( electrs, new_txid) ;
1235+ println ! ( "New txid from the RBF: {}" , new_txid) ;
1236+ return new_txid;
1237+ } ,
1238+ Err ( _) => {
1239+ if original_tx. output [ fee_output_index] . value . to_sat ( ) < pump_fee_amount {
1240+ panic ! ( "Insufficient funds to increase fee" ) ;
1241+ }
1242+ } ,
1243+ }
1244+ }
1245+
1246+ panic ! ( "Failed to pump fee after 3 attempts" ) ;
1247+ }
1248+
1249+ pub ( crate ) fn init_rbf < E : ElectrumApi > (
1250+ & self , electrs : & E , original_txid : Txid ,
1251+ ) -> ( Transaction , HashSet < ScriptBuf > , HashSet < ScriptBuf > , usize ) {
1252+ let original_tx: Transaction = electrs. transaction_get ( & original_txid) . unwrap ( ) ;
1253+
1254+ let total_addresses_to_modify = & self . addrs_a . len ( ) + & self . addrs_b . len ( ) ;
1255+ if original_tx. output . len ( ) <= total_addresses_to_modify {
1256+ panic ! (
1257+ "Transaction must have more outputs ({}) than addresses to modify ({}) to allow fee pumping" ,
1258+ original_tx. output. len( ) ,
1259+ total_addresses_to_modify
1260+ ) ;
1261+ }
1262+
1263+ let scripts_a: HashSet < ScriptBuf > =
1264+ self . addrs_a . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1265+ let scripts_b: HashSet < ScriptBuf > =
1266+ self . addrs_b . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1267+
1268+ let mut fee_output_index: Option < usize > = None ;
1269+ for ( index, output) in original_tx. output . iter ( ) . enumerate ( ) {
1270+ if !scripts_a. contains ( & output. script_pubkey )
1271+ && !scripts_b. contains ( & output. script_pubkey )
1272+ {
1273+ fee_output_index = Some ( index) ;
1274+ break ;
1275+ }
1276+ }
1277+
1278+ let fee_output_index = fee_output_index. expect (
1279+ "No output available for fee pumping. Need at least one output not being modified." ,
1280+ ) ;
1281+
1282+ ( original_tx, scripts_a, scripts_b, fee_output_index)
1283+ }
1284+ }
1285+
10771286// A `KVStore` impl for testing purposes that wraps all our `KVStore`s and asserts their synchronicity.
10781287pub ( crate ) struct TestSyncStore {
10791288 serializer : RwLock < ( ) > ,
0 commit comments