Skip to content

Commit a228289

Browse files
reorg test
1 parent d2cadd0 commit a228289

File tree

3 files changed

+318
-11
lines changed

3 files changed

+318
-11
lines changed

src/chain/bitcoind.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use serde::Serialize;
3838

3939
use base64::prelude::BASE64_STANDARD;
4040
use base64::Engine;
41+
use bitcoin::hashes::Hash;
4142
use bitcoin::{BlockHash, FeeRate, Network, Transaction, Txid};
4243

4344
use std::collections::{HashMap, VecDeque};
@@ -1119,11 +1120,19 @@ impl BitcoindClient {
11191120
) -> std::io::Result<Vec<(Txid, u64)>> {
11201121
let latest_mempool_timestamp = latest_mempool_timestamp.load(Ordering::Relaxed);
11211122
let mempool_entries_cache = mempool_entries_cache.lock().await;
1123+
println!("unconfirmed_txids: {:?}", unconfirmed_txids);
1124+
println!("mempool_entries_cache: {:?}", mempool_entries_cache);
11221125
let evicted_txids = unconfirmed_txids
11231126
.into_iter()
1124-
.filter(|txid| mempool_entries_cache.contains_key(txid))
1127+
.filter(|txid| {
1128+
let mut bytes = txid.to_byte_array();
1129+
bytes.reverse();
1130+
let normalized_txid = Txid::from_byte_array(bytes);
1131+
!mempool_entries_cache.contains_key(&normalized_txid)
1132+
})
11251133
.map(|txid| (txid, latest_mempool_timestamp))
11261134
.collect();
1135+
println!("evicted_txids: {:?}", evicted_txids);
11271136
Ok(evicted_txids)
11281137
}
11291138
}

tests/common/mod.rs

Lines changed: 218 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ use lightning_types::payment::{PaymentHash, PaymentPreimage};
2929

3030
use lightning_persister::fs_store::FilesystemStore;
3131

32+
use bitcoin::hashes::hex::FromHex;
3233
use bitcoin::hashes::sha256::Hash as Sha256;
3334
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+
};
3538

3639
use electrsd::corepc_node::Client as BitcoindClient;
3740
use electrsd::corepc_node::Node as BitcoinD;
@@ -40,7 +43,10 @@ use electrum_client::ElectrumApi;
4043

4144
use rand::distributions::Alphanumeric;
4245
use rand::{thread_rng, Rng};
46+
use serde_json::{json, Value};
4347

48+
use std::collections::HashMap;
49+
use std::collections::HashSet;
4450
use std::env;
4551
use std::path::PathBuf;
4652
use 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

398419
pub(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

485537
pub 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

518572
pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
@@ -1074,6 +1128,161 @@ pub(crate) fn do_channel_full_cycle<E: ElectrumApi>(
10741128
println!("\nB 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.
10781287
pub(crate) struct TestSyncStore {
10791288
serializer: RwLock<()>,

0 commit comments

Comments
 (0)