@@ -470,16 +470,47 @@ where
470
470
pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
471
471
bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
472
472
) {
473
+ premine_blocks ( bitcoind, electrs) ;
474
+
475
+ distribute_funds ( bitcoind, electrs, addrs, amount) ;
476
+ }
477
+
478
+ pub ( crate ) fn premine_blocks < E : ElectrumApi > ( bitcoind : & BitcoindClient , electrs : & E ) {
473
479
let _ = bitcoind. create_wallet ( "ldk_node_test" ) ;
474
480
let _ = bitcoind. load_wallet ( "ldk_node_test" ) ;
475
481
generate_blocks_and_wait ( bitcoind, electrs, 101 ) ;
482
+ }
476
483
477
- for addr in addrs {
478
- let txid = bitcoind. send_to_address ( & addr, amount) . unwrap ( ) . 0 . parse ( ) . unwrap ( ) ;
479
- wait_for_tx ( electrs, txid) ;
484
+ pub ( crate ) fn distribute_funds_unconfirmed < E : ElectrumApi > (
485
+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
486
+ ) -> Txid {
487
+ let mut amounts = HashMap :: < String , f64 > :: new ( ) ;
488
+ for addr in & addrs {
489
+ amounts. insert ( addr. to_string ( ) , amount. to_btc ( ) ) ;
480
490
}
481
491
492
+ let empty_account = json ! ( "" ) ;
493
+ let amounts_json = json ! ( amounts) ;
494
+ let txid = bitcoind
495
+ . call :: < Value > ( "sendmany" , & [ empty_account, amounts_json] )
496
+ . unwrap ( )
497
+ . as_str ( )
498
+ . unwrap ( )
499
+ . parse ( )
500
+ . unwrap ( ) ;
501
+
502
+ wait_for_tx ( electrs, txid) ;
503
+
504
+ txid
505
+ }
506
+
507
+ pub ( crate ) fn distribute_funds < E : ElectrumApi > (
508
+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
509
+ ) -> Txid {
510
+ let address_txid_map = distribute_funds_unconfirmed ( bitcoind, electrs, addrs, amount) ;
482
511
generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
512
+
513
+ address_txid_map
483
514
}
484
515
485
516
pub fn open_channel (
@@ -1074,6 +1105,161 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
1074
1105
println ! ( "\n B stopped" ) ;
1075
1106
}
1076
1107
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
+
1077
1263
// A `KVStore` impl for testing purposes that wraps all our `KVStore`s and asserts their synchronicity.
1078
1264
pub ( crate ) struct TestSyncStore {
1079
1265
serializer : RwLock < ( ) > ,
0 commit comments