@@ -30,6 +30,7 @@ use std::time::Duration;
3030use bitcoin:: hashes:: hex:: FromHex ;
3131use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
3232use bitcoin:: hashes:: Hash ;
33+ use bitcoin:: secp256k1:: PublicKey ;
3334use bitcoin:: {
3435 Address , Amount , Network , OutPoint , ScriptBuf , Sequence , Transaction , Txid , Witness ,
3536} ;
@@ -42,7 +43,7 @@ use ldk_node::config::{
4243} ;
4344use ldk_node:: entropy:: { generate_entropy_mnemonic, NodeEntropy } ;
4445use ldk_node:: io:: sqlite_store:: SqliteStore ;
45- use ldk_node:: payment:: { PaymentDirection , PaymentKind , PaymentStatus } ;
46+ use ldk_node:: payment:: { PaymentDirection , PaymentKind , PaymentStatus , TransactionType } ;
4647use ldk_node:: {
4748 Builder , ChannelShutdownState , CustomTlvRecord , Event , LightningBalance , Node , NodeError ,
4849 PendingSweepBalance , UserChannelId ,
@@ -407,6 +408,116 @@ type TestNode = Arc<Node>;
407408#[ cfg( not( feature = "uniffi" ) ) ]
408409type TestNode = Node ;
409410
411+ fn has_onchain_tx_type < F : Fn ( & TransactionType ) -> bool > ( node : & TestNode , predicate : F ) -> bool {
412+ node. list_payments ( ) . into_iter ( ) . any ( |payment| {
413+ matches ! (
414+ payment. kind,
415+ PaymentKind :: Onchain { tx_type: Some ( ref tx_type) , .. } if predicate( tx_type)
416+ )
417+ } )
418+ }
419+
420+ fn assert_any_node_has_onchain_tx_type < F : Fn ( & TransactionType ) -> bool + Copy > (
421+ nodes : & [ ( & str , & TestNode ) ] , tx_type_name : & str , predicate : F ,
422+ ) {
423+ if nodes. iter ( ) . any ( |( _, node) | has_onchain_tx_type ( node, predicate) ) {
424+ return ;
425+ }
426+
427+ let observed: Vec < String > = nodes
428+ . iter ( )
429+ . flat_map ( |( name, node) | {
430+ node. list_payments ( ) . into_iter ( ) . filter_map ( move |payment| match payment. kind {
431+ PaymentKind :: Onchain { tx_type, .. } => Some ( format ! ( "{}:{:?}" , name, tx_type) ) ,
432+ _ => None ,
433+ } )
434+ } )
435+ . collect ( ) ;
436+ panic ! ( "Expected on-chain payment with tx_type {}; observed {:?}" , tx_type_name, observed) ;
437+ }
438+
439+ fn assert_all_nodes_have_onchain_tx_type < F : Fn ( & TransactionType ) -> bool + Copy > (
440+ nodes : & [ ( & str , & TestNode ) ] , tx_type_name : & str , predicate : F ,
441+ ) {
442+ if nodes. iter ( ) . all ( |( _, node) | has_onchain_tx_type ( node, predicate) ) {
443+ return ;
444+ }
445+
446+ let observed: Vec < String > = nodes
447+ . iter ( )
448+ . flat_map ( |( name, node) | {
449+ node. list_payments ( ) . into_iter ( ) . filter_map ( move |payment| match payment. kind {
450+ PaymentKind :: Onchain { tx_type, .. } => Some ( format ! ( "{}:{:?}" , name, tx_type) ) ,
451+ _ => None ,
452+ } )
453+ } )
454+ . collect ( ) ;
455+ panic ! (
456+ "Expected all nodes to have on-chain payment with tx_type {}; observed {:?}" ,
457+ tx_type_name, observed
458+ ) ;
459+ }
460+
461+ async fn settle_force_close_balance < E : ElectrumApi > (
462+ node : & TestNode , counterparty_node_id : PublicKey , peer_node : & TestNode ,
463+ bitcoind : & BitcoindClient , electrsd : & E ,
464+ ) {
465+ let balances = node. list_balances ( ) ;
466+ if balances. lightning_balances . len ( ) == 1 {
467+ match balances. lightning_balances [ 0 ] {
468+ LightningBalance :: ClaimableAwaitingConfirmations {
469+ counterparty_node_id : actual_counterparty_node_id,
470+ confirmation_height,
471+ ..
472+ } => {
473+ assert_eq ! ( actual_counterparty_node_id, counterparty_node_id) ;
474+ let cur_height = node. status ( ) . current_best_block . height ;
475+ let blocks_to_go = confirmation_height - cur_height;
476+ generate_blocks_and_wait ( bitcoind, electrsd, blocks_to_go as usize ) . await ;
477+ node. sync_wallets ( ) . unwrap ( ) ;
478+ peer_node. sync_wallets ( ) . unwrap ( ) ;
479+ } ,
480+ _ => panic ! ( "Unexpected balance state!" ) ,
481+ }
482+ } else {
483+ assert ! ( balances. lightning_balances. is_empty( ) , "Unexpected balance state: {:?}" , balances) ;
484+ assert_eq ! ( balances. pending_balances_from_channel_closures. len( ) , 1 ) ;
485+ }
486+
487+ for _ in 0 ..6 {
488+ if node. list_balances ( ) . lightning_balances . is_empty ( ) {
489+ break ;
490+ }
491+ generate_blocks_and_wait ( bitcoind, electrsd, 1 ) . await ;
492+ node. sync_wallets ( ) . unwrap ( ) ;
493+ peer_node. sync_wallets ( ) . unwrap ( ) ;
494+ }
495+
496+ let balances = node. list_balances ( ) ;
497+ assert ! ( balances. lightning_balances. is_empty( ) , "Unexpected balance state: {:?}" , balances) ;
498+ assert_eq ! ( balances. pending_balances_from_channel_closures. len( ) , 1 ) ;
499+ match balances. pending_balances_from_channel_closures [ 0 ] {
500+ PendingSweepBalance :: BroadcastAwaitingConfirmation { .. } => {
501+ generate_blocks_and_wait ( bitcoind, electrsd, 1 ) . await ;
502+ node. sync_wallets ( ) . unwrap ( ) ;
503+ peer_node. sync_wallets ( ) . unwrap ( ) ;
504+
505+ assert ! ( node. list_balances( ) . lightning_balances. is_empty( ) ) ;
506+ assert_eq ! ( node. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
507+ match node. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
508+ PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
509+ _ => panic ! ( "Unexpected balance state!" ) ,
510+ }
511+ } ,
512+ PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
513+ _ => panic ! ( "Unexpected balance state!" ) ,
514+ }
515+
516+ generate_blocks_and_wait ( bitcoind, electrsd, 5 ) . await ;
517+ node. sync_wallets ( ) . unwrap ( ) ;
518+ peer_node. sync_wallets ( ) . unwrap ( ) ;
519+ }
520+
410521#[ derive( Clone ) ]
411522pub ( crate ) enum TestChainSource < ' a > {
412523 Esplora ( & ' a ElectrsD ) ,
@@ -1474,84 +1585,11 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
14741585 node_b. sync_wallets ( ) . unwrap ( ) ;
14751586
14761587 if force_close {
1477- // Check node_b properly sees all balances and sweeps them.
1478- assert_eq ! ( node_b. list_balances( ) . lightning_balances. len( ) , 1 ) ;
1479- match node_b. list_balances ( ) . lightning_balances [ 0 ] {
1480- LightningBalance :: ClaimableAwaitingConfirmations {
1481- counterparty_node_id,
1482- confirmation_height,
1483- ..
1484- } => {
1485- assert_eq ! ( counterparty_node_id, node_a. node_id( ) ) ;
1486- let cur_height = node_b. status ( ) . current_best_block . height ;
1487- let blocks_to_go = confirmation_height - cur_height;
1488- generate_blocks_and_wait ( & bitcoind, electrsd, blocks_to_go as usize ) . await ;
1489- node_b. sync_wallets ( ) . unwrap ( ) ;
1490- node_a. sync_wallets ( ) . unwrap ( ) ;
1491- } ,
1492- _ => panic ! ( "Unexpected balance state!" ) ,
1493- }
1494-
1495- assert ! ( node_b. list_balances( ) . lightning_balances. is_empty( ) ) ;
1496- assert_eq ! ( node_b. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
1497- match node_b. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
1498- PendingSweepBalance :: BroadcastAwaitingConfirmation { .. } => { } ,
1499- _ => panic ! ( "Unexpected balance state!" ) ,
1500- }
1501- generate_blocks_and_wait ( & bitcoind, electrsd, 1 ) . await ;
1502- node_b. sync_wallets ( ) . unwrap ( ) ;
1503- node_a. sync_wallets ( ) . unwrap ( ) ;
1504-
1588+ settle_force_close_balance ( & node_b, node_a. node_id ( ) , & node_a, & bitcoind, electrsd) . await ;
15051589 assert ! ( node_b. list_balances( ) . lightning_balances. is_empty( ) ) ;
15061590 assert_eq ! ( node_b. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
1507- match node_b. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
1508- PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
1509- _ => panic ! ( "Unexpected balance state!" ) ,
1510- }
1511- generate_blocks_and_wait ( & bitcoind, electrsd, 5 ) . await ;
1512- node_b. sync_wallets ( ) . unwrap ( ) ;
1513- node_a. sync_wallets ( ) . unwrap ( ) ;
1514-
1515- assert ! ( node_b. list_balances( ) . lightning_balances. is_empty( ) ) ;
1516- assert_eq ! ( node_b. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
1517-
1518- // Check node_a properly sees all balances and sweeps them.
1519- assert_eq ! ( node_a. list_balances( ) . lightning_balances. len( ) , 1 ) ;
1520- match node_a. list_balances ( ) . lightning_balances [ 0 ] {
1521- LightningBalance :: ClaimableAwaitingConfirmations {
1522- counterparty_node_id,
1523- confirmation_height,
1524- ..
1525- } => {
1526- assert_eq ! ( counterparty_node_id, node_b. node_id( ) ) ;
1527- let cur_height = node_a. status ( ) . current_best_block . height ;
1528- let blocks_to_go = confirmation_height - cur_height;
1529- generate_blocks_and_wait ( & bitcoind, electrsd, blocks_to_go as usize ) . await ;
1530- node_a. sync_wallets ( ) . unwrap ( ) ;
1531- node_b. sync_wallets ( ) . unwrap ( ) ;
1532- } ,
1533- _ => panic ! ( "Unexpected balance state!" ) ,
1534- }
1535-
1536- assert ! ( node_a. list_balances( ) . lightning_balances. is_empty( ) ) ;
1537- assert_eq ! ( node_a. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
1538- match node_a. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
1539- PendingSweepBalance :: BroadcastAwaitingConfirmation { .. } => { } ,
1540- _ => panic ! ( "Unexpected balance state!" ) ,
1541- }
1542- generate_blocks_and_wait ( & bitcoind, electrsd, 1 ) . await ;
1543- node_a. sync_wallets ( ) . unwrap ( ) ;
1544- node_b. sync_wallets ( ) . unwrap ( ) ;
15451591
1546- assert ! ( node_a. list_balances( ) . lightning_balances. is_empty( ) ) ;
1547- assert_eq ! ( node_a. list_balances( ) . pending_balances_from_channel_closures. len( ) , 1 ) ;
1548- match node_a. list_balances ( ) . pending_balances_from_channel_closures [ 0 ] {
1549- PendingSweepBalance :: AwaitingThresholdConfirmations { .. } => { } ,
1550- _ => panic ! ( "Unexpected balance state!" ) ,
1551- }
1552- generate_blocks_and_wait ( & bitcoind, electrsd, 5 ) . await ;
1553- node_a. sync_wallets ( ) . unwrap ( ) ;
1554- node_b. sync_wallets ( ) . unwrap ( ) ;
1592+ settle_force_close_balance ( & node_a, node_b. node_id ( ) , & node_b, & bitcoind, electrsd) . await ;
15551593 } else {
15561594 assert_eq ! ( node_a. list_balances( ) . lightning_balances. len( ) , 1 ) ;
15571595 assert ! ( node_a. list_balances( ) . pending_balances_from_channel_closures. is_empty( ) ) ;
@@ -1597,6 +1635,25 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
15971635 assert ! ( node_b. list_balances( ) . pending_balances_from_channel_closures. is_empty( ) ) ;
15981636 }
15991637
1638+ if force_close {
1639+ assert_any_node_has_onchain_tx_type (
1640+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1641+ "UnilateralClose" ,
1642+ |tx_type| matches ! ( tx_type, TransactionType :: UnilateralClose { .. } ) ,
1643+ ) ;
1644+ assert_any_node_has_onchain_tx_type (
1645+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1646+ "Sweep" ,
1647+ |tx_type| matches ! ( tx_type, TransactionType :: Sweep { .. } ) ,
1648+ ) ;
1649+ } else {
1650+ assert_all_nodes_have_onchain_tx_type (
1651+ & [ ( "node_a" , & node_a) , ( "node_b" , & node_b) ] ,
1652+ "CooperativeClose" ,
1653+ |tx_type| matches ! ( tx_type, TransactionType :: CooperativeClose { .. } ) ,
1654+ ) ;
1655+ }
1656+
16001657 let sum_of_all_payments_sat = ( push_msat
16011658 + invoice_amount_1_msat
16021659 + overpaid_amount_msat
@@ -1619,20 +1676,20 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
16191676 assert_eq ! ( node_b. list_balances( ) . total_anchor_channels_reserve_sats, 0 ) ;
16201677
16211678 // Now we should have seen the channel closing transaction on-chain.
1622- assert_eq ! (
1623- node_a
1624- . list_payments_with_filter ( |p| p. direction == PaymentDirection :: Inbound
1625- && matches!( p. kind, PaymentKind :: Onchain { .. } ) )
1626- . len ( ) ,
1627- 3
1628- ) ;
1629- assert_eq ! (
1630- node_b
1631- . list_payments_with_filter ( |p| p . direction == PaymentDirection :: Inbound
1632- && matches! ( p . kind , PaymentKind :: Onchain { .. } ) )
1633- . len( ) ,
1634- 2
1635- ) ;
1679+ let node_a_inbound_onchain_count = node_a
1680+ . list_payments_with_filter ( |p| {
1681+ p. direction == PaymentDirection :: Inbound
1682+ && matches ! ( p. kind, PaymentKind :: Onchain { .. } )
1683+ } )
1684+ . len ( ) ;
1685+ let node_b_inbound_onchain_count = node_b
1686+ . list_payments_with_filter ( |p| {
1687+ p . direction == PaymentDirection :: Inbound
1688+ && matches ! ( p . kind , PaymentKind :: Onchain { .. } )
1689+ } )
1690+ . len ( ) ;
1691+ assert ! ( node_a_inbound_onchain_count >= 3 ) ;
1692+ assert ! ( node_b_inbound_onchain_count >= 2 ) ;
16361693
16371694 // Check we handled all events
16381695 assert_eq ! ( node_a. next_event( ) , None ) ;
0 commit comments