Skip to content

Commit dfe1263

Browse files
committed
Track reorged on-chain payments as pending
Move affected on-chain payments back to pending when BDK reports that their transaction is unconfirmed again. This keeps payment history aligned with wallet events after a reorg. It does not update payment records directly from disconnected-block notifications. Co-Authored-By: HAL 9000
1 parent 82feeed commit dfe1263

2 files changed

Lines changed: 75 additions & 5 deletions

File tree

src/wallet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ impl Wallet {
345345
}
346346
}
347347
},
348-
WalletEvent::TxUnconfirmed { txid, tx, old_block_time: None } => {
348+
WalletEvent::TxUnconfirmed { txid, tx, .. } => {
349349
let payment_id = self
350350
.find_payment_by_txid(txid)
351351
.unwrap_or_else(|| PaymentId(txid.to_byte_array()));

tests/integration_tests_rust.rs

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ use common::{
2121
expect_channel_pending_event, expect_channel_ready_event, expect_channel_ready_events,
2222
expect_event, expect_payment_claimable_event, expect_payment_received_event,
2323
expect_payment_successful_event, expect_splice_negotiated_event, generate_blocks_and_wait,
24-
generate_listening_addresses, open_channel, open_channel_push_amt, open_channel_with_all,
25-
premine_and_distribute_funds, premine_blocks, prepare_rbf, random_chain_source, random_config,
26-
setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_two_nodes, splice_in_with_all,
27-
wait_for_tx, TestChainSource, TestConfig, TestStoreType, TestSyncStore,
24+
generate_listening_addresses, invalidate_blocks, open_channel, open_channel_push_amt,
25+
open_channel_with_all, premine_and_distribute_funds, premine_blocks, prepare_rbf,
26+
random_chain_source, random_config, setup_bitcoind_and_electrsd, setup_builder, setup_node,
27+
setup_two_nodes, splice_in_with_all, wait_for_block, wait_for_tx, TestChainSource, TestConfig,
28+
TestStoreType, TestSyncStore,
2829
};
2930
use electrsd::corepc_node::Node as BitcoinD;
3031
use electrsd::ElectrsD;
@@ -42,6 +43,7 @@ use lightning::routing::router::RouteParametersConfig;
4243
use lightning_invoice::{Bolt11InvoiceDescription, Description};
4344
use lightning_types::payment::{PaymentHash, PaymentPreimage};
4445
use log::LevelFilter;
46+
use serde_json::json;
4547

4648
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
4749
async fn channel_full_cycle() {
@@ -577,6 +579,74 @@ async fn onchain_send_receive() {
577579
assert_eq!(node_b_payments.len(), 5);
578580
}
579581

582+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
583+
async fn reorged_onchain_payment_returns_to_unconfirmed() {
584+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
585+
let chain_source = random_chain_source(&bitcoind, &electrsd);
586+
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
587+
588+
let addr_a = node_a.onchain_payment().new_address().unwrap();
589+
let addr_b = node_b.onchain_payment().new_address().unwrap();
590+
let premine_amount_sat = 500_000;
591+
premine_and_distribute_funds(
592+
&bitcoind.client,
593+
&electrsd.client,
594+
vec![addr_b],
595+
Amount::from_sat(premine_amount_sat),
596+
)
597+
.await;
598+
599+
node_a.sync_wallets().unwrap();
600+
node_b.sync_wallets().unwrap();
601+
602+
let amount_to_send_sats = 100_000;
603+
let txid =
604+
node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();
605+
wait_for_tx(&electrsd.client, txid).await;
606+
607+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1).await;
608+
node_a.sync_wallets().unwrap();
609+
node_b.sync_wallets().unwrap();
610+
611+
let payment_id = PaymentId(txid.to_byte_array());
612+
for node in [&node_a, &node_b] {
613+
let payment = node.payment(&payment_id).unwrap();
614+
assert_eq!(payment.status, PaymentStatus::Pending);
615+
match payment.kind {
616+
PaymentKind::Onchain { status, .. } => {
617+
assert!(matches!(status, ConfirmationStatus::Confirmed { .. }));
618+
},
619+
_ => panic!("Unexpected payment kind"),
620+
}
621+
}
622+
623+
let original_height =
624+
bitcoind.client.get_blockchain_info().expect("failed to get blockchain info").blocks;
625+
invalidate_blocks(&bitcoind.client, 1);
626+
let replacement_address = bitcoind.client.new_address().expect("failed to get new address");
627+
for _ in 0..2 {
628+
let _res: serde_json::Value = bitcoind
629+
.client
630+
.call("generateblock", &[json!(replacement_address.to_string()), json!([])])
631+
.expect("failed to generate empty block");
632+
}
633+
wait_for_block(&electrsd.client, original_height as usize + 1).await;
634+
635+
node_a.sync_wallets().unwrap();
636+
node_b.sync_wallets().unwrap();
637+
638+
for node in [&node_a, &node_b] {
639+
let payment = node.payment(&payment_id).unwrap();
640+
assert_eq!(payment.status, PaymentStatus::Pending);
641+
match payment.kind {
642+
PaymentKind::Onchain { status, .. } => {
643+
assert!(matches!(status, ConfirmationStatus::Unconfirmed));
644+
},
645+
_ => panic!("Unexpected payment kind"),
646+
}
647+
}
648+
}
649+
580650
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
581651
async fn onchain_send_all_retains_reserve() {
582652
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)