@@ -36,7 +36,7 @@ use ldk_node::payment::{
3636 ConfirmationStatus , PaymentDetails , PaymentDirection , PaymentKind , PaymentStatus ,
3737 TransactionType , UnifiedPaymentResult ,
3838} ;
39- use ldk_node:: { Builder , Event , NodeError } ;
39+ use ldk_node:: { Builder , Event , Node , NodeError } ;
4040use lightning:: ln:: channelmanager:: PaymentId ;
4141use lightning:: routing:: gossip:: { NodeAlias , NodeId } ;
4242use lightning:: routing:: router:: RouteParametersConfig ;
@@ -45,6 +45,34 @@ use lightning_types::payment::{PaymentHash, PaymentPreimage};
4545use log:: LevelFilter ;
4646use serde_json:: json;
4747
48+ /// Waits until `node` has classified the funding broadcast `funding_txid` (a channel open or splice
49+ /// candidate) into a payment record carrying a `tx_type`. Classification runs off the broadcaster's
50+ /// queue, which can lag a `sync_wallets` call under load — and for a splice the counterparty also
51+ /// broadcasts the same tx, so a racing sync can see it before this node classifies. Waiting here
52+ /// keeps the next sync on the funding short-circuit instead of recording a generic on-chain payment
53+ /// that clobbers the classification.
54+ async fn wait_for_classified_funding_payment ( node : & Node , funding_txid : Txid ) {
55+ let poll = async {
56+ loop {
57+ let classified = node. list_payments ( ) . into_iter ( ) . any ( |p| {
58+ matches ! (
59+ p. kind,
60+ PaymentKind :: Onchain { txid, tx_type: Some ( _) , .. } if txid == funding_txid
61+ )
62+ } ) ;
63+ if classified {
64+ return ;
65+ }
66+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 100 ) ) . await ;
67+ }
68+ } ;
69+ tokio:: time:: timeout ( std:: time:: Duration :: from_secs ( common:: INTEROP_TIMEOUT_SECS ) , poll)
70+ . await
71+ . unwrap_or_else ( |_| {
72+ panic ! ( "timed out waiting for funding broadcast {} to be classified" , funding_txid)
73+ } ) ;
74+ }
75+
4876#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
4977async fn channel_full_cycle ( ) {
5078 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
@@ -1236,6 +1264,10 @@ async fn splice_channel() {
12361264 let txo = expect_splice_negotiated_event ! ( node_a, node_b. node_id( ) ) ;
12371265 expect_splice_negotiated_event ! ( node_b, node_a. node_id( ) ) ;
12381266
1267+ // Node B contributed to this splice, so wait for its funding broadcast to be classified before
1268+ // syncing — otherwise a sync racing the broadcaster's queue records a generic on-chain payment.
1269+ wait_for_classified_funding_payment ( & node_b, txo. txid ) . await ;
1270+
12391271 generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
12401272
12411273 node_a. sync_wallets ( ) . unwrap ( ) ;
@@ -1292,6 +1324,10 @@ async fn splice_channel() {
12921324 let txo = expect_splice_negotiated_event ! ( node_a, node_b. node_id( ) ) ;
12931325 expect_splice_negotiated_event ! ( node_b, node_a. node_id( ) ) ;
12941326
1327+ // Node A contributed to this splice, so wait for its funding broadcast to be classified before
1328+ // syncing — otherwise a sync racing the broadcaster's queue records a generic on-chain payment.
1329+ wait_for_classified_funding_payment ( & node_a, txo. txid ) . await ;
1330+
12951331 generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
12961332
12971333 node_a. sync_wallets ( ) . unwrap ( ) ;
@@ -1407,6 +1443,9 @@ async fn run_rbf_splice_channel_test(confirm_original: bool) {
14071443 // replaced (a `WalletEvent::TxReplaced`), which must not drop the payment's durable funding
14081444 // classification — the `tx_type` assertion below catches a regression deterministically.
14091445 wait_for_tx ( & electrsd. client , original_txo. txid ) . await ;
1446+ // Node B contributed to this splice; wait for its classification before syncing so the sync
1447+ // takes the funding short-circuit rather than racing the broadcaster's queue.
1448+ wait_for_classified_funding_payment ( & node_b, original_txo. txid ) . await ;
14101449 node_a. sync_wallets ( ) . unwrap ( ) ;
14111450 node_b. sync_wallets ( ) . unwrap ( ) ;
14121451
@@ -1441,6 +1480,9 @@ async fn run_rbf_splice_channel_test(confirm_original: bool) {
14411480
14421481 // Wait for the RBF transaction to replace the original in the mempool.
14431482 wait_for_tx ( & electrsd. client , rbf_txo. txid ) . await ;
1483+ // Wait for node_b's re-classification of the RBF candidate before syncing, so the recorded
1484+ // candidate figures reflect the replacement rather than racing the broadcaster's queue.
1485+ wait_for_classified_funding_payment ( & node_b, rbf_txo. txid ) . await ;
14441486 node_a. sync_wallets ( ) . unwrap ( ) ;
14451487 node_b. sync_wallets ( ) . unwrap ( ) ;
14461488
@@ -1640,6 +1682,9 @@ async fn splice_payment_reorged_to_unconfirmed() {
16401682 let splice_txo = expect_splice_negotiated_event ! ( node_a, node_b. node_id( ) ) ;
16411683 expect_splice_negotiated_event ! ( node_b, node_a. node_id( ) ) ;
16421684 wait_for_tx ( & electrsd. client , splice_txo. txid ) . await ;
1685+ // Ensure node_b classified the splice before syncing so the test exercises a funding payment's
1686+ // reorg rather than a generic on-chain payment's.
1687+ wait_for_classified_funding_payment ( & node_b, splice_txo. txid ) . await ;
16431688
16441689 // Confirm the splice with a single block — confirmed, but short of `ANTI_REORG_DELAY`, so the
16451690 // payment is `Confirmed`/`Pending` rather than graduated.
0 commit comments