@@ -29,9 +29,12 @@ use lightning_types::payment::{PaymentHash, PaymentPreimage};
29
29
30
30
use lightning_persister:: fs_store:: FilesystemStore ;
31
31
32
+ use bitcoin:: hashes:: hex:: FromHex ;
32
33
use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
33
34
use 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
+ } ;
35
38
36
39
use electrsd:: corepc_node:: Client as BitcoindClient ;
37
40
use electrsd:: corepc_node:: Node as BitcoinD ;
@@ -40,7 +43,9 @@ use electrum_client::ElectrumApi;
40
43
41
44
use rand:: distributions:: Alphanumeric ;
42
45
use rand:: { thread_rng, Rng } ;
46
+ use serde_json:: { json, Value } ;
43
47
48
+ use std:: collections:: { HashMap , HashSet } ;
44
49
use std:: env;
45
50
use std:: path:: PathBuf ;
46
51
use std:: sync:: { Arc , RwLock } ;
@@ -470,16 +475,47 @@ where
470
475
pub ( crate ) fn premine_and_distribute_funds < E : ElectrumApi > (
471
476
bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
472
477
) {
478
+ premine_blocks ( bitcoind, electrs) ;
479
+
480
+ distribute_funds ( bitcoind, electrs, addrs, amount) ;
481
+ }
482
+
483
+ pub ( crate ) fn premine_blocks < E : ElectrumApi > ( bitcoind : & BitcoindClient , electrs : & E ) {
473
484
let _ = bitcoind. create_wallet ( "ldk_node_test" ) ;
474
485
let _ = bitcoind. load_wallet ( "ldk_node_test" ) ;
475
486
generate_blocks_and_wait ( bitcoind, electrs, 101 ) ;
487
+ }
476
488
477
- for addr in addrs {
478
- let txid = bitcoind. send_to_address ( & addr, amount) . unwrap ( ) . 0 . parse ( ) . unwrap ( ) ;
479
- wait_for_tx ( electrs, txid) ;
489
+ pub ( crate ) fn distribute_funds < E : ElectrumApi > (
490
+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
491
+ ) -> Txid {
492
+ let address_txid_map = distribute_funds_unconfirmed ( bitcoind, electrs, addrs, amount) ;
493
+ generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
494
+
495
+ address_txid_map
496
+ }
497
+
498
+ pub ( crate ) fn distribute_funds_unconfirmed < E : ElectrumApi > (
499
+ bitcoind : & BitcoindClient , electrs : & E , addrs : Vec < Address > , amount : Amount ,
500
+ ) -> Txid {
501
+ let mut amounts = HashMap :: < String , f64 > :: new ( ) ;
502
+ for addr in & addrs {
503
+ amounts. insert ( addr. to_string ( ) , amount. to_btc ( ) ) ;
480
504
}
481
505
482
- generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
506
+ let empty_account = json ! ( "" ) ;
507
+ let amounts_json = json ! ( amounts) ;
508
+ let txid = bitcoind
509
+ . call :: < Value > ( "sendmany" , & [ empty_account, amounts_json] )
510
+ . unwrap ( )
511
+ . as_str ( )
512
+ . unwrap ( )
513
+ . parse ( )
514
+ . unwrap ( ) ;
515
+
516
+ wait_for_tx ( electrs, txid) ;
517
+
518
+ txid
483
519
}
484
520
485
521
pub fn open_channel (
@@ -1074,6 +1110,170 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
1074
1110
println ! ( "\n B stopped" ) ;
1075
1111
}
1076
1112
1113
+ pub ( crate ) struct MultiNodeTestSetup {
1114
+ pub nodes_a : Vec < TestNode > ,
1115
+ pub nodes_b : Vec < TestNode > ,
1116
+ pub addrs_a : Vec < Address > ,
1117
+ pub addrs_b : Vec < Address > ,
1118
+ }
1119
+
1120
+ impl MultiNodeTestSetup {
1121
+ pub ( crate ) fn new ( bitcoind : & BitcoinD , electrsd : & ElectrsD ) -> Self {
1122
+ let chain_source_bitcoind = TestChainSource :: BitcoindRpcSync ( bitcoind) ;
1123
+ let chain_source_electrum = TestChainSource :: Electrum ( electrsd) ;
1124
+ let chain_source_esplora = TestChainSource :: Esplora ( electrsd) ;
1125
+
1126
+ let ( node_bitcoind_a, node_bitcoind_b) =
1127
+ setup_two_nodes ( & chain_source_bitcoind, false , false , false ) ;
1128
+ let ( node_electrsd_a, node_electrsd_b) =
1129
+ setup_two_nodes ( & chain_source_electrum, false , false , false ) ;
1130
+ let ( node_esplora_a, node_esplora_b) =
1131
+ setup_two_nodes ( & chain_source_esplora, false , false , false ) ;
1132
+
1133
+ let nodes_a = vec ! [ node_bitcoind_a, node_electrsd_a, node_esplora_a] ;
1134
+ let nodes_b = vec ! [ node_bitcoind_b, node_electrsd_b, node_esplora_b] ;
1135
+
1136
+ let addrs_a = nodes_a
1137
+ . iter ( )
1138
+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1139
+ . collect :: < Vec < _ > > ( ) ;
1140
+ let addrs_b = nodes_b
1141
+ . iter ( )
1142
+ . map ( |node| node. onchain_payment ( ) . new_address ( ) . unwrap ( ) )
1143
+ . collect :: < Vec < _ > > ( ) ;
1144
+
1145
+ Self { nodes_a, nodes_b, addrs_a, addrs_b }
1146
+ }
1147
+
1148
+ pub ( crate ) fn sync_wallets ( & self ) {
1149
+ for node in & self . nodes_a {
1150
+ node. sync_wallets ( ) . unwrap ( ) ;
1151
+ }
1152
+ for node in & self . nodes_b {
1153
+ node. sync_wallets ( ) . unwrap ( ) ;
1154
+ }
1155
+ }
1156
+
1157
+ pub ( crate ) fn validate_balances (
1158
+ & self , nodes : & [ TestNode ] , expected_balance : u64 , is_spendable : bool ,
1159
+ ) {
1160
+ let spend_balance = if is_spendable { expected_balance } else { 0 } ;
1161
+ for node in nodes. iter ( ) {
1162
+ assert_eq ! ( node. list_balances( ) . total_onchain_balance_sats, expected_balance) ;
1163
+ assert_eq ! ( node. list_balances( ) . spendable_onchain_balance_sats, spend_balance) ;
1164
+ }
1165
+ }
1166
+
1167
+ pub ( crate ) fn setup_initial_funding < E : ElectrumApi > (
1168
+ & self , bitcoind : & BitcoindClient , electrs : & E , amount : u64 ,
1169
+ ) -> bitcoin:: Txid {
1170
+ premine_blocks ( bitcoind, electrs) ;
1171
+ self . distribute_funds ( bitcoind, electrs, amount)
1172
+ }
1173
+
1174
+ pub ( crate ) fn distribute_funds < E : ElectrumApi > (
1175
+ & self , bitcoind : & BitcoindClient , electrs : & E , amount : u64 ,
1176
+ ) -> bitcoin:: Txid {
1177
+ let all_addrs = self . addrs_a . iter ( ) . chain ( self . addrs_b . iter ( ) ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
1178
+ distribute_funds_unconfirmed ( bitcoind, electrs, all_addrs, Amount :: from_sat ( amount) )
1179
+ }
1180
+
1181
+ pub ( crate ) fn bump_fee_rbf < E : ElectrumApi > (
1182
+ & self , bitcoind : & BitcoindClient , electrs : & E , original_tx : & mut Transaction ,
1183
+ fee_output_index : usize , is_insert_block : bool ,
1184
+ ) -> Txid {
1185
+ let mut bump_fee_amount = original_tx. vsize ( ) as u64 ;
1186
+
1187
+ macro_rules! bump_fee {
1188
+ ( ) => { {
1189
+ let fee_output = & mut original_tx. output[ fee_output_index] ;
1190
+ let new_fee_value = fee_output. value. to_sat( ) . saturating_sub( bump_fee_amount) ;
1191
+ fee_output. value = Amount :: from_sat( new_fee_value) ;
1192
+
1193
+ // dust limit
1194
+ if new_fee_value < 546 {
1195
+ panic!( "Warning: Fee output approaching dust limit ({} sats)" , new_fee_value) ;
1196
+ }
1197
+
1198
+ for input in & mut original_tx. input {
1199
+ input. sequence = Sequence :: ENABLE_RBF_NO_LOCKTIME ;
1200
+ input. script_sig = ScriptBuf :: new( ) ;
1201
+ input. witness = Witness :: new( ) ;
1202
+ }
1203
+
1204
+ let signed_result =
1205
+ bitcoind. sign_raw_transaction_with_wallet( & original_tx) . unwrap( ) ;
1206
+ assert!( signed_result. complete, "Failed to sign RBF transaction" ) ;
1207
+
1208
+ let tx_bytes = Vec :: <u8 >:: from_hex( & signed_result. hex) . unwrap( ) ;
1209
+ let tx = bitcoin:: consensus:: encode:: deserialize:: <Transaction >( & tx_bytes) . unwrap( ) ;
1210
+
1211
+ tx
1212
+ } } ;
1213
+ }
1214
+ let attempts = 3 ;
1215
+ for _attempt in 0 ..attempts {
1216
+ let tx = bump_fee ! ( ) ;
1217
+ match bitcoind. send_raw_transaction ( & tx) {
1218
+ Ok ( res) => {
1219
+ // Mine a block immediately so the transaction is confirmed
1220
+ // before any node identifies it as a transaction that was in the mempool.
1221
+ if is_insert_block {
1222
+ generate_blocks_and_wait ( bitcoind, electrs, 1 ) ;
1223
+ }
1224
+ let new_txid = res. 0 . parse ( ) . unwrap ( ) ;
1225
+ wait_for_tx ( electrs, new_txid) ;
1226
+ return new_txid;
1227
+ } ,
1228
+ Err ( _) => {
1229
+ bump_fee_amount += bump_fee_amount * 5 ;
1230
+ if original_tx. output [ fee_output_index] . value . to_sat ( ) < bump_fee_amount {
1231
+ panic ! ( "Insufficient funds to increase fee" ) ;
1232
+ }
1233
+ } ,
1234
+ }
1235
+ }
1236
+
1237
+ panic ! ( "Failed to pump fee after {} attempts" , attempts) ;
1238
+ }
1239
+
1240
+ pub ( crate ) fn prepare_rbf < E : ElectrumApi > (
1241
+ & self , electrs : & E , original_txid : Txid ,
1242
+ ) -> ( Transaction , HashSet < ScriptBuf > , HashSet < ScriptBuf > , usize ) {
1243
+ let original_tx: Transaction = electrs. transaction_get ( & original_txid) . unwrap ( ) ;
1244
+
1245
+ let total_addresses_to_modify = & self . addrs_a . len ( ) + & self . addrs_b . len ( ) ;
1246
+ if original_tx. output . len ( ) <= total_addresses_to_modify {
1247
+ panic ! (
1248
+ "Transaction must have more outputs ({}) than addresses to modify ({}) to allow fee pumping" ,
1249
+ original_tx. output. len( ) ,
1250
+ total_addresses_to_modify
1251
+ ) ;
1252
+ }
1253
+
1254
+ let scripts_a: HashSet < ScriptBuf > =
1255
+ self . addrs_a . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1256
+ let scripts_b: HashSet < ScriptBuf > =
1257
+ self . addrs_b . iter ( ) . map ( |addr| addr. script_pubkey ( ) ) . collect ( ) ;
1258
+
1259
+ let mut fee_output_index: Option < usize > = None ;
1260
+ for ( index, output) in original_tx. output . iter ( ) . enumerate ( ) {
1261
+ if !scripts_a. contains ( & output. script_pubkey )
1262
+ && !scripts_b. contains ( & output. script_pubkey )
1263
+ {
1264
+ fee_output_index = Some ( index) ;
1265
+ break ;
1266
+ }
1267
+ }
1268
+
1269
+ let fee_output_index = fee_output_index. expect (
1270
+ "No output available for fee pumping. Need at least one output not being modified." ,
1271
+ ) ;
1272
+
1273
+ ( original_tx, scripts_a, scripts_b, fee_output_index)
1274
+ }
1275
+ }
1276
+
1077
1277
// A `KVStore` impl for testing purposes that wraps all our `KVStore`s and asserts their synchronicity.
1078
1278
pub ( crate ) struct TestSyncStore {
1079
1279
serializer : RwLock < ( ) > ,
0 commit comments